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}