Associate states with replays (#15070)

* change bsv file suffix to replay, update strings

* add trivial RPLY block to save states

* WIP rerecording support, doesn't load states properly yet--issue with checking identifiers?

* Fixed a type error to get time identifiers working right, ready for testing

* handle case where state without replay data is loaded during replay

* cleanups

* whitespace cleanup

* Cleanups, change replay file format magic, fix logic around future states

* Remove failed future message

* Add play-replay-from-slot command, fix load-state-from-slot to use given slot

* build fixes

* Fix race conditions in emscripten build and incorrect replay state incrementing

* Style fix for single line if

---------

Co-authored-by: Joseph C. Osborn <jcoa2018@pomona.edu>
This commit is contained in:
Joe Osborn 2023-03-07 15:10:59 -08:00 committed by GitHub
parent ed2ffb5393
commit 807640fed3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 348 additions and 93 deletions

View file

@ -77,6 +77,9 @@ LDFLAGS := -L. --no-heap-copy -s $(LIBS) -s TOTAL_MEMORY=$(MEMORY) -s NO_EXIT_RU
ifneq ($(PTHREAD), 0)
LDFLAGS += -s WASM_MEM_MAX=1073741824 -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD)
CFLAGS += -pthread
HAVE_THREADS=1
else
HAVE_THREADS=0
endif
ifeq ($(ASYNC), 1)

View file

@ -679,8 +679,7 @@ bool command_load_state_slot(command_t *cmd, const char *arg)
state_path[0] = '\0';
if (savestates_enabled)
{
runloop_get_current_savestate_path(state_path,
sizeof(state_path));
runloop_get_savestate_path(state_path, sizeof(state_path), slot);
core_serialize_size(&info);
savestates_enabled = (info.size > 0);
@ -697,6 +696,40 @@ bool command_load_state_slot(command_t *cmd, const char *arg)
return ret;
}
bool command_play_replay_slot(command_t *cmd, const char *arg)
{
#ifdef HAVE_BSV_MOVIE
char replay_path[16384];
retro_ctx_size_info_t info;
char reply[128] = "";
unsigned int slot = (unsigned int)strtoul(arg, NULL, 10);
char *reply_at = reply + snprintf(reply, sizeof(reply) - 1, "PLAY_REPLAY_SLOT %d", slot);
bool savestates_enabled = core_info_current_supports_savestate();
bool ret = false;
replay_path[0] = '\0';
if (savestates_enabled)
{
runloop_get_replay_path(replay_path, sizeof(replay_path), slot);
core_serialize_size(&info);
savestates_enabled = (info.size > 0);
}
if (savestates_enabled)
{
ret = movie_start_playback(input_state_get_ptr(), replay_path);
if (ret)
command_post_state_loaded();
}
else
ret = false;
cmd->replier(cmd, reply, strlen(reply));
return ret;
#else
return false;
#endif
}
#if defined(HAVE_CHEEVOS)
bool command_read_ram(command_t *cmd, const char *arg)
@ -1302,7 +1335,7 @@ bool command_event_load_entry_state(settings_t *settings)
);
if (ret)
configuration_set_int(settings, settings->ints.state_slot, runloop_st->entry_state_slot);
configuration_set_int(settings, settings->ints.state_slot, runloop_st->entry_state_slot);
return ret;
}
@ -1935,19 +1968,6 @@ bool command_event_main_state(unsigned cmd)
case CMD_EVENT_LOAD_STATE_FROM_RAM:
{
bool res = false;
#ifdef HAVE_BSV_MOVIE
input_driver_state_t *input_st = input_state_get_ptr();
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_RECORDING)
{
RARCH_ERR("[Load] [Movie] Can't load state during movie record\n");
return false;
}
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_PLAYBACK)
{
RARCH_LOG("[Load] [Movie] Loaded state during movie playback, halting playback\n");
movie_stop(input_st);
}
#endif
if (cmd == CMD_EVENT_LOAD_STATE)
res = content_load_state(state_path, false, false);
else

View file

