all repos — mgba @ b8593bdb7bd2d2c790be24c7b1e94ee88d4f5390

mGBA Game Boy Advance Emulator

src/gba/core.c (view raw)

  1/* Copyright (c) 2013-2016 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 <mgba/gba/core.h>
  7
  8#include <mgba/core/core.h>
  9#include <mgba/core/log.h>
 10#include <mgba/internal/arm/debugger/debugger.h>
 11#include <mgba/internal/gba/cheats.h>
 12#include <mgba/internal/gba/gba.h>
 13#include <mgba/internal/gba/io.h>
 14#include <mgba/internal/gba/extra/cli.h>
 15#include <mgba/internal/gba/overrides.h>
 16#ifndef DISABLE_THREADING
 17#include <mgba/internal/gba/renderers/thread-proxy.h>
 18#endif
 19#include <mgba/internal/gba/renderers/video-software.h>
 20#include <mgba/internal/gba/savedata.h>
 21#include <mgba/internal/gba/serialize.h>
 22#include <mgba-util/memory.h>
 23#include <mgba-util/patch.h>
 24#include <mgba-util/vfs.h>
 25
 26const static struct mCoreChannelInfo _GBAVideoLayers[] = {
 27	{ 0, "bg0", "Background 0", NULL },
 28	{ 1, "bg1", "Background 1", NULL },
 29	{ 2, "bg2", "Background 2", NULL },
 30	{ 3, "bg3", "Background 3", NULL },
 31	{ 4, "obj", "Objects", NULL },
 32};
 33
 34const static struct mCoreChannelInfo _GBAAudioChannels[] = {
 35	{ 0, "ch0", "PSG Channel 0", "Square/Sweep" },
 36	{ 1, "ch1", "PSG Channel 1", "Square" },
 37	{ 2, "ch2", "PSG Channel 2", "PCM" },
 38	{ 3, "ch3", "PSG Channel 3", "Noise" },
 39	{ 4, "chA", "FIFO Channel A", NULL },
 40	{ 5, "chB", "FIFO Channel B", NULL },
 41};
 42
 43struct GBACore {
 44	struct mCore d;
 45	struct GBAVideoSoftwareRenderer renderer;
 46	struct GBAVideoProxyRenderer logProxy;
 47	struct mVideoLogContext* logContext;
 48	struct mCoreCallbacks logCallbacks;
 49#ifndef DISABLE_THREADING
 50	struct GBAVideoThreadProxyRenderer threadProxy;
 51	int threadedVideo;
 52#endif
 53	int keys;
 54	struct mCPUComponent* components[CPU_COMPONENT_MAX];
 55	const struct Configuration* overrides;
 56	struct mDebuggerPlatform* debuggerPlatform;
 57	struct mCheatDevice* cheatDevice;
 58};
 59
 60static bool _GBACoreInit(struct mCore* core) {
 61	struct GBACore* gbacore = (struct GBACore*) core;
 62
 63	struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore));
 64	struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA));
 65	if (!cpu || !gba) {
 66		free(cpu);
 67		free(gba);
 68		return false;
 69	}
 70	core->cpu = cpu;
 71	core->board = gba;
 72	core->debugger = NULL;
 73	gbacore->overrides = NULL;
 74	gbacore->debuggerPlatform = NULL;
 75	gbacore->cheatDevice = NULL;
 76	gbacore->logContext = NULL;
 77
 78	GBACreate(gba);
 79	// TODO: Restore cheats
 80	memset(gbacore->components, 0, sizeof(gbacore->components));
 81	ARMSetComponents(cpu, &gba->d, CPU_COMPONENT_MAX, gbacore->components);
 82	ARMInit(cpu);
 83	mRTCGenericSourceInit(&core->rtc, core);
 84	gba->rtcSource = &core->rtc.d;
 85
 86	GBAVideoSoftwareRendererCreate(&gbacore->renderer);
 87	gbacore->renderer.outputBuffer = NULL;
 88
 89#ifndef DISABLE_THREADING
 90	gbacore->threadedVideo = false;
 91	GBAVideoThreadProxyRendererCreate(&gbacore->threadProxy, &gbacore->renderer.d);
 92#endif
 93
 94	gbacore->keys = 0;
 95	gba->keySource = &gbacore->keys;
 96
 97#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 98	mDirectorySetInit(&core->dirs);
 99#endif
100	
101	return true;
102}
103
104static void _GBACoreDeinit(struct mCore* core) {
105	ARMDeinit(core->cpu);
106	GBADestroy(core->board);
107	mappedMemoryFree(core->cpu, sizeof(struct ARMCore));
108	mappedMemoryFree(core->board, sizeof(struct GBA));
109#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
110	mDirectorySetDeinit(&core->dirs);
111#endif
112
113	struct GBACore* gbacore = (struct GBACore*) core;
114	free(gbacore->debuggerPlatform);
115	if (gbacore->cheatDevice) {
116		mCheatDeviceDestroy(gbacore->cheatDevice);
117	}
118	free(gbacore->cheatDevice);
119	mCoreConfigFreeOpts(&core->opts);
120	free(core);
121}
122
123static enum mPlatform _GBACorePlatform(const struct mCore* core) {
124	UNUSED(core);
125	return PLATFORM_GBA;
126}
127
128static void _GBACoreSetSync(struct mCore* core, struct mCoreSync* sync) {
129	struct GBA* gba = core->board;
130	gba->sync = sync;
131}
132
133static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* config) {
134	struct GBA* gba = core->board;
135	if (core->opts.mute) {
136		gba->audio.masterVolume = 0;
137	} else {
138		gba->audio.masterVolume = core->opts.volume;
139	}
140	gba->video.frameskip = core->opts.frameskip;
141
142#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
143	struct GBACore* gbacore = (struct GBACore*) core;
144	gbacore->overrides = mCoreConfigGetOverridesConst(config);
145#endif
146
147	const char* idleOptimization = mCoreConfigGetValue(config, "idleOptimization");
148	if (idleOptimization) {
149		if (strcasecmp(idleOptimization, "ignore") == 0) {
150			gba->idleOptimization = IDLE_LOOP_IGNORE;
151		} else if (strcasecmp(idleOptimization, "remove") == 0) {
152			gba->idleOptimization = IDLE_LOOP_REMOVE;
153		} else if (strcasecmp(idleOptimization, "detect") == 0) {
154			if (gba->idleLoop == IDLE_LOOP_NONE) {
155				gba->idleOptimization = IDLE_LOOP_DETECT;
156			} else {
157				gba->idleOptimization = IDLE_LOOP_REMOVE;
158			}
159		}
160	}
161
162	mCoreConfigCopyValue(&core->config, config, "gba.bios");
163
164#ifndef DISABLE_THREADING
165	mCoreConfigGetIntValue(config, "threadedVideo", &gbacore->threadedVideo);
166#endif
167}
168
169static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
170	UNUSED(core);
171	*width = VIDEO_HORIZONTAL_PIXELS;
172	*height = VIDEO_VERTICAL_PIXELS;
173}
174
175static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
176	struct GBACore* gbacore = (struct GBACore*) core;
177	gbacore->renderer.outputBuffer = buffer;
178	gbacore->renderer.outputBufferStride = stride;
179}
180
181static void _GBACoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
182	struct GBACore* gbacore = (struct GBACore*) core;
183	gbacore->renderer.d.getPixels(&gbacore->renderer.d, stride, buffer);
184}
185
186static void _GBACorePutPixels(struct mCore* core, const void* buffer, size_t stride) {
187	struct GBACore* gbacore = (struct GBACore*) core;
188	gbacore->renderer.d.putPixels(&gbacore->renderer.d, stride, buffer);
189}
190
191static struct blip_t* _GBACoreGetAudioChannel(struct mCore* core, int ch) {
192	struct GBA* gba = core->board;
193	switch (ch) {
194	case 0:
195		return gba->audio.psg.left;
196	case 1:
197		return gba->audio.psg.right;
198	default:
199		return NULL;
200	}
201}
202
203static void _GBACoreSetAudioBufferSize(struct mCore* core, size_t samples) {
204	struct GBA* gba = core->board;
205	GBAAudioResizeBuffer(&gba->audio, samples);
206}
207
208static size_t _GBACoreGetAudioBufferSize(struct mCore* core) {
209	struct GBA* gba = core->board;
210	return gba->audio.samples;
211}
212
213static void _GBACoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
214	struct GBA* gba = core->board;
215	*mCoreCallbacksListAppend(&gba->coreCallbacks) = *coreCallbacks;
216}
217
218static void _GBACoreClearCoreCallbacks(struct mCore* core) {
219	struct GBA* gba = core->board;
220	mCoreCallbacksListClear(&gba->coreCallbacks);
221}
222
223static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
224	struct GBA* gba = core->board;
225	gba->stream = stream;
226	if (stream && stream->videoDimensionsChanged) {
227		stream->videoDimensionsChanged(stream, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
228	}
229}
230
231static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) {
232	if (GBAIsMB(vf)) {
233		return GBALoadMB(core->board, vf);
234	}
235	return GBALoadROM(core->board, vf);
236}
237
238static bool _GBACoreLoadBIOS(struct mCore* core, struct VFile* vf, int type) {
239	UNUSED(type);
240	if (!GBAIsBIOS(vf)) {
241		return false;
242	}
243	GBALoadBIOS(core->board, vf);
244	return true;
245}
246
247static bool _GBACoreLoadSave(struct mCore* core, struct VFile* vf) {
248	return GBALoadSave(core->board, vf);
249}
250
251static bool _GBACoreLoadTemporarySave(struct mCore* core, struct VFile* vf) {
252	struct GBA* gba = core->board;
253	GBASavedataMask(&gba->memory.savedata, vf, false);
254	return true; // TODO: Return a real value
255}
256
257static bool _GBACoreLoadPatch(struct mCore* core, struct VFile* vf) {
258	if (!vf) {
259		return false;
260	}
261	struct Patch patch;
262	if (!loadPatch(vf, &patch)) {
263		return false;
264	}
265	GBAApplyPatch(core->board, &patch);
266	return true;
267}
268
269static void _GBACoreUnloadROM(struct mCore* core) {
270	struct GBACore* gbacore = (struct GBACore*) core;
271	struct ARMCore* cpu = core->cpu;
272	if (gbacore->cheatDevice) {
273		ARMHotplugDetach(cpu, CPU_COMPONENT_CHEAT_DEVICE);
274		cpu->components[CPU_COMPONENT_CHEAT_DEVICE] = NULL;
275		mCheatDeviceDestroy(gbacore->cheatDevice);
276		gbacore->cheatDevice = NULL;
277	}
278	return GBAUnloadROM(core->board);
279}
280
281static void _GBACoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
282	struct GBA* gba = (struct GBA*) core->board;
283	switch (type) {
284	case CHECKSUM_CRC32:
285		memcpy(data, &gba->romCrc32, sizeof(gba->romCrc32));
286		break;
287	}
288	return;
289}
290
291static void _GBACoreReset(struct mCore* core) {
292	struct GBACore* gbacore = (struct GBACore*) core;
293	struct GBA* gba = (struct GBA*) core->board;
294	if (gbacore->renderer.outputBuffer) {
295		struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
296#ifndef DISABLE_THREADING
297		if (gbacore->threadedVideo) {
298			renderer = &gbacore->threadProxy.d.d;
299		}
300#endif
301		GBAVideoAssociateRenderer(&gba->video, renderer);
302	}
303
304	struct GBACartridgeOverride override;
305	const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
306	if (cart) {
307		memcpy(override.id, &cart->id, sizeof(override.id));
308		if (GBAOverrideFind(gbacore->overrides, &override)) {
309			GBAOverrideApply(gba, &override);
310		}
311	}
312
313#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
314	if (!gba->biosVf && core->opts.useBios) {
315		struct VFile* bios = NULL;
316		bool found = false;
317		if (core->opts.bios) {
318			bios = VFileOpen(core->opts.bios, O_RDONLY);
319			if (bios && GBAIsBIOS(bios)) {
320				found = true;
321			} else if (bios) {
322				bios->close(bios);
323				bios = NULL;
324			}
325		}
326		if (!found) {
327			const char* configPath = mCoreConfigGetValue(&core->config, "gba.bios");
328			if (configPath) {
329				bios = VFileOpen(configPath, O_RDONLY);
330			}
331			if (bios && GBAIsBIOS(bios)) {
332				found = true;
333			} else if (bios) {
334				bios->close(bios);
335				bios = NULL;
336			}
337		}
338		if (!found) {
339			char path[PATH_MAX];
340			mCoreConfigDirectory(path, PATH_MAX);
341			strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path));
342			bios = VFileOpen(path, O_RDONLY);
343			if (bios && GBAIsBIOS(bios)) {
344				found = true;
345			} else if (bios) {
346				bios->close(bios);
347				bios = NULL;
348			}
349		}
350		if (bios) {
351			GBALoadBIOS(gba, bios);
352		}
353	}
354#endif
355
356	ARMReset(core->cpu);
357	if (core->opts.skipBios && gba->isPristine) {
358		GBASkipBIOS(core->board);
359	}
360}
361
362static void _GBACoreRunFrame(struct mCore* core) {
363	struct GBA* gba = core->board;
364	int32_t frameCounter = gba->video.frameCounter;
365	while (gba->video.frameCounter == frameCounter) {
366		ARMRunLoop(core->cpu);
367	}
368}
369
370static void _GBACoreRunLoop(struct mCore* core) {
371	ARMRunLoop(core->cpu);
372}
373
374static void _GBACoreStep(struct mCore* core) {
375	ARMRun(core->cpu);
376}
377
378static size_t _GBACoreStateSize(struct mCore* core) {
379	UNUSED(core);
380	return sizeof(struct GBASerializedState);
381}
382
383static bool _GBACoreLoadState(struct mCore* core, const void* state) {
384	return GBADeserialize(core->board, state);
385}
386
387static bool _GBACoreSaveState(struct mCore* core, void* state) {
388	GBASerialize(core->board, state);
389	return true;
390}
391
392static void _GBACoreSetKeys(struct mCore* core, uint32_t keys) {
393	struct GBACore* gbacore = (struct GBACore*) core;
394	gbacore->keys = keys;
395}
396
397static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
398	struct GBACore* gbacore = (struct GBACore*) core;
399	gbacore->keys |= keys;
400}
401
402static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
403	struct GBACore* gbacore = (struct GBACore*) core;
404	gbacore->keys &= ~keys;
405}
406
407static int32_t _GBACoreFrameCounter(const struct mCore* core) {
408	const struct GBA* gba = core->board;
409	return gba->video.frameCounter;
410}
411
412static int32_t _GBACoreFrameCycles(const struct mCore* core) {
413	UNUSED(core);
414	return VIDEO_TOTAL_LENGTH;
415}
416
417static int32_t _GBACoreFrequency(const struct mCore* core) {
418	UNUSED(core);
419	return GBA_ARM7TDMI_FREQUENCY;
420}
421
422static void _GBACoreGetGameTitle(const struct mCore* core, char* title) {
423	GBAGetGameTitle(core->board, title);
424}
425
426static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
427	GBAGetGameCode(core->board, title);
428}
429
430static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
431	struct GBA* gba = core->board;
432	switch (type) {
433	case mPERIPH_ROTATION:
434		gba->rotationSource = periph;
435		break;
436	case mPERIPH_RUMBLE:
437		gba->rumble = periph;
438		break;
439	case mPERIPH_GBA_LUMINANCE:
440		gba->luminanceSource = periph;
441		break;
442	default:
443		return;
444	}
445}
446
447static uint32_t _GBACoreBusRead8(struct mCore* core, uint32_t address) {
448	struct ARMCore* cpu = core->cpu;
449	return cpu->memory.load8(cpu, address, 0);
450}
451
452static uint32_t _GBACoreBusRead16(struct mCore* core, uint32_t address) {
453	struct ARMCore* cpu = core->cpu;
454	return cpu->memory.load16(cpu, address, 0);
455
456}
457
458static uint32_t _GBACoreBusRead32(struct mCore* core, uint32_t address) {
459	struct ARMCore* cpu = core->cpu;
460	return cpu->memory.load32(cpu, address, 0);
461}
462
463static void _GBACoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
464	struct ARMCore* cpu = core->cpu;
465	cpu->memory.store8(cpu, address, value, 0);
466}
467
468static void _GBACoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
469	struct ARMCore* cpu = core->cpu;
470	cpu->memory.store16(cpu, address, value, 0);
471}
472
473static void _GBACoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
474	struct ARMCore* cpu = core->cpu;
475	cpu->memory.store32(cpu, address, value, 0);
476}
477
478static uint32_t _GBACoreRawRead8(struct mCore* core, uint32_t address, int segment) {
479	UNUSED(segment);
480	struct ARMCore* cpu = core->cpu;
481	return GBAView8(cpu, address);
482}
483
484static uint32_t _GBACoreRawRead16(struct mCore* core, uint32_t address, int segment) {
485	UNUSED(segment);
486	struct ARMCore* cpu = core->cpu;
487	return GBAView16(cpu, address);
488}
489
490static uint32_t _GBACoreRawRead32(struct mCore* core, uint32_t address, int segment) {
491	UNUSED(segment);
492	struct ARMCore* cpu = core->cpu;
493	return GBAView32(cpu, address);
494}
495
496static void _GBACoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
497	UNUSED(segment);
498	struct ARMCore* cpu = core->cpu;
499	GBAPatch8(cpu, address, value, NULL);
500}
501
502static void _GBACoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
503	UNUSED(segment);
504	struct ARMCore* cpu = core->cpu;
505	GBAPatch16(cpu, address, value, NULL);
506}
507
508static void _GBACoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
509	UNUSED(segment);
510	struct ARMCore* cpu = core->cpu;
511	GBAPatch32(cpu, address, value, NULL);
512}
513
514#ifdef USE_DEBUGGERS
515static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
516	UNUSED(core);
517	switch (type) {
518	case DEBUGGER_CLI:
519		return true;
520#ifdef USE_GDB_STUB
521	case DEBUGGER_GDB:
522		return true;
523#endif
524	default:
525		return false;
526	}
527}
528
529static struct mDebuggerPlatform* _GBACoreDebuggerPlatform(struct mCore* core) {
530	struct GBACore* gbacore = (struct GBACore*) core;
531	if (!gbacore->debuggerPlatform) {
532		gbacore->debuggerPlatform = ARMDebuggerPlatformCreate();
533	}
534	return gbacore->debuggerPlatform;
535}
536
537static struct CLIDebuggerSystem* _GBACoreCliDebuggerSystem(struct mCore* core) {
538	return &GBACLIDebuggerCreate(core)->d;
539}
540
541static void _GBACoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
542	if (core->debugger) {
543		GBADetachDebugger(core->board);
544	}
545	GBAAttachDebugger(core->board, debugger);
546	core->debugger = debugger;
547}
548
549static void _GBACoreDetachDebugger(struct mCore* core) {
550	GBADetachDebugger(core->board);
551	core->debugger = NULL;
552}
553#endif
554
555static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) {
556	struct GBACore* gbacore = (struct GBACore*) core;
557	if (!gbacore->cheatDevice) {
558		gbacore->cheatDevice = GBACheatDeviceCreate();
559		((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbacore->cheatDevice->d;
560		ARMHotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
561		gbacore->cheatDevice->p = core;
562	}
563	return gbacore->cheatDevice;
564}
565
566static size_t _GBACoreSavedataClone(struct mCore* core, void** sram) {
567	struct GBA* gba = core->board;
568	size_t size = GBASavedataSize(&gba->memory.savedata);
569	if (!size) {
570		*sram = NULL;
571		return 0;
572	}
573	*sram = malloc(size);
574	struct VFile* vf = VFileFromMemory(*sram, size);
575	if (!vf) {
576		free(*sram);
577		*sram = NULL;
578		return 0;
579	}
580	bool success = GBASavedataClone(&gba->memory.savedata, vf);
581	vf->close(vf);
582	if (!success) {
583		free(*sram);
584		*sram = NULL;
585		return 0;
586	}
587	return size;
588}
589
590static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
591	struct VFile* vf = VFileMemChunk(sram, size);
592	if (!vf) {
593		return false;
594	}
595	struct GBA* gba = core->board;
596	bool success = true;
597	if (writeback) {
598		success = GBASavedataLoad(&gba->memory.savedata, vf);
599		vf->close(vf);
600	} else {
601		GBASavedataMask(&gba->memory.savedata, vf, true);
602	}
603	return success;
604}
605
606static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
607	UNUSED(core);
608	*info = _GBAVideoLayers;
609	return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers);
610}
611
612static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
613	UNUSED(core);
614	*info = _GBAAudioChannels;
615	return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels);
616}
617
618static void _GBACoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
619	struct GBA* gba = core->board;
620	switch (id) {
621	case 0:
622	case 1:
623	case 2:
624	case 3:
625		gba->video.renderer->disableBG[id] = !enable;
626		break;
627	case 4:
628		gba->video.renderer->disableOBJ = !enable;
629		break;
630	default:
631		break;
632	}
633}
634
635static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
636	struct GBA* gba = core->board;
637	switch (id) {
638	case 0:
639	case 1:
640	case 2:
641	case 3:
642		gba->audio.psg.forceDisableCh[id] = !enable;
643		break;
644	case 4:
645		gba->audio.forceDisableChA = !enable;
646	case 5:
647		gba->audio.forceDisableChB = !enable;
648		break;
649	default:
650		break;
651	}
652}
653
654static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
655	struct GBACore* gbacore = (struct GBACore*) core;
656	struct GBA* gba = core->board;
657	gbacore->logContext = context;
658
659	GBAVideoProxyRendererCreate(&gbacore->logProxy, gba->video.renderer, false);
660
661	context->initialStateSize = core->stateSize(core);
662	context->initialState = anonymousMemoryMap(context->initialStateSize);
663	core->saveState(core, context->initialState);
664	struct GBASerializedState* state = context->initialState;
665	state->id = 0;
666	state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM;
667
668	struct VFile* vf = VFileMemChunk(NULL, 0);
669	context->nChannels = 1;
670	context->channels[0].initialState = NULL;
671	context->channels[0].initialStateSize = 0;
672	context->channels[0].channelData = vf;
673	context->channels[0].type = 0;
674	gbacore->logProxy.logger.vf = vf;
675	gbacore->logProxy.logger.block = false;
676
677	GBAVideoProxyRendererShim(&gba->video, &gbacore->logProxy);
678}
679
680static void _GBACoreEndVideoLog(struct mCore* core) {
681	struct GBACore* gbacore = (struct GBACore*) core;
682	struct GBA* gba = core->board;
683	GBAVideoProxyRendererUnshim(&gba->video, &gbacore->logProxy);
684
685	mappedMemoryFree(gbacore->logContext->initialState, gbacore->logContext->initialStateSize);
686	gbacore->logContext->channels[0].channelData->close(gbacore->logContext->channels[0].channelData);
687}
688
689struct mCore* GBACoreCreate(void) {
690	struct GBACore* gbacore = malloc(sizeof(*gbacore));
691	struct mCore* core = &gbacore->d;
692	memset(&core->opts, 0, sizeof(core->opts));
693	core->cpu = NULL;
694	core->board = NULL;
695	core->debugger = NULL;
696	core->init = _GBACoreInit;
697	core->deinit = _GBACoreDeinit;
698	core->platform = _GBACorePlatform;
699	core->setSync = _GBACoreSetSync;
700	core->loadConfig = _GBACoreLoadConfig;
701	core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
702	core->setVideoBuffer = _GBACoreSetVideoBuffer;
703	core->getPixels = _GBACoreGetPixels;
704	core->putPixels = _GBACorePutPixels;
705	core->getAudioChannel = _GBACoreGetAudioChannel;
706	core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
707	core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
708	core->addCoreCallbacks = _GBACoreAddCoreCallbacks;
709	core->clearCoreCallbacks = _GBACoreClearCoreCallbacks;
710	core->setAVStream = _GBACoreSetAVStream;
711	core->isROM = GBAIsROM;
712	core->loadROM = _GBACoreLoadROM;
713	core->loadBIOS = _GBACoreLoadBIOS;
714	core->loadSave = _GBACoreLoadSave;
715	core->loadTemporarySave = _GBACoreLoadTemporarySave;
716	core->loadPatch = _GBACoreLoadPatch;
717	core->unloadROM = _GBACoreUnloadROM;
718	core->checksum = _GBACoreChecksum;
719	core->reset = _GBACoreReset;
720	core->runFrame = _GBACoreRunFrame;
721	core->runLoop = _GBACoreRunLoop;
722	core->step = _GBACoreStep;
723	core->stateSize = _GBACoreStateSize;
724	core->loadState = _GBACoreLoadState;
725	core->saveState = _GBACoreSaveState;
726	core->setKeys = _GBACoreSetKeys;
727	core->addKeys = _GBACoreAddKeys;
728	core->clearKeys = _GBACoreClearKeys;
729	core->frameCounter = _GBACoreFrameCounter;
730	core->frameCycles = _GBACoreFrameCycles;
731	core->frequency = _GBACoreFrequency;
732	core->getGameTitle = _GBACoreGetGameTitle;
733	core->getGameCode = _GBACoreGetGameCode;
734	core->setPeripheral = _GBACoreSetPeripheral;
735	core->busRead8 = _GBACoreBusRead8;
736	core->busRead16 = _GBACoreBusRead16;
737	core->busRead32 = _GBACoreBusRead32;
738	core->busWrite8 = _GBACoreBusWrite8;
739	core->busWrite16 = _GBACoreBusWrite16;
740	core->busWrite32 = _GBACoreBusWrite32;
741	core->rawRead8 = _GBACoreRawRead8;
742	core->rawRead16 = _GBACoreRawRead16;
743	core->rawRead32 = _GBACoreRawRead32;
744	core->rawWrite8 = _GBACoreRawWrite8;
745	core->rawWrite16 = _GBACoreRawWrite16;
746	core->rawWrite32 = _GBACoreRawWrite32;
747#ifdef USE_DEBUGGERS
748	core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
749	core->debuggerPlatform = _GBACoreDebuggerPlatform;
750	core->cliDebuggerSystem = _GBACoreCliDebuggerSystem;
751	core->attachDebugger = _GBACoreAttachDebugger;
752	core->detachDebugger = _GBACoreDetachDebugger;
753#endif
754	core->cheatDevice = _GBACoreCheatDevice;
755	core->savedataClone = _GBACoreSavedataClone;
756	core->savedataRestore = _GBACoreSavedataRestore;
757	core->listVideoLayers = _GBACoreListVideoLayers;
758	core->listAudioChannels = _GBACoreListAudioChannels;
759	core->enableVideoLayer = _GBACoreEnableVideoLayer;
760	core->enableAudioChannel = _GBACoreEnableAudioChannel;
761	core->startVideoLog = _GBACoreStartVideoLog;
762	core->endVideoLog = _GBACoreEndVideoLog;
763	return core;
764}
765
766#ifndef MINIMAL_CORE
767static void _GBAVLPStartFrameCallback(void *context) {
768	struct mCore* core = context;
769	struct GBACore* gbacore = (struct GBACore*) core;
770	struct GBA* gba = core->board;
771
772	if (!mVideoLoggerRendererRun(&gbacore->logProxy.logger, true)) {
773		GBAVideoProxyRendererUnshim(&gba->video, &gbacore->logProxy);
774		gbacore->logProxy.logger.vf->seek(gbacore->logProxy.logger.vf, 0, SEEK_SET);
775		core->loadState(core, gbacore->logContext->initialState);
776		GBAVideoProxyRendererShim(&gba->video, &gbacore->logProxy);
777
778		// Make sure CPU loop never spins
779		GBAHalt(gba);
780		gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
781		gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
782	}
783}
784
785static bool _GBAVLPInit(struct mCore* core) {
786	struct GBACore* gbacore = (struct GBACore*) core;
787	GBAVideoProxyRendererCreate(&gbacore->logProxy, NULL, true);
788	memset(&gbacore->logCallbacks, 0, sizeof(gbacore->logCallbacks));
789	gbacore->logCallbacks.videoFrameStarted = _GBAVLPStartFrameCallback;
790	gbacore->logCallbacks.context = core;
791	if (_GBACoreInit(core)) {
792		core->addCoreCallbacks(core, &gbacore->logCallbacks);
793		return true;
794	}
795	return false;
796}
797
798static void _GBAVLPDeinit(struct mCore* core) {
799	struct GBACore* gbacore = (struct GBACore*) core;
800	if (gbacore->logContext) {
801		mVideoLoggerDestroy(core, gbacore->logContext);
802	}
803	_GBACoreDeinit(core);
804}
805
806static void _GBAVLPReset(struct mCore* core) {
807	struct GBACore* gbacore = (struct GBACore*) core;
808	struct GBA* gba = (struct GBA*) core->board;
809	if (gba->video.renderer == &gbacore->logProxy.d) {
810		GBAVideoProxyRendererUnshim(&gba->video, &gbacore->logProxy);
811	} else if (gbacore->renderer.outputBuffer) {
812		struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
813		GBAVideoAssociateRenderer(&gba->video, renderer);
814	}
815	gbacore->logProxy.logger.vf->seek(gbacore->logProxy.logger.vf, 0, SEEK_SET);
816
817	ARMReset(core->cpu);
818	core->loadState(core, gbacore->logContext->initialState);
819	GBAVideoProxyRendererShim(&gba->video, &gbacore->logProxy);
820
821	// Make sure CPU loop never spins
822	GBAHalt(gba);
823	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
824	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
825}
826
827static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) {
828	struct GBACore* gbacore = (struct GBACore*) core;
829	gbacore->logContext = mVideoLoggerCreate(NULL);
830	if (!mVideoLogContextLoad(vf, gbacore->logContext)) {
831		mVideoLoggerDestroy(core, gbacore->logContext);
832		gbacore->logContext = NULL;
833		return false;
834	}
835	gbacore->logProxy.logger.vf = gbacore->logContext->channels[0].channelData;
836	return true;
837}
838
839static bool _returnTrue(struct VFile* vf) {
840	UNUSED(vf);
841	return true;
842}
843
844struct mCore* GBAVideoLogPlayerCreate(void) {
845	struct mCore* core = GBACoreCreate();
846	core->init = _GBAVLPInit;
847	core->deinit = _GBAVLPDeinit;
848	core->reset = _GBAVLPReset;
849	core->loadROM = _GBAVLPLoadROM;
850	core->isROM = _returnTrue;
851	return core;
852}
853#else
854struct mCore* GBAVideoLogPlayerCreate(void) {
855	return false;
856}
857#endif