Movies now have a "finished" option. If a playback stops the movie isn't cleared from memory, and can be replayed or a state loaded. Similar functionality as DeSmuME and GENS rerecording.

This commit is contained in:
adelikat 2010-05-12 15:31:24 +00:00
parent 4456368632
commit 9006fd7a73
8 changed files with 41 additions and 24 deletions

View File

@ -1,4 +1,5 @@
11-may-2010 - 10-may-2010 - adelikat - Loadstate updates input display 12-may-2010 - adelikat - Movies now have a "finished" option. If a playback stops the movie isn't cleared from memory, and can be replayed or a state loaded. Similar functionality as DeSmuME and GENS rerecording
11-may-2010 - adelikat - Loadstate updates input display
11-may-2010 - ugetab - Win32 - Added Ram Search hotkeys for the first 6 search types in the list. 11-may-2010 - ugetab - Win32 - Added Ram Search hotkeys for the first 6 search types in the list.
10-may-2010 - ugetab - Added gui.getpixel() which gets any gui.pixel() set pixel colors, and possibly other functions. Added emu.getscreenpixel() which gets the RGB and Palette of any pixel on the screen. 10-may-2010 - ugetab - Added gui.getpixel() which gets any gui.pixel() set pixel colors, and possibly other functions. Added emu.getscreenpixel() which gets the RGB and Palette of any pixel on the screen.
08-may-2010 - ugetab - Added savestate.object() which is savestate.create() with intuitive numbering under windows 08-may-2010 - ugetab - Added savestate.object() which is savestate.create() with intuitive numbering under windows

View File

