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
This commit is contained in:
stephena 2013-02-13 23:09:31 +00:00
parent 28fa4af875
commit 29cc0df572
15 changed files with 161 additions and 227 deletions

View File

@ -83,8 +83,8 @@ void FilesystemNodeZIP::setFlags(const string& zipfile,
if(_virtualFile.size() != 0) if(_virtualFile.size() != 0)
{ {
_isVirtual = true; _isVirtual = true;
_path += (BSPF_PATH_SEPARATOR + _virtualFile); _path += ("/" + _virtualFile);
_shortPath += (BSPF_PATH_SEPARATOR + _virtualFile); _shortPath += ("/" + _virtualFile);
} }
else else
_isVirtual = false; _isVirtual = false;
@ -112,6 +112,32 @@ bool FilesystemNodeZIP::getChildren(AbstractFSList& myList, ListMode mode,
return true; 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 bool FilesystemNodeZIP::isAbsolute() const
{ {

View File

@ -66,6 +66,8 @@ class FilesystemNodeZIP : public AbstractFSNode
bool getChildren(AbstractFSList& list, ListMode mode, bool hidden) const; bool getChildren(AbstractFSList& list, ListMode mode, bool hidden) const;
AbstractFSNode* getParent() const; AbstractFSNode* getParent() const;
bool read(uInt8*& image, uInt32& size) const;
private: private:
FilesystemNodeZIP(const string& zipfile, const string& virtualfile, FilesystemNodeZIP(const string& zipfile, const string& virtualfile,
Common::SharedPtr<AbstractFSNode> realnode); Common::SharedPtr<AbstractFSNode> realnode);

View File

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

View File

@ -80,7 +80,7 @@ class ZipHandler
string next(); // Get next file string next(); // Get next file
// Decompress the currently selected file, return false on any errors // Decompress the currently selected file, return false on any errors
bool decompress(uInt8* buffer, uInt32 length); bool decompress(uInt8*& image, uInt32& length);
private: private:
// Replaces functionaity of various osd_xxxx functions // Replaces functionaity of various osd_xxxx functions

View File

@ -20,6 +20,8 @@
// Copyright (C) 2002-2004 The ScummVM project // Copyright (C) 2002-2004 The ScummVM project
//============================================================================ //============================================================================
#include <zlib.h>
#include "bspf.hxx" #include "bspf.hxx"
#include "SharedPtr.hxx" #include "SharedPtr.hxx"
#include "FSNodeFactory.hxx" #include "FSNodeFactory.hxx"
@ -154,6 +156,26 @@ bool FilesystemNode::rename(const string& newfile)
return (_realNode && _realNode->exists()) ? _realNode->rename(newfile) : false; 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( string FilesystemNode::createAbsolutePath(
const string& p, const string& startpath, const string& ext) const string& p, const string& startpath, const string& ext)

View File

@ -228,12 +228,23 @@ class FilesystemNode
virtual bool makeDir(); 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. * @return bool true if the node was renamed, false otherwise.
*/ */
virtual bool rename(const string& newfile); 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 ... // TODO - this function is deprecated, and will be removed soon ...
/** /**
Create an absolute pathname from the given path (if it isn't already Create an absolute pathname from the given path (if it isn't already
@ -366,12 +377,23 @@ class AbstractFSNode
virtual bool makeDir() = 0; 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. * @return bool true if the node was renamed, false otherwise.
*/ */
virtual bool rename(const string& newfile) = 0; 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 node of this directory.
* The parent of the root is the root itself. * The parent of the root is the root itself.

View File

@ -345,3 +345,16 @@ string MD5(const uInt8* buffer, uInt32 length)
return result; 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;
}

View File

@ -20,6 +20,7 @@
#ifndef MD5_HXX #ifndef MD5_HXX
#define MD5_HXX #define MD5_HXX
#include "FSNode.hxx"
#include "bspf.hxx" #include "bspf.hxx"
/** /**
@ -32,4 +33,13 @@
*/ */
string MD5(const uInt8* buffer, uInt32 length); 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 #endif

View File

@ -20,7 +20,6 @@
#include <cassert> #include <cassert>
#include <sstream> #include <sstream>
#include <fstream> #include <fstream>
#include <zlib.h>
#include <ctime> #include <ctime>
#ifdef HAVE_GETTIMEOFDAY #ifdef HAVE_GETTIMEOFDAY
@ -71,8 +70,6 @@
#include "OSystem.hxx" #include "OSystem.hxx"
#define MAX_ROM_SIZE 512 * 1024
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OSystem::OSystem() OSystem::OSystem()
: myEventHandler(NULL), : myEventHandler(NULL),
@ -662,20 +659,6 @@ string OSystem::getROMInfo(const FilesystemNode& romfile)
return result; 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) 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 // and that the md5 (and hence the cart) has changed
if(props.get(Cartridge_MD5) != cartmd5) if(props.get(Cartridge_MD5) != cartmd5)
{ {
string name = props.get(Cartridge_Name); const string& name = props.get(Cartridge_Name);
if(!myPropSet->getMD5(cartmd5, props)) if(!myPropSet->getMD5(cartmd5, props))
{ {
// Cart md5 wasn't found, so we create a new props for it // 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: // This method has a documented side-effect:
// It not only loads a ROM and creates an array with its contents, // 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 // contain a valid name
uInt8* image = 0; uInt8* image = 0;
if(!rom.read(image, size))
// 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)
{ {
delete[] image; delete[] image;
return (uInt8*) 0; 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 // be an entry in stella.pro. In that case, we use the rom name
// and reinsert the properties object // and reinsert the properties object
Properties props; Properties props;
if(!myPropSet->getMD5(md5, props)) myPropSet->getMD5WithInsert(rom, md5, props);
{
props.set(Cartridge_MD5, md5);
props.set(Cartridge_Name, romfile.getName());
myPropSet->insert(props, false);
}
return image; 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) string OSystem::getROMInfo(const Console* console)
{ {

View File

@ -432,13 +432,6 @@ class OSystem
*/ */
const string& buildInfo() const { return myBuildInfo; } 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. Issue a quit event to the OSystem.
*/ */
@ -701,21 +694,6 @@ class OSystem
*/ */
uInt8* openROM(const FilesystemNode& rom, string& md5, uInt32& size); 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. Gets all possible info about the given console.

View File

@ -148,6 +148,23 @@ bool PropertiesSet::getMD5(const string& md5, Properties& properties,
return found; 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) void PropertiesSet::insert(const Properties& properties, bool save)
{ {

View File

@ -23,6 +23,7 @@
#include <map> #include <map>
#include "bspf.hxx" #include "bspf.hxx"
#include "FSNode.hxx"
#include "Props.hxx" #include "Props.hxx"
class OSystem; class OSystem;
@ -84,6 +85,21 @@ class PropertiesSet
bool getMD5(const string& md5, Properties& properties, bool getMD5(const string& md5, Properties& properties,
bool useDefaults = false) const; 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 Insert the properties into the set. If a duplicate is inserted
the old properties are overwritten with the new ones. the old properties are overwritten with the new ones.

View File

@ -226,10 +226,8 @@ const string& LauncherDialog::selectedRomMD5()
// Make sure we have a valid md5 for this ROM // Make sure we have a valid md5 for this ROM
if(myGameList->md5(item) == "") if(myGameList->md5(item) == "")
{ myGameList->setMd5(item, MD5(node));
const string& md5 = instance().MD5FromFile(node);
myGameList->setMd5(item, md5);
}
return myGameList->md5(item); return myGameList->md5(item);
} }
@ -353,51 +351,6 @@ void LauncherDialog::loadDirListing()
if(myCurrentNode.isDirectory()) if(myCurrentNode.isDirectory())
myCurrentNode.getChildren(files, FilesystemNode::kListAll); 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 // Add '[..]' to indicate previous folder
if(myCurrentNode.hasParent()) if(myCurrentNode.hasParent())
myGameList->appendGame(" [..]", "", "", true); myGameList->appendGame(" [..]", "", "", true);
@ -445,11 +398,11 @@ void LauncherDialog::loadRomInfo()
{ {
// Make sure we have a valid md5 for this ROM // Make sure we have a valid md5 for this ROM
if(myGameList->md5(item) == "") if(myGameList->md5(item) == "")
myGameList->setMd5(item, instance().MD5FromFile(node)); myGameList->setMd5(item, MD5(node));
// Get the properties for this entry // Get the properties for this entry
Properties props; Properties props;
instance().propSet().getMD5(myGameList->md5(item), props); instance().propSet().getMD5WithInsert(node, myGameList->md5(item), props);
myRomInfoWidget->setProperties(props); myRomInfoWidget->setProperties(props);
} }
@ -527,11 +480,12 @@ bool LauncherDialog::matchPattern(const string& s, const string& pattern) const
return false; return false;
} }
#if 0
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int LauncherDialog::filesInArchive(const string& archive) int LauncherDialog::filesInArchive(const string& archive)
{ {
int count = -1; int count = -1;
#if 0
unzFile tz; unzFile tz;
if((tz = unzOpen(archive.c_str())) != NULL) if((tz = unzOpen(archive.c_str())) != NULL)
{ {
@ -566,9 +520,9 @@ int LauncherDialog::filesInArchive(const string& archive)
} }
} }
} }
#endif
return count; return count;
} }
#endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::handleKeyDown(StellaKey key, StellaMod mod, char ascii) void LauncherDialog::handleKeyDown(StellaKey key, StellaMod mod, char ascii)

View File

@ -96,7 +96,6 @@ class LauncherDialog : public Dialog
void handleContextMenu(); void handleContextMenu();
void setListFilters(); void setListFilters();
bool matchPattern(const string& s, const string& pattern) const; bool matchPattern(const string& s, const string& pattern) const;
int filesInArchive(const string& archive);
private: private:
ButtonWidget* myStartButton; ButtonWidget* myStartButton;

View File

@ -30,6 +30,7 @@
#include "ProgressDialog.hxx" #include "ProgressDialog.hxx"
#include "FSNode.hxx" #include "FSNode.hxx"
#include "MessageBox.hxx" #include "MessageBox.hxx"
#include "MD5.hxx"
#include "Props.hxx" #include "Props.hxx"
#include "PropsSet.hxx" #include "PropsSet.hxx"
#include "Settings.hxx" #include "Settings.hxx"
@ -147,7 +148,7 @@ void RomAuditDialog::auditRoms()
{ {
// Calculate the MD5 so we can get the rest of the info // Calculate the MD5 so we can get the rest of the info
// from the PropertiesSet (stella.pro) // from the PropertiesSet (stella.pro)
const string& md5 = instance().MD5FromFile(files[idx]); const string& md5 = MD5(files[idx]);
instance().propSet().getMD5(md5, props); instance().propSet().getMD5(md5, props);
const string& name = props.get(Cartridge_Name); const string& name = props.get(Cartridge_Name);