2006-07-29 05:46:15 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2008-05-22 07:43:48 +00:00
# include <assert.h>
2008-06-03 05:01:07 +00:00
# include <zlib.h>
2008-06-06 08:23:12 +00:00
# include <iomanip>
2008-05-22 07:43:48 +00:00
2008-05-27 05:29:00 +00:00
# ifdef WIN32
2006-07-29 05:46:15 +00:00
# include <windows.h>
# endif
# include "types.h"
2006-08-01 05:50:19 +00:00
# include "utils/endian.h"
2006-07-29 05:46:15 +00:00
# include "palette.h"
# include "input.h"
# include "fceu.h"
# include "driver.h"
# include "state.h"
2006-08-01 05:50:19 +00:00
# include "file.h"
2006-07-29 05:46:15 +00:00
# include "video.h"
# include "movie.h"
2008-05-23 09:58:38 +00:00
# include "utils/memory.h"
2008-06-03 04:04:04 +00:00
# include "utils/memorystream.h"
2008-05-23 09:58:38 +00:00
# include "utils/xstring.h"
2008-05-24 17:53:14 +00:00
2008-06-03 04:04:04 +00:00
using namespace std ;
2008-05-23 09:58:38 +00:00
# define MOVIE_VERSION 3 // still at 2 since the format itself is still compatible - to detect which version the movie was made with, check the fceu version stored in the movie header (e.g against FCEU_VERSION_NUMERIC)
2006-07-29 05:46:15 +00:00
extern char FileBase [ ] ;
2008-05-26 01:10:13 +00:00
extern int EmulationPaused ;
2008-06-02 14:02:02 +00:00
using namespace std ;
2006-07-29 05:46:15 +00:00
2008-05-23 09:58:38 +00:00
//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
2008-05-26 00:03:20 +00:00
//todo - better error handling for the compressed savestate
2006-07-29 05:46:15 +00:00
2008-05-26 00:03:20 +00:00
//todo - consider a MemoryBackedFile class..
//..a lot of work.. instead lets just read back from the current fcm
2006-07-29 05:46:15 +00:00
2008-05-26 00:03:20 +00:00
//todo - handle case where read+write is requested, but the file is actually readonly (could be confusing)
2006-07-29 05:46:15 +00:00
2008-05-23 09:58:38 +00:00
//sometimes we accidentally produce movie stop signals while we're trying to do other things with movies..
bool suppressMovieStop = false ;
2006-07-29 05:46:15 +00:00
2008-05-26 00:03:20 +00:00
//----movie engine main state
2008-05-23 09:58:38 +00:00
2008-06-05 06:49:11 +00:00
EMOVIEMODE movieMode = MOVIEMODE_INACTIVE ;
2006-07-29 05:46:15 +00:00
2008-05-23 09:58:38 +00:00
//this should not be set unless we are in MOVIEMODE_RECORD!
2008-06-06 08:23:12 +00:00
//FILE* fpRecordingMovie = 0;
fstream * osRecordingMovie = 0 ;
2008-05-26 00:03:20 +00:00
2008-05-23 09:58:38 +00:00
int currFrameCounter ;
2008-05-26 00:03:20 +00:00
uint32 cur_input_display = 0 ;
int pauseframe ;
bool movie_readonly = true ;
int input_display = 0 ;
int frame_display = 0 ;
2008-05-23 09:58:38 +00:00
2008-05-24 22:18:16 +00:00
SFORMAT FCEUMOV_STATEINFO [ ] = {
{ & currFrameCounter , 4 | FCEUSTATE_RLSB , " FCNT " } ,
{ 0 }
} ;
2008-05-26 07:34:40 +00:00
char curMovieFilename [ 512 ] = { 0 } ;
2008-05-28 07:41:49 +00:00
MovieData currMovieData ;
2008-05-23 09:58:38 +00:00
2008-05-28 07:41:49 +00:00
void MovieData : : clearRecordRange ( int start , int len )
2006-07-29 05:46:15 +00:00
{
2008-05-28 07:41:49 +00:00
for ( int i = 0 ; i < len ; i + + )
records [ i + start ] . clear ( ) ;
}
2008-05-26 00:03:20 +00:00
2008-05-28 07:41:49 +00:00
void MovieData : : TryDumpIncremental ( )
{
2008-06-05 06:49:11 +00:00
if ( movieMode = = MOVIEMODE_TASEDIT )
2008-05-26 00:03:20 +00:00
{
2008-05-28 07:41:49 +00:00
//only log the savestate if we are appending to the green zone
if ( currFrameCounter = = currMovieData . greenZoneCount )
2008-05-26 00:03:20 +00:00
{
2008-05-28 07:41:49 +00:00
if ( currFrameCounter < ( int ) currMovieData . records . size ( ) )
2008-05-26 00:03:20 +00:00
{
2008-06-03 05:50:58 +00:00
MovieData : : dumpSavestateTo ( & currMovieData . records [ currFrameCounter ] . savestate , Z_DEFAULT_COMPRESSION ) ;
2008-05-28 07:41:49 +00:00
currMovieData . greenZoneCount + + ;
2008-05-26 00:03:20 +00:00
}
}
}
2008-05-28 07:41:49 +00:00
}
2006-07-29 05:46:15 +00:00
2008-05-28 07:41:49 +00:00
const char MovieRecord : : mnemonics [ 8 ] = { ' A ' , ' B ' , ' S ' , ' T ' , ' U ' , ' D ' , ' L ' , ' R ' } ;
2008-06-09 08:15:20 +00:00
void MovieRecord : : dumpJoy ( std : : ostream * os , uint8 joystate )
{
//these are mnemonics for each joystick bit.
//since we usually use the regular joypad, these will be more helpful.
//but any character other than ' ' 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 )
os - > put ( mnemonic ) ;
else //otherwise write a space
os - > put ( ' ' ) ;
}
}
void MovieRecord : : parseJoy ( std : : istream * is , uint8 & joystate )
{
joystate = 0 ;
for ( int bit = 7 ; bit > = 0 ; bit - - )
{
int c = is - > get ( ) ;
if ( c = = - 1 )
return ;
if ( c ! = ' ' )
joystate | = ( 1 < < bit ) ;
}
}
void MovieRecord : : parse ( MovieData * md , std : : istream * is )
{
//by the time we get in here, the initial pipe has already been extracted
//a special case: if fourscore is enabled, parse four gamepads
if ( md - > fourscore )
{
parseJoy ( is , joysticks [ 0 ] ) ; is - > get ( ) ; //eat the pipe
parseJoy ( is , joysticks [ 1 ] ) ; is - > get ( ) ; //eat the pipe
parseJoy ( is , joysticks [ 2 ] ) ; is - > get ( ) ; //eat the pipe
parseJoy ( is , joysticks [ 3 ] ) ; is - > get ( ) ; //eat the pipe
}
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 )
{
int x , y , b ;
* is > > x > > y > > b ;
zappers [ port ] . x = x ;
zappers [ port ] . y = y ;
zappers [ port ] . b = b ;
}
is - > get ( ) ; //eat the pipe
}
}
//(no fcexp data is logged right now)
is - > get ( ) ; //eat the pipe
//should be left at a newline
}
void MovieRecord : : dump ( MovieData * md , std : : ostream * os , int index )
2008-06-03 04:04:04 +00:00
{
//todo: if we want frame numbers in the output (which we dont since we couldnt cut and paste in movies)
//but someone would need to change the parser to ignore it
//fputc('|',fp);
//fprintf(fp,"%08d",index);
2008-06-09 08:15:20 +00:00
//a special case: if fourscore is enabled, dump four gamepads
if ( md - > fourscore )
{
os - > put ( ' | ' ) ;
dumpJoy ( os , joysticks [ 0 ] ) ; os - > put ( ' | ' ) ;
dumpJoy ( os , joysticks [ 1 ] ) ; os - > put ( ' | ' ) ;
dumpJoy ( os , joysticks [ 2 ] ) ; os - > put ( ' | ' ) ;
dumpJoy ( os , joysticks [ 3 ] ) ; os - > put ( ' | ' ) ;
}
else
2008-06-03 04:04:04 +00:00
{
2008-06-09 08:15:20 +00:00
for ( int port = 0 ; port < 2 ; port + + )
2008-06-03 04:04:04 +00:00
{
2008-06-09 08:15:20 +00:00
os - > put ( ' | ' ) ;
if ( md - > ports [ port ] = = SI_GAMEPAD )
dumpJoy ( os , joysticks [ port ] ) ;
else if ( md - > ports [ port ] = = SI_ZAPPER )
* os < < setw ( 3 ) < < setfill ( ' 0 ' ) < < ( int ) zappers [ port ] . x < < ' ' < < setw ( 3 ) < < setfill ( ' 0 ' ) < < ( int ) zappers [ port ] . y < < setw ( 1 ) < < ' ' < < ( int ) zappers [ port ] . b ;
2008-06-03 04:04:04 +00:00
}
2008-06-06 08:23:12 +00:00
os - > put ( ' | ' ) ;
2008-06-03 04:04:04 +00:00
}
2008-06-09 08:15:20 +00:00
//(no fcexp data is logged right now)
os - > put ( ' | ' ) ;
2006-07-29 05:46:15 +00:00
2008-05-28 07:41:49 +00:00
//each frame is on a new line
2008-06-06 08:23:12 +00:00
os - > put ( ' \n ' ) ;
2008-05-28 07:41:49 +00:00
}
2008-05-23 09:58:38 +00:00
2008-05-28 07:41:49 +00:00
MovieData : : MovieData ( )
: version ( MOVIE_VERSION )
, emuVersion ( FCEU_VERSION_NUMERIC )
, palFlag ( false )
, poweronFlag ( false )
, resetFlag ( false )
, recordCount ( 1 )
, greenZoneCount ( 0 )
{
memset ( & romChecksum , 0 , sizeof ( MD5DATA ) ) ;
}
2006-07-29 05:46:15 +00:00
2008-05-28 07:41:49 +00:00
void MovieData : : truncateAt ( int frame )
{
records . resize ( frame ) ;
}
2008-05-23 09:58:38 +00:00
2008-06-09 08:15:20 +00:00
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
if ( key = = " version " )
installInt ( val , version ) ;
else if ( key = = " emuVersion " )
installInt ( val , emuVersion ) ;
else if ( key = = " recordCount " )
installInt ( val , recordCount ) ;
else if ( key = = " palFlag " )
installBool ( val , palFlag ) ;
else if ( key = = " poweronFlag " )
installBool ( val , poweronFlag ) ;
else if ( key = = " resetFlag " )
installBool ( val , resetFlag ) ;
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 ) ;
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 = = " savestate " )
2008-05-23 09:58:38 +00:00
{
2008-06-09 08:15:20 +00:00
int len = HexStringToBytesLength ( val ) ;
2008-05-28 07:41:49 +00:00
if ( len > = 1 )
2008-05-23 09:58:38 +00:00
{
2008-05-28 07:41:49 +00:00
savestate . resize ( len ) ;
2008-06-09 08:15:20 +00:00
StringToBytes ( val , & savestate [ 0 ] , len ) ;
2008-05-23 09:58:38 +00:00
}
2006-07-29 05:46:15 +00:00
}
2008-05-28 07:41:49 +00:00
}
2006-07-29 05:46:15 +00:00
2008-06-02 14:02:02 +00:00
void MovieData : : dump ( std : : ostream * os )
{
2008-06-03 04:04:04 +00:00
* os < < " version " < < version < < endl ;
* os < < " emuVersion " < < emuVersion < < endl ;
* os < < " recordCount " < < recordCount < < endl ;
* os < < " palFlag " < < ( palFlag ? 1 : 0 ) < < endl ;
* os < < " poweronFlag " < < ( poweronFlag ? 1 : 0 ) < < endl ;
* os < < " resetFlag " < < ( resetFlag ? 1 : 0 ) < < endl ;
* os < < " romFilename " < < romFilename < < endl ;
* os < < " romChecksum " < < BytesToString ( romChecksum . data , MD5DATA : : size ) < < endl ;
2008-06-03 05:06:54 +00:00
* os < < " guid " < < guid . toString ( ) < < endl ;
2008-06-09 08:15:20 +00:00
* os < < " fourscore " < < ( fourscore ? 1 : 0 ) < < endl ;
* os < < " port0 " < < ports [ 0 ] < < endl ;
* os < < " port1 " < < ports [ 1 ] < < endl ;
* os < < " port2 " < < ports [ 2 ] < < endl ;
2008-06-03 05:06:54 +00:00
2008-06-03 04:04:04 +00:00
if ( savestate . size ( ) ! = 0 )
* os < < " savestate " < < BytesToString ( & savestate [ 0 ] , savestate . size ( ) ) < < endl ;
for ( int i = 0 ; i < ( int ) records . size ( ) ; i + + )
2008-06-09 08:15:20 +00:00
records [ i ] . dump ( this , os , i ) ;
2008-06-02 14:02:02 +00:00
}
2008-05-28 07:41:49 +00:00
int MovieData : : dumpLen ( )
{
2008-06-03 04:04:04 +00:00
memorystream ms ;
dump ( & ms ) ;
return ms . size ( ) ;
2008-05-28 07:41:49 +00:00
}
2008-05-26 00:03:20 +00:00
2008-05-23 09:58:38 +00:00
2008-05-26 00:03:20 +00:00
int FCEUMOV_GetFrame ( void )
{
return currFrameCounter ;
}
2008-05-24 22:18:16 +00:00
2008-05-26 00:03:20 +00:00
bool FCEUMOV_ShouldPause ( void )
2008-05-23 09:58:38 +00:00
{
2008-05-26 00:03:20 +00:00
if ( pauseframe & & currFrameCounter = = pauseframe )
{
pauseframe = 0 ; //only pause once!
return true ;
}
else
{
return false ;
}
}
2008-06-05 06:49:11 +00:00
EMOVIEMODE FCEUMOV_Mode ( )
{
return movieMode ;
}
bool FCEUMOV_Mode ( EMOVIEMODE modemask )
2008-05-26 00:03:20 +00:00
{
2008-06-05 06:49:11 +00:00
return ( movieMode & modemask ) ! = 0 ;
2008-05-26 00:03:20 +00:00
}
2008-06-05 06:49:11 +00:00
bool FCEUMOV_Mode ( int modemask )
2008-05-26 00:03:20 +00:00
{
2008-06-05 06:49:11 +00:00
return FCEUMOV_Mode ( ( EMOVIEMODE ) modemask ) ;
2006-07-29 05:46:15 +00:00
}
2008-05-23 09:58:38 +00:00
//yuck... another custom text parser.
2008-06-09 08:15:20 +00:00
void LoadFM2 ( MovieData & movieData , std : : istream * fp )
2006-07-29 05:46:15 +00:00
{
2008-05-23 09:58:38 +00:00
std : : string key , value ;
enum {
2008-06-06 08:23:12 +00:00
NEWLINE , KEY , SEPARATOR , VALUE , RECORD , COMMENT
2008-05-23 09:58:38 +00:00
} state = NEWLINE ;
bool bail = false ;
for ( ; ; )
2007-02-06 18:59:15 +00:00
{
2008-05-26 00:33:27 +00:00
bool iswhitespace , isrecchar , isnewline ;
2008-06-09 08:15:20 +00:00
int c = fp - > get ( ) ;
2008-05-23 09:58:38 +00:00
if ( c = = - 1 )
goto bail ;
2008-05-26 00:33:27 +00:00
iswhitespace = ( c = = ' ' | | c = = ' \t ' ) ;
isrecchar = ( c = = ' | ' ) ;
isnewline = ( c = = 10 | | c = = 13 ) ;
2008-05-23 09:58:38 +00:00
switch ( state )
{
case NEWLINE :
if ( isnewline ) goto done ;
2008-05-24 22:18:16 +00:00
if ( iswhitespace ) goto done ;
if ( isrecchar )
goto dorecord ;
//must be a key
key = " " ;
value = " " ;
goto dokey ;
2008-05-23 09:58:38 +00:00
break ;
2008-06-06 08:23:12 +00:00
case RECORD :
{
2008-05-23 09:58:38 +00:00
dorecord :
MovieRecord record ;
2008-06-09 08:15:20 +00:00
record . parse ( & movieData , fp ) ;
2008-05-23 09:58:38 +00:00
movieData . records . push_back ( record ) ;
state = NEWLINE ;
break ;
}
2008-06-06 08:23:12 +00:00
2008-05-23 09:58:38 +00:00
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 ;
commit :
2008-06-09 08:15:20 +00:00
movieData . installValue ( key , value ) ;
2008-05-23 09:58:38 +00:00
state = NEWLINE ;
if ( bail ) break ;
done : ;
2007-02-06 18:59:15 +00:00
}
2008-05-23 09:58:38 +00:00
}
2007-02-06 18:59:15 +00:00
2008-05-23 09:58:38 +00:00
/// Stop movie playback.
void StopPlayback ( )
{
2008-05-27 04:40:03 +00:00
FCEU_DispMessageOnMovie ( " Movie playback stopped. " ) ;
2008-05-23 09:58:38 +00:00
movieMode = MOVIEMODE_INACTIVE ;
}
/// Stop movie recording
void StopRecording ( )
{
FCEU_DispMessage ( " Movie recording stopped. " ) ;
2007-02-06 18:59:15 +00:00
2008-05-26 00:03:20 +00:00
movieMode = MOVIEMODE_INACTIVE ;
2008-06-06 08:23:12 +00:00
//fclose(fpRecordingMovie);
//fpRecordingMovie = 0;
osRecordingMovie = 0 ;
2006-07-29 05:46:15 +00:00
}
2008-05-23 09:58:38 +00:00
2007-02-06 18:59:15 +00:00
void FCEUI_StopMovie ( )
2006-07-29 05:46:15 +00:00
{
2008-05-23 09:58:38 +00:00
if ( suppressMovieStop )
return ;
if ( movieMode = = MOVIEMODE_PLAY )
2007-02-06 18:59:15 +00:00
StopPlayback ( ) ;
2008-05-23 09:58:38 +00:00
else if ( movieMode = = MOVIEMODE_RECORD )
2007-02-06 18:59:15 +00:00
StopRecording ( ) ;
2008-05-26 07:34:40 +00:00
curMovieFilename [ 0 ] = 0 ;
2006-07-29 05:46:15 +00:00
}
2008-06-05 06:49:11 +00:00
static void poweron ( bool shouldDisableBatteryLoading )
{
// 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 ) ;
//mbg 5/23/08 - wtf? why would this return null?
//if(!gi) PowerNES();
assert ( gi ) ;
EmulationPaused = oldPaused ;
}
suppressMovieStop = false ;
if ( shouldDisableBatteryLoading ) disableBatteryLoading = 0 ;
suppressAddPowerCommand = 0 ;
}
void FCEUMOV_EnterTasEdit ( )
{
//stop any current movie activity
FCEUI_StopMovie ( ) ;
//clear the current movie
currFrameCounter = 0 ;
currMovieData = MovieData ( ) ;
currMovieData . guid . newGuid ( ) ;
currMovieData . palFlag = FCEUI_GetCurrentVidSystem ( 0 , 0 ) ! = 0 ;
currMovieData . poweronFlag = true ;
currMovieData . romChecksum = GameInfo - > MD5 ;
currMovieData . romFilename = FileBase ;
//reset the rom
poweron ( false ) ;
//todo - think about this
2008-06-09 08:15:20 +00:00
//ResetInputTypes();
2008-06-05 06:49:11 +00:00
//pause the emulator
FCEUI_SetEmulationPaused ( 1 ) ;
//and enter tasedit mode
movieMode = MOVIEMODE_TASEDIT ;
FCEU_DispMessage ( " Tasedit engaged " ) ;
}
void FCEUMOV_ExitTasEdit ( )
{
movieMode = MOVIEMODE_INACTIVE ;
FCEU_DispMessage ( " Tasedit disengaged " ) ;
currMovieData = MovieData ( ) ;
}
2008-06-03 05:01:07 +00:00
bool MovieData : : loadSavestateFrom ( std : : vector < char > * buf )
2008-05-28 07:41:49 +00:00
{
//dump the savestate to disk
FILE * fp = tmpfile ( ) ;
fwrite ( & ( * buf ) [ 0 ] , 1 , buf - > size ( ) , fp ) ;
fseek ( fp , 0 , SEEK_SET ) ;
//and load the state
bool success = FCEUSS_LoadFP ( fp , SSLOADPARAM_BACKUP ) ;
fclose ( fp ) ;
return success ;
}
2008-06-03 05:01:07 +00:00
void MovieData : : dumpSavestateTo ( std : : vector < char > * buf , int compressionLevel )
2008-05-28 07:41:49 +00:00
{
2008-06-03 05:01:07 +00:00
memorystream ms ( buf ) ;
FCEUSS_SaveMS ( & ms , compressionLevel ) ;
2008-06-03 05:50:58 +00:00
ms . trim ( ) ;
2008-05-28 07:41:49 +00:00
}
2006-07-29 05:46:15 +00:00
2008-05-23 09:58:38 +00:00
//begin playing an existing movie
2008-05-26 00:03:20 +00:00
void FCEUI_LoadMovie ( char * fname , bool _read_only , int _pauseframe )
2006-07-29 05:46:15 +00:00
{
2008-06-05 06:49:11 +00:00
if ( ! FCEU_IsValidUI ( FCEUI_PLAYMOVIE ) )
return ;
2008-05-23 09:58:38 +00:00
assert ( fname ) ;
2008-05-22 07:43:48 +00:00
FCEUI_StopMovie ( ) ;
2008-05-23 09:58:38 +00:00
currMovieData = MovieData ( ) ;
2008-05-26 00:03:20 +00:00
strcpy ( curMovieFilename , fname ) ;
2008-06-09 08:15:20 +00:00
std : : fstream * fp = FCEUD_UTF8_fstream ( fname , " rb " ) ;
2008-05-29 15:15:00 +00:00
if ( ! fp ) return ;
2008-05-23 09:58:38 +00:00
LoadFM2 ( currMovieData , fp ) ;
2008-06-09 08:15:20 +00:00
fp - > close ( ) ;
delete fp ;
2008-05-22 07:43:48 +00:00
// fully reload the game to reinitialize everything before playing any movie
// to try fixing nondeterministic playback of some games
{
2008-06-05 06:49:11 +00:00
poweron ( false ) ;
2008-05-22 07:43:48 +00:00
}
2008-05-23 09:58:38 +00:00
//todo - if reset flag is set, will the poweron flag be set?
//WE NEED TO LOAD A SAVESTATE
if ( ! currMovieData . poweronFlag )
2008-05-22 07:43:48 +00:00
{
2008-05-24 17:53:14 +00:00
//and load the state
2008-05-28 07:41:49 +00:00
bool success = MovieData : : loadSavestateFrom ( & currMovieData . savestate ) ;
2008-05-23 09:58:38 +00:00
if ( ! success ) return ;
2008-05-22 07:43:48 +00:00
}
2008-05-23 09:58:38 +00:00
//TODO - handle reset flag
2008-05-22 07:43:48 +00:00
2008-05-26 07:34:40 +00: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
2008-05-23 09:58:38 +00:00
if ( currMovieData . palFlag )
2008-05-22 07:43:48 +00:00
FCEUI_SetVidSystem ( 1 ) ;
else
FCEUI_SetVidSystem ( 0 ) ;
2008-06-09 08:15:20 +00:00
//force the input configuration stored in the movie to apply
FCEUD_SetInput ( currMovieData . fourscore , ( ESI ) currMovieData . ports [ 0 ] , ( ESI ) currMovieData . ports [ 1 ] , ( ESIFC ) currMovieData . ports [ 2 ] ) ;
2008-05-22 07:43:48 +00:00
2008-05-26 00:03:20 +00:00
//stuff that should only happen when we're ready to positively commit to the replay
2008-05-23 09:58:38 +00:00
currFrameCounter = 0 ;
2008-05-26 00:03:20 +00:00
pauseframe = _pauseframe ;
movie_readonly = _read_only ;
2008-05-23 09:58:38 +00:00
movieMode = MOVIEMODE_PLAY ;
2008-05-26 00:03:20 +00:00
2008-05-28 07:41:49 +00:00
currMovieData . TryDumpIncremental ( ) ;
2008-05-26 00:03:20 +00:00
if ( movie_readonly )
FCEU_DispMessage ( " Replay started Read-Only. " ) ;
else
FCEU_DispMessage ( " Replay started Read+Write. " ) ;
2008-05-23 09:58:38 +00:00
}
2008-05-22 07:43:48 +00:00
2008-05-26 00:03:20 +00:00
static void closeRecordingMovie ( )
{
2008-06-06 08:23:12 +00:00
//if(fpRecordingMovie)
// fclose(fpRecordingMovie);
if ( osRecordingMovie )
{
osRecordingMovie - > close ( ) ;
delete osRecordingMovie ;
}
2008-05-26 00:03:20 +00:00
}
static void openRecordingMovie ( const char * fname )
{
2008-06-06 08:23:12 +00:00
//fpRecordingMovie = FCEUD_UTF8fopen(fname, "wb");
//if(!fpRecordingMovie)
// FCEU_PrintError("Error opening movie output file: %s",fname);
osRecordingMovie = FCEUD_UTF8_fstream ( fname , " wb " ) ;
if ( ! osRecordingMovie )
2008-05-26 00:03:20 +00:00
FCEU_PrintError ( " Error opening movie output file: %s " , fname ) ;
strcpy ( curMovieFilename , fname ) ;
}
2008-05-22 07:43:48 +00:00
2008-06-05 06:49:11 +00:00
2008-05-23 09:58:38 +00:00
//begin recording a new movie
2008-05-27 00:51:31 +00:00
void FCEUI_SaveMovie ( char * fname , uint8 flags )
2006-07-29 05:46:15 +00:00
{
2008-06-05 06:49:11 +00:00
if ( ! FCEU_IsValidUI ( FCEUI_RECORDMOVIE ) )
return ;
2008-05-23 09:58:38 +00:00
assert ( fname ) ;
2008-05-22 07:43:48 +00:00
FCEUI_StopMovie ( ) ;
2008-05-26 00:03:20 +00:00
openRecordingMovie ( fname ) ;
2008-05-22 07:43:48 +00:00
2008-05-26 02:20:56 +00:00
currFrameCounter = 0 ;
2008-05-23 09:58:38 +00:00
currMovieData = MovieData ( ) ;
2008-05-26 00:03:20 +00:00
currMovieData . guid . newGuid ( ) ;
2008-05-22 07:43:48 +00:00
2008-05-26 00:03:20 +00:00
currMovieData . palFlag = FCEUI_GetCurrentVidSystem ( 0 , 0 ) ! = 0 ;
2008-05-23 09:58:38 +00:00
currMovieData . poweronFlag = ( flags & MOVIE_FLAG_FROM_POWERON ) ! = 0 ;
currMovieData . resetFlag = ( flags & MOVIE_FLAG_FROM_RESET ) ! = 0 ;
currMovieData . romChecksum = GameInfo - > MD5 ;
currMovieData . romFilename = FileBase ;
2008-06-09 08:15:20 +00:00
currMovieData . fourscore = FCEUI_GetInputFourscore ( ) ;
currMovieData . ports [ 0 ] = joyports [ 0 ] . type ;
currMovieData . ports [ 1 ] = joyports [ 1 ] . type ;
currMovieData . ports [ 2 ] = portFC . type ;
2008-05-22 07:43:48 +00:00
2008-05-23 09:58:38 +00:00
if ( currMovieData . poweronFlag )
2008-05-22 07:43:48 +00:00
{
2008-06-05 06:49:11 +00:00
poweron ( true ) ;
2008-05-22 07:43:48 +00:00
}
else
{
2008-06-03 05:01:07 +00:00
MovieData : : dumpSavestateTo ( & currMovieData . savestate , Z_BEST_COMPRESSION ) ;
2008-05-22 07:43:48 +00:00
}
2008-05-23 09:58:38 +00:00
//we are going to go ahead and dump the header. from now on we will only be appending frames
2008-06-06 08:23:12 +00:00
currMovieData . dump ( osRecordingMovie ) ;
2008-05-22 07:43:48 +00:00
2008-05-23 09:58:38 +00:00
//todo - think about this
2008-06-09 08:15:20 +00:00
//ResetInputTypes();
2008-05-22 07:43:48 +00:00
2008-05-23 09:58:38 +00:00
//todo - think about this
2008-05-23 04:11:35 +00:00
// trigger a reset
if ( flags & MOVIE_FLAG_FROM_RESET )
{
ResetNES ( ) ; // NOTE: this will write an FCEUNPCMD_RESET into the movie file
}
2008-05-22 07:43:48 +00:00
2008-05-26 00:03:20 +00:00
movieMode = MOVIEMODE_RECORD ;
2008-05-26 03:13:16 +00:00
movie_readonly = false ;
2008-05-26 00:03:20 +00:00
2008-05-23 09:58:38 +00:00
FCEU_DispMessage ( " Movie recording started. " ) ;
2006-07-29 05:46:15 +00:00
}
2008-05-22 07:43:48 +00: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
2008-06-08 10:22:33 +00:00
void FCEUMOV_AddInputState ( )
2006-07-29 05:46:15 +00:00
{
2008-06-05 06:49:11 +00:00
//todo - for tasedit, either dump or load depending on whether input recording is enabled
//or something like that
//(input recording is just like standard read+write movie recording with input taken from gamepad)
//otherwise, it will come from the tasedit data.
2008-05-23 09:58:38 +00:00
if ( movieMode = = MOVIEMODE_PLAY )
2008-05-22 07:43:48 +00:00
{
2008-05-23 09:58:38 +00:00
//stop when we run out of frames
if ( currFrameCounter = = currMovieData . records . size ( ) )
2008-05-22 07:43:48 +00:00
{
2008-05-26 22:45:17 +00:00
StopPlayback ( ) ;
2008-05-23 09:58:38 +00:00
}
else
{
2008-06-08 10:22:33 +00:00
MovieRecord * mr = & currMovieData . records [ currFrameCounter ] ;
joyports [ 0 ] . load ( mr ) ;
joyports [ 1 ] . load ( mr ) ;
2008-05-23 09:58:38 +00:00
}
2008-05-26 22:45:17 +00:00
//if we are on the last frame, then pause the emulator if the player requested it
if ( currFrameCounter = = currMovieData . records . size ( ) - 1 )
{
if ( FCEUD_PauseAfterPlayback ( ) )
{
FCEUI_ToggleEmulationPause ( ) ;
}
}
2008-05-23 09:58:38 +00:00
//pause the movie at a specified frame
if ( FCEUMOV_ShouldPause ( ) & & FCEUI_EmulationPaused ( ) = = 0 )
{
FCEUI_ToggleEmulationPause ( ) ;
FCEU_DispMessage ( " Paused at specified movie frame " ) ;
2008-05-22 07:43:48 +00:00
}
}
2008-05-23 09:58:38 +00:00
else if ( movieMode = = MOVIEMODE_RECORD )
2008-05-22 07:43:48 +00:00
{
2008-05-26 00:03:20 +00:00
MovieRecord mr ;
2008-05-24 22:18:16 +00:00
2008-06-08 10:22:33 +00:00
joyports [ 0 ] . log ( & mr ) ;
joyports [ 1 ] . log ( & mr ) ;
2008-06-06 08:23:12 +00:00
2008-06-09 08:15:20 +00:00
mr . dump ( & currMovieData , osRecordingMovie , currMovieData . records . size ( ) ) ;
2008-05-26 00:03:20 +00:00
currMovieData . records . push_back ( mr ) ;
2008-05-22 07:43:48 +00:00
}
2008-05-26 02:20:56 +00:00
currFrameCounter + + ;
2008-06-08 10:22:33 +00:00
extern uint8 joy [ 4 ] ;
memcpy ( & cur_input_display , joy , 4 ) ;
2006-07-29 05:46:15 +00:00
}
2008-06-08 10:22:33 +00:00
2008-05-24 22:18:16 +00:00
//TODO
2006-07-29 05:46:15 +00:00
void FCEUMOV_AddCommand ( int cmd )
{
2008-05-24 22:18:16 +00:00
// do nothing if not recording a movie
if ( movieMode ! = MOVIEMODE_RECORD )
return ;
2008-05-22 07:43:48 +00:00
//printf("%d\n",cmd);
2006-07-29 05:46:15 +00:00
2008-05-23 09:58:38 +00:00
//MBG TODO BIG TODO TODO TODO
//DoEncode((cmd>>3)&0x3,cmd&0x7,1);
2006-07-29 05:46:15 +00:00
}
void FCEU_DrawMovies ( uint8 * XBuf )
{
2008-05-26 01:55:45 +00:00
if ( frame_display )
{
char counterbuf [ 32 ] = { 0 } ;
if ( movieMode = = MOVIEMODE_PLAY )
sprintf ( counterbuf , " %d/%d " , currFrameCounter , currMovieData . records . size ( ) ) ;
else if ( movieMode = = MOVIEMODE_RECORD )
sprintf ( counterbuf , " %d " , currMovieData . records . size ( ) ) ;
if ( counterbuf [ 0 ] )
DrawTextTrans ( XBuf + FCEU_TextScanlineOffsetFromBottom ( 24 ) , 256 , ( uint8 * ) counterbuf , 0x20 + 0x80 ) ;
}
2006-07-29 05:46:15 +00:00
}
2008-06-02 14:02:02 +00:00
int FCEUMOV_WriteState ( std : : ostream * os )
{
//we are supposed to dump the movie data into the savestate
if ( movieMode = = MOVIEMODE_RECORD | | movieMode = = MOVIEMODE_PLAY )
{
int todo = currMovieData . dumpLen ( ) ;
if ( os )
currMovieData . dump ( os ) ;
return todo ;
}
else return 0 ;
}
2008-05-24 22:18:16 +00:00
static bool load_successful ;
2006-07-29 05:46:15 +00:00
2008-05-26 00:03:20 +00:00
bool FCEUMOV_ReadState ( FILE * st , uint32 size )
2006-07-29 05:46:15 +00:00
{
2008-05-24 22:18:16 +00:00
load_successful = false ;
//write the state to disk so we can reload
2008-06-09 08:15:20 +00:00
std : : vector < char > buf ( size ) ;
2008-05-24 22:18:16 +00:00
fread ( & buf [ 0 ] , 1 , size , st ) ;
2008-06-03 05:50:58 +00:00
//---------
//(debug)
//FILE* wtf = fopen("d:\\wtf.txt","wb");
//fwrite(&buf[0],1,size,wtf);
//fclose(wtf);
//---------
2008-06-09 08:15:20 +00:00
memorystream mstemp ( & buf ) ;
2008-05-26 00:03:20 +00:00
MovieData tempMovieData = MovieData ( ) ;
2008-06-09 08:15:20 +00:00
LoadFM2 ( tempMovieData , & mstemp ) ;
2008-05-24 22:18:16 +00:00
2008-05-24 22:31:58 +00:00
//complex TAS logic for when a savestate is loaded:
2008-05-26 00:03:20 +00:00
//----------------
//if we are playing or recording and toggled read-only:
// then, the movie we are playing must match the guid of the one stored in the savestate or else error.
// the savestate is assumed to be in the same timeline as the current movie.
// if the current movie is not long enough to get to the savestate's frame#, then it is an error.
// the movie contained in the savestate will be discarded.
// the emulator will be put into play mode.
//if we are playing or recording and toggled read+write
// then, the movie we are playing must match the guid of the one stored in the savestate or else error.
// the movie contained in the savestate will be loaded into memory
// the frames in the movie after the savestate frame will be discarded
2008-05-26 05:54:21 +00:00
// the in-memory movie will have its rerecord count incremented
2008-05-26 00:03:20 +00:00
// the in-memory movie will be dumped to disk as fcm.
// the emulator will be put into record mode.
2008-05-24 22:31:58 +00:00
//if we are doing neither:
2008-05-26 00:03:20 +00:00
// then, we must discard this movie and just load the savestate
2008-05-24 22:31:58 +00:00
2008-05-24 22:18:16 +00:00
2008-05-26 00:03:20 +00:00
if ( movieMode = = MOVIEMODE_PLAY | | movieMode = = MOVIEMODE_RECORD )
{
//handle moviefile mismatch
if ( tempMovieData . guid ! = currMovieData . guid )
{
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 ( ) ) ;
return false ;
}
2008-05-23 09:58:38 +00:00
2008-05-26 00:03:20 +00:00
closeRecordingMovie ( ) ;
if ( movie_readonly )
{
//if the frame counter is longer than our current movie, then error
2008-05-26 23:29:00 +00:00
if ( currFrameCounter > ( int ) currMovieData . records . size ( ) )
2008-05-26 00:03:20 +00:00
{
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 ;
}
else
{
//truncate before we copy, just to save some time
tempMovieData . truncateAt ( currFrameCounter ) ;
currMovieData = tempMovieData ;
2008-05-26 05:54:21 +00:00
currMovieData . recordCount + + ;
2008-05-23 09:58:38 +00:00
2008-05-26 00:03:20 +00:00
openRecordingMovie ( curMovieFilename ) ;
2008-06-06 08:23:12 +00:00
currMovieData . dump ( osRecordingMovie ) ;
2008-05-26 00:03:20 +00:00
movieMode = MOVIEMODE_RECORD ;
}
}
load_successful = true ;
2008-05-23 09:58:38 +00:00
//// Maximus: Show the last input combination entered from the
//// movie within the state
//if(current!=0) // <- mz: only if playing or recording a movie
// memcpy(&cur_input_display, joop, 4);
2008-05-22 07:43:48 +00:00
2008-05-26 00:03:20 +00:00
return true ;
2006-07-29 05:46:15 +00:00
}
void FCEUMOV_PreLoad ( void )
{
load_successful = 0 ;
}
2008-06-05 06:49:11 +00:00
bool FCEUMOV_PostLoad ( void )
2006-07-29 05:46:15 +00:00
{
2008-06-05 06:49:11 +00:00
if ( movieMode = = MOVIEMODE_INACTIVE | | movieMode = = MOVIEMODE_TASEDIT )
return true ;
2006-07-29 05:46:15 +00:00
else
2008-06-05 06:49:11 +00:00
return load_successful ;
}
//int FCEUI_IsMovieActive(void)
//{
// //this is a lame method. we should change all the fceu code that uses it to call the
// //IsRecording or IsPlaying methods
// //return > 0 for recording, < 0 for playback
// if(FCEUMOV_IsRecording()) return 1;
// else if(FCEUMOV_IsPlaying()) return -1;
// else return 0;
//}
2006-07-29 05:46:15 +00:00
void FCEUI_MovieToggleFrameDisplay ( void )
{
2008-05-26 01:55:45 +00:00
frame_display = ! frame_display ;
2006-07-29 05:46:15 +00:00
}
void FCEUI_ToggleInputDisplay ( void )
{
2008-05-26 02:05:01 +00:00
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 ;
}
2006-07-29 05:46:15 +00:00
}
2008-05-26 00:03:20 +00:00
bool FCEUI_GetMovieToggleReadOnly ( )
2006-07-29 05:46:15 +00:00
{
2008-05-26 00:03:20 +00:00
return movie_readonly ;
}
void FCEUI_MovieToggleReadOnly ( )
{
if ( movie_readonly )
FCEU_DispMessage ( " Movie is now Read+Write. " ) ;
2006-07-29 05:46:15 +00:00
else
2008-05-26 00:03:20 +00:00
FCEU_DispMessage ( " Movie is now Read-Only. " ) ;
movie_readonly = ! movie_readonly ;
2006-07-29 05:46:15 +00:00
}
2008-06-04 00:53:09 +00:00
void FCEUI_MoviePlayFromBeginning ( void )
{
if ( movieMode ! = MOVIEMODE_INACTIVE )
{
char * fname = strdup ( curMovieFilename ) ;
FCEUI_LoadMovie ( fname , true , 0 ) ;
FCEU_DispMessage ( " Movie is now Read-Only. Playing from beginning. " ) ;
free ( fname ) ;
}
}
2008-06-09 08:15:20 +00:00
bool FCEUI_MovieGetInfo ( const char * fname , MOVIE_INFO * info )
2006-07-29 05:46:15 +00:00
{
2008-05-23 09:58:38 +00:00
memset ( info , 0 , sizeof ( MOVIE_INFO ) ) ;
2006-07-29 05:46:15 +00:00
2008-05-23 09:58:38 +00:00
MovieData md ;
2008-06-09 08:15:20 +00:00
std : : fstream * fp = FCEUD_UTF8_fstream ( fname , " rb " ) ;
if ( ! fp ) return false ;
2008-05-23 09:58:38 +00:00
LoadFM2 ( md , fp ) ;
2008-06-09 08:15:20 +00:00
fp - > close ( ) ;
delete fp ;
2008-05-22 07:43:48 +00:00
2008-05-23 09:58:38 +00:00
info - > movie_version = md . version ;
info - > poweron = md . poweronFlag ;
info - > reset = md . resetFlag ;
info - > pal = md . palFlag ;
info - > nosynchack = true ;
info - > num_frames = md . records . size ( ) ;
info - > md5_of_rom_used = md . romChecksum ;
info - > md5_of_rom_used_present = 1 ;
info - > emu_version_used = md . emuVersion ;
info - > name_of_rom_used = md . romFilename ;
2008-05-26 21:58:24 +00:00
info - > rerecord_count = md . recordCount ;
2008-05-22 07:43:48 +00:00
2008-06-09 08:15:20 +00:00
return true ;
2008-05-23 09:58:38 +00:00
}
2008-05-22 07:43:48 +00:00
2008-05-26 00:03:20 +00:00
//struct MovieHeader
//{
//uint32 magic; // +0
//uint32 version=2; // +4
//uint8 flags[4]; // +8
//uint32 length_frames; // +12
//uint32 rerecord_count; // +16
//uint32 movie_data_size; // +20
//uint32 offset_to_savestate; // +24, should be 4-byte-aligned
//uint32 offset_to_movie_data; // +28, should be 4-byte-aligned
//uint8 md5_of_rom_used[16]; // +32
//uint32 version_of_emu_used // +48
//char name_of_rom_used[] // +52, utf-8, null-terminated
//char metadata[]; // utf-8, null-terminated
//uint8 padding[];
//uint8 savestate[]; // always present, even in a "from reset" recording
//uint8 padding[]; // used for byte-alignment
//uint8 movie_data[];
//}
// backwards compat
//static void FCEUI_LoadMovie_v1(char *fname, int _read_only);
//static int FCEUI_MovieGetInfo_v1(const char* fname, MOVIE_INFO* info);
//
//#define MOVIE_MAGIC 0x1a4d4346 // FCM\x1a
2008-05-23 09:58:38 +00:00
//
//int _old_FCEUI_MovieGetInfo(const char* fname, MOVIE_INFO* info)
//{
// //mbg: wtf?
// //MovieFlushHeader();
//
// // main get info part of function
// {
// uint32 magic;
// uint32 version;
// uint8 _flags[4];
//
// FILE* fp = FCEUD_UTF8fopen(fname, "rb");
// if(!fp)
// return 0;
//
// read32le(&magic, fp);
// if(magic != MOVIE_MAGIC)
// {
// fclose(fp);
// return 0;
// }
//
// read32le(&version, fp);
// if(version != MOVIE_VERSION)
// {
// fclose(fp);
// if(version == 1)
// return FCEUI_MovieGetInfo_v1(fname, info);
// else
// return 0;
// }
//
// info->movie_version = MOVIE_VERSION;
//
// fread(_flags, 1, 4, fp);
//
// info->flags = _flags[0];
// read32le(&info->num_frames, fp);
// read32le(&info->rerecord_count, fp);
//
// if(access(fname, W_OK))
// info->read_only = 1;
// else
// info->read_only = 0;
//
// fseek(fp, 12, SEEK_CUR); // skip movie_data_size, offset_to_savestate, and offset_to_movie_data
//
// fread(&info->md5_of_rom_used, 1, 16, fp);
// info->md5_of_rom_used_present = 1;
//
// read32le(&info->emu_version_used, fp);
//
// // I probably could have planned this better...
// {
// char str[256];
// size_t r;
// uint32 p; //mbg merge 7/17/06 change to uint32
// int p2=0;
// char last_c=32;
//
// if(info->name_of_rom_used && info->name_of_rom_used_size)
// info->name_of_rom_used[0]='\0';
//
// r=fread(str, 1, 256, fp);
// while(r > 0)
// {
// for(p=0; p<r && last_c != '\0'; ++p)
// {
// if(info->name_of_rom_used && info->name_of_rom_used_size && (p2 < info->name_of_rom_used_size-1))
// {
// info->name_of_rom_used[p2]=str[p];
// p2++;
// last_c=str[p];
// }
// }
// if(p<r)
// {
// memmove(str, str+p, r-p);
// r -= p;
// break;
// }
// r=fread(str, 1, 256, fp);
// }
//
// p2=0;
// last_c=32;
//
// if(info->metadata && info->metadata_size)
// info->metadata[0]='\0';
//
// while(r > 0)
// {
// for(p=0; p<r && last_c != '\0'; ++p)
// {
// if(info->metadata && info->metadata_size && (p2 < info->metadata_size-1))
// {
// info->metadata[p2]=str[p];
// p2++;
// last_c=str[p];
// }
// }
// if(p != r)
// break;
// r=fread(str, 1, 256, fp);
// }
//
// if(r<=0)
// {
// // somehow failed to read romname and metadata
// fclose(fp);
// return 0;
// }
// }
//
// // check what hacks are necessary
// fseek(fp, 24, SEEK_SET); // offset_to_savestate offset
// uint32 temp_savestate_offset;
// read32le(&temp_savestate_offset, fp);
// if(temp_savestate_offset != 0xFFFFFFFF)
// {
// if(fseek(fp, temp_savestate_offset, SEEK_SET))
// {
// fclose(fp);
// return 0;
// }
//
// //don't really load, just load to find what's there then load backup
// if(!FCEUSS_LoadFP(fp,SSLOADPARAM_DUMMY)) return 0;
// }
//
// fclose(fp);
// return 1;
// }
//}
2006-07-29 05:46:15 +00:00
/*
2008-05-22 07:43:48 +00:00
Backwards compat
2006-07-29 05:46:15 +00:00
*/
/*
struct MovieHeader_v1
{
2008-05-22 07:43:48 +00:00
uint32 magic ;
uint32 version = 1 ;
uint8 flags [ 4 ] ;
uint32 length_frames ;
uint32 rerecord_count ;
uint32 movie_data_size ;
uint32 offset_to_savestate ;
uint32 offset_to_movie_data ;
uint16 metadata_ucs2 [ ] ; // ucs-2, ick! sizeof(metadata) = offset_to_savestate - MOVIE_HEADER_SIZE
2006-07-29 05:46:15 +00:00
}
*/
2008-05-23 09:58:38 +00:00
//
//#define MOVIE_V1_HEADER_SIZE 32
//
//static void FCEUI_LoadMovie_v1(char *fname, int _read_only)
//{
// FILE *fp;
// char *fn = NULL;
//
// FCEUI_StopMovie();
//
// if(!fname)
// fname = fn = FCEU_MakeFName(FCEUMKF_MOVIE,0,0);
//
// // check movie_readonly
// movie_readonly = _read_only;
// if(access(fname, W_OK))
// movie_readonly = 2;
//
// fp = FCEUD_UTF8fopen(fname, (movie_readonly>=2) ? "rb" : "r+b");
//
// if(fn)
// {
// free(fn);
// fname = NULL;
// }
//
// if(!fp) return;
//
// // read header
// {
// uint32 magic;
// uint32 version;
// uint8 flags[4];
// uint32 fc;
//
// read32le(&magic, fp);
// if(magic != MOVIE_MAGIC)
// {
// fclose(fp);
// return;
// }
//
// read32le(&version, fp);
// if(version != 1)
// {
// fclose(fp);
// return;
// }
//
// fread(flags, 1, 4, fp);
// read32le(&fc, fp);
// read32le(&rerecord_count, fp);
// read32le(&moviedatasize, fp);
// read32le(&savestate_offset, fp);
// read32le(&firstframeoffset, fp);
// if(fseek(fp, savestate_offset, SEEK_SET))
// {
// fclose(fp);
// return;
// }
//
// if(flags[0] & MOVIE_FLAG_NOSYNCHACK)
// movieSyncHackOn=0;
// else
// movieSyncHackOn=1;
// }
//
// // fully reload the game to reinitialize everything before playing any movie
// // to try fixing nondeterministic playback of some games
// {
// extern char lastLoadedGameName [2048];
// extern int disableBatteryLoading, suppressAddPowerCommand;
// suppressAddPowerCommand=1;
// suppressMovieStop=true;
// {
// FCEUGI * gi = FCEUI_LoadGame(lastLoadedGameName, 0);
// if(!gi)
// PowerNES();
// }
// suppressMovieStop=false;
// suppressAddPowerCommand=0;
// }
//
// if(!FCEUSS_LoadFP(fp,SSLOADPARAM_BACKUP)) return;
//
// ResetInputTypes();
//
// fseek(fp, firstframeoffset, SEEK_SET);
// moviedata = (uint8*)realloc(moviedata, moviedatasize);
// fread(moviedata, 1, moviedatasize, fp);
//
// framecount = 0; // movies start at frame 0!
// frameptr = 0;
// current = 0;
// slots = fp;
//
// memset(joop,0,sizeof(joop));
// current = -1 - current;
// framets=0;
// nextts=0;
// nextd = -1;
// FCEU_DispMessage("Movie playback started.");
//}
//
//static int FCEUI_MovieGetInfo_v1(const char* fname, MOVIE_INFO* info)
//{
// uint32 magic;
// uint32 version;
// uint8 _flags[4];
// uint32 savestateoffset;
// uint8 tmp[MOVIE_MAX_METADATA<<1];
// int metadata_length;
//
// FILE* fp = FCEUD_UTF8fopen(fname, "rb");
// if(!fp)
// return 0;
//
// read32le(&magic, fp);
// if(magic != MOVIE_MAGIC)
// {
// fclose(fp);
// return 0;
// }
//
// read32le(&version, fp);
// if(version != 1)
// {
// fclose(fp);
// return 0;
// }
//
// info->movie_version = 1;
// info->emu_version_used = 0; // unknown
//
// fread(_flags, 1, 4, fp);
//
// info->flags = _flags[0];
// read32le(&info->num_frames, fp);
// read32le(&info->rerecord_count, fp);
//
// if(access(fname, W_OK))
// info->read_only = 1;
// else
// info->read_only = 0;
//
// fseek(fp, 4, SEEK_CUR);
// read32le(&savestateoffset, fp);
//
// metadata_length = (int)savestateoffset - MOVIE_V1_HEADER_SIZE;
// if(metadata_length > 0)
// {
// //int i; //mbg merge 7/17/06 removed
//
// metadata_length >>= 1;
// if(metadata_length >= MOVIE_MAX_METADATA)
// metadata_length = MOVIE_MAX_METADATA-1;
//
// fseek(fp, MOVIE_V1_HEADER_SIZE, SEEK_SET);
// fread(tmp, 1, metadata_length<<1, fp);
// }
//
// // turn old ucs2 metadata into utf8
// if(info->metadata && info->metadata_size)
// {
// char* ptr=info->metadata;
// char* ptr_end=info->metadata+info->metadata_size-1;
// int c_ptr=0;
// while(ptr<ptr_end && c_ptr<metadata_length)
// {
// uint16 c=(tmp[c_ptr<<1] | (tmp[(c_ptr<<1)+1] << 8));
// //mbg merge 7/17/06 changed to if..elseif
// if(c<=0x7f)
// *ptr++ = (char)(c&0x7f);
// else if(c<=0x7FF)
// if(ptr+1>=ptr_end)
// ptr_end=ptr;
// else
// {
// *ptr++=(0xc0 | (c>>6));
// *ptr++=(0x80 | (c & 0x3f));
// }
// else
// if(ptr+2>=ptr_end)
// ptr_end=ptr;
// else
// {
// *ptr++=(0xe0 | (c>>12));
// *ptr++=(0x80 | ((c>>6) & 0x3f));
// *ptr++=(0x80 | (c & 0x3f));
// }
//
// c_ptr++;
// }
// *ptr='\0';
// }
//
// // md5 info not available from v1
// info->md5_of_rom_used_present = 0;
// // rom name used for the movie not available from v1
// if(info->name_of_rom_used && info->name_of_rom_used_size)
// info->name_of_rom_used[0] = '\0';
//
// // check what hacks are necessary
// fseek(fp, 24, SEEK_SET); // offset_to_savestate offset
// uint32 temp_savestate_offset;
// read32le(&temp_savestate_offset, fp);
// if(fseek(fp, temp_savestate_offset, SEEK_SET))
// {
// fclose(fp);
// return 0;
// }
// if(!FCEUSS_LoadFP(fp,SSLOADPARAM_DUMMY)) return 0; // 2 -> don't really load, just load to find what's there then load backup
//
//
// fclose(fp);
// return 1;
//}
//
//// PlayMovie / MoviePlay function
//void _old_FCEUI_LoadMovie(char *fname, int _read_only, int _stopframe)
//{
// char buffer [512];
// //fname = (char*)convertToFCM(fname,buffer);
//
// FILE *fp;
// char *fn = NULL;
//
// FCEUI_StopMovie();
//
// if(!fname)
// fname = fn = FCEU_MakeFName(FCEUMKF_MOVIE,0,0);
//
// char origname[512];
// strcpy(origname,fname);
//
// pauseframe = _stopframe;
//
// // check movie_readonly
// movie_readonly = _read_only;
// if(access(fname, W_OK))
// movie_readonly = 2;
//
// fp = FCEUD_UTF8fopen(fname, (movie_readonly>=2) ? "rb" : "r+b");
//
// if(fn)
// {
// free(fn);
// fname = NULL;
// }
//
// if(!fp) return;
//
// // read header
//
// uint32 magic;
// uint32 version;
// uint8 flags[4];
//
// read32le(&magic, fp);
// if(magic != MOVIE_MAGIC)
// {
// fclose(fp);
// return;
// }
// //DEBUG_COMPARE_RAM(__LINE__);
//
// read32le(&version, fp);
// if(version == 1)
// {
// // attempt to load previous version's format
// fclose(fp);
// FCEUI_LoadMovie_v1(fname, _read_only);
// return;
// }
// else if(version == MOVIE_VERSION)
// {}
// else
// {
// // unsupported version
// fclose(fp);
// return;
// }
//
// fread(flags, 1, 4, fp);
// read32le(&framecount, fp);
// read32le(&rerecord_count, fp);
// read32le(&moviedatasize, fp);
// read32le(&savestate_offset, fp);
// read32le(&firstframeoffset, fp);
//
// // FCEU_PrintError("flags[0] & MOVIE_FLAG_NOSYNCHACK=%d",flags[0] & MOVIE_FLAG_NOSYNCHACK);
// if(flags[0] & MOVIE_FLAG_NOSYNCHACK)
// movieSyncHackOn=0;
// else
// movieSyncHackOn=1;
//
// if(flags[0] & MOVIE_FLAG_PAL)
// {
// FCEUI_SetVidSystem(1);
// }
// else
// {
// FCEUI_SetVidSystem(0);
// }
//
//
// // fully reload the game to reinitialize everything before playing any movie
// // to try fixing nondeterministic playback of some games
// {
// extern char lastLoadedGameName [2048];
// extern int disableBatteryLoading, suppressAddPowerCommand;
// suppressAddPowerCommand=1;
// suppressMovieStop=true;
// {
// FCEUGI * gi = FCEUI_LoadGame(lastLoadedGameName, 0);
// if(!gi)
// PowerNES();
// }
// suppressMovieStop=false;
// suppressAddPowerCommand=0;
// }
//
// if(flags[0] & MOVIE_FLAG_FROM_POWERON)
// {
// //don't need to load a savestate
// //there shouldn't be a savestate!
// if(savestate_offset != 0xFFFFFFFF)
// FCEU_PrintError("Savestate found in a start-from-poweron movie!");
// }
// else
// {
// if(savestate_offset == 0xFFFFFFFF)
// FCEU_PrintError("No savestate found in a start-from-savestate movie!");
//
// if(fseek(fp, savestate_offset, SEEK_SET))
// {
// fclose(fp);
// return;
// }
//
// if(!FCEUSS_LoadFP(fp,SSLOADPARAM_BACKUP))
// return;
// }
//
// if(flags[0] & MOVIE_FLAG_PAL)
// {
// FCEUI_SetVidSystem(1);
// }
// else
// {
// FCEUI_SetVidSystem(0);
// }
// ResetInputTypes();
//
// fseek(fp, firstframeoffset, SEEK_SET);
// moviedata = (uint8*)realloc(moviedata, moviedatasize);
// fread(moviedata, 1, moviedatasize, fp);
//
// framecount = 0; // movies start at frame 0!
// frameptr = 0;
// current = 0;
// slots = fp;
//
// memset(joop,0,sizeof(joop));
// current = -1 - current;
// framets=0;
// nextts=0;
// nextd = -1;
//
// FCEU_DispMessage("Movie playback started.");
//
// strcpy(curMovieFilename, origname);
//}
//static void DoEncode(int joy, int button, int dummy)
//{
// uint8 d;
//
// d = 0;
//
// if(framets >= 65536)
// d = 3 << 5;
// else if(framets >= 256)
// d = 2 << 5;
// else if(framets > 0)
// d = 1 << 5;
//
// if(dummy) d|=0x80;
//
// d |= joy << 3;
// d |= button;
//
// movie_writechar(d);
// //printf("Wr: %02x, %d\n",d,slots[current-1]);
// while(framets)
// {
// movie_writechar(framets & 0xff);
// //printf("Wrts: %02x\n",framets & 0xff);
// framets >>= 8;
// }
//}