add keyboard recording support to bsv (#14933)

* add keyboard recording support to bsv

BSV movies recorded in older RA *WILL NOT* replay properly after this
patch.  While looking to see if the core actually uses a keyboard
device could mitigate this, it is an unavoidable consequence of using
BSV, a format which carries no metadata whatsoever.

* Fix for loop declarations and some whitespace

---------

Co-authored-by: Joseph C. Osborn <jcoa2018@pomona.edu>
This commit is contained in:
Joe Osborn 2023-01-31 15:22:00 -08:00 committed by GitHub
parent c27e73e736
commit 155e99b92c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 192 additions and 75 deletions

View File

@ -4692,6 +4692,144 @@ static int16_t input_state_device(
return res;
}
#ifdef HAVE_BSV_MOVIE
void bsv_movie_handle_clear_key_events(bsv_movie_t*);
void bsv_movie_free(bsv_movie_t*);
void bsv_movie_deinit(input_driver_state_t *input_st)
{
if (input_st->bsv_movie_state_handle)
bsv_movie_free(input_st->bsv_movie_state_handle);
input_st->bsv_movie_state_handle = NULL;
}
void bsv_movie_frame_rewind(void)
{
input_driver_state_t *input_st = &input_driver_st;
bsv_movie_t *handle = input_st->bsv_movie_state_handle;
if (!handle)
return;
handle->did_rewind = true;
if ( (handle->frame_ptr <= 1)
&& (handle->frame_pos[0] == handle->min_file_pos))
{
/* If we're at the beginning... */
handle->frame_ptr = 0;
intfstream_seek(handle->file, (int)handle->min_file_pos, SEEK_SET);
}
else
{
/* First time rewind is performed, the old frame is simply replayed.
* However, playing back that frame caused us to read data, and push
* data to the ring buffer.
*
* Sucessively rewinding frames, we need to rewind past the read data,
* plus another. */
handle->frame_ptr = (handle->frame_ptr -
(handle->first_rewind ? 1 : 2)) & handle->frame_mask;
intfstream_seek(handle->file,
(int)handle->frame_pos[handle->frame_ptr], SEEK_SET);
}
if (intfstream_tell(handle->file) <= (long)handle->min_file_pos)
{
/* We rewound past the beginning. */
if (!handle->playback)
{
retro_ctx_serialize_info_t serial_info;
/* If recording, we simply reset
* the starting point. Nice and easy. */
intfstream_seek(handle->file, 4 * sizeof(uint32_t), SEEK_SET);
serial_info.data = handle->state;
serial_info.size = handle->state_size;
core_serialize(&serial_info);
intfstream_write(handle->file, handle->state, handle->state_size);
}
else
intfstream_seek(handle->file, (int)handle->min_file_pos, SEEK_SET);
}
}
void bsv_movie_handle_clear_key_events(bsv_movie_t *movie)
{
/* zero out key events when playing back or recording */
movie->key_event_count=0;
}
void bsv_movie_handle_push_key_event(bsv_movie_t *movie, uint8_t down, uint16_t mod, uint32_t code, uint32_t character)
{
bsv_key_data_t data;
data.down = down;
data.mod = swap_if_big16(mod);
data.code = swap_if_big32(code);
data.character = swap_if_big32(character);
movie->key_events[movie->key_event_count] = data;
movie->key_event_count++;
}
void bsv_movie_finish_rewind(input_driver_state_t *input_st)
{
bsv_movie_t *handle = input_st->bsv_movie_state_handle;
if(!handle)
return;
handle->frame_ptr = (handle->frame_ptr + 1) & handle->frame_mask;
handle->first_rewind = !handle->did_rewind;
handle->did_rewind = false;
}
void bsv_movie_next_frame(input_driver_state_t *input_st)
{
/* Used for rewinding while playback/record. */
bsv_movie_t *handle = input_st->bsv_movie_state_handle;
if (!handle)
return;
handle->frame_pos[handle->frame_ptr] = intfstream_tell(handle->file);
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_RECORDING)
{
int i;
/* write key events, frame is over */
intfstream_write(handle->file, &(handle->key_event_count), 1);
for(i = 0; i < handle->key_event_count; i++)
{
intfstream_write(handle->file, &(handle->key_events[i]), sizeof(bsv_key_data_t));
}
bsv_movie_handle_clear_key_events(handle);
}
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_PLAYBACK)
{
/* read next key events, a frame happened for sure? but don't apply them yet */
if(handle->key_event_count != 0)
{
RARCH_ERR("[Movie] BSV keyboard replay reading next frame while some unused keys still in queue\n");
}
if (intfstream_read(handle->file, &(handle->key_event_count), 1) == 1)
{
int i;
for(i = 0; i < handle->key_event_count; i++)
{
if (intfstream_read(handle->file, &(handle->key_events[i]), sizeof(bsv_key_data_t)) != sizeof(bsv_key_data_t))
{
/* Unnatural EOF */
RARCH_ERR("[Movie] BSV keyboard replay ran out of keyboard inputs too early\n");
}
}
}
else
{
RARCH_LOG("[Movie] EOF after buttons\n",handle->key_event_count);
/* Natural(?) EOF */
input_st->bsv_movie_state.flags |= BSV_FLAG_MOVIE_END;
}
}
}
#endif
void input_driver_poll(void)
{
size_t i, j;
@ -5145,74 +5283,30 @@ void input_driver_poll(void)
}
}
#endif
}
#ifdef HAVE_BSV_MOVIE
void bsv_movie_free(bsv_movie_t*);
void bsv_movie_deinit(input_driver_state_t *input_st)
{
if (input_st->bsv_movie_state_handle)
bsv_movie_free(input_st->bsv_movie_state_handle);
input_st->bsv_movie_state_handle = NULL;
}
void bsv_movie_frame_rewind(void)
{
input_driver_state_t *input_st = &input_driver_st;
bsv_movie_t *handle = input_st->bsv_movie_state_handle;
if (!handle)
return;
handle->did_rewind = true;
if ( (handle->frame_ptr <= 1)
&& (handle->frame_pos[0] == handle->min_file_pos))
if (BSV_MOVIE_IS_PLAYBACK_ON())
{
/* If we're at the beginning... */
handle->frame_ptr = 0;
intfstream_seek(handle->file, (int)handle->min_file_pos, SEEK_SET);
}
else
{
/* First time rewind is performed, the old frame is simply replayed.
* However, playing back that frame caused us to read data, and push
* data to the ring buffer.
*
* Sucessively rewinding frames, we need to rewind past the read data,
* plus another. */
handle->frame_ptr = (handle->frame_ptr -
(handle->first_rewind ? 1 : 2)) & handle->frame_mask;
intfstream_seek(handle->file,
(int)handle->frame_pos[handle->frame_ptr], SEEK_SET);
}
runloop_state_t *runloop_st = runloop_state_get_ptr();
retro_keyboard_event_t *key_event = &runloop_st->key_event;
if (intfstream_tell(handle->file) <= (long)handle->min_file_pos)
{
/* We rewound past the beginning. */
if (!handle->playback)
if(*key_event && *key_event == runloop_st->frontend_key_event)
{
retro_ctx_serialize_info_t serial_info;
/* If recording, we simply reset
* the starting point. Nice and easy. */
intfstream_seek(handle->file, 4 * sizeof(uint32_t), SEEK_SET);
serial_info.data = handle->state;
serial_info.size = handle->state_size;
core_serialize(&serial_info);
intfstream_write(handle->file, handle->state, handle->state_size);
}
else
intfstream_seek(handle->file, (int)handle->min_file_pos, SEEK_SET);
}
}
int i;
bsv_key_data_t k;
for(i = 0; i < input_st->bsv_movie_state_handle->key_event_count; i++)
{
#ifdef HAVE_CHEEVOS
rcheevos_pause_hardcore();
#endif
k = input_st->bsv_movie_state_handle->key_events[i];
input_keyboard_event(k.down, swap_if_big32(k.code), swap_if_big32(k.character), swap_if_big16(k.mod), RETRO_DEVICE_KEYBOARD);
}
/* Have to clear here so we don't double-apply key events */
bsv_movie_handle_clear_key_events(input_st->bsv_movie_state_handle);
}
}
#endif
}
int16_t input_state_internal(unsigned port, unsigned device,
unsigned idx, unsigned id)
@ -6206,6 +6300,18 @@ void input_keyboard_event(bool down, unsigned code,
}
if (*key_event)
{
if(*key_event == runloop_st->frontend_key_event)
{
#ifdef HAVE_BSV_MOVIE
/* Save input to BSV record, if recording */
if (BSV_MOVIE_IS_RECORDING())
{
bsv_movie_handle_push_key_event(input_st->bsv_movie_state_handle, down, mod, code, character);
}
#endif
}
(*key_event)(down, code, character, mod);
}
}
}