@ -409,6 +409,7 @@ bool command_get_status(command_t *cmd, const char* arg);
bool command_get_config_param(command_t *cmd, const char* arg);
bool command_show_osd_msg(command_t *cmd, const char* arg);
bool command_load_state_slot(command_t *cmd, const char* arg);
bool command_play_replay_slot(command_t *cmd, const char* arg);
#ifdef HAVE_CHEEVOS
bool command_read_ram(command_t *cmd, const char *arg);
bool command_write_ram(command_t *cmd, const char *arg);
@ -441,7 +442,7 @@ static const struct cmd_action_map action_map[] = {
{ "WRITE_CORE_MEMORY",command_write_memory, "<address> <byte1> <byte2> ..." },
{ "LOAD_STATE_SLOT",command_load_state_slot, "<slot number>"},
/*{ "PLAY_REPLAY_SLOT",command_play_replay_slot, "<slot number>"},*/
{ "PLAY_REPLAY_SLOT",command_play_replay_slot, "<slot number>"},
};
static const struct cmd_map map[] = {

View file

@ -4827,6 +4827,129 @@ void bsv_movie_next_frame(input_driver_state_t *input_st)
}
}
}
size_t replay_get_serialize_size(void)
{
input_driver_state_t *input_st = input_state_get_ptr();
if (input_st->bsv_movie_state.flags & (BSV_FLAG_MOVIE_RECORDING | BSV_FLAG_MOVIE_PLAYBACK))
return sizeof(int32_t)+intfstream_tell(input_st->bsv_movie_state_handle->file);
else
return 0;
}
bool replay_get_serialized_data(void* buffer)
{
input_driver_state_t *input_st = input_state_get_ptr();
bsv_movie_t *handle = input_st->bsv_movie_state_handle;
if (input_st->bsv_movie_state.flags & (BSV_FLAG_MOVIE_RECORDING | BSV_FLAG_MOVIE_PLAYBACK))
{
long file_end = intfstream_tell(handle->file);
long read_amt = 0;
long file_end_lil = swap_if_big32(file_end);
uint8_t *file_end_bytes = (uint8_t *)(&file_end_lil);
uint8_t *buf = buffer;
buf[0] = file_end_bytes[0];
buf[1] = file_end_bytes[1];
buf[2] = file_end_bytes[2];
buf[3] = file_end_bytes[3];
buf += 4;
intfstream_rewind(handle->file);
read_amt = intfstream_read(handle->file, (void *)buf, file_end);
if (read_amt != file_end)
RARCH_ERR("[Replay] Failed to write correct number of replay bytes into state file: %d / %d\n", read_amt, file_end);
}
return true;
}
bool replay_set_serialized_data(void* buf)
{
uint8_t *buffer = buf;
input_driver_state_t *input_st = input_state_get_ptr();
bool playback = input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_PLAYBACK;
bool recording = input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_RECORDING;
/* If there is no current replay, ignore this entirely.
TODO: Later, consider loading up the replay and allow the user to continue it? or would that be better done from the replay hotkeys? */
if(!(playback || recording))
return true;
if (buffer == NULL)
{
if (recording)
{
const char *load_fail_str =
msg_hash_to_str(MSG_REPLAY_LOAD_STATE_FAILED_INCOMPAT);
runloop_msg_queue_push(load_fail_str,
1, 180, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_ERROR);
RARCH_ERR("[Replay] %s.\n", load_fail_str);
return false;
}
if (playback)
{
const char *load_warn_str =
msg_hash_to_str(MSG_REPLAY_LOAD_STATE_HALT_INCOMPAT);
runloop_msg_queue_push(load_warn_str,
1, 180, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_WARNING);
RARCH_WARN("[Replay] %s.\n", load_warn_str);
movie_stop(input_st);
}
return true;
}
else
{
int32_t loaded_len = swap_if_big32(((int32_t *)buffer)[0]);
/* TODO: should factor the next few lines away, magic numbers ahoy */
uint32_t *header = (uint32_t *)(buffer+sizeof(int32_t));
int64_t *identifier_spot = (int64_t *)(header+4);
int64_t identifier = swap_if_big64(*identifier_spot);
int32_t handle_idx = intfstream_tell(input_st->bsv_movie_state_handle->file);
bool is_compatible = identifier == input_st->bsv_movie_state_handle->identifier;
if (is_compatible)
{
/* If the state is part of this replay, go back to that state
and rewind the replay; otherwise
halt playback and go to that state normally. */
/* if the savestate movie is after the current replay
length we can replace the current replay data with it,
but if it's earlier we can rewind the replay to the
savestate movie time point. */
/* This can truncate the current recording, so beware! */
/* TODO: figure out what to do about rewinding across load */
if (loaded_len > handle_idx)
{
/* TODO: Really, to be very careful, we should be
checking that the events in the loaded state are the
same up to handle_idx. Right? */
intfstream_rewind(input_st->bsv_movie_state_handle->file);
intfstream_write(input_st->bsv_movie_state_handle->file, buffer+sizeof(int32_t), loaded_len);
}
else
intfstream_seek(input_st->bsv_movie_state_handle->file, loaded_len, SEEK_SET);
}
else
{
if (recording)
{
const char *load_fail_str =
msg_hash_to_str(MSG_REPLAY_LOAD_STATE_FAILED_INCOMPAT);
runloop_msg_queue_push(load_fail_str,
1, 180, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_ERROR);
RARCH_ERR("[Replay] %s.\n", load_fail_str);
return false;
}
if (playback)
{
const char *load_warn_str =
msg_hash_to_str(MSG_REPLAY_LOAD_STATE_HALT_INCOMPAT);
runloop_msg_queue_push(load_warn_str,
1, 180, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_WARNING);
RARCH_WARN("[Replay] %s.\n", load_warn_str);
movie_stop(input_st);
}
}
}
return true;
}
#endif