@ -336,7 +336,7 @@ void FCEU_DrawRecordingStatus(uint8* XBuf)
drawstatus(XBuf-ClipSidesOffset,2,28,0); drawstatus(XBuf-ClipSidesOffset,2,28,0);
hasPlayRecIcon = true; hasPlayRecIcon = true;
} }
else if(FCEUMOV_Mode(MOVIEMODE_PLAY)) else if(FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_FINISHED))
{ {
drawstatus(XBuf-ClipSidesOffset,1,28,0); drawstatus(XBuf-ClipSidesOffset,1,28,0);
hasPlayRecIcon = true; hasPlayRecIcon = true;

View File

@ -1237,14 +1237,14 @@ LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
hfceuxcontext = LoadMenu(fceu_hInstance,"FCEUCONTEXTMENUS"); hfceuxcontext = LoadMenu(fceu_hInstance,"FCEUCONTEXTMENUS");
//If There is a movie loaded in read only //If There is a movie loaded in read only
if (GameInfo && FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD) && movie_readonly) if (GameInfo && FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD|MOVIEMODE_FINISHED) && movie_readonly)
{ {
hfceuxcontextsub = GetSubMenu(hfceuxcontext,0); hfceuxcontextsub = GetSubMenu(hfceuxcontext,0);
whichContext = 0; // Game+Movie+readonly whichContext = 0; // Game+Movie+readonly
} }
//If there is a movie loaded in read+write //If there is a movie loaded in read+write
else if (GameInfo && FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD) && !movie_readonly) else if (GameInfo && FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD|MOVIEMODE_FINISHED) && !movie_readonly)
{ {
hfceuxcontextsub = GetSubMenu(hfceuxcontext,3); hfceuxcontextsub = GetSubMenu(hfceuxcontext,3);
whichContext = 3; // Game+Movie+readwrite whichContext = 3; // Game+Movie+readwrite
@ -2484,7 +2484,7 @@ void FCEUD_AviRecordTo(void)
aviDirectory.append("\\"); //if directory override has no \ then add one aviDirectory.append("\\"); //if directory override has no \ then add one
//if we are playing a movie, construct the filename from the current movie. //if we are playing a movie, construct the filename from the current movie.
if(FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD)) if(FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD)) //adelikat: TOOD: Think about this. I think MOVIEMODE_FINISHED shouldn't not be included here. Am I wrong?
{ {
tempFilename = GetMfn(); //get movie filename tempFilename = GetMfn(); //get movie filename
tempFilename.erase(0,1); //remove dot tempFilename.erase(0,1); //remove dot

View File

@ -611,7 +611,7 @@ void AutoFire(void)
//doesn't get screwed up when loading. //doesn't get screwed up when loading.
if(FCEUMOV_Mode(MOVIEMODE_RECORD | MOVIEMODE_PLAY)) if(FCEUMOV_Mode(MOVIEMODE_RECORD | MOVIEMODE_PLAY))
{ {
rapidAlternator= AutoFirePattern[(AutoFireOffset + FCEUMOV_GetFrame())%AutoFirePatternLength]; rapidAlternator= AutoFirePattern[(AutoFireOffset + FCEUMOV_GetFrame())%AutoFirePatternLength]; //adelikat: TODO: Think through this, MOVIEMODE_FINISHED should not use movie data for auto-fire?
} }
else else
{ {
@ -1032,7 +1032,7 @@ bool FCEU_IsValidUI(EFCEUI ui)
case FCEUI_STOPMOVIE: case FCEUI_STOPMOVIE:
case FCEUI_PLAYFROMBEGINNING: case FCEUI_PLAYFROMBEGINNING:
return FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD); return (FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD|MOVIEMODE_FINISHED));
case FCEUI_STOPAVI: case FCEUI_STOPAVI:
return FCEUI_AviIsRecording(); return FCEUI_AviIsRecording();
@ -1043,7 +1043,7 @@ bool FCEU_IsValidUI(EFCEUI ui)
case FCEUI_RESET: case FCEUI_RESET:
if(!GameInfo) return false; if(!GameInfo) return false;
if(FCEUMOV_Mode(MOVIEMODE_TASEDIT|MOVIEMODE_PLAY)) return false; if(FCEUMOV_Mode(MOVIEMODE_FINISHED|MOVIEMODE_TASEDIT|MOVIEMODE_PLAY)) return false;
break; break;
case FCEUI_POWER: case FCEUI_POWER:

View File

@ -2161,7 +2161,7 @@ static int zapper_read(lua_State *L){
int z = 0; int z = 0;
extern void GetMouseData(uint32 (&md)[3]); //adelikat: shouldn't this be ifdef'ed for Win32? extern void GetMouseData(uint32 (&md)[3]); //adelikat: shouldn't this be ifdef'ed for Win32?
int x,y,click; int x,y,click;
if (/*FCEUMOV_IsPlaying()*/ FCEUMOV_Mode(MOVIEMODE_PLAY)) if (FCEUMOV_Mode(MOVIEMODE_PLAY))
{ {
if (!currFrameCounter) if (!currFrameCounter)
z = 0; z = 0;
@ -2580,10 +2580,12 @@ int emu_emulating(lua_State *L) {
// string movie.mode() // string movie.mode()
// //
// "record", "playback" or nil // "record", "playback", "finished", or nil
int movie_mode(lua_State *L) { int movie_mode(lua_State *L) {
if (FCEUMOV_IsRecording()) if (FCEUMOV_IsRecording())
lua_pushstring(L, "record"); lua_pushstring(L, "record");
else if (FCEUMOV_IsFinished())
lua_pushstring(L, "finished"); //Note: this comes before plaback since playback checks for finished as well
else if (FCEUMOV_IsPlaying()) else if (FCEUMOV_IsPlaying())
lua_pushstring(L, "playback"); lua_pushstring(L, "playback");
else else

View File

@ -721,7 +721,7 @@ void FCEUI_StopMovie()
if(suppressMovieStop) if(suppressMovieStop)
return; return;
if(movieMode == MOVIEMODE_PLAY) if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_FINISHED)
StopPlayback(); StopPlayback();
else if(movieMode == MOVIEMODE_RECORD) else if(movieMode == MOVIEMODE_RECORD)
StopRecording(); StopRecording();
@ -834,7 +834,7 @@ bool FCEUI_LoadMovie(const char *fname, bool _read_only, bool tasedit, int _paus
assert(fname); assert(fname);
//mbg 6/10/08 - we used to call StopMovie here, but that cleared curMovieFilename and gave us crashes... //mbg 6/10/08 - we used to call StopMovie here, but that cleared curMovieFilename and gave us crashes...
if(movieMode == MOVIEMODE_PLAY) if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_FINISHED)
StopPlayback(); StopPlayback();
else if(movieMode == MOVIEMODE_RECORD) else if(movieMode == MOVIEMODE_RECORD)
StopRecording(); StopRecording();
@ -978,6 +978,14 @@ void FCEUI_SaveMovie(const char *fname, EMOVIE_FLAG flags, std::wstring author)
static int _currCommand = 0; static int _currCommand = 0;
// Stop movie playback without closing the movie.
static void FinishPlayback()
{
FCEU_DispMessage("Movie finished playing.");
movieMode = MOVIEMODE_FINISHED;
}
//the main interaction point between the emulator and the movie system. //the main interaction point between the emulator and the movie system.
//either dumps the current joystick state or loads one state from the movie //either dumps the current joystick state or loads one state from the movie
void FCEUMOV_AddInputState() void FCEUMOV_AddInputState()
@ -1009,9 +1017,9 @@ void FCEUMOV_AddInputState()
else if(movieMode == MOVIEMODE_PLAY) else if(movieMode == MOVIEMODE_PLAY)
{ {
//stop when we run out of frames //stop when we run out of frames
if(currFrameCounter == currMovieData.records.size()) if(currFrameCounter == (int)currMovieData.records.size())
{ {
StopPlayback(); FinishPlayback();
} }
else else
{ {
@ -1099,6 +1107,8 @@ void FCEU_DrawMovies(uint8 *XBuf)
sprintf(counterbuf,"%d/%d",currFrameCounter,currMovieData.records.size()); sprintf(counterbuf,"%d/%d",currFrameCounter,currMovieData.records.size());
else if(movieMode == MOVIEMODE_RECORD) else if(movieMode == MOVIEMODE_RECORD)
sprintf(counterbuf,"%d",currMovieData.records.size()); sprintf(counterbuf,"%d",currMovieData.records.size());
else if (movieMode == MOVIEMODE_FINISHED)
sprintf(counterbuf,"%d/%d (finished)",currFrameCounter,currMovieData.records.size());
else else
sprintf(counterbuf,"%d (no movie)",currFrameCounter); sprintf(counterbuf,"%d (no movie)",currFrameCounter);
@ -1127,7 +1137,7 @@ void FCEU_DrawLagCounter(uint8 *XBuf)
int FCEUMOV_WriteState(std::ostream* os) int FCEUMOV_WriteState(std::ostream* os)
{ {
//we are supposed to dump the movie data into the savestate //we are supposed to dump the movie data into the savestate
if(movieMode == MOVIEMODE_RECORD || movieMode == MOVIEMODE_PLAY) if(movieMode == MOVIEMODE_RECORD || movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_FINISHED)
return currMovieData.dump(os, true); return currMovieData.dump(os, true);
else return 0; else return 0;
} }
@ -1151,7 +1161,7 @@ bool FCEUMOV_ReadState(std::istream* is, uint32 size)
is->seekg((uint32)curr+size); is->seekg((uint32)curr+size);
extern bool FCEU_state_loading_old_format; extern bool FCEU_state_loading_old_format;
if(FCEU_state_loading_old_format) { if(FCEU_state_loading_old_format) {
if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_RECORD) { if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_RECORD || movieMode == MOVIEMODE_FINISHED) {
FCEUI_StopMovie(); FCEUI_StopMovie();
FCEU_PrintError("You have tried to use an old savestate while playing a movie. This is unsupported (since the old savestate has old-format movie data in it which can't be converted on the fly)"); FCEU_PrintError("You have tried to use an old savestate while playing a movie. This is unsupported (since the old savestate has old-format movie data in it which can't be converted on the fly)");
} }
@ -1178,7 +1188,7 @@ bool FCEUMOV_ReadState(std::istream* is, uint32 size)
// then, we must discard this movie and just load the savestate // then, we must discard this movie and just load the savestate
if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_RECORD) if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_RECORD || movieMode == MOVIEMODE_FINISHED)
{ {
//handle moviefile mismatch //handle moviefile mismatch
if(tempMovieData.guid != currMovieData.guid) if(tempMovieData.guid != currMovieData.guid)
@ -1203,8 +1213,10 @@ bool FCEUMOV_ReadState(std::istream* is, uint32 size)
//if the frame counter is longer than our current movie, then error //if the frame counter is longer than our current movie, then error
if(currFrameCounter > (int)currMovieData.records.size()) if(currFrameCounter > (int)currMovieData.records.size())
{ {
FCEU_PrintError("Savestate is from a frame (%d) after the final frame in the movie (%d). This is not permitted.", currFrameCounter, currMovieData.records.size()-1); FinishPlayback();
return false; //TODO: turn frame counter to red to get attention
//FCEU_PrintError("Savestate is from a frame (%d) after the final frame in the movie (%d). This is not permitted.", currFrameCounter, currMovieData.records.size()-1);
//return false;
} }
movieMode = MOVIEMODE_PLAY; movieMode = MOVIEMODE_PLAY;
} }

View File

@ -58,7 +58,8 @@ enum EMOVIEMODE
MOVIEMODE_INACTIVE = 1, MOVIEMODE_INACTIVE = 1,
MOVIEMODE_RECORD = 2, MOVIEMODE_RECORD = 2,
MOVIEMODE_PLAY = 4, MOVIEMODE_PLAY = 4,
MOVIEMODE_TASEDIT = 8 MOVIEMODE_TASEDIT = 8,
MOVIEMODE_FINISHED = 16
}; };
enum EMOVIECMD enum EMOVIECMD
@ -72,8 +73,9 @@ enum EMOVIECMD
EMOVIEMODE FCEUMOV_Mode(); EMOVIEMODE FCEUMOV_Mode();
bool FCEUMOV_Mode(EMOVIEMODE modemask); bool FCEUMOV_Mode(EMOVIEMODE modemask);
bool FCEUMOV_Mode(int modemask); bool FCEUMOV_Mode(int modemask);
inline bool FCEUMOV_IsPlaying() { return FCEUMOV_Mode(MOVIEMODE_PLAY); } inline bool FCEUMOV_IsPlaying() { return (FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_FINISHED)); }
inline bool FCEUMOV_IsRecording() { return FCEUMOV_Mode(MOVIEMODE_RECORD); } inline bool FCEUMOV_IsRecording() { return FCEUMOV_Mode(MOVIEMODE_RECORD); }
inline bool FCEUMOV_IsFinished() { return FCEUMOV_Mode(MOVIEMODE_FINISHED);}
bool FCEUMOV_ShouldPause(void); bool FCEUMOV_ShouldPause(void);
int FCEUMOV_GetFrame(void); int FCEUMOV_GetFrame(void);

View File

@ -284,7 +284,7 @@ static bool ReadStateChunks(std::istream* is, int32 totalsize)
read_snd=1; read_snd=1;
break; break;
case 6: case 6:
if(FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD)) if(FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD|MOVIEMODE_FINISHED))
{ {
if(!ReadStateChunk(is,FCEUMOV_STATEINFO,size)) ret=false; if(!ReadStateChunk(is,FCEUMOV_STATEINFO,size)) ret=false;
} }
@ -372,7 +372,7 @@ bool FCEUSS_SaveMS(std::ostream* outstream, int compressionLevel)
totalsize+=WriteStateChunk(os,31,FCEU_NEWPPU_STATEINFO); totalsize+=WriteStateChunk(os,31,FCEU_NEWPPU_STATEINFO);
totalsize+=WriteStateChunk(os,4,FCEUCTRL_STATEINFO); totalsize+=WriteStateChunk(os,4,FCEUCTRL_STATEINFO);
totalsize+=WriteStateChunk(os,5,FCEUSND_STATEINFO); totalsize+=WriteStateChunk(os,5,FCEUSND_STATEINFO);
if(FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD)) if(FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD|MOVIEMODE_FINISHED))
{ {
totalsize+=WriteStateChunk(os,6,FCEUMOV_STATEINFO); totalsize+=WriteStateChunk(os,6,FCEUMOV_STATEINFO);
@ -776,7 +776,7 @@ bool FCEUSS_Load(const char *fname)
//Update input display if movie is loaded //Update input display if movie is loaded
extern uint32 cur_input_display; extern uint32 cur_input_display;
extern uint8 FCEU_GetJoyJoy(void); extern uint8 FCEU_GetJoyJoy(void);
if (FCEUMOV_Mode(MOVIEMODE_RECORD) || FCEUMOV_Mode(MOVIEMODE_PLAY)) //adelikat: just doing GetJoyJoy regardless should work, but I just felt conceptually movies should be relying on movie data. There might be some fringe cases where this is necessary. if (FCEUMOV_Mode(MOVIEMODE_RECORD|MOVIEMODE_PLAY|MOVIEMODE_FINISHED)) //adelikat: just doing GetJoyJoy regardless should work, but I just felt conceptually movies should be relying on movie data. There might be some fringe cases where this is necessary.
memcpy(&cur_input_display,currMovieData.records[currFrameCounter-1].joysticks.data,4); memcpy(&cur_input_display,currMovieData.records[currFrameCounter-1].joysticks.data,4);
else else
cur_input_display = FCEU_GetJoyJoy(); cur_input_display = FCEU_GetJoyJoy();