View File

@ -128,6 +128,17 @@ struct bsv_state
char movie_start_path[PATH_MAX_LENGTH];
};
/* These data are always little-endian. */
struct bsv_key_data {
uint8_t down;
uint16_t mod;
uint8_t _padding;
uint32_t code;
uint32_t character;
};
typedef struct bsv_key_data bsv_key_data_t;
struct bsv_movie
{
intfstream_t *file;
@ -140,6 +151,11 @@ struct bsv_movie
size_t min_file_pos;
size_t state_size;
/* Staging variables for keyboard events */
uint8_t key_event_count;
bsv_key_data_t key_events[255];
/* Rewind state */
bool playback;
bool first_rewind;
bool did_rewind;
@ -989,6 +1005,8 @@ void input_overlay_init(void);
#ifdef HAVE_BSV_MOVIE
void bsv_movie_frame_rewind(void);
void bsv_movie_next_frame(input_driver_state_t *input_st);
void bsv_movie_finish_rewind(input_driver_state_t *input_st);
void bsv_movie_deinit(input_driver_state_t *input_st);
bool movie_start_playback(input_driver_state_t *input_st, char *path);

View File

@ -6740,10 +6740,7 @@ int runloop_iterate(void)
#endif
#ifdef HAVE_BSV_MOVIE
/* Used for rewinding while playback/record. */
if (input_st->bsv_movie_state_handle)
input_st->bsv_movie_state_handle->frame_pos[input_st->bsv_movie_state_handle->frame_ptr]
= intfstream_tell(input_st->bsv_movie_state_handle->file);
bsv_movie_next_frame(input_st);
#endif
if ( camera_st->cb.caps
@ -6974,15 +6971,11 @@ int runloop_iterate(void)
}
#ifdef HAVE_BSV_MOVIE
if (input_st->bsv_movie_state_handle)
bsv_movie_finish_rewind(input_st);
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_END)
{
input_st->bsv_movie_state_handle->frame_ptr =
(input_st->bsv_movie_state_handle->frame_ptr + 1)
& input_st->bsv_movie_state_handle->frame_mask;
input_st->bsv_movie_state_handle->first_rewind =
!input_st->bsv_movie_state_handle->did_rewind;
input_st->bsv_movie_state_handle->did_rewind = false;
movie_stop_playback(input_st);
command_event(CMD_EVENT_PAUSE, NULL);
}
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_END)
{