mirror of https://github.com/stella-emu/stella.git
Begin the process of converting all file open/close operations to be done in FSNode.
This will eventually allow ZIP files (and any other compression scheme we use in the future) to read and write as if they were normal files. Basically an implementation of a mini-VFS.
This commit is contained in:
parent
2a6c493f6f
commit
25913b791e
|
@ -181,7 +181,7 @@ size_t FilesystemNodeZIP::read(ByteBuffer& image) const
|
|||
while(myZipHandler->hasNext() && !found)
|
||||
found = myZipHandler->next() == _virtualPath;
|
||||
|
||||
return found ? uInt32(myZipHandler->decompress(image)) : 0; // TODO: 64bit
|
||||
return found ? myZipHandler->decompress(image) : 0;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -197,6 +197,20 @@ size_t FilesystemNodeZIP::read(stringstream& image) const
|
|||
return size;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
size_t FilesystemNodeZIP::write(const ByteBuffer& buffer, size_t size) const
|
||||
{
|
||||
// TODO: Not yet implemented
|
||||
throw runtime_error("ZIP file not writable");
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
size_t FilesystemNodeZIP::write(const stringstream& buffer) const
|
||||
{
|
||||
// TODO: Not yet implemented
|
||||
throw runtime_error("ZIP file not writable");
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
AbstractFSNodePtr FilesystemNodeZIP::getParent() const
|
||||
{
|
||||
|
|
|
@ -64,6 +64,8 @@ class FilesystemNodeZIP : public AbstractFSNode
|
|||
|
||||
size_t read(ByteBuffer& image) const override;
|
||||
size_t read(stringstream& image) const override;
|
||||
size_t write(const ByteBuffer& buffer, size_t size) const override;
|
||||
size_t write(const stringstream& buffer) const override;
|
||||
|
||||
private:
|
||||
FilesystemNodeZIP(const string& zipfile, const string& virtualpath,
|
||||
|
|
|
@ -1347,8 +1347,7 @@ string CartDebug::saveRom()
|
|||
const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".a26";
|
||||
|
||||
FilesystemNode node(myOSystem.defaultSaveDir() + rom);
|
||||
ofstream out(node.getPath(), std::ios::binary);
|
||||
if(out && myConsole.cartridge().saveROM(out))
|
||||
if(myConsole.cartridge().saveROM(node))
|
||||
return "saved ROM as " + node.getShortPath();
|
||||
else
|
||||
return DebuggerParser::red("failed to save ROM");
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include "FSNode.hxx"
|
||||
#include "Settings.hxx"
|
||||
#include "System.hxx"
|
||||
#include "MD5.hxx"
|
||||
|
@ -52,19 +53,24 @@ void Cartridge::setAbout(const string& about, const string& type,
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::saveROM(ofstream& out) const
|
||||
bool Cartridge::saveROM(const FilesystemNode& out) const
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
const ByteBuffer& image = getImage(size);
|
||||
if(size == 0)
|
||||
try
|
||||
{
|
||||
size_t size = 0;
|
||||
const ByteBuffer& image = getImage(size);
|
||||
if(size == 0)
|
||||
{
|
||||
cerr << "save not supported" << endl;
|
||||
return false;
|
||||
}
|
||||
out.write(image, size);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
cerr << "save not supported" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
out.write(reinterpret_cast<const char*>(image.get()), size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
class Cartridge;
|
||||
class Properties;
|
||||
class FilesystemNode;
|
||||
class CartDebugWidget;
|
||||
class CartRamWidget;
|
||||
class GuiObject;
|
||||
|
@ -72,9 +73,9 @@ class Cartridge : public Device
|
|||
/**
|
||||
Save the internal (patched) ROM image.
|
||||
|
||||
@param out The output file stream to save the image
|
||||
@param out The output file to save the image
|
||||
*/
|
||||
bool saveROM(ofstream& out) const;
|
||||
bool saveROM(const FilesystemNode& out) const;
|
||||
|
||||
/**
|
||||
Lock/unlock bankswitching capability. The debugger will lock
|
||||
|
|
|
@ -13,12 +13,8 @@
|
|||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//
|
||||
// Based on code from ScummVM - Scumm Interpreter
|
||||
// Copyright (C) 2002-2004 The ScummVM project
|
||||
//============================================================================
|
||||
|
||||
#include "Cart.hxx"
|
||||
#include "FSNodeFactory.hxx"
|
||||
#include "FSNode.hxx"
|
||||
|
||||
|
@ -227,49 +223,48 @@ bool FilesystemNode::rename(const string& newfile)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
size_t FilesystemNode::read(ByteBuffer& buffer) const
|
||||
{
|
||||
size_t size = 0;
|
||||
size_t sizeRead = 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;
|
||||
if (_realNode && (sizeRead = _realNode->read(buffer)) > 0)
|
||||
return sizeRead;
|
||||
|
||||
// Otherwise, the default behaviour is to read from a normal C++ ifstream
|
||||
buffer = make_unique<uInt8[]>(Cartridge::maxSize());
|
||||
ifstream in(getPath(), std::ios::binary);
|
||||
if (in)
|
||||
{
|
||||
in.seekg(0, std::ios::end);
|
||||
std::streampos length = in.tellg();
|
||||
sizeRead = static_cast<size_t>(in.tellg());
|
||||
in.seekg(0, std::ios::beg);
|
||||
|
||||
if (length == 0)
|
||||
if (sizeRead == 0)
|
||||
throw runtime_error("Zero-byte file");
|
||||
|
||||
size = std::min<size_t>(length, Cartridge::maxSize());
|
||||
in.read(reinterpret_cast<char*>(buffer.get()), size);
|
||||
buffer = make_unique<uInt8[]>(sizeRead);
|
||||
in.read(reinterpret_cast<char*>(buffer.get()), sizeRead);
|
||||
}
|
||||
else
|
||||
throw runtime_error("File open/read error");
|
||||
|
||||
return size;
|
||||
return sizeRead;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
size_t FilesystemNode::read(stringstream& buffer) const
|
||||
{
|
||||
size_t size = 0;
|
||||
size_t sizeRead = 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;
|
||||
if (_realNode && (sizeRead = _realNode->read(buffer)) > 0)
|
||||
return sizeRead;
|
||||
|
||||
// Otherwise, the default behaviour is to read from a normal C++ ifstream
|
||||
// and convert to a stringstream
|
||||
|
@ -277,17 +272,66 @@ size_t FilesystemNode::read(stringstream& buffer) const
|
|||
if (in)
|
||||
{
|
||||
in.seekg(0, std::ios::end);
|
||||
std::streampos length = in.tellg();
|
||||
sizeRead = static_cast<size_t>(in.tellg());
|
||||
in.seekg(0, std::ios::beg);
|
||||
|
||||
if (length == 0)
|
||||
if (sizeRead == 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");
|
||||
|
||||
return size;
|
||||
return sizeRead;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
size_t FilesystemNode::write(const ByteBuffer& buffer, size_t size) const
|
||||
{
|
||||
size_t sizeWritten = 0;
|
||||
|
||||
// First let the private subclass attempt to open the file
|
||||
if (_realNode && (sizeWritten = _realNode->write(buffer, size)) > 0)
|
||||
return sizeWritten;
|
||||
|
||||
// Otherwise, the default behaviour is to write to a normal C++ ofstream
|
||||
ofstream out(getPath(), std::ios::binary);
|
||||
if (out)
|
||||
{
|
||||
out.write(reinterpret_cast<const char*>(buffer.get()), size);
|
||||
|
||||
out.seekp(0, std::ios::end);
|
||||
sizeWritten = static_cast<size_t>(out.tellp());
|
||||
out.seekp(0, std::ios::beg);
|
||||
}
|
||||
else
|
||||
throw runtime_error("File open/write error");
|
||||
|
||||
return sizeWritten;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
size_t FilesystemNode::write(const stringstream& buffer) const
|
||||
{
|
||||
size_t sizeWritten = 0;
|
||||
|
||||
// First let the private subclass attempt to open the file
|
||||
if (_realNode && (sizeWritten = _realNode->write(buffer)) > 0)
|
||||
return sizeWritten;
|
||||
|
||||
// Otherwise, the default behaviour is to write to a normal C++ ofstream
|
||||
ofstream out(getPath(), std::ios::binary);
|
||||
if (out)
|
||||
{
|
||||
out << buffer.rdbuf();
|
||||
|
||||
out.seekp(0, std::ios::end);
|
||||
sizeWritten = static_cast<size_t>(out.tellp());
|
||||
out.seekp(0, std::ios::beg);
|
||||
}
|
||||
else
|
||||
throw runtime_error("File open/write error");
|
||||
|
||||
return sizeWritten;
|
||||
}
|
||||
|
|
|
@ -13,42 +13,24 @@
|
|||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//
|
||||
// Based on code from ScummVM - Scumm Interpreter
|
||||
// Copyright (C) 2002-2004 The ScummVM project
|
||||
//============================================================================
|
||||
|
||||
#ifndef FS_NODE_HXX
|
||||
#define FS_NODE_HXX
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "bspf.hxx"
|
||||
|
||||
/*
|
||||
* The API described in this header is meant to allow for file system browsing in a
|
||||
* portable fashions. To this ends, multiple or single roots have to be supported
|
||||
* portable fashion. To this end, multiple or single roots have to be supported
|
||||
* (compare Unix with a single root, Windows with multiple roots C:, D:, ...).
|
||||
*
|
||||
* To this end, we abstract away from paths; implementations can be based on
|
||||
* paths (and it's left to them whether / or \ or : is the path separator :-);
|
||||
* but it is also possible to use inodes or vrefs (MacOS 9) or anything else.
|
||||
*
|
||||
* You may ask now: "isn't this cheating? Why do we go through all this when we use
|
||||
* a path in the end anyway?!?".
|
||||
* Well, for once as long as we don't provide our own file open/read/write API, we
|
||||
* still have to use fopen(). Since all our targets already support fopen(), it should
|
||||
* be possible to get a fopen() compatible string for any file system node.
|
||||
*
|
||||
* Secondly, with this abstraction layer, we still avoid a lot of complications based on
|
||||
* differences in FS roots, different path separators, or even systems with no real
|
||||
* paths (MacOS 9 doesn't even have the notion of a "current directory").
|
||||
* And if we ever want to support devices with no FS in the classical sense (Palm...),
|
||||
* we can build upon this.
|
||||
* paths (and it's left to them whether / or \ or : is the path separator :-).
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "bspf.hxx"
|
||||
|
||||
class FilesystemNode;
|
||||
class AbstractFSNode;
|
||||
using AbstractFSNodePtr = shared_ptr<AbstractFSNode>;
|
||||
|
@ -233,7 +215,7 @@ class FilesystemNode
|
|||
/**
|
||||
* Read data (binary format) into the given buffer.
|
||||
*
|
||||
* @param buffer The buffer to contain the data.
|
||||
* @param buffer The buffer to contain the data (allocated in this method).
|
||||
*
|
||||
* @return The number of bytes read (0 in the case of failure)
|
||||
* This method can throw exceptions, and should be used inside
|
||||
|
@ -252,6 +234,29 @@ class FilesystemNode
|
|||
*/
|
||||
size_t read(stringstream& buffer) const;
|
||||
|
||||
/**
|
||||
* Write data (binary format) from the given buffer.
|
||||
*
|
||||
* @param buffer The buffer that contains the data.
|
||||
* @param size The size of the buffer.
|
||||
*
|
||||
* @return The number of bytes written (0 in the case of failure)
|
||||
* This method can throw exceptions, and should be used inside
|
||||
* a try-catch block.
|
||||
*/
|
||||
size_t write(const ByteBuffer& buffer, size_t size) const;
|
||||
|
||||
/**
|
||||
* Write data (text format) from the given stream.
|
||||
*
|
||||
* @param buffer The buffer stream that contains the data.
|
||||
*
|
||||
* @return The number of bytes written (0 in the case of failure)
|
||||
* This method can throw exceptions, and should be used inside
|
||||
* a try-catch block.
|
||||
*/
|
||||
size_t write(const stringstream& buffer) const;
|
||||
|
||||
/**
|
||||
* The following methods are almost exactly the same as the various
|
||||
* getXXXX() methods above. Internally, they call the respective methods
|
||||
|
@ -403,9 +408,8 @@ class AbstractFSNode
|
|||
/**
|
||||
* 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 buffer The buffer to contain the data (allocated in this method).
|
||||
*
|
||||
* @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.
|
||||
|
@ -413,15 +417,38 @@ class AbstractFSNode
|
|||
virtual size_t read(ByteBuffer& buffer) const { return 0; }
|
||||
|
||||
/**
|
||||
* Read data (text format) into the given steam.
|
||||
* Read data (text format) into the given stream.
|
||||
*
|
||||
* @param buffer The buffer stream to containing the data
|
||||
* @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.
|
||||
*/
|
||||
virtual size_t read(stringstream& buffer) const { return 0; }
|
||||
|
||||
/**
|
||||
* Write data (binary format) from the given buffer.
|
||||
*
|
||||
* @param buffer The buffer that contains the data.
|
||||
* @param size The size of the buffer.
|
||||
*
|
||||
* @return The number of bytes written (0 in the case of failure)
|
||||
* This method can throw exceptions, and should be used inside
|
||||
* a try-catch block.
|
||||
*/
|
||||
virtual size_t write(const ByteBuffer& buffer, size_t size) const { return 0; }
|
||||
|
||||
/**
|
||||
* Write data (text format) from the given stream.
|
||||
*
|
||||
* @param buffer The buffer stream that contains the data.
|
||||
*
|
||||
* @return The number of bytes written (0 in the case of failure)
|
||||
* This method can throw exceptions, and should be used inside
|
||||
* a try-catch block.
|
||||
*/
|
||||
virtual size_t write(const stringstream& buffer) const { return 0; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -251,7 +251,7 @@ void OSystem::saveConfig()
|
|||
Logger::debug("Saving config options ...");
|
||||
mySettings->save();
|
||||
|
||||
if(myPropSet && myPropSet->save(myPropertiesFile.getPath()))
|
||||
if(myPropSet && myPropSet->save(myPropertiesFile))
|
||||
Logger::debug("Saving properties set ...");
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "bspf.hxx"
|
||||
#include "FSNode.hxx"
|
||||
#include "Logger.hxx"
|
||||
#include "DefProps.hxx"
|
||||
#include "Props.hxx"
|
||||
#include "PropsSet.hxx"
|
||||
|
@ -42,22 +43,31 @@ void PropertiesSet::load(const FilesystemNode& file, bool save)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool PropertiesSet::save(const string& filename) const
|
||||
bool PropertiesSet::save(const FilesystemNode& file) const
|
||||
{
|
||||
// Only save properties when it won't create an empty file
|
||||
FilesystemNode props(filename);
|
||||
if(!props.exists() && myExternalProps.size() == 0)
|
||||
return false;
|
||||
try
|
||||
{
|
||||
// Only save properties when it won't create an empty file
|
||||
if(!file.exists() && myExternalProps.size() == 0)
|
||||
return false;
|
||||
|
||||
ofstream out(filename);
|
||||
if(!out)
|
||||
return false;
|
||||
// Only save those entries in the external list
|
||||
stringstream out;
|
||||
for(const auto& i: myExternalProps)
|
||||
out << i.second;
|
||||
|
||||
// Only save those entries in the external list
|
||||
for(const auto& i: myExternalProps)
|
||||
out << i.second;
|
||||
file.write(out);
|
||||
return true;
|
||||
}
|
||||
catch(const runtime_error& e)
|
||||
{
|
||||
Logger::error(e.what());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -57,14 +57,14 @@ class PropertiesSet
|
|||
/**
|
||||
Save properties to the specified file.
|
||||
|
||||
@param filename Full pathname of output file to use
|
||||
@param file The node representing the output file to use
|
||||
|
||||
@return True on success, false on failure or save not needed
|
||||
Failure occurs if file couldn't be opened for writing,
|
||||
or if the file doesn't exist and a zero-byte file
|
||||
would be created
|
||||
*/
|
||||
bool save(const string& filename) const;
|
||||
bool save(const FilesystemNode& file) const;
|
||||
|
||||
/**
|
||||
Get the property from the set with the given MD5.
|
||||
|
|
Loading…
Reference in New Issue