src/gba/rr/vbm.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 "vbm.h"
7
8#include "gba/gba.h"
9#include "gba/serialize.h"
10#include "util/vfs.h"
11
12static const char VBM_MAGIC[] = "VBM\x1A";
13
14static void GBAVBMContextDestroy(struct GBARRContext*);
15
16static bool GBAVBMStartPlaying(struct GBARRContext*, bool autorecord);
17static void GBAVBMStopPlaying(struct GBARRContext*);
18static bool GBAVBMStartRecording(struct GBARRContext*);
19static void GBAVBMStopRecording(struct GBARRContext*);
20
21static bool GBAVBMIsPlaying(const struct GBARRContext*);
22static bool GBAVBMIsRecording(const struct GBARRContext*);
23
24static void GBAVBMNextFrame(struct GBARRContext*);
25static uint16_t GBAVBMQueryInput(struct GBARRContext*);
26
27static void GBAVBMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state);
28static void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state);
29
30static struct VFile* GBAVBMOpenSavedata(struct GBARRContext*, int flags);
31static struct VFile* GBAVBMOpenSavestate(struct GBARRContext*, int flags);
32
33void GBAVBMContextCreate(struct GBAVBMContext* vbm) {
34 memset(vbm, 0, sizeof(*vbm));
35
36 vbm->d.destroy = GBAVBMContextDestroy;
37
38 vbm->d.startPlaying = GBAVBMStartPlaying;
39 vbm->d.stopPlaying = GBAVBMStopPlaying;
40 vbm->d.startRecording = GBAVBMStartRecording;
41 vbm->d.stopRecording = GBAVBMStopRecording;
42
43 vbm->d.isPlaying = GBAVBMIsPlaying;
44 vbm->d.isRecording = GBAVBMIsRecording;
45
46 vbm->d.nextFrame = GBAVBMNextFrame;
47 vbm->d.logInput = 0;
48 vbm->d.queryInput = GBAVBMQueryInput;
49
50 vbm->d.stateSaved = GBAVBMStateSaved;
51 vbm->d.stateLoaded = GBAVBMStateLoaded;
52
53 vbm->d.openSavedata = GBAVBMOpenSavedata;
54 vbm->d.openSavestate = GBAVBMOpenSavestate;
55}
56
57bool GBAVBMStartPlaying(struct GBARRContext* rr, bool autorecord) {
58 if (rr->isRecording(rr) || rr->isPlaying(rr) || autorecord) {
59 return false;
60 }
61
62 struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
63 vbm->isPlaying = true;
64 vbm->vbmFile->seek(vbm->vbmFile, vbm->inputOffset, SEEK_SET);
65 return true;
66}
67
68void GBAVBMStopPlaying(struct GBARRContext* rr) {
69 if (!rr->isPlaying(rr)) {
70 return;
71 }
72
73 struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
74 vbm->isPlaying = false;
75}
76
77bool GBAVBMStartRecording(struct GBARRContext* rr) {
78 UNUSED(rr);
79 return false;
80}
81
82void GBAVBMStopRecording(struct GBARRContext* rr) {
83 UNUSED(rr);
84}
85
86bool GBAVBMIsPlaying(const struct GBARRContext* rr) {
87 struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
88 return vbm->isPlaying;
89}
90
91bool GBAVBMIsRecording(const struct GBARRContext* rr) {
92 UNUSED(rr);
93 return false;
94}
95
96void GBAVBMNextFrame(struct GBARRContext* rr) {
97 if (!rr->isPlaying(rr)) {
98 return;
99 }
100
101 struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
102 vbm->vbmFile->seek(vbm->vbmFile, sizeof(uint16_t), SEEK_CUR);
103}
104
105uint16_t GBAVBMQueryInput(struct GBARRContext* rr) {
106 if (!rr->isPlaying(rr)) {
107 return 0;
108 }
109
110 struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
111 uint16_t input;
112 vbm->vbmFile->read(vbm->vbmFile, &input, sizeof(input));
113 vbm->vbmFile->seek(vbm->vbmFile, -sizeof(input), SEEK_CUR);
114 return input & 0x3FF;
115}
116
117void GBAVBMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state) {
118 UNUSED(rr);
119 UNUSED(state);
120}
121
122void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state) {
123 UNUSED(rr);
124 UNUSED(state);
125}
126
127struct VFile* GBAVBMOpenSavedata(struct GBARRContext* rr, int flags) {
128 UNUSED(rr);
129 UNUSED(flags);
130 return 0;
131}
132
133struct VFile* GBAVBMOpenSavestate(struct GBARRContext* rr, int flags) {
134 UNUSED(rr);
135 UNUSED(flags);
136 return 0;
137}
138
139void GBAVBMContextDestroy(struct GBARRContext* rr) {
140 struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
141 if (vbm->vbmFile) {
142 vbm->vbmFile->close(vbm->vbmFile);
143 }
144}
145
146bool GBAVBMSetStream(struct GBAVBMContext* vbm, struct VFile* vf) {
147 vf->seek(vf, 0, SEEK_SET);
148 char magic[4];
149 vf->read(vf, magic, sizeof(magic));
150 if (memcmp(magic, VBM_MAGIC, sizeof(magic)) != 0) {
151 return false;
152 }
153
154 uint32_t id;
155 vf->read(vf, &id, sizeof(id));
156 if (id != 1) {
157 return false;
158 }
159
160 vf->seek(vf, 4, SEEK_CUR);
161 vf->read(vf, &vbm->d.frames, sizeof(vbm->d.frames));
162 vf->read(vf, &vbm->d.rrCount, sizeof(vbm->d.rrCount));
163
164 uint8_t flags;
165 vf->read(vf, &flags, sizeof(flags));
166 if (flags & 1) {
167 // Incompatible savestate format
168 return false;
169 }
170 if (flags & 2) {
171 // TODO: Implement SRAM loading
172 return false;
173 }
174
175 vf->seek(vf, 1, SEEK_CUR);
176 vf->read(vf, &flags, sizeof(flags));
177 if ((flags & 0x7) != 1) {
178 // Non-GBA movie
179 return false;
180 }
181
182 // TODO: parse more flags
183
184 vf->seek(vf, 0x3C, SEEK_SET);
185 vf->read(vf, &vbm->inputOffset, sizeof(vbm->inputOffset));
186 vf->seek(vf, vbm->inputOffset, SEEK_SET);
187 vbm->vbmFile = vf;
188 return true;
189}