Added ability to descend into multi-ROM ZIP archives.

git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2450 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2012-04-27 13:34:30 +00:00
parent e3ba9f0ec2
commit 973a8edf30
5 changed files with 150 additions and 16 deletions

View File

@ -12,7 +12,7 @@
Release History
===========================================================================
3.6.1 to 3.7: (April xx, 2012)
3.6.1 to 3.7: (May xx, 2012)
* Updated the CompuMate keyboard handler to recognize more keys on an
actual keyboard, instead of having to remember the weird combinations
@ -27,6 +27,11 @@
* Updated FA2 bankswitch scheme (Star Castle) to emulate load/save
high score functionality to the Harmony cart flash RAM.
* Added ability for ROM launcher to 'descend' into ZIP files when it
contains more than one ROM file. This means you no longer have to unzip
a multi-file archive before using each ROM. Thanks go to Roland
Schabenberger (webOS maintainer) for this idea and sample code.
* Replaced commandline argument 'uselauncher' with 'exitlauncher'. The
new option specifies the behaviour of the ROM launcher when exiting
a ROM (always exit to launcher, or only when the launcher was actually

View File

@ -130,6 +130,20 @@ inline string BSPF_toString(int num)
return buf.str();
}
// Test whether two characters are equal (case insensitive)
static bool BSPF_equalsIgnoreCaseChar(char ch1, char ch2)
{
return toupper((unsigned char)ch1) == toupper((unsigned char)ch2);
}
// Find location (if any) of the second string within the first,
// starting from 'startpos' in the first string
inline size_t BSPF_findIgnoreCase(const string& s1, const string& s2, int startpos = 0)
{
string::const_iterator pos = std::search(s1.begin()+startpos, s1.end(),
s2.begin(), s2.end(), BSPF_equalsIgnoreCaseChar);
return pos == s1.end() ? string::npos : pos - (s1.begin()+startpos);
}
// Test whether two strings are equal (case insensitive)
inline bool BSPF_equalsIgnoreCase(const string& s1, const string& s2)
{
@ -150,17 +164,12 @@ inline bool BSPF_startsWithIgnoreCase(const char* s1, const char* s2)
return BSPF_strncasecmp(s1, s2, strlen(s2)) == 0;
}
// Test whether two characters are equal (case insensitive)
static bool BSPF_equalsIgnoreCaseChar(char ch1, char ch2)
// Test whether the first string ends with the second one (case insensitive)
inline bool BSPF_endsWithIgnoreCase(const string& s1, const string& s2)
{
return toupper((unsigned char)ch1) == toupper((unsigned char)ch2);
}
// Find location (if any) of the second string within the first
inline size_t BSPF_findIgnoreCase(const string& s1, const string& s2)
{
string::const_iterator pos = std::search(s1.begin(), s1.end(),
s2.begin(), s2.end(), BSPF_equalsIgnoreCaseChar);
return pos == s1.end() ? string::npos : pos - s1.begin();
return (s1.length() >= s2.length()) ?
(BSPF_findIgnoreCase(s1, s2, s1.length() - s2.length()) != string::npos) :
false;
}
static const string EmptyString("");

View File

@ -758,6 +758,26 @@ uInt8* OSystem::openROM(string file, string& md5, uInt32& size)
uInt8* image = 0;
// Split zip file path and file name within zip archive
string fileInZip = "";
FilesystemNode fileNode(file);
if (!fileNode.exists())
{
size_t slashPos = file.rfind(BSPF_PATH_SEPARATOR);
if(slashPos != string::npos)
{
string parent = file.substr(0, slashPos);
string name = file.substr(slashPos+1);
FilesystemNode parentNode(parent);
if(parentNode.exists() && !parentNode.isDirectory() &&
BSPF_endsWithIgnoreCase(parent, ".zip"))
{
fileInZip = name;
file = parent;
}
}
}
// Try to open the file as a zipped archive
// If that fails, we assume it's just a gzipped or normal data file
unzFile tz;
@ -783,11 +803,14 @@ uInt8* OSystem::openROM(string file, string& md5, uInt32& size)
if(BSPF_equalsIgnoreCase(ext, ".a26") || BSPF_equalsIgnoreCase(ext, ".bin") ||
BSPF_equalsIgnoreCase(ext, ".rom"))
{
if(fileInZip.empty() || fileInZip == filename)
{
file = filename;
break;
}
}
}
// Scan the next file in the zip
if(unzGoToNextFile(tz) != UNZ_OK)

View File

@ -44,6 +44,7 @@
#include "StringList.hxx"
#include "StringListWidget.hxx"
#include "Widget.hxx"
#include "unzip.h"
#include "LauncherDialog.hxx"
@ -341,12 +342,55 @@ void LauncherDialog::updateListing(const string& nameToSelect)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::loadDirListing()
{
if(!myCurrentNode.isDirectory())
if(!myCurrentNode.isDirectory() && !myCurrentNode.exists())
return;
FSList files;
files.reserve(2048);
if(myCurrentNode.isDirectory())
{
myCurrentNode.getChildren(files, FilesystemNode::kListAll);
}
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)
{
// Grab 3-character extension
const char* ext = filename + strlen(filename) - 4;
if(BSPF_equalsIgnoreCase(ext, ".a26") || BSPF_equalsIgnoreCase(ext, ".bin") ||
BSPF_equalsIgnoreCase(ext, ".rom"))
{
FilesystemNode newFile(AbstractFilesystemNode::getAbsolutePath(
filename, myCurrentNode.getPath(), ""));
files.push_back(newFile);
}
}
// Scan the next file in the zip
if(unzGoToNextFile(tz) != UNZ_OK)
break;
}
}
}
}
// Add '[..]' to indicate previous folder
if(myCurrentNode.hasParent())
@ -364,7 +408,9 @@ void LauncherDialog::loadDirListing()
// that we want - if there are no extensions, it implies show all files
// In this way, showing all files is on the 'fast code path'
if(isDir)
{
name = " [" + name + "]";
}
else if(myRomExts.size() > 0)
{
// Skip over those names we've filtered out
@ -478,6 +524,46 @@ bool LauncherDialog::matchPattern(const string& s, const string& pattern) const
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int LauncherDialog::filesInArchive(const string& archive)
{
int count = -1;
unzFile tz;
if((tz = unzOpen(archive.c_str())) != NULL)
{
count = 0;
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)
{
// Grab 3-character extension
const char* ext = filename + strlen(filename) - 4;
if(BSPF_equalsIgnoreCase(ext, ".a26") || BSPF_equalsIgnoreCase(ext, ".bin") ||
BSPF_equalsIgnoreCase(ext, ".rom"))
++count;
}
// Scan the next file in the zip
if(unzGoToNextFile(tz) != UNZ_OK)
break;
}
}
}
return count;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::handleKeyDown(StellaKey key, StellaMod mod, char ascii)
{
@ -519,8 +605,18 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
const string& md5 = myGameList->md5(item);
string extension;
int numFilesInArchive = filesInArchive(rom);
bool isArchive = !myGameList->isDir(item) &&
BSPF_endsWithIgnoreCase(rom, ".zip");
// Directory's should be selected (ie, enter them and redisplay)
if(myGameList->isDir(item))
// Archives should be entered if they contain more than 1 file
if(isArchive && numFilesInArchive < 1)
{
instance().frameBuffer().showMessage("Archive does not contain any valid ROM files",
kMiddleCenter, true);
}
else if((isArchive && numFilesInArchive > 1) || myGameList->isDir(item))
{
string dirname = "";
if(myGameList->name(item) == " [..]")

View File

@ -96,6 +96,7 @@ 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;