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 "core/cheats.h"
7#include "core/config.h"
8#include "core/core.h"
9#include "core/serialize.h"
10#include "gb/core.h"
11#include "gba/gba.h"
12
13#include "feature/commandline.h"
14#include "util/memory.h"
15#include "util/string.h"
16#include "util/vfs.h"
17
18#include <errno.h>
19#include <signal.h>
20
21#define FUZZ_OPTIONS "F:NO:S:V:"
22#define FUZZ_USAGE \
23 "\nAdditional options:\n" \
24 " -F FRAMES Run for the specified number of FRAMES before exiting\n" \
25 " -N Disable video rendering entirely\n" \
26 " -O OFFSET Offset to apply savestate overlay\n" \
27 " -S FILE Load a savestate when starting the test\n" \
28 " -V FILE Overlay a second savestate over the loaded savestate\n" \
29
30struct FuzzOpts {
31 bool noVideo;
32 int frames;
33 size_t overlayOffset;
34 char* savestate;
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 core->init(core);
70 mCoreInitConfig(core, "fuzz");
71 applyArguments(&args, NULL, &core->config);
72
73 mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "remove");
74
75 void* outputBuffer;
76 outputBuffer = 0;
77
78 if (!fuzzOpts.noVideo) {
79 outputBuffer = malloc(256 * 256 * 4);
80 core->setVideoBuffer(core, outputBuffer, 256);
81 }
82
83#ifdef M_CORE_GBA
84 if (core->platform(core) == PLATFORM_GBA) {
85 ((struct GBA*) core->board)->hardCrash = false;
86 }
87#endif
88
89#ifdef __AFL_HAVE_MANUAL_CONTROL
90 __AFL_INIT();
91#endif
92 if (args.patch) {
93 core->loadPatch(core, VFileOpen(args.patch, O_RDONLY));
94 }
95 mCoreLoadFile(core, args.fname);
96
97 struct VFile* savestate = 0;
98 struct VFile* savestateOverlay = 0;
99 size_t overlayOffset;
100
101 if (fuzzOpts.savestate) {
102 savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY);
103 free(fuzzOpts.savestate);
104 }
105 if (fuzzOpts.ssOverlay) {
106 overlayOffset = fuzzOpts.overlayOffset;
107 if (overlayOffset < core->stateSize(core)) {
108 savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY);
109 }
110 free(fuzzOpts.ssOverlay);
111 }
112
113 core->reset(core);
114
115 struct mCheatDevice* device;
116 if (args.cheatsFile && (device = core->cheatDevice(core))) {
117 struct VFile* vf = VFileOpen(args.cheatsFile, O_RDONLY);
118 if (vf) {
119 mCheatDeviceClear(device);
120 mCheatParseFile(device, vf);
121 vf->close(vf);
122 }
123 }
124
125 if (savestate) {
126 if (!savestateOverlay) {
127 mCoreLoadStateNamed(core, savestate, 0);
128 } else {
129 size_t size = core->stateSize(core);
130 uint8_t* state = malloc(size);
131 savestate->read(savestate, state, size);
132 savestateOverlay->read(savestateOverlay, state + overlayOffset, size - overlayOffset);
133 core->loadState(core, state);
134 free(state);
135 savestateOverlay->close(savestateOverlay);
136 savestateOverlay = 0;
137 }
138 savestate->close(savestate);
139 savestate = 0;
140 }
141
142 blip_set_rates(core->getAudioChannel(core, 0), GBA_ARM7TDMI_FREQUENCY, 0x8000);
143 blip_set_rates(core->getAudioChannel(core, 1), GBA_ARM7TDMI_FREQUENCY, 0x8000);
144
145 _fuzzRunloop(core, fuzzOpts.frames);
146
147 core->unloadROM(core);
148
149 if (savestate) {
150 savestate->close(savestate);
151 }
152 if (savestateOverlay) {
153 savestateOverlay->close(savestateOverlay);
154 }
155
156 freeArguments(&args);
157 if (outputBuffer) {
158 free(outputBuffer);
159 }
160 core->deinit(core);
161
162 return 0;
163}
164
165static void _fuzzRunloop(struct mCore* core, int frames) {
166 do {
167 core->runFrame(core);
168 blip_clear(core->getAudioChannel(core, 0));
169 blip_clear(core->getAudioChannel(core, 1));
170 } while (core->frameCounter(core) < frames && !_dispatchExiting);
171}
172
173static void _fuzzShutdown(int signal) {
174 UNUSED(signal);
175 _dispatchExiting = true;
176}
177
178static bool _parseFuzzOpts(struct mSubParser* parser, int option, const char* arg) {
179 struct FuzzOpts* opts = parser->opts;
180 errno = 0;
181 switch (option) {
182 case 'F':
183 opts->frames = strtoul(arg, 0, 10);
184 return !errno;
185 case 'N':
186 opts->noVideo = true;
187 return true;
188 case 'O':
189 opts->overlayOffset = strtoul(arg, 0, 10);
190 return !errno;
191 case 'S':
192 opts->savestate = strdup(arg);
193 return true;
194 case 'V':
195 opts->ssOverlay = strdup(arg);
196 return true;
197 default:
198 return false;
199 }
200}