A start on movie rewind.

This commit is contained in:
Themaister 2011-02-25 11:47:27 +01:00
parent b07827a856
commit e8865060ca
3 changed files with 64 additions and 12 deletions

45
movie.c
View File

@ -87,6 +87,14 @@ struct bsv_movie
{
FILE *file;
uint8_t *state;
uint16_t *frame_state; // A ring buffer keeping track of many key presses were requested per frame.
size_t frame_mask;
uint16_t current_frame_count;
size_t frame_ptr;
bool playback;
unsigned min_file_pos;
};
#define BSV_MAGIC 0x42535631
@ -127,6 +135,7 @@ static inline uint16_t swap_if_big16(uint16_t val)
static bool init_playback(bsv_movie_t *handle, const char *path)
{
handle->playback = true;
handle->file = fopen(path, "rb");
if (!handle->file)
{
@ -158,6 +167,8 @@ static bool init_playback(bsv_movie_t *handle, const char *path)
if (!handle->state)
return false;
handle->min_file_pos = sizeof(header) + state_size;
if (fread(handle->state, 1, state_size, handle->file) != state_size)
{
SSNES_ERR("Couldn't read state from movie.\n");
@ -196,6 +207,8 @@ static bool init_record(bsv_movie_t *handle, const char *path)
if (!handle->state)
return false;
handle->min_file_pos = sizeof(header) + state_size;
psnes_serialize(handle->state, state_size);
fwrite(handle->state, 1, state_size, handle->file);
return true;
@ -208,6 +221,7 @@ void bsv_movie_free(bsv_movie_t *handle)
if (handle->file)
fclose(handle->file);
free(handle->state);
free(handle->frame_state);
free(handle);
}
}
@ -218,12 +232,14 @@ bool bsv_movie_get_input(bsv_movie_t *handle, int16_t *input)
return false;
*input = swap_if_big16(*input);
handle->current_frame_count++;
return true;
}
void bsv_movie_set_input(bsv_movie_t *handle, int16_t input)
{
fwrite(&input, sizeof(int16_t), 1, handle->file);
handle->current_frame_count++;
}
bsv_movie_t *bsv_movie_init(const char *path, enum ssnes_movie_type type)
@ -240,9 +256,38 @@ bsv_movie_t *bsv_movie_init(const char *path, enum ssnes_movie_type type)
else if (!init_record(handle, path))
goto error;
if (!(handle->frame_state = malloc((1 << 20) * sizeof(uint16_t)))) // Just pick something really large :D
goto error;
handle->frame_mask = (1 << 20) - 1;
return handle;
error:
bsv_movie_free(handle);
return NULL;
}
void bsv_movie_set_frame_end(bsv_movie_t *handle)
{
handle->frame_state[handle->frame_ptr] = handle->current_frame_count;
handle->current_frame_count = 0;
handle->frame_ptr = (handle->frame_ptr + 1) & handle->frame_mask;
}
void bsv_movie_frame_rewind(bsv_movie_t *handle)
{
if (ftell(handle->file) <= (long)handle->min_file_pos)
{
if (!handle->playback)
{
fseek(handle->file, 4 * sizeof(uint32_t), SEEK_SET);
psnes_serialize(handle->state, handle->min_file_pos - 4 * sizeof(uint32_t));
fwrite(handle->state, 1, handle->min_file_pos - 4 * sizeof(uint32_t), handle->file);
}
}
else
{
handle->frame_ptr = (handle->frame_ptr - 1) & handle->frame_mask;
fseek(handle->file, -((long)handle->frame_state[handle->frame_ptr] * sizeof(int16_t)), SEEK_CUR);
}
}

View File

@ -37,12 +37,13 @@ bool bsv_movie_get_input(bsv_movie_t *handle, int16_t *input);
// Recording
void bsv_movie_set_input(bsv_movie_t *handle, int16_t input);
// Used for rewinding while playback/record.
void bsv_movie_set_frame_end(bsv_movie_t *handle);
void bsv_movie_frame_rewind(bsv_movie_t *handle);
void bsv_movie_free(bsv_movie_t *handle);
uint32_t crc32_calculate(const uint8_t *data, unsigned length);
#endif

24
ssnes.c
View File

@ -241,7 +241,7 @@ static void input_poll(void)
static int16_t input_state(bool port, unsigned device, unsigned index, unsigned id)
{
if (g_extern.bsv_movie && g_extern.bsv_movie_playback)
if (g_extern.bsv_movie && g_extern.bsv_movie_playback && !g_extern.frame_is_reverse)
{
int16_t ret;
if (bsv_movie_get_input(g_extern.bsv_movie, &ret))
@ -258,7 +258,7 @@ static int16_t input_state(bool port, unsigned device, unsigned index, unsigned
binds[i] = g_settings.input.binds[i];
int16_t res = driver.input->input_state(driver.input_data, binds, port, device, index, id);
if (g_extern.bsv_movie && !g_extern.bsv_movie_playback)
if (g_extern.bsv_movie && !g_extern.bsv_movie_playback && !g_extern.frame_is_reverse)
bsv_movie_set_input(g_extern.bsv_movie, res);
return res;
@ -1042,6 +1042,9 @@ static void check_rewind(void)
if (driver.input->key_pressed(driver.input_data, SSNES_REWIND))
{
if (g_extern.bsv_movie)
bsv_movie_frame_rewind(g_extern.bsv_movie);
msg_queue_clear(g_extern.msg_queue);
void *buf;
if (state_manager_pop(g_extern.state_manager, &buf))
@ -1174,8 +1177,8 @@ static void do_state_checks(void)
{
check_stateslots();
check_savestates();
check_rewind();
}
check_rewind();
if (!g_extern.bsv_movie_playback)
check_movie_record();
@ -1207,14 +1210,15 @@ int main(int argc, char *argv[])
init_movie();
if (!g_extern.bsv_movie)
{
load_save_files();
init_rewind();
}
init_netplay();
init_drivers();
if (!g_extern.netplay)
init_rewind();
psnes_set_video_refresh(g_extern.netplay ? video_frame_net : video_frame);
psnes_set_audio_sample(g_extern.netplay ? audio_sample_net : audio_sample);
psnes_set_input_poll(g_extern.netplay ? input_poll_net : input_poll);
@ -1250,6 +1254,8 @@ int main(int argc, char *argv[])
psnes_run();
if (g_extern.bsv_movie)
bsv_movie_set_frame_end(g_extern.bsv_movie);
if (g_extern.netplay)
netplay_post_frame(g_extern.netplay);
@ -1280,10 +1286,10 @@ int main(int argc, char *argv[])
#endif
if (!g_extern.bsv_movie_playback && !g_extern.netplay_is_client)
{
deinit_rewind();
save_files();
}
if (!g_extern.netplay)
deinit_rewind();
deinit_movie();
deinit_msg_queue();