Add ability to use .pro file stored in a ZIP file (containing the ROM, with the same name).

This commit is contained in:
Stephen Anthony 2020-07-13 18:54:52 -02:30
parent 0a3d18ee65
commit 94d6715384
9 changed files with 106 additions and 22 deletions

View File

@ -18,6 +18,13 @@
* Extended global hotkeys for debug options.
* Added ability to load per-ROM properties file from a ZIP file containing
the ROM. This allows to distribute ROM and properties in one file,
which Stella can use directly.
-Have fun!
6.2 to 6.2.1: (June 20, 2020)
* Fixed Pitfall II ROM not working correctly.
@ -61,8 +68,6 @@
* The codebase now compiles under gcc6 again. Future versions will
require gcc7, though.
-Have fun!
6.1.2 to 6.2: (June 7, 2020)

View File

@ -184,6 +184,19 @@ size_t FilesystemNodeZIP::read(ByteBuffer& image) const
return found ? uInt32(myZipHandler->decompress(image)) : 0; // TODO: 64bit
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
size_t FilesystemNodeZIP::read(stringstream& image) const
{
// For now, we just read into a buffer and store in the stream
// TODO: maybe there's a more efficient way to do this?
ByteBuffer buffer;
size_t size = read(buffer);
if(size > 0)
image.write(reinterpret_cast<char*>(buffer.get()), size);
return size;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AbstractFSNodePtr FilesystemNodeZIP::getParent() const
{

View File

@ -63,6 +63,7 @@ class FilesystemNodeZIP : public AbstractFSNode
AbstractFSNodePtr getParent() const override;
size_t read(ByteBuffer& image) const override;
size_t read(stringstream& image) const override;
private:
FilesystemNodeZIP(const string& zipfile, const string& virtualpath,

View File

@ -225,7 +225,7 @@ bool FilesystemNode::rename(const string& newfile)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
size_t FilesystemNode::read(ByteBuffer& image) const
size_t FilesystemNode::read(ByteBuffer& buffer) const
{
size_t size = 0;
@ -234,11 +234,11 @@ size_t FilesystemNode::read(ByteBuffer& image) const
throw runtime_error("File not found/readable");
// First let the private subclass attempt to open the file
if (_realNode && (size = _realNode->read(image)) > 0)
if (_realNode && (size = _realNode->read(buffer)) > 0)
return size;
// Otherwise, the default behaviour is to read from a normal C++ ifstream
image = make_unique<uInt8[]>(Cartridge::maxSize());
buffer = make_unique<uInt8[]>(Cartridge::maxSize());
ifstream in(getPath(), std::ios::binary);
if (in)
{
@ -250,7 +250,41 @@ size_t FilesystemNode::read(ByteBuffer& image) const
throw runtime_error("Zero-byte file");
size = std::min<size_t>(length, Cartridge::maxSize());
in.read(reinterpret_cast<char*>(image.get()), size);
in.read(reinterpret_cast<char*>(buffer.get()), size);
}
else
throw runtime_error("File open/read error");
return size;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
size_t FilesystemNode::read(stringstream& buffer) const
{
size_t size = 0;
// File must actually exist
if (!(exists() && isReadable()))
throw runtime_error("File not found/readable");
// First let the private subclass attempt to open the file
if (_realNode && (size = _realNode->read(buffer)) > 0)
return size;
// Otherwise, the default behaviour is to read from a normal C++ ifstream
// and convert to a stringstream
ifstream in(getPath(), std::ios::binary);
if (in)
{
in.seekg(0, std::ios::end);
std::streampos length = in.tellg();
in.seekg(0, std::ios::beg);
if (length == 0)
throw runtime_error("Zero-byte file");
size = std::min<size_t>(length, Cartridge::maxSize());
buffer << in.rdbuf();
}
else
throw runtime_error("File open/read error");

View File

@ -241,6 +241,17 @@ class FilesystemNode
*/
size_t read(ByteBuffer& buffer) const;
/**
* Read data (text format) into the given stream.
*
* @param buffer The buffer stream to contain the data.
*
* @return The number of bytes read (0 in the case of failure)
* This method can throw exceptions, and should be used inside
* a try-catch block.
*/
size_t read(stringstream& buffer) const;
/**
* The following methods are almost exactly the same as the various
* getXXXX() methods above. Internally, they call the respective methods
@ -400,6 +411,17 @@ class AbstractFSNode
* a try-catch block.
*/
virtual size_t read(ByteBuffer& buffer) const { return 0; }
/**
* Read data (text format) into the given steam.
*
* @param buffer The buffer stream to containing the data
*
* @return The number of bytes read (0 in the case of failure)
* This method can throw exceptions, and should be used inside
* a try-catch block.
*/
virtual size_t read(stringstream& buffer) const { return 0; }
};
#endif

View File

@ -132,7 +132,7 @@ bool OSystem::create()
<< FilesystemNode(myConfigFile).getShortPath() << "'" << endl;
buf << "Game properties: '"
<< FilesystemNode(myPropertiesFile).getShortPath() << "'" << endl
<< myPropertiesFile.getShortPath() << "'" << endl
<< "Cheat file: '"
<< FilesystemNode(myCheatFile).getShortPath() << "'" << endl
<< "Palette file: '"
@ -251,7 +251,7 @@ void OSystem::saveConfig()
Logger::debug("Saving config options ...");
mySettings->save();
if(myPropSet && myPropSet->save(myPropertiesFile))
if(myPropSet && myPropSet->save(myPropertiesFile.getPath()))
Logger::debug("Saving properties set ...");
}
@ -286,7 +286,7 @@ void OSystem::setConfigPaths()
myCheatFile = FilesystemNode(myBaseDir + "stella.cht").getPath();
myPaletteFile = FilesystemNode(myBaseDir + "stella.pal").getPath();
myPropertiesFile = FilesystemNode(myBaseDir + "stella.pro").getPath();
myPropertiesFile = FilesystemNode(myBaseDir + "stella.pro");
#if 0
// Debug code

View File

@ -545,7 +545,7 @@ class OSystem
string myCheatFile;
string myConfigFile;
string myPaletteFile;
string myPropertiesFile;
FilesystemNode myPropertiesFile;
FilesystemNode myRomFile;
string myRomMD5;

View File

@ -24,13 +24,21 @@
#include "PropsSet.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PropertiesSet::load(const string& filename, bool save)
void PropertiesSet::load(const FilesystemNode& file, bool save)
{
ifstream in(filename);
Properties prop;
while(in >> prop)
insert(prop, save);
try
{
stringstream in;
if(file.exists() && file.read(in) > 0)
{
Properties prop;
while(in >> prop)
insert(prop, save);
}
}
catch(...)
{
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -166,8 +174,9 @@ void PropertiesSet::loadPerROM(const FilesystemNode& rom, const string& md5)
// First, does this ROM have a per-ROM properties entry?
// If so, load it into the database
FilesystemNode propsNode(rom.getPathWithExt(".pro"));
if(propsNode.exists() && propsNode.isFile())
load(propsNode.getPath(), false);
if(propsNode.exists())
load(propsNode, false);
// Next, make sure we have a valid md5 and name
Properties props;

View File

@ -48,11 +48,11 @@ class PropertiesSet
Load properties from the specified file, and create an internal
searchable list.
@param filename Full pathname of input file to use
@param save Indicates whether the properties should be saved
when the program exits
@param file The node representing the input file to use
@param save Indicates whether the properties should be saved
when the program exits
*/
void load(const string& filename, bool save = true);
void load(const FilesystemNode& file, bool save = true);
/**
Save properties to the specified file.