diff options
Diffstat (limited to 'src/engine/e_demorec.c')
| -rw-r--r-- | src/engine/e_demorec.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/src/engine/e_demorec.c b/src/engine/e_demorec.c new file mode 100644 index 00000000..67f7f237 --- /dev/null +++ b/src/engine/e_demorec.c @@ -0,0 +1,354 @@ + +#include <base/system.h> +#include "e_demorec.h" +#include "e_memheap.h" +#include "e_if_other.h" + +static IOHANDLE record_file = 0; +static const unsigned char header_marker[8] = {'T', 'W', 'D', 'E', 'M', 'O', 0, 1}; + +/* Record */ + +int demorec_isrecording() { return record_file != 0; } + +int demorec_record_start(const char *filename, const char *netversion, const char *map, int crc, const char *type) +{ + DEMOREC_HEADER header; + if(record_file) + return -1; + + record_file = io_open(filename, IOFLAG_WRITE); + + if(!record_file) + { + dbg_msg("demorec/record", "Unable to open '%s' for recording", filename); + return -1; + } + + /* write header */ + mem_zero(&header, sizeof(header)); + mem_copy(header.marker, header_marker, sizeof(header.marker)); + str_copy(header.netversion, netversion, sizeof(header.netversion)); + str_copy(header.map, map, sizeof(header.map)); + str_copy(header.type, type, sizeof(header.type)); + header.crc[0] = (crc>>24)&0xff; + header.crc[1] = (crc>>16)&0xff; + header.crc[2] = (crc>>8)&0xff; + header.crc[3] = (crc)&0xff; + io_write(record_file, &header, sizeof(header)); + + dbg_msg("demorec/record", "Recording to '%s'", filename); + return 0; +} + +void demorec_record_write(const char *type, int size, const void *data) +{ + DEMOREC_CHUNK chunk; + if(!record_file) + return; + + chunk.type[0] = type[0]; + chunk.type[1] = type[1]; + chunk.type[2] = type[2]; + chunk.type[3] = type[3]; + chunk.size = size; + swap_endian(&chunk.size, sizeof(int), 1); + io_write(record_file, &chunk, sizeof(chunk)); + io_write(record_file, data, size); +} + +int demorec_record_stop() +{ + if(!record_file) + return -1; + + dbg_msg("demorec/record", "Stopped recording"); + io_close(record_file); + record_file = 0; + return 0; +} + +/* Playback */ + +typedef struct KEYFRAME +{ + long filepos; + int tick; +} KEYFRAME; + +typedef struct KEYFRAME_SEARCH +{ + KEYFRAME frame; + struct KEYFRAME_SEARCH *next; +} KEYFRAME_SEARCH; + +static IOHANDLE play_file = 0; +static DEMOREC_PLAYCALLBACK play_callback = 0; +static KEYFRAME *keyframes = 0; + +static DEMOREC_PLAYBACKINFO playbackinfo; +const DEMOREC_PLAYBACKINFO *demorec_playback_info() { return &playbackinfo; } + +int demorec_isplaying() { return play_file != 0; } + +int demorec_playback_registercallback(DEMOREC_PLAYCALLBACK cb) +{ + play_callback = cb; + return 0; +} + +static int read_chunk_header(DEMOREC_CHUNK *chunk) +{ + if(io_read(play_file, chunk, sizeof(*chunk)) != sizeof(*chunk)) + return -1; + swap_endian(&chunk->size, sizeof(int), 1); + return 0; +} + +static void scan_file() +{ + long start_pos; + HEAP *heap = 0; + KEYFRAME_SEARCH *first_key = 0; + KEYFRAME_SEARCH *current_key = 0; + DEMOREC_CHUNK chunk; + int i; + + heap = memheap_create(); + + start_pos = io_tell(play_file); + playbackinfo.seekable_points = 0; + + while(1) + { + long current_pos = io_tell(play_file); + + if(read_chunk_header(&chunk)) + break; + + /* read the chunk */ + + if(mem_comp(chunk.type, "TICK", 4) == 0) + { + KEYFRAME_SEARCH *key; + DEMOREC_TICKMARKER marker; + io_read(play_file, &marker, chunk.size); + swap_endian(&marker.tick, sizeof(int), 1); + + /* save the position */ + key = memheap_allocate(heap, sizeof(KEYFRAME_SEARCH)); + key->frame.filepos = current_pos; + key->frame.tick = marker.tick; + key->next = 0; + if(current_key) + current_key->next = key; + if(!first_key) + first_key = key; + current_key = key; + + if(playbackinfo.first_tick == -1) + playbackinfo.first_tick = marker.tick; + playbackinfo.last_tick = marker.tick; + playbackinfo.seekable_points++; + } + else + io_skip(play_file, chunk.size); + } + + /* copy all the frames to an array instead for fast access */ + keyframes = (KEYFRAME*)mem_alloc(playbackinfo.seekable_points*sizeof(KEYFRAME), 1); + for(current_key = first_key, i = 0; current_key; current_key = current_key->next, i++) + keyframes[i] = current_key->frame; + + /* destroy the temporary heap and seek back to the start */ + memheap_destroy(heap); + io_seek(play_file, start_pos, IOSEEK_START); +} + +static void do_tick() +{ + static char buffer[64*1024]; + DEMOREC_CHUNK chunk; + + /* update ticks */ + playbackinfo.previous_tick = playbackinfo.current_tick; + playbackinfo.current_tick = playbackinfo.next_tick; + + while(1) + { + int r = read_chunk_header(&chunk); + if(chunk.size > sizeof(buffer)) + { + dbg_msg("demorec/playback", "chunk is too big %d", chunk.size); + r = 1; + } + + if(r) + { + /* stop on error or eof */ + demorec_playback_stop(); + break; + } + + /* read the chunk */ + io_read(play_file, buffer, chunk.size); + + if(mem_comp(chunk.type, "TICK", 4) == 0) + { + DEMOREC_TICKMARKER marker = *(DEMOREC_TICKMARKER *)buffer; + swap_endian(&marker.tick, sizeof(int), 1); + playbackinfo.next_tick = marker.tick; + break; + } + else if(play_callback) + play_callback(chunk, buffer); + } +} + +void demorec_playback_pause() +{ + playbackinfo.paused = 1; +} + +void demorec_playback_unpause() +{ + if(playbackinfo.paused) + { + /*playbackinfo.start_tick = playbackinfo.current_tick; + playbackinfo.start_time = time_get();*/ + playbackinfo.paused = 0; + } +} + +int demorec_playback_load(const char *filename) +{ + play_file = io_open(filename, IOFLAG_READ); + if(!play_file) + { + dbg_msg("demorec/playback", "could not open '%s'", filename); + return -1; + } + + /* clear the playback info */ + mem_zero(&playbackinfo, sizeof(playbackinfo)); + playbackinfo.first_tick = -1; + playbackinfo.last_tick = -1; + /*playbackinfo.start_tick = -1;*/ + playbackinfo.next_tick = -1; + playbackinfo.current_tick = -1; + playbackinfo.previous_tick = -1; + playbackinfo.speed = 1; + + /* read the header */ + io_read(play_file, &playbackinfo.header, sizeof(playbackinfo.header)); + if(mem_comp(playbackinfo.header.marker, header_marker, sizeof(header_marker)) != 0) + { + dbg_msg("demorec/playback", "'%s' is not a demo file", filename); + io_close(play_file); + play_file = 0; + return -1; + } + + /* scan the file for interessting points */ + scan_file(); + + /* ready for playback */ + return 0; +} + +int demorec_playback_play() +{ + /* fill in previous and next tick */ + while(playbackinfo.previous_tick == -1) + do_tick(); + + /* set start info */ + /*playbackinfo.start_tick = playbackinfo.previous_tick; + playbackinfo.start_time = time_get();*/ + playbackinfo.current_time = playbackinfo.previous_tick*time_freq()/SERVER_TICK_SPEED; + playbackinfo.last_update = time_get(); + return 0; +} + +int demorec_playback_set(int keyframe) +{ + if(!play_file) + return -1; + if(keyframe < 0 || keyframe >= playbackinfo.seekable_points) + return -1; + + io_seek(play_file, keyframes[keyframe].filepos, IOSEEK_START); + + /*playbackinfo.start_tick = -1;*/ + playbackinfo.next_tick = -1; + playbackinfo.current_tick = -1; + playbackinfo.previous_tick = -1; + + demorec_playback_play(); + + return 0; +} + +void demorec_playback_setspeed(float speed) +{ + playbackinfo.speed = speed; +} + +int demorec_playback_update() +{ + int64 now = time_get(); + int64 deltatime = now-playbackinfo.last_update; + playbackinfo.last_update = now; + + if(playbackinfo.paused) + { + + } + else + { + int64 freq = time_freq(); + playbackinfo.current_time += (int64)(deltatime*(double)playbackinfo.speed); + + while(1) + { + int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED; + + /* break if we are ready */ + if(curtick_start > playbackinfo.current_time) + break; + + /* do one more tick */ + do_tick(); + } + + /* update intratick */ + { + int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED; + int64 prevtick_start = (playbackinfo.previous_tick)*freq/SERVER_TICK_SPEED; + playbackinfo.intratick = (playbackinfo.current_time - prevtick_start) / (float)(curtick_start-prevtick_start); + playbackinfo.ticktime = (playbackinfo.current_time - prevtick_start) / (float)freq; + } + + if(playbackinfo.current_tick == playbackinfo.previous_tick || + playbackinfo.current_tick == playbackinfo.next_tick) + { + dbg_msg("demorec/playback", "tick error prev=%d cur=%d next=%d", + playbackinfo.previous_tick, playbackinfo.current_tick, playbackinfo.next_tick); + } + } + + return 0; +} + +int demorec_playback_stop(const char *filename) +{ + if(!play_file) + return -1; + + dbg_msg("demorec/playback", "Stopped playback"); + io_close(play_file); + play_file = 0; + mem_free(keyframes); + keyframes = 0; + return 0; +} |