rev |
line source |
paulo@0
|
1 /* Replay functionality for LOCKJAW, an implementation of the Soviet Mind Game
|
paulo@0
|
2
|
paulo@0
|
3 Copyright (C) 2006-2008 Damian Yerrick <tepples+lj@spamcop.net>
|
paulo@0
|
4
|
paulo@0
|
5 This work is free software; you can redistribute it and/or modify
|
paulo@0
|
6 it under the terms of the GNU General Public License as published by
|
paulo@0
|
7 the Free Software Foundation; either version 2 of the License, or
|
paulo@0
|
8 (at your option) any later version.
|
paulo@0
|
9
|
paulo@0
|
10 This program is distributed in the hope that it will be useful,
|
paulo@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
paulo@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
paulo@0
|
13 GNU General Public License for more details.
|
paulo@0
|
14
|
paulo@0
|
15 You should have received a copy of the GNU General Public License
|
paulo@0
|
16 along with this program; if not, write to the Free Software
|
paulo@0
|
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
paulo@0
|
18
|
paulo@0
|
19 Original game concept and design by Alexey Pajitnov.
|
paulo@0
|
20 The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg,
|
paulo@0
|
21 or The Tetris Company LLC.
|
paulo@0
|
22
|
paulo@0
|
23 */
|
paulo@0
|
24
|
paulo@0
|
25 #include "ljreplay.h"
|
paulo@0
|
26 #include <stdio.h>
|
paulo@0
|
27
|
paulo@0
|
28 #define FORMAT_VERSION 0x20080525
|
paulo@0
|
29 #define LAST_FORMAT_VERSION 20080420
|
paulo@0
|
30 #define DEBUG_OFFSETS 0
|
paulo@0
|
31
|
paulo@0
|
32 #if DEBUG_OFFSETS
|
paulo@0
|
33 #include <allegro.h>
|
paulo@0
|
34 #endif
|
paulo@0
|
35
|
paulo@0
|
36 struct LJReplayFrame {
|
paulo@0
|
37 char length;
|
paulo@0
|
38 char reserved;
|
paulo@0
|
39 unsigned short keys;
|
paulo@0
|
40 LJInput x;
|
paulo@0
|
41 };
|
paulo@0
|
42
|
paulo@0
|
43 struct LJReplay {
|
paulo@0
|
44 FILE *file;
|
paulo@0
|
45 };
|
paulo@0
|
46
|
paulo@0
|
47 static void fput32(unsigned int src, FILE *dst) {
|
paulo@0
|
48 fputc(src >> 24, dst);
|
paulo@0
|
49 fputc(src >> 16, dst);
|
paulo@0
|
50 fputc(src >> 8, dst);
|
paulo@0
|
51 fputc(src, dst);
|
paulo@0
|
52 }
|
paulo@0
|
53
|
paulo@0
|
54 static unsigned int fget32(FILE *src) {
|
paulo@0
|
55 int c0 = fgetc(src) & 0xFF;
|
paulo@0
|
56 c0 = (c0 << 8) | (fgetc(src) & 0xFF);
|
paulo@0
|
57 c0 = (c0 << 8) | (fgetc(src) & 0xFF);
|
paulo@0
|
58 c0 = (c0 << 8) | (fgetc(src) & 0xFF);
|
paulo@0
|
59 return c0;
|
paulo@0
|
60 }
|
paulo@0
|
61 static void fput16(unsigned int src, FILE *dst) {
|
paulo@0
|
62 fputc(src >> 8, dst);
|
paulo@0
|
63 fputc(src, dst);
|
paulo@0
|
64 }
|
paulo@0
|
65
|
paulo@0
|
66 static unsigned int fget16(FILE *src) {
|
paulo@0
|
67 int c0 = fgetc(src) & 0xFF;
|
paulo@0
|
68 c0 = (c0 << 8) | (fgetc(src) & 0xFF);
|
paulo@0
|
69 return c0;
|
paulo@0
|
70 }
|
paulo@0
|
71
|
paulo@0
|
72 static void LJField_serialize(const LJField *p, FILE *fp) {
|
paulo@0
|
73 fput32(FORMAT_VERSION, fp);
|
paulo@0
|
74
|
paulo@0
|
75 for (int y = 0; y < LJ_PF_HT; ++y) {
|
paulo@0
|
76 for (int x = 0; x < LJ_PF_WID; ++x) {
|
paulo@0
|
77 fputc(p->b[y][x], fp);
|
paulo@0
|
78 }
|
paulo@0
|
79 }
|
paulo@0
|
80 for (int y = 0; y < LJ_PF_HT; ++y) {
|
paulo@0
|
81 for (int x = 0; x < LJ_PF_WID; ++x) {
|
paulo@0
|
82 fputc(p->c[y][x], fp);
|
paulo@0
|
83 }
|
paulo@0
|
84 }
|
paulo@0
|
85 fput32(p->clearedLines, fp);
|
paulo@0
|
86 fput32(p->sounds, fp);
|
paulo@0
|
87 fput32(p->tempRows, fp);
|
paulo@0
|
88 for (int y = 0; y < 1 + LJ_NEXT_PIECES; ++y) {
|
paulo@0
|
89 fputc(p->curPiece[y], fp);
|
paulo@0
|
90 }
|
paulo@0
|
91 for (int y = 0; y < 2 * MAX_BAG_LEN; ++y) {
|
paulo@0
|
92 fputc(p->permuPiece[y], fp);
|
paulo@0
|
93 }
|
paulo@0
|
94 fputc(p->permuPhase, fp);
|
paulo@0
|
95 for (int y = 0; y < LJ_MAX_LINES_PER_PIECE; ++y) {
|
paulo@0
|
96 fput16(p->nLineClears[y], fp);
|
paulo@0
|
97 }
|
paulo@0
|
98 fput32(p->y, fp);
|
paulo@0
|
99 fputc(p->state, fp);
|
paulo@0
|
100 fputc(p->stateTime, fp);
|
paulo@0
|
101 fputc(p->theta, fp);
|
paulo@0
|
102 fputc(p->x, fp);
|
paulo@0
|
103 fputc(p->hardDropY, fp);
|
paulo@0
|
104 fputc(p->alreadyHeld, fp);
|
paulo@0
|
105 fputc(p->isSpin, fp);
|
paulo@0
|
106 fputc(p->nLinesThisPiece, fp);
|
paulo@0
|
107 fputc(p->canRotate, fp);
|
paulo@0
|
108
|
paulo@0
|
109 #if DEBUG_OFFSETS
|
paulo@0
|
110 allegro_message("before score, offset = %u\n", (unsigned int)ftell(fp));
|
paulo@0
|
111 #endif
|
paulo@0
|
112 fput32(p->score, fp);
|
paulo@0
|
113 fput32(p->lines, fp);
|
paulo@0
|
114 fput32(p->gameTime, fp);
|
paulo@0
|
115 fput32(p->activeTime, fp);
|
paulo@0
|
116 fput16(p->holdPiece, fp);
|
paulo@0
|
117 fputc(p->chain, fp);
|
paulo@0
|
118 fputc(p->garbage, fp);
|
paulo@0
|
119 fputc(p->garbageX, fp);
|
paulo@0
|
120 fput16(p->nPieces, fp);
|
paulo@0
|
121 fput16(p->outGarbage, fp);
|
paulo@0
|
122
|
paulo@0
|
123 fputc(p->gimmick, fp);
|
paulo@0
|
124 fput32(p->speedState.level, fp);
|
paulo@0
|
125 fput32(p->bpmCounter, fp);
|
paulo@0
|
126 fput32(p->speedupCounter, fp);
|
paulo@0
|
127 fput32(p->goalCount, fp);
|
paulo@0
|
128 fput32(p->seed, fp);
|
paulo@0
|
129 fput32(p->speed.gravity, fp);
|
paulo@0
|
130 fputc(p->speedState.curve, fp);
|
paulo@0
|
131 fputc(p->goalType, fp);
|
paulo@0
|
132 fputc(p->speed.entryDelay, fp);
|
paulo@0
|
133 fputc(p->areStyle, fp);
|
paulo@0
|
134 fputc(p->lockReset, fp);
|
paulo@0
|
135 fputc(p->speed.lockDelay, fp);
|
paulo@0
|
136 fputc(p->speed.lineDelay, fp);
|
paulo@0
|
137 fputc(p->ceiling, fp);
|
paulo@0
|
138 fputc(p->enterAbove, fp);
|
paulo@0
|
139 fputc(p->leftWall, fp);
|
paulo@0
|
140 fputc(p->rightWall, fp);
|
paulo@0
|
141 fputc(p->pieceSet, fp);
|
paulo@0
|
142 fputc(p->randomizer, fp);
|
paulo@0
|
143 fputc(p->rotationSystem, fp);
|
paulo@0
|
144 fputc(p->garbageRandomness, fp);
|
paulo@0
|
145 fputc(p->tSpinAlgo, fp);
|
paulo@0
|
146 fputc(p->clearGravity, fp);
|
paulo@0
|
147 fputc(p->gluing, fp);
|
paulo@0
|
148 fputc(p->scoreStyle, fp);
|
paulo@0
|
149 fputc(p->setLockDelay, fp);
|
paulo@0
|
150 fputc(p->upwardKicks, fp);
|
paulo@0
|
151 fputc(p->maxUpwardKicks, fp);
|
paulo@0
|
152 fputc(p->setLineDelay, fp);
|
paulo@0
|
153 fputc(p->garbageStyle, fp);
|
paulo@0
|
154 fputc(p->holdStyle, fp);
|
paulo@0
|
155 fputc(p->bottomBlocks, fp);
|
paulo@0
|
156
|
paulo@0
|
157 fput16(p->multisquares, fp);
|
paulo@0
|
158 fput16(p->monosquares, fp);
|
paulo@0
|
159
|
paulo@0
|
160 #if DEBUG_OFFSETS
|
paulo@0
|
161 allegro_message("final offset = %u\n", (unsigned int)ftell(fp));
|
paulo@0
|
162 #endif
|
paulo@0
|
163 }
|
paulo@0
|
164
|
paulo@0
|
165 static int LJField_deserialize(LJField *p, FILE *fp) {
|
paulo@0
|
166 int format = fget32(fp);
|
paulo@0
|
167 if (format != FORMAT_VERSION
|
paulo@0
|
168 && format != LAST_FORMAT_VERSION) {
|
paulo@0
|
169 return -1;
|
paulo@0
|
170 }
|
paulo@0
|
171
|
paulo@0
|
172 for (int y = 0; y < LJ_PF_HT; ++y) {
|
paulo@0
|
173 for (int x = 0; x < LJ_PF_WID; ++x) {
|
paulo@0
|
174 p->b[y][x] = fgetc(fp);
|
paulo@0
|
175 }
|
paulo@0
|
176 }
|
paulo@0
|
177 for (int y = 0; y < LJ_PF_HT; ++y) {
|
paulo@0
|
178 for (int x = 0; x < LJ_PF_WID; ++x) {
|
paulo@0
|
179 p->c[y][x] = fgetc(fp);
|
paulo@0
|
180 }
|
paulo@0
|
181 }
|
paulo@0
|
182 p->clearedLines = fget32(fp);
|
paulo@0
|
183 p->sounds = fget32(fp);
|
paulo@0
|
184 p->tempRows = fget32(fp);
|
paulo@0
|
185 for (int y = 0; y < 1 + LJ_NEXT_PIECES; ++y) {
|
paulo@0
|
186 p->curPiece[y] = fgetc(fp);
|
paulo@0
|
187 }
|
paulo@0
|
188 for (int y = 0; y < 2 * MAX_BAG_LEN; ++y) {
|
paulo@0
|
189 p->permuPiece[y] = fgetc(fp);
|
paulo@0
|
190 }
|
paulo@0
|
191 p->permuPhase = fgetc(fp);
|
paulo@0
|
192 for (int y = 0; y < LJ_MAX_LINES_PER_PIECE; ++y) {
|
paulo@0
|
193 p->nLineClears[y] = fget16(fp);
|
paulo@0
|
194 }
|
paulo@0
|
195 p->y = fget32(fp);
|
paulo@0
|
196 p->state = fgetc(fp);
|
paulo@0
|
197 p->stateTime = fgetc(fp);
|
paulo@0
|
198 p->theta = fgetc(fp);
|
paulo@0
|
199 p->x = fgetc(fp);
|
paulo@0
|
200 p->hardDropY = fgetc(fp);
|
paulo@0
|
201 p->alreadyHeld = fgetc(fp);
|
paulo@0
|
202 p->isSpin = fgetc(fp);
|
paulo@0
|
203 p->nLinesThisPiece = fgetc(fp);
|
paulo@0
|
204 p->canRotate = fgetc(fp);
|
paulo@0
|
205
|
paulo@0
|
206 #if DEBUG_OFFSETS
|
paulo@0
|
207 allegro_message("before score, offset = %u\n", (unsigned int)ftell(fp));
|
paulo@0
|
208 #endif
|
paulo@0
|
209 p->score = fget32(fp);
|
paulo@0
|
210 p->lines = fget32(fp);
|
paulo@0
|
211 p->gameTime = fget32(fp);
|
paulo@0
|
212 p->activeTime = fget32(fp);
|
paulo@0
|
213 p->holdPiece = fget16(fp);
|
paulo@0
|
214 p->chain = fgetc(fp);
|
paulo@0
|
215 p->garbage = fgetc(fp);
|
paulo@0
|
216 p->garbageX = fgetc(fp);
|
paulo@0
|
217 p->nPieces = fget16(fp);
|
paulo@0
|
218 p->outGarbage = fget16(fp);
|
paulo@0
|
219
|
paulo@0
|
220 p->gimmick = fgetc(fp);
|
paulo@0
|
221 p->speedState.level = fget32(fp);
|
paulo@0
|
222 p->bpmCounter = fget32(fp);
|
paulo@0
|
223 p->speedupCounter = fget32(fp);
|
paulo@0
|
224 p->goalCount = fget32(fp);
|
paulo@0
|
225 p->seed = fget32(fp);
|
paulo@0
|
226 p->speed.gravity = fget32(fp);
|
paulo@0
|
227 p->speedState.curve = fgetc(fp);
|
paulo@0
|
228 p->goalType = fgetc(fp);
|
paulo@0
|
229 p->speed.entryDelay = fgetc(fp);
|
paulo@0
|
230 p->areStyle = fgetc(fp);
|
paulo@0
|
231 p->lockReset = fgetc(fp);
|
paulo@0
|
232 p->speed.lockDelay = fgetc(fp);
|
paulo@0
|
233 p->speed.lineDelay = fgetc(fp);
|
paulo@0
|
234 p->ceiling = fgetc(fp);
|
paulo@0
|
235 p->enterAbove = fgetc(fp);
|
paulo@0
|
236 p->leftWall = fgetc(fp);
|
paulo@0
|
237 p->rightWall = fgetc(fp);
|
paulo@0
|
238 p->pieceSet = fgetc(fp);
|
paulo@0
|
239 p->randomizer = fgetc(fp);
|
paulo@0
|
240 p->rotationSystem = fgetc(fp);
|
paulo@0
|
241 p->garbageRandomness = fgetc(fp);
|
paulo@0
|
242 p->tSpinAlgo = fgetc(fp);
|
paulo@0
|
243 p->clearGravity = fgetc(fp);
|
paulo@0
|
244 p->gluing = fgetc(fp);
|
paulo@0
|
245 p->scoreStyle = fgetc(fp);
|
paulo@0
|
246 p->setLockDelay = fgetc(fp);
|
paulo@0
|
247 p->upwardKicks = fgetc(fp);
|
paulo@0
|
248 p->maxUpwardKicks = fgetc(fp);
|
paulo@0
|
249 p->setLineDelay = fgetc(fp);
|
paulo@0
|
250 p->garbageStyle = fgetc(fp);
|
paulo@0
|
251 p->holdStyle = fgetc(fp);
|
paulo@0
|
252 p->bottomBlocks = fgetc(fp);
|
paulo@0
|
253
|
paulo@0
|
254 if (format == FORMAT_VERSION) {
|
paulo@0
|
255 p->multisquares = fget16(fp);
|
paulo@0
|
256 p->monosquares = fget16(fp);
|
paulo@0
|
257 }
|
paulo@0
|
258 #if DEBUG_OFFSETS
|
paulo@0
|
259 allegro_message("final offset = %u\n", (unsigned int)ftell(fp));
|
paulo@0
|
260 #endif
|
paulo@0
|
261 return 0;
|
paulo@0
|
262 }
|
paulo@0
|
263
|
paulo@0
|
264
|
paulo@0
|
265 LJReplay *newReplay(const char *filename, LJField *p) {
|
paulo@0
|
266 LJReplay *r = malloc(sizeof(struct LJReplay));
|
paulo@0
|
267
|
paulo@0
|
268 if (!r) {
|
paulo@0
|
269 return NULL;
|
paulo@0
|
270 }
|
paulo@0
|
271 r->file = fopen(filename, "wb");
|
paulo@0
|
272 if (!r->file) {
|
paulo@0
|
273 free(r);
|
paulo@0
|
274 return NULL;
|
paulo@0
|
275 }
|
paulo@0
|
276 LJField_serialize(p, r->file);
|
paulo@0
|
277 return r;
|
paulo@0
|
278 }
|
paulo@0
|
279
|
paulo@0
|
280 void replayRecord(LJReplay *r, LJBits keys, const LJInput *in) {
|
paulo@0
|
281 fputc(0, r->file);
|
paulo@0
|
282 fputc(0, r->file);
|
paulo@0
|
283 fputc(keys >> 8, r->file);
|
paulo@0
|
284 fputc(keys, r->file);
|
paulo@0
|
285 fputc(in->rotation, r->file);
|
paulo@0
|
286 fputc(in->movement, r->file);
|
paulo@0
|
287 fputc(in->gravity, r->file);
|
paulo@0
|
288 fputc(in->other, r->file);
|
paulo@0
|
289 }
|
paulo@0
|
290
|
paulo@0
|
291 LJReplay *openReplay(const char *filename, LJField *p) {
|
paulo@0
|
292 LJReplay *r = malloc(sizeof(struct LJReplay));
|
paulo@0
|
293
|
paulo@0
|
294 if (!r) {
|
paulo@0
|
295 return NULL;
|
paulo@0
|
296 }
|
paulo@0
|
297 r->file = fopen(filename, "rb");
|
paulo@0
|
298 if (!r->file) {
|
paulo@0
|
299 free(r);
|
paulo@0
|
300 return NULL;
|
paulo@0
|
301 }
|
paulo@0
|
302
|
paulo@0
|
303 /* This deserialization is still NOT robust. */
|
paulo@0
|
304 if (LJField_deserialize(p, r->file) < 0) {
|
paulo@0
|
305 fclose(r->file);
|
paulo@0
|
306 free(r);
|
paulo@0
|
307 return 0;
|
paulo@0
|
308 }
|
paulo@0
|
309 return r;
|
paulo@0
|
310 }
|
paulo@0
|
311
|
paulo@0
|
312 int getReplayFrame(LJReplay *r, LJInput *d) {
|
paulo@0
|
313 fgetc(r->file);
|
paulo@0
|
314 fgetc(r->file);
|
paulo@0
|
315 int keys = fgetc(r->file);
|
paulo@0
|
316
|
paulo@0
|
317 if (keys == EOF) {
|
paulo@0
|
318 return LJREPLAY_EOF;
|
paulo@0
|
319 }
|
paulo@0
|
320 keys = (keys << 8 & 0xFF) | (fgetc(r->file) & 0xFF);
|
paulo@0
|
321 d->rotation = fgetc(r->file);
|
paulo@0
|
322 d->movement = fgetc(r->file);
|
paulo@0
|
323 d->gravity = fgetc(r->file);
|
paulo@0
|
324 d->other = fgetc(r->file);
|
paulo@0
|
325 return keys;
|
paulo@0
|
326 }
|
paulo@0
|
327
|
paulo@0
|
328 void replayClose(LJReplay *r) {
|
paulo@0
|
329 if (r) {
|
paulo@0
|
330 if (r->file) {
|
paulo@0
|
331 fclose(r->file);
|
paulo@0
|
332 }
|
paulo@0
|
333 free(r);
|
paulo@0
|
334 }
|
paulo@0
|
335 }
|