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