Mercurial > hg > index.fcgi > lj > lj046-2players
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 } |