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}