View file

@ -150,6 +150,7 @@ struct bsv_movie
size_t frame_ptr;
size_t min_file_pos;
size_t state_size;
int64_t identifier;
/* Staging variables for keyboard events */
uint8_t key_event_count;
@ -1015,6 +1016,9 @@ bool movie_stop_playback(input_driver_state_t *input_st);
bool movie_stop_record(input_driver_state_t *input_st);
bool movie_stop(input_driver_state_t *input_st);
size_t replay_get_serialize_size(void);
bool replay_get_serialized_data(void* buffer);
bool replay_set_serialized_data(void* buffer);
#endif
/**

View file

@ -8514,8 +8514,8 @@ MSG_HASH(
"الذاكرة"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"ملف إعادة عرض فيلم الإدخال ليس ملف BSV1 صالح."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"ملف إعادة عرض فيلم الإدخال ليس ملف REPLAY صالح."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -11402,8 +11402,8 @@ MSG_HASH(
"内存"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"视频不是有效的BSV1文件。"
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"视频不是有效的REPLAY文件。"
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -13278,8 +13278,8 @@ MSG_HASH(
"記憶體"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"操作重播不是有效的BSV1檔案。"
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"操作重播不是有效的REPLAY檔案。"
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -7244,6 +7244,10 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_SAVEFILE_DIRECTORY,
"Do tohoto adresáře uložte všechny uložené soubory. Pokud není nastaveno, pokusí se uložit do pracovního adresáře souboru s obsahem."
)
MSG_HASH(
MENU_ENUM_LABEL_HELP_SAVEFILE_DIRECTORY,
"Do tohoto adresáře uložte všechny ukládací soubory (*.srm). Patří sem i související soubory jako .rt, .psrm atd... Toto bude potlačeno explicitními volbami příkazového řádku."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SAVESTATE_DIRECTORY,
"Uložené Pozice"
@ -12670,8 +12674,8 @@ MSG_HASH(
"Paměť"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Vstupní soubor videa pro přehrávání videa není platným BSV1 souborem."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Vstupní soubor videa pro přehrávání videa není platným REPLAY souborem."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -12646,8 +12646,8 @@ MSG_HASH(
"Speicher"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Filmdatei ist keine gültige BSV1-Datei."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Filmdatei ist keine gültige REPLAY-Datei."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -2006,8 +2006,8 @@ MSG_HASH(
"Loading favourites file"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Input replay film file is not a valid BSV1 file."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Input replay film file is not a valid REPLAY file."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -1002,8 +1002,8 @@ MSG_HASH(
"CRC32 checksum mismatch between content file and saved content checksum in replay file header) replay highly likely to desync on playback."
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Movie file is not a valid BSV1 file."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Movie file is not a valid REPLAY file."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -12966,8 +12966,8 @@ MSG_HASH(
"Memoria"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"El archivo de la repetición de entrada no es un archivo BSV1 válido."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"El archivo de la repetición de entrada no es un archivo REPLAY válido."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -11982,8 +11982,8 @@ MSG_HASH(
"Muisti"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Sisääntulon uudelleentoiston elokuvatiedosto ei ole kelvollinen BSV1-tiedosto."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Sisääntulon uudelleentoiston elokuvatiedosto ei ole kelvollinen REPLAY-tiedosto."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -12922,8 +12922,8 @@ MSG_HASH(
"Mémoire"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Le fichier vidéo de relecture n'est pas un fichier BSV1 valide."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Le fichier vidéo de relecture n'est pas un fichier REPLAY valide."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -12734,8 +12734,8 @@ MSG_HASH(
"Memória"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"A bemenet-újrajátszás fájl nem egy érvényes BSV1 fájl."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"A bemenet-újrajátszás fájl nem egy érvényes REPLAY fájl."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -12806,8 +12806,8 @@ MSG_HASH(
"Memoria"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Il File del filmato non è un file valido di BSV1."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Il File del filmato non è un file valido di REPLAY."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -10242,8 +10242,8 @@ MSG_HASH(
"メモリ"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"入力リプレイムービーファイルは有効なBSV1ファイルではありません. "
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"入力リプレイムービーファイルは有効なREPLAYファイルではありません. "
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -13018,8 +13018,8 @@ MSG_HASH(
"메모리"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"입력 리플레이 영상 파일이 올바른 BSV1 파일이 아닙니다."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"동영상 파일이 올바른 REPLAY 파일이 아닙니다."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -5930,8 +5930,8 @@ MSG_HASH(
"Geheugen"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Movie file is not a valid BSV1 file."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Movie file is not a valid REPLAY file."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -11666,8 +11666,8 @@ MSG_HASH(
"Pamięć"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Plik filmu nie jest prawidłowym plikiem BSV1."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Plik filmu nie jest prawidłowym plikiem REPLAY."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -11770,8 +11770,8 @@ MSG_HASH(
"Memória"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"O arquivo de filme não é um arquivo BSV1 válido."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"O arquivo de filme não é um arquivo REPLAY válido."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -4834,8 +4834,8 @@ MSG_HASH(
"Memória"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"O ficheiro de vídeo não é um ficheiro BSV1 válido."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"O ficheiro de vídeo não é um ficheiro REPLAY válido."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -13034,8 +13034,8 @@ MSG_HASH(
"Объём памяти"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Запись повтора не является правильным файлом BSV1."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Запись повтора не является правильным файлом REPLAY."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -12254,8 +12254,8 @@ MSG_HASH(
"Bellek"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Giriş tekrar film dosyası geçerli bir BSV1 dosyası değil."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Giriş tekrar film dosyası geçerli bir REPLAY dosyası değil."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -13613,6 +13613,14 @@ MSG_HASH(
MSG_FOUND_LAST_REPLAY_SLOT,
"Found last replay slot"
)
MSG_HASH(
MSG_REPLAY_LOAD_STATE_FAILED_INCOMPAT,
"Not from current recording"
)
MSG_HASH(
MSG_REPLAY_LOAD_STATE_HALT_INCOMPAT,
"Not compatible with replay"
)
MSG_HASH(
MSG_FOUND_SHADER,
"Found shader"
@ -13742,8 +13750,8 @@ MSG_HASH(
"Memory"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Input replay movie file is not a valid BSV1 file."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Input replay movie file is not a valid REPLAY file."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -2518,8 +2518,8 @@ MSG_HASH(
"Đang tải state"
)
MSG_HASH(
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
"Movie file is not a valid BSV1 file."
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
"Movie file is not a valid REPLAY file."
)
MSG_HASH(
MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,

View file

@ -281,6 +281,8 @@ enum msg_hash_enums
MSG_FAILED_TO_START_AUDIO_DRIVER,
MSG_FOUND_LAST_STATE_SLOT,
MSG_FOUND_LAST_REPLAY_SLOT,
MSG_REPLAY_LOAD_STATE_HALT_INCOMPAT,
MSG_REPLAY_LOAD_STATE_FAILED_INCOMPAT,
MSG_RESTORED_OLD_SAVE_STATE,
MSG_NO_STATE_HAS_BEEN_LOADED_YET,
MSG_GOT_CONNECTION_FROM,
@ -337,7 +339,7 @@ enum msg_hash_enums
MSG_LOADING_HISTORY_FILE,
MSG_LOADING_FAVORITES_FILE,
MSG_COULD_NOT_READ_STATE_FROM_MOVIE,
MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE,
MSG_OVERRIDES_NOT_SAVED,
MSG_OVERRIDES_ACTIVE_NOT_SAVING,
MSG_OVERRIDES_SAVED_SUCCESSFULLY,

View file

@ -2544,25 +2544,24 @@ bool command_event(enum event_command cmd, void *data)
bool res = false;
#ifdef HAVE_BSV_MOVIE
input_driver_state_t *input_st = input_state_get_ptr();
int replay_slot = settings->ints.replay_slot;
char replay_path[PATH_MAX_LENGTH];
if (settings->bools.replay_auto_index)
replay_slot += 1;
res = true;
/* TODO: Consider cloning and extending the current replay if we start recording during a recording */
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_RECORDING)
res = false;
else if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_PLAYBACK)
res = movie_stop(input_st);
RARCH_ERR("[Movie] res after stop check: %d\n",res);
if (!runloop_get_current_replay_path(replay_path, sizeof(replay_path)))
if (!runloop_get_replay_path(replay_path, sizeof(replay_path), replay_slot))
res = false;
RARCH_ERR("[Movie] res after path get: %d\n",res);
if(res)
res = movie_start_record(input_st, replay_path);
RARCH_ERR("[Movie] res after start record: %d\n",res);
if(res && settings->bools.replay_auto_index)
{
int new_replay_slot = settings->ints.replay_slot + 1;
configuration_set_int(settings, settings->ints.replay_slot, new_replay_slot);
configuration_set_int(settings, settings->ints.replay_slot, replay_slot);
}
if(!res)
{

View file

@ -4114,6 +4114,18 @@ static bool event_init_content(
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_START_RECORDING)
{
configuration_set_uint(settings, settings->uints.rewind_granularity, 1);
#ifndef HAVE_THREADS
/* Hack: the regular scheduler doesn't do the right thing here at
least in emscripten builds. I would expect that the check in
task_movie.c:343 should defer recording until the movie task
is done, but maybe that task isn't enqueued again yet when the
movie-record task is checked? Or the finder call in
content_load_state_in_progress is not correct? Either way,
the load happens after the recording starts rather than the
right way around.
*/
task_queue_wait(NULL,NULL);
#endif
movie_start_record(input_st, input_st->bsv_movie_state.movie_start_path);
}
else if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_START_PLAYBACK)

