all repos — mgba @ 182efc916e0a21522f2277742fedbc23fb4c8aac

mGBA Game Boy Advance Emulator

src/platform/test/fuzz-main.c (view raw)

  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	"  -S FILE          Load a savestate when starting the test\n" \
 30	"  -V FILE          Overlay a second savestate over the loaded savestate\n" \
 31
 32struct FuzzOpts {
 33	bool noVideo;
 34	int frames;
 35	size_t overlayOffset;
 36	char* savestate;
 37	char* ssOverlay;
 38};
 39
 40static void _fuzzRunloop(struct mCore* core, int frames);
 41static void _fuzzShutdown(int signal);
 42static bool _parseFuzzOpts(struct mSubParser* parser, int option, const char* arg);
 43
 44static bool _dispatchExiting = false;
 45
 46int main(int argc, char** argv) {
 47	signal(SIGINT, _fuzzShutdown);
 48
 49	struct FuzzOpts fuzzOpts = { false, 0, 0, 0, 0 };
 50	struct mSubParser subparser = {
 51		.usage = FUZZ_USAGE,
 52		.parse = _parseFuzzOpts,
 53		.extraOptions = FUZZ_OPTIONS,
 54		.opts = &fuzzOpts
 55	};
 56
 57	struct mArguments args;
 58	bool parsed = parseArguments(&args, argc, argv, &subparser);
 59	if (!args.fname) {
 60		parsed = false;
 61	}
 62	if (!parsed || args.showHelp) {
 63		usage(argv[0], FUZZ_USAGE);
 64		return !parsed;
 65	}
 66	if (args.showVersion) {
 67		version(argv[0]);
 68		return 0;
 69	}
 70	struct mCore* core = mCoreFind(args.fname);
 71	if (!core) {
 72		return 1;
 73	}
 74	core->init(core);
 75	mCoreInitConfig(core, "fuzz");
 76	applyArguments(&args, NULL, &core->config);
 77
 78	mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "remove");
 79
 80	void* outputBuffer;
 81	outputBuffer = 0;
 82
 83	if (!fuzzOpts.noVideo) {
 84		outputBuffer = malloc(256 * 256 * 4);
 85		core->setVideoBuffer(core, outputBuffer, 256);
 86	}
 87
 88#ifdef M_CORE_GBA
 89	if (core->platform(core) == PLATFORM_GBA) {
 90		((struct GBA*) core->board)->hardCrash = false;
 91	}
 92#endif
 93
 94#ifdef __AFL_HAVE_MANUAL_CONTROL
 95	__AFL_INIT();
 96#endif
 97
 98	bool cleanExit = true;
 99	if (!mCoreLoadFile(core, args.fname)) {
100		cleanExit = false;
101		goto loadError;
102	}
103	if (args.patch) {
104		core->loadPatch(core, VFileOpen(args.patch, O_RDONLY));
105	}
106
107	struct VFile* savestate = 0;
108	struct VFile* savestateOverlay = 0;
109	size_t overlayOffset;
110
111	if (fuzzOpts.savestate) {
112		savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY);
113		free(fuzzOpts.savestate);
114	}
115	if (fuzzOpts.ssOverlay) {
116		overlayOffset = fuzzOpts.overlayOffset;
117		if (overlayOffset < core->stateSize(core)) {
118			savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY);
119		}
120		free(fuzzOpts.ssOverlay);
121	}
122
123	core->reset(core);
124
125	struct mCheatDevice* device;
126	if (args.cheatsFile && (device = core->cheatDevice(core))) {
127		struct VFile* vf = VFileOpen(args.cheatsFile, O_RDONLY);
128		if (vf) {
129			mCheatDeviceClear(device);
130			mCheatParseFile(device, vf);
131			vf->close(vf);
132		}
133	}
134
135	if (savestate) {
136		if (!savestateOverlay) {
137			mCoreLoadStateNamed(core, savestate, 0);
138		} else {
139			size_t size = core->stateSize(core);
140			uint8_t* state = malloc(size);
141			savestate->read(savestate, state, size);
142			savestateOverlay->read(savestateOverlay, state + overlayOffset, size - overlayOffset);
143			core->loadState(core, state);
144			free(state);
145			savestateOverlay->close(savestateOverlay);
146			savestateOverlay = 0;
147		}
148		savestate->close(savestate);
149		savestate = 0;
150	}
151
152	blip_set_rates(core->getAudioChannel(core, 0), GBA_ARM7TDMI_FREQUENCY, 0x8000);
153	blip_set_rates(core->getAudioChannel(core, 1), GBA_ARM7TDMI_FREQUENCY, 0x8000);
154
155	_fuzzRunloop(core, fuzzOpts.frames);
156
157	core->unloadROM(core);
158
159	if (savestate) {
160		savestate->close(savestate);
161	}
162	if (savestateOverlay) {
163		savestateOverlay->close(savestateOverlay);
164	}
165
166loadError:
167	freeArguments(&args);
168	if (outputBuffer) {
169		free(outputBuffer);
170	}
171	core->deinit(core);
172
173	return !cleanExit;
174}
175
176static void _fuzzRunloop(struct mCore* core, int frames) {
177	do {
178		core->runFrame(core);
179		--frames;
180		blip_clear(core->getAudioChannel(core, 0));
181		blip_clear(core->getAudioChannel(core, 1));
182	} while (frames > 0 && !_dispatchExiting);
183}
184
185static void _fuzzShutdown(int signal) {
186	UNUSED(signal);
187	_dispatchExiting = true;
188}
189
190static bool _parseFuzzOpts(struct mSubParser* parser, int option, const char* arg) {
191	struct FuzzOpts* opts = parser->opts;
192	errno = 0;
193	switch (option) {
194	case 'F':
195		opts->frames = strtoul(arg, 0, 10);
196		return !errno;
197	case 'N':
198		opts->noVideo = true;
199		return true;
200	case 'O':
201		opts->overlayOffset = strtoul(arg, 0, 10);
202		return !errno;
203	case 'S':
204		opts->savestate = strdup(arg);
205		return true;
206	case 'V':
207		opts->ssOverlay = strdup(arg);
208		return true;
209	default:
210		return false;
211	}
212}