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
10struct GBARRBlock {
11 union GBARRInput {
12 struct {
13 uint16_t keys : 10;
14 uint16_t : 4;
15 bool reset : 1;
16 bool : 1;
17 };
18 uint16_t packed;
19 } inputs[GBA_RR_BLOCK_SIZE];
20 size_t numInputs;
21 struct GBARRBlock* next;
22};
23
24static void _allocBlock(struct GBARRContext* rr);
25
26void GBARRContextCreate(struct GBA* gba) {
27 gba->rr = calloc(1, sizeof(*gba->rr));
28}
29
30void GBARRContextDestroy(struct GBA* gba) {
31 if (!gba->rr) {
32 return;
33 }
34
35 struct GBARRBlock* block = gba->rr->rootBlock;
36 while (block) {
37 struct GBARRBlock* nextBlock = block->next;
38 free(block);
39 block = nextBlock;
40 }
41 gba->rr->rootBlock = 0;
42 gba->rr->currentBlock = 0;
43 gba->rr->playbackBlock = 0;
44 free(gba->rr);
45}
46
47bool GBARRStartPlaying(struct GBA* gba) {
48 if (!gba->rr) {
49 return false;
50 }
51 if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
52 return false;
53 }
54
55 gba->rr->playbackBlock = gba->rr->rootBlock;
56 gba->rr->inputId = 0;
57 return !!gba->rr->playbackBlock;
58}
59
60void GBARRStopPlaying(struct GBA* gba) {
61 if (!gba->rr) {
62 return;
63 }
64
65 gba->rr->playbackBlock = 0;
66}
67
68bool GBARRStartRecording(struct GBA* gba) {
69 if (!gba->rr) {
70 GBARRContextCreate(gba);
71 }
72 if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
73 return false;
74 }
75
76 gba->rr->isRecording = true;
77 return true;
78}
79
80void GBARRStopRecording(struct GBA* gba) {
81 if (!gba->rr) {
82 return;
83 }
84
85 gba->rr->isRecording = false;
86}
87
88bool GBARRIsPlaying(struct GBA* gba) {
89 if (!gba->rr) {
90 return false;
91 }
92 return gba->rr->playbackBlock;
93}
94
95bool GBARRIsRecording(struct GBA* gba) {
96 if (!gba->rr) {
97 return false;
98 }
99 return gba->rr->isRecording;
100}
101
102void GBARRNextFrame(struct GBA* gba) {
103 if (!GBARRIsRecording(gba)) {
104 return;
105 }
106
107 struct GBARRContext* rr = gba->rr;
108
109 ++rr->frames;
110 if (!rr->inputThisFrame) {
111 ++rr->lagFrames;
112 }
113
114 rr->inputThisFrame = false;
115}
116
117void GBARRLogInput(struct GBA* gba, uint16_t input) {
118 if (!GBARRIsRecording(gba)) {
119 return;
120 }
121
122 struct GBARRContext* rr = gba->rr;
123 if (!rr->currentBlock) {
124 _allocBlock(rr);
125 }
126
127 size_t currentId = rr->currentBlock->numInputs;
128 if (currentId == GBA_RR_BLOCK_SIZE) {
129 _allocBlock(rr);
130 currentId = 0;
131 }
132
133 rr->currentBlock->inputs[currentId].keys = input;
134 ++rr->currentBlock->numInputs;
135 rr->inputThisFrame = true;
136}
137
138uint16_t GBARRQueryInput(struct GBA* gba) {
139 if (!GBARRIsPlaying(gba)) {
140 return 0;
141 }
142
143 struct GBARRBlock* block = gba->rr->playbackBlock;
144 size_t inputId = gba->rr->inputId;
145 uint16_t keys = 0;
146
147 keys = block->inputs[inputId].keys;
148 ++inputId;
149 if (inputId == GBA_RR_BLOCK_SIZE) {
150 inputId = 0;
151 gba->rr->playbackBlock = gba->rr->playbackBlock->next;
152 } else if (!gba->rr->playbackBlock->next && gba->rr->playbackBlock->numInputs == inputId) {
153 gba->rr->playbackBlock = 0;
154 }
155 gba->rr->inputId = inputId;
156 return keys;
157}
158
159void _allocBlock(struct GBARRContext* rr) {
160 struct GBARRBlock* block = calloc(1, sizeof(*rr->currentBlock));
161
162 if (!rr->currentBlock) {
163 rr->currentBlock = block;
164 rr->rootBlock = block;
165 return;
166 }
167
168 rr->currentBlock->next = block;
169 rr->currentBlock = block;
170}