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 ssize_t read;
90 do {
91 read = inputs->read(inputs, block.inputs, sizeof(block.inputs));
92 if (read) {
93 struct GBARRBlock* newBlock = calloc(1, sizeof(*rr->currentBlock));
94 memcpy(newBlock, &block, sizeof(*newBlock));
95 if (!rr->rootBlock) {
96 rr->rootBlock = newBlock;
97 }
98 if (rr->currentBlock) {
99 rr->currentBlock->next = newBlock;
100 }
101 rr->currentBlock = newBlock;
102 }
103 } while (read > 0);
104
105 if (!inputs->close(inputs)) {
106 return false;
107 }
108
109 return read >= 0;
110}
111
112bool GBARRStartPlaying(struct GBA* gba) {
113 if (!gba->rr) {
114 return false;
115 }
116 if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
117 return false;
118 }
119
120 gba->rr->playbackBlock = gba->rr->rootBlock;
121 gba->rr->inputId = 0;
122 return !!gba->rr->playbackBlock;
123}
124
125void GBARRStopPlaying(struct GBA* gba) {
126 if (!gba->rr) {
127 return;
128 }
129
130 gba->rr->playbackBlock = 0;
131}
132
133bool GBARRStartRecording(struct GBA* gba) {
134 if (!gba->rr) {
135 GBARRContextCreate(gba);
136 }
137 if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
138 return false;
139 }
140
141 gba->rr->isRecording = true;
142 return true;
143}
144
145void GBARRStopRecording(struct GBA* gba) {
146 if (!gba->rr) {
147 return;
148 }
149
150 gba->rr->isRecording = false;
151}
152
153bool GBARRIsPlaying(struct GBA* gba) {
154 if (!gba->rr) {
155 return false;
156 }
157 return gba->rr->playbackBlock;
158}
159
160bool GBARRIsRecording(struct GBA* gba) {
161 if (!gba->rr) {
162 return false;
163 }
164 return gba->rr->isRecording;
165}
166
167void GBARRNextFrame(struct GBA* gba) {
168 if (!GBARRIsRecording(gba)) {
169 return;
170 }
171
172 struct GBARRContext* rr = gba->rr;
173
174 ++rr->frames;
175 if (!rr->inputThisFrame) {
176 ++rr->lagFrames;
177 }
178
179 rr->inputThisFrame = false;
180}
181
182void GBARRLogInput(struct GBA* gba, uint16_t input) {
183 if (!GBARRIsRecording(gba)) {
184 return;
185 }
186
187 struct GBARRContext* rr = gba->rr;
188 if (!rr->currentBlock) {
189 _allocBlock(rr);
190 }
191
192 size_t currentId = rr->currentBlock->numInputs;
193 if (currentId == GBA_RR_BLOCK_SIZE) {
194 _allocBlock(rr);
195 currentId = 0;
196 }
197
198 rr->currentBlock->inputs[currentId].keys = input;
199 ++rr->currentBlock->numInputs;
200 rr->inputThisFrame = true;
201}
202
203uint16_t GBARRQueryInput(struct GBA* gba) {
204 if (!GBARRIsPlaying(gba)) {
205 return 0;
206 }
207
208 struct GBARRBlock* block = gba->rr->playbackBlock;
209 size_t inputId = gba->rr->inputId;
210 uint16_t keys = 0;
211
212 keys = block->inputs[inputId].keys;
213 ++inputId;
214 if (inputId == GBA_RR_BLOCK_SIZE) {
215 inputId = 0;
216 gba->rr->playbackBlock = gba->rr->playbackBlock->next;
217 } else if (!gba->rr->playbackBlock->next && gba->rr->playbackBlock->numInputs == inputId) {
218 gba->rr->playbackBlock = 0;
219 }
220 gba->rr->inputId = inputId;
221 return keys;
222}
223
224void _allocBlock(struct GBARRContext* rr) {
225 struct GBARRBlock* block = calloc(1, sizeof(*rr->currentBlock));
226
227 if (!rr->currentBlock) {
228 rr->currentBlock = block;
229 rr->rootBlock = block;
230 return;
231 }
232
233 rr->currentBlock->next = block;
234 rr->currentBlock = block;
235}