From 29cc0df5720594419a5358570ec43046573ef04e Mon Sep 17 00:00:00 2001 From: stephena Date: Wed, 13 Feb 2013 23:09:31 +0000 Subject: [PATCH] Loading from ZIP files now works from the ROM launcher. Still TODO is add support for from the commandline, as well as optimizations (automatically loading the first ROM when only one exists, or when the name of the ZIP file itself is used). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2608 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba --- src/common/FSNodeZIP.cxx | 30 +++++++- src/common/FSNodeZIP.hxx | 2 + src/common/ZipHandler.cxx | 17 ++++- src/common/ZipHandler.hxx | 2 +- src/emucore/FSNode.cxx | 22 ++++++ src/emucore/FSNode.hxx | 26 ++++++- src/emucore/MD5.cxx | 13 ++++ src/emucore/MD5.hxx | 10 +++ src/emucore/OSystem.cxx | 147 +------------------------------------ src/emucore/OSystem.hxx | 22 ------ src/emucore/PropsSet.cxx | 17 +++++ src/emucore/PropsSet.hxx | 16 ++++ src/gui/LauncherDialog.cxx | 60 ++------------- src/gui/LauncherDialog.hxx | 1 - src/gui/RomAuditDialog.cxx | 3 +- 15 files changed, 161 insertions(+), 227 deletions(-) diff --git a/src/common/FSNodeZIP.cxx b/src/common/FSNodeZIP.cxx index 4ee8b0ae7..81a2c99e4 100644 --- a/src/common/FSNodeZIP.cxx +++ b/src/common/FSNodeZIP.cxx @@ -83,8 +83,8 @@ void FilesystemNodeZIP::setFlags(const string& zipfile, if(_virtualFile.size() != 0) { _isVirtual = true; - _path += (BSPF_PATH_SEPARATOR + _virtualFile); - _shortPath += (BSPF_PATH_SEPARATOR + _virtualFile); + _path += ("/" + _virtualFile); + _shortPath += ("/" + _virtualFile); } else _isVirtual = false; @@ -112,6 +112,32 @@ bool FilesystemNodeZIP::getChildren(AbstractFSList& myList, ListMode mode, return true; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool FilesystemNodeZIP::read(uInt8*& image, uInt32& size) const +{ +cerr << "FilesystemNodeZIP::read" << endl + << " zfile: " << _zipFile << endl + << " vfile: " << _virtualFile << endl + << endl; + + ZipHandler& zip = OSystem::zip(_zipFile); + bool found = false; + while(zip.hasNext() && !found) + { +const string& next = zip.next(); +cerr << "searching: " << next << endl; + found = _virtualFile == next; + } + + if(found) + { +cerr << "decompressing ...\n"; + return zip.decompress(image, size); + } + else + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool FilesystemNodeZIP::isAbsolute() const { diff --git a/src/common/FSNodeZIP.hxx b/src/common/FSNodeZIP.hxx index 6086e46e7..15d2146a7 100644 --- a/src/common/FSNodeZIP.hxx +++ b/src/common/FSNodeZIP.hxx @@ -66,6 +66,8 @@ class FilesystemNodeZIP : public AbstractFSNode bool getChildren(AbstractFSList& list, ListMode mode, bool hidden) const; AbstractFSNode* getParent() const; + bool read(uInt8*& image, uInt32& size) const; + private: FilesystemNodeZIP(const string& zipfile, const string& virtualfile, Common::SharedPtr realnode); diff --git a/src/common/ZipHandler.cxx b/src/common/ZipHandler.cxx index 5eb46388a..e10d21089 100644 --- a/src/common/ZipHandler.cxx +++ b/src/common/ZipHandler.cxx @@ -82,9 +82,22 @@ string ZipHandler::next() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool ZipHandler::decompress(uInt8* buffer, uInt32 length) +bool ZipHandler::decompress(uInt8*& image, uInt32& length) { - return myZip && (zip_file_decompress(myZip, buffer, length) == ZIPERR_NONE); + if(myZip) + { + length = myZip->header.uncompressed_length; + image = new uInt8[length]; + if(zip_file_decompress(myZip, image, length) != ZIPERR_NONE) + { + delete[] image; image = 0; + length = 0; + return false; + } + return true; + } + else + return false; } /*------------------------------------------------- diff --git a/src/common/ZipHandler.hxx b/src/common/ZipHandler.hxx index 8d76ec89b..5cd49782e 100644 --- a/src/common/ZipHandler.hxx +++ b/src/common/ZipHandler.hxx @@ -80,7 +80,7 @@ class ZipHandler string next(); // Get next file // Decompress the currently selected file, return false on any errors - bool decompress(uInt8* buffer, uInt32 length); + bool decompress(uInt8*& image, uInt32& length); private: // Replaces functionaity of various osd_xxxx functions diff --git a/src/emucore/FSNode.cxx b/src/emucore/FSNode.cxx index e6244e68a..1d694e3ee 100644 --- a/src/emucore/FSNode.cxx +++ b/src/emucore/FSNode.cxx @@ -20,6 +20,8 @@ // Copyright (C) 2002-2004 The ScummVM project //============================================================================ +#include + #include "bspf.hxx" #include "SharedPtr.hxx" #include "FSNodeFactory.hxx" @@ -154,6 +156,26 @@ bool FilesystemNode::rename(const string& newfile) return (_realNode && _realNode->exists()) ? _realNode->rename(newfile) : false; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool FilesystemNode::read(uInt8*& image, uInt32& size) const +{ + // First let the private subclass attempt to open the file + if(_realNode->read(image, size)) + return true; + + // Otherwise, assume the file is either gzip'ed or not compressed at all + gzFile f = gzopen(getPath().c_str(), "rb"); + if(f) + { + image = new uInt8[512 * 1024]; + size = gzread(f, image, 512 * 1024); + gzclose(f); + + return true; + } + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - string FilesystemNode::createAbsolutePath( const string& p, const string& startpath, const string& ext) diff --git a/src/emucore/FSNode.hxx b/src/emucore/FSNode.hxx index 27a1753f2..db32756e5 100644 --- a/src/emucore/FSNode.hxx +++ b/src/emucore/FSNode.hxx @@ -228,12 +228,23 @@ class FilesystemNode virtual bool makeDir(); /** - Rename the current node path with the new given name. + * Rename the current node path with the new given name. * * @return bool true if the node was renamed, false otherwise. */ virtual bool rename(const string& newfile); + /** + * Read data (binary format) into the given buffer. + * + * @param buffer The buffer to containing the data + * This will be allocated by the method, and must be + * freed by the caller. + * @param size Holds the size of the created buffer. + * @return True if the read succeeded, else false for any reason + */ + virtual bool read(uInt8*& buffer, uInt32& size) const; + // TODO - this function is deprecated, and will be removed soon ... /** Create an absolute pathname from the given path (if it isn't already @@ -366,12 +377,23 @@ class AbstractFSNode virtual bool makeDir() = 0; /** - Rename the current node path with the new given name. + * Rename the current node path with the new given name. * * @return bool true if the node was renamed, false otherwise. */ virtual bool rename(const string& newfile) = 0; + /** + * Read data (binary format) into the given buffer. + * + * @param buffer The buffer to containing the data + * This will be allocated by the method, and must be + * freed by the caller. + * @param size Holds the size of the created buffer. + * @return True if the read succeeded, else false for any reason + */ + virtual bool read(uInt8*& buffer, uInt32& size) const { return false; } + /** * The parent node of this directory. * The parent of the root is the root itself. diff --git a/src/emucore/MD5.cxx b/src/emucore/MD5.cxx index 5be117623..f203699eb 100644 --- a/src/emucore/MD5.cxx +++ b/src/emucore/MD5.cxx @@ -345,3 +345,16 @@ string MD5(const uInt8* buffer, uInt32 length) return result; } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string MD5(const FilesystemNode& node) +{ + uInt8* image = 0; + uInt32 size = 0; + if(!node.read(image, size)) + return EmptyString; + + const string& md5 = MD5(image, size); + delete[] image; + return md5; +} diff --git a/src/emucore/MD5.hxx b/src/emucore/MD5.hxx index c7af54cdb..8f292ce04 100644 --- a/src/emucore/MD5.hxx +++ b/src/emucore/MD5.hxx @@ -20,6 +20,7 @@ #ifndef MD5_HXX #define MD5_HXX +#include "FSNode.hxx" #include "bspf.hxx" /** @@ -32,4 +33,13 @@ */ string MD5(const uInt8* buffer, uInt32 length); +/** + Get the MD5 Message-Digest of the file contained in 'node'. + The digest consists of 32 hexadecimal digits. + + @param node The file node to compute the digest of + @return The message-digest +*/ +string MD5(const FilesystemNode& node); + #endif diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx index e6712b1e4..22f1159db 100644 --- a/src/emucore/OSystem.cxx +++ b/src/emucore/OSystem.cxx @@ -20,7 +20,6 @@ #include #include #include -#include #include #ifdef HAVE_GETTIMEOFDAY @@ -71,8 +70,6 @@ #include "OSystem.hxx" -#define MAX_ROM_SIZE 512 * 1024 - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSystem::OSystem() : myEventHandler(NULL), @@ -662,20 +659,6 @@ string OSystem::getROMInfo(const FilesystemNode& romfile) return result; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string OSystem::MD5FromFile(const FilesystemNode& file) -{ - string md5 = ""; - - uInt8* image = 0; - uInt32 size = 0; - if((image = openROM(file, md5, size)) != 0) - if(image != 0 && size > 0) - delete[] image; - - return md5; -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void OSystem::logMessage(const string& message, uInt8 level) { @@ -725,7 +708,7 @@ Console* OSystem::openConsole(const FilesystemNode& romfile, string& md5, // and that the md5 (and hence the cart) has changed if(props.get(Cartridge_MD5) != cartmd5) { - string name = props.get(Cartridge_Name); + const string& name = props.get(Cartridge_Name); if(!myPropSet->getMD5(cartmd5, props)) { // Cart md5 wasn't found, so we create a new props for it @@ -771,7 +754,7 @@ Console* OSystem::openConsole(const FilesystemNode& romfile, string& md5, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8* OSystem::openROM(const FilesystemNode& romfile, string& md5, uInt32& size) +uInt8* OSystem::openROM(const FilesystemNode& rom, string& md5, uInt32& size) { // This method has a documented side-effect: // It not only loads a ROM and creates an array with its contents, @@ -779,32 +762,7 @@ uInt8* OSystem::openROM(const FilesystemNode& romfile, string& md5, uInt32& size // contain a valid name uInt8* image = 0; - -// FIXME - this entire code to be replaced by romfile.read(...) -#if 0 - // First try to load as ZIP archive - if(loadFromZIP(file, &image, size)) - { - // Empty file means it *was* a ZIP file, but it didn't contain - // a valid ROM - if(image == 0) - return image; - } - else -#endif - { - // Assume the file is either gzip'ed or not compressed at all - gzFile f = gzopen(romfile.getPath().c_str(), "rb"); - if(!f) - return image; - - image = new uInt8[MAX_ROM_SIZE]; - size = gzread(f, image, MAX_ROM_SIZE); - gzclose(f); - } - - // Zero-byte files should be automatically discarded - if(size == 0) + if(!rom.read(image, size)) { delete[] image; return (uInt8*) 0; @@ -820,108 +778,11 @@ uInt8* OSystem::openROM(const FilesystemNode& romfile, string& md5, uInt32& size // be an entry in stella.pro. In that case, we use the rom name // and reinsert the properties object Properties props; - if(!myPropSet->getMD5(md5, props)) - { - props.set(Cartridge_MD5, md5); - props.set(Cartridge_Name, romfile.getName()); - myPropSet->insert(props, false); - } + myPropSet->getMD5WithInsert(rom, md5, props); return image; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool OSystem::loadFromZIP(const string& filename, uInt8** image, uInt32& size) -{ -return false; -#if 0 // FIXME - // First determine if this actually is a ZIP file - // by seeing if it contains the '.zip' extension - size_t extpos = BSPF_findIgnoreCase(filename, ".zip"); - if(extpos == string::npos) - return false; - - // Now get the file after the .zip extension (if any) - string archive = filename, fileinzip = ""; - if(filename.size() > extpos + 4) - { - archive = filename.substr(0, extpos + 4); - fileinzip = filename.substr(extpos + 5); - if(fileinzip.size() == 0) - return true; // This was a ZIP file, but invalid name - } - - // Open archive - FilesystemNode searchnode(fileinzip); - unzFile tz; - bool found = false; - if((tz = unzOpen(archive.c_str())) != NULL) - { - if(unzGoToFirstFile(tz) == UNZ_OK) - { - unz_file_info ufo; - - for(;;) // Loop through all files for valid 2600 images - { - // Longer filenames might be possible, but I don't - // think people would name files that long in zip files... - char currfile[1024]; - - unzGetCurrentFileInfo(tz, &ufo, currfile, 1024, 0, 0, 0, 0); - currfile[1023] = '\0'; - - if(strlen(currfile) >= 4 && - !BSPF_startsWithIgnoreCase(filename, "__MACOSX")) - { - // Grab 3-character extension - const char* ext = currfile + strlen(currfile) - 4; - - if(BSPF_equalsIgnoreCase(ext, ".a26") || BSPF_equalsIgnoreCase(ext, ".bin") || - BSPF_equalsIgnoreCase(ext, ".rom")) - { - // Either match the first file or the one we're looking for - if(fileinzip.empty() || searchnode == FilesystemNode(currfile)) - { - found = true; - break; - } - } - } - - // Scan the next file in the zip - if(unzGoToNextFile(tz) != UNZ_OK) - break; - } - - // Now see if we got a valid image - if(!found || ufo.uncompressed_size <= 0) - { - unzClose(tz); - return true; - } - size = ufo.uncompressed_size; - *image = new uInt8[size]; - - // We don't have to check for any return errors from these functions, - // since if there are, 'image' will not contain a valid ROM and the - // calling method can take care of it - unzOpenCurrentFile(tz); - unzReadCurrentFile(tz, *image, size); - unzCloseCurrentFile(tz); - unzClose(tz); - - return true; - } - else - { - unzClose(tz); - return true; - } - } - return false; -#endif -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - string OSystem::getROMInfo(const Console* console) { diff --git a/src/emucore/OSystem.hxx b/src/emucore/OSystem.hxx index 97225d20c..1e41b890f 100644 --- a/src/emucore/OSystem.hxx +++ b/src/emucore/OSystem.hxx @@ -432,13 +432,6 @@ class OSystem */ const string& buildInfo() const { return myBuildInfo; } - /** - Calculate the MD5sum of the given file. - - @param file File node of potential ROM file - */ - string MD5FromFile(const FilesystemNode& file); - /** Issue a quit event to the OSystem. */ @@ -701,21 +694,6 @@ class OSystem */ uInt8* openROM(const FilesystemNode& rom, string& md5, uInt32& size); - /** - Open the given ZIP archive, parsing filename for the contents of the - desired file to load. If the filename ends in '.zip', use the first - file found in the archive, otherwise traverse the archive and use the - file specified after '.zip' in the filename. - - @param name The absolute pathname of the file - @param image Pointer to the array, with size >=0 indicating valid data - @param size The amount of data read into the image array - - @return True if this was a zip file (regardless of whether it - actually loaded any valid data, else false - */ - bool loadFromZIP(const string& filename, uInt8** image, uInt32& size); - /** Gets all possible info about the given console. diff --git a/src/emucore/PropsSet.cxx b/src/emucore/PropsSet.cxx index c0c823c7d..9c2c0eec9 100644 --- a/src/emucore/PropsSet.cxx +++ b/src/emucore/PropsSet.cxx @@ -148,6 +148,23 @@ bool PropertiesSet::getMD5(const string& md5, Properties& properties, return found; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PropertiesSet::getMD5WithInsert(const FilesystemNode& rom, + const string& md5, Properties& properties) +{ + if(!getMD5(md5, properties)) + { + // Create a name suitable for using in properties + size_t pos = rom.getName().find_last_of("/\\"); + const string& name = pos == string::npos ? rom.getName() : + rom.getName().substr(pos+1); + + properties.set(Cartridge_MD5, md5); + properties.set(Cartridge_Name, name); + insert(properties, false); + } +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PropertiesSet::insert(const Properties& properties, bool save) { diff --git a/src/emucore/PropsSet.hxx b/src/emucore/PropsSet.hxx index 468c359c0..d8224285f 100644 --- a/src/emucore/PropsSet.hxx +++ b/src/emucore/PropsSet.hxx @@ -23,6 +23,7 @@ #include #include "bspf.hxx" +#include "FSNode.hxx" #include "Props.hxx" class OSystem; @@ -84,6 +85,21 @@ class PropertiesSet bool getMD5(const string& md5, Properties& properties, bool useDefaults = false) const; + /** + Get the property from the set with the given MD5, at the same time + checking if it exists. If it doesn't, insert a temporary copy into + the set. + + @param file The node representing the + @param md5 The md5 of the property to get + @param properties The properties with the given MD5, or the default + properties if not found + @param defaults Use the built-in defaults, ignoring any properties + from an external file + */ + void getMD5WithInsert(const FilesystemNode& rom, const string& md5, + Properties& properties); + /** Insert the properties into the set. If a duplicate is inserted the old properties are overwritten with the new ones. diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx index bd5a98408..02394bae7 100644 --- a/src/gui/LauncherDialog.cxx +++ b/src/gui/LauncherDialog.cxx @@ -226,10 +226,8 @@ const string& LauncherDialog::selectedRomMD5() // Make sure we have a valid md5 for this ROM if(myGameList->md5(item) == "") - { - const string& md5 = instance().MD5FromFile(node); - myGameList->setMd5(item, md5); - } + myGameList->setMd5(item, MD5(node)); + return myGameList->md5(item); } @@ -353,51 +351,6 @@ void LauncherDialog::loadDirListing() if(myCurrentNode.isDirectory()) myCurrentNode.getChildren(files, FilesystemNode::kListAll); -#if 0 - else - { - unzFile tz; - if((tz = unzOpen(myCurrentNode.getPath().c_str())) != NULL) - { - if(unzGoToFirstFile(tz) == UNZ_OK) - { - unz_file_info ufo; - - for(;;) // Loop through all files for valid 2600 images - { - // Longer filenames might be possible, but I don't - // think people would name files that long in zip files... - char filename[1024]; - - unzGetCurrentFileInfo(tz, &ufo, filename, 1024, 0, 0, 0, 0); - filename[1023] = '\0'; - - if(strlen(filename) >= 4 && - !BSPF_startsWithIgnoreCase(filename, "__MACOSX")) - { -#if 0 - // Grab 3-character extension - const char* ext = filename + strlen(filename) - 4; - - if(BSPF_equalsIgnoreCase(ext, ".a26") || BSPF_equalsIgnoreCase(ext, ".bin") || - BSPF_equalsIgnoreCase(ext, ".rom")) -#endif - { - FilesystemNode newFile(FilesystemNode::createAbsolutePath( - filename, myCurrentNode.getPath(), "")); - files.push_back(newFile); - } - } - - // Scan the next file in the zip - if(unzGoToNextFile(tz) != UNZ_OK) - break; - } - } - } - } -#endif - // Add '[..]' to indicate previous folder if(myCurrentNode.hasParent()) myGameList->appendGame(" [..]", "", "", true); @@ -445,11 +398,11 @@ void LauncherDialog::loadRomInfo() { // Make sure we have a valid md5 for this ROM if(myGameList->md5(item) == "") - myGameList->setMd5(item, instance().MD5FromFile(node)); + myGameList->setMd5(item, MD5(node)); // Get the properties for this entry Properties props; - instance().propSet().getMD5(myGameList->md5(item), props); + instance().propSet().getMD5WithInsert(node, myGameList->md5(item), props); myRomInfoWidget->setProperties(props); } @@ -527,11 +480,12 @@ bool LauncherDialog::matchPattern(const string& s, const string& pattern) const return false; } +#if 0 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - int LauncherDialog::filesInArchive(const string& archive) { int count = -1; -#if 0 + unzFile tz; if((tz = unzOpen(archive.c_str())) != NULL) { @@ -566,9 +520,9 @@ int LauncherDialog::filesInArchive(const string& archive) } } } -#endif return count; } +#endif // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void LauncherDialog::handleKeyDown(StellaKey key, StellaMod mod, char ascii) diff --git a/src/gui/LauncherDialog.hxx b/src/gui/LauncherDialog.hxx index 90a1d35f1..27d640d08 100644 --- a/src/gui/LauncherDialog.hxx +++ b/src/gui/LauncherDialog.hxx @@ -96,7 +96,6 @@ class LauncherDialog : public Dialog void handleContextMenu(); void setListFilters(); bool matchPattern(const string& s, const string& pattern) const; - int filesInArchive(const string& archive); private: ButtonWidget* myStartButton; diff --git a/src/gui/RomAuditDialog.cxx b/src/gui/RomAuditDialog.cxx index e972d35d0..18cd2632f 100644 --- a/src/gui/RomAuditDialog.cxx +++ b/src/gui/RomAuditDialog.cxx @@ -30,6 +30,7 @@ #include "ProgressDialog.hxx" #include "FSNode.hxx" #include "MessageBox.hxx" +#include "MD5.hxx" #include "Props.hxx" #include "PropsSet.hxx" #include "Settings.hxx" @@ -147,7 +148,7 @@ void RomAuditDialog::auditRoms() { // Calculate the MD5 so we can get the rest of the info // from the PropertiesSet (stella.pro) - const string& md5 = instance().MD5FromFile(files[idx]); + const string& md5 = MD5(files[idx]); instance().propSet().getMD5(md5, props); const string& name = props.get(Cartridge_Name);