annotate src/ljreplay.c @ 0:c84446dfb3f5

initial add
author paulo@localhost
date Fri, 13 Mar 2009 00:39:12 -0700
parents
children
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 }