mirror of https://github.com/stella-emu/stella.git
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:
parent
e3ba9f0ec2
commit
973a8edf30
|
@ -12,7 +12,7 @@
|
||||||
Release History
|
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
|
* Updated the CompuMate keyboard handler to recognize more keys on an
|
||||||
actual keyboard, instead of having to remember the weird combinations
|
actual keyboard, instead of having to remember the weird combinations
|
||||||
|
@ -27,6 +27,11 @@
|
||||||
* Updated FA2 bankswitch scheme (Star Castle) to emulate load/save
|
* Updated FA2 bankswitch scheme (Star Castle) to emulate load/save
|
||||||
high score functionality to the Harmony cart flash RAM.
|
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
|
* Replaced commandline argument 'uselauncher' with 'exitlauncher'. The
|
||||||
new option specifies the behaviour of the ROM launcher when exiting
|
new option specifies the behaviour of the ROM launcher when exiting
|
||||||
a ROM (always exit to launcher, or only when the launcher was actually
|
a ROM (always exit to launcher, or only when the launcher was actually
|
||||||
|
|
|
@ -130,6 +130,20 @@ inline string BSPF_toString(int num)
|
||||||
return buf.str();
|
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)
|
// Test whether two strings are equal (case insensitive)
|
||||||
inline bool BSPF_equalsIgnoreCase(const string& s1, const string& s2)
|
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;
|
return BSPF_strncasecmp(s1, s2, strlen(s2)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test whether two characters are equal (case insensitive)
|
// Test whether the first string ends with the second one (case insensitive)
|
||||||
static bool BSPF_equalsIgnoreCaseChar(char ch1, char ch2)
|
inline bool BSPF_endsWithIgnoreCase(const string& s1, const string& s2)
|
||||||
{
|
{
|
||||||
return toupper((unsigned char)ch1) == toupper((unsigned char)ch2);
|
return (s1.length() >= s2.length()) ?
|
||||||
}
|
(BSPF_findIgnoreCase(s1, s2, s1.length() - s2.length()) != string::npos) :
|
||||||
// Find location (if any) of the second string within the first
|
false;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const string EmptyString("");
|
static const string EmptyString("");
|
||||||
|
|
|
@ -758,6 +758,26 @@ uInt8* OSystem::openROM(string file, string& md5, uInt32& size)
|
||||||
|
|
||||||
uInt8* image = 0;
|
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
|
// Try to open the file as a zipped archive
|
||||||
// If that fails, we assume it's just a gzipped or normal data file
|
// If that fails, we assume it's just a gzipped or normal data file
|
||||||
unzFile tz;
|
unzFile tz;
|
||||||
|
@ -783,11 +803,14 @@ uInt8* OSystem::openROM(string file, string& md5, uInt32& size)
|
||||||
|
|
||||||
if(BSPF_equalsIgnoreCase(ext, ".a26") || BSPF_equalsIgnoreCase(ext, ".bin") ||
|
if(BSPF_equalsIgnoreCase(ext, ".a26") || BSPF_equalsIgnoreCase(ext, ".bin") ||
|
||||||
BSPF_equalsIgnoreCase(ext, ".rom"))
|
BSPF_equalsIgnoreCase(ext, ".rom"))
|
||||||
|
{
|
||||||
|
if(fileInZip.empty() || fileInZip == filename)
|
||||||
{
|
{
|
||||||
file = filename;
|
file = filename;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Scan the next file in the zip
|
// Scan the next file in the zip
|
||||||
if(unzGoToNextFile(tz) != UNZ_OK)
|
if(unzGoToNextFile(tz) != UNZ_OK)
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "StringList.hxx"
|
#include "StringList.hxx"
|
||||||
#include "StringListWidget.hxx"
|
#include "StringListWidget.hxx"
|
||||||
#include "Widget.hxx"
|
#include "Widget.hxx"
|
||||||
|
#include "unzip.h"
|
||||||
|
|
||||||
#include "LauncherDialog.hxx"
|
#include "LauncherDialog.hxx"
|
||||||
|
|
||||||
|
@ -341,12 +342,55 @@ void LauncherDialog::updateListing(const string& nameToSelect)
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void LauncherDialog::loadDirListing()
|
void LauncherDialog::loadDirListing()
|
||||||
{
|
{
|
||||||
if(!myCurrentNode.isDirectory())
|
if(!myCurrentNode.isDirectory() && !myCurrentNode.exists())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FSList files;
|
FSList files;
|
||||||
files.reserve(2048);
|
files.reserve(2048);
|
||||||
|
|
||||||
|
if(myCurrentNode.isDirectory())
|
||||||
|
{
|
||||||
myCurrentNode.getChildren(files, FilesystemNode::kListAll);
|
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
|
// Add '[..]' to indicate previous folder
|
||||||
if(myCurrentNode.hasParent())
|
if(myCurrentNode.hasParent())
|
||||||
|
@ -364,7 +408,9 @@ void LauncherDialog::loadDirListing()
|
||||||
// that we want - if there are no extensions, it implies show all files
|
// 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'
|
// In this way, showing all files is on the 'fast code path'
|
||||||
if(isDir)
|
if(isDir)
|
||||||
|
{
|
||||||
name = " [" + name + "]";
|
name = " [" + name + "]";
|
||||||
|
}
|
||||||
else if(myRomExts.size() > 0)
|
else if(myRomExts.size() > 0)
|
||||||
{
|
{
|
||||||
// Skip over those names we've filtered out
|
// Skip over those names we've filtered out
|
||||||
|
@ -478,6 +524,46 @@ bool LauncherDialog::matchPattern(const string& s, const string& pattern) const
|
||||||
return false;
|
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)
|
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);
|
const string& md5 = myGameList->md5(item);
|
||||||
string extension;
|
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)
|
// 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 = "";
|
string dirname = "";
|
||||||
if(myGameList->name(item) == " [..]")
|
if(myGameList->name(item) == " [..]")
|
||||||
|
|
|
@ -96,6 +96,7 @@ 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;
|
||||||
|
|
Loading…
Reference in New Issue