View file

@ -20,6 +20,7 @@
#include <sys/types.h>
#include <string.h>
#include <time.h>
#include <time/rtime.h>
#include <compat/strl.h>
#include <file/file_path.h>
#include <streams/file_stream.h>
@ -43,11 +44,15 @@
#include "../input/input_driver.h"
#define MAGIC_INDEX 0
#define SERIALIZER_INDEX 1
#define VERSION_INDEX 1
#define CRC_INDEX 2
#define STATE_SIZE_INDEX 3
/* Identifier is int64_t, so takes up two slots */
#define IDENTIFIER_INDEX 4
#define HEADER_LEN 6
#define BSV_MAGIC 0x42535631
#define REPLAY_FORMAT_VERSION 0
#define REPLAY_MAGIC 0x42535632
/* Forward declaration */
bool content_load_state_in_progress(void* data);
@ -57,9 +62,10 @@ bool content_load_state_in_progress(void* data);
static bool bsv_movie_init_playback(
bsv_movie_t *handle, const char *path)
{
uint32_t state_size = 0;
uint32_t header[4] = {0};
intfstream_t *file = intfstream_open_file(path,
int64_t *identifier_loc;
uint32_t state_size = 0;
uint32_t header[HEADER_LEN] = {0};
intfstream_t *file = intfstream_open_file(path,
RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
@ -72,23 +78,31 @@ static bool bsv_movie_init_playback(
handle->file = file;
handle->playback = true;
intfstream_read(handle->file, header, sizeof(uint32_t) * 4);
/* Compatibility with old implementation that
* used incorrect documentation. */
if (swap_if_little32(header[MAGIC_INDEX]) != BSV_MAGIC
&& swap_if_big32(header[MAGIC_INDEX]) != BSV_MAGIC)
intfstream_read(handle->file, header, sizeof(uint32_t) * HEADER_LEN);
if (swap_if_big32(header[MAGIC_INDEX]) != REPLAY_MAGIC)
{
RARCH_ERR("%s\n", msg_hash_to_str(MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE));
RARCH_ERR("%s\n", msg_hash_to_str(MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE));
return false;
}
/*
if (swap_if_big32(header[VERSION_INDEX]) > REPLAY_FORMAT_VERSION)
{
RARCH_ERR("%s\n", msg_hash_to_str(MSG_MOVIE_FILE_IS_NOT_A_VALID_REPLAY_FILE));
return false;
}
*/
state_size = swap_if_big32(header[STATE_SIZE_INDEX]);
identifier_loc = (int64_t *)(header+IDENTIFIER_INDEX);
handle->identifier = swap_if_big64(*identifier_loc);
#if 0
RARCH_ERR("----- debug %u -----\n", header[0]);
RARCH_ERR("----- debug %u -----\n", header[1]);
RARCH_ERR("----- debug %u -----\n", header[2]);
RARCH_ERR("----- debug %u -----\n", header[3]);
RARCH_ERR("----- debug %u -----\n", header[4]);
RARCH_ERR("----- debug %u -----\n", header[5]);
#endif
if (state_size)
@ -130,11 +144,13 @@ static bool bsv_movie_init_record(
bsv_movie_t *handle, const char *path)
{
retro_ctx_size_info_t info;
uint32_t state_size = 0;
uint32_t content_crc = 0;
uint32_t header[4] = {0};
intfstream_t *file = intfstream_open_file(path,
RETRO_VFS_FILE_ACCESS_WRITE,
time_t t = time(NULL);
time_t time_lil = swap_if_big64(t);
uint32_t state_size = 0;
uint32_t content_crc = 0;
uint32_t header[HEADER_LEN] = {0};
intfstream_t *file = intfstream_open_file(path,
RETRO_VFS_FILE_ACCESS_WRITE | RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file)
@ -147,17 +163,18 @@ static bool bsv_movie_init_record(
content_crc = content_get_crc();
/* This value is supposed to show up as
* BSV1 in a HEX editor, big-endian. */
header[MAGIC_INDEX] = swap_if_little32(BSV_MAGIC);
header[MAGIC_INDEX] = swap_if_big32(REPLAY_MAGIC);
header[VERSION_INDEX] = REPLAY_FORMAT_VERSION;
header[CRC_INDEX] = swap_if_big32(content_crc);
core_serialize_size(&info);
state_size = (unsigned)info.size;
header[STATE_SIZE_INDEX] = swap_if_big32(state_size);
intfstream_write(handle->file, header, 4 * sizeof(uint32_t));
handle->identifier = (int64_t)t;
*((int64_t *)(header+IDENTIFIER_INDEX)) = time_lil;
intfstream_write(handle->file, header, HEADER_LEN * sizeof(uint32_t));
handle->min_file_pos = sizeof(header) + state_size;
handle->state_size = state_size;
@ -459,4 +476,5 @@ error:
return false;
}
#endif

View file

@ -71,6 +71,7 @@
#define RASTATE_VERSION 1
#define RASTATE_MEM_BLOCK "MEM "
#define RASTATE_CHEEVOS_BLOCK "ACHV"
#define RASTATE_REPLAY_BLOCK "RPLY"
#define RASTATE_END_BLOCK "END "
struct ram_type
@ -184,6 +185,9 @@ typedef struct rastate_size_info
#ifdef HAVE_CHEEVOS
size_t cheevos_size;
#endif
#ifdef HAVE_BSV_MOVIE
size_t replay_size;
#endif
} rastate_size_info_t;
#ifdef HAVE_THREADS
@ -629,6 +633,11 @@ static size_t content_get_rastate_size(rastate_size_info_t* size)
/* 8-byte block header + content */
if ((size->cheevos_size = rcheevos_get_serialize_size()) > 0)
size->total_size += 8 + CONTENT_ALIGN_SIZE(size->cheevos_size);
#endif
#ifdef HAVE_BSV_MOVIE
/* 8-byte block header + content */
if ((size->replay_size = replay_get_serialize_size()) > 0)
size->total_size += 8 + CONTENT_ALIGN_SIZE(size->replay_size);
#endif
return size->total_size;
}
@ -658,6 +667,21 @@ static bool content_write_serialized_state(void* buffer,
memcpy(output, "RASTATE", 7);
output[7] = RASTATE_VERSION;
output += 8;
/* Replay block---this has to come before the mem block since its
contents may prevent the state from loading (e.g., if it's
incompatible with the current recording). */
#ifdef HAVE_BSV_MOVIE
{
input_driver_state_t *input_st = input_state_get_ptr();
if (input_st->bsv_movie_state.flags & (BSV_FLAG_MOVIE_RECORDING | BSV_FLAG_MOVIE_PLAYBACK))
{
content_write_block_header(output,
RASTATE_REPLAY_BLOCK, size->replay_size);
if (replay_get_serialized_data(output + 8))
output += CONTENT_ALIGN_SIZE(size->replay_size) + 8;
}
}
#endif
/* important - write the unaligned size - some cores fail if they aren't passed the exact right size. */
content_write_block_header(output, RASTATE_MEM_BLOCK, size->coremem_size);
@ -1070,6 +1094,9 @@ static bool content_load_rastate1(unsigned char* input, size_t size)
#ifdef HAVE_CHEEVOS
bool seen_cheevos = false;
#endif
#ifdef HAVE_BSV_MOVIE
bool seen_replay = false;
#endif
input += 8;
@ -1086,6 +1113,23 @@ static bool content_load_rastate1(unsigned char* input, size_t size)
retro_ctx_serialize_info_t serial_info;
serial_info.data_const = (void*)input;
serial_info.size = block_size;
#ifdef HAVE_BSV_MOVIE
{
input_driver_state_t *input_st = input_state_get_ptr();
if (BSV_MOVIE_IS_RECORDING() && !seen_replay)
{
/* TODO OSD message */
RARCH_ERR("[Replay] Can't load state without replay data during recording.\n");
return false;
}
if (BSV_MOVIE_IS_PLAYBACK_ON() && !seen_replay)
{
/* TODO OSD message */
RARCH_WARN("[Replay] Loading state without replay data during replay will cancel replay.\n");
movie_stop(input_st);
}
}
#endif
if (!core_unserialize(&serial_info))
return false;
@ -1097,6 +1141,15 @@ static bool content_load_rastate1(unsigned char* input, size_t size)
if (rcheevos_set_serialized_data((void*)input))
seen_cheevos = true;
}
#endif
#ifdef HAVE_BSV_MOVIE
else if (memcmp(marker, RASTATE_REPLAY_BLOCK, 4) == 0)
{
if (replay_set_serialized_data((void*)input))
seen_replay = true;
else
return false;
}
#endif
else if (memcmp(marker, RASTATE_END_BLOCK, 4) == 0)
break;
@ -1104,13 +1157,19 @@ static bool content_load_rastate1(unsigned char* input, size_t size)
input += CONTENT_ALIGN_SIZE(block_size);
}
if (!seen_core)
if (!seen_core) {
RARCH_LOG("[State] no core\n");
return false;
}
#ifdef HAVE_CHEEVOS
if (!seen_cheevos)
rcheevos_set_serialized_data(NULL);
#endif
#ifdef HAVE_BSV_MOVIE
if (!seen_replay)
replay_set_serialized_data(NULL);
#endif
return true;
}
@ -1129,6 +1188,9 @@ bool content_deserialize_state(
#ifdef HAVE_CHEEVOS
rcheevos_set_serialized_data(NULL);
#endif
#ifdef HAVE_BSV_MOVIE
replay_set_serialized_data(NULL);
#endif
}
else
@ -1535,7 +1597,6 @@ bool content_save_state(const char *path, bool save_to_disk, bool autosave)
/* TODO/FIXME - Use msg_hash_to_str here */
RARCH_LOG("[State]: %s ...\n",
msg_hash_to_str(MSG_FILE_ALREADY_EXISTS_SAVING_TO_BACKUP_BUFFER));
task_push_load_and_save_state(path, data, serial_size, true, autosave);
}
else
@ -1601,7 +1662,7 @@ static bool content_save_state_in_progress(void* data)
task_finder_data_t find_data;
find_data.func = task_save_state_finder;
find_data.userdata = NULL;
find_data.userdata = data;
return task_queue_find(&find_data);
}
@ -1623,7 +1684,7 @@ bool content_load_state_in_progress(void* data)
task_finder_data_t find_data;
find_data.func = task_load_state_finder;
find_data.userdata = NULL;
find_data.userdata = data;
return task_queue_find(&find_data);
}