Add movie "finished" state. For details, see http://tasvideos.org/LawsOfTAS/OnSavestates.html

This commit is contained in:
gocha 2012-08-24 20:15:22 +09:00
parent ac53f5110e
commit 1b8208f4a6
3 changed files with 2506 additions and 2435 deletions

View File

@ -210,7 +210,8 @@ enum MovieState
{
MOVIE_STATE_NONE = 0,
MOVIE_STATE_PLAY,
MOVIE_STATE_RECORD
MOVIE_STATE_RECORD,
MOVIE_STATE_FINISHED
};
struct SMovie
@ -274,6 +275,7 @@ static int read_movie_extrarominfo (FILE *, SMovie *);
static void write_movie_header (FILE *, SMovie *);
static void write_movie_extrarominfo (FILE *, SMovie *);
static void change_state (MovieState);
static void finish_playback (void);
// HACK: reduce movie size by not storing changes that can only affect polled input in the movie for these types,
// because currently no port sets these types to polling
@ -675,13 +677,24 @@ static void change_state (MovieState new_state)
fclose(Movie.File);
Movie.File = NULL;
if (S9xMoviePlaying() || S9xMovieRecording())
if (S9xMovieActive())
restore_previous_settings();
}
if (new_state == MOVIE_STATE_FINISHED)
{
truncate_movie();
}
Movie.State = new_state;
}
static void finish_playback (void)
{
change_state(MOVIE_STATE_FINISHED);
S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_END);
}
void S9xMovieFreeze (uint8 **buf, uint32 *size)
{
if (!S9xMovieActive())
@ -711,10 +724,21 @@ void S9xMovieFreeze (uint8 **buf, uint32 *size)
int S9xMovieUnfreeze (uint8 *buf, uint32 size)
{
if (!S9xMovieActive())
return (FILE_NOT_FOUND);
{
return (SUCCESS);
}
// not a movie snapshot
if (buf == NULL)
{
finish_playback();
return (SUCCESS);
}
if (size < sizeof(Movie.MovieId) + sizeof(Movie.CurrentFrame) + sizeof(Movie.MaxFrame) + sizeof(Movie.CurrentSample) + sizeof(Movie.MaxSample))
{
return (WRONG_FORMAT);
}
uint8 *ptr = buf;
@ -724,14 +748,26 @@ int S9xMovieUnfreeze (uint8 *buf, uint32 size)
uint32 current_sample = Read32(ptr);
uint32 max_sample = Read32(ptr);
uint32 space_needed = (Movie.BytesPerSample * (max_sample + 1));
uint32 space_played = (Movie.BytesPerSample * (current_sample + 1));
if (current_frame > max_frame || current_sample > max_sample || space_needed > size)
// (current_frame > max_frame) is legal, because of "finished" state
if (current_sample > max_sample || space_needed > size)
return (WRONG_MOVIE_SNAPSHOT);
// complex TAS logic for loadstate
// fully conforms to the savestate logic documented in the Laws of TAS
// http://tasvideos.org/LawsOfTAS/OnSavestates.html
if (Settings.WrongMovieStateProtection)
if (movie_id != Movie.MovieId)
if (max_frame < Movie.MaxFrame || max_sample < Movie.MaxSample || memcmp(Movie.InputBuffer, ptr, space_needed))
return (WRONG_MOVIE_SNAPSHOT);
if (movie_id != Movie.MovieId) {
return (WRONG_MOVIE_SNAPSHOT);
}
if (Settings.WrongMovieStateProtection)
if (Movie.ReadOnly) {
if (current_frame <= Movie.MaxFrame && current_sample <= Movie.MaxSample && memcmp(Movie.InputBuffer, ptr, space_played) != 0)
return (SNAPSHOT_INCONSISTENT);
}
if (!Movie.ReadOnly)
{
@ -754,18 +790,21 @@ int S9xMovieUnfreeze (uint8 *buf, uint32 size)
}
else
{
uint32 space_processed = (Movie.BytesPerSample * (current_sample + 1));
if (current_frame > Movie.MaxFrame || current_sample > Movie.MaxSample || memcmp(Movie.InputBuffer, ptr, space_processed))
return (SNAPSHOT_INCONSISTENT);
change_state(MOVIE_STATE_PLAY);
Movie.CurrentFrame = current_frame;
Movie.CurrentSample = current_sample;
}
Movie.InputBufferPtr = Movie.InputBuffer + (Movie.BytesPerSample * Movie.CurrentSample);
read_frame_controller_data(true);
if (Movie.CurrentFrame <= Movie.MaxFrame && Movie.CurrentSample <= Movie.MaxSample)
{
Movie.InputBufferPtr = Movie.InputBuffer + (Movie.BytesPerSample * Movie.CurrentSample);
read_frame_controller_data(true);
}
else
{
finish_playback();
}
return (SUCCESS);
}
@ -1037,8 +1076,11 @@ void S9xMovieUpdate (bool addFrame)
{
if (Movie.CurrentFrame >= Movie.MaxFrame || Movie.CurrentSample >= Movie.MaxSample)
{
change_state(MOVIE_STATE_NONE);
S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_END);
finish_playback();
if (addFrame) {
S9xUpdateFrameCounter();
Movie.CurrentFrame++;
}
return;
}
else
@ -1077,6 +1119,16 @@ void S9xMovieUpdate (bool addFrame)
break;
}
case MOVIE_STATE_FINISHED:
{
if (addFrame) {
S9xUpdateFrameCounter();
Movie.CurrentFrame++;
}
break;
}
default:
{
if (addFrame)
@ -1165,6 +1217,11 @@ bool8 S9xMovieRecording (void)
return (Movie.State == MOVIE_STATE_RECORD);
}
bool8 S9xMovieFinished (void)
{
return (Movie.State == MOVIE_STATE_FINISHED);
}
uint8 S9xMovieControllers (void)
{
return (Movie.ControllersMask);
@ -1282,6 +1339,10 @@ void S9xUpdateFrameCounter (int offset)
if (Movie.State == MOVIE_STATE_PLAY)
sprintf(GFX.FrameDisplayString, "Playing frame: %d / %d",
max(0, (int) (Movie.CurrentFrame + offset)), Movie.MaxFrame);
else
if (Movie.State == MOVIE_STATE_FINISHED)
sprintf(GFX.FrameDisplayString, "Playing frame: %d / %d (finished)",
max(0, (int) (Movie.CurrentFrame + offset)), Movie.MaxFrame);
#ifdef NETPLAY_SUPPORT
else
if (Settings.NetPlay)

View File

@ -235,6 +235,7 @@ int S9xMovieUnfreeze (uint8 *, uint32);
bool8 S9xMovieActive (void);
bool8 S9xMoviePlaying (void);
bool8 S9xMovieRecording (void);
bool8 S9xMovieFinished (void);
bool8 S9xMovieReadOnly (void);
bool8 S9xMovieSetReadOnly (bool8 readonly);
uint8 S9xMovieControllers (void);

File diff suppressed because it is too large Load Diff