mirror of
https://github.com/libretro/RetroArch
synced 2025-04-01 04:20:27 +00:00
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:
parent
c27e73e736
commit
155e99b92c
@ -4692,6 +4692,144 @@ static int16_t input_state_device(
|
|||||||
return res;
|
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)
|
void input_driver_poll(void)
|
||||||
{
|
{
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
@ -5145,74 +5283,30 @@ void input_driver_poll(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_BSV_MOVIE
|
#ifdef HAVE_BSV_MOVIE
|
||||||
void bsv_movie_free(bsv_movie_t*);
|
if (BSV_MOVIE_IS_PLAYBACK_ON())
|
||||||
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... */
|
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
||||||
handle->frame_ptr = 0;
|
retro_keyboard_event_t *key_event = &runloop_st->key_event;
|
||||||
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)
|
if(*key_event && *key_event == runloop_st->frontend_key_event)
|
||||||
{
|
|
||||||
/* We rewound past the beginning. */
|
|
||||||
|
|
||||||
if (!handle->playback)
|
|
||||||
{
|
{
|
||||||
retro_ctx_serialize_info_t serial_info;
|
int i;
|
||||||
|
bsv_key_data_t k;
|
||||||
/* If recording, we simply reset
|
for(i = 0; i < input_st->bsv_movie_state_handle->key_event_count; i++)
|
||||||
* the starting point. Nice and easy. */
|
{
|
||||||
|
#ifdef HAVE_CHEEVOS
|
||||||
intfstream_seek(handle->file, 4 * sizeof(uint32_t), SEEK_SET);
|
rcheevos_pause_hardcore();
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#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,
|
int16_t input_state_internal(unsigned port, unsigned device,
|
||||||
unsigned idx, unsigned id)
|
unsigned idx, unsigned id)
|
||||||
@ -6206,6 +6300,18 @@ void input_keyboard_event(bool down, unsigned code,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (*key_event)
|
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);
|
(*key_event)(down, code, character, mod);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,6 +128,17 @@ struct bsv_state
|
|||||||
char movie_start_path[PATH_MAX_LENGTH];
|
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
|
struct bsv_movie
|
||||||
{
|
{
|
||||||
intfstream_t *file;
|
intfstream_t *file;
|
||||||
@ -140,6 +151,11 @@ struct bsv_movie
|
|||||||
size_t min_file_pos;
|
size_t min_file_pos;
|
||||||
size_t state_size;
|
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 playback;
|
||||||
bool first_rewind;
|
bool first_rewind;
|
||||||
bool did_rewind;
|
bool did_rewind;
|
||||||
@ -989,6 +1005,8 @@ void input_overlay_init(void);
|
|||||||
|
|
||||||
#ifdef HAVE_BSV_MOVIE
|
#ifdef HAVE_BSV_MOVIE
|
||||||
void bsv_movie_frame_rewind(void);
|
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);
|
void bsv_movie_deinit(input_driver_state_t *input_st);
|
||||||
|
|
||||||
bool movie_start_playback(input_driver_state_t *input_st, char *path);
|
bool movie_start_playback(input_driver_state_t *input_st, char *path);
|
||||||
|
17
runloop.c
17
runloop.c
@ -6740,10 +6740,7 @@ int runloop_iterate(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_BSV_MOVIE
|
#ifdef HAVE_BSV_MOVIE
|
||||||
/* Used for rewinding while playback/record. */
|
bsv_movie_next_frame(input_st);
|
||||||
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);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( camera_st->cb.caps
|
if ( camera_st->cb.caps
|
||||||
@ -6974,15 +6971,11 @@ int runloop_iterate(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_BSV_MOVIE
|
#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 =
|
movie_stop_playback(input_st);
|
||||||
(input_st->bsv_movie_state_handle->frame_ptr + 1)
|
command_event(CMD_EVENT_PAUSE, NULL);
|
||||||
& 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;
|
|
||||||
}
|
}
|
||||||
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_END)
|
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_END)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user