diff --git a/src/drivers/win/archive.cpp b/src/drivers/win/archive.cpp index e8f4fb27..2df42051 100644 --- a/src/drivers/win/archive.cpp +++ b/src/drivers/win/archive.cpp @@ -451,7 +451,7 @@ ArchiveScanRecord FCEUD_ScanArchive(std::string fname) extern HWND hAppWnd; -FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename) +FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename, int innerIndex) { FCEUFILE* fp = 0; @@ -530,10 +530,10 @@ FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::str //try to load the file directly if we're in autopilot int ret = LB_ERR; - if(innerFilename) + if(innerFilename || innerIndex != -1) { for(uint32 i=0;iarchiveFilename = fname; fp->filename = fileSelectorContext.items[ret].name; + fp->fullFilename = fp->archiveFilename + "|" + fp->filename; fp->archiveIndex = ret; fp->mode = FCEUFILE::READ; fp->size = fileSelectorContext.items[ret].size; @@ -579,4 +580,14 @@ FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::str } return fp; +} + +FCEUFILE* FCEUD_OpenArchiveIndex(ArchiveScanRecord& asr, std::string& fname, int innerIndex) +{ + return FCEUD_OpenArchive(asr, fname, 0, innerIndex); +} + +FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename) +{ + return FCEUD_OpenArchive(asr, fname, innerFilename, -1); } \ No newline at end of file diff --git a/src/drivers/win/archive.h b/src/drivers/win/archive.h index b1d6c1e2..f4eb40ca 100644 --- a/src/drivers/win/archive.h +++ b/src/drivers/win/archive.h @@ -12,6 +12,8 @@ void initArchiveSystem(); //if you want to autopilot this, pass in an innerfilename to try and automatically load FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename); +FCEUFILE* FCEUD_OpenArchiveIndex(ArchiveScanRecord& asr, std::string& fname, int innerIndex); + //scans a file to see if it is an archive you can handle ArchiveScanRecord FCEUD_ScanArchive(std::string fname); diff --git a/src/drivers/win/main.cpp b/src/drivers/win/main.cpp index 72c7f857..12c0895d 100644 --- a/src/drivers/win/main.cpp +++ b/src/drivers/win/main.cpp @@ -644,7 +644,11 @@ int main(int argc,char *argv[]) if(GameInfo && MovieToLoad) { - FCEUI_LoadMovie(MovieToLoad, replayReadOnlySetting!=0, false, replayStopFrameSetting!=0); + //switch to readonly mode if the file is an archive + if(FCEU_isFileInArchive(MovieToLoad)) + replayReadOnlySetting = true; + + FCEUI_LoadMovie(MovieToLoad, replayReadOnlySetting, false, replayStopFrameSetting!=0); free(MovieToLoad); MovieToLoad = NULL; } diff --git a/src/drivers/win/replay.cpp b/src/drivers/win/replay.cpp index ad2765d1..d445f1db 100644 --- a/src/drivers/win/replay.cpp +++ b/src/drivers/win/replay.cpp @@ -3,6 +3,7 @@ #include "main.h" #include "window.h" #include "movie.h" +#include "archive.h" // Used when deciding to automatically make the stop movie checkbox checked static bool stopframeWasEditedByUser = false; @@ -115,7 +116,11 @@ void UpdateReplayDialog(HWND hwndDlg) { MOVIE_INFO info; - if(FCEUI_MovieGetInfo(fn, &info, false)) + FCEUFILE* fp = FCEU_fopen(fn,0,"rb",0); + bool isarchive = FCEU_isFileInArchive(fn); + bool ismovie = FCEUI_MovieGetInfo(fp, &info, false); + delete fp; + if(ismovie) { char tmp[256]; uint32 div; @@ -139,6 +144,12 @@ void UpdateReplayDialog(HWND hwndDlg) SetWindowText(GetDlgItem(hwndDlg,IDC_LABEL_RECORDEDFROM),info.poweron ? "Power-On" : (info.reset?"Soft-Reset":"Savestate")); + if(isarchive) { + EnableWindow(GetDlgItem(hwndDlg,IDC_CHECK_READONLY),FALSE); + Button_SetCheck(GetDlgItem(hwndDlg,IDC_CHECK_READONLY),BST_CHECKED); + } else + EnableWindow(GetDlgItem(hwndDlg,IDC_CHECK_READONLY),TRUE); + //----------- //mbg 5/26/08 - getting rid of old movie formats @@ -321,6 +332,54 @@ BOOL CALLBACK ReplayMetadataDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, L return FALSE; } +extern char FileBase[]; + +void HandleScan(HWND hwndDlg, FCEUFILE* file, int& i) +{ + MOVIE_INFO info; + + bool scanok = FCEUI_MovieGetInfo(file, &info, true); + if(!scanok) + return; + + //------------ + //attempt to match the movie with the rom + //first, try matching md5 + //then try matching base name + char md51 [256]; + char md52 [256]; + strcpy(md51, md5_asciistr(GameInfo->MD5)); + strcpy(md52, md5_asciistr(info.md5_of_rom_used)); + if(strcmp(md51, md52)) + { + unsigned int k, count1=0, count2=0; //mbg merge 7/17/06 changed to uint + for(k=0;kfilename.c_str(), " ("); + const char* tlen2=strstr(FileBase, " ("); + int tlen3=tlen1?(int)(tlen1-file->filename.c_str()):file->filename.size(); + int tlen4=tlen2?(int)(tlen2-FileBase):strlen(FileBase); + int len=MAX(0,MIN(tlen3,tlen4)); + if(strnicmp(file->filename.c_str(), FileBase, len)) + { + char temp[512]; + strcpy(temp,FileBase); + temp[len]='\0'; + if(!strstr(file->filename.c_str(), temp)) + return; + } + } + //------------- + //if we get here, then we had a match + + char relative[MAX_PATH]; + AbsoluteToRelative(relative, file->fullFilename.c_str(), BaseDirectory.c_str()); + SendDlgItemMessage(hwndDlg, IDC_COMBO_FILENAME, CB_INSERTSTRING, i++, (LPARAM)relative); +} + BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) @@ -345,9 +404,9 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP char* findGlob[2] = {strdup(FCEU_MakeFName(FCEUMKF_MOVIEGLOB, 0, 0).c_str()), strdup(FCEU_MakeFName(FCEUMKF_MOVIEGLOB2, 0, 0).c_str())}; - int i=0, j=0; + int items=0; - for(j=0;j<2;j++) + for(int j=0;j<2;j++) { char* temp=0; do { @@ -364,7 +423,7 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP // FCEU_PrintError(findGlob[0]); // FCEU_PrintError(findGlob[1]); - for(j=0;j<2;j++) + for(int j=0;j<2;j++) { // if the two directories are the same, only look through one of them to avoid adding everything twice if(j==1 && !strnicmp(findGlob[0],findGlob[1],MAX(strlen(findGlob[0]),strlen(findGlob[1]))-6)) @@ -374,7 +433,6 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP strcpy(globBase,findGlob[j]); globBase[strlen(globBase)-5]='\0'; - extern char FileBase[]; //char szFindPath[512]; //mbg merge 7/17/06 removed WIN32_FIND_DATA wfd; HANDLE hFind; @@ -388,7 +446,7 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; - // filter out everything that's not *.fm2 + // filter out everything that's not an extension we like *.fm2 // (because FindFirstFile is too dumb to do that) { char* dot=strrchr(wfd.cFileName,'.'); @@ -399,56 +457,36 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP int k, extlen=strlen(ext); for(k=0;kMD5)); - strcpy(md52, md5_asciistr(info.md5_of_rom_used)); - if(strcmp(md51, md52)) + ArchiveScanRecord asr = FCEUD_ScanArchive(filename); + if(asr.numFiles>1) { + for(int i=0;i0) - SendDlgItemMessage(hwndDlg, IDC_COMBO_FILENAME, CB_SETCURSEL, i-1, 0); - SendDlgItemMessage(hwndDlg, IDC_COMBO_FILENAME, CB_INSERTSTRING, i++, (LPARAM)"Browse..."); + if(items>0) + SendDlgItemMessage(hwndDlg, IDC_COMBO_FILENAME, CB_SETCURSEL, items-1, 0); + SendDlgItemMessage(hwndDlg, IDC_COMBO_FILENAME, CB_INSERTSTRING, items++, (LPARAM)"Browse..."); UpdateReplayDialog(hwndDlg); } @@ -520,7 +558,7 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP memset(&ofn, 0, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hwndDlg; - ofn.lpstrFilter = "FCEUX Movie Files (*.fm2)\0*.fm2\0All files(*.*)\0*.*\0\0"; + ofn.lpstrFilter = "FCEUX Movie Files (*.fm2)\0*.fm2\0Archive files(*.zip,*.rar,*.7z)\0*.zip;*.rar;*.7z\0All files(*.*)\0*.*\0\0"; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrInitialDir = pn; @@ -530,9 +568,17 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP if(GetOpenFileName(&ofn)) { - char relative[MAX_PATH]; + char relative[MAX_PATH*2]; AbsoluteToRelative(relative, szFile, BaseDirectory.c_str()); + ArchiveScanRecord asr = FCEUD_ScanArchive(relative); + FCEUFILE* fp = FCEU_fopen(relative,0,"rb",0); + if(!fp) { + delete fp; + goto abort; + } + strcpy(relative,fp->fullFilename.c_str()); + LONG lOtherIndex = SendDlgItemMessage(hwndDlg, IDC_COMBO_FILENAME, CB_FINDSTRING, (WPARAM)-1, (LPARAM)relative); if(lOtherIndex != CB_ERR) { @@ -547,6 +593,7 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP SetFocus(GetDlgItem(hwndDlg, IDC_COMBO_FILENAME)); UpdateReplayDialog(hwndDlg); } + abort: free(pn); } diff --git a/src/file.cpp b/src/file.cpp index eb374dd6..3e543a02 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -230,7 +230,7 @@ void FCEU_SplitArchiveFilename(std::string src, std::string& archive, std::strin } } -FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, char *mode, char *ext) +FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, char *mode, char *ext, int index) { FILE *ipsfile=0; FCEUFILE *fceufp=0; @@ -264,6 +264,7 @@ FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, char *mode, char *ext } fceufp = new FCEUFILE(); fceufp->filename = fileToOpen; + fceufp->fullFilename = fileToOpen; fceufp->archiveIndex = -1; fceufp->stream = (std::iostream*)fp; FCEU_fseek(fceufp,0,SEEK_END); @@ -275,7 +276,10 @@ FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, char *mode, char *ext { //open an archive file if(archive == "") - fceufp = FCEUD_OpenArchive(asr, fileToOpen, 0); + if(index != -1) + fceufp = FCEUD_OpenArchiveIndex(asr, fileToOpen, index); + else + fceufp = FCEUD_OpenArchive(asr, fileToOpen, 0); else fceufp = FCEUD_OpenArchive(asr, archive, &fname); return fceufp; @@ -289,6 +293,7 @@ FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, char *mode, char *ext } fceufp = new FCEUFILE(); fceufp->filename = fileToOpen; + fceufp->filename = fileToOpen; fceufp->archiveIndex = -1; fceufp->stream = (std::iostream*)fp; FCEU_fseek(fceufp,0,SEEK_END); @@ -642,7 +647,8 @@ std::string FCEU_MakeFName(int type, int id1, char *cd1) break; } - return ret; + //convert | to . for archive filenames. + return mass_replace(ret,"|","."); } void GetFileBase(const char *f) @@ -684,3 +690,14 @@ void GetFileBase(const char *f) FileExt[0]=0; } } + +bool FCEU_isFileInArchive(const char *path) +{ + bool isarchive = false; + FCEUFILE* fp = FCEU_fopen(path,0,"rb",0,0); + if(fp) { + isarchive = fp->isArchive(); + delete fp; + } + return isarchive; +} \ No newline at end of file diff --git a/src/file.h b/src/file.h index 6596e865..fecbe744 100644 --- a/src/file.h +++ b/src/file.h @@ -6,8 +6,7 @@ #include "types.h" struct FCEUFILE { - //void *fp; // FILE* or ptr to ZIPWRAP - //uint32 type; // 0=normal file, 1=gzip, 2=zip + //the stream you can use to access the data std::iostream *stream; //the name of the file, or the logical name of the file within the archive @@ -16,6 +15,9 @@ struct FCEUFILE { //the filename of the archive (maybe "" if it is not in an archive) std::string archiveFilename; + //a the path to the filename, possibly using | to get into the archive + std::string fullFilename; + //the number of files that were in the archive int archiveCount; @@ -25,6 +27,9 @@ struct FCEUFILE { //the size of the file int size; + //whether the file is contained in an archive + bool isArchive() { return archiveCount > 0; } + FCEUFILE() : stream(0) , archiveCount(-1) @@ -53,10 +58,13 @@ struct ArchiveScanRecord } int type; int numFiles; + + bool isArchive() { return type != -1; } }; -FCEUFILE *FCEU_fopen(const char *path, const char *ipsfn, char *mode, char *ext); +FCEUFILE *FCEU_fopen(const char *path, const char *ipsfn, char *mode, char *ext, int index=-1); +bool FCEU_isFileInArchive(const char *path); int FCEU_fclose(FCEUFILE*); uint64 FCEU_fread(void *ptr, size_t size, size_t nmemb, FCEUFILE*); uint64 FCEU_fwrite(void *ptr, size_t size, size_t nmemb, FCEUFILE*); diff --git a/src/movie.cpp b/src/movie.cpp index c992e526..1226c4d7 100644 --- a/src/movie.cpp +++ b/src/movie.cpp @@ -456,13 +456,20 @@ static void LoadFM2_binarychunk(MovieData& movieData, std::istream* fp, int size { movieData.records[i].parseBinary(&movieData,fp); } - - } //yuck... another custom text parser. -static void LoadFM2(MovieData& movieData, std::istream* fp, int size, bool stopAfterHeader) +static bool LoadFM2(MovieData& movieData, std::istream* fp, int size, bool stopAfterHeader) { + //movie must start with "version 3" + char buf[9]; + std::ios::pos_type curr = fp->tellg(); + fp->read(buf,9); + if(fp->fail()) return false; + if(memcmp(buf,"version 3",9)) + return false; + fp->seekg(curr); + std::string key,value; enum { NEWLINE, KEY, SEPARATOR, VALUE, RECORD, COMMENT @@ -482,7 +489,7 @@ static void LoadFM2(MovieData& movieData, std::istream* fp, int size, bool stopA if(isrecchar && movieData.binaryFlag && !stopAfterHeader) { LoadFM2_binarychunk(movieData, fp, size); - return; + return true; } switch(state) { @@ -499,7 +506,7 @@ static void LoadFM2(MovieData& movieData, std::istream* fp, int size, bool stopA case RECORD: { dorecord: - if (stopAfterHeader) return; + if (stopAfterHeader) return true; int currcount = movieData.records.size(); movieData.records.resize(currcount+1); int preparse = fp->tellg(); @@ -541,6 +548,8 @@ static void LoadFM2(MovieData& movieData, std::istream* fp, int size, bool stopA done: ; if(bail) break; } + + return true; } static void closeRecordingMovie() @@ -691,10 +700,14 @@ void FCEUI_LoadMovie(const char *fname, bool _read_only, bool tasedit, int _paus currMovieData = MovieData(); strcpy(curMovieFilename, fname); - std::fstream* fp = FCEUD_UTF8_fstream(fname, "rb"); + FCEUFILE *fp = FCEU_fopen(fname,0,"rb",0); if (!fp) return; - LoadFM2(currMovieData, fp, INT_MAX, false); - fp->close(); + if(fp->isArchive() && !_read_only) { + FCEU_PrintError("Cannot open a movie in read+write from an archive."); + return; + } + + LoadFM2(currMovieData, fp->stream, INT_MAX, false); delete fp; //fully reload the game to reinitialize everything before playing any movie @@ -937,6 +950,14 @@ bool FCEUMOV_ReadState(std::istream* is, uint32 size) { load_successful = false; + //a little rule: cant load states in read+write mode with a movie from an archive. + //so we are going to switch it to readonly mode in that case + if(!movie_readonly && FCEU_isFileInArchive(curMovieFilename)) { + FCEU_PrintError("Cannot loadstate in Read+Write with movie from archive. Movie is now Read-Only."); + movie_readonly = true; + } + + ////write the state to disk so we can reload //std::vector buf(size); //fread(&buf[0],1,size,st); @@ -1081,13 +1102,11 @@ void FCEUI_MoviePlayFromBeginning(void) } -bool FCEUI_MovieGetInfo(const std::string& fname, MOVIE_INFO* info, bool skipFrameCount) +bool FCEUI_MovieGetInfo(FCEUFILE* fp, MOVIE_INFO* /* [in, out] */ info, bool skipFrameCount) { MovieData md; - std::fstream* fp = FCEUD_UTF8_fstream(fname, "rb"); - if(!fp) return false; - LoadFM2(md, fp, INT_MAX, skipFrameCount); - delete fp; + if(!LoadFM2(md, fp->stream, INT_MAX, skipFrameCount)) + return false; info->movie_version = md.version; info->poweron = md.savestate.size()==0; diff --git a/src/movie.h b/src/movie.h index f395797d..7eb6bfa1 100644 --- a/src/movie.h +++ b/src/movie.h @@ -9,6 +9,8 @@ #include "input/zapper.h" #include "utils/guid.h" +struct FCEUFILE; + enum EMOVIE_FLAG { MOVIE_FLAG_NONE = 0, @@ -232,7 +234,7 @@ void FCEUI_SaveMovie(const char *fname, EMOVIE_FLAG flags); void FCEUI_LoadMovie(const char *fname, bool read_only, bool tasedit, int _stopframe); void FCEUI_MoviePlayFromBeginning(void); void FCEUI_StopMovie(void); -bool FCEUI_MovieGetInfo(const std::string& fname, MOVIE_INFO* /* [in, out] */ info, bool skipFrameCount = false); +bool FCEUI_MovieGetInfo(FCEUFILE* fp, MOVIE_INFO* /* [in, out] */ info, bool skipFrameCount = false); char* FCEUI_MovieGetCurrentName(int addSlotNumber); void FCEUI_MovieToggleReadOnly(void); bool FCEUI_GetMovieToggleReadOnly(); diff --git a/src/utils/xstring.cpp b/src/utils/xstring.cpp index 90051b7c..8e7838d9 100644 --- a/src/utils/xstring.cpp +++ b/src/utils/xstring.cpp @@ -565,3 +565,13 @@ std::string readNullTerminatedAscii(std::istream* is) } return ret; } + +// replace all instances of victim with replacement +std::string mass_replace(const std::string &source, const std::string &victim, const std::string &replacement) +{ + std::string answer = source; + std::string::size_type j = 0; + while ((j = answer.find(victim, j)) != std::string::npos ) + answer.replace(j, victim.length(), replacement); + return answer; +} diff --git a/src/utils/xstring.h b/src/utils/xstring.h index c4cb9a89..facb6191 100644 --- a/src/utils/xstring.h +++ b/src/utils/xstring.h @@ -113,3 +113,6 @@ template void putdec(std::ostream* os, T dec) else os->write(temp,DIGITS); } + + +std::string mass_replace(const std::string &source, const std::string &victim, const std::string &replacement); \ No newline at end of file