mirror of
https://github.com/libretro/RetroArch
synced 2025-02-06 00:39:53 +00:00
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:
parent
ed2ffb5393
commit
807640fed3
@ -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)
|
||||
|
52
command.c
52
command.c
@ -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
|
||||
|
@ -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[] = {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
11
retroarch.c
11
retroarch.c
@ -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)
|
||||
{
|
||||
|
12
runloop.c
12
runloop.c
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user