tas logic done?

This commit is contained in:
zeromus 2008-05-26 00:03:20 +00:00
parent 2c6492cafb
commit 52bb5ccbd8
17 changed files with 467 additions and 505 deletions

View File

@ -121,16 +121,15 @@ void DrawTextTrans(uint8 *dest, uint32 width, uint8 *textmsg, uint8 fgcolor)
for(; *textmsg; ++textmsg)
{
int ch, wid;
unsigned ny, nx;
if(*textmsg == '\n') { x=beginx; y+=6; continue; }
ch = FixJoedChar(*textmsg);
wid = JoedCharWidth(*textmsg);
for(ny=0; ny<5; ++ny)
for(int ny=0; ny<5; ++ny)
{
uint8 d = Font6x5[ch*6 + 1+ny];
for(nx=0; nx<wid; ++nx)
for(int nx=0; nx<wid; ++nx)
{
int c = (d >> (7-nx)) & 1;
if(c)

View File

@ -185,6 +185,11 @@ void FCEUD_LoadStateFrom(void);
#define MOVIE_FLAG_PAL (1<<2)
// set in newer version, used for old movie compatibility
//TODO - only use this flag to print a warning that the sync might be bad
//so that we can get rid of the sync hack code
#define MOVIE_FLAG_NOSYNCHACK (1<<4)
#define MOVIE_MAX_METADATA 512
typedef struct
@ -203,13 +208,14 @@ typedef struct
} MOVIE_INFO;
void FCEUI_SaveMovie(char *fname, uint8 flags, const char* metadata);
void FCEUI_LoadMovie(char *fname, int read_only, int _stopframe);
void FCEUI_LoadMovie(char *fname, bool read_only, int _stopframe);
void FCEUI_StopMovie(void);
int FCEUI_IsMovieActive(void);
int FCEUI_MovieGetInfo(const char* fname, MOVIE_INFO* /* [in, out] */ info);
char* FCEUI_MovieGetCurrentName(int addSlotNumber);
void FCEUI_MovieToggleReadOnly(void);
void FCEUI_MovieToggleFrameDisplay(void);
bool FCEUI_GetMovieToggleReadOnly();
void FCEUI_MovieToggleFrameDisplay();
void FCEUI_ToggleInputDisplay(void);

View File

@ -159,7 +159,6 @@ static CFGSTRUCT fceuconfig[] = {
ADDCFGSTRUCT(InputConfig),
ADDCFGSTRUCT(HotkeyConfig),
AC(moviereadonly),
AC(autoHoldKey),
AC(autoHoldClearKey),
AC(frame_display),

View File

@ -64,8 +64,7 @@ void KeyboardUpdateState(void)
#define KEY_REPEAT_REPEATING_DELAY (6) // must be >= 1 and <= 255
#define KEY_JUST_DOWN_DURATION (4) // must be >= 1 and <= 255
int i;
for(i = 0 ; i < 256 ; i++)
for(int i = 0 ; i < 256 ; i++)
if(tk[i])
if(keys_nr[i] < 255)
keys_nr[i]++; // activate key, and count up for repeat
@ -77,7 +76,7 @@ void KeyboardUpdateState(void)
memcpy(keys,keys_nr,256);
// key-down detection
for(i = 0 ; i < 256 ; i++)
for(int i = 0 ; i < 256 ; i++)
if(!keys_nr[i])
{
keys_jd[i] = 0;
@ -98,8 +97,8 @@ void KeyboardUpdateState(void)
keys_jd[i] = 1;
// key repeat
for(i = 0 ; i < 256 ; i++)
if(keys[i] >= KEY_REPEAT_INITIAL_DELAY && !(keys[i]%KEY_REPEAT_REPEATING_DELAY))
for(int i = 0 ; i < 256 ; i++)
if((int)keys[i] >= KEY_REPEAT_INITIAL_DELAY && !(keys[i]%KEY_REPEAT_REPEATING_DELAY))
keys[i] = 0;
}
break;

View File

@ -25,8 +25,6 @@
extern int maxconbskip;
extern int ffbskip;
static int moviereadonly = 1;
static int fullscreen = 0;
static int soundflush = 0;
// Flag that indicates whether Game Genie is enabled or not.

View File

@ -68,18 +68,18 @@ static struct
//{ EMUCMD_LOAD_STATE_SLOT_7, SCAN_F7, },
//{ EMUCMD_LOAD_STATE_SLOT_8, SCAN_F8, },
//{ EMUCMD_LOAD_STATE_SLOT_9, SCAN_F9, },
/* { EMUCMD_MOVIE_SLOT_0, SCAN_0 | CMD_KEY_ALT, },
{ EMUCMD_MOVIE_SLOT_1, SCAN_1 | CMD_KEY_ALT, },
{ EMUCMD_MOVIE_SLOT_2, SCAN_2 | CMD_KEY_ALT, },
{ EMUCMD_MOVIE_SLOT_3, SCAN_3 | CMD_KEY_ALT, },
{ EMUCMD_MOVIE_SLOT_4, SCAN_4 | CMD_KEY_ALT, },
{ EMUCMD_MOVIE_SLOT_5, SCAN_5 | CMD_KEY_ALT, },
{ EMUCMD_MOVIE_SLOT_6, SCAN_6 | CMD_KEY_ALT, },
{ EMUCMD_MOVIE_SLOT_7, SCAN_7 | CMD_KEY_ALT, },
{ EMUCMD_MOVIE_SLOT_8, SCAN_8 | CMD_KEY_ALT, },
{ EMUCMD_MOVIE_SLOT_9, SCAN_9 | CMD_KEY_ALT, },
{ EMUCMD_MOVIE_RECORD, SCAN_F5 | CMD_KEY_ALT, },
{ EMUCMD_MOVIE_REPLAY, SCAN_F7 | CMD_KEY_ALT, },*/
//{ EMUCMD_MOVIE_SLOT_0, SCAN_0 | CMD_KEY_ALT, },
//{ EMUCMD_MOVIE_SLOT_1, SCAN_1 | CMD_KEY_ALT, },
//{ EMUCMD_MOVIE_SLOT_2, SCAN_2 | CMD_KEY_ALT, },
//{ EMUCMD_MOVIE_SLOT_3, SCAN_3 | CMD_KEY_ALT, },
//{ EMUCMD_MOVIE_SLOT_4, SCAN_4 | CMD_KEY_ALT, },
//{ EMUCMD_MOVIE_SLOT_5, SCAN_5 | CMD_KEY_ALT, },
//{ EMUCMD_MOVIE_SLOT_6, SCAN_6 | CMD_KEY_ALT, },
//{ EMUCMD_MOVIE_SLOT_7, SCAN_7 | CMD_KEY_ALT, },
//{ EMUCMD_MOVIE_SLOT_8, SCAN_8 | CMD_KEY_ALT, },
//{ EMUCMD_MOVIE_SLOT_9, SCAN_9 | CMD_KEY_ALT, },
//{ EMUCMD_MOVIE_RECORD, SCAN_F5 | CMD_KEY_ALT, },
//{ EMUCMD_MOVIE_REPLAY, SCAN_F7 | CMD_KEY_ALT, },
};
#define NUM_DEFAULT_MAPPINGS (sizeof(DefaultCommandMapping)/sizeof(DefaultCommandMapping[0]))

View File

@ -6,14 +6,13 @@
bool autoInfo1003 = true; //This is a hacky variable that checks when dialog 1003 is given a
//value by the program rather than the user. This will be used when deciding to automatically make the stop movie checkbox checked.
extern int movieConvertOffset1, movieConvertOffset2, movieConvertOK;
extern FCEUGI *GameInfo;
static int ReplayDialogReadOnlyStatus = 0;
//retains the state of the readonly checkbox
static bool ReplayDialogReadOnlyStatus;
//retains the state of the stopframe value
static int ReplayDialogStopFrame = 0;
static int movieHackType = 3;
void RefreshThrottleFPS();
static char* GetReplayPath(HWND hwndDlg)
@ -101,8 +100,6 @@ static char* GetSavePath(HWND hwndDlg)
void UpdateReplayDialog(HWND hwndDlg)
{
movieConvertOffset1=0, movieConvertOffset2=0,movieConvertOK=0;
int doClear=1;
char *fn=GetReplayPath(hwndDlg);
@ -118,73 +115,6 @@ void UpdateReplayDialog(HWND hwndDlg)
if(FCEUI_MovieGetInfo(fn, &info))
{
#define MOVIE_FLAG_NOSYNCHACK (1<<4) // set in newer version, used for old movie compatibility
extern int resetDMCacc;
extern int justAutoConverted;
int noNoSyncHack=!(info.nosynchack) && resetDMCacc;
EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT_OFFSET),justAutoConverted || noNoSyncHack);
EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT_FROM),justAutoConverted || noNoSyncHack);
if(justAutoConverted)
{
// use values as nesmock offsets
if(movieHackType != 0)
{
movieHackType=0;
SendDlgItemMessage(hwndDlg, IDC_EDIT_OFFSET, WM_SETTEXT, 0,(LPARAM)"2");
SendDlgItemMessage(hwndDlg, IDC_EDIT_FROM, WM_SETTEXT, 0,(LPARAM)"0");
SendDlgItemMessage(hwndDlg, 2000, WM_SETTEXT, 0,(LPARAM)"Offset:");
SendDlgItemMessage(hwndDlg, 2001, WM_SETTEXT, 0,(LPARAM)"from");
}
}
else if(noNoSyncHack)
{
// use values as sound reset hack values
if(movieHackType != 1)
{
movieHackType=1;
// extern int32 DMCacc;
// extern int8 DMCBitCount;
// char str[256];
// sprintf(str, "%d", DMCacc);
// SendDlgItemMessage(hwndDlg, IDC_EDIT_OFFSET, WM_SETTEXT, 0,(LPARAM)str);
// sprintf(str, "%d", DMCBitCount);
// SendDlgItemMessage(hwndDlg, IDC_EDIT_FROM, WM_SETTEXT, 0,(LPARAM)str);
SendDlgItemMessage(hwndDlg, IDC_EDIT_OFFSET, WM_SETTEXT, 0,(LPARAM)"8");
SendDlgItemMessage(hwndDlg, IDC_EDIT_FROM, WM_SETTEXT, 0,(LPARAM)"0");
SendDlgItemMessage(hwndDlg, 2000, WM_SETTEXT, 0,(LPARAM)"Missing data: acc=");
SendDlgItemMessage(hwndDlg, 2001, WM_SETTEXT, 0,(LPARAM)"bc=");
}
}
else if(movieHackType != 2)
{
movieHackType=2;
SendDlgItemMessage(hwndDlg, IDC_EDIT_OFFSET, WM_SETTEXT, 0,(LPARAM)"");
SendDlgItemMessage(hwndDlg, IDC_EDIT_FROM, WM_SETTEXT, 0,(LPARAM)"");
SendDlgItemMessage(hwndDlg, 2000, WM_SETTEXT, 0,(LPARAM)"");
SendDlgItemMessage(hwndDlg, 2001, WM_SETTEXT, 0,(LPARAM)"");
}
/* { // select away to autoconverted movie... but actually we don't want to do that now that there's an offset setting in the dialog
extern char lastMovieInfoFilename [512];
char relative[MAX_PATH];
AbsoluteToRelative(relative, lastMovieInfoFilename, BaseDirectory);
LONG lOtherIndex = SendDlgItemMessage(hwndDlg, 200, CB_FINDSTRING, (WPARAM)-1, (LPARAM)relative);
if(lOtherIndex != CB_ERR)
{
// select already existing string
SendDlgItemMessage(hwndDlg, 200, CB_SETCURSEL, lOtherIndex, 0);
} else {
LONG lIndex = SendDlgItemMessage(hwndDlg, 200, CB_GETCURSEL, 0, 0);
SendDlgItemMessage(hwndDlg, 200, CB_INSERTSTRING, lIndex, (LPARAM)relative);
SendDlgItemMessage(hwndDlg, 200, CB_SETCURSEL, lIndex, 0);
}
// restore focus to the dialog
// SetFocus(GetDlgItem(hwndDlg, 200));
}*/
char tmp[256];
uint32 div;
@ -332,8 +262,7 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP
{
case WM_INITDIALOG:
{
movieHackType=3;
SendDlgItemMessage(hwndDlg, IDC_CHECK_READONLY, BM_SETCHECK, moviereadonly?BST_CHECKED:BST_UNCHECKED, 0);
SendDlgItemMessage(hwndDlg, IDC_CHECK_READONLY, BM_SETCHECK, ReplayDialogReadOnlyStatus?BST_CHECKED:BST_UNCHECKED, 0);
SendDlgItemMessage(hwndDlg, IDC_CHECK_STOPMOVIE,BM_SETCHECK, BST_UNCHECKED, 0);
char* findGlob[2] = {FCEU_MakeFName(FCEUMKF_MOVIEGLOB, 0, 0),
@ -558,19 +487,6 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP
//char TempArray[16]; //mbg merge 7/17/06 removed
ReplayDialogReadOnlyStatus = (SendDlgItemMessage(hwndDlg, IDC_CHECK_READONLY, BM_GETCHECK, 0, 0) == BST_CHECKED) ? 1 : 0;
char offset1Str[32]={0};
char offset2Str[32]={0};
SendDlgItemMessage(hwndDlg, IDC_EDIT_STOPFRAME, WM_GETTEXT, (WPARAM)32, (LPARAM)offset1Str);
ReplayDialogStopFrame = (SendDlgItemMessage(hwndDlg, IDC_CHECK_STOPMOVIE, BM_GETCHECK,0,0) == BST_CHECKED)? strtol(offset1Str,0,10):0;
SendDlgItemMessage(hwndDlg, IDC_EDIT_OFFSET, WM_GETTEXT, (WPARAM)32, (LPARAM)offset1Str);
SendDlgItemMessage(hwndDlg, IDC_EDIT_FROM, WM_GETTEXT, (WPARAM)32, (LPARAM)offset2Str);
movieConvertOffset1=strtol(offset1Str,0,10);
movieConvertOffset2=strtol(offset2Str,0,10);
movieConvertOK=1;
EndDialog(hwndDlg, (INT_PTR)fn);
}
}
@ -605,14 +521,12 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP
return FALSE;
};
/**
* Show movie replay dialog and replay the movie if necessary.
**/
/// Show movie replay dialog and replay the movie if necessary.
void FCEUD_MovieReplayFrom(void)
{
char* fn;
ReplayDialogReadOnlyStatus = FCEUI_GetMovieToggleReadOnly();
fn = (char*)DialogBox(fceu_hInstance, "IDD_REPLAYINP", hAppWnd, ReplayDialogProc);
char* fn = (char*)DialogBox(fceu_hInstance, "IDD_REPLAYINP", hAppWnd, ReplayDialogProc);
if(fn)
{
@ -625,9 +539,6 @@ void FCEUD_MovieReplayFrom(void)
FixFL();
SetMainWindowStuff();
RefreshThrottleFPS();
extern int movie_readonly;
moviereadonly = movie_readonly; // for prefs
}
}

