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 "gba/supervisor/thread.h"
7#include "gba/supervisor/config.h"
8#include "gba/gba.h"
9#include "gba/renderers/video-software.h"
10#include "gba/serialize.h"
11
12#include "platform/commandline.h"
13#include "util/memory.h"
14#include "util/string.h"
15#include "util/vfs.h"
16
17#include <errno.h>
18#include <signal.h>
19
20#define FUZZ_OPTIONS "F:NO:S:V:"
21#define FUZZ_USAGE \
22 "\nAdditional options:\n" \
23 " -F FRAMES Run for the specified number of FRAMES before exiting\n" \
24 " -N Disable video rendering entirely\n" \
25 " -O OFFSET Offset to apply savestate overlay\n" \
26 " -S FILE Load a savestate when starting the test\n" \
27 " -V FILE Overlay a second savestate over the loaded savestate\n" \
28
29struct FuzzOpts {
30 bool noVideo;
31 int frames;
32 size_t overlayOffset;
33 char* savestate;
34 char* ssOverlay;
35};
36
37static void _GBAFuzzRunloop(struct GBA* gba, int frames);
38static void _GBAFuzzShutdown(int signal);
39static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg);
40
41static bool _dispatchExiting = false;
42
43int main(int argc, char** argv) {
44 signal(SIGINT, _GBAFuzzShutdown);
45
46 struct FuzzOpts fuzzOpts = { false, 0, 0, 0, 0 };
47 struct SubParser subparser = {
48 .usage = FUZZ_USAGE,
49 .parse = _parseFuzzOpts,
50 .extraOptions = FUZZ_OPTIONS,
51 .opts = &fuzzOpts
52 };
53
54 struct GBAConfig config;
55 GBAConfigInit(&config, "fuzz");
56 GBAConfigLoad(&config);
57
58 struct GBAOptions opts = {
59 .idleOptimization = IDLE_LOOP_DETECT
60 };
61 GBAConfigLoadDefaults(&config, &opts);
62
63 struct GBAArguments args;
64 bool parsed = parseArguments(&args, &config, argc, argv, &subparser);
65 if (!parsed || args.showHelp) {
66 usage(argv[0], FUZZ_USAGE);
67 freeArguments(&args);
68 GBAConfigFreeOpts(&opts);
69 GBAConfigDeinit(&config);
70 return !parsed;
71 }
72
73 struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA));
74 struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore));
75 struct VFile* rom = VFileOpen(args.fname, O_RDONLY);
76
77 GBACreate(gba);
78 ARMSetComponents(cpu, &gba->d, 0, 0);
79 ARMInit(cpu);
80 gba->sync = 0;
81 gba->hardCrash = false;
82
83 GBALoadROM(gba, rom, 0, 0);
84 ARMReset(cpu);
85
86 struct GBACartridgeOverride override;
87 const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
88 memcpy(override.id, &cart->id, sizeof(override.id));
89 if (GBAOverrideFind(GBAConfigGetOverrides(&config), &override)) {
90 GBAOverrideApply(gba, &override);
91 }
92
93 struct GBAVideoSoftwareRenderer renderer;
94 renderer.outputBuffer = 0;
95
96 struct VFile* savestate = 0;
97 struct VFile* savestateOverlay = 0;
98 size_t overlayOffset;
99
100 if (!fuzzOpts.noVideo) {
101 GBAVideoSoftwareRendererCreate(&renderer);
102 renderer.outputBuffer = malloc(256 * 256 * 4);
103 renderer.outputBufferStride = 256;
104 GBAVideoAssociateRenderer(&gba->video, &renderer.d);
105 }
106
107 if (fuzzOpts.savestate) {
108 savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY);
109 free(fuzzOpts.savestate);
110 }
111 if (fuzzOpts.ssOverlay) {
112 overlayOffset = fuzzOpts.overlayOffset;
113 if (overlayOffset < sizeof(struct GBASerializedState)) {
114 savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY);
115 }
116 free(fuzzOpts.ssOverlay);
117 }
118 if (savestate) {
119 if (!savestateOverlay) {
120 GBALoadStateNamed(gba, savestate);
121 } else {
122 struct GBASerializedState* state = GBAAllocateState();
123 savestate->read(savestate, state, sizeof(*state));
124 savestateOverlay->read(savestateOverlay, (uint8_t*) state + overlayOffset, sizeof(*state) - overlayOffset);
125 GBADeserialize(gba, state);
126 GBADeallocateState(state);
127 savestateOverlay->close(savestateOverlay);
128 savestateOverlay = 0;
129 }
130 savestate->close(savestate);
131 savestate = 0;
132 }
133
134 GBAConfigMap(&config, &opts);
135
136 blip_set_rates(gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 0x8000);
137 blip_set_rates(gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 0x8000);
138
139 _GBAFuzzRunloop(gba, fuzzOpts.frames);
140
141 if (savestate) {
142 savestate->close(savestate);
143 }
144 if (savestateOverlay) {
145 savestateOverlay->close(savestateOverlay);
146 }
147 GBAConfigFreeOpts(&opts);
148 freeArguments(&args);
149 GBAConfigDeinit(&config);
150 if (renderer.outputBuffer) {
151 free(renderer.outputBuffer);
152 }
153
154 return 0;
155}
156
157static void _GBAFuzzRunloop(struct GBA* gba, int frames) {
158 do {
159 ARMRunLoop(gba->cpu);
160 } while (gba->video.frameCounter < frames && !_dispatchExiting);
161}
162
163static void _GBAFuzzShutdown(int signal) {
164 UNUSED(signal);
165 _dispatchExiting = true;
166}
167
168static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg) {
169 UNUSED(config);
170 struct FuzzOpts* opts = parser->opts;
171 errno = 0;
172 switch (option) {
173 case 'F':
174 opts->frames = strtoul(arg, 0, 10);
175 return !errno;
176 case 'N':
177 opts->noVideo = true;
178 return true;
179 case 'O':
180 opts->overlayOffset = strtoul(arg, 0, 10);
181 return !errno;
182 case 'S':
183 opts->savestate = strdup(arg);
184 return true;
185 case 'V':
186 opts->ssOverlay = strdup(arg);
187 return true;
188 default:
189 return false;
190 }
191}