first attempt - support loading movies from archives.

This commit is contained in:
zeromus 2008-08-10 02:33:14 +00:00
parent 9d0e6ecb9e
commit 3a0b893662
10 changed files with 197 additions and 74 deletions

View File

@ -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;i<fileSelectorContext.items.size();i++)
if(fileSelectorContext.items[i].name == *innerFilename)
if(i == (uint32)innerIndex || (innerFilename && fileSelectorContext.items[i].name == *innerFilename))
{
ret = i;
break;
@ -559,6 +559,7 @@ FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::str
fp = new FCEUFILE();
fp->archiveFilename = 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;
@ -580,3 +581,13 @@ 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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;k<strlen(md51);k++) count1 += md51[k]-'0';
for(k=0;k<strlen(md52);k++) count2 += md52[k]-'0';
if(count1 && count2)
return;
const char* tlen1=strstr(file->filename.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;k<extlen;k++)
ext[k]=tolower(ext[k]);
if(strcmp(ext,"fm2"))
continue;
if(stricmp(ext,"fm2"))
if(stricmp(ext,"zip"))
if(stricmp(ext,"rar"))
if(stricmp(ext,"7z"))
continue;
}
MOVIE_INFO info;
char filename [512];
sprintf(filename, "%s%s", globBase, wfd.cFileName);
char* dot = strrchr(filename, '.');
if(!FCEUI_MovieGetInfo(filename, &info, true))
continue;
//------------
//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))
ArchiveScanRecord asr = FCEUD_ScanArchive(filename);
if(asr.numFiles>1) {
for(int i=0;i<asr.numFiles;i++) {
FCEUFILE* fp = FCEU_fopen(filename,0,"rb",0,i);
if(fp) {
HandleScan(hwndDlg,fp, items);
delete fp;
}
}
} else
{
unsigned int k, count1=0, count2=0; //mbg merge 7/17/06 changed to uint
for(k=0;k<strlen(md51);k++) count1 += md51[k]-'0';
for(k=0;k<strlen(md52);k++) count2 += md52[k]-'0';
if(count1 && count2)
continue;
char* tlen1=strstr(wfd.cFileName, " (");
char* tlen2=strstr(FileBase, " (");
int tlen3=tlen1?(int)(tlen1-wfd.cFileName):strlen(wfd.cFileName);
int tlen4=tlen2?(int)(tlen2-FileBase):strlen(FileBase);
int len=MAX(0,MIN(tlen3,tlen4));
if(strnicmp(wfd.cFileName, FileBase, len))
{
char temp[512];
strcpy(temp,FileBase);
temp[len]='\0';
if(!strstr(wfd.cFileName, temp))
continue;
FCEUFILE* fp = FCEU_fopen(filename,0,"rb",0);
if(fp) {
HandleScan(hwndDlg,fp ,items);
delete fp;
}
}
//-------------
//if we get here, then we had a match
char relative[MAX_PATH];
AbsoluteToRelative(relative, filename, BaseDirectory.c_str());
SendDlgItemMessage(hwndDlg, IDC_COMBO_FILENAME, CB_INSERTSTRING, i++, (LPARAM)relative);
} while(FindNextFile(hFind, &wfd));
FindClose(hFind);
}
@ -457,9 +495,9 @@ BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP
free(findGlob[0]);
free(findGlob[1]);
if(i>0)
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);
}

View File

@ -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;
}

View File

@ -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*);

View File

@ -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<char> 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;

View File

@ -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();

View File

@ -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;
}

View File

@ -113,3 +113,6 @@ template<typename T, int DIGITS, bool PAD> 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);