Binary file not shown.

View File

@ -570,7 +570,7 @@ void FCEUI_CloseGame(void)
**/
void RestartMovieOrReset(unsigned int do_power_off)
{
extern int movie_readonly;
extern bool movie_readonly;
extern char curMovieFilename[512];
if(FCEUMOV_IsPlaying() || FCEUMOV_IsRecording() && movie_readonly)

View File

@ -70,13 +70,11 @@ typedef struct {
int SoundVolume;
int GameGenie;
/* Current first and last rendered scanlines. */
//Current first and last rendered scanlines.
int FirstSLine;
int LastSLine;
/* Driver code(user)-specified first and last rendered scanlines.
Usr*SLine[0] is for NTSC, Usr*SLine[1] is for PAL.
*/
//Driver code(user)-specified first and last rendered scanlines.
//Usr*SLine[0] is for NTSC, Usr*SLine[1] is for PAL.
int UsrFirstSLine[2];
int UsrLastSLine[2];
int SnapName;
@ -115,3 +113,4 @@ extern uint8 vsdip;
#endif
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))

View File

@ -24,82 +24,25 @@
#include "utils/xstring.h"
#define MOVIE_MAGIC 0x1a4d4346 // FCM\x1a
#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)
#define MOVIE_FLAG_NOSYNCHACK (1<<4) // set in newer version, used for old movie compatibility
// backwards compat
static void FCEUI_LoadMovie_v1(char *fname, int _read_only);
static int FCEUI_MovieGetInfo_v1(const char* fname, MOVIE_INFO* info);
extern char FileBase[];
//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 (handle compression)
/*
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[];
}
*/
FILE* slots;
static int current = 0; // > 0 for recording, < 0 for playback
static uint8 joop[4]; //mbg 5/22/08 - seems to be a snapshot of the joystates for delta compression
static uint32 framets = 0;
static uint32 frameptr = 0;
static uint8* moviedata = NULL;
static uint32 moviedatasize = 0;
static uint32 firstframeoffset = 0;
static uint32 savestate_offset = 0;
static uint32 framecount = 0;
static uint32 rerecord_count = 0;
/*static*/ int movie_readonly = 1;
static uint32 pauseframe = 0;
int frame_display = 0;
static uint32 last_frame_display = ~0;
int input_display = 0;
uint32 cur_input_display = 0;
static uint32 last_input_display = ~0;
int resetDMCacc=0;
int justAutoConverted = 0; //PLEASE REMOVE ME
/* Cache variables used for playback. */
static uint32 nextts = 0;
static int32 nextd = 0;
static int MovieShow = 0;
//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 - handle case where read+write is requested, but the file is actually readonly (could be confusing)
//sometimes we accidentally produce movie stop signals while we're trying to do other things with movies..
bool suppressMovieStop=false;
int movieConvertOffset1=0, movieConvertOffset2=0,movieConvertOK=0,movieSyncHackOn=0;
//todo - consider a MemoryBackedFile class..
//..a lot of work.. instead lets just read back from the current fcm
//----movie engine main state
static enum EMOVIEMODE
{
@ -108,48 +51,57 @@ static enum EMOVIEMODE
//this should not be set unless we are in MOVIEMODE_RECORD!
FILE* fpRecordingMovie = 0;
int currFrameCounter;
uint32 cur_input_display = 0;
int pauseframe;
bool movie_readonly = true;
int input_display = 0;
int frame_display = 0;
SFORMAT FCEUMOV_STATEINFO[]={
{ &currFrameCounter, 4|FCEUSTATE_RLSB, "FCNT"},
{ 0 }
};
int FCEUMOV_GetFrame(void)
{
return currFrameCounter;
}
bool FCEUMOV_ShouldPause(void)
{
if(pauseframe && currFrameCounter == pauseframe)
{
pauseframe = 0; //only pause once!
return true;
}
else
{
return false;
}
}
int FCEUMOV_IsPlaying(void)
{
return movieMode == MOVIEMODE_PLAY;
}
int FCEUMOV_IsRecording(void)
{
return movieMode == MOVIEMODE_RECORD;
}
char curMovieFilename[512];
class MovieRecord
{
public:
ValueArray<uint8,4> joysticks;
void dump(FILE* fp)
{
fputc('|',fp);
//for each joystick
for(int i=0;i<4;i++)
{
//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..
const char mnemonics[] = {'A','B','S','T','U','D','L','R'};
for(int bit=7;bit>=0;bit--)
{
uint8 &joystate = joysticks[i];
int bitmask = (1<<bit);
char mnemonic = mnemonics[bit];
//if the bit is set write the mnemonic
if(joystate & bitmask)
fputc(mnemonic,fp);
else //otherwise write a space
fputc(' ',fp);
}
//separate the joysticks
if(i != 3) fputc('|',fp);
}
//each frame is on a new line
fputc('\n',fp);
}
};
class MovieData
@ -176,9 +128,16 @@ public:
std::vector<char> savestate;
std::vector<MovieRecord> records;
FCEU_Guid guid;
//the entire contents of the disk file that was loaded
std::vector<char> serializedFile;
void truncateAt(int frame)
{
records.resize(frame);
}
class TDictionary : public std::map<std::string,std::string>
{
public:
@ -217,6 +176,8 @@ public:
dictionary.tryInstallString("romFilename",romFilename);
if(dictionary.containsKey("romChecksum"))
StringToBytes(dictionary["romChecksum"],&romChecksum,MD5DATA::size);
if(dictionary.containsKey("guid"))
guid = FCEU_Guid::fromString(dictionary["guid"]);
if(dictionary.containsKey("savestate"))
{
std::string& str = dictionary["savestate"];
@ -229,20 +190,60 @@ public:
}
}
int dumpLen()
{
FILE* tmp = tmpfile();
dump(tmp);
int len = ftell(tmp);
fclose(tmp);
return len;
}
void dump(FILE *fp)
{
fprintf(fp,"version %d\n", version);
fprintf(fp,"emuVersion %d\n", emuVersion);
fprintf(fp,"palFlag %d\n", palFlag?1:0);
fprintf(fp,"poweronFlag %d\n", poweronFlag?1:0);
fprintf(fp,"resetFlag %d\n", resetFlag?1:0);
fprintf(fp,"romFilename %s\n", romFilename.c_str());
fprintf(fp,"romChecksum %s\n", BytesToString(romChecksum.data,MD5DATA::size).c_str());
fprintf(fp,"guid %s\n", guid.toString().c_str());
if(savestate.size() != 0)
fprintf(fp,"savestate %s\n", BytesToString(&savestate[0],savestate.size()).c_str());
for(unsigned int i=0;i<records.size();i++)
records[i].dump(fp);
}
} currMovieData;
//---------
void DumpCurrentHeader(FILE* fp)
int FCEUMOV_GetFrame(void)
{
fprintf(fp,"version %d\n", currMovieData.version);
fprintf(fp,"emuVersion %d\n", currMovieData.emuVersion);
fprintf(fp,"palFlag %d\n", currMovieData.palFlag?1:0);
fprintf(fp,"poweronFlag %d\n", currMovieData.poweronFlag?1:0);
fprintf(fp,"resetFlag %d\n", currMovieData.resetFlag?1:0);
fprintf(fp,"romFilename %s\n", currMovieData.romFilename.c_str());
fprintf(fp,"romChecksum %s\n", BytesToString(currMovieData.romChecksum.data,MD5DATA::size).c_str());
if(currMovieData.savestate.size() != 0)
fprintf(fp,"savestate %s\n", BytesToString(&currMovieData.savestate[0],currMovieData.savestate.size()).c_str());
return currFrameCounter;
}
bool FCEUMOV_ShouldPause(void)
{
if(pauseframe && currFrameCounter == pauseframe)
{
pauseframe = 0; //only pause once!
return true;
}
else
{
return false;
}
}
int FCEUMOV_IsPlaying(void)
{
return movieMode == MOVIEMODE_PLAY;
}
int FCEUMOV_IsRecording(void)
{
return movieMode == MOVIEMODE_RECORD;
}
//yuck... another custom text parser.
@ -354,20 +355,8 @@ void StopPlayback()
void StopRecording()
{
FCEU_DispMessage("Movie recording stopped.");
movieMode = MOVIEMODE_INACTIVE;
//mbg todo - think about this
resetDMCacc = movieSyncHackOn = 0;
//mbg todo - think about this
//DoEncode(0,0,1); // Write a dummy timestamp value so that the movie will keep "playing" after user input has stopped.
//mbg todo - add the equivalent
// finish header
//MovieFlushHeader();
// FIXME: truncate movie to length
// ftruncate();
fclose(fpRecordingMovie);
fpRecordingMovie = 0;
}
@ -406,16 +395,15 @@ static void ResetInputTypes()
//begin playing an existing movie
void FCEUI_LoadMovie(char *fname, int _read_only, int _pauseframe)
void FCEUI_LoadMovie(char *fname, bool _read_only, int _pauseframe)
{
assert(fname);
pauseframe = _pauseframe;
FCEUI_StopMovie();
currMovieData = MovieData();
strcpy(curMovieFilename, fname);
FILE* fp = FCEUD_UTF8fopen(fname, "rb");
LoadFM2(currMovieData, fp);
fclose(fp);
@ -465,10 +453,31 @@ void FCEUI_LoadMovie(char *fname, int _read_only, int _pauseframe)
//we really need to research this...
ResetInputTypes();
//stuff that should only happen when we're ready to positively commit to the replay
currFrameCounter = 0;
pauseframe = _pauseframe;
movie_readonly = _read_only;
movieMode = MOVIEMODE_PLAY;
if(movie_readonly)
FCEU_DispMessage("Replay started Read-Only.");
else
FCEU_DispMessage("Replay started Read+Write.");
}
static void closeRecordingMovie()
{
if(fpRecordingMovie)
fclose(fpRecordingMovie);
}
static void openRecordingMovie(const char* fname)
{
fpRecordingMovie = FCEUD_UTF8fopen(fname, "wb");
if(!fpRecordingMovie)
FCEU_PrintError("Error opening movie output file: %s",fname);
strcpy(curMovieFilename, fname);
}
//begin recording a new movie
@ -478,20 +487,12 @@ void FCEUI_SaveMovie(char *fname, uint8 flags, const char* metadata)
FCEUI_StopMovie();
fpRecordingMovie = FCEUD_UTF8fopen(fname, "wb");
if(!fpRecordingMovie)
{
FCEU_PrintError("Error opening movie output file: %s",fname);
}
openRecordingMovie(fname);
movieMode = MOVIEMODE_RECORD;
currMovieData = MovieData();
currMovieData.guid.newGuid();
//old sync management code:
//flags |= MOVIE_FLAG_NOSYNCHACK;
resetDMCacc=movieSyncHackOn=0;
currMovieData.palFlag = FCEUI_GetCurrentVidSystem(0,0);
currMovieData.palFlag = FCEUI_GetCurrentVidSystem(0,0)!=0;
currMovieData.poweronFlag = (flags & MOVIE_FLAG_FROM_POWERON)!=0;
currMovieData.resetFlag = (flags & MOVIE_FLAG_FROM_RESET)!=0;
currMovieData.romChecksum = GameInfo->MD5;
@ -532,7 +533,7 @@ void FCEUI_SaveMovie(char *fname, uint8 flags, const char* metadata)
}
//we are going to go ahead and dump the header. from now on we will only be appending frames
DumpCurrentHeader(fpRecordingMovie);
currMovieData.dump(fpRecordingMovie);
//todo - think about this
ResetInputTypes();
@ -544,22 +545,15 @@ void FCEUI_SaveMovie(char *fname, uint8 flags, const char* metadata)
ResetNES(); // NOTE: this will write an FCEUNPCMD_RESET into the movie file
}
movieMode = MOVIEMODE_RECORD;
FCEU_DispMessage("Movie recording started.");
strcpy(curMovieFilename, fname);
}
static void movie_writechar(int c)
{
fputc(c,fpRecordingMovie);
}
//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_AddJoy(uint8 *js, int SkipFlush)
{
int x,y;
if(movieMode == MOVIEMODE_PLAY)
{
//TODO - rock solid stability and error detection
@ -587,40 +581,18 @@ void FCEUMOV_AddJoy(uint8 *js, int SkipFlush)
}
else if(movieMode == MOVIEMODE_RECORD)
{
movie_writechar('|');
MovieRecord mr;
//for each joystick
for(int i=0;i<4;i++)
{
//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..
const char mnemonics[] = {'A','B','S','T','U','D','L','R'};
for(int bit=7;bit>=0;bit--)
{
uint8 &joystate = js[i];
int bitmask = (1<<bit);
char mnemonic = mnemonics[bit];
//if the bit is set write the mnemonic
if(joystate & bitmask)
movie_writechar(mnemonic);
else //otherwise write a space
movie_writechar(' ');
}
mr.joysticks[i] = js[i];
//separate the joysticks
if(i != 3) movie_writechar('|');
}
currMovieData.records.push_back(mr);
mr.dump(fpRecordingMovie);
//each frame is on a new line
movie_writechar('\n');
}
memcpy(&cur_input_display,js,4);
}
//TODO
@ -698,33 +670,21 @@ int FCEUMOV_WriteState(FILE* st)
{
//we are supposed to dump the movie data into the savestate
if(movieMode == MOVIEMODE_RECORD)
if(movieMode == MOVIEMODE_RECORD || movieMode == MOVIEMODE_PLAY)
{
int todo = ftell(fpRecordingMovie);
if(st)
{
fseek(fpRecordingMovie,0,SEEK_SET);
std::vector<uint8> tempbuf(todo);
fread(&tempbuf[0],1,todo,fpRecordingMovie);
fwrite(&tempbuf[0],1,todo,st);
}
return todo;
}
else
{
int todo = currMovieData.serializedFile.size();
if(st)
{
fwrite(&currMovieData.serializedFile[0],1,currMovieData.serializedFile.size(),st);
}
return todo;
}
int todo = currMovieData.dumpLen();
if(st)
currMovieData.dump(st);
return todo;
}
else return 0;
}
static bool load_successful;
int FCEUMOV_ReadState(FILE* st, uint32 size)
bool FCEUMOV_ReadState(FILE* st, uint32 size)
{
load_successful = false;
@ -733,79 +693,73 @@ int FCEUMOV_ReadState(FILE* st, uint32 size)
fread(&buf[0],1,size,st);
FILE* tmp = tmpfile();
fwrite(&buf[0],1,size,tmp);
FILE* wtf = fopen("d:\\wtf.txt","wb");
fwrite(&buf[0],1,size,wtf);
fclose(wtf);
fseek(tmp,0,SEEK_SET);
currMovieData = MovieData();
LoadFM2(currMovieData, tmp);
MovieData tempMovieData = MovieData();
LoadFM2(tempMovieData, tmp);
fclose(tmp);
//complex TAS logic for when a savestate is loaded:
//if we are playing:
// then, the movie we are playing must match the one stored in the savestate.
//if we are recording:
// then, the movie we are playing must match the one stored in the savestate.
//----------------
//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
// the in-memory movie will be dumped to disk as fcm.
// the emulator will be put into record mode.
//if we are doing neither:
// then, we must discard this movie
// then, we must discard this movie and just load the savestate
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.\ncurrent: %s\nsavestate: %s\n",currMovieData.guid.toString().c_str(),tempMovieData.guid.toString().c_str());
return false;
}
closeRecordingMovie();
if(movie_readonly)
{
//if the frame counter is longer than our current movie, then error
if(currFrameCounter >= (int)currMovieData.records.size())
{
FCEU_PrintError("Savestate is from a frame (%d) after the final frame in the movie (%d). This is not permitted.", currFrameCounter, currMovieData.records.size()-1);
return false;
}
movieMode = MOVIEMODE_PLAY;
}
else
{
//truncate before we copy, just to save some time
tempMovieData.truncateAt(currFrameCounter);
currMovieData = tempMovieData;
openRecordingMovie(curMovieFilename);
currMovieData.dump(fpRecordingMovie);
movieMode = MOVIEMODE_RECORD;
}
}
//movieMode = MOVIEMODE_PLAY;
load_successful = true;
//// if this savestate was made while replaying,
//// we need to "undo" nextd and nextts
//if(nextd != -1)
//{
// int d = 1;
// if(nextts > 65536)
// d = 4;
// else if(nextts > 256)
// d = 3;
// else if(nextts > 0)
// d = 2;
// frameptr -= d;
// nextd = -1;
//}
//// if(current > 0 || (!movie_readonly && current < 0)) /* Recording or Playback (!read-only) */
//if(current!=0 && !movie_readonly)
//{
// // copy movie data from savestate
// moviedata = (uint8*)realloc(moviedata, size);
// moviedatasize = size;
// if(size && fread(moviedata, 1, size, st)<size)
// return 0;
// if(current < 0) // switch to recording
// current = -current;
// fseek(slots[current - 1], firstframeoffset, SEEK_SET);
// fwrite(moviedata, 1, frameptr, slots[current - 1]);
// if(!FCEU_BotMode())
// {
// rerecord_count++;
// }
//}
//// else if(current < 0) /* Playback (read-only) */
//else if(current!=0 && movie_readonly)
//{
// if(current > 0) // switch to playback
// current = -current;
// // allow frameptr to be updated but keep movie data
// fseek(st, size, SEEK_CUR);
// // prevent seeking beyond the end of the movie
// if(frameptr > moviedatasize)
// frameptr = moviedatasize;
//}
//else /* Neither recording or replaying */
//{
// // skip movie data
// fseek(st, size, SEEK_CUR);
//}
//load_successful=1;
//// 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);
return 1;
return true;
}
void FCEUMOV_PreLoad(void)
@ -833,58 +787,61 @@ int FCEUI_IsMovieActive(void)
void FCEUI_MovieToggleFrameDisplay(void)
{
frame_display=!frame_display;
if(!(current != 0 && frame_display))// && !input_display)
FCEU_ResetMessages();
else
{
last_frame_display = ~framecount;
last_input_display = ~cur_input_display;
}
//todo
//frame_display=!frame_display;
//if(!(current != 0 && frame_display))// && !input_display)
// FCEU_ResetMessages();
//else
//{
// last_frame_display = ~framecount;
// last_input_display = ~cur_input_display;
//}
}
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;
}
//todo
//if(!input_display && !(current != 0 && frame_display))
//FCEU_ResetMessages();
//else
if(input_display)
{
last_frame_display = ~framecount;
last_input_display = ~cur_input_display;
}
//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;
//}
////if(!input_display && !(current != 0 && frame_display))
////FCEU_ResetMessages();
////else
//if(input_display)
//{
// last_frame_display = ~framecount;
// last_input_display = ~cur_input_display;
//}
}
void FCEUI_MovieToggleReadOnly(void)
bool FCEUI_GetMovieToggleReadOnly()
{
return movie_readonly;
}
void FCEUI_MovieToggleReadOnly()
{
if(movie_readonly < 2)
{
movie_readonly = !movie_readonly;
if(movie_readonly)
FCEU_DispMessage("Movie is now Read-Only.");
else
FCEU_DispMessage("Movie is now Read+Write.");
}
else
{
FCEU_DispMessage("Movie file is Read-Only.");
}
FCEU_DispMessage("Movie is now Read-Only.");
movie_readonly = !movie_readonly;
}
int FCEUI_MovieGetInfo(const char* fname, MOVIE_INFO* info)
@ -912,6 +869,32 @@ int FCEUI_MovieGetInfo(const char* fname, MOVIE_INFO* info)
}
//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
//
//int _old_FCEUI_MovieGetInfo(const char* fname, MOVIE_INFO* info)
//{

