replay.c (3085B)
1 #include "game/replay.h" 2 #include <string.h> 3 #include "config.h" 4 5 int 6 ph_replay_header(FILE *file, unsigned int *seed, int writing) 7 { 8 unsigned char header[16] = { 'P', 'H', 'R', 'E', 'P', 'L', 'A', 'Y', 1, 0 }; 9 unsigned int value = *seed; 10 int i; 11 12 header[10] = PH_SIMULATION_HZ & 255; 13 header[11] = (PH_SIMULATION_HZ >> 8) & 255; 14 if (writing) { 15 for (i = 0; i < 4; ++i) header[12 + i] = (value >> (8 * i)) & 255; 16 return fwrite(header, 1, sizeof(header), file) == sizeof(header) ? 0 : -1; 17 } 18 { 19 unsigned char actual[16]; 20 21 if (fread(actual, 1, sizeof(actual), file) != sizeof(actual) || 22 memcmp(actual, header, 12)) return -1; 23 value = 0; 24 for (i = 0; i < 4; ++i) value |= (unsigned int)actual[12 + i] << (8 * i); 25 *seed = value; 26 } 27 return 0; 28 } 29 30 int 31 ph_replay_write(FILE *file, int kind, PhAction action, PhInput input) 32 { 33 unsigned char record[4] = { (unsigned char)kind }; 34 size_t size = 1; 35 36 if (kind == PH_REPLAY_ACTION) { 37 if (action <= PH_ACTION_NONE || action > PH_ACTION_SPELLBOOK) return -1; 38 record[1] = action; 39 size = 2; 40 } else if (kind == PH_REPLAY_TICK) { 41 if (input.move_x < -1 || input.move_x > 1 || input.move_y < -1 || 42 input.move_y > 1 || input.interact < 0 || input.interact > 1) return -1; 43 record[1] = input.move_x + 1; 44 record[2] = input.move_y + 1; 45 record[3] = input.interact; 46 size = 4; 47 } else if (kind != PH_REPLAY_END) return -1; 48 return fwrite(record, 1, size, file) == size ? 0 : -1; 49 } 50 51 int 52 ph_replay_read(FILE *file, PhAction *action, PhInput *input) 53 { 54 int kind = fgetc(file), x, y, interact; 55 56 *action = PH_ACTION_NONE; 57 *input = (PhInput){ 0 }; 58 if (kind == PH_REPLAY_END) 59 return fgetc(file) == EOF && !ferror(file) ? PH_REPLAY_END : -1; 60 if (kind == PH_REPLAY_ACTION) { 61 x = fgetc(file); 62 if (x <= PH_ACTION_NONE || x > PH_ACTION_SPELLBOOK) return -1; 63 *action = (PhAction)x; 64 } else if (kind == PH_REPLAY_TICK) { 65 x = fgetc(file); 66 y = fgetc(file); 67 interact = fgetc(file); 68 if (x < 0 || x > 2 || y < 0 || y > 2 || interact < 0 || interact > 1) return -1; 69 *input = (PhInput){ .move_x = x - 1, .move_y = y - 1, .interact = interact }; 70 } else return -1; 71 return kind; 72 } 73 74 int 75 ph_replay_run(FILE *file, PhGameContext *context, uint64_t *ticks) 76 { 77 PhAction action; 78 PhInput input; 79 int kind, exited = 0; 80 81 *ticks = 0; 82 while ((kind = ph_replay_read(file, &action, &input)) > 0) { 83 PhWorldEvent event; 84 85 if (exited) return -1; 86 if (kind == PH_REPLAY_ACTION) { 87 int request = ph_game_input(context, action); 88 89 if (request & PH_GAME_REQUEST_NEW) return -1; 90 exited = request & PH_GAME_REQUEST_EXIT; 91 } else { 92 if (context->menu != PH_MENU_NONE || *context->mode == PH_GAME_VICTORY || 93 *context->mode == PH_GAME_OVER) return -1; 94 ph_game_update(context, input, 1.0f / PH_SIMULATION_HZ, NULL); 95 ph_game_interaction(context); 96 ++*ticks; 97 } 98 /* Match the live host's consumption of one-shot presentation events. */ 99 ph_battle_take_event(context->battle); 100 context->spellbook->spell_used = 0; 101 while (ph_world_take_event(context->world, &event)) { } 102 } 103 return kind == PH_REPLAY_END ? 0 : -1; 104 }