src/gba/gba-rr.c (view raw)
1#include "gba-rr.h"
2
3#include "gba.h"
4#include "util/vfs.h"
5
6enum {
7 GBA_RR_BLOCK_SIZE = 1018
8};
9
10#define FILE_INPUTS "input.log"
11
12struct GBARRBlock {
13 union GBARRInput {
14 struct {
15 uint16_t keys : 10;
16 uint16_t : 4;
17 bool reset : 1;
18 bool : 1;
19 };
20 uint16_t packed;
21 } inputs[GBA_RR_BLOCK_SIZE];
22 size_t numInputs;
23 struct GBARRBlock* next;
24};
25
26static void _allocBlock(struct GBARRContext* rr);
27
28void GBARRContextCreate(struct GBA* gba) {
29 gba->rr = calloc(1, sizeof(*gba->rr));
30}
31
32void GBARRContextDestroy(struct GBA* gba) {
33 if (!gba->rr) {
34 return;
35 }
36
37 struct GBARRBlock* block = gba->rr->rootBlock;
38 while (block) {
39 struct GBARRBlock* nextBlock = block->next;
40 free(block);
41 block = nextBlock;
42 }
43 gba->rr->rootBlock = 0;
44 gba->rr->currentBlock = 0;
45 gba->rr->playbackBlock = 0;
46 free(gba->rr);
47 gba->rr = 0;
48}
49
50bool GBARRSave(struct GBARRContext* rr, struct VDir* vdir) {
51 if (!rr) {
52 return false;
53 }
54
55 struct VFile* inputs = vdir->openFile(vdir, FILE_INPUTS, O_WRONLY | O_CREAT | O_TRUNC);
56 if (!inputs) {
57 return false;
58 }
59
60 ssize_t written = 0;
61 struct GBARRBlock* inputBlock;
62 for (inputBlock = rr->rootBlock; inputBlock; inputBlock = inputBlock->next) {
63 ssize_t thisWrite = inputs->write(inputs, inputBlock->inputs, sizeof(*inputBlock->inputs) * inputBlock->numInputs);
64 if (!thisWrite) {
65 written = -1;
66 break;
67 }
68 written += thisWrite;
69 }
70
71 if (!inputs->close(inputs)) {
72 return false;
73 }
74
75 return written >= 0;
76}
77
78bool GBARRLoad(struct GBARRContext* rr, struct VDir* vdir) {
79 if (!rr) {
80 return false;
81 }
82
83 struct VFile* inputs = vdir->openFile(vdir, FILE_INPUTS, O_RDONLY);
84 if (!inputs) {
85 return false;
86 }
87
88 struct GBARRBlock block = {
89 .next = 0,
90 .numInputs = GBA_RR_BLOCK_SIZE
91 };
92
93 ssize_t read;
94 do {
95 read = inputs->read(inputs, block.inputs, sizeof(block.inputs));
96 if (read) {
97 struct GBARRBlock* newBlock = calloc(1, sizeof(*rr->currentBlock));
98 memcpy(newBlock, &block, sizeof(*newBlock));
99 if (!rr->rootBlock) {
100 rr->rootBlock = newBlock;
101 }
102 if (rr->currentBlock) {
103 rr->currentBlock->next = newBlock;
104 }
105 rr->currentBlock = newBlock;
106 newBlock->numInputs = read / sizeof(block.inputs[0]);
107 }
108 } while (read > 0);
109
110 if (!inputs->close(inputs)) {
111 return false;
112 }
113
114 return read >= 0;
115}
116
117bool GBARRStartPlaying(struct GBA* gba) {
118 if (!gba->rr) {
119 return false;
120 }
121 if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
122 return false;
123 }
124
125 gba->rr->playbackBlock = gba->rr->rootBlock;
126 gba->rr->inputId = 0;
127 return !!gba->rr->playbackBlock;
128}
129
130void GBARRStopPlaying(struct GBA* gba) {
131 if (!gba->rr) {
132 return;
133 }
134
135 gba->rr->playbackBlock = 0;
136}
137
138bool GBARRStartRecording(struct GBA* gba) {
139 if (!gba->rr) {
140 GBARRContextCreate(gba);
141 }
142 if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
143 return false;
144 }
145
146 gba->rr->isRecording = true;
147 return true;
148}
149
150void GBARRStopRecording(struct GBA* gba) {
151 if (!gba->rr) {
152 return;
153 }
154
155 gba->rr->isRecording = false;
156}
157
158bool GBARRIsPlaying(struct GBA* gba) {
159 if (!gba->rr) {
160 return false;
161 }
162 return gba->rr->playbackBlock;
163}
164
165bool GBARRIsRecording(struct GBA* gba) {
166 if (!gba->rr) {
167 return false;
168 }
169 return gba->rr->isRecording;
170}
171
172void GBARRNextFrame(struct GBA* gba) {
173 if (!GBARRIsRecording(gba)) {
174 return;
175 }
176
177 struct GBARRContext* rr = gba->rr;
178
179 ++rr->frames;
180 if (!rr->inputThisFrame) {
181 ++rr->lagFrames;
182 }
183
184 rr->inputThisFrame = false;
185}
186
187void GBARRLogInput(struct GBA* gba, uint16_t input) {
188 if (!GBARRIsRecording(gba)) {
189 return;
190 }
191
192 struct GBARRContext* rr = gba->rr;
193 if (!rr->currentBlock) {
194 _allocBlock(rr);
195 }
196
197 size_t currentId = rr->currentBlock->numInputs;
198 if (currentId == GBA_RR_BLOCK_SIZE) {
199 _allocBlock(rr);
200 currentId = 0;
201 }
202
203 rr->currentBlock->inputs[currentId].keys = input;
204 ++rr->currentBlock->numInputs;
205 rr->inputThisFrame = true;
206}
207
208uint16_t GBARRQueryInput(struct GBA* gba) {
209 if (!GBARRIsPlaying(gba)) {
210 return 0;
211 }
212
213 struct GBARRBlock* block = gba->rr->playbackBlock;
214 size_t inputId = gba->rr->inputId;
215 uint16_t keys = 0;
216
217 keys = block->inputs[inputId].keys;
218 ++inputId;
219 if (inputId == GBA_RR_BLOCK_SIZE) {
220 inputId = 0;
221 gba->rr->playbackBlock = gba->rr->playbackBlock->next;
222 } else if (!gba->rr->playbackBlock->next && gba->rr->playbackBlock->numInputs == inputId) {
223 gba->rr->playbackBlock = 0;
224 }
225 gba->rr->inputId = inputId;
226 return keys;
227}
228
229void _allocBlock(struct GBARRContext* rr) {
230 struct GBARRBlock* block = calloc(1, sizeof(*rr->currentBlock));
231
232 if (!rr->currentBlock) {
233 rr->currentBlock = block;
234 rr->rootBlock = block;
235 return;
236 }
237
238 rr->currentBlock->next = block;
239 rr->currentBlock = block;
240}