View File

@ -10,7 +10,7 @@ bool FCEUMOV_ShouldPause(void);
int FCEUMOV_GetFrame(void);
int FCEUMOV_WriteState(FILE* st);
int FCEUMOV_ReadState(FILE* st, uint32 size);
bool FCEUMOV_ReadState(FILE* st, uint32 size);
void FCEUMOV_PreLoad(void);
int FCEUMOV_PostLoad(void);
void MovieFlushHeader(void);

View File

@ -140,6 +140,9 @@ static void (*DoSQ2)(void)=Dummyfunc;
static uint32 ChannelBC[5];
//savestate sync hack stuff
int movieSyncHackOn=0,resetDMCacc=0,movieConvertOffset1,movieConvertOffset2;
static void LoadDMCPeriod(uint8 V)
{
if(PAL)
@ -1081,21 +1084,21 @@ void FCEUSND_Reset(void)
// MAJOR BUG WAS HERE: DMCacc and DMCBitCount never got reset...
// so, do some ridiculous hackery if a movie's about to play to keep it in sync...
extern int movieSyncHackOn,resetDMCacc,movieConvertOffset1,movieConvertOffset2;
if(movieSyncHackOn)
{
if(resetDMCacc)
{
// no value in movie save state
#ifdef WIN32
#ifdef WIN32
// use editbox fields
DMCacc=movieConvertOffset1;
DMCBitCount=movieConvertOffset2;
#else
#else
// no editbox fields, so leave the values alone
// and print out a warning that says what they are
FCEU_PrintError("Warning: These variables were not found in the save state and will keep their current value: DMCacc=%d, DMCBitCount=%d\n", DMCacc, DMCBitCount);
#endif
#endif
}
else
{

View File

@ -148,4 +148,57 @@ typedef uint32_t uint32;
typedef void (FP_FASTAPASS(2) *writefunc)(uint32 A, uint8 V);
typedef uint8 (FP_FASTAPASS(1) *readfunc)(uint32 A);
template<typename T, int N>
struct ValueArray
{
T data[N];
T &operator[](int index) { return data[index]; }
static const int size = N;
bool operator!=(ValueArray<T,N> &other) { return !operator==(other); }
bool operator==(ValueArray<T,N> &other)
{
for(int i=0;i<size;i++)
if(data[i] != other[i])
return false;
return true;
}
};
#include "utils/endian.h"
struct FCEU_Guid : public ValueArray<uint8,16>
{
void newGuid()
{
for(int i=0;i<size;i++)
data[i] = rand();
}
std::string toString()
{
char buf[37];
sprintf(buf,"%08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X",
FCEU_de32lsb(data),FCEU_de16lsb(data+4),FCEU_de16lsb(data+6),FCEU_de16lsb(data+8),data[10],data[11],data[12],data[13],data[14],data[15]);
return std::string(buf);
}
static FCEU_Guid fromString(std::string str)
{
FCEU_Guid ret;
ret.scan(str);
return ret;
}
void scan(std::string str)
{
//scanf sucks and tramples bytes. so we read to an oversize tempbuf and copy
char buf[20];
sscanf(str.c_str(),"%08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X",buf,buf+4,buf+6,buf+8,buf+10,buf+11,buf+12,buf+13,buf+14,buf+15);
*this = *(FCEU_Guid*)buf;
}
};
#endif

View File

@ -1,22 +1,22 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/// \file
/// \brief contains file I/O functions that write/read data LSB first.
@ -71,25 +71,25 @@ int read32le(uint32 *Bufo, FILE *fp)
uint32 buf;
if(fread(&buf,1,4,fp)<4)
return 0;
#ifdef LSB_FIRST
#ifdef LSB_FIRST
*(uint32*)Bufo=buf;
#else
#else
*(uint32*)Bufo=((buf&0xFF)<<24)|((buf&0xFF00)<<8)|((buf&0xFF0000)>>8)|((buf&0xFF000000)>>24);
#endif
#endif
return 1;
}
///reads a little endian 16bit value from the specified file
int read16le(char *d, FILE *fp)
{
#ifdef LSB_FIRST
#ifdef LSB_FIRST
return((fread(d,1,2,fp)<2)?0:2);
#else
#else
int ret;
ret=fread(d+1,1,1,fp);
ret+=fread(d,1,1,fp);
return ret<2?0:2;
#endif
#endif
}
///stores a 32bit value into the provided byte array in guaranteed little endian form
@ -101,9 +101,21 @@ void FCEU_en32lsb(uint8 *buf, uint32 morp)
buf[3]=morp>>24;
}
///unpacks a 64bit little endian value from the provided byte array into host byte order
uint64 FCEU_de64lsb(uint8 *morp)
{
return morp[0]|(morp[1]<<8)|(morp[2]<<16)|(morp[3]<<24)|((uint64)morp[4]<<32)|((uint64)morp[5]<<40)|((uint64)morp[6]<<48)|((uint64)morp[7]<<56);
}
///unpacks a 32bit little endian value from the provided byte array into host byte order
uint32 FCEU_de32lsb(uint8 *morp)
{
return(morp[0]|(morp[1]<<8)|(morp[2]<<16)|(morp[3]<<24));
return morp[0]|(morp[1]<<8)|(morp[2]<<16)|(morp[3]<<24);
}
///unpacks a 16bit little endian value from the provided byte array into host byte order
uint16 FCEU_de16lsb(uint8 *morp)
{
return morp[0]|(morp[1]<<8);
}

View File

@ -1,7 +1,14 @@
#ifndef __FCEU_ENDIAN
#define __FCEU_ENDIAN
int write16le(uint16 b, FILE *fp);
int write32le(uint32 b, FILE *fp);
int read32le(uint32 *Bufo, FILE *fp);
void FlipByteOrder(uint8 *src, uint32 count);
void FCEU_en32lsb(uint8 *, uint32);
uint32 FCEU_de32lsb(uint8 *);
uint64 FCEU_de64lsb(uint8 *morp);
uint32 FCEU_de32lsb(uint8 *morp);
uint16 FCEU_de16lsb(uint8 *morp);
#endif

View File

@ -1,6 +1,8 @@
#ifndef _MD5_H
#define _MD5_H
#include "../types.h"
struct md5_context
{
uint32 total[2];
@ -8,17 +10,8 @@ struct md5_context
uint8 buffer[64];
};
template<typename T, int N>
struct ValueArray
{
T data[N];
T &operator[](int index) { return data[index]; }
static const int size = N;
};
typedef ValueArray<uint8,16> MD5DATA;
void md5_starts( struct md5_context *ctx );
void md5_update( struct md5_context *ctx, uint8 *input, uint32 length );
void md5_finish( struct md5_context *ctx, uint8 digest[16] );