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