1 /* Copyright (c) 2013-2015 Jeffrey Pfau
2  *
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <mgba/core/blip_buf.h>
7 #include <mgba/core/cheats.h>
8 #include <mgba/core/config.h>
9 #include <mgba/core/core.h>
10 #include <mgba/core/serialize.h>
11 #include <mgba/gb/core.h>
12 #include <mgba/gba/core.h>
13 #include <mgba/internal/gba/gba.h>
14 
15 #include <mgba/feature/commandline.h>
16 #include <mgba-util/memory.h>
17 #include <mgba-util/string.h>
18 #include <mgba-util/vfs.h>
19 
20 #include <errno.h>
21 #include <signal.h>
22 
23 #define FUZZ_OPTIONS "F:NO:S:V:"
24 #define FUZZ_USAGE \
25 	"\nAdditional options:\n" \
26 	"  -F FRAMES        Run for the specified number of FRAMES before exiting\n" \
27 	"  -N               Disable video rendering entirely\n" \
28 	"  -O OFFSET        Offset to apply savestate overlay\n" \
29 	"  -V FILE          Overlay a second savestate over the loaded savestate\n" \
30 
31 struct FuzzOpts {
32 	bool noVideo;
33 	int frames;
34 	size_t overlayOffset;
35 	char* ssOverlay;
36 };
37 
38 static void _fuzzRunloop(struct mCore* core, int frames);
39 static void _fuzzShutdown(int signal);
40 static bool _parseFuzzOpts(struct mSubParser* parser, int option, const char* arg);
41 
42 static bool _dispatchExiting = false;
43 
main(int argc,char ** argv)44 int main(int argc, char** argv) {
45 	signal(SIGINT, _fuzzShutdown);
46 
47 	struct FuzzOpts fuzzOpts = { false, 0, 0, 0 };
48 	struct mSubParser subparser = {
49 		.usage = FUZZ_USAGE,
50 		.parse = _parseFuzzOpts,
51 		.extraOptions = FUZZ_OPTIONS,
52 		.opts = &fuzzOpts
53 	};
54 
55 	struct mArguments args;
56 	bool parsed = parseArguments(&args, argc, argv, &subparser);
57 	if (!args.fname) {
58 		parsed = false;
59 	}
60 	if (!parsed || args.showHelp) {
61 		usage(argv[0], FUZZ_USAGE);
62 		return !parsed;
63 	}
64 	if (args.showVersion) {
65 		version(argv[0]);
66 		return 0;
67 	}
68 	struct mCore* core = mCoreFind(args.fname);
69 	if (!core) {
70 		return 1;
71 	}
72 	core->init(core);
73 	mCoreInitConfig(core, "fuzz");
74 	applyArguments(&args, NULL, &core->config);
75 
76 	mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "remove");
77 
78 	void* outputBuffer;
79 	outputBuffer = 0;
80 
81 	if (!fuzzOpts.noVideo) {
82 		outputBuffer = malloc(256 * 256 * 4);
83 		core->setVideoBuffer(core, outputBuffer, 256);
84 	}
85 
86 #ifdef M_CORE_GBA
87 	if (core->platform(core) == PLATFORM_GBA) {
88 		((struct GBA*) core->board)->hardCrash = false;
89 	}
90 #endif
91 
92 #ifdef __AFL_HAVE_MANUAL_CONTROL
93 	__AFL_INIT();
94 #endif
95 
96 	bool cleanExit = true;
97 	if (!mCoreLoadFile(core, args.fname)) {
98 		cleanExit = false;
99 		goto loadError;
100 	}
101 	if (args.patch) {
102 		core->loadPatch(core, VFileOpen(args.patch, O_RDONLY));
103 	}
104 
105 	struct VFile* savestate = 0;
106 	struct VFile* savestateOverlay = 0;
107 	size_t overlayOffset;
108 
109 	if (args.savestate) {
110 		savestate = VFileOpen(args.savestate, O_RDONLY);
111 	}
112 	if (fuzzOpts.ssOverlay) {
113 		overlayOffset = fuzzOpts.overlayOffset;
114 		if (overlayOffset < core->stateSize(core)) {
115 			savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY);
116 		}
117 		free(fuzzOpts.ssOverlay);
118 	}
119 
120 	core->reset(core);
121 
122 	struct mCheatDevice* device;
123 	if (args.cheatsFile && (device = core->cheatDevice(core))) {
124 		struct VFile* vf = VFileOpen(args.cheatsFile, O_RDONLY);
125 		if (vf) {
126 			mCheatDeviceClear(device);
127 			mCheatParseFile(device, vf);
128 			vf->close(vf);
129 		}
130 	}
131 
132 	if (savestate) {
133 		if (!savestateOverlay) {
134 			mCoreLoadStateNamed(core, savestate, 0);
135 		} else {
136 			size_t size = core->stateSize(core);
137 			uint8_t* state = malloc(size);
138 			savestate->read(savestate, state, size);
139 			savestateOverlay->read(savestateOverlay, state + overlayOffset, size - overlayOffset);
140 			core->loadState(core, state);
141 			free(state);
142 			savestateOverlay->close(savestateOverlay);
143 			savestateOverlay = 0;
144 		}
145 		savestate->close(savestate);
146 		savestate = 0;
147 	}
148 
149 	blip_set_rates(core->getAudioChannel(core, 0), GBA_ARM7TDMI_FREQUENCY, 0x8000);
150 	blip_set_rates(core->getAudioChannel(core, 1), GBA_ARM7TDMI_FREQUENCY, 0x8000);
151 
152 	_fuzzRunloop(core, fuzzOpts.frames);
153 
154 	core->unloadROM(core);
155 
156 	if (savestate) {
157 		savestate->close(savestate);
158 	}
159 	if (savestateOverlay) {
160 		savestateOverlay->close(savestateOverlay);
161 	}
162 
163 loadError:
164 	freeArguments(&args);
165 	if (outputBuffer) {
166 		free(outputBuffer);
167 	}
168 	mCoreConfigDeinit(&core->config);
169 	core->deinit(core);
170 
171 	return !cleanExit;
172 }
173 
_fuzzRunloop(struct mCore * core,int frames)174 static void _fuzzRunloop(struct mCore* core, int frames) {
175 	do {
176 		core->runFrame(core);
177 		--frames;
178 		blip_clear(core->getAudioChannel(core, 0));
179 		blip_clear(core->getAudioChannel(core, 1));
180 	} while (frames > 0 && !_dispatchExiting);
181 }
182 
_fuzzShutdown(int signal)183 static void _fuzzShutdown(int signal) {
184 	UNUSED(signal);
185 	_dispatchExiting = true;
186 }
187 
_parseFuzzOpts(struct mSubParser * parser,int option,const char * arg)188 static bool _parseFuzzOpts(struct mSubParser* parser, int option, const char* arg) {
189 	struct FuzzOpts* opts = parser->opts;
190 	errno = 0;
191 	switch (option) {
192 	case 'F':
193 		opts->frames = strtoul(arg, 0, 10);
194 		return !errno;
195 	case 'N':
196 		opts->noVideo = true;
197 		return true;
198 	case 'O':
199 		opts->overlayOffset = strtoul(arg, 0, 10);
200 		return !errno;
201 	case 'V':
202 		opts->ssOverlay = strdup(arg);
203 		return true;
204 	default:
205 		return false;
206 	}
207 }
208