New semantics for BSV movie recording.

This commit is contained in:
Themaister 2011-11-20 20:19:05 +01:00
parent 379b14a0f3
commit b8064f5d42
4 changed files with 106 additions and 107 deletions

View File

@ -137,12 +137,15 @@ Play back a movie recorded in the .bsv format (bSNES). Cart ROM and movie file n
It also requires to play back with the same libsnes backend that was used for recording. It also requires to play back with the same libsnes backend that was used for recording.
.TP .TP
\fB--bsvrecord MODE, -R MODE\fR \fB--bsvrecord PATH, -R PATH\fR
Start recording a .bsv video immediately after startup. Start recording a .bsv video to PATH immediately after startup.
.TP
\fB--sram-mode MODE, -M MODE\fR
MODE designates how to handle SRAM. MODE designates how to handle SRAM.
The format of the second parameter is {no,}load-{no,}save. The format of the second parameter is {no,}load-{no,}save.
These two boolean values tell if SRAM loading and SRAM saving should take place. These two boolean values tell if SRAM loading and SRAM saving should take place.
Not loading SRAM when recording allows SSNES to avoid including a save state in the .bsv to conserve space. Note that noload-save implies that the SRAM will be overwritten with new data.
.TP .TP
\fB--verbose, -v\fR \fB--verbose, -v\fR

View File

@ -265,16 +265,22 @@ struct global
void *state_buf; void *state_buf;
bool frame_is_reverse; bool frame_is_reverse;
// Movie record support // Movie playback/recording support.
bsv_movie_t *bsv_movie; struct
char bsv_movie_path[MAXPATHLEN]; {
bool bsv_movie_end; bsv_movie_t *movie;
bool bsv_movie_playback; char movie_path[MAXPATHLEN];
bool movie_playback;
// Immediate recording. // Immediate playback/recording.
bool bsv_movie_record_start; char movie_start_path[MAXPATHLEN];
bool bsv_movie_record_sram_start; bool movie_start_recording;
bool bsv_movie_record_sram_end; bool movie_start_playback;
bool movie_end;
} bsv;
bool sram_load_disable;
bool sram_save_disable;
// Pausing support // Pausing support
bool is_paused; bool is_paused;

View File

@ -196,8 +196,6 @@ static bool init_record(bsv_movie_t *handle, const char *path)
header[CRC_INDEX] = swap_if_big32(g_extern.cart_crc); header[CRC_INDEX] = swap_if_big32(g_extern.cart_crc);
uint32_t state_size = psnes_serialize_size(); uint32_t state_size = psnes_serialize_size();
if (!g_extern.bsv_movie_record_sram_start)
state_size = 0;
header[STATE_SIZE_INDEX] = swap_if_big32(state_size); header[STATE_SIZE_INDEX] = swap_if_big32(state_size);
fwrite(header, 4, sizeof(uint32_t), handle->file); fwrite(header, 4, sizeof(uint32_t), handle->file);

178
ssnes.c
View File

