From 1e539ad6819828561732a890dface236b88b670b Mon Sep 17 00:00:00 2001 From: zeromus Date: Fri, 23 May 2008 09:58:38 +0000 Subject: [PATCH] phase 1 of new movie system we can now round trip a text movie format --- src/cheat.cpp | 1 + src/driver.h | 11 +- src/drivers/common/config.cpp | 75 +- src/drivers/win/replay.cpp | 43 +- src/drivers/win/res.rc | Bin 149320 -> 149508 bytes src/fceu.cpp | 2 +- src/file.cpp | 4 - src/git.h | 6 +- src/input.cpp | 34 +- src/movie.cpp | 2142 ++++++++++++++++----------------- src/movie.h | 3 +- src/state.cpp | 15 +- src/types.h | 3 +- src/utils/md5.cpp | 3 +- src/utils/md5.h | 13 +- src/utils/xstring.cpp | 120 ++ src/utils/xstring.h | 10 +- src/video.cpp | 5 +- vc8/fceux.vcproj | 9 + 19 files changed, 1211 insertions(+), 1288 deletions(-) diff --git a/src/cheat.cpp b/src/cheat.cpp index 6160a3e4..74a5ef45 100644 --- a/src/cheat.cpp +++ b/src/cheat.cpp @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include diff --git a/src/driver.h b/src/driver.h index 7a520968..84a63503 100644 --- a/src/driver.h +++ b/src/driver.h @@ -2,6 +2,7 @@ #define __DRIVER_H_ #include +#include #include "types.h" #include "git.h" @@ -191,18 +192,16 @@ typedef struct int movie_version; // version of the movie format in the file uint32 num_frames; uint32 rerecord_count; - uint8 flags; + bool poweron, reset, pal, nosynchack; int read_only; uint32 emu_version_used; // 9813 = 0.98.13 char* metadata; // caller-supplied buffer to store metadata. can be NULL. int metadata_size; // size of the buffer pointed to by metadata - uint8 md5_of_rom_used[16]; - int md5_of_rom_used_present; // v1 movies don't have md5 info available - char* name_of_rom_used; // caller-supplied buffer to store metadata. can be NULL. - int name_of_rom_used_size; // size of the buffer pointer to by name_of_rom_used + MD5DATA md5_of_rom_used; + bool md5_of_rom_used_present; // v1 movies don't have md5 info available + std::string name_of_rom_used; } MOVIE_INFO; -int FCEUI_SelectMovie(int, int); void FCEUI_SaveMovie(char *fname, uint8 flags, const char* metadata); void FCEUI_LoadMovie(char *fname, int read_only, int _stopframe); void FCEUI_StopMovie(void); diff --git a/src/drivers/common/config.cpp b/src/drivers/common/config.cpp index ab0a060d..4f3db4d4 100644 --- a/src/drivers/common/config.cpp +++ b/src/drivers/common/config.cpp @@ -32,6 +32,7 @@ #include "../../types.h" #include "../../driver.h" +#include "../../utils/xstring.h" #include "config.h" static int FReadString(FILE *fp, char *str, int n) @@ -240,77 +241,6 @@ void LoadParse(CFGSTRUCT *cfgst, FILE *fp) } -static void StringToBytes(std::string& str, void* data, int len) -{ - if(str.size()>2 && str[0] == '0' && toupper(str[1]) == 'X') - goto hex; - - if(len==1) { - int x = atoi(str.c_str()); - *(unsigned char*)data = x; - return; - } else if(len==2) { - int x = atoi(str.c_str()); - *(unsigned short*)data = x; - return; - } else if(len==4) { - int x = atoi(str.c_str()); - *(unsigned int*)data = x; - return; - } - //else it had better be hex - FCEUD_PrintError("Config error: no hex data found somewhere it is required"); -hex: - int amt = len; - int bytesAvailable = str.size()/2; - if(bytesAvailable < amt) - amt = bytesAvailable; - const char* cstr = str.c_str()+2; - for(int i=0;i='A') a=a-'A'+10; - else a-='0'; - if(b>='A') b=b-'A'+10; - else b-='0'; - unsigned char val = ((unsigned char)a<<4)|(unsigned char)b; - ((unsigned char*)data)[i] = val; - } -} - -static std::string BytesToString(void* data, int len) -{ - char temp[16]; - if(len==1) { - sprintf(temp,"%d",*(unsigned char*)data); - return temp; - } else if(len==2) { - sprintf(temp,"%d",*(unsigned short*)data); - return temp; - } else if(len==4) { - sprintf(temp,"%d",*(unsigned int*)data); - return temp; - } - std::string ret; - ret.resize(len*2+2); - char* str= (char*)ret.c_str(); - str[0] = '0'; - str[1] = 'x'; - str += 2; - for(int i=0;i>4); - int b = (((unsigned char*)data)[i])&15; - if(a>9) a += 'A'-10; - else a += '0'; - if(b>9) b += 'A'-10; - else b += '0'; - str[i*2] = a; - str[i*2+1] = b; - } - return ret; -} - static void cfg_OldToNew(const CFGSTRUCT *cfgst) { int x=0; @@ -364,7 +294,8 @@ void cfg_NewToOld(CFGSTRUCT *cfgst) if(cfgst[x].len) { //binary data - StringToBytes(cfgmap[cfgst[x].name],cfgst[x].ptr,cfgst[x].len); + if(!StringToBytes(cfgmap[cfgst[x].name],cfgst[x].ptr,cfgst[x].len)) + FCEUD_PrintError("Config error: error parsing parameter"); } else { diff --git a/src/drivers/win/replay.cpp b/src/drivers/win/replay.cpp index 22c928c4..b1b15fe9 100644 --- a/src/drivers/win/replay.cpp +++ b/src/drivers/win/replay.cpp @@ -14,7 +14,6 @@ static int ReplayDialogStopFrame = 0; static int movieHackType = 3; -char *md5_asciistr(uint8 digest[16]); void RefreshThrottleFPS(); static char* GetReplayPath(HWND hwndDlg) @@ -114,21 +113,15 @@ void UpdateReplayDialog(HWND hwndDlg) if(fn) { MOVIE_INFO info; - char metadata[MOVIE_MAX_METADATA]; - char rom_name[MAX_PATH]; memset(&info, 0, sizeof(info)); - info.metadata = metadata; - info.metadata_size = sizeof(metadata); - info.name_of_rom_used = rom_name; - info.name_of_rom_used_size = sizeof(rom_name); 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.flags&MOVIE_FLAG_NOSYNCHACK) && resetDMCacc; + int noNoSyncHack=!(info.nosynchack) && resetDMCacc; EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT_OFFSET),justAutoConverted || noNoSyncHack); EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT_FROM),justAutoConverted || noNoSyncHack); if(justAutoConverted) @@ -206,28 +199,30 @@ void UpdateReplayDialog(HWND hwndDlg) SetWindowTextA(GetDlgItem(hwndDlg,IDC_LABEL_LENGTH), tmp); // length sprintf(tmp, "%lu", info.rerecord_count); - SetWindowTextA(GetDlgItem(hwndDlg,IDC_LABEL_UNDOCOUNT), tmp); // rerecord + //SetWindowTextA(GetDlgItem(hwndDlg,IDC_LABEL_UNDOCOUNT), tmp); // rerecord { - // convert utf8 metadata to windows widechar - WCHAR wszMeta[MOVIE_MAX_METADATA]; - if(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, metadata, -1, wszMeta, MOVIE_MAX_METADATA)) - { - if(wszMeta[0]) - SetWindowTextW(GetDlgItem(hwndDlg,IDC_LABEL_AUTHORINFO), wszMeta); // metadata - else - SetWindowTextW(GetDlgItem(hwndDlg,IDC_LABEL_AUTHORINFO), L"(this movie has no author info)"); // metadata - } + //// convert utf8 metadata to windows widechar + //WCHAR wszMeta[MOVIE_MAX_METADATA]; + //if(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, metadata, -1, wszMeta, MOVIE_MAX_METADATA)) + //{ + // if(wszMeta[0]) + // SetWindowTextW(GetDlgItem(hwndDlg,IDC_LABEL_AUTHORINFO), wszMeta); // metadata + // else + // SetWindowTextW(GetDlgItem(hwndDlg,IDC_LABEL_AUTHORINFO), L"(this movie has no author info)"); // metadata + //} + + SetDlgItemTextW(hwndDlg,IDC_LABEL_AUTHORINFO,L"Temporarily non-functional"); } EnableWindow(GetDlgItem(hwndDlg,IDC_CHECK_READONLY),(info.read_only)? FALSE : TRUE); // disable read-only checkbox if the file access is read-only SendDlgItemMessage(hwndDlg,IDC_CHECK_READONLY,BM_SETCHECK,info.read_only ? BST_CHECKED : (ReplayDialogReadOnlyStatus ? BST_CHECKED : BST_UNCHECKED), 0); - SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_RECORDEDFROM),(info.flags & MOVIE_FLAG_FROM_POWERON) ? "Power-On" : "Savestate"); + SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_RECORDEDFROM),info.poweron ? "Power-On" : (info.reset?"Soft-Reset":"Savestate")); if(info.movie_version > 1) { char emuStr[128]; - SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_ROMUSED),info.name_of_rom_used); + SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_ROMUSED),info.name_of_rom_used.c_str()); SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_ROMCHECKSUM),md5_asciistr(info.md5_of_rom_used)); if(info.emu_version_used > 64) sprintf(emuStr, "FCEU %d.%02d.%02d%s", info.emu_version_used/10000, (info.emu_version_used/100)%100, (info.emu_version_used)%100, info.emu_version_used < 9813 ? " (blip)" : ""); @@ -283,7 +278,7 @@ void UpdateReplayDialog(HWND hwndDlg) { SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_LENGTH),""); SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_FRAMES),""); - SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_UNDOCOUNT),""); + //SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_UNDOCOUNT),""); SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_AUTHORINFO),""); SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_ROMUSED),""); SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_ROMCHECKSUM),""); @@ -410,10 +405,6 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP char rom_name[MAX_PATH]; memset(&info, 0, sizeof(info)); - info.metadata = metadata; - info.metadata_size = sizeof(metadata); - info.name_of_rom_used = rom_name; - info.name_of_rom_used_size = sizeof(rom_name); char filename [512]; sprintf(filename, "%s%s", globBase, wfd.cFileName); @@ -836,7 +827,7 @@ static BOOL CALLBACK RecordDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LP void FCEUD_MovieRecordTo() { struct CreateMovieParameters p; - p.szFilename = FCEUI_MovieGetCurrentName(0); + p.szFilename = FCEU_MakeFName(FCEUMKF_MOVIE,0,0); if(DialogBoxParam(fceu_hInstance, "IDD_RECORDINP", hAppWnd, RecordDialogProc, (LPARAM)&p)) { diff --git a/src/drivers/win/res.rc b/src/drivers/win/res.rc index 1abce6f322acc3a344ef556bae4cde94f2010e91..0addf5912dcb102ffc4e85032589fc1ba555d65b 100644 GIT binary patch delta 250 zcmX@n$JsK0v!R7?3zLlow;_WOgE@l^5F1S2ugWAoeSsRI(&PzKS*E{JXXKbJqrt?p zeVPVS3kPQaLn1>dLoq|@bVWm^#mp8A#*+o72~Q7jWKx(sL6K$p134zX={!bE5}cMm z^NblR7<48l?$exhoRNp!k0F%7m%(W|<1$9$a6g881_g#3AeqOI&XCGb1mu+f$wY=^ zhHM}|1<2Q6$OKvi63>9L@_}*+4CO!`NKApD5~#lvNT&nIN}yW%>4nTpvV!IeW?2%w>KCuoe%*4g+n)e delta 82 zcmV-Y0ImOokO|0+34nwFv;sUR12+IRmw_e%9haac0V216C<2@amk^=>1OsmXaF-=9 o0;2&pmr@~ diff --git a/src/fceu.cpp b/src/fceu.cpp index 2a85e5ba..99ab9a67 100644 --- a/src/fceu.cpp +++ b/src/fceu.cpp @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include @@ -374,7 +375,6 @@ FCEUGI *FCEUI_LoadGame(const char *name, int OverwriteVidMode) PowerNES(); FCEUSS_CheckStates(); - FCEUMOV_CheckMovies(); if(GameInfo->type!=GIT_NSF) { diff --git a/src/file.cpp b/src/file.cpp index e7348664..5d5e6e53 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -615,10 +615,6 @@ void FCEUI_SetDirOverride(int which, char *n) { FCEUSS_CheckStates(); } - else if(which == FCEUIOD_MISC) - { - FCEUMOV_CheckMovies(); - } } } diff --git a/src/git.h b/src/git.h index bbe64051..c1946ea4 100644 --- a/src/git.h +++ b/src/git.h @@ -9,7 +9,9 @@ #define GIV_NTSC 0 /* NTSC emulation. */ #define GIV_PAL 1 /* PAL emulation. */ #define GIV_USER 2 /* What was set by FCEUI_SetVidSys(). */ - + +#include "utils/md5.h" + typedef struct { uint8 *name; /* Game name, UTF8 encoding */ @@ -22,7 +24,7 @@ typedef struct { int cspecial; /* Special cart expansion: DIP switches, barcode reader, etc. */ - uint8 MD5[16]; + MD5DATA MD5; int soundrate; /* For Ogg Vorbis expansion sound wacky support. 0 for default. */ int soundchan; /* Number of sound channels. */ } FCEUGI; diff --git a/src/input.cpp b/src/input.cpp index a60aad88..783fe66d 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -18,6 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include #include #include "types.h" @@ -749,42 +751,16 @@ static void CommandStateLoad(void) FCEUI_LoadState(0); } -void FCEUI_SelectMovieNext(int); -/* -static void CommandMovieSelectSlot(void) -{ - if(execcmd <= EMUCMD_MOVIE_SLOT_9) - FCEUI_SelectMovie(execcmd-EMUCMD_MOVIE_SLOT_0, 1); - else if(execcmd == EMUCMD_MOVIE_SLOT_NEXT) - FCEUI_SelectMovieNext(1); - else if(execcmd == EMUCMD_MOVIE_SLOT_PREV) - FCEUI_SelectMovieNext(-1); -} - static void CommandMovieRecord(void) { - if(execcmd >= EMUCMD_MOVIE_RECORD_SLOT_0 && execcmd <= EMUCMD_MOVIE_RECORD_SLOT_9) - { - int oldslot=FCEUI_SelectMovie(execcmd-EMUCMD_MOVIE_RECORD_SLOT_0, 0); - FCEUI_SaveMovie(0, 0, 0); - FCEUI_SelectMovie(oldslot, 0); - } - else - FCEUI_SaveMovie(0, 0, 0); + FCEUI_SaveMovie(0, 0, 0); } static void CommandMovieReplay(void) { - if(execcmd >= EMUCMD_MOVIE_REPLAY_SLOT_0 && execcmd <= EMUCMD_MOVIE_REPLAY_SLOT_9) - { - int oldslot=FCEUI_SelectMovie(execcmd-EMUCMD_MOVIE_REPLAY_SLOT_0, 0); - FCEUI_LoadMovie(0, 0); - FCEUI_SelectMovie(oldslot, 0); - } - else - FCEUI_LoadMovie(0, 0); + FCEUI_LoadMovie(0, 0, 0); } -*/ + static void CommandSoundAdjust(void) { int n; diff --git a/src/movie.cpp b/src/movie.cpp index c3f3db18..206bc625 100644 --- a/src/movie.cpp +++ b/src/movie.cpp @@ -3,6 +3,9 @@ #include #include +#include +#include + #ifdef MSVC #include #endif @@ -17,10 +20,11 @@ #include "file.h" #include "video.h" #include "movie.h" -#include "utils/memory.h" //mbg merge 7/17/06 added +#include "utils/memory.h" +#include "utils/xstring.h" #define MOVIE_MAGIC 0x1a4d4346 // FCM\x1a -#define MOVIE_VERSION 2 // 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_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 @@ -30,6 +34,10 @@ 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 + /* struct MovieHeader { @@ -52,8 +60,8 @@ uint8 movie_data[]; } */ +FILE* slots; static int current = 0; // > 0 for recording, < 0 for playback -static FILE *slots[10]={0}; 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; @@ -64,7 +72,7 @@ static uint32 savestate_offset = 0; static uint32 framecount = 0; static uint32 rerecord_count = 0; /*static*/ int movie_readonly = 1; -static uint32 stopframe = 0; +static uint32 pauseframe = 0; int frame_display = 0; static uint32 last_frame_display = ~0; int input_display = 0; @@ -73,6 +81,8 @@ 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; @@ -88,287 +98,285 @@ SFORMAT FCEUMOV_STATEINFO[]={ { 0 } }; -static int CurrentMovie = 1; static int MovieShow = 0; -static int MovieStatus[10]; -static void DoEncode(int joy, int button, int); + +//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 + +static enum EMOVIEMODE +{ + MOVIEMODE_INACTIVE, MOVIEMODE_RECORD, MOVIEMODE_PLAY +} movieMode = MOVIEMODE_INACTIVE; + +//this should not be set unless we are in MOVIEMODE_RECORD! +FILE* fpRecordingMovie = 0; +int currFrameCounter; int FCEUMOV_GetFrame(void) { - return framecount; + return currFrameCounter; +} + + +bool FCEUMOV_ShouldPause(void) +{ + if(pauseframe && currFrameCounter == pauseframe) + { + pauseframe = 0; //only pause once! + return true; + } + else + { + return false; + } } int FCEUMOV_IsPlaying(void) { - if(current < 0) return(1); - else return(0); + return movieMode == MOVIEMODE_PLAY; } int FCEUMOV_IsRecording(void) { - if(current > 0) return(1); - else return(0); + return movieMode == MOVIEMODE_RECORD; } -int FCEUMOV_ShouldPause(void) + +char curMovieFilename[512]; + +class MovieRecord { - if(stopframe && framecount == stopframe) +public: + ValueArray joysticks; +}; + +class MovieData +{ +public: + MovieData() + : version(MOVIE_VERSION) + , emuVersion(FCEU_VERSION_NUMERIC) + , palFlag(false) + , poweronFlag(false) + , resetFlag(false) { - stopframe = 0; //only pause once! - return 1; + memset(&romChecksum,0,sizeof(MD5DATA)); } - else + + int emuVersion; + int version; + //todo - somehow force mutual exclusion for poweron and reset (with an error in the parser) + bool palFlag; + bool poweronFlag; + bool resetFlag; + MD5DATA romChecksum; + std::string romFilename; + std::vector savestate; + std::vector records; + + class TDictionary : public std::map { - return 0; + public: + bool containsKey(std::string key) + { + return find(key) != end(); + } + + void tryInstallBool(std::string key, bool& val) + { + if(containsKey(key)) + val = atoi(operator [](key).c_str())!=0; + } + + void tryInstallString(std::string key, std::string& val) + { + if(containsKey(key)) + val = operator [](key); + } + + void tryInstallInt(std::string key, int& val) + { + if(containsKey(key)) + val = atoi(operator [](key).c_str()); + } + + }; + + void installDictionary(TDictionary& dictionary) + { + dictionary.tryInstallInt("version",version); + dictionary.tryInstallInt("emuVersion",emuVersion); + dictionary.tryInstallBool("palFlag",palFlag); + dictionary.tryInstallBool("poweronFlag",poweronFlag); + dictionary.tryInstallBool("resetFlag",resetFlag); + dictionary.tryInstallString("romFilename",romFilename); + if(dictionary.containsKey("romChecksum")) + StringToBytes(dictionary["romChecksum"],&romChecksum,MD5DATA::size); + if(dictionary.containsKey("savestate")) + { + std::string& str = dictionary["savestate"]; + int len = HexStringToBytesLength(str); + if(len >= 1) + { + savestate.resize(len); + StringToBytes(str,&savestate[0],len); + } + } } + +} currMovieData; + +void DumpCurrentHeader(FILE* fp) +{ + 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()); } -int suppressMovieStop=0; -int movieConvertOffset1=0, movieConvertOffset2=0,movieConvertOK=0,movieSyncHackOn=0; - -/** -* Stop movie playback. -**/ -static void StopPlayback(void) +//yuck... another custom text parser. +void LoadFM2(MovieData& movieData, FILE *fp) { - if(suppressMovieStop) - return; + MovieData::TDictionary dictionary; - resetDMCacc = movieSyncHackOn = 0; - fclose(slots[-1 - current]); - current = 0; + std::string key,value; + enum { + NEWLINE, KEY, SEPARATOR, VALUE, RECORD + } state = NEWLINE; + bool bail = false; + for(;;) + { + int c = fgetc(fp); + if(c == -1) + goto bail; + bool iswhitespace = (c==' '||c=='\t'); + bool iskeychar = (c==':'); + bool isnewline = (c==10||c==13); + switch(state) + { + case NEWLINE: + if(isnewline) goto done; + if(iskeychar) + { + key = ""; + value = ""; + state = KEY; + goto done; + } + goto dorecord; + break; + case RECORD: { + dorecord: + MovieRecord record; + ungetc(c,fp); + //for each joystick + for(int i=0;i<4;i++) + { + uint8& joystate = record.joysticks[i]; + joystate = 0; + for(int bit=7;bit>=0;bit--) + { + int c = fgetc(fp); + if(c == -1) + goto bail; + if(c != ' ') + joystate |= (1<MD5, 1, 16, fp); // write ROM checksum - write32le(FCEU_VERSION_NUMERIC, fp); // write emu version used - - // write ROM name used - fseek(fp, 52, SEEK_SET); - char str[512]; - fgets(str,512,fp); - str[511]='\0'; - int strdiff=strlen(FileBase)-strlen(str); - if(strdiff) - { - // resize the whole damn movie because the ROM name in the header is of variable length - int off=52; - fseek(fp, 52, SEEK_SET); - do { off++; - } while(fgetc(fp) && !feof(fp) && !ferror(fp)); - - if(feof(fp) || ferror(fp)) - { - fseek(fp, loc, SEEK_SET); - return; - } - - fseek(fp, 0, SEEK_END); - uint32 fsize=ftell(fp)-off; - char* ctemp=(char*)FCEU_malloc(fsize*sizeof(char)+4); - if(!ctemp) - { - fseek(fp, loc, SEEK_SET); - return; - } - fseek(fp, off, SEEK_SET); - fread(ctemp, 1,fsize, fp); - fseek(fp, 52+strlen(FileBase)+1, SEEK_SET); - int wrote = fwrite(ctemp, fsize,1, fp); - FCEU_free(ctemp); - if(!wrote) - { - fseek(fp, loc, SEEK_SET); - return; - } - - if(loc >= firstframeoffset) - loc += strdiff; - savestate_offset += strdiff; - firstframeoffset += strdiff; - fseek(fp, 24, SEEK_SET); - write32le(savestate_offset, fp); - write32le(firstframeoffset, fp); - } - fseek(fp, 52, SEEK_SET); - fputs(FileBase, fp); - fputc('\0', fp); - - fseek(fp, loc, SEEK_SET); -} - -/** -* Stop movie recording -**/ -void StopRecording(void) -{ - if(suppressMovieStop) - { - return; - } + FCEU_DispMessage("Movie recording stopped."); + movieMode = MOVIEMODE_INACTIVE; + //mbg todo - think about this resetDMCacc = movieSyncHackOn = 0; - DoEncode(0,0,1); /* Write a dummy timestamp value so that the movie will keep - "playing" after user input has stopped. */ - + //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(); - + //MovieFlushHeader(); // FIXME: truncate movie to length // ftruncate(); - fclose(slots[current - 1]); - MovieStatus[current - 1] = 1; - current = 0; - FCEU_DispMessage("Movie recording stopped."); + fclose(fpRecordingMovie); + fpRecordingMovie = 0; } -/** -* Stop movie recording or movie playback. -**/ + void FCEUI_StopMovie() { - if(current < 0) - { - StopPlayback(); - } - - if(current > 0) - { - StopRecording(); - } -} - -#ifdef MSVC -#include "process.h" -void executeCommand(const char* cmd) -{ - if(!cmd || !*cmd) + if(suppressMovieStop) return; - - const char *argv[4]; - argv[0] = getenv("COMSPEC"); - argv[1] = "/c"; - argv[2] = cmd; - argv[3] = NULL; - if(*argv && *(*argv)) - _spawnve(_P_WAIT, argv[0], argv, NULL); + + if(movieMode == MOVIEMODE_PLAY) + StopPlayback(); + else if(movieMode == MOVIEMODE_RECORD) + StopRecording(); } -#endif -int justAutoConverted=0; -static const char* convertToFCM(const char *fname, char *buffer) -{ -#ifdef MSVC - justAutoConverted=0; - - // convert to fcm if not already - const char* dot = strrchr(fname, '.'); - if(dot) - { - int fmv = !stricmp(dot, ".fmv"); - int nmv = !stricmp(dot, ".nmv"); - int vmv = !stricmp(dot, ".vmv"); - if(fmv || nmv || vmv) - { - strcpy(buffer, fname); - buffer[dot-fname]='\0'; - strcat(buffer,"-autoconverted.fcm"); - - int fceuver=0; - if(fmv) - fceuver=1; - else if(nmv) - fceuver=2; - else if(vmv) - fceuver=3; - - extern char lastLoadedGameName [2048]; - char cmd [1024], offset[64], romInfo[1024]; - if(movieConvertOK) - sprintf(romInfo, "-smd5=\"%s\" -sromname=\"%s (MAYBE)\" -s", lastLoadedGameName, FileBase); - else - sprintf(romInfo, "-sromname=\"(unknown)\" -s"); - if(movieConvertOffset2) sprintf(offset, "-o %d:%d", movieConvertOffset2,movieConvertOffset1); - else sprintf(offset, "-o %d", movieConvertOffset1); - sprintf(cmd, ".\\util\\nesmock\\nesmock.exe %s %s -spal=%c -sfceuver=%d \"%s\" \"%s\" ", offset, romInfo, FCEUI_GetCurrentVidSystem(0,0)?'1':'0', fceuver, fname, buffer); - // FCEU_PrintError(cmd); - executeCommand(cmd); - - FILE* file = FCEUD_UTF8fopen(buffer,"rb"); - if(file) - { - fseek(file, 12, SEEK_SET); - uint32 frames=0; //mbg merge 7/17/06 changed to uint32 - read32le(&frames, file); - if(frames) - { - fname = buffer; - justAutoConverted=1; - } - else - { - static int errAlready=0; - if(!errAlready) - { - errAlready=1; - FCEU_PrintError("For some reason, nesmock was unable to create a valid FCM from the given file.\nThe command given was:\n%s\nPerhaps the file specified is not a movie file or contains no input data,\nor perhaps it is a movie file of a version unsupported by nesmock.\n\n(This error message will self-destruct until you restart FCEU.)", cmd); - } - } - fclose(file); - } - else - { - char str [512]; - str[0] = '\0'; - GetCurrentDirectory(512,str); - strcat(str, "\\util\\nesmock\\nesmock.exe"); - file = FCEUD_UTF8fopen(str, "rb"); - if(file) - { - static int errAlready=0; - if(!errAlready) - { - errAlready=1; - FCEU_PrintError("For some reason, nesmock was unable to convert the movie to FCM format.\nThe command given was:\n%s\n\n(This error message will self-destruct until you restart FCEU.)", cmd); - fclose(file); - } - } - else - { - static int errAlready=0; - if(!errAlready) - { - errAlready=1; - FCEU_PrintError("Nesmock not found, so the movie could not be converted to FCM format.\nYou must place nesmock.exe at this location so FCEU can find it:\n%s\n\n(This error message will self-destruct until you restart FCEU.)", str); - } - } - } - } - } -#endif - return fname; -} void ParseGIInput(FCEUGI *GI); //mbg merge 7/17/06 - had to add. gross. void InitOtherInput(void); //mbg merge 7/17/06 - had to add. gross. @@ -389,96 +397,21 @@ static void ResetInputTypes() #endif } -char curMovieFilename[512]; - -// PlayMovie / MoviePlay function -void FCEUI_LoadMovie(char *fname, int _read_only, int _stopframe) +//begin playing an existing movie +void FCEUI_LoadMovie(char *fname, int _read_only, int _pauseframe) { - char buffer [512]; - fname = (char*)convertToFCM(fname,buffer); + assert(fname); - FILE *fp; - char *fn = NULL; + pauseframe = _pauseframe; FCEUI_StopMovie(); - if(!fname) - fname = fn = FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0); - - char origname[512]; - strcpy(origname,fname); - - stopframe = _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); - } - + currMovieData = MovieData(); + + FILE* fp = FCEUD_UTF8fopen(fname, "rb"); + LoadFM2(currMovieData, fp); + fclose(fp); // fully reload the game to reinitialize everything before playing any movie // to try fixing nondeterministic playback of some games @@ -486,252 +419,128 @@ void FCEUI_LoadMovie(char *fname, int _read_only, int _stopframe) extern char lastLoadedGameName [2048]; extern int disableBatteryLoading, suppressAddPowerCommand; suppressAddPowerCommand=1; - suppressMovieStop=1; + suppressMovieStop=true; { FCEUGI * gi = FCEUI_LoadGame(lastLoadedGameName, 0); - if(!gi) - PowerNES(); + //mbg 5/23/08 - wtf? why would this return null? + //if(!gi) PowerNES(); + assert(gi); } - suppressMovieStop=0; + suppressMovieStop=false; suppressAddPowerCommand=0; } - if(flags[0] & MOVIE_FLAG_FROM_POWERON) + //todo - if reset flag is set, will the poweron flag be set? + //WE NEED TO LOAD A SAVESTATE + if(!currMovieData.poweronFlag) { - //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; + FILE* fp = tmpfile(); + fwrite(&currMovieData.savestate[0],1,currMovieData.savestate.size(),fp); + fseek(fp,0,SEEK_SET); + bool success = FCEUSS_LoadFP(fp,SSLOADPARAM_BACKUP); + fclose(fp); + if(!success) return; } - if(flags[0] & MOVIE_FLAG_PAL) - { + //TODO - handle reset flag + + //...why do we have to do this. isnt it setup by the rom? + if(currMovieData.palFlag) FCEUI_SetVidSystem(1); - } else - { FCEUI_SetVidSystem(0); - } + + //we really need to research this... 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 = CurrentMovie; - slots[current] = fp; - - memset(joop,0,sizeof(joop)); - current = -1 - current; - framets=0; - nextts=0; - nextd = -1; - - MovieStatus[CurrentMovie] = 1; - if(!fname) - FCEUI_SelectMovie(CurrentMovie,1); /* Quick hack to display status. */ - else - FCEU_DispMessage("Movie playback started."); - - strcpy(curMovieFilename, origname); + currFrameCounter = 0; + movieMode = MOVIEMODE_PLAY; } + + +//begin recording a new movie void FCEUI_SaveMovie(char *fname, uint8 flags, const char* metadata) { - FILE *fp; - char *fn; - uint8 padding[4] = {0,0,0,0}; - int n_padding; + assert(fname); FCEUI_StopMovie(); - char origname[512]; - if(fname) + fpRecordingMovie = FCEUD_UTF8fopen(fname, "wb"); + if(!fpRecordingMovie) { - fp = FCEUD_UTF8fopen(fname, "wb"); - strcpy(origname,fname); - } - else - { - fp=FCEUD_UTF8fopen(fn=FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0),"wb"); - strcpy(origname,fn); - free(fn); + FCEU_PrintError("Error opening movie output file: %s",fname); } - if(!fp) return; + movieMode = MOVIEMODE_RECORD; + currMovieData = MovieData(); - // don't need the movieSyncHackOn sync hack for newly recorded movies - flags |= MOVIE_FLAG_NOSYNCHACK; + //old sync management code: + //flags |= MOVIE_FLAG_NOSYNCHACK; resetDMCacc=movieSyncHackOn=0; - // add PAL flag - if(FCEUI_GetCurrentVidSystem(0,0)) - flags |= MOVIE_FLAG_PAL; + currMovieData.palFlag = FCEUI_GetCurrentVidSystem(0,0); + currMovieData.poweronFlag = (flags & MOVIE_FLAG_FROM_POWERON)!=0; + currMovieData.resetFlag = (flags & MOVIE_FLAG_FROM_RESET)!=0; + currMovieData.romChecksum = GameInfo->MD5; + currMovieData.romFilename = FileBase; - // write header - write32le(MOVIE_MAGIC, fp); - write32le(MOVIE_VERSION, fp); - fputc(flags, fp); - fputc(0, fp); // reserved - fputc(0, fp); // reserved - fputc(0, fp); // reserved - write32le(0, fp); // leave room for length frames - write32le(0, fp); // leave room for rerecord count - write32le(0, fp); // leave room for movie data size - write32le(0, fp); // leave room for savestate_offset - write32le(0, fp); // leave room for offset_to_controller_data - fwrite(GameInfo->MD5, 1, 16, fp); // write ROM checksum - write32le(FCEU_VERSION_NUMERIC, fp); // write emu version used - fputs(FileBase, fp); // write ROM name used - fputc(0, fp); - if(metadata) - { - if(strlen(metadata) < MOVIE_MAX_METADATA) - fputs(metadata, fp); - else - fwrite(metadata, 1, MOVIE_MAX_METADATA-1, fp); - } - fputc(0, fp); - - // add padding - n_padding = (4 - (ftell(fp) & 0x3)) & 0x3; - fwrite(padding, 1, n_padding, fp); - - if(flags & MOVIE_FLAG_FROM_POWERON) + if(currMovieData.poweronFlag) { // make a for-movie-recording power-on clear the game's save data, too - // (note: FCEU makes a save state immediately after this and that loads that on movie playback) extern char lastLoadedGameName [2048]; extern int disableBatteryLoading, suppressAddPowerCommand; suppressAddPowerCommand=1; disableBatteryLoading=1; - suppressMovieStop=1; + suppressMovieStop=true; { // NOTE: this will NOT write an FCEUNPCMD_POWER into the movie file - FCEUGI * gi = FCEUI_LoadGame(lastLoadedGameName, 0); - if(!gi) - PowerNES(); // and neither will this, if it can even happen + FCEUGI* gi = FCEUI_LoadGame(lastLoadedGameName, 0); + //mbg 5/23/08 - wtf? why would this return null? + //if(!gi) PowerNES(); + assert(gi); } - suppressMovieStop=0; + suppressMovieStop=false; disableBatteryLoading=0; suppressAddPowerCommand=0; - - //no savestate - savestate_offset = 0xFFFFFFFF; } else { - savestate_offset = ftell(fp); - FCEUSS_SaveFP(fp); + //dump a savestate + FILE* tmp = tmpfile(); + FCEUSS_SaveFP(tmp); + fseek(tmp,0,SEEK_END); + int len = (int)ftell(tmp); + fseek(tmp,0,SEEK_SET); + currMovieData.savestate.resize(len); + fread(&currMovieData.savestate[0],1,len,tmp); + fclose(tmp); } - fseek(fp, 0, SEEK_END); + //we are going to go ahead and dump the header. from now on we will only be appending frames + DumpCurrentHeader(fpRecordingMovie); + //todo - think about this ResetInputTypes(); - // add padding - n_padding = (4 - (ftell(fp) & 0x3)) & 0x3; - fwrite(padding, 1, n_padding, fp); - - firstframeoffset = ftell(fp); - - // finish header - fseek(fp, 24, SEEK_SET); // offset_to_savestate offset - write32le(savestate_offset, fp); - write32le(firstframeoffset, fp); - - fseek(fp, firstframeoffset, SEEK_SET); - - // set recording flag - current=CurrentMovie; - - movie_readonly = 0; - frameptr = 0; - framecount = 0; - rerecord_count = 0; - slots[current] = fp; - memset(joop,0,sizeof(joop)); - current++; - framets=0; - nextd = -1; - + //todo - think about this // trigger a reset if(flags & MOVIE_FLAG_FROM_RESET) { ResetNES(); // NOTE: this will write an FCEUNPCMD_RESET into the movie file } - if(!fname) - FCEUI_SelectMovie(CurrentMovie,1); /* Quick hack to display status. */ - else - FCEU_DispMessage("Movie recording started."); + FCEU_DispMessage("Movie recording started."); - strcpy(curMovieFilename, origname); + strcpy(curMovieFilename, fname); } static void movie_writechar(int c) { - if(frameptr == moviedatasize) - { - moviedatasize += 4096; - moviedata = (uint8*)realloc(moviedata, moviedatasize); - } - moviedata[frameptr++] = (uint8)(c & 0xff); - fputc(c, slots[current - 1]); + fputc(c,fpRecordingMovie); } -static int movie_readchar() -{ - if(frameptr >= moviedatasize) - { - return -1; - } - return (int)(moviedata[frameptr++]); -} -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; - } -} //the main interaction point between the emulator and the movie system. //either dumps the current joystick state or loads one state from the movie @@ -739,109 +548,33 @@ void FCEUMOV_AddJoy(uint8 *js, int SkipFlush) { int x,y; - // if(!current) return; // Not playback nor recording. - - if(current < 0) // Playback + if(movieMode == MOVIEMODE_PLAY) { - //while(nextts == framets || nextd == -1) - //{ - // int tmp,ti; - // uint8 d; - - // if(nextd != -1) - // { - // if(nextd&0x80) - // { - // //puts("Egads"); - // FCEU_DoSimpleCommand(nextd&0x1F); - // } - // else - // joop[(nextd >> 3)&0x3] ^= 1 << (nextd&0x7); - // } - - - // tmp = movie_readchar(); - // d = tmp; - - // if(tmp < 0) - // { - // StopPlayback(); - // memcpy(&cur_input_display,js,4); - // return; - // } - - // nextts = 0; - // tmp >>= 5; - // tmp &= 0x3; - // ti=0; - - // int tmpfix = tmp; - // while(tmp--) { nextts |= movie_readchar() << (ti * 8); ti++; } - - // // This fixes a bug in movies recorded before version 0.98.11 - // // It's probably not necessary, but it may keep away CRAZY PEOPLE who recorded - // // movies on <= 0.98.10 and don't work on playback. - // if(tmpfix == 1 && !nextts) - // {nextts |= movie_readchar()<<8; } - // else if(tmpfix == 2 && !nextts) {nextts |= movie_readchar()<<16; } - - // if(nextd != -1) - // framets = 0; - // nextd = d; - //} - - //memcpy(js,joop,4); - //TODO - rock solid stability and error detection - //for each joystick - for(int i=0;i<4;i++) + //stop when we run out of frames + if(currFrameCounter == currMovieData.records.size()) { - uint8 &joystate = js[i]; - joystate = 0; - for(int bit=7;bit>=0;bit--) - { - int c = movie_readchar(); - if(c == -1) - { - StopPlayback(); - goto bail; - } - if(c != ' ') - joystate |= (1< 0) // Recording + else if(movieMode == MOVIEMODE_RECORD) { - //mbg 5/22/08 - keep dumping the header for now - MovieFlushHeader(); - //// flush header info every 300 frames in case of crash - ////Don't if in bot mode--we don't need extra overhead - //if(!SkipFlush) - //{ - // static int fcounter=0; - // fcounter++; - // if(!(fcounter%300)) - // MovieFlushHeader(); - //} - - //for(x=0;x<4;x++) - //{ - // if(js[x] != joop[x]) - // { - // for(y=0;y<8;y++) - // if((js[x] ^ joop[x]) & (1 << y)) - // DoEncode(x, y, 0); - // joop[x] = js[x]; - // } - // else if(framets == ((1<<24)-1)) DoEncode(0,0,1); // Overflow will happen, so do dummy update. - //} - //for each joystick for(int i=0;i<4;i++) { @@ -870,19 +603,9 @@ void FCEUMOV_AddJoy(uint8 *js, int SkipFlush) movie_writechar('\n'); } - if(current) - { - framets++; - framecount++; - } - memcpy(&cur_input_display,js,4); - //Stop the movie at a specified frame - if(current < 0 && FCEUMOV_ShouldPause() && FCEUI_EmulationPaused()==0) - { - FCEUI_ToggleEmulationPause(); - } + } @@ -890,118 +613,67 @@ void FCEUMOV_AddCommand(int cmd) { if(current <= 0) return; // Return if not recording a movie //printf("%d\n",cmd); - DoEncode((cmd>>3)&0x3,cmd&0x7,1); + + //MBG TODO BIG TODO TODO TODO + //DoEncode((cmd>>3)&0x3,cmd&0x7,1); } -void FCEUMOV_CheckMovies(void) -{ - FILE *st=NULL; - char *fn; - int ssel; - - for(ssel=0;ssel<10;ssel++) - { - st=FCEUD_UTF8fopen(fn=FCEU_MakeFName(FCEUMKF_MOVIE,ssel,0),"rb"); - free(fn); - if(st) - { - MovieStatus[ssel]=1; - fclose(st); - } - else - MovieStatus[ssel]=0; - } - -} - -void FCEUI_SelectMovieNext(int n) -{ - if(n>0) - CurrentMovie=(CurrentMovie+1)%10; - else - CurrentMovie=(CurrentMovie+9)%10; - FCEUI_SelectMovie(CurrentMovie, 1); -} - - -int FCEUI_SelectMovie(int w, int show) -{ - int oldslot=CurrentMovie; - if(w == -1) { MovieShow = 0; return 0; } //mbg merge 7/17/06 had to add return value - FCEUI_SelectState(-1,0); - - CurrentMovie=w; - MovieShow=180; - - if(show) - { - MovieShow=180; - if(current > 0) - FCEU_DispMessage("-recording movie %d-",current-1); - else if (current < 0) - FCEU_DispMessage("-playing movie %d-",-1 - current); - else - FCEU_DispMessage("-select movie-"); - } - return 0; //mbg merge 7/17/06 had to add return value -} - -int movcounter=0; - void FCEU_DrawMovies(uint8 *XBuf) { - int frameDisplayOn = current != 0 && frame_display; - extern int howlong; -#if MSVC - extern int32 fps_scale; -#else - int32 fps_scale=256; -#endif - int howl=(180-(FCEUI_EmulationPaused()?(60):(20*fps_scale/256))); - if(howl>176) howl=180; - if(howl<1) howl=1; - if((howlong':' ', (c&0x20)?'v':' ', - (c&0x01)?'A':' ', (c&0x02)?'B':' ', (c&0x08)?'S':' ', (c&0x04)?'s':' ', - (c&0x4000)?'<':' ', (c&0x1000)?'^':' ', (c&0x8000)?'>':' ', (c&0x2000)?'v':' ', - (c&0x0100)?'A':' ', (c&0x0200)?'B':' ', (c&0x0800)?'S':' ', (c&0x0400)?'s':' '); - if(!(c&0xff00)) - inputstr[8] = '\0'; - } - if(frameDisplayOn && !input_display) - FCEU_DispMessage("%s frame %u",current >= 0?"Recording":"Playing",framecount); - else if(input_display && !frameDisplayOn) - FCEU_DispMessage("Input: %s",inputstr); - else //if(input_display && frame_display) - FCEU_DispMessage("%s %u %s",current >= 0?"Recording":"Playing",framecount,inputstr); - */ - if(frameDisplayOn) - { - FCEU_DispMessage("%s frame %u",current >= 0?"Recording":"Playing",framecount); - } - last_frame_display = framecount; - last_input_display = cur_input_display; - movcounter=180-1; - return; - } + //mbg todo - if(movcounter) movcounter--; - - if(!MovieShow) return; - - FCEU_DrawNumberRow(XBuf,MovieStatus, CurrentMovie); - MovieShow--; +// int frameDisplayOn = current != 0 && frame_display; +// extern int howlong; +//#if MSVC +// extern int32 fps_scale; +//#else +// int32 fps_scale=256; +//#endif +// int howl=(180-(FCEUI_EmulationPaused()?(60):(20*fps_scale/256))); +// if(howl>176) howl=180; +// if(howl<1) howl=1; +// if((howlong':' ', (c&0x20)?'v':' ', +// (c&0x01)?'A':' ', (c&0x02)?'B':' ', (c&0x08)?'S':' ', (c&0x04)?'s':' ', +// (c&0x4000)?'<':' ', (c&0x1000)?'^':' ', (c&0x8000)?'>':' ', (c&0x2000)?'v':' ', +// (c&0x0100)?'A':' ', (c&0x0200)?'B':' ', (c&0x0800)?'S':' ', (c&0x0400)?'s':' '); +// if(!(c&0xff00)) +// inputstr[8] = '\0'; +// } +// if(frameDisplayOn && !input_display) +// FCEU_DispMessage("%s frame %u",current >= 0?"Recording":"Playing",framecount); +// else if(input_display && !frameDisplayOn) +// FCEU_DispMessage("Input: %s",inputstr); +// else //if(input_display && frame_display) +// FCEU_DispMessage("%s %u %s",current >= 0?"Recording":"Playing",framecount,inputstr); +// */ +// if(frameDisplayOn) +// { +// FCEU_DispMessage("%s frame %u",current >= 0?"Recording":"Playing",framecount); +// } +// last_frame_display = framecount; +// last_input_display = cur_input_display; +// movcounter=180-1; +// return; +// } +// +// if(movcounter) movcounter--; +// +// if(!MovieShow) return; +// +// FCEU_DrawNumberRow(XBuf,MovieStatus, CurrentMovie); +// MovieShow--; } int FCEUMOV_WriteState(FILE* st) @@ -1024,61 +696,61 @@ static int load_successful; int FCEUMOV_ReadState(FILE* st, uint32 size) { - // 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 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); - } + //// 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; + //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); + //// 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; } @@ -1096,14 +768,14 @@ int FCEUMOV_PostLoad(void) return load_successful; } -char* FCEUI_MovieGetCurrentName(int addSlotNumber) -{ - return FCEU_MakeFName(FCEUMKF_MOVIE,(addSlotNumber ? CurrentMovie : -1),0); -} - int FCEUI_IsMovieActive(void) { - return current; + //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; } void FCEUI_MovieToggleFrameDisplay(void) @@ -1162,144 +834,166 @@ void FCEUI_MovieToggleReadOnly(void) } } -char lastMovieInfoFilename [512] = {'\0',}; int FCEUI_MovieGetInfo(const char* fname, MOVIE_INFO* info) { - MovieFlushHeader(); + memset(info,0,sizeof(MOVIE_INFO)); - char buffer [512]; - fname = (const char*)convertToFCM(fname,buffer); - strncpy(lastMovieInfoFilename, fname, 512); + MovieData md; + FILE* fp = FCEUD_UTF8fopen(fname, "rb"); + if(!fp) return 0; + LoadFM2(md, fp); + fclose(fp); - // main get info part of function - { - uint32 magic; - uint32 version; - uint8 _flags[4]; + 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; - 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; pname_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(pmetadata && info->metadata_size) - info->metadata[0]='\0'; - - while(r > 0) - { - for(p=0; pmetadata && 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; - } + return 1; } + + +// +//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; pname_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(pmetadata && info->metadata_size) +// info->metadata[0]='\0'; +// +// while(r > 0) +// { +// for(p=0; pmetadata && 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; +// } +//} /* Backwards compat */ @@ -1319,225 +1013,407 @@ uint32 offset_to_movie_data; uint16 metadata_ucs2[]; // ucs-2, ick! sizeof(metadata) = offset_to_savestate - MOVIE_HEADER_SIZE } */ - -#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,CurrentMovie,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=1; - { - FCEUGI * gi = FCEUI_LoadGame(lastLoadedGameName, 0); - if(!gi) - PowerNES(); - } - suppressMovieStop=0; - 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 = CurrentMovie; - slots[current] = fp; - - memset(joop,0,sizeof(joop)); - current = -1 - current; - framets=0; - nextts=0; - nextd = -1; - MovieStatus[CurrentMovie] = 1; - if(!fname) - FCEUI_SelectMovie(CurrentMovie,1); /* Quick hack to display status. */ - else - 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) - 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 +// +//#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) +// 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; +//} - 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; +// } +//} diff --git a/src/movie.h b/src/movie.h index c48f05ae..205d041d 100644 --- a/src/movie.h +++ b/src/movie.h @@ -2,12 +2,11 @@ #define __MOVIE_H_ void FCEUMOV_AddJoy(uint8 *, int SkipFlush); -void FCEUMOV_CheckMovies(void); void FCEUMOV_AddCommand(int cmd); void FCEU_DrawMovies(uint8 *); int FCEUMOV_IsPlaying(void); int FCEUMOV_IsRecording(void); -int FCEUMOV_ShouldPause(void); +bool FCEUMOV_ShouldPause(void); int FCEUMOV_GetFrame(void); int FCEUMOV_WriteState(FILE* st); diff --git a/src/state.cpp b/src/state.cpp index 74596ebd..fbf62955 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -21,6 +21,7 @@ /* TODO: Add (better) file io error checking */ /* TODO: Change save state file format. */ +#include #include #include #include @@ -490,11 +491,12 @@ int FCEUSS_Load(char *fname) FILE *st; char *fn; - //this fixes read-only toggle problems - if(FCEUMOV_IsRecording()) { - FCEUMOV_AddCommand(0); - MovieFlushHeader(); - } + //mbg movie - this needs to be overhauled + ////this fixes read-only toggle problems + //if(FCEUMOV_IsRecording()) { + // FCEUMOV_AddCommand(0); + // MovieFlushHeader(); + //} if(geniestage==1) { @@ -621,8 +623,7 @@ int FCEUI_SelectState(int w, int show) { int oldstate=CurrentState; if(w == -1) { StateShow = 0; return 0; } //mbg merge 7/17/06 had to make return a value - FCEUI_SelectMovie(-1,0); - + CurrentState=w; if(show) { diff --git a/src/types.h b/src/types.h index f2b14624..31dd7e1c 100644 --- a/src/types.h +++ b/src/types.h @@ -24,7 +24,7 @@ #include -#define FCEU_VERSION_NUMERIC 9816 +#define FCEU_VERSION_NUMERIC 19901 #define FCEU_NAME "FCE Ultra" #define FCEU_VERSION_STRING "1.9.9" #define FCEU_NAME_AND_VERSION FCEU_NAME " " FCEU_VERSION_STRING @@ -54,6 +54,7 @@ typedef signed int int32; #define mkdir _mkdir #define alloca _alloca #define snprintf _snprintf +#define vsnprintf _vsnprintf #define W_OK 2 #define R_OK 2 #define X_OK 1 diff --git a/src/utils/md5.cpp b/src/utils/md5.cpp index 7d4aed8f..6471c3c3 100644 --- a/src/utils/md5.cpp +++ b/src/utils/md5.cpp @@ -228,8 +228,9 @@ void md5_finish( struct md5_context *ctx, uint8 digest[16] ) /* Uses a static buffer, so beware of how it's used. */ -char *md5_asciistr(uint8 digest[16]) +char *md5_asciistr(MD5DATA& md5) { + uint8* digest = md5.data; static char str[33]; static char trans[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; int x; diff --git a/src/utils/md5.h b/src/utils/md5.h index 39c01ec7..eb40cf7c 100644 --- a/src/utils/md5.h +++ b/src/utils/md5.h @@ -8,11 +8,22 @@ 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 ); void md5_finish( struct md5_context *ctx, uint8 digest[16] ); /* Uses a static buffer, so beware of how it's used. */ -char *md5_asciistr(uint8 digest[16]); +char *md5_asciistr(MD5DATA& md5); #endif /* md5.h */ diff --git a/src/utils/xstring.cpp b/src/utils/xstring.cpp index 972088ac..c90dfc38 100644 --- a/src/utils/xstring.cpp +++ b/src/utils/xstring.cpp @@ -22,6 +22,7 @@ /// \brief various string manipulation utilities #include "xstring.h" +#include ///Upper case routine. Returns number of characters modified int str_ucase(char *str) { @@ -189,3 +190,122 @@ int str_replace(char *str, char *search, char *replace) { free(astr); return j; } + +///Converts the provided data to a string in a standard, user-friendly, round-trippable format +std::string BytesToString(void* data, int len) +{ + char temp[16]; + if(len==1) { + sprintf(temp,"%d",*(unsigned char*)data); + return temp; + } else if(len==2) { + sprintf(temp,"%d",*(unsigned short*)data); + return temp; + } else if(len==4) { + sprintf(temp,"%d",*(unsigned int*)data); + return temp; + } + std::string ret; + ret.resize(len*2+2); + char* str= (char*)ret.c_str(); + str[0] = '0'; + str[1] = 'x'; + str += 2; + for(int i=0;i>4); + int b = (((unsigned char*)data)[i])&15; + if(a>9) a += 'A'-10; + else a += '0'; + if(b>9) b += 'A'-10; + else b += '0'; + str[i*2] = a; + str[i*2+1] = b; + } + return ret; +} + +///returns -1 if this is not a hex string +int HexStringToBytesLength(std::string& str) +{ + if(str.size()>2 && str[0] == '0' && toupper(str[1]) == 'X') + return str.size()/2-1; + else return -1; +} + +///parses a string in the same format as BytesToString +///returns true if success. +bool StringToBytes(std::string& str, void* data, int len) +{ + if(str.size()>2 && str[0] == '0' && toupper(str[1]) == 'X') + goto hex; + + if(len==1) { + int x = atoi(str.c_str()); + *(unsigned char*)data = x; + return true; + } else if(len==2) { + int x = atoi(str.c_str()); + *(unsigned short*)data = x; + return true; + } else if(len==4) { + int x = atoi(str.c_str()); + *(unsigned int*)data = x; + return true; + } + //we can't handle it + return false; +hex: + int amt = len; + int bytesAvailable = str.size()/2; + if(bytesAvailable < amt) + amt = bytesAvailable; + const char* cstr = str.c_str()+2; + for(int i=0;i='A') a=a-'A'+10; + else a-='0'; + if(b>='A') b=b-'A'+10; + else b-='0'; + unsigned char val = ((unsigned char)a<<4)|(unsigned char)b; + ((unsigned char*)data)[i] = val; + } + + return true; +} + +#include +#include +/// \brief convert input string into vector of string tokens +/// +/// \note consecutive delimiters will be treated as single delimiter +/// \note delimiters are _not_ included in return data +/// +/// \param input string to be parsed +/// \param delims list of delimiters. + +std::vector tokenize_str(const std::string & str, + const std::string & delims=", \t") +{ + using namespace std; + // Skip delims at beginning, find start of first token + string::size_type lastPos = str.find_first_not_of(delims, 0); + // Find next delimiter @ end of token + string::size_type pos = str.find_first_of(delims, lastPos); + + // output vector + vector tokens; + + while (string::npos != pos || string::npos != lastPos) + { + // Found a token, add it to the vector. + tokens.push_back(str.substr(lastPos, pos - lastPos)); + // Skip delims. Note the "not_of". this is beginning of token + lastPos = str.find_first_not_of(delims, pos); + // Find next delimiter at end of token. + pos = str.find_first_of(delims, lastPos); + } + + return tokens; +} diff --git a/src/utils/xstring.h b/src/utils/xstring.h index 32646bca..b86eb139 100644 --- a/src/utils/xstring.h +++ b/src/utils/xstring.h @@ -17,9 +17,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - +#include #include #include +#include + //definitions for str_strip() flags #define STRIP_SP 0x01 /* space */ @@ -35,3 +37,9 @@ int str_rtrim(char *str, int flags); int str_strip(char *str, int flags); int chr_replace(char *str, char search, char replace); int str_replace(char *str, char *search, char *replace); + +int HexStringToBytesLength(std::string& str); +std::string BytesToString(void* data, int len); +bool StringToBytes(std::string& str, void* data, int len); + +std::vector tokenize_str(const std::string & str,const std::string & delims); \ No newline at end of file diff --git a/src/video.cpp b/src/video.cpp index eb8db600..d0d5b3ad 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -342,8 +342,9 @@ void FCEU_DispMessage(char *format, ...) howlong=180; - extern int movcounter; - movcounter=0; + //mbg movie 5/23/08 + //extern int movcounter; + //movcounter=0; } void FCEU_ResetMessages(void) diff --git a/vc8/fceux.vcproj b/vc8/fceux.vcproj index aa44e537..335af180 100644 --- a/vc8/fceux.vcproj +++ b/vc8/fceux.vcproj @@ -748,6 +748,7 @@ > @@ -2170,6 +2171,14 @@ + + +