src/gba/context/context.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/context/context.h"
7
8#include "gba/context/overrides.h"
9
10#include "util/memory.h"
11#include "util/vfs.h"
12
13static struct VFile* _logFile = 0;
14static void _GBAContextLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args);
15
16bool GBAContextInit(struct GBAContext* context, const char* port) {
17 context->gba = anonymousMemoryMap(sizeof(struct GBA));
18 context->cpu = anonymousMemoryMap(sizeof(struct ARMCore));
19 context->rom = 0;
20 context->romDir = 0;
21 context->bios = 0;
22 context->fname = 0;
23 context->save = 0;
24 context->renderer = 0;
25 memset(context->components, 0, sizeof(context->components));
26
27 if (!context->gba || !context->cpu) {
28 if (context->gba) {
29 mappedMemoryFree(context->gba, sizeof(struct GBA));
30 }
31 if (context->cpu) {
32 mappedMemoryFree(context->cpu, sizeof(struct ARMCore));
33 }
34 return false;
35 }
36 GBACreate(context->gba);
37 ARMSetComponents(context->cpu, &context->gba->d, GBA_COMPONENT_MAX, context->components);
38 ARMInit(context->cpu);
39
40 GBAConfigInit(&context->config, port);
41 if (port) {
42 if (!_logFile) {
43 char logPath[PATH_MAX];
44 GBAConfigDirectory(logPath, PATH_MAX);
45 strncat(logPath, PATH_SEP "log", PATH_MAX - strlen(logPath));
46 _logFile = VFileOpen(logPath, O_WRONLY | O_CREAT | O_TRUNC);
47 }
48 context->gba->logHandler = _GBAContextLog;
49
50 char biosPath[PATH_MAX];
51 GBAConfigDirectory(biosPath, PATH_MAX);
52 strncat(biosPath, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(biosPath));
53
54 struct GBAOptions opts = {
55 .bios = biosPath,
56 .useBios = true,
57 .idleOptimization = IDLE_LOOP_DETECT,
58 .logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL | GBA_LOG_STATUS
59 };
60 GBAConfigLoad(&context->config);
61 GBAConfigLoadDefaults(&context->config, &opts);
62 }
63
64 context->gba->sync = 0;
65 return true;
66}
67
68void GBAContextDeinit(struct GBAContext* context) {
69 ARMDeinit(context->cpu);
70 GBADestroy(context->gba);
71 mappedMemoryFree(context->gba, 0);
72 mappedMemoryFree(context->cpu, 0);
73 GBAConfigDeinit(&context->config);
74}
75
76bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autoloadSave) {
77 struct VDir* dir = VDirOpenArchive(path);
78 if (dir) {
79 struct VDirEntry* de;
80 while ((de = dir->listNext(dir))) {
81 struct VFile* vf = dir->openFile(dir, de->name(de), O_RDONLY);
82 if (!vf) {
83 continue;
84 }
85 if (GBAIsROM(vf)) {
86 context->rom = vf;
87 break;
88 }
89 vf->close(vf);
90 }
91 if (!context->rom) {
92 dir->close(dir);
93 } else {
94 context->romDir = dir;
95 }
96 } else {
97 context->rom = VFileOpen(path, O_RDONLY);
98 }
99
100 if (!context->rom) {
101 return false;
102 }
103
104 if (!GBAIsROM(context->rom)) {
105 context->rom->close(context->rom);
106 context->rom = 0;
107 return false;
108 }
109
110 context->fname = path;
111 if (autoloadSave) {
112 context->save = VDirOptionalOpenFile(0, path, 0, ".sav", O_RDWR | O_CREAT);
113 }
114 return true;
115}
116
117void GBAContextUnloadROM(struct GBAContext* context) {
118 GBAUnloadROM(context->gba);
119 if (context->bios) {
120 context->bios->close(context->bios);
121 context->bios = 0;
122 }
123 if (context->rom) {
124 context->rom->close(context->rom);
125 context->rom = 0;
126 }
127 if (context->romDir) {
128 context->romDir->close(context->romDir);
129 context->romDir = 0;
130 }
131 if (context->save) {
132 context->save->close(context->save);
133 context->save = 0;
134 }
135}
136
137bool GBAContextLoadROMFromVFile(struct GBAContext* context, struct VFile* rom, struct VFile* save) {
138 context->rom = rom;
139 if (!GBAIsROM(context->rom)) {
140 context->rom = 0;
141 return false;
142 }
143 context->save = save;
144 return true;
145}
146
147bool GBAContextLoadBIOS(struct GBAContext* context, const char* path) {
148 context->bios = VFileOpen(path, O_RDONLY);
149 if (!context->bios) {
150 return false;
151 }
152
153 if (!GBAIsBIOS(context->bios)) {
154 context->bios->close(context->bios);
155 context->bios = 0;
156 return false;
157 }
158 return true;
159}
160
161bool GBAContextLoadBIOSFromVFile(struct GBAContext* context, struct VFile* bios) {
162 context->bios = bios;
163 if (!GBAIsBIOS(context->bios)) {
164 context->bios = 0;
165 return false;
166 }
167 return true;
168}
169
170bool GBAContextStart(struct GBAContext* context) {
171 struct GBAOptions opts = { .bios = 0 };
172
173 if (context->renderer) {
174 GBAVideoAssociateRenderer(&context->gba->video, context->renderer);
175 }
176
177 if (!GBALoadROM(context->gba, context->rom, context->save, context->fname)) {
178 return false;
179 }
180
181 GBAConfigMap(&context->config, &opts);
182
183 if (!context->bios && opts.bios) {
184 GBAContextLoadBIOS(context, opts.bios);
185 }
186 if (opts.useBios && context->bios) {
187 GBALoadBIOS(context->gba, context->bios);
188 }
189 context->gba->logLevel = opts.logLevel;
190 context->gba->idleOptimization = opts.idleOptimization;
191
192 GBAContextReset(context);
193
194 // TODO: Move this into GBAContextReset
195 if (opts.skipBios) {
196 GBASkipBIOS(context->gba);
197 }
198
199 struct GBACartridgeOverride override;
200 const struct GBACartridge* cart = (const struct GBACartridge*) context->gba->memory.rom;
201 memcpy(override.id, &cart->id, sizeof(override.id));
202 if (GBAOverrideFind(GBAConfigGetOverrides(&context->config), &override)) {
203 GBAOverrideApply(context->gba, &override);
204 }
205 GBAConfigFreeOpts(&opts);
206 return true;
207}
208
209void GBAContextReset(struct GBAContext* context) {
210 ARMReset(context->cpu);
211}
212
213void GBAContextStop(struct GBAContext* context) {
214 UNUSED(context);
215 // TODO?
216}
217
218void GBAContextFrame(struct GBAContext* context, uint16_t keys) {
219 int activeKeys = keys;
220 context->gba->keySource = &activeKeys;
221
222 int frameCounter = context->gba->video.frameCounter;
223 while (frameCounter == context->gba->video.frameCounter) {
224 ARMRunLoop(context->cpu);
225 }
226}
227
228static void _GBAContextLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) {
229 UNUSED(thread);
230 UNUSED(level);
231 // TODO: Make this local
232 if (!_logFile) {
233 return;
234 }
235 char out[256];
236 size_t len = vsnprintf(out, sizeof(out), format, args);
237 if (len >= sizeof(out)) {
238 len = sizeof(out) - 1;
239 }
240 out[len] = '\n';
241 _logFile->write(_logFile, out, len + 1);
242}