all repos — mgba @ f8fb86ef7986d6935234589972a792349c3c1d71

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	"  -V FILE          Overlay a second savestate over the loaded savestate\n" \
 30
 31struct FuzzOpts {
 32	bool noVideo;
 33	int frames;
 34	size_t overlayOffset;
 35	char* ssOverlay;
 36};
 37
 38static void _fuzzRunloop(struct mCore* core, int frames);
 39static void _fuzzShutdown(int signal);
 40static bool _parseFuzzOpts(struct mSubParser* parser, int option, const char* arg);
 41
 42static bool _dispatchExiting = false;
 43
 44int main(int argc, char** argv) {
 45	signal(SIGINT, _fuzzShutdown);
 46
 47	struct FuzzOpts fuzzOpts = { false, 0, 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
163loadError:
164	freeArguments(&args);
165	if (outputBuffer) {
166		free(outputBuffer);
167	}
168	core->deinit(core);
169
170	return !cleanExit;
171}
172
173static void _fuzzRunloop(struct mCore* core, int frames) {
174	do {
175		core->runFrame(core);
176		--frames;
177		blip_clear(core->getAudioChannel(core, 0));
178		blip_clear(core->getAudioChannel(core, 1));
179	} while (frames > 0 && !_dispatchExiting);
180}
181
182static void _fuzzShutdown(int signal) {
183	UNUSED(signal);
184	_dispatchExiting = true;
185}
186
187static bool _parseFuzzOpts(struct mSubParser* parser, int option, const char* arg) {
188	struct FuzzOpts* opts = parser->opts;
189	errno = 0;
190	switch (option) {
191	case 'F':
192		opts->frames = strtoul(arg, 0, 10);
193		return !errno;
194	case 'N':
195		opts->noVideo = true;
196		return true;
197	case 'O':
198		opts->overlayOffset = strtoul(arg, 0, 10);
199		return !errno;
200	case 'V':
201		opts->ssOverlay = strdup(arg);
202		return true;
203	default:
204		return false;
205	}
206}