comparison src/ljreplay.c @ 0:c84446dfb3f5

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