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