static short get_recording_queue_size( short which_queue) { short size; ActionQueue *queue= get_player_recording_queue(which_queue); /* Note that this is a circular queue */ size= queue->write_index-queue->read_index; if(size<0) size+= MAXIMUM_QUEUE_SIZE; return size; }
static void record_action_flags( short player_identifier, long *action_flags, short count) { short index; ActionQueue *queue; queue= get_player_recording_queue(player_identifier); assert(queue && queue->write_index >= 0 && queue->write_index < MAXIMUM_QUEUE_SIZE); for (index= 0; index<count; index++) { *(queue->buffer + queue->write_index) = *action_flags++; INCREMENT_QUEUE_COUNTER(queue->write_index); if (queue->write_index == queue->read_index) { dprintf("blew recording queue for player %d", player_identifier); } } }
/********************************************************************************************* * * Function: pull_flags_from_recording * Purpose: remove one flag from each queue from the recording buffer. * Returns: TRUE if it pulled the flags, FALSE if it didn't * *********************************************************************************************/ static boolean pull_flags_from_recording( short count) { short player_index; boolean success= TRUE; // first check that we can pull something from each playerÕs queue // (at the beginning of the game, we wonÕt be able to) // i'm not sure that i really need to do this check. oh well. for (player_index = 0; success && player_index<dynamic_world->player_count; player_index++) { if(get_recording_queue_size(player_index)==0) success= FALSE; } if(success) { for (player_index = 0; player_index < dynamic_world->player_count; player_index++) { short index; ActionQueue *queue; queue= get_player_recording_queue(player_index); for (index= 0; index<count; index++) { if (queue->read_index != queue->write_index) { #ifdef DEBUG_REPLAY debug_stream_of_flags(*(queue->buffer+queue->read_index), player_index); #endif queue_action_flags(player_index, queue->buffer+queue->read_index, 1); INCREMENT_QUEUE_COUNTER(queue->read_index); } else { dprintf("Dropping flag?"); } } } } return success; }
/* ---------- code */ void initialize_keyboard_controller( void) { ActionQueue *queue; short player_index; // vassert(NUMBER_OF_KEYS == NUMBER_OF_STANDARD_KEY_DEFINITIONS, // csprintf(temporary, "NUMBER_OF_KEYS == %d, NUMBER_OF_KEY_DEFS = %d. Not Equal!", NUMBER_OF_KEYS, NUMBER_OF_STANDARD_KEY_DEFINITIONS)); assert(NUMBER_OF_STANDARD_KEY_DEFINITIONS==NUMBER_OF_LEFT_HANDED_KEY_DEFINITIONS); assert(NUMBER_OF_LEFT_HANDED_KEY_DEFINITIONS==NUMBER_OF_POWERBOOK_KEY_DEFINITIONS); // get globals initialized heartbeat_count= 0; input_task_active= FALSE; memset(&replay, 0, sizeof(struct replay_private_data)); input_task= install_timer_task(TICKS_PER_SECOND, input_controller); assert(input_task); atexit(remove_input_controller); set_keys_to_match_preferences(); /* Allocate the recording queues */ replay.recording_queues = (ActionQueue *) malloc(MAXIMUM_NUMBER_OF_PLAYERS * sizeof(ActionQueue)); assert(replay.recording_queues); if(!replay.recording_queues) alert_user(fatalError, strERRORS, outOfMemory, memory_error()); /* Allocate the individual ones */ for (player_index= 0; player_index<MAXIMUM_NUMBER_OF_PLAYERS; player_index++) { queue= get_player_recording_queue(player_index); queue->read_index= queue->write_index = 0; queue->buffer= (long *) malloc(MAXIMUM_QUEUE_SIZE*sizeof(long)); if(!queue->buffer) alert_user(fatalError, strERRORS, outOfMemory, memory_error()); } enter_mouse(0); return; }
static void read_recording_queue_chunks( void) { long i, sizeof_read, action_flags; short count, player_index, num_flags; ActionQueue *queue; short error; for (player_index = 0; player_index < dynamic_world->player_count; player_index++) { queue= get_player_recording_queue(player_index); for (count = 0; count < RECORD_CHUNK_SIZE; ) { if (replay.resource_data) { boolean hit_end= FALSE; if (replay.film_resource_offset >= replay.resource_data_size) { hit_end = TRUE; } else { num_flags = * (short *) (replay.resource_data + replay.film_resource_offset); replay.film_resource_offset += sizeof(num_flags); action_flags = *(long *) (replay.resource_data + replay.film_resource_offset); replay.film_resource_offset+= sizeof(action_flags); } if (hit_end || num_flags == END_OF_RECORDING_INDICATOR) { replay.have_read_last_chunk= TRUE; break; } } else { sizeof_read = sizeof(num_flags); error= vblFSRead(replay.recording_file_refnum, &sizeof_read, &num_flags); if (!error) { sizeof_read = sizeof(action_flags); error= vblFSRead(replay.recording_file_refnum, &sizeof_read, &action_flags); assert(!error || (error == errHitFileEOF && sizeof_read == sizeof(action_flags))); } if ((error == errHitFileEOF && sizeof_read != sizeof(long)) || num_flags == END_OF_RECORDING_INDICATOR) { replay.have_read_last_chunk = TRUE; break; } } assert(replay.have_read_last_chunk || num_flags); count += num_flags; vassert((num_flags != 0 && count <= RECORD_CHUNK_SIZE) || replay.have_read_last_chunk, csprintf(temporary, "num_flags = %d, count = %d", num_flags, count)); for (i = 0; i < num_flags; i++) { *(queue->buffer + queue->write_index) = action_flags; INCREMENT_QUEUE_COUNTER(queue->write_index); assert(queue->read_index != queue->write_index); } } assert(replay.have_read_last_chunk || count == RECORD_CHUNK_SIZE); } return; }
/********************************************************************************************* * * Function: save_recording_queue_chunk * Purpose: saves one chunk of the queue to the recording file, using run-length encoding. * *********************************************************************************************/ void save_recording_queue_chunk( short player_index) { long *location; long last_flag, count, flag = 0; short i, run_count, num_flags_saved, max_flags; static long *buffer= NULL; ActionQueue *queue; if (buffer == NULL) { buffer = (long *)malloc((RECORD_CHUNK_SIZE * sizeof(long)) + RECORD_CHUNK_SIZE * sizeof(short)); } location= buffer; count= 0; // keeps track of how many bytes we'll save. last_flag= NONE; queue= get_player_recording_queue(player_index); // don't want to save too much stuff max_flags= MIN(RECORD_CHUNK_SIZE, get_recording_queue_size(player_index)); // save what's in the queue run_count= num_flags_saved= 0; for (i = 0; i<max_flags; i++) { flag = *(queue->buffer + queue->read_index); INCREMENT_QUEUE_COUNTER(queue->read_index); if (i && flag != last_flag) { *(short*)location = run_count; ((short*)location)++; *location++ = last_flag; count += sizeof(short) + sizeof(long); num_flags_saved += run_count; run_count = 1; } else { run_count++; } last_flag = flag; } // now save the final run *(short*)location = run_count; ((short*)location)++; *location++ = last_flag; count += sizeof(short) + sizeof(long); num_flags_saved += run_count; if (max_flags<RECORD_CHUNK_SIZE) { *(short*)location = END_OF_RECORDING_INDICATOR; ((short*)location)++; *location++ = 0; count += sizeof(short) + sizeof(long); num_flags_saved += RECORD_CHUNK_SIZE-max_flags; } write_file(replay.recording_file_refnum, count, buffer); replay.header.length+= count; vwarn(num_flags_saved == RECORD_CHUNK_SIZE, csprintf(temporary, "bad recording: %d flags, max=%d, count = %d;dm #%d #%d", num_flags_saved, max_flags, count, buffer, count)); }
static void read_recording_queue_chunks( void) { logContext("reading recording queue chunks"); int32 i, sizeof_read; uint32 action_flags; int16 count, player_index, num_flags; ActionQueue *queue; for (player_index = 0; player_index < dynamic_world->player_count; player_index++) { queue= get_player_recording_queue(player_index); for (count = 0; count < RECORD_CHUNK_SIZE; ) { if (replay.resource_data) { bool hit_end= false; if (replay.film_resource_offset >= replay.resource_data_size) { hit_end = true; } else { uint8* S; S = (uint8 *)(replay.resource_data + replay.film_resource_offset); StreamToValue(S,num_flags); replay.film_resource_offset += sizeof(num_flags); S = (uint8 *)(replay.resource_data + replay.film_resource_offset); StreamToValue(S,action_flags); replay.film_resource_offset+= sizeof(action_flags); } if (hit_end || num_flags == END_OF_RECORDING_INDICATOR) { replay.have_read_last_chunk= true; break; } } else { sizeof_read = sizeof(num_flags); uint8 NumFlagsBuffer[sizeof(num_flags)]; bool HitEOF = false; if (vblFSRead(FilmFile, &sizeof_read, NumFlagsBuffer, HitEOF)) { uint8 *S = NumFlagsBuffer; StreamToValue(S,num_flags); sizeof_read = sizeof(action_flags); uint8 ActionFlagsBuffer[sizeof(action_flags)]; bool status = vblFSRead(FilmFile, &sizeof_read, ActionFlagsBuffer, HitEOF); S = ActionFlagsBuffer; StreamToValue(S,action_flags); assert(status || (HitEOF && sizeof_read == sizeof(action_flags))); } if ((HitEOF && sizeof_read != sizeof(action_flags)) || num_flags == END_OF_RECORDING_INDICATOR) { replay.have_read_last_chunk = true; break; } } if (!(replay.have_read_last_chunk || num_flags)) { logAnomaly("chunk contains no flags"); } count += num_flags; for (i = 0; i < num_flags; i++) { *(queue->buffer + queue->write_index) = action_flags; INCREMENT_QUEUE_COUNTER(queue->write_index); assert(queue->read_index != queue->write_index); } } assert(replay.have_read_last_chunk || count == RECORD_CHUNK_SIZE); } }
/********************************************************************************************* * * Function: save_recording_queue_chunk * Purpose: saves one chunk of the queue to the recording file, using run-length encoding. * *********************************************************************************************/ void save_recording_queue_chunk( short player_index) { uint8 *location; uint32 last_flag, count, flag = 0; int16 i, run_count, num_flags_saved, max_flags; static uint8 *buffer= NULL; ActionQueue *queue; // The data format is (run length (int16)) + (action flag (uint32)) int DataSize = sizeof(int16) + sizeof(uint32); if (buffer == NULL) buffer = new byte[RECORD_CHUNK_SIZE * DataSize]; location= buffer; count= 0; // keeps track of how many bytes we'll save. last_flag= (uint32)NONE; queue= get_player_recording_queue(player_index); // don't want to save too much stuff max_flags= MIN(RECORD_CHUNK_SIZE, get_recording_queue_size(player_index)); // save what's in the queue run_count= num_flags_saved= 0; for (i = 0; i<max_flags; i++) { flag = queue->buffer[queue->read_index]; INCREMENT_QUEUE_COUNTER(queue->read_index); if (i && flag != last_flag) { ValueToStream(location,run_count); ValueToStream(location,last_flag); count += DataSize; num_flags_saved += run_count; run_count = 1; } else { run_count++; } last_flag = flag; } // now save the final run ValueToStream(location,run_count); ValueToStream(location,last_flag); count += DataSize; num_flags_saved += run_count; if (max_flags<RECORD_CHUNK_SIZE) { short end_indicator = END_OF_RECORDING_INDICATOR; ValueToStream(location,end_indicator); int32 end_flag = 0; ValueToStream(location,end_flag); count += DataSize; num_flags_saved += RECORD_CHUNK_SIZE-max_flags; } FilmFile.Write(count,buffer); replay.header.length+= count; vwarn(num_flags_saved == RECORD_CHUNK_SIZE, csprintf(temporary, "bad recording: %d flags, max=%d, count = %u;dm #%p #%u", num_flags_saved, max_flags, count, buffer, count)); }