@ -348,13 +348,13 @@ static void input_poll(void)
static int16_t input_state(bool port, unsigned device, unsigned index, unsigned id) 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)
{ {
int16_t ret; int16_t ret;
if (bsv_movie_get_input(g_extern.bsv_movie, &ret)) if (bsv_movie_get_input(g_extern.bsv.movie, &ret))
return ret; return ret;
else else
g_extern.bsv_movie_end = true; g_extern.bsv.movie_end = true;
} }
static const struct snes_keybind *binds[MAX_PLAYERS] = { static const struct snes_keybind *binds[MAX_PLAYERS] = {
@ -369,8 +369,8 @@ static int16_t input_state(bool port, unsigned device, unsigned index, unsigned
if (id < SSNES_FIRST_META_KEY) if (id < SSNES_FIRST_META_KEY)
res = driver.input->input_state(driver.input_data, binds, port, device, index, id); 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)
bsv_movie_set_input(g_extern.bsv_movie, res); bsv_movie_set_input(g_extern.bsv.movie, res);
return res; return res;
} }
@ -451,8 +451,10 @@ static void print_help(void)
puts("\t-4/--multitap: Connect a multitap to port 2 of the SNES."); puts("\t-4/--multitap: Connect a multitap to port 2 of the SNES.");
puts("\t-P/--bsvplay: Playback a BSV movie file."); puts("\t-P/--bsvplay: Playback a BSV movie file.");
puts("\t-R/--bsvrecord: Start recording a BSV movie file from the beginning."); puts("\t-R/--bsvrecord: Start recording a BSV movie file from the beginning.");
puts("\t\tThis option takes arguments telling how to handle SRAM in the recording session."); puts("\t-M/--sram-mode: Takes an argument telling how SRAM should be handled in the session.");
puts("\t\t{no,}load-{no,}save describes if SRAM should be loaded, and if SRAM should be saved."); puts("\t\t{no,}load-{no,}save describes if SRAM should be loaded, and if SRAM should be saved.");
puts("\t\tDo note that noload-save implies that save files will be deleted and overwritten.");
#ifdef HAVE_NETPLAY #ifdef HAVE_NETPLAY
puts("\t-H/--host: Host netplay as player 1."); puts("\t-H/--host: Host netplay as player 1.");
puts("\t-C/--connect: Connect to netplay as player 2."); puts("\t-C/--connect: Connect to netplay as player 2.");
@ -600,6 +602,7 @@ static void parse_input(int argc, char *argv[])
{ "sufamiB", 1, NULL, 'Z' }, { "sufamiB", 1, NULL, 'Z' },
{ "bsvplay", 1, NULL, 'P' }, { "bsvplay", 1, NULL, 'P' },
{ "bsvrecord", 1, NULL, 'R' }, { "bsvrecord", 1, NULL, 'R' },
{ "sram-mode", 1, NULL, 'M' },
{ "host", 0, NULL, 'H' }, { "host", 0, NULL, 'H' },
{ "connect", 1, NULL, 'C' }, { "connect", 1, NULL, 'C' },
{ "frames", 1, NULL, 'F' }, { "frames", 1, NULL, 'F' },
@ -632,7 +635,7 @@ static void parse_input(int argc, char *argv[])
#define DYNAMIC_ARG #define DYNAMIC_ARG
#endif #endif
char optstring[] = "hs:fvS:m:p4jJg:b:B:Y:Z:P:R:HC:F:U:DN:X:" DYNAMIC_ARG FFMPEG_RECORD_ARG CONFIG_FILE_ARG; char optstring[] = "hs:fvS:m:p4jJg:b:B:Y:Z:P:R:M:HC:F:U:DN:X:" DYNAMIC_ARG FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
for (;;) for (;;)
{ {
val = 0; val = 0;
@ -749,37 +752,26 @@ static void parse_input(int argc, char *argv[])
#endif #endif
case 'P': case 'P':
strlcpy(g_extern.bsv_movie_path, optarg, sizeof(g_extern.bsv_movie_path)); case 'R':
g_extern.bsv_movie_playback = true; strlcpy(g_extern.bsv.movie_start_path, optarg,
g_extern.bsv_movie_record_start = false; sizeof(g_extern.bsv.movie_start_path));
g_extern.bsv.movie_start_playback = c == 'P';
g_extern.bsv.movie_start_recording = c == 'R';
break; break;
case 'R': case 'M':
g_extern.bsv_movie_playback = false;
g_extern.bsv_movie_record_start = true;
if (strcmp(optarg, "noload-nosave") == 0) if (strcmp(optarg, "noload-nosave") == 0)
{ {
g_extern.bsv_movie_record_sram_start = false; g_extern.sram_load_disable = true;
g_extern.bsv_movie_record_sram_end = false; g_extern.sram_save_disable = true;
} }
else if (strcmp(optarg, "noload-save") == 0) else if (strcmp(optarg, "noload-save") == 0)
{ g_extern.sram_load_disable = true;
g_extern.bsv_movie_record_sram_start = false;
g_extern.bsv_movie_record_sram_end = true;
}
else if (strcmp(optarg, "load-nosave") == 0) else if (strcmp(optarg, "load-nosave") == 0)
g_extern.sram_save_disable = true;
else if (strcmp(optarg, "load-save") != 0)
{ {
g_extern.bsv_movie_record_sram_start = true; SSNES_ERR("Invalid argument in --sram-mode!\n");
g_extern.bsv_movie_record_sram_end = false;
}
else if (strcmp(optarg, "load-save") == 0)
{
g_extern.bsv_movie_record_sram_start = true;
g_extern.bsv_movie_record_sram_end = true;
}
else
{
SSNES_ERR("Invalid argument in --bsvrecord!\n");
print_help(); print_help();
exit(1); exit(1);
} }
@ -796,8 +788,8 @@ static void parse_input(int argc, char *argv[])
case 'F': case 'F':
g_extern.netplay_sync_frames = strtol(optarg, NULL, 0); g_extern.netplay_sync_frames = strtol(optarg, NULL, 0);
if (g_extern.netplay_sync_frames > 32) if (g_extern.netplay_sync_frames > 16)
g_extern.netplay_sync_frames = 32; g_extern.netplay_sync_frames = 16;
break; break;
case 'U': case 'U':
@ -1109,38 +1101,34 @@ static void deinit_rewind(void)
static void init_movie(void) static void init_movie(void)
{ {
if (g_extern.bsv_movie_playback) g_settings.rewind_granularity = 1;
if (g_extern.bsv.movie_start_playback)
{ {
g_extern.bsv_movie = bsv_movie_init(g_extern.bsv_movie_path, SSNES_MOVIE_PLAYBACK); g_extern.bsv.movie = bsv_movie_init(g_extern.bsv.movie_start_path, SSNES_MOVIE_PLAYBACK);
if (!g_extern.bsv_movie) if (!g_extern.bsv.movie)
{ {
SSNES_ERR("Failed to load movie file: \"%s\"!\n", g_extern.bsv_movie_path); SSNES_ERR("Failed to load movie file: \"%s\"!\n", g_extern.bsv.movie_start_path);
exit(1); exit(1);
} }
g_extern.bsv.movie_playback = true;
msg_queue_push(g_extern.msg_queue, "Starting movie playback!", 2, 180); msg_queue_push(g_extern.msg_queue, "Starting movie playback!", 2, 180);
g_settings.rewind_granularity = 1;
SSNES_LOG("Starting movie playback!\n"); SSNES_LOG("Starting movie playback!\n");
} }
else if (g_extern.bsv_movie_record_start) else if (g_extern.bsv.movie_start_recording)
{ {
g_settings.rewind_granularity = 1;
char path[MAXPATHLEN];
if (g_extern.state_slot > 0)
snprintf(path, sizeof(path), "%s%u.bsv", g_extern.bsv_movie_path, g_extern.state_slot);
else
snprintf(path, sizeof(path), "%s.bsv", g_extern.bsv_movie_path);
char msg[MAXPATHLEN]; char msg[MAXPATHLEN];
snprintf(msg, sizeof(msg), "Starting movie record to \"%s\"!", path); snprintf(msg, sizeof(msg), "Starting movie record to \"%s\"!",
g_extern.bsv.movie_start_path);
g_extern.bsv_movie = bsv_movie_init(path, SSNES_MOVIE_RECORD); g_extern.bsv.movie = bsv_movie_init(g_extern.bsv.movie_start_path, SSNES_MOVIE_RECORD);
msg_queue_clear(g_extern.msg_queue); msg_queue_clear(g_extern.msg_queue);
msg_queue_push(g_extern.msg_queue, g_extern.bsv_movie ? msg : "Failed to start movie record!", 1, 180); msg_queue_push(g_extern.msg_queue,
g_extern.bsv.movie ? msg : "Failed to start movie record!", 1, 180);
if (g_extern.bsv_movie) if (g_extern.bsv.movie)
SSNES_LOG("Starting movie record to \"%s\"!\n", path); SSNES_LOG("Starting movie record to \"%s\"!\n", g_extern.bsv.movie_start_path);
else else
SSNES_ERR("Failed to start movie record!\n"); SSNES_ERR("Failed to start movie record!\n");
} }
@ -1148,8 +1136,8 @@ static void init_movie(void)
static void deinit_movie(void) static void deinit_movie(void)
{ {
if (g_extern.bsv_movie) if (g_extern.bsv.movie)
bsv_movie_free(g_extern.bsv_movie); bsv_movie_free(g_extern.bsv.movie);
} }
#define SSNES_DEFAULT_PORT 55435 #define SSNES_DEFAULT_PORT 55435
@ -1344,8 +1332,7 @@ static void fill_pathnames(void)
fill_pathname(g_extern.savefile_name_rtc, g_extern.savefile_name_srm, ".rtc", sizeof(g_extern.savefile_name_rtc)); fill_pathname(g_extern.savefile_name_rtc, g_extern.savefile_name_srm, ".rtc", sizeof(g_extern.savefile_name_rtc));
} }
if (!g_extern.bsv_movie_playback) fill_pathname(g_extern.bsv.movie_path, g_extern.savefile_name_srm, "", sizeof(g_extern.bsv.movie_path));
fill_pathname(g_extern.bsv_movie_path, g_extern.savefile_name_srm, "", sizeof(g_extern.bsv_movie_path));
if (*g_extern.basename) if (*g_extern.basename)
{ {
@ -1561,8 +1548,8 @@ static void check_rewind(void)
msg_queue_push(g_extern.msg_queue, "Rewinding!", 0, g_extern.is_paused ? 1 : 30); msg_queue_push(g_extern.msg_queue, "Rewinding!", 0, g_extern.is_paused ? 1 : 30);
psnes_unserialize(buf, psnes_serialize_size()); psnes_unserialize(buf, psnes_serialize_size());
if (g_extern.bsv_movie) if (g_extern.bsv.movie)
bsv_movie_frame_rewind(g_extern.bsv_movie); bsv_movie_frame_rewind(g_extern.bsv.movie);
} }
else else
msg_queue_push(g_extern.msg_queue, "Reached end of rewind buffer!", 0, 30); msg_queue_push(g_extern.msg_queue, "Reached end of rewind buffer!", 0, 30);
@ -1571,7 +1558,7 @@ static void check_rewind(void)
{ {
static unsigned cnt = 0; static unsigned cnt = 0;
cnt = (cnt + 1) % (g_settings.rewind_granularity ? g_settings.rewind_granularity : 1); // Avoid possible SIGFPE. cnt = (cnt + 1) % (g_settings.rewind_granularity ? g_settings.rewind_granularity : 1); // Avoid possible SIGFPE.
if (cnt == 0 || g_extern.bsv_movie) if (cnt == 0 || g_extern.bsv.movie)
{ {
psnes_serialize(g_extern.state_buf, psnes_serialize_size()); psnes_serialize(g_extern.state_buf, psnes_serialize_size());
state_manager_push(g_extern.state_manager, g_extern.state_buf); state_manager_push(g_extern.state_manager, g_extern.state_buf);
@ -1587,31 +1574,38 @@ static void check_movie_record(void)
bool new_button; bool new_button;
if ((new_button = driver.input->key_pressed(driver.input_data, SSNES_MOVIE_RECORD_TOGGLE)) && !old_button) if ((new_button = driver.input->key_pressed(driver.input_data, SSNES_MOVIE_RECORD_TOGGLE)) && !old_button)
{ {
if (g_extern.bsv_movie) if (g_extern.bsv.movie)
{ {
msg_queue_clear(g_extern.msg_queue); msg_queue_clear(g_extern.msg_queue);
msg_queue_push(g_extern.msg_queue, "Stopping movie record!", 2, 180); msg_queue_push(g_extern.msg_queue, "Stopping movie record!", 2, 180);
SSNES_LOG("Stopping movie record!\n"); SSNES_LOG("Stopping movie record!\n");
bsv_movie_free(g_extern.bsv_movie); bsv_movie_free(g_extern.bsv.movie);
g_extern.bsv_movie = NULL; g_extern.bsv.movie = NULL;
} }
else else
{ {
g_settings.rewind_granularity = 1; g_settings.rewind_granularity = 1;
char path[MAXPATHLEN]; char path[MAXPATHLEN];
if (g_extern.state_slot > 0) if (g_extern.state_slot > 0)
snprintf(path, sizeof(path), "%s%u.bsv", g_extern.bsv_movie_path, g_extern.state_slot); {
snprintf(path, sizeof(path), "%s%u.bsv",
g_extern.bsv.movie_path, g_extern.state_slot);
}
else else
snprintf(path, sizeof(path), "%s.bsv", g_extern.bsv_movie_path); {
snprintf(path, sizeof(path), "%s.bsv",
g_extern.bsv.movie_path);
}
char msg[MAXPATHLEN]; char msg[MAXPATHLEN];
snprintf(msg, sizeof(msg), "Starting movie record to \"%s\"!", path); snprintf(msg, sizeof(msg), "Starting movie record to \"%s\"!", path);
g_extern.bsv_movie = bsv_movie_init(path, SSNES_MOVIE_RECORD); g_extern.bsv.movie = bsv_movie_init(path, SSNES_MOVIE_RECORD);
msg_queue_clear(g_extern.msg_queue); msg_queue_clear(g_extern.msg_queue);
msg_queue_push(g_extern.msg_queue, g_extern.bsv_movie ? msg : "Failed to start movie record!", 1, 180); msg_queue_push(g_extern.msg_queue, g_extern.bsv.movie ? msg : "Failed to start movie record!", 1, 180);
if (g_extern.bsv_movie) if (g_extern.bsv.movie)
SSNES_LOG("Starting movie record to \"%s\"!\n", path); SSNES_LOG("Starting movie record to \"%s\"!\n", path);
else else
SSNES_ERR("Failed to start movie record!\n"); SSNES_ERR("Failed to start movie record!\n");
@ -1623,13 +1617,15 @@ static void check_movie_record(void)
static void check_movie_playback(void) static void check_movie_playback(void)
{ {
if (g_extern.bsv_movie_end) if (g_extern.bsv.movie_end)
{ {
msg_queue_push(g_extern.msg_queue, "Movie playback ended!", 1, 180); msg_queue_push(g_extern.msg_queue, "Movie playback ended!", 1, 180);
bsv_movie_free(g_extern.bsv_movie); SSNES_LOG("Movie playback ended!\n");
g_extern.bsv_movie = NULL;
g_extern.bsv_movie_end = false; bsv_movie_free(g_extern.bsv.movie);
g_extern.bsv_movie_playback = false; g_extern.bsv.movie = NULL;
g_extern.bsv.movie_end = false;
g_extern.bsv.movie_playback = false;
} }
} }
@ -1841,14 +1837,15 @@ static void do_state_checks(void)
driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_KEY), driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_KEY),
driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_HOLD_KEY)); driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_HOLD_KEY));
if (!g_extern.bsv_movie) if (!g_extern.bsv.movie)
{ {
check_stateslots(); check_stateslots();
check_savestates(); check_savestates();
} }
check_rewind(); check_rewind();
if (g_extern.bsv_movie_playback)
if (g_extern.bsv.movie_playback)
check_movie_playback(); check_movie_playback();
else else
check_movie_record(); check_movie_record();
@ -1911,18 +1908,12 @@ int main(int argc, char *argv[])
init_msg_queue(); init_msg_queue();
if (g_extern.bsv_movie_record_start) if (!g_extern.sram_load_disable)
{ load_save_files();
if (g_extern.bsv_movie_record_sram_start)
load_save_files();
init_movie();
}
else else
{ SSNES_LOG("Skipping SRAM load!\n");
init_movie();
if (!g_extern.bsv_movie) init_movie();
load_save_files();
}
#ifdef HAVE_NETPLAY #ifdef HAVE_NETPLAY
init_netplay(); init_netplay();
@ -1952,16 +1943,17 @@ int main(int argc, char *argv[])
init_recording(); init_recording();
#endif #endif
bool use_sram = (!g_extern.bsv_movie_playback && bool use_sram = !g_extern.netplay_is_client &&
!g_extern.netplay_is_client && !g_extern.sram_save_disable;
!g_extern.bsv_movie_record_start) ||
g_extern.bsv_movie_record_sram_end; if (!use_sram)
SSNES_LOG("SRAM will not be saved!\n");
if (use_sram) if (use_sram)
init_autosave(); init_autosave();
#ifdef HAVE_XML #ifdef HAVE_XML
if (!g_extern.netplay && !g_extern.bsv_movie) if (!g_extern.netplay && !g_extern.bsv.movie)
init_cheats(); init_cheats();
#endif #endif
@ -1989,13 +1981,13 @@ int main(int argc, char *argv[])
if (g_extern.netplay) if (g_extern.netplay)
netplay_pre_frame(g_extern.netplay); netplay_pre_frame(g_extern.netplay);
#endif #endif
if (g_extern.bsv_movie) if (g_extern.bsv.movie)
bsv_movie_set_frame_start(g_extern.bsv_movie); bsv_movie_set_frame_start(g_extern.bsv.movie);
psnes_run(); psnes_run();
if (g_extern.bsv_movie) if (g_extern.bsv.movie)
bsv_movie_set_frame_end(g_extern.bsv_movie); bsv_movie_set_frame_end(g_extern.bsv.movie);
#ifdef HAVE_NETPLAY #ifdef HAVE_NETPLAY
if (g_extern.netplay) if (g_extern.netplay)
netplay_post_frame(g_extern.netplay); netplay_post_frame(g_extern.netplay);