From 52bb5ccbd8dfaeb1c964b54e3eb0c380bb9e57fe Mon Sep 17 00:00:00 2001 From: zeromus Date: Mon, 26 May 2008 00:03:20 +0000 Subject: [PATCH] tas logic done? --- src/drawing.h | 5 +- src/driver.h | 10 +- src/drivers/win/config.cpp | 1 - src/drivers/win/keyboard.cpp | 9 +- src/drivers/win/main.h | 2 - src/drivers/win/mapinput.cpp | 24 +- src/drivers/win/replay.cpp | 103 +------ src/drivers/win/res.rc | Bin 152150 -> 151600 bytes src/fceu.cpp | 2 +- src/fceu.h | 35 ++- src/movie.cpp | 557 +++++++++++++++++------------------ src/movie.h | 2 +- src/sound.cpp | 11 +- src/types.h | 55 +++- src/utils/endian.cpp | 136 +++++---- src/utils/endian.h | 9 +- src/utils/md5.h | 11 +- 17 files changed, 467 insertions(+), 505 deletions(-) diff --git a/src/drawing.h b/src/drawing.h index fa10612d..de86d77c 100644 --- a/src/drawing.h +++ b/src/drawing.h @@ -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> (7-nx)) & 1; if(c) diff --git a/src/driver.h b/src/driver.h index 2aadfe43..eff1eaf9 100644 --- a/src/driver.h +++ b/src/driver.h @@ -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); diff --git a/src/drivers/win/config.cpp b/src/drivers/win/config.cpp index e677e33c..f086cfb5 100644 --- a/src/drivers/win/config.cpp +++ b/src/drivers/win/config.cpp @@ -159,7 +159,6 @@ static CFGSTRUCT fceuconfig[] = { ADDCFGSTRUCT(InputConfig), ADDCFGSTRUCT(HotkeyConfig), - AC(moviereadonly), AC(autoHoldKey), AC(autoHoldClearKey), AC(frame_display), diff --git a/src/drivers/win/keyboard.cpp b/src/drivers/win/keyboard.cpp index c64c38d6..e65c0e46 100644 --- a/src/drivers/win/keyboard.cpp +++ b/src/drivers/win/keyboard.cpp @@ -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; diff --git a/src/drivers/win/main.h b/src/drivers/win/main.h index d90c0769..23321535 100644 --- a/src/drivers/win/main.h +++ b/src/drivers/win/main.h @@ -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. diff --git a/src/drivers/win/mapinput.cpp b/src/drivers/win/mapinput.cpp index 4668e947..e69f7a20 100644 --- a/src/drivers/win/mapinput.cpp +++ b/src/drivers/win/mapinput.cpp @@ -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])) diff --git a/src/drivers/win/replay.cpp b/src/drivers/win/replay.cpp index b1b15fe9..8b658cb4 100644 --- a/src/drivers/win/replay.cpp +++ b/src/drivers/win/replay.cpp @@ -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 } } diff --git a/src/drivers/win/res.rc b/src/drivers/win/res.rc index 005708f494faa9b2e107e6f16b223b2d1b7b07b7..a4bb35ea9dcae9834229d6e528e54aa93a76d051 100644 GIT binary patch delta 79 zcmV-V0I>hoqzSN~39udllY|GPgTMl}zybjx>zB?e0t~l&B?6`slQ8WBw@^C*J_47p lOacUxpfawP<_`j#w=nMkKsL9IJp$Ylm)a5nq?Zg*0!rPo9wYz& delta 322 zcmdn6fb-fC&JA)*(=TZ-Eowf*wEYkhqtffi^Nco4zoW`DXS*6JlY`mzCvO=y2~U1! zuP|NjGb6|JHJ=!{MgD+Tv2|kT>)At=? 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 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< savestate; std::vector records; + FCEU_Guid guid; + //the entire contents of the disk file that was loaded std::vector serializedFile; + void truncateAt(int frame) + { + records.resize(frame); + } + class TDictionary : public std::map { 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;iMD5; @@ -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< tempbuf(todo); - fread(&tempbuf[0],1,todo,fpRecordingMovie); - fwrite(&tempbuf[0],1,todo,st); - } + currMovieData.dump(st); + return todo; } - else - { - int todo = currMovieData.serializedFile.size(); - if(st) - { - fwrite(&currMovieData.serializedFile[0],1,currMovieData.serializedFile.size(),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 - //movieMode = MOVIEMODE_PLAY; + + 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; + } + } + 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) 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() { - 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."); - } + return movie_readonly; +} + +void FCEUI_MovieToggleReadOnly() +{ + if(movie_readonly) + 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) //{ diff --git a/src/movie.h b/src/movie.h index 205d041d..b5de5b30 100644 --- a/src/movie.h +++ b/src/movie.h @@ -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); diff --git a/src/sound.cpp b/src/sound.cpp index b16ae727..dc7409ea 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -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 { diff --git a/src/types.h b/src/types.h index 491fb2cd..454ed40f 100644 --- a/src/types.h +++ b/src/types.h @@ -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); -#endif + + +template +struct ValueArray +{ + T data[N]; + T &operator[](int index) { return data[index]; } + static const int size = N; + bool operator!=(ValueArray &other) { return !operator==(other); } + bool operator==(ValueArray &other) + { + for(int i=0;i +{ + void newGuid() + { + for(int i=0;i>8; - return((fwrite(s,1,2,fp)<2)?0:2); + uint8 s[2]; + s[0]=b; + s[1]=b>>8; + return((fwrite(s,1,2,fp)<2)?0:2); } ///writes a little endian 32bit value to the specified file int write32le(uint32 b, FILE *fp) { - uint8 s[4]; - s[0]=b; - s[1]=b>>8; - s[2]=b>>16; - s[3]=b>>24; - return((fwrite(s,1,4,fp)<4)?0:4); + uint8 s[4]; + s[0]=b; + s[1]=b>>8; + s[2]=b>>16; + s[3]=b>>24; + return((fwrite(s,1,4,fp)<4)?0:4); } ///reads a little endian 32bit value from the specified file int read32le(uint32 *Bufo, FILE *fp) { - uint32 buf; - if(fread(&buf,1,4,fp)<4) - return 0; - #ifdef LSB_FIRST - *(uint32*)Bufo=buf; - #else - *(uint32*)Bufo=((buf&0xFF)<<24)|((buf&0xFF00)<<8)|((buf&0xFF0000)>>8)|((buf&0xFF000000)>>24); - #endif - return 1; + uint32 buf; + if(fread(&buf,1,4,fp)<4) + return 0; +#ifdef LSB_FIRST + *(uint32*)Bufo=buf; +#else + *(uint32*)Bufo=((buf&0xFF)<<24)|((buf&0xFF00)<<8)|((buf&0xFF0000)>>8)|((buf&0xFF000000)>>24); +#endif + return 1; } ///reads a little endian 16bit value from the specified file int read16le(char *d, FILE *fp) { - #ifdef LSB_FIRST - return((fread(d,1,2,fp)<2)?0:2); - #else - int ret; - ret=fread(d+1,1,1,fp); - ret+=fread(d,1,1,fp); - return ret<2?0:2; - #endif +#ifdef LSB_FIRST + return((fread(d,1,2,fp)<2)?0:2); +#else + int ret; + ret=fread(d+1,1,1,fp); + ret+=fread(d,1,1,fp); + return ret<2?0:2; +#endif } ///stores a 32bit value into the provided byte array in guaranteed little endian form void FCEU_en32lsb(uint8 *buf, uint32 morp) { - buf[0]=morp; - buf[1]=morp>>8; - buf[2]=morp>>16; - buf[3]=morp>>24; + buf[0]=morp; + buf[1]=morp>>8; + buf[2]=morp>>16; + 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); } diff --git a/src/utils/endian.h b/src/utils/endian.h index 85b7d381..5f82b39f 100644 --- a/src/utils/endian.h +++ b/src/utils/endian.h @@ -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 \ No newline at end of file diff --git a/src/utils/md5.h b/src/utils/md5.h index eb40cf7c..54f6de96 100644 --- a/src/utils/md5.h +++ b/src/utils/md5.h @@ -1,6 +1,8 @@ #ifndef _MD5_H #define _MD5_H +#include "../types.h" + struct md5_context { uint32 total[2]; @@ -8,16 +10,7 @@ struct md5_context uint8 buffer[64]; }; -template -struct ValueArray -{ - T data[N]; - T &operator[](int index) { return data[index]; } - static const int size = N; -}; - typedef ValueArray MD5DATA; - void md5_starts( struct md5_context *ctx ); void md5_update( struct md5_context *ctx, uint8 *input, uint32 length );