all repos — mgba @ a268d462c0ed4d834cf1035bbb645cea2978c86e

mGBA Game Boy Advance Emulator

GBA RR: Add VBM parser, for comparing VBA sync
Jeffrey Pfau jeffrey@endrift.com
Thu, 05 Mar 2015 14:25:35 -0800
commit

a268d462c0ed4d834cf1035bbb645cea2978c86e

parent

f69d9db9d46322a71761c7f98da21a3da8c5dbe4

3 files changed, 216 insertions(+), 0 deletions(-)

jump to
A src/gba/rr/vbm.c

@@ -0,0 +1,183 @@

+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "vbm.h" + +#include "gba/gba.h" +#include "gba/serialize.h" +#include "util/vfs.h" + +static const char VBM_MAGIC[] = "VBM\x1A"; + +static void GBAVBMContextDestroy(struct GBARRContext*); + +static bool GBAVBMStartPlaying(struct GBARRContext*, bool autorecord); +static void GBAVBMStopPlaying(struct GBARRContext*); +static bool GBAVBMStartRecording(struct GBARRContext*); +static void GBAVBMStopRecording(struct GBARRContext*); + +static bool GBAVBMIsPlaying(const struct GBARRContext*); +static bool GBAVBMIsRecording(const struct GBARRContext*); + +static void GBAVBMNextFrame(struct GBARRContext*); +static uint16_t GBAVBMQueryInput(struct GBARRContext*); + +static void GBAVBMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state); +static void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state); + +static struct VFile* GBAVBMOpenSavedata(struct GBARRContext*, int flags); +static struct VFile* GBAVBMOpenSavestate(struct GBARRContext*, int flags); + +void GBAVBMContextCreate(struct GBAVBMContext* vbm) { + memset(vbm, 0, sizeof(*vbm)); + + vbm->d.destroy = GBAVBMContextDestroy; + + vbm->d.startPlaying = GBAVBMStartPlaying; + vbm->d.stopPlaying = GBAVBMStopPlaying; + vbm->d.startRecording = GBAVBMStartRecording; + vbm->d.stopRecording = GBAVBMStopRecording; + + vbm->d.isPlaying = GBAVBMIsPlaying; + vbm->d.isRecording = GBAVBMIsRecording; + + vbm->d.nextFrame = GBAVBMNextFrame; + vbm->d.logInput = 0; + vbm->d.queryInput = GBAVBMQueryInput; + + vbm->d.stateSaved = GBAVBMStateSaved; + vbm->d.stateLoaded = GBAVBMStateLoaded; + + vbm->d.openSavedata = GBAVBMOpenSavedata; + vbm->d.openSavestate = GBAVBMOpenSavestate; +} + +bool GBAVBMStartPlaying(struct GBARRContext* rr, bool autorecord) { + if (rr->isRecording(rr) || rr->isPlaying(rr) || autorecord) { + return false; + } + + struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; + vbm->isPlaying = true; + vbm->vbmFile->seek(vbm->vbmFile, vbm->inputOffset, SEEK_SET); + return true; +} + +void GBAVBMStopPlaying(struct GBARRContext* rr) { + if (!rr->isPlaying(rr)) { + return; + } + + struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; + vbm->isPlaying = false; +} + +bool GBAVBMStartRecording(struct GBARRContext* rr) { + UNUSED(rr); + return false; +} + +void GBAVBMStopRecording(struct GBARRContext* rr) { + UNUSED(rr); +} + +bool GBAVBMIsPlaying(const struct GBARRContext* rr) { + struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; + return vbm->isPlaying; +} + +bool GBAVBMIsRecording(const struct GBARRContext* rr) { + UNUSED(rr); + return false; +} + +void GBAVBMNextFrame(struct GBARRContext* rr) { + UNUSED(rr); +} + +uint16_t GBAVBMQueryInput(struct GBARRContext* rr) { + if (!rr->isPlaying(rr)) { + return 0; + } + + struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; + uint16_t input; + vbm->vbmFile->read(vbm->vbmFile, &input, sizeof(input)); + return input & 0x3FF; +} + +void GBAVBMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state) { + UNUSED(rr); + UNUSED(state); +} + +void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state) { + UNUSED(rr); + UNUSED(state); +} + +struct VFile* GBAVBMOpenSavedata(struct GBARRContext* rr, int flags) { + UNUSED(rr); + UNUSED(flags); + return 0; +} + +struct VFile* GBAVBMOpenSavestate(struct GBARRContext* rr, int flags) { + UNUSED(rr); + UNUSED(flags); + return 0; +} + +void GBAVBMContextDestroy(struct GBARRContext* rr) { + struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; + if (vbm->vbmFile) { + vbm->vbmFile->close(vbm->vbmFile); + } +} + +bool GBAVBMSetStream(struct GBAVBMContext* vbm, struct VFile* vf) { + vf->seek(vf, 0, SEEK_SET); + char magic[4]; + vf->read(vf, magic, sizeof(magic)); + if (memcmp(magic, VBM_MAGIC, sizeof(magic)) != 0) { + return false; + } + + uint32_t id; + vf->read(vf, &id, sizeof(id)); + if (id != 1) { + return false; + } + + vf->seek(vf, 4, SEEK_CUR); + vf->read(vf, &vbm->d.frames, sizeof(vbm->d.frames)); + vf->read(vf, &vbm->d.rrCount, sizeof(vbm->d.rrCount)); + + uint8_t flags; + vf->read(vf, &flags, sizeof(flags)); + if (flags & 1) { + // Incompatible savestate format + return false; + } + if (flags & 2) { + // TODO: Implement SRAM loading + return false; + } + + vf->seek(vf, 1, SEEK_CUR); + vf->read(vf, &flags, sizeof(flags)); + if ((flags & 0x7) != 1) { + // Non-GBA movie + return false; + } + + // TODO: parse more flags + + vf->seek(vf, 0x3C, SEEK_SET); + vf->read(vf, &vbm->inputOffset, sizeof(vbm->inputOffset)); + vf->seek(vf, vbm->inputOffset, SEEK_SET); + vbm->vbmFile = vf; + return true; +}
A src/gba/rr/vbm.h

@@ -0,0 +1,21 @@

+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "util/common.h" + +#include "gba/supervisor/rr.h" + +struct GBAVBMContext { + struct GBARRContext d; + + bool isPlaying; + + struct VFile* vbmFile; + int32_t inputOffset; +}; + +void GBAVBMContextCreate(struct GBAVBMContext*); + +bool GBAVBMSetStream(struct GBAVBMContext*, struct VFile*);
M src/gba/supervisor/thread.csrc/gba/supervisor/thread.c

@@ -11,6 +11,7 @@ #include "gba/cheats.h"

#include "gba/serialize.h" #include "gba/supervisor/config.h" #include "gba/rr/mgm.h" +#include "gba/rr/vbm.h" #include "debugger/debugger.h"

@@ -186,6 +187,17 @@ if (!GBAMGMSetStream(mgm, movieDir)) {

mgm->d.destroy(&mgm->d); } else { movie = &mgm->d; + } + } else { + struct VFile* movieFile = VFileOpen(threadContext->movie, O_RDONLY); + if (movieFile) { + struct GBAVBMContext* vbm = malloc(sizeof(*vbm)); + GBAVBMContextCreate(vbm); + if (!GBAVBMSetStream(vbm, movieFile)) { + vbm->d.destroy(&vbm->d); + } else { + movie = &vbm->d; + } } } }