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)
{
_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
{

View File

@ -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<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
// 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

View File

@ -20,6 +20,8 @@
// Copyright (C) 2002-2004 The ScummVM project
//============================================================================
#include <zlib.h>
#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)

View File

@ -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.

View File

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

View File

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

View File

@ -20,7 +20,6 @@
#include <cassert>
#include <sstream>
#include <fstream>
#include <zlib.h>
#include <ctime>
#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)
{

View File

@ -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.

View File

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

View File

@ -23,6 +23,7 @@
#include <map>
#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.

View File

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

View File

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

View File

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