2010-08-29 23:15:42 +02:00
# include "emufile.h"
2009-10-22 04:44:03 +02:00
# include "version.h"
2009-07-17 19:27:04 +02:00
# include "types.h"
# include "utils/endian.h"
# include "palette.h"
# include "input.h"
# include "fceu.h"
2009-09-15 10:20:48 +02:00
# include "netplay.h"
2009-07-17 19:27:04 +02:00
# include "driver.h"
# include "state.h"
# include "file.h"
# include "video.h"
# include "movie.h"
2018-08-13 17:04:20 +02:00
# include "cart.h"
2009-09-15 10:20:48 +02:00
# include "fds.h"
2016-09-18 05:43:24 +02:00
# include "vsuni.h"
2009-07-17 19:27:04 +02:00
# ifdef _S9XLUA_H
# include "fceulua.h"
# endif
# include "utils/guid.h"
# include "utils/memory.h"
# include "utils/xstring.h"
# include <sstream>
# ifdef CREATE_AVI
# include "drivers/videolog/nesvideos-piece.h"
# endif
# ifdef WIN32
# include <windows.h>
2010-08-29 23:15:42 +02:00
# include "./drivers/win/common.h"
# include "./drivers/win/window.h"
2009-07-18 00:54:58 +02:00
extern void AddRecentMovieFile ( const char * filename ) ;
2012-12-14 18:18:20 +01:00
# include "./drivers/win/taseditor.h"
2016-09-18 05:43:24 +02:00
extern bool mustEngageTaseditor ;
2009-07-17 19:27:04 +02:00
# endif
2018-08-13 17:04:20 +02:00
extern int RAMInitOption ;
extern int RAMInitSeed ;
2016-09-18 05:43:24 +02:00
# include <cstdio>
# include <cstdlib>
# include <cstring>
# include <cassert>
# include <iomanip>
# include <fstream>
# include <climits>
# include <cstdarg>
# include <zlib.h>
2009-07-17 19:27:04 +02:00
using namespace std ;
2012-12-14 18:18:20 +01:00
# define MOVIE_VERSION 3
2009-07-17 19:27:04 +02:00
extern char FileBase [ ] ;
extern bool AutoSS ; //Declared in fceu.cpp, keeps track if a auto-savestate has been made
std : : vector < int > subtitleFrames ; //Frame numbers for subtitle messages
std : : vector < string > subtitleMessages ; //Messages of subtitles
bool subtitlesOnAVI = false ;
bool autoMovieBackup = false ; //Toggle that determines if movies should be backed up automatically before altering them
bool freshMovie = false ; //True when a movie loads, false when movie is altered. Used to determine if a movie has been altered since opening
2010-08-29 23:15:42 +02:00
bool movieFromPoweron = true ;
2009-07-17 19:27:04 +02:00
2012-06-27 01:14:39 +02:00
static int _currCommand = 0 ;
2009-07-17 19:27:04 +02:00
// Function declarations------------------------
//TODO - remove the synchack stuff from the replay gui and require it to be put into the fm2 file
//which the user would have already converted from fcm
//also cleanup the whole emulator version bullshit in replay. we dont support that old stuff anymore
//todo - better error handling for the compressed savestate
//todo - consider a MemoryBackedFile class..
//..a lot of work.. instead lets just read back from the current fcm
//todo - could we, given a field size, over-read from an inputstream and then parse out an integer?
//that would be faster than several reads, perhaps.
//sometimes we accidentally produce movie stop signals while we're trying to do other things with movies..
bool suppressMovieStop = false ;
//----movie engine main state
EMOVIEMODE movieMode = MOVIEMODE_INACTIVE ;
//this should not be set unless we are in MOVIEMODE_RECORD!
//FILE* fpRecordingMovie = 0;
2010-08-29 23:15:42 +02:00
EMUFILE * osRecordingMovie = NULL ;
2009-07-17 19:27:04 +02:00
int currFrameCounter ;
uint32 cur_input_display = 0 ;
int pauseframe = - 1 ;
bool movie_readonly = true ;
int input_display = 0 ;
int frame_display = 0 ;
2012-01-09 02:59:06 +01:00
int rerecord_display = 0 ;
2010-08-29 23:15:42 +02:00
bool fullSaveStateLoads = false ; //Option for loading a savestates full contents in read+write mode instead of up to the frame count in the savestate (useful as a recovery option)
2009-07-17 19:27:04 +02:00
SFORMAT FCEUMOV_STATEINFO [ ] = {
{ & currFrameCounter , 4 | FCEUSTATE_RLSB , " FCNT " } ,
{ 0 }
} ;
char curMovieFilename [ 512 ] = { 0 } ;
MovieData currMovieData ;
2010-08-29 23:15:42 +02:00
MovieData defaultMovieData ;
2016-09-18 05:43:24 +02:00
int currRerecordCount ; // Keep the global value
2009-07-17 19:27:04 +02:00
2012-12-14 18:18:20 +01:00
char lagcounterbuf [ 32 ] = { 0 } ;
2010-08-29 23:15:42 +02:00
# ifdef GEKKO
void MovieData : : clearRecordRange ( int start , int len ) { }
2012-12-14 18:18:20 +01:00
void MovieData : : eraseRecords ( int at , int frames ) { }
2010-08-29 23:15:42 +02:00
void MovieData : : insertEmpty ( int at , int frames ) { }
2012-01-09 02:59:06 +01:00
void MovieData : : cloneRegion ( int at , int frames ) { }
2010-08-29 23:15:42 +02:00
MovieRecord : : MovieRecord ( ) { }
void MovieRecord : : clear ( ) { }
bool MovieRecord : : Compare ( MovieRecord & compareRec ) { return false ; }
2012-01-09 02:59:06 +01:00
void MovieRecord : : Clone ( MovieRecord & sourceRec ) { }
2010-08-29 23:15:42 +02:00
void MovieRecord : : dumpJoy ( EMUFILE * os , uint8 joystate ) { }
void MovieRecord : : parseJoy ( EMUFILE * is , uint8 & joystate ) { }
void MovieRecord : : parse ( MovieData * md , EMUFILE * is ) { }
bool MovieRecord : : parseBinary ( MovieData * md , EMUFILE * is ) { return false ; }
void MovieRecord : : dumpBinary ( MovieData * md , EMUFILE * os , int index ) { }
void MovieRecord : : dump ( MovieData * md , EMUFILE * os , int index ) { }
MovieData : : MovieData ( ) { }
void MovieData : : truncateAt ( int frame ) { }
void MovieData : : installValue ( std : : string & key , std : : string & val ) { }
int MovieData : : dump ( EMUFILE * os , bool binary ) { return 0 ; }
int FCEUMOV_GetFrame ( void ) { return 0 ; }
int FCEUI_GetLagCount ( void ) { return 0 ; }
bool FCEUI_GetLagged ( void ) { return false ; }
2012-12-14 18:18:20 +01:00
void FCEUI_SetLagFlag ( bool value ) { }
2010-08-29 23:15:42 +02:00
bool FCEUMOV_ShouldPause ( void ) { return false ; }
EMOVIEMODE FCEUMOV_Mode ( ) { return movieMode ; }
bool FCEUMOV_Mode ( EMOVIEMODE modemask ) { return false ; }
bool FCEUMOV_Mode ( int modemask ) { return false ; }
bool LoadFM2 ( MovieData & movieData , EMUFILE * fp , int size , bool stopAfterHeader ) { return false ; }
void FCEUI_StopMovie ( ) { }
void poweron ( bool shouldDisableBatteryLoading ) { }
2012-01-09 02:59:06 +01:00
void CreateCleanMovie ( ) { }
2010-08-29 23:15:42 +02:00
bool FCEUMOV_FromPoweron ( ) { return false ; }
bool MovieData : : loadSavestateFrom ( std : : vector < uint8 > * buf ) { return false ; }
void MovieData : : dumpSavestateTo ( std : : vector < uint8 > * buf , int compressionLevel ) { }
bool FCEUI_LoadMovie ( const char * fname , bool _read_only , bool tasedit , int _pauseframe ) { return false ; }
void FCEUI_SaveMovie ( const char * fname , EMOVIE_FLAG flags , std : : wstring author ) { }
void FCEUMOV_AddInputState ( ) { }
void FCEUMOV_AddCommand ( int cmd ) { }
void FCEU_DrawMovies ( uint8 * XBuf ) { }
void FCEU_DrawLagCounter ( uint8 * XBuf ) { }
int FCEUMOV_WriteState ( EMUFILE * os ) { return 0 ; }
2012-12-14 18:18:20 +01:00
int CheckTimelines ( MovieData & stateMovie , MovieData & currMovie ) { return - 1 ; }
2010-08-29 23:15:42 +02:00
bool FCEUMOV_ReadState ( EMUFILE * is , uint32 size ) { return false ; }
void FCEUMOV_PreLoad ( void ) { }
bool FCEUMOV_PostLoad ( void ) { return false ; }
void FCEUI_MovieToggleFrameDisplay ( void ) { }
2012-01-09 02:59:06 +01:00
void FCEUI_MovieToggleRerecordDisplay ( ) { }
2010-08-29 23:15:42 +02:00
void FCEUI_ToggleInputDisplay ( void ) { }
int FCEUI_GetMovieLength ( ) { return 0 ; }
int FCEUI_GetMovieRerecordCount ( ) { return 0 ; }
bool FCEUI_GetMovieToggleReadOnly ( ) { return false ; }
void FCEUI_SetMovieToggleReadOnly ( bool which ) { }
void FCEUI_MovieToggleReadOnly ( ) { }
void FCEUI_MoviePlayFromBeginning ( void ) { }
string FCEUI_GetMovieName ( void ) { return curMovieFilename ; }
bool FCEUI_MovieGetInfo ( FCEUFILE * fp , MOVIE_INFO & info , bool skipFrameCount ) { return false ; }
void LoadSubtitles ( MovieData & moviedata ) { }
void ProcessSubtitles ( void ) { }
void FCEU_DisplaySubtitles ( char * format , . . . ) { }
void FCEUI_CreateMovieFile ( std : : string fn ) { }
void FCEUI_MakeBackupMovie ( bool dispMessage ) { }
# else
2009-07-17 19:27:04 +02:00
void MovieData : : clearRecordRange ( int start , int len )
{
for ( int i = 0 ; i < len ; i + + )
2012-01-09 02:59:06 +01:00
{
2009-07-17 19:27:04 +02:00
records [ i + start ] . clear ( ) ;
2012-01-09 02:59:06 +01:00
}
2009-07-17 19:27:04 +02:00
}
2012-12-14 18:18:20 +01:00
void MovieData : : eraseRecords ( int at , int frames )
{
if ( at < ( int ) records . size ( ) )
{
if ( frames = = 1 )
{
// erase 1 frame
records . erase ( records . begin ( ) + at ) ;
} else
{
// erase many frames
if ( at + frames > ( int ) records . size ( ) )
frames = ( int ) records . size ( ) - at ;
records . erase ( records . begin ( ) + at , records . begin ( ) + ( at + frames ) ) ;
}
}
}
2009-07-17 19:27:04 +02:00
void MovieData : : insertEmpty ( int at , int frames )
{
2012-12-14 18:18:20 +01:00
if ( at = = - 1 )
2009-07-17 19:27:04 +02:00
{
2012-12-14 18:18:20 +01:00
records . resize ( records . size ( ) + frames ) ;
2012-01-09 02:59:06 +01:00
} else
2009-07-17 19:27:04 +02:00
{
2012-01-09 02:59:06 +01:00
records . insert ( records . begin ( ) + at , frames , MovieRecord ( ) ) ;
2009-07-17 19:27:04 +02:00
}
}
2012-01-09 02:59:06 +01:00
void MovieData : : cloneRegion ( int at , int frames )
2009-07-17 19:27:04 +02:00
{
2012-12-14 18:18:20 +01:00
if ( at < 0 ) return ;
2012-01-09 02:59:06 +01:00
records . insert ( records . begin ( ) + at , frames , MovieRecord ( ) ) ;
for ( int i = 0 ; i < frames ; i + + )
2012-12-14 18:18:20 +01:00
records [ i + at ] . Clone ( records [ i + at + frames ] ) ;
2010-08-29 23:15:42 +02:00
}
2012-12-14 18:18:20 +01:00
// ----------------------------------------------------------------------------
2010-08-29 23:15:42 +02:00
MovieRecord : : MovieRecord ( )
{
commands = 0 ;
2012-12-14 18:18:20 +01:00
* ( uint32 * ) & joysticks = 0 ;
memset ( zappers , 0 , sizeof ( zappers ) ) ;
2009-07-17 19:27:04 +02:00
}
void MovieRecord : : clear ( )
2012-12-14 18:18:20 +01:00
{
2009-07-17 19:27:04 +02:00
commands = 0 ;
* ( uint32 * ) & joysticks = 0 ;
2012-12-14 18:18:20 +01:00
memset ( zappers , 0 , sizeof ( zappers ) ) ;
2010-08-29 23:15:42 +02:00
}
bool MovieRecord : : Compare ( MovieRecord & compareRec )
{
//Joysticks, Zappers, and commands
2012-12-14 18:18:20 +01:00
if ( this - > commands ! = compareRec . commands )
return false ;
if ( ( * ( uint32 * ) & ( this - > joysticks ) ) ! = ( * ( uint32 * ) & ( compareRec . joysticks ) ) )
return false ;
if ( memcmp ( this - > zappers , compareRec . zappers , sizeof ( zappers ) ) )
return false ;
/*
if ( this - > joysticks ! = compareRec . joysticks )
2010-08-29 23:15:42 +02:00
return false ;
//if new commands are ever recordable, they need to be added here if we go with this method
if ( this - > command_reset ( ) ! = compareRec . command_reset ( ) ) return false ;
if ( this - > command_power ( ) ! = compareRec . command_power ( ) ) return false ;
if ( this - > command_fds_insert ( ) ! = compareRec . command_fds_insert ( ) ) return false ;
if ( this - > command_fds_select ( ) ! = compareRec . command_fds_select ( ) ) return false ;
2012-12-14 18:18:20 +01:00
2010-08-29 23:15:42 +02:00
if ( this - > zappers [ 0 ] . x ! = compareRec . zappers [ 0 ] . x ) return false ;
if ( this - > zappers [ 0 ] . y ! = compareRec . zappers [ 0 ] . y ) return false ;
if ( this - > zappers [ 0 ] . zaphit ! = compareRec . zappers [ 0 ] . zaphit ) return false ;
if ( this - > zappers [ 0 ] . b ! = compareRec . zappers [ 0 ] . b ) return false ;
if ( this - > zappers [ 0 ] . bogo ! = compareRec . zappers [ 0 ] . bogo ) return false ;
if ( this - > zappers [ 1 ] . x ! = compareRec . zappers [ 1 ] . x ) return false ;
if ( this - > zappers [ 1 ] . y ! = compareRec . zappers [ 1 ] . y ) return false ;
if ( this - > zappers [ 1 ] . zaphit ! = compareRec . zappers [ 1 ] . zaphit ) return false ;
if ( this - > zappers [ 1 ] . b ! = compareRec . zappers [ 1 ] . b ) return false ;
if ( this - > zappers [ 1 ] . bogo ! = compareRec . zappers [ 1 ] . bogo ) return false ;
2012-12-14 18:18:20 +01:00
*/
2010-08-29 23:15:42 +02:00
return true ;
2009-07-17 19:27:04 +02:00
}
2012-01-09 02:59:06 +01:00
void MovieRecord : : Clone ( MovieRecord & sourceRec )
{
2012-12-14 18:18:20 +01:00
* ( uint32 * ) & joysticks = * ( uint32 * ) ( & ( sourceRec . joysticks ) ) ;
memcpy ( this - > zappers , sourceRec . zappers , sizeof ( zappers ) ) ;
2012-01-09 02:59:06 +01:00
this - > commands = sourceRec . commands ;
}
2009-07-17 19:27:04 +02:00
const char MovieRecord : : mnemonics [ 8 ] = { ' A ' , ' B ' , ' S ' , ' T ' , ' U ' , ' D ' , ' L ' , ' R ' } ;
2012-12-14 18:18:20 +01:00
2010-08-29 23:15:42 +02:00
void MovieRecord : : dumpJoy ( EMUFILE * os , uint8 joystate )
2009-07-17 19:27:04 +02:00
{
//these are mnemonics for each joystick bit.
//since we usually use the regular joypad, these will be more helpful.
//but any character other than ' ' or '.' should count as a set bit
//maybe other input types will need to be encoded another way..
for ( int bit = 7 ; bit > = 0 ; bit - - )
{
int bitmask = ( 1 < < bit ) ;
char mnemonic = mnemonics [ bit ] ;
//if the bit is set write the mnemonic
if ( joystate & bitmask )
2010-08-29 23:15:42 +02:00
os - > fwrite ( & mnemonic , 1 ) ;
2009-07-17 19:27:04 +02:00
else //otherwise write an unset bit
2010-08-29 23:15:42 +02:00
write8le ( ' . ' , os ) ;
2009-07-17 19:27:04 +02:00
}
}
2010-08-29 23:15:42 +02:00
void MovieRecord : : parseJoy ( EMUFILE * is , uint8 & joystate )
2009-07-17 19:27:04 +02:00
{
char buf [ 8 ] ;
2010-08-29 23:15:42 +02:00
is - > fread ( buf , 8 ) ;
2009-07-17 19:27:04 +02:00
joystate = 0 ;
for ( int i = 0 ; i < 8 ; i + + )
{
joystate < < = 1 ;
joystate | = ( ( buf [ i ] = = ' . ' | | buf [ i ] = = ' ' ) ? 0 : 1 ) ;
}
}
2010-08-29 23:15:42 +02:00
void MovieRecord : : parse ( MovieData * md , EMUFILE * is )
2009-07-17 19:27:04 +02:00
{
//by the time we get in here, the initial pipe has already been extracted
//extract the commands
commands = uint32DecFromIstream ( is ) ;
//*is >> commands;
2010-08-29 23:15:42 +02:00
is - > fgetc ( ) ; //eat the pipe
2009-07-17 19:27:04 +02:00
//a special case: if fourscore is enabled, parse four gamepads
if ( md - > fourscore )
{
2010-08-29 23:15:42 +02:00
parseJoy ( is , joysticks [ 0 ] ) ; is - > fgetc ( ) ; //eat the pipe
parseJoy ( is , joysticks [ 1 ] ) ; is - > fgetc ( ) ; //eat the pipe
parseJoy ( is , joysticks [ 2 ] ) ; is - > fgetc ( ) ; //eat the pipe
parseJoy ( is , joysticks [ 3 ] ) ; is - > fgetc ( ) ; //eat the pipe
2009-07-17 19:27:04 +02:00
}
else
{
for ( int port = 0 ; port < 2 ; port + + )
{
if ( md - > ports [ port ] = = SI_GAMEPAD )
parseJoy ( is , joysticks [ port ] ) ;
else if ( md - > ports [ port ] = = SI_ZAPPER )
{
zappers [ port ] . x = uint32DecFromIstream ( is ) ;
zappers [ port ] . y = uint32DecFromIstream ( is ) ;
zappers [ port ] . b = uint32DecFromIstream ( is ) ;
zappers [ port ] . bogo = uint32DecFromIstream ( is ) ;
zappers [ port ] . zaphit = uint64DecFromIstream ( is ) ;
}
2012-12-14 18:18:20 +01:00
2010-08-29 23:15:42 +02:00
is - > fgetc ( ) ; //eat the pipe
2009-07-17 19:27:04 +02:00
}
}
//(no fcexp data is logged right now)
2010-08-29 23:15:42 +02:00
is - > fgetc ( ) ; //eat the pipe
2009-07-17 19:27:04 +02:00
//should be left at a newline
}
2010-08-29 23:15:42 +02:00
bool MovieRecord : : parseBinary ( MovieData * md , EMUFILE * is )
2009-07-17 19:27:04 +02:00
{
2010-08-29 23:15:42 +02:00
commands = ( uint8 ) is - > fgetc ( ) ;
2009-07-17 19:27:04 +02:00
//check for eof
2010-08-29 23:15:42 +02:00
if ( is - > eof ( ) ) return false ;
2009-07-17 19:27:04 +02:00
if ( md - > fourscore )
{
2010-08-29 23:15:42 +02:00
is - > fread ( ( char * ) & joysticks , 4 ) ;
2009-07-17 19:27:04 +02:00
}
else
{
for ( int port = 0 ; port < 2 ; port + + )
{
if ( md - > ports [ port ] = = SI_GAMEPAD )
2010-08-29 23:15:42 +02:00
joysticks [ port ] = ( uint8 ) is - > fgetc ( ) ;
2009-07-17 19:27:04 +02:00
else if ( md - > ports [ port ] = = SI_ZAPPER )
{
2010-08-29 23:15:42 +02:00
zappers [ port ] . x = ( uint8 ) is - > fgetc ( ) ;
zappers [ port ] . y = ( uint8 ) is - > fgetc ( ) ;
zappers [ port ] . b = ( uint8 ) is - > fgetc ( ) ;
zappers [ port ] . bogo = ( uint8 ) is - > fgetc ( ) ;
2009-07-17 19:27:04 +02:00
read64le ( & zappers [ port ] . zaphit , is ) ;
}
}
}
2010-08-29 23:15:42 +02:00
2009-07-17 19:27:04 +02:00
return true ;
}
2010-08-29 23:15:42 +02:00
void MovieRecord : : dumpBinary ( MovieData * md , EMUFILE * os , int index )
2009-07-17 19:27:04 +02:00
{
2010-08-29 23:15:42 +02:00
write8le ( commands , os ) ;
2009-07-17 19:27:04 +02:00
if ( md - > fourscore )
{
2010-08-29 23:15:42 +02:00
for ( int i = 0 ; i < 4 ; i + + )
os - > fwrite ( & joysticks [ i ] , sizeof ( joysticks [ i ] ) ) ;
2009-07-17 19:27:04 +02:00
}
else
{
for ( int port = 0 ; port < 2 ; port + + )
{
if ( md - > ports [ port ] = = SI_GAMEPAD )
2010-08-29 23:15:42 +02:00
os - > fwrite ( & joysticks [ port ] , sizeof ( joysticks [ port ] ) ) ;
2009-07-17 19:27:04 +02:00
else if ( md - > ports [ port ] = = SI_ZAPPER )
{
2010-08-29 23:15:42 +02:00
write8le ( zappers [ port ] . x , os ) ;
write8le ( zappers [ port ] . y , os ) ;
write8le ( zappers [ port ] . b , os ) ;
write8le ( zappers [ port ] . bogo , os ) ;
2009-07-17 19:27:04 +02:00
write64le ( zappers [ port ] . zaphit , os ) ;
}
}
}
}
2010-08-29 23:15:42 +02:00
void MovieRecord : : dump ( MovieData * md , EMUFILE * os , int index )
2009-07-17 19:27:04 +02:00
{
2016-09-18 05:43:24 +02:00
// dump the commands
2009-07-17 19:27:04 +02:00
//*os << '|' << setw(1) << (int)commands;
2010-08-29 23:15:42 +02:00
os - > fputc ( ' | ' ) ;
2016-09-18 05:43:24 +02:00
putdec < uint8 , 3 , false > ( os , commands ) ; // "variable length decimal integer"
2009-07-17 19:27:04 +02:00
//a special case: if fourscore is enabled, dump four gamepads
if ( md - > fourscore )
{
2010-08-29 23:15:42 +02:00
os - > fputc ( ' | ' ) ;
dumpJoy ( os , joysticks [ 0 ] ) ; os - > fputc ( ' | ' ) ;
dumpJoy ( os , joysticks [ 1 ] ) ; os - > fputc ( ' | ' ) ;
dumpJoy ( os , joysticks [ 2 ] ) ; os - > fputc ( ' | ' ) ;
dumpJoy ( os , joysticks [ 3 ] ) ; os - > fputc ( ' | ' ) ;
2009-07-17 19:27:04 +02:00
}
else
{
for ( int port = 0 ; port < 2 ; port + + )
{
2010-08-29 23:15:42 +02:00
os - > fputc ( ' | ' ) ;
2009-07-17 19:27:04 +02:00
if ( md - > ports [ port ] = = SI_GAMEPAD )
dumpJoy ( os , joysticks [ port ] ) ;
else if ( md - > ports [ port ] = = SI_ZAPPER )
{
2010-08-29 23:15:42 +02:00
putdec < uint8 , 3 , true > ( os , zappers [ port ] . x ) ; os - > fputc ( ' ' ) ;
putdec < uint8 , 3 , true > ( os , zappers [ port ] . y ) ; os - > fputc ( ' ' ) ;
putdec < uint8 , 1 , true > ( os , zappers [ port ] . b ) ; os - > fputc ( ' ' ) ;
putdec < uint8 , 1 , true > ( os , zappers [ port ] . bogo ) ; os - > fputc ( ' ' ) ;
2009-07-17 19:27:04 +02:00
putdec < uint64 , 20 , false > ( os , zappers [ port ] . zaphit ) ;
}
}
2010-08-29 23:15:42 +02:00
os - > fputc ( ' | ' ) ;
2009-07-17 19:27:04 +02:00
}
2012-12-14 18:18:20 +01:00
2009-07-17 19:27:04 +02:00
//(no fcexp data is logged right now)
2010-08-29 23:15:42 +02:00
os - > fputc ( ' | ' ) ;
2009-07-17 19:27:04 +02:00
//each frame is on a new line
2010-08-29 23:15:42 +02:00
os - > fputc ( ' \n ' ) ;
2009-07-17 19:27:04 +02:00
}
MovieData : : MovieData ( )
: version ( MOVIE_VERSION )
, emuVersion ( FCEU_VERSION_NUMERIC )
2018-08-13 17:04:20 +02:00
, fds ( false )
2009-07-17 19:27:04 +02:00
, palFlag ( false )
2010-08-29 23:15:42 +02:00
, PPUflag ( false )
2018-08-13 17:04:20 +02:00
, RAMInitOption ( 0 )
, RAMInitSeed ( 0 )
2009-11-10 23:13:41 +01:00
, rerecordCount ( 0 )
2009-07-17 19:27:04 +02:00
, binaryFlag ( false )
2012-01-09 02:59:06 +01:00
, loadFrameCount ( - 1 )
2010-08-29 23:15:42 +02:00
, microphone ( false )
2009-07-17 19:27:04 +02:00
{
memset ( & romChecksum , 0 , sizeof ( MD5DATA ) ) ;
}
void MovieData : : truncateAt ( int frame )
{
records . resize ( frame ) ;
}
void MovieData : : installValue ( std : : string & key , std : : string & val )
{
//todo - use another config system, or drive this from a little data structure. because this is gross
2010-03-11 07:30:34 +01:00
if ( key = = " FDS " )
installInt ( val , fds ) ;
2010-08-29 23:15:42 +02:00
else if ( key = = " NewPPU " )
installBool ( val , PPUflag ) ;
2018-08-13 17:04:20 +02:00
else if ( key = = " RAMInitOption " )
installInt ( val , RAMInitOption ) ;
else if ( key = = " RAMInitSeed " )
installInt ( val , RAMInitSeed ) ;
2010-03-11 07:30:34 +01:00
else if ( key = = " version " )
2009-07-17 19:27:04 +02:00
installInt ( val , version ) ;
else if ( key = = " emuVersion " )
installInt ( val , emuVersion ) ;
else if ( key = = " rerecordCount " )
installInt ( val , rerecordCount ) ;
else if ( key = = " palFlag " )
installBool ( val , palFlag ) ;
else if ( key = = " romFilename " )
romFilename = val ;
else if ( key = = " romChecksum " )
StringToBytes ( val , & romChecksum , MD5DATA : : size ) ;
else if ( key = = " guid " )
guid = FCEU_Guid : : fromString ( val ) ;
else if ( key = = " fourscore " )
installBool ( val , fourscore ) ;
2010-08-29 23:15:42 +02:00
else if ( key = = " microphone " )
installBool ( val , microphone ) ;
2009-07-17 19:27:04 +02:00
else if ( key = = " port0 " )
installInt ( val , ports [ 0 ] ) ;
else if ( key = = " port1 " )
installInt ( val , ports [ 1 ] ) ;
else if ( key = = " port2 " )
installInt ( val , ports [ 2 ] ) ;
else if ( key = = " binary " )
installBool ( val , binaryFlag ) ;
else if ( key = = " comment " )
comments . push_back ( mbstowcs ( val ) ) ;
else if ( key = = " subtitle " )
subtitles . push_back ( val ) ; //mbstowcs(val));
else if ( key = = " savestate " )
{
int len = Base64StringToBytesLength ( val ) ;
if ( len = = - 1 ) len = HexStringToBytesLength ( val ) ; // wasn't base64, try hex
if ( len > = 1 )
{
savestate . resize ( len ) ;
StringToBytes ( val , & savestate [ 0 ] , len ) ; // decodes either base64 or hex
}
}
2018-08-13 17:04:20 +02:00
else if ( key = = " saveram " )
{
int len = Base64StringToBytesLength ( val ) ;
if ( len = = - 1 ) len = HexStringToBytesLength ( val ) ; // wasn't base64, try hex
if ( len > = 1 )
{
saveram . resize ( len ) ;
StringToBytes ( val , & saveram [ 0 ] , len ) ; // decodes either base64 or hex
}
}
2009-12-23 01:15:33 +01:00
else if ( key = = " length " )
{
installInt ( val , loadFrameCount ) ;
}
2009-07-17 19:27:04 +02:00
}
2010-08-29 23:15:42 +02:00
int MovieData : : dump ( EMUFILE * os , bool binary )
2009-07-17 19:27:04 +02:00
{
2010-08-29 23:15:42 +02:00
int start = os - > ftell ( ) ;
os - > fprintf ( " version %d \n " , version ) ;
os - > fprintf ( " emuVersion %d \n " , emuVersion ) ;
os - > fprintf ( " rerecordCount %d \n " , rerecordCount ) ;
os - > fprintf ( " palFlag %d \n " , ( palFlag ? 1 : 0 ) ) ;
os - > fprintf ( " romFilename %s \n " , romFilename . c_str ( ) ) ;
os - > fprintf ( " romChecksum %s \n " , BytesToString ( romChecksum . data , MD5DATA : : size ) . c_str ( ) ) ;
os - > fprintf ( " guid %s \n " , guid . toString ( ) . c_str ( ) ) ;
os - > fprintf ( " fourscore %d \n " , ( fourscore ? 1 : 0 ) ) ;
os - > fprintf ( " microphone %d \n " , ( microphone ? 1 : 0 ) ) ;
os - > fprintf ( " port0 %d \n " , ports [ 0 ] ) ;
os - > fprintf ( " port1 %d \n " , ports [ 1 ] ) ;
os - > fprintf ( " port2 %d \n " , ports [ 2 ] ) ;
2012-01-09 02:59:06 +01:00
os - > fprintf ( " FDS %d \n " , fds ? 1 : 0 ) ;
os - > fprintf ( " NewPPU %d \n " , PPUflag ? 1 : 0 ) ;
2018-08-13 17:04:20 +02:00
os - > fprintf ( " RAMInitOption %d \n " , RAMInitOption ) ;
os - > fprintf ( " RAMInitSeed %d \n " , RAMInitSeed ) ;
2009-07-17 19:27:04 +02:00
for ( uint32 i = 0 ; i < comments . size ( ) ; i + + )
2010-08-29 23:15:42 +02:00
os - > fprintf ( " comment %s \n " , wcstombs ( comments [ i ] ) . c_str ( ) ) ;
2009-07-17 19:27:04 +02:00
for ( uint32 i = 0 ; i < subtitles . size ( ) ; i + + )
2010-08-29 23:15:42 +02:00
os - > fprintf ( " subtitle %s \n " , subtitles [ i ] . c_str ( ) ) ;
2012-12-14 18:18:20 +01:00
2009-07-17 19:27:04 +02:00
if ( binary )
2010-08-29 23:15:42 +02:00
os - > fprintf ( " binary 1 \n " ) ;
2012-12-14 18:18:20 +01:00
2012-01-09 02:59:06 +01:00
if ( savestate . size ( ) )
2010-08-29 23:15:42 +02:00
os - > fprintf ( " savestate %s \n " , BytesToString ( & savestate [ 0 ] , savestate . size ( ) ) . c_str ( ) ) ;
2009-12-23 01:15:33 +01:00
2018-08-13 17:04:20 +02:00
if ( saveram . size ( ) )
os - > fprintf ( " saveram %s \n " , BytesToString ( & saveram [ 0 ] , saveram . size ( ) ) . c_str ( ) ) ;
2012-01-09 02:59:06 +01:00
if ( this - > loadFrameCount > = 0 )
os - > fprintf ( " length %d \n " , this - > loadFrameCount ) ;
2009-12-23 01:15:33 +01:00
2009-07-17 19:27:04 +02:00
if ( binary )
{
//put one | to start the binary dump
2010-08-29 23:15:42 +02:00
os - > fputc ( ' | ' ) ;
2009-07-17 19:27:04 +02:00
for ( int i = 0 ; i < ( int ) records . size ( ) ; i + + )
2012-01-09 02:59:06 +01:00
records [ i ] . dumpBinary ( this , os , i ) ;
} else
2009-12-23 01:15:33 +01:00
{
2012-01-09 02:59:06 +01:00
for ( int i = 0 ; i < ( int ) records . size ( ) ; i + + )
records [ i ] . dump ( this , os , i ) ;
2009-12-23 01:15:33 +01:00
}
2012-01-09 02:59:06 +01:00
int end = os - > ftell ( ) ;
2009-12-23 01:15:33 +01:00
return end - start ;
}
2009-07-17 19:27:04 +02:00
int FCEUMOV_GetFrame ( void )
{
return currFrameCounter ;
}
int FCEUI_GetLagCount ( void )
{
return lagCounter ;
}
bool FCEUI_GetLagged ( void )
{
2012-12-14 18:18:20 +01:00
if ( lagFlag )
return true ;
else
return false ;
}
void FCEUI_SetLagFlag ( bool value )
{
lagFlag = ( value ) ? 1 : 0 ;
2009-07-17 19:27:04 +02:00
}
bool FCEUMOV_ShouldPause ( void )
{
2012-01-09 02:59:06 +01:00
if ( pauseframe & & currFrameCounter + 1 = = pauseframe )
2009-07-17 19:27:04 +02:00
{
2012-01-09 02:59:06 +01:00
pauseframe = 0 ;
2009-07-17 19:27:04 +02:00
return true ;
}
else
{
return false ;
}
}
EMOVIEMODE FCEUMOV_Mode ( )
{
return movieMode ;
}
bool FCEUMOV_Mode ( EMOVIEMODE modemask )
{
return ( movieMode & modemask ) ! = 0 ;
}
bool FCEUMOV_Mode ( int modemask )
{
return FCEUMOV_Mode ( ( EMOVIEMODE ) modemask ) ;
}
2010-08-29 23:15:42 +02:00
static void LoadFM2_binarychunk ( MovieData & movieData , EMUFILE * fp , int size )
2009-07-17 19:27:04 +02:00
{
int recordsize = 1 ; //1 for the command
if ( movieData . fourscore )
recordsize + = 4 ; //4 joysticks
else
{
for ( int i = 0 ; i < 2 ; i + + )
{
switch ( movieData . ports [ i ] )
{
case SI_GAMEPAD : recordsize + + ; break ;
case SI_ZAPPER : recordsize + = 12 ; break ;
}
}
}
//find out how much remains in the file
2010-08-29 23:15:42 +02:00
int curr = fp - > ftell ( ) ;
fp - > fseek ( 0 , SEEK_END ) ;
int end = fp - > ftell ( ) ;
2009-07-17 19:27:04 +02:00
int flen = end - curr ;
2010-08-29 23:15:42 +02:00
fp - > fseek ( curr , SEEK_SET ) ;
2009-07-17 19:27:04 +02:00
//the amount todo is the min of the limiting size we received and the remaining contents of the file
int todo = std : : min ( size , flen ) ;
int numRecords = todo / recordsize ;
2009-12-23 01:15:33 +01:00
if ( movieData . loadFrameCount ! = - 1 & & movieData . loadFrameCount < numRecords )
numRecords = movieData . loadFrameCount ;
2009-07-17 19:27:04 +02:00
movieData . records . resize ( numRecords ) ;
for ( int i = 0 ; i < numRecords ; i + + )
{
movieData . records [ i ] . parseBinary ( & movieData , fp ) ;
}
}
//yuck... another custom text parser.
2010-08-29 23:15:42 +02:00
bool LoadFM2 ( MovieData & movieData , EMUFILE * fp , int size , bool stopAfterHeader )
2009-07-17 19:27:04 +02:00
{
2012-01-09 02:59:06 +01:00
// if there's no "binary" tag in the movie header, consider it as a movie in text format
movieData . binaryFlag = false ;
// Non-TASEditor projects consume until EOF
movieData . loadFrameCount = - 1 ;
2009-12-23 01:15:33 +01:00
2010-08-29 23:15:42 +02:00
std : : ios : : pos_type curr = fp - > ftell ( ) ;
2012-06-27 01:14:39 +02:00
if ( ! stopAfterHeader )
{
// first, look for an fcm signature
char fcmbuf [ 3 ] ;
fp - > fread ( fcmbuf , 3 ) ;
fp - > fseek ( curr , SEEK_SET ) ;
if ( ! strncmp ( fcmbuf , " FCM " , 3 ) ) {
FCEU_PrintError ( " FCM File format is no longer supported. Please use Tools > Convert FCM " ) ;
return false ;
}
2009-07-17 19:27:04 +02:00
}
//movie must start with "version 3"
char buf [ 9 ] ;
2010-08-29 23:15:42 +02:00
curr = fp - > ftell ( ) ;
fp - > fread ( buf , 9 ) ;
fp - > fseek ( curr , SEEK_SET ) ;
2009-07-17 19:27:04 +02:00
if ( fp - > fail ( ) ) return false ;
2012-12-14 18:18:20 +01:00
if ( memcmp ( buf , " version 3 " , 9 ) )
2009-07-17 19:27:04 +02:00
return false ;
std : : string key , value ;
enum {
NEWLINE , KEY , SEPARATOR , VALUE , RECORD , COMMENT , SUBTITLE
} state = NEWLINE ;
bool bail = false ;
2012-06-27 01:14:39 +02:00
bool iswhitespace , isrecchar , isnewline ;
int c ;
2009-07-17 19:27:04 +02:00
for ( ; ; )
{
if ( size - - < = 0 ) goto bail ;
2010-08-29 23:15:42 +02:00
c = fp - > fgetc ( ) ;
2009-07-17 19:27:04 +02:00
if ( c = = - 1 )
goto bail ;
iswhitespace = ( c = = ' ' | | c = = ' \t ' ) ;
isrecchar = ( c = = ' | ' ) ;
isnewline = ( c = = 10 | | c = = 13 ) ;
if ( isrecchar & & movieData . binaryFlag & & ! stopAfterHeader )
{
LoadFM2_binarychunk ( movieData , fp , size ) ;
return true ;
2012-01-09 02:59:06 +01:00
} else if ( isnewline & & movieData . loadFrameCount = = movieData . records . size ( ) )
// exit prematurely if loaded the specified amound of records
return true ;
2009-07-17 19:27:04 +02:00
switch ( state )
{
case NEWLINE :
if ( isnewline ) goto done ;
if ( iswhitespace ) goto done ;
2012-12-14 18:18:20 +01:00
if ( isrecchar )
2009-07-17 19:27:04 +02:00
goto dorecord ;
//must be a key
key = " " ;
value = " " ;
goto dokey ;
break ;
case RECORD :
{
dorecord :
if ( stopAfterHeader ) return true ;
int currcount = movieData . records . size ( ) ;
movieData . records . resize ( currcount + 1 ) ;
2010-08-29 23:15:42 +02:00
int preparse = fp - > ftell ( ) ;
2009-07-17 19:27:04 +02:00
movieData . records [ currcount ] . parse ( & movieData , fp ) ;
2010-08-29 23:15:42 +02:00
int postparse = fp - > ftell ( ) ;
2009-07-17 19:27:04 +02:00
size - = ( postparse - preparse ) ;
state = NEWLINE ;
break ;
}
case KEY :
dokey : //dookie
state = KEY ;
if ( iswhitespace ) goto doseparator ;
if ( isnewline ) goto commit ;
key + = c ;
break ;
case SEPARATOR :
doseparator :
state = SEPARATOR ;
if ( isnewline ) goto commit ;
if ( ! iswhitespace ) goto dovalue ;
break ;
case VALUE :
dovalue :
state = VALUE ;
if ( isnewline ) goto commit ;
value + = c ;
}
goto done ;
bail :
bail = true ;
if ( state = = VALUE ) goto commit ;
goto done ;
commit :
movieData . installValue ( key , value ) ;
state = NEWLINE ;
done : ;
if ( bail ) break ;
}
2010-08-29 23:15:42 +02:00
2009-07-17 19:27:04 +02:00
return true ;
2010-08-29 23:15:42 +02:00
}
/// Stop movie playback.
static void StopPlayback ( )
{
FCEU_DispMessageOnMovie ( " Movie playback stopped. " ) ;
movieMode = MOVIEMODE_INACTIVE ;
}
// Stop movie playback without closing the movie.
static void FinishPlayback ( )
{
extern int closeFinishedMovie ;
if ( closeFinishedMovie )
StopPlayback ( ) ;
else
{
FCEU_DispMessage ( " Movie finished playing. " , 0 ) ;
movieMode = MOVIEMODE_FINISHED ;
}
2009-07-17 19:27:04 +02:00
}
static void closeRecordingMovie ( )
{
if ( osRecordingMovie )
{
delete osRecordingMovie ;
osRecordingMovie = 0 ;
}
}
/// Stop movie recording
static void StopRecording ( )
{
2010-08-29 23:15:42 +02:00
FCEU_DispMessage ( " Movie recording stopped. " , 0 ) ;
2009-07-17 19:27:04 +02:00
movieMode = MOVIEMODE_INACTIVE ;
2012-12-14 18:18:20 +01:00
2009-07-17 19:27:04 +02:00
closeRecordingMovie ( ) ;
}
void FCEUI_StopMovie ( )
{
if ( suppressMovieStop )
return ;
2012-12-14 18:18:20 +01:00
2010-08-29 23:15:42 +02:00
if ( movieMode = = MOVIEMODE_PLAY | | movieMode = = MOVIEMODE_FINISHED )
2009-07-17 19:27:04 +02:00
StopPlayback ( ) ;
else if ( movieMode = = MOVIEMODE_RECORD )
StopRecording ( ) ;
curMovieFilename [ 0 ] = 0 ; //No longer a current movie filename
freshMovie = false ; //No longer a fresh movie loaded
if ( bindSavestate ) AutoSS = false ; //If bind movies to savestates is true, then there is no longer a valid auto-save to load
2010-08-29 23:15:42 +02:00
# ifdef WIN32
SetMainWindowText ( ) ;
2009-07-17 19:27:04 +02:00
# endif
}
2018-08-13 17:04:20 +02:00
bool bogorf ;
2009-11-16 01:22:36 +01:00
void poweron ( bool shouldDisableBatteryLoading )
2009-07-17 19:27:04 +02:00
{
//// make a for-movie-recording power-on clear the game's save data, too
//extern char lastLoadedGameName [2048];
//extern int disableBatteryLoading, suppressAddPowerCommand;
//suppressAddPowerCommand=1;
//if(shouldDisableBatteryLoading) disableBatteryLoading=1;
//suppressMovieStop=true;
//{
// //we need to save the pause state through this process
// int oldPaused = EmulationPaused;
// // NOTE: this will NOT write an FCEUNPCMD_POWER into the movie file
// FCEUGI* gi = FCEUI_LoadGame(lastLoadedGameName, 0);
// assert(gi);
// PowerNES();
// EmulationPaused = oldPaused;
//}
//suppressMovieStop=false;
//if(shouldDisableBatteryLoading) disableBatteryLoading=0;
//suppressAddPowerCommand=0;
2010-08-29 23:15:42 +02:00
2009-07-17 19:27:04 +02:00
extern int disableBatteryLoading ;
2018-08-13 17:04:20 +02:00
if ( ! bogorf ) disableBatteryLoading = 1 ;
2009-07-17 19:27:04 +02:00
PowerNES ( ) ;
2018-08-13 17:04:20 +02:00
if ( ! bogorf ) disableBatteryLoading = 0 ;
2009-07-17 19:27:04 +02:00
}
2012-06-27 01:14:39 +02:00
void FCEUMOV_CreateCleanMovie ( )
2009-07-17 19:27:04 +02:00
{
currMovieData = MovieData ( ) ;
2012-01-09 02:59:06 +01:00
currMovieData . palFlag = FCEUI_GetCurrentVidSystem ( 0 , 0 ) ! = 0 ;
currMovieData . romFilename = FileBase ;
currMovieData . romChecksum = GameInfo - > MD5 ;
currMovieData . guid . newGuid ( ) ;
currMovieData . fourscore = FCEUI_GetInputFourscore ( ) ;
currMovieData . microphone = FCEUI_GetInputMicrophone ( ) ;
currMovieData . ports [ 0 ] = joyports [ 0 ] . type ;
currMovieData . ports [ 1 ] = joyports [ 1 ] . type ;
currMovieData . ports [ 2 ] = portFC . type ;
currMovieData . fds = isFDS ;
currMovieData . PPUflag = ( newppu ! = 0 ) ;
2018-08-13 17:04:20 +02:00
currMovieData . RAMInitOption = RAMInitOption ;
currMovieData . RAMInitSeed = RAMInitSeed ;
2009-07-17 19:27:04 +02:00
}
2012-06-27 01:14:39 +02:00
void FCEUMOV_ClearCommands ( )
{
_currCommand = 0 ;
}
2009-07-17 19:27:04 +02:00
2010-08-29 23:15:42 +02:00
bool FCEUMOV_FromPoweron ( )
2009-07-17 19:27:04 +02:00
{
2010-08-29 23:15:42 +02:00
return movieFromPoweron ;
}
bool MovieData : : loadSavestateFrom ( std : : vector < uint8 > * buf )
{
EMUFILE_MEMORY ms ( buf ) ;
2009-07-17 19:27:04 +02:00
return FCEUSS_LoadFP ( & ms , SSLOADPARAM_BACKUP ) ;
}
2010-08-29 23:15:42 +02:00
void MovieData : : dumpSavestateTo ( std : : vector < uint8 > * buf , int compressionLevel )
2009-07-17 19:27:04 +02:00
{
2010-08-29 23:15:42 +02:00
EMUFILE_MEMORY ms ( buf ) ;
2009-07-17 19:27:04 +02:00
FCEUSS_SaveMS ( & ms , compressionLevel ) ;
ms . trim ( ) ;
2010-08-29 23:15:42 +02:00
}
2018-08-13 17:04:20 +02:00
bool MovieData : : loadSaveramFrom ( std : : vector < uint8 > * buf )
{
EMUFILE_MEMORY ms ( buf ) ;
bool hasBattery = ! ! ms . read32le ( ) ;
if ( hasBattery ! = ! ! currCartInfo - > battery )
{
FCEU_PrintError ( " movie battery load mismatch 1 " ) ;
return false ;
}
for ( int i = 0 ; i < 4 ; i + + )
{
int len = ms . read32le ( ) ;
if ( ! currCartInfo - > SaveGame [ i ] & & len ! = 0 )
{
FCEU_PrintError ( " movie battery load mismatch 2 " ) ;
return false ;
}
if ( currCartInfo - > SaveGameLen [ i ] ! = len )
{
FCEU_PrintError ( " movie battery load mismatch 3 " ) ;
return false ;
}
ms . fread ( currCartInfo - > SaveGame [ i ] , len ) ;
}
return true ;
}
void MovieData : : dumpSaveramTo ( std : : vector < uint8 > * buf , int compressionLevel )
{
EMUFILE_MEMORY ms ( buf ) ;
ms . write32le ( currCartInfo - > battery ? 1 : 0 ) ;
for ( int i = 0 ; i < 4 ; i + + )
{
if ( ! currCartInfo - > SaveGame [ i ] )
{
ms . write32le ( ( u32 ) 0 ) ;
continue ;
}
ms . write32le ( currCartInfo - > SaveGameLen [ i ] ) ;
ms . fwrite ( currCartInfo - > SaveGame [ i ] , currCartInfo - > SaveGameLen [ i ] ) ;
}
}
2009-07-17 19:27:04 +02:00
//begin playing an existing movie
2012-01-09 02:59:06 +01:00
bool FCEUI_LoadMovie ( const char * fname , bool _read_only , int _pauseframe )
2009-07-17 19:27:04 +02:00
{
2012-01-09 02:59:06 +01:00
if ( ! FCEU_IsValidUI ( FCEUI_PLAYMOVIE ) )
2009-07-18 00:54:58 +02:00
return true ; //adelikat: file did not fail to load, so let's return true here, just do nothing
2009-07-17 19:27:04 +02:00
assert ( fname ) ;
//mbg 6/10/08 - we used to call StopMovie here, but that cleared curMovieFilename and gave us crashes...
2010-08-29 23:15:42 +02:00
if ( movieMode = = MOVIEMODE_PLAY | | movieMode = = MOVIEMODE_FINISHED )
2009-07-17 19:27:04 +02:00
StopPlayback ( ) ;
else if ( movieMode = = MOVIEMODE_RECORD )
StopRecording ( ) ;
//--------------
currMovieData = MovieData ( ) ;
2012-12-14 18:18:20 +01:00
2009-07-17 19:27:04 +02:00
strcpy ( curMovieFilename , fname ) ;
FCEUFILE * fp = FCEU_fopen ( fname , 0 , " rb " , 0 ) ;
2009-07-18 00:54:58 +02:00
if ( ! fp ) return false ;
2009-07-17 19:27:04 +02:00
if ( fp - > isArchive ( ) & & ! _read_only ) {
FCEU_PrintError ( " Cannot open a movie in read+write from an archive. " ) ;
2009-07-18 00:54:58 +02:00
return true ; //adelikat: file did not fail to load, so return true (false is only for file not exist/unable to open errors
2009-07-17 19:27:04 +02:00
}
2009-07-18 00:54:58 +02:00
# ifdef WIN32
2010-08-29 23:15:42 +02:00
//Fix relative path if necessary and then add to the recent movie menu
extern std : : string BaseDirectory ;
string name = fname ;
if ( IsRelativePath ( fname ) )
{
name = ConvertRelativePath ( name ) ;
}
AddRecentMovieFile ( name . c_str ( ) ) ;
2009-07-18 00:54:58 +02:00
# endif
2010-08-29 23:15:42 +02:00
LoadFM2 ( currMovieData , fp - > stream , fp - > size , false ) ;
2009-07-18 00:54:58 +02:00
LoadSubtitles ( currMovieData ) ;
2009-07-17 19:27:04 +02:00
delete fp ;
2018-08-13 17:04:20 +02:00
RAMInitOption = currMovieData . RAMInitOption ;
RAMInitSeed = currMovieData . RAMInitSeed ;
2009-07-17 19:27:04 +02:00
freshMovie = true ; //Movie has been loaded, so it must be unaltered
if ( bindSavestate ) AutoSS = false ; //If bind savestate to movie is true, then their isn't a valid auto-save to load, so flag it
//fully reload the game to reinitialize everything before playing any movie
poweron ( true ) ;
2012-01-09 02:59:06 +01:00
if ( currMovieData . savestate . size ( ) )
2009-07-17 19:27:04 +02:00
{
2012-01-09 02:59:06 +01:00
//WE NEED TO LOAD A SAVESTATE
2010-08-29 23:15:42 +02:00
movieFromPoweron = false ;
2009-07-17 19:27:04 +02:00
bool success = MovieData : : loadSavestateFrom ( & currMovieData . savestate ) ;
2012-01-09 02:59:06 +01:00
if ( ! success ) return true ; //adelikat: I guess return true here? False is only for a bad movie filename, if it got this far the file was good?
2018-08-13 17:04:20 +02:00
}
else if ( currMovieData . saveram . size ( ) )
{
movieFromPoweron = true ;
bool success = MovieData : : loadSaveramFrom ( & currMovieData . saveram ) ;
if ( ! success ) return true ; //adelikat: I guess return true here? False is only for a bad movie filename, if it got this far the file was good?
}
else {
2010-08-29 23:15:42 +02:00
movieFromPoweron = true ;
2009-07-17 19:27:04 +02:00
}
//if there is no savestate, we won't have this crucial piece of information at the start of the movie.
//so, we have to include it with the movie
if ( currMovieData . palFlag )
FCEUI_SetVidSystem ( 1 ) ;
else
FCEUI_SetVidSystem ( 0 ) ;
2018-08-13 17:04:20 +02:00
2009-07-17 19:27:04 +02:00
//force the input configuration stored in the movie to apply
2012-01-09 02:59:06 +01:00
FCEUD_SetInput ( currMovieData . fourscore , currMovieData . microphone , ( ESI ) currMovieData . ports [ 0 ] , ( ESI ) currMovieData . ports [ 1 ] , ( ESIFC ) currMovieData . ports [ 2 ] ) ;
2009-07-17 19:27:04 +02:00
//stuff that should only happen when we're ready to positively commit to the replay
2012-01-09 02:59:06 +01:00
currFrameCounter = 0 ;
pauseframe = _pauseframe ;
movie_readonly = _read_only ;
movieMode = MOVIEMODE_PLAY ;
2016-09-18 05:43:24 +02:00
if ( movieMode ! = MOVIEMODE_TASEDITOR )
currRerecordCount = currMovieData . rerecordCount ;
2009-07-17 19:27:04 +02:00
2012-01-09 02:59:06 +01:00
if ( movie_readonly )
FCEU_DispMessage ( " Replay started Read-Only. " , 0 ) ;
2009-07-17 19:27:04 +02:00
else
2012-01-09 02:59:06 +01:00
FCEU_DispMessage ( " Replay started Read+Write. " , 0 ) ;
2012-12-14 18:18:20 +01:00
2010-08-29 23:15:42 +02:00
# ifdef WIN32
SetMainWindowText ( ) ;
# endif
2009-07-17 19:27:04 +02:00
# ifdef CREATE_AVI
if ( LoggingEnabled )
{
2010-08-29 23:15:42 +02:00
FCEU_DispMessage ( " Video recording enabled. \n " , 0 ) ;
2009-07-17 19:27:04 +02:00
LoggingEnabled = 2 ;
}
# endif
2012-12-14 18:18:20 +01:00
2009-07-18 00:54:58 +02:00
return true ;
2009-07-17 19:27:04 +02:00
}
static void openRecordingMovie ( const char * fname )
{
osRecordingMovie = FCEUD_UTF8_fstream ( fname , " wb " ) ;
if ( ! osRecordingMovie )
FCEU_PrintError ( " Error opening movie output file: %s " , fname ) ;
strcpy ( curMovieFilename , fname ) ;
2010-08-29 23:15:42 +02:00
# ifdef WIN32
//Add to the recent movie menu
AddRecentMovieFile ( fname ) ;
2009-07-17 19:27:04 +02:00
# endif
}
2010-08-29 23:15:42 +02:00
2009-07-17 19:27:04 +02:00
//begin recording a new movie
//TODO - BUG - the record-from-another-savestate doesnt work.
void FCEUI_SaveMovie ( const char * fname , EMOVIE_FLAG flags , std : : wstring author )
{
if ( ! FCEU_IsValidUI ( FCEUI_RECORDMOVIE ) )
return ;
assert ( fname ) ;
FCEUI_StopMovie ( ) ;
openRecordingMovie ( fname ) ;
currFrameCounter = 0 ;
LagCounterReset ( ) ;
2012-06-27 01:14:39 +02:00
FCEUMOV_CreateCleanMovie ( ) ;
2009-07-17 19:27:04 +02:00
if ( author ! = L " " ) currMovieData . comments . push_back ( L " author " + author ) ;
2012-01-09 02:59:06 +01:00
2009-07-17 19:27:04 +02:00
if ( flags & MOVIE_FLAG_FROM_POWERON )
{
2010-08-29 23:15:42 +02:00
movieFromPoweron = true ;
2009-07-17 19:27:04 +02:00
poweron ( true ) ;
}
2018-08-13 17:04:20 +02:00
else if ( flags & MOVIE_FLAG_FROM_SAVERAM )
{
movieFromPoweron = true ;
MovieData : : dumpSaveramTo ( & currMovieData . saveram , Z_NO_COMPRESSION ) ; //i guess with this there's a chance someone could hack the file, at least, so maybe it's helpfu
bogorf = true ;
poweron ( false ) ;
bogorf = false ;
}
else //from savestate
2009-07-17 19:27:04 +02:00
{
2010-08-29 23:15:42 +02:00
movieFromPoweron = false ;
2009-07-17 19:27:04 +02:00
MovieData : : dumpSavestateTo ( & currMovieData . savestate , Z_BEST_COMPRESSION ) ;
}
2012-06-27 01:14:39 +02:00
FCEUMOV_ClearCommands ( ) ;
2009-07-17 19:27:04 +02:00
//we are going to go ahead and dump the header. from now on we will only be appending frames
currMovieData . dump ( osRecordingMovie , false ) ;
movieMode = MOVIEMODE_RECORD ;
movie_readonly = false ;
2016-09-18 05:43:24 +02:00
if ( movieMode ! = MOVIEMODE_TASEDITOR )
currRerecordCount = 0 ;
2012-12-14 18:18:20 +01:00
2010-08-29 23:15:42 +02:00
FCEU_DispMessage ( " Movie recording started. " , 0 ) ;
2010-03-11 07:30:34 +01:00
}
2009-07-17 19:27:04 +02:00
//the main interaction point between the emulator and the movie system.
//either dumps the current joystick state or loads one state from the movie
void FCEUMOV_AddInputState ( )
{
2012-01-09 02:59:06 +01:00
# ifdef _WIN32
2012-12-14 18:18:20 +01:00
if ( movieMode = = MOVIEMODE_TASEDITOR )
2009-07-17 19:27:04 +02:00
{
2012-06-27 01:14:39 +02:00
// if movie length is less or equal to currFrame, pad it with empty frames
2012-12-14 18:18:20 +01:00
if ( ( ( int ) currMovieData . records . size ( ) - 1 ) < ( currFrameCounter + 1 ) )
currMovieData . insertEmpty ( - 1 , ( currFrameCounter + 1 ) - ( ( int ) currMovieData . records . size ( ) - 1 ) ) ;
2012-01-09 02:59:06 +01:00
2009-07-17 19:27:04 +02:00
MovieRecord * mr = & currMovieData . records [ currFrameCounter ] ;
2016-09-18 05:43:24 +02:00
if ( isTaseditorRecording ( ) )
2009-07-17 19:27:04 +02:00
{
2012-06-27 01:14:39 +02:00
// record commands and buttons
mr - > commands | = _currCommand ;
2009-07-17 19:27:04 +02:00
joyports [ 0 ] . log ( mr ) ;
joyports [ 1 ] . log ( mr ) ;
2016-09-18 05:43:24 +02:00
recordInputByTaseditor ( ) ;
2009-07-17 19:27:04 +02:00
}
2012-06-27 01:14:39 +02:00
// replay buttons
joyports [ 0 ] . load ( mr ) ;
joyports [ 1 ] . load ( mr ) ;
// replay commands
2016-09-18 05:43:24 +02:00
if ( mr - > command_power ( ) )
2012-06-27 01:14:39 +02:00
PowerNES ( ) ;
2016-09-18 05:43:24 +02:00
if ( mr - > command_reset ( ) )
2012-06-27 01:14:39 +02:00
ResetNES ( ) ;
2016-09-18 05:43:24 +02:00
if ( mr - > command_fds_insert ( ) )
2012-06-27 01:14:39 +02:00
FCEU_FDSInsert ( ) ;
2016-09-18 05:43:24 +02:00
if ( mr - > command_fds_select ( ) )
2012-06-27 01:14:39 +02:00
FCEU_FDSSelect ( ) ;
2016-09-18 05:43:24 +02:00
if ( mr - > command_vs_insertcoin ( ) )
FCEU_VSUniCoin ( ) ;
2012-01-09 02:59:06 +01:00
_currCommand = 0 ;
} else
# endif
2016-09-18 05:43:24 +02:00
if ( movieMode = = MOVIEMODE_PLAY )
2009-07-17 19:27:04 +02:00
{
//stop when we run out of frames
2016-09-18 05:43:24 +02:00
if ( currFrameCounter > = ( int ) currMovieData . records . size ( ) )
2009-07-17 19:27:04 +02:00
{
2010-08-29 23:15:42 +02:00
FinishPlayback ( ) ;
2012-12-14 18:18:20 +01:00
//tell all drivers to poll input and set up their logical states
for ( int port = 0 ; port < 2 ; port + + )
joyports [ port ] . driver - > Update ( port , joyports [ port ] . ptr , joyports [ port ] . attrib ) ;
portFC . driver - > Update ( portFC . ptr , portFC . attrib ) ;
2016-09-18 05:43:24 +02:00
} else
2009-07-17 19:27:04 +02:00
{
MovieRecord * mr = & currMovieData . records [ currFrameCounter ] ;
2012-12-14 18:18:20 +01:00
2009-07-17 19:27:04 +02:00
//reset and power cycle if necessary
if ( mr - > command_power ( ) )
PowerNES ( ) ;
if ( mr - > command_reset ( ) )
ResetNES ( ) ;
2009-09-15 10:20:48 +02:00
if ( mr - > command_fds_insert ( ) )
FCEU_FDSInsert ( ) ;
if ( mr - > command_fds_select ( ) )
FCEU_FDSSelect ( ) ;
2016-09-18 05:43:24 +02:00
if ( mr - > command_vs_insertcoin ( ) )
FCEU_VSUniCoin ( ) ;
2009-09-15 10:20:48 +02:00
2009-07-17 19:27:04 +02:00
joyports [ 0 ] . load ( mr ) ;
joyports [ 1 ] . load ( mr ) ;
}
//if we are on the last frame, then pause the emulator if the player requested it
2016-09-18 05:43:24 +02:00
if ( currFrameCounter = = currMovieData . records . size ( ) - 1 )
2009-07-17 19:27:04 +02:00
{
if ( FCEUD_PauseAfterPlayback ( ) )
{
FCEUI_ToggleEmulationPause ( ) ;
}
}
2012-12-14 18:18:20 +01:00
//pause the movie at a specified frame
2016-09-18 05:43:24 +02:00
if ( FCEUMOV_ShouldPause ( ) & & FCEUI_EmulationPaused ( ) = = 0 )
2009-07-17 19:27:04 +02:00
{
FCEUI_ToggleEmulationPause ( ) ;
2010-08-29 23:15:42 +02:00
FCEU_DispMessage ( " Paused at specified movie frame " , 0 ) ;
2009-07-17 19:27:04 +02:00
}
2012-12-14 18:18:20 +01:00
2016-09-18 05:43:24 +02:00
} else if ( movieMode = = MOVIEMODE_RECORD )
2009-07-17 19:27:04 +02:00
{
MovieRecord mr ;
joyports [ 0 ] . log ( & mr ) ;
joyports [ 1 ] . log ( & mr ) ;
mr . commands = _currCommand ;
_currCommand = 0 ;
2010-08-29 23:15:42 +02:00
//Adelikat: in normal mode, this is done at the time of loading a savestate in read+write mode
//If the user chooses it can be delayed to here
if ( fullSaveStateLoads & & ( currFrameCounter < ( int ) currMovieData . records . size ( ) ) )
currMovieData . truncateAt ( currFrameCounter ) ;
2012-01-09 02:59:06 +01:00
mr . dump ( & currMovieData , osRecordingMovie , currMovieData . records . size ( ) ) ; // to disk
2009-07-17 19:27:04 +02:00
currMovieData . records . push_back ( mr ) ;
}
currFrameCounter + + ;
extern uint8 joy [ 4 ] ;
memcpy ( & cur_input_display , joy , 4 ) ;
}
2012-12-14 18:18:20 +01:00
//TODO
2009-07-17 19:27:04 +02:00
void FCEUMOV_AddCommand ( int cmd )
{
// do nothing if not recording a movie
2012-06-27 01:14:39 +02:00
if ( movieMode ! = MOVIEMODE_RECORD & & movieMode ! = MOVIEMODE_TASEDITOR )
2009-07-17 19:27:04 +02:00
return ;
2016-09-18 05:43:24 +02:00
// translate "FCEU NetPlay" command to "FCEU Movie" command
switch ( cmd )
{
case FCEUNPCMD_RESET : cmd = MOVIECMD_RESET ; break ;
case FCEUNPCMD_POWER : cmd = MOVIECMD_POWER ; break ;
2009-09-15 10:20:48 +02:00
case FCEUNPCMD_FDSINSERT : cmd = MOVIECMD_FDS_INSERT ; break ;
case FCEUNPCMD_FDSSELECT : cmd = MOVIECMD_FDS_SELECT ; break ;
2016-09-18 05:43:24 +02:00
case FCEUNPCMD_VSUNICOIN : cmd = MOVIECMD_VS_INSERTCOIN ; break ;
// all other netplay commands (e.g. FCEUNPCMD_VSUNIDIP0) are not supported by movie recorder for now
default : return ;
2009-09-15 10:20:48 +02:00
}
2009-07-17 19:27:04 +02:00
_currCommand | = cmd ;
}
void FCEU_DrawMovies ( uint8 * XBuf )
{
2012-01-09 02:59:06 +01:00
if ( frame_display )
2009-07-17 19:27:04 +02:00
{
char counterbuf [ 32 ] = { 0 } ;
2010-08-29 23:15:42 +02:00
int color = 0x20 ;
2009-07-17 19:27:04 +02:00
if ( movieMode = = MOVIEMODE_PLAY )
2016-09-18 05:43:24 +02:00
sprintf ( counterbuf , " %d/%d " , currFrameCounter , ( int ) currMovieData . records . size ( ) ) ;
2012-12-14 18:18:20 +01:00
else if ( movieMode = = MOVIEMODE_RECORD )
2010-08-29 23:15:42 +02:00
sprintf ( counterbuf , " %d " , currFrameCounter ) ;
else if ( movieMode = = MOVIEMODE_FINISHED )
{
2016-09-18 05:43:24 +02:00
sprintf ( counterbuf , " %d/%d (finished) " , currFrameCounter , ( int ) currMovieData . records . size ( ) ) ;
2010-08-29 23:15:42 +02:00
color = 0x17 ; //Show red to get attention
2012-01-14 23:35:51 +01:00
} else if ( movieMode = = MOVIEMODE_TASEDITOR )
2012-01-09 02:59:06 +01:00
{
sprintf ( counterbuf , " %d " , currFrameCounter ) ;
} else
2009-07-17 19:27:04 +02:00
sprintf ( counterbuf , " %d (no movie) " , currFrameCounter ) ;
if ( counterbuf [ 0 ] )
2010-08-29 23:15:42 +02:00
DrawTextTrans ( ClipSidesOffset + XBuf + FCEU_TextScanlineOffsetFromBottom ( 30 ) + 1 , 256 , ( uint8 * ) counterbuf , color + 0x80 ) ;
2009-07-17 19:27:04 +02:00
}
2012-01-09 02:59:06 +01:00
if ( rerecord_display & & movieMode ! = MOVIEMODE_INACTIVE )
{
2012-12-14 18:18:20 +01:00
char counterbuf [ 32 ] = { 0 } ;
2012-01-09 02:59:06 +01:00
sprintf ( counterbuf , " %d " , currMovieData . rerecordCount ) ;
2012-12-14 18:18:20 +01:00
2012-01-09 02:59:06 +01:00
if ( counterbuf [ 0 ] )
DrawTextTrans ( ClipSidesOffset + XBuf + FCEU_TextScanlineOffsetFromBottom ( 50 ) + 1 , 256 , ( uint8 * ) counterbuf , 0x28 + 0x80 ) ;
}
2009-07-17 19:27:04 +02:00
}
void FCEU_DrawLagCounter ( uint8 * XBuf )
{
2012-12-14 18:18:20 +01:00
if ( lagCounterDisplay )
2009-07-17 19:27:04 +02:00
{
2012-12-14 18:18:20 +01:00
// If currently lagging - display red, else display green
uint8 color = ( lagFlag ) ? ( 0x16 + 0x80 ) : ( 0x2A + 0x80 ) ;
sprintf ( lagcounterbuf , " %d " , lagCounter ) ;
if ( lagcounterbuf [ 0 ] )
DrawTextTrans ( ClipSidesOffset + XBuf + FCEU_TextScanlineOffsetFromBottom ( 40 ) + 1 , 256 , ( uint8 * ) lagcounterbuf , color ) ;
2009-07-17 19:27:04 +02:00
}
}
2010-08-29 23:15:42 +02:00
int FCEUMOV_WriteState ( EMUFILE * os )
2009-07-17 19:27:04 +02:00
{
//we are supposed to dump the movie data into the savestate
2010-08-29 23:15:42 +02:00
if ( movieMode = = MOVIEMODE_RECORD | | movieMode = = MOVIEMODE_PLAY | | movieMode = = MOVIEMODE_FINISHED )
2009-07-17 19:27:04 +02:00
return currMovieData . dump ( os , true ) ;
2010-08-29 23:15:42 +02:00
else return 0 ;
}
2012-12-14 18:18:20 +01:00
// returns
int CheckTimelines ( MovieData & stateMovie , MovieData & currMovie )
2010-08-29 23:15:42 +02:00
{
2012-12-14 18:18:20 +01:00
// end_frame = min(urrMovie.records.size(), stateMovie.records.size(), currFrameCounter)
int end_frame = currMovie . records . size ( ) ;
if ( end_frame > ( int ) stateMovie . records . size ( ) )
end_frame = stateMovie . records . size ( ) ;
if ( end_frame > currFrameCounter )
end_frame = currFrameCounter ;
2010-08-29 23:15:42 +02:00
2012-12-14 18:18:20 +01:00
for ( int x = 0 ; x < end_frame ; x + + )
2010-08-29 23:15:42 +02:00
{
if ( ! stateMovie . records [ x ] . Compare ( currMovie . records [ x ] ) )
2012-12-14 18:18:20 +01:00
return x ;
2010-08-29 23:15:42 +02:00
}
2012-12-14 18:18:20 +01:00
// no mismatch found
return - 1 ;
2009-07-17 19:27:04 +02:00
}
2010-08-29 23:15:42 +02:00
2009-07-17 19:27:04 +02:00
static bool load_successful ;
2010-08-29 23:15:42 +02:00
bool FCEUMOV_ReadState ( EMUFILE * is , uint32 size )
2009-07-17 19:27:04 +02:00
{
load_successful = false ;
2012-01-09 02:59:06 +01:00
if ( ! movie_readonly )
{
if ( currMovieData . loadFrameCount > = 0 )
{
2012-06-27 01:14:39 +02:00
# ifdef WIN32
2012-01-09 02:59:06 +01:00
int result = MessageBox ( hAppWnd , " This movie is a TAS Editor project file. \n It can be modified in TAS Editor only. \n \n Open it in TAS Editor now? " , " Movie Replay " , MB_YESNO ) ;
if ( result = = IDYES )
2016-09-18 05:43:24 +02:00
mustEngageTaseditor = true ;
2012-06-27 01:14:39 +02:00
# else
2012-01-09 02:59:06 +01:00
FCEUI_printf ( " This movie is a TAS Editor project file! It can be modified in TAS Editor only. \n Movie is now Read-Only. \n " ) ;
2012-06-27 01:14:39 +02:00
# endif
2012-01-09 02:59:06 +01:00
movie_readonly = true ;
}
if ( FCEU_isFileInArchive ( curMovieFilename ) )
{
//a little rule: cant load states in read+write mode with a movie from an archive.
//so we are going to switch it to readonly mode in that case
FCEU_PrintError ( " Cannot loadstate in Read+Write with movie from archive. Movie is now Read-Only. " ) ;
movie_readonly = true ;
}
2009-07-17 19:27:04 +02:00
}
MovieData tempMovieData = MovieData ( ) ;
2010-08-29 23:15:42 +02:00
std : : ios : : pos_type curr = is - > ftell ( ) ;
2009-07-17 19:27:04 +02:00
if ( ! LoadFM2 ( tempMovieData , is , size , false ) ) {
2010-08-29 23:15:42 +02:00
is - > fseek ( ( uint32 ) curr + size , SEEK_SET ) ;
2009-07-17 19:27:04 +02:00
extern bool FCEU_state_loading_old_format ;
if ( FCEU_state_loading_old_format ) {
2010-08-29 23:15:42 +02:00
if ( movieMode = = MOVIEMODE_PLAY | | movieMode = = MOVIEMODE_RECORD | | movieMode = = MOVIEMODE_FINISHED ) {
2012-12-14 18:18:20 +01:00
//FCEUI_StopMovie(); //No reason to stop the movie, nothing destructive has happened yet.
2009-07-17 19:27:04 +02:00
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) " ) ;
}
}
return false ;
}
//----------------
2010-08-29 23:15:42 +02:00
//complex TAS logic for loadstate
//fully conforms to the savestate logic documented in the Laws of TAS
//http://tasvideos.org/LawsOfTAS/OnSavestates.html
//----------------
2012-12-14 18:18:20 +01:00
2010-08-29 23:15:42 +02:00
/*
Playback or Recording + Read - only
* Check that GUID of movie and savestate - movie must match or else error
o on error : a message informing that the savestate doesn ' t belong to this movie . This is a GUID mismatch error . Give user a choice to load it anyway .
+ failstate : if use declines , loadstate attempt canceled , movie can resume as if not attempted if user has backup savstates enabled else stop movie
* Check that movie and savestate - movie are in same timeline . If not then this is a wrong timeline error .
o on error : a message informing that the savestate doesn ' t belong to this movie
+ failstate : loadstate attempt canceled , movie can resume as if not attempted if user has backup savestates enabled else stop movie
2012-12-14 18:18:20 +01:00
* Check that savestate - movie is not greater than movie . If it ' s greater then this is a future event error and is not allowed in read - only
2010-08-29 23:15:42 +02:00
o on error : message informing that the savestate is from a frame after the last frame of the movie
+ failstate - loadstate attempt cancelled , movie can resume if user has backup savesattes enabled , else stop movie
2012-12-14 18:18:20 +01:00
* Check that savestate framcount < = savestate movie length . If not this is a post - movie savestate and is not allowed in read - only
o on error : message informing that the savestate is from a frame after the last frame of the savestated movie
+ failstate - loadstate attempt cancelled , movie can resume if user has backup savesattes enabled , else stop movie
2010-08-29 23:15:42 +02:00
* All error checks have passed , state will be loaded
* Movie contained in the savestate will be discarded
2012-12-14 18:18:20 +01:00
* Movie is now in Playback mode
2010-08-29 23:15:42 +02:00
Playback , Recording + Read + write
* Check that GUID of movie and savestate - movie must match or else error
o on error : a message informing that the savestate doesn ' t belong to this movie . This is a GUID mismatch error . Give user a choice to load it anyway .
+ failstate : if use declines , loadstate attempt canceled , movie can resume as if not attempted ( stop movie if resume fails ) canceled , movie can resume if backup savestates enabled else stopmovie
* Check that savestate framcount < = savestate movie length . If not this is a post - movie savestate
2012-12-14 18:18:20 +01:00
o on post - movie : See post - movie event section .
2010-08-29 23:15:42 +02:00
* savestate passed all error checks and will now be loaded in its entirety and replace movie ( note : there will be no truncation yet )
* current framecount will be set to savestate framecount
* on the next frame of input , movie will be truncated to framecount
2012-12-14 18:18:20 +01:00
o ( note : savestate - movie can be a future event of the movie timeline , or a completely new timeline and it is still allowed )
2010-08-29 23:15:42 +02:00
* Rerecord count of movie will be incremented
2012-12-14 18:18:20 +01:00
* Movie is now in record mode
2010-08-29 23:15:42 +02:00
Post - movie savestate event
2012-12-14 18:18:20 +01:00
* Whan a savestate is loaded and is determined that the savestate - movie length is less than the savestate framecount then it is a post - movie savestate . These occur when a savestate was made during Movie Finished mode .
2010-08-29 23:15:42 +02:00
* If read + write , the entire savestate movie will be loaded and replace current movie .
* If read - only , the savestate movie will be discarded
* Mode will be switched to Move Finished
* Savestate will be loaded
* Current framecount changes to savestate framecount
2012-12-14 18:18:20 +01:00
* User will have control of input as if Movie inactive mode
2010-08-29 23:15:42 +02:00
*/
if ( movieMode = = MOVIEMODE_PLAY | | movieMode = = MOVIEMODE_RECORD | | movieMode = = MOVIEMODE_FINISHED )
2009-07-17 19:27:04 +02:00
{
//handle moviefile mismatch
if ( tempMovieData . guid ! = currMovieData . guid )
{
//mbg 8/18/08 - this code can be used to turn the error message into an OK/CANCEL
# ifdef WIN32
std : : string msg = " There is a mismatch between savestate's movie and current movie. \n current: " + currMovieData . guid . toString ( ) + " \n savestate: " + tempMovieData . guid . toString ( ) + " \n \n This means that you have loaded a savestate belonging to a different movie than the one you are playing now. \n \n Continue loading this savestate anyway? " ;
extern HWND pwindow ;
int result = MessageBox ( pwindow , msg . c_str ( ) , " Error loading savestate " , MB_OKCANCEL ) ;
if ( result = = IDCANCEL )
2010-08-29 23:15:42 +02:00
{
if ( ! backupSavestates ) //If backups are disabled we can just resume normally since we can't restore so stop movie and inform user
{
FCEU_PrintError ( " Unable to restore backup, movie playback stopped. " ) ;
FCEUI_StopMovie ( ) ;
}
2009-07-17 19:27:04 +02:00
return false ;
2010-08-29 23:15:42 +02:00
}
2009-07-17 19:27:04 +02:00
# else
2010-08-29 23:15:42 +02:00
if ( ! backupSavestates ) //If backups are disabled we can just resume normally since we can't restore so stop movie and inform user
{
FCEU_PrintError ( " Mismatch between savestate's movie and current movie. \n current: %s \n savestate: %s \n Unable to restore backup, movie playback stopped. \n " , currMovieData . guid . toString ( ) . c_str ( ) , tempMovieData . guid . toString ( ) . c_str ( ) ) ;
FCEUI_StopMovie ( ) ;
}
else
2009-07-17 19:27:04 +02:00
FCEU_PrintError ( " Mismatch between savestate's movie and current movie. \n current: %s \n savestate: %s \n " , currMovieData . guid . toString ( ) . c_str ( ) , tempMovieData . guid . toString ( ) . c_str ( ) ) ;
2010-08-29 23:15:42 +02:00
2009-07-17 19:27:04 +02:00
return false ;
# endif
}
closeRecordingMovie ( ) ;
2012-12-14 18:18:20 +01:00
if ( movie_readonly )
2009-07-17 19:27:04 +02:00
{
2012-12-14 18:18:20 +01:00
// currFrameCounter at this point represents the savestate framecount
int frame_of_mismatch = CheckTimelines ( tempMovieData , currMovieData ) ;
if ( frame_of_mismatch > = 0 )
2010-08-29 23:15:42 +02:00
{
2012-12-14 18:18:20 +01:00
// Wrong timeline, do apprioriate logic here
if ( ! backupSavestates ) //If backups are disabled we can just resume normally since we can't restore so stop movie and inform user
2010-08-29 23:15:42 +02:00
{
2012-12-14 18:18:20 +01:00
FCEU_PrintError ( " Error: Savestate not in the same timeline as movie! \n Frame %d branches from current timeline \n Unable to restore backup, movie playback stopped. " , frame_of_mismatch ) ;
FCEUI_StopMovie ( ) ;
} else
FCEU_PrintError ( " Error: Savestate not in the same timeline as movie! \n Frame %d branches from current timeline " , frame_of_mismatch ) ;
return false ;
} else if ( movieMode = = MOVIEMODE_FINISHED
& & currFrameCounter > ( int ) currMovieData . records . size ( )
& & currMovieData . records . size ( ) = = tempMovieData . records . size ( ) )
{
// special case (in MOVIEMODE_FINISHED mode)
// allow loading post-movie savestates that were made after finishing current movie
2010-08-29 23:15:42 +02:00
2012-12-14 18:18:20 +01:00
} else if ( currFrameCounter > ( int ) currMovieData . records . size ( ) )
2009-07-17 19:27:04 +02:00
{
2012-12-14 18:18:20 +01:00
// this is future event state, don't allow it
//TODO: turn frame counter to red to get attention
2010-08-29 23:15:42 +02:00
if ( ! backupSavestates ) //If backups are disabled we can just resume normally since we can't restore so stop movie and inform user
{
2012-12-14 18:18:20 +01:00
FCEU_PrintError ( " Error: Savestate is from a frame (%d) after the final frame in the movie (%d). This is not permitted. \n Unable to restore backup, movie playback stopped. " , currFrameCounter , currMovieData . records . size ( ) - 1 ) ;
2010-08-29 23:15:42 +02:00
FCEUI_StopMovie ( ) ;
2012-12-14 18:18:20 +01:00
} else
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 ;
} else if ( currFrameCounter > ( int ) tempMovieData . records . size ( ) )
{
// this is post-movie savestate, don't allow it
//TODO: turn frame counter to red to get attention
if ( ! backupSavestates ) //If backups are disabled we can just resume normally since we can't restore so stop movie and inform user
{
FCEU_PrintError ( " Error: Savestate is from a frame (%d) after the final frame in the savestated movie (%d). This is not permitted. \n Unable to restore backup, movie playback stopped. " , currFrameCounter , tempMovieData . records . size ( ) - 1 ) ;
FCEUI_StopMovie ( ) ;
} else
FCEU_PrintError ( " Savestate is from a frame (%d) after the final frame in the savestated movie (%d). This is not permitted. " , currFrameCounter , tempMovieData . records . size ( ) - 1 ) ;
2009-07-17 19:27:04 +02:00
return false ;
2012-12-14 18:18:20 +01:00
} else
{
// Finally, this is a savestate file for this movie
movieMode = MOVIEMODE_PLAY ;
2009-07-17 19:27:04 +02:00
}
2016-09-18 05:43:24 +02:00
} else
2009-07-17 19:27:04 +02:00
{
2016-09-18 05:43:24 +02:00
//Read+Write mode
2010-08-29 23:15:42 +02:00
if ( currFrameCounter > ( int ) tempMovieData . records . size ( ) )
{
//This is a post movie savestate, handle it differently
2012-01-09 02:59:06 +01:00
//Replace movie contents but then switch to movie finished mode
2012-12-14 18:18:20 +01:00
currMovieData = tempMovieData ;
2010-08-29 23:15:42 +02:00
openRecordingMovie ( curMovieFilename ) ;
currMovieData . dump ( osRecordingMovie , false /*currMovieData.binaryFlag*/ ) ;
FinishPlayback ( ) ;
2016-09-18 05:43:24 +02:00
} else
2010-08-29 23:15:42 +02:00
{
//truncate before we copy, just to save some time, unless the user selects a full copy option
if ( ! fullSaveStateLoads )
2016-09-18 05:43:24 +02:00
//we can only assume this here since we have checked that the frame counter is not greater than the movie data
tempMovieData . truncateAt ( currFrameCounter ) ;
2012-12-14 18:18:20 +01:00
currMovieData = tempMovieData ;
2016-09-18 05:43:24 +02:00
FCEUMOV_IncrementRerecordCount ( ) ;
2010-08-29 23:15:42 +02:00
openRecordingMovie ( curMovieFilename ) ;
currMovieData . dump ( osRecordingMovie , false /*currMovieData.binaryFlag*/ ) ;
movieMode = MOVIEMODE_RECORD ;
2009-07-17 19:27:04 +02:00
2010-08-29 23:15:42 +02:00
}
2009-07-17 19:27:04 +02:00
}
}
2012-12-14 18:18:20 +01:00
2009-07-17 19:27:04 +02:00
load_successful = true ;
return true ;
}
void FCEUMOV_PreLoad ( void )
{
load_successful = 0 ;
}
bool FCEUMOV_PostLoad ( void )
{
2012-01-14 23:35:51 +01:00
if ( movieMode = = MOVIEMODE_INACTIVE | | movieMode = = MOVIEMODE_TASEDITOR )
2009-07-17 19:27:04 +02:00
return true ;
else
return load_successful ;
}
2016-09-18 05:43:24 +02:00
void FCEUMOV_IncrementRerecordCount ( )
{
# ifdef _S9XLUA_H
if ( ! FCEU_LuaRerecordCountSkip ( ) )
if ( movieMode ! = MOVIEMODE_TASEDITOR )
currRerecordCount + + ;
else
currMovieData . rerecordCount + + ;
# else
2018-08-13 17:04:20 +02:00
if ( movieMode ! = MOVIEMODE_TASEDITOR )
2016-09-18 05:43:24 +02:00
currRerecordCount + + ;
else
currMovieData . rerecordCount + + ;
# endif
if ( movieMode ! = MOVIEMODE_TASEDITOR )
currMovieData . rerecordCount = currRerecordCount ;
}
2009-07-17 19:27:04 +02:00
void FCEUI_MovieToggleFrameDisplay ( void )
{
frame_display = ! frame_display ;
}
2012-01-09 02:59:06 +01:00
void FCEUI_MovieToggleRerecordDisplay ( )
{
rerecord_display ^ = 1 ;
}
2009-07-17 19:27:04 +02:00
void FCEUI_ToggleInputDisplay ( void )
{
switch ( input_display )
{
case 0 :
input_display = 1 ;
break ;
case 1 :
input_display = 2 ;
break ;
case 2 :
input_display = 4 ;
break ;
default :
input_display = 0 ;
break ;
}
}
int FCEUI_GetMovieLength ( )
{
return currMovieData . records . size ( ) ;
}
int FCEUI_GetMovieRerecordCount ( )
{
return currMovieData . rerecordCount ;
}
bool FCEUI_GetMovieToggleReadOnly ( )
{
return movie_readonly ;
}
void FCEUI_SetMovieToggleReadOnly ( bool which )
{
if ( which ) //If set to readonly
{
if ( ! movie_readonly ) //If not already set
{
movie_readonly = true ;
2010-08-29 23:15:42 +02:00
FCEU_DispMessage ( " Movie is now Read-Only. " , 0 ) ;
2009-07-17 19:27:04 +02:00
}
else //Else restate message
2010-08-29 23:15:42 +02:00
FCEU_DispMessage ( " Movie is Read-Only. " , 0 ) ;
2009-07-17 19:27:04 +02:00
}
else //If set to read+write
{
if ( movie_readonly ) //If not already set
{
movie_readonly = false ;
2010-08-29 23:15:42 +02:00
FCEU_DispMessage ( " Movie is now Read+Write. " , 0 ) ;
2009-07-17 19:27:04 +02:00
}
else //Else restate message
2010-08-29 23:15:42 +02:00
FCEU_DispMessage ( " Movie is Read+Write. " , 0 ) ;
2009-07-17 19:27:04 +02:00
}
}
void FCEUI_MovieToggleReadOnly ( )
{
2010-08-29 23:15:42 +02:00
char message [ 260 ] ;
2012-12-14 18:18:20 +01:00
2009-07-17 19:27:04 +02:00
if ( movie_readonly )
2010-08-29 23:15:42 +02:00
strcpy ( message , " Movie is now Read+Write " ) ;
2009-07-17 19:27:04 +02:00
else
2010-08-29 23:15:42 +02:00
strcpy ( message , " Movie is now Read-Only " ) ;
2012-12-14 18:18:20 +01:00
2016-09-18 05:43:24 +02:00
if ( movieMode = = MOVIEMODE_INACTIVE )
2010-08-29 23:15:42 +02:00
strcat ( message , " (no movie) " ) ;
else if ( movieMode = = MOVIEMODE_FINISHED )
strcat ( message , " (finished) " ) ;
2012-12-14 18:18:20 +01:00
2010-08-29 23:15:42 +02:00
FCEU_DispMessage ( message , 0 ) ;
2009-07-17 19:27:04 +02:00
movie_readonly = ! movie_readonly ;
}
void FCEUI_MoviePlayFromBeginning ( void )
{
2012-01-14 23:35:51 +01:00
if ( movieMode = = MOVIEMODE_TASEDITOR )
2012-01-09 02:59:06 +01:00
{
# ifdef WIN32
2016-09-18 05:43:24 +02:00
handleEmuCmdByTaseditor ( EMUCMD_MOVIE_PLAY_FROM_BEGINNING ) ;
2012-01-09 02:59:06 +01:00
# endif
} else if ( movieMode ! = MOVIEMODE_INACTIVE )
2009-07-17 19:27:04 +02:00
{
2010-08-29 23:15:42 +02:00
if ( currMovieData . savestate . empty ( ) )
{
2016-09-18 05:43:24 +02:00
movie_readonly = true ;
2012-01-09 02:59:06 +01:00
movieMode = MOVIEMODE_PLAY ;
2012-12-14 18:18:20 +01:00
poweron ( true ) ;
2016-09-18 05:43:24 +02:00
currFrameCounter = 0 ;
2010-08-29 23:15:42 +02:00
FCEU_DispMessage ( " Movie is now Read-Only. Playing from beginning. " , 0 ) ;
}
else
{
2012-01-09 02:59:06 +01:00
// movie starting from savestate - reload movie file
2010-08-29 23:15:42 +02:00
string str = curMovieFilename ;
FCEUI_StopMovie ( ) ;
2012-01-09 02:59:06 +01:00
if ( FCEUI_LoadMovie ( str . c_str ( ) , 1 , 0 ) )
2010-08-29 23:15:42 +02:00
{
movieMode = MOVIEMODE_PLAY ;
2012-01-09 02:59:06 +01:00
movie_readonly = true ;
2010-08-29 23:15:42 +02:00
FCEU_DispMessage ( " Movie is now Read-Only. Playing from beginning. " , 0 ) ;
}
2012-01-09 02:59:06 +01:00
//currMovieData.loadSavestateFrom(&currMovieData.savestate); //TODO: make something like this work instead so it doesn't have to reload
2010-08-29 23:15:42 +02:00
}
2009-07-17 19:27:04 +02:00
}
2010-08-29 23:15:42 +02:00
# ifdef WIN32
SetMainWindowText ( ) ;
2009-07-17 19:27:04 +02:00
# endif
}
string FCEUI_GetMovieName ( void )
{
return curMovieFilename ;
}
bool FCEUI_MovieGetInfo ( FCEUFILE * fp , MOVIE_INFO & info , bool skipFrameCount )
{
MovieData md ;
2010-08-29 23:15:42 +02:00
if ( ! LoadFM2 ( md , fp - > stream , fp - > size , skipFrameCount ) )
2009-07-17 19:27:04 +02:00
return false ;
2012-12-14 18:18:20 +01:00
2009-07-17 19:27:04 +02:00
info . movie_version = md . version ;
info . poweron = md . savestate . size ( ) = = 0 ;
2010-08-29 23:15:42 +02:00
info . reset = false ; //Soft-reset isn't used from starting movies anymore, so this will be false, better for FCEUFILE to have that info (as |1| on the first frame indicates it
2009-07-17 19:27:04 +02:00
info . pal = md . palFlag ;
2010-08-29 23:15:42 +02:00
info . ppuflag = md . PPUflag ;
2018-08-13 17:04:20 +02:00
info . RAMInitOption = md . RAMInitOption ;
info . RAMInitSeed = md . RAMInitSeed ;
2009-07-17 19:27:04 +02:00
info . nosynchack = true ;
info . num_frames = md . records . size ( ) ;
info . md5_of_rom_used = md . romChecksum ;
info . emu_version_used = md . emuVersion ;
info . name_of_rom_used = md . romFilename ;
info . rerecord_count = md . rerecordCount ;
info . comments = md . comments ;
info . subtitles = md . subtitles ;
2010-08-29 23:15:42 +02:00
2009-07-17 19:27:04 +02:00
return true ;
}
//This function creates an array of frame numbers and corresponding strings for displaying subtitles
2010-08-29 23:15:42 +02:00
void LoadSubtitles ( MovieData & moviedata )
2009-07-17 19:27:04 +02:00
{
2012-01-09 02:59:06 +01:00
subtitleFrames . resize ( 0 ) ;
subtitleMessages . resize ( 0 ) ;
2009-07-17 19:27:04 +02:00
extern std : : vector < string > subtitles ;
2009-07-18 00:54:58 +02:00
for ( uint32 i = 0 ; i < moviedata . subtitles . size ( ) ; i + + )
2009-07-17 19:27:04 +02:00
{
2009-07-18 00:54:58 +02:00
std : : string & subtitle = moviedata . subtitles [ i ] ;
2009-07-17 19:27:04 +02:00
size_t splitat = subtitle . find_first_of ( ' ' ) ;
std : : string key , value ;
2012-12-14 18:18:20 +01:00
2009-07-17 19:27:04 +02:00
//If we can't split them, then don't process this one
if ( splitat = = std : : string : : npos )
2012-01-09 02:59:06 +01:00
{
}
2009-07-17 19:27:04 +02:00
//Else split the subtitle into the int and string arrays
else
2012-01-09 02:59:06 +01:00
{
key = subtitle . substr ( 0 , splitat ) ;
value = subtitle . substr ( splitat + 1 ) ;
subtitleFrames . push_back ( atoi ( key . c_str ( ) ) ) ;
subtitleMessages . push_back ( value ) ;
}
2009-07-17 19:27:04 +02:00
}
2010-08-29 23:15:42 +02:00
2009-07-17 19:27:04 +02:00
}
//Every frame, this will be called to determine if a subtitle should be displayed, which one, and then to display it
void ProcessSubtitles ( void )
{
if ( movieMode = = MOVIEMODE_INACTIVE ) return ;
for ( uint32 i = 0 ; i < currMovieData . subtitles . size ( ) ; i + + )
{
if ( currFrameCounter = = subtitleFrames [ i ] )
FCEU_DisplaySubtitles ( " %s " , subtitleMessages [ i ] . c_str ( ) ) ;
}
}
void FCEU_DisplaySubtitles ( char * format , . . . )
{
va_list ap ;
va_start ( ap , format ) ;
vsnprintf ( subtitleMessage . errmsg , sizeof ( subtitleMessage . errmsg ) , format , ap ) ;
va_end ( ap ) ;
2012-01-09 02:59:06 +01:00
subtitleMessage . howlong = 400 ;
2009-07-17 19:27:04 +02:00
subtitleMessage . isMovieMessage = subtitlesOnAVI ;
2010-08-29 23:15:42 +02:00
subtitleMessage . linesFromBottom = 0 ;
2009-07-17 19:27:04 +02:00
}
2009-07-18 00:54:58 +02:00
void FCEUI_CreateMovieFile ( std : : string fn )
{
MovieData md = currMovieData ; //Get current movie data
2010-08-29 23:15:42 +02:00
EMUFILE * outf = FCEUD_UTF8_fstream ( fn , " wb " ) ; //open/create file
2009-07-18 00:54:58 +02:00
md . dump ( outf , false ) ; //dump movie data
delete outf ; //clean up, delete file object
}
2009-07-17 19:27:04 +02:00
void FCEUI_MakeBackupMovie ( bool dispMessage )
{
//This function generates backup movie files
string currentFn ; //Current movie fillename
string backupFn ; //Target backup filename
string tempFn ; //temp used in back filename creation
stringstream stream ;
int x ; //Temp variable for string manip
bool exist = false ; //Used to test if filename exists
bool overflow = false ; //Used for special situation when backup numbering exceeds limit
currentFn = curMovieFilename ; //Get current moviefilename
backupFn = curMovieFilename ; //Make backup filename the same as current moviefilename
x = backupFn . find_last_of ( " . " ) ; //Find file extension
backupFn = backupFn . substr ( 0 , x ) ; //Remove extension
tempFn = backupFn ; //Store the filename at this point
for ( unsigned int backNum = 0 ; backNum < 999 ; backNum + + ) //999 = arbituary limit to backup files
{
stream . str ( " " ) ; //Clear stream
if ( backNum > 99 )
stream < < " - " < < backNum ; //assign backNum to stream
else if ( backNum < = 99 & & backNum > = 10 )
stream < < " -0 " ; //Make it 010, etc if two digits
else
stream < < " -00 " < < backNum ; //Make it 001, etc if single digit
backupFn . append ( stream . str ( ) ) ; //add number to bak filename
backupFn . append ( " .bak " ) ; //add extension
exist = CheckFileExists ( backupFn . c_str ( ) ) ; //Check if file exists
2012-12-14 18:18:20 +01:00
if ( ! exist )
2009-07-17 19:27:04 +02:00
break ; //Yeah yeah, I should use a do loop or something
else
{
backupFn = tempFn ; //Before we loop again, reset the filename
2012-12-14 18:18:20 +01:00
2009-07-17 19:27:04 +02:00
if ( backNum = = 999 ) //If 999 exists, we have overflowed, let's handle that
{
backupFn . append ( " -001.bak " ) ; //We are going to simply overwrite 001.bak
overflow = true ; //Flag that we have exceeded limit
break ; //Just in case
}
}
}
2009-07-18 00:54:58 +02:00
FCEUI_CreateMovieFile ( backupFn ) ;
2012-12-14 18:18:20 +01:00
2009-07-17 19:27:04 +02:00
//TODO, decide if fstream successfully opened the file and print error message if it doesn't
2012-12-14 18:18:20 +01:00
if ( dispMessage ) //If we should inform the user
2009-07-17 19:27:04 +02:00
{
if ( overflow )
2010-08-29 23:15:42 +02:00
FCEUI_DispMessage ( " Backup overflow, overwriting %s " , 0 , backupFn . c_str ( ) ) ; //Inform user of overflow
2009-07-17 19:27:04 +02:00
else
2010-08-29 23:15:42 +02:00
FCEUI_DispMessage ( " %s created " , 0 , backupFn . c_str ( ) ) ; //Inform user of backup filename
2009-07-17 19:27:04 +02:00
}
}
# endif