diff --git a/src/common/FSNodeZIP.cxx b/src/common/FSNodeZIP.cxx index 50e84ab3f..c49f8359f 100644 --- a/src/common/FSNodeZIP.cxx +++ b/src/common/FSNodeZIP.cxx @@ -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 { diff --git a/src/common/FSNodeZIP.hxx b/src/common/FSNodeZIP.hxx index b2ae18f43..de8904315 100644 --- a/src/common/FSNodeZIP.hxx +++ b/src/common/FSNodeZIP.hxx @@ -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, diff --git a/src/debugger/CartDebug.cxx b/src/debugger/CartDebug.cxx index 3198eb9ea..228e7b5d0 100644 --- a/src/debugger/CartDebug.cxx +++ b/src/debugger/CartDebug.cxx @@ -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"); diff --git a/src/emucore/Cart.cxx b/src/emucore/Cart.cxx index 045d4c784..d4b5c5c22 100644 --- a/src/emucore/Cart.cxx +++ b/src/emucore/Cart.cxx @@ -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(image.get()), size); - return true; } diff --git a/src/emucore/Cart.hxx b/src/emucore/Cart.hxx index 8a2e4656e..008135a73 100644 --- a/src/emucore/Cart.hxx +++ b/src/emucore/Cart.hxx @@ -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 diff --git a/src/emucore/FSNode.cxx b/src/emucore/FSNode.cxx index 633eb6eaf..590705d03 100644 --- a/src/emucore/FSNode.cxx +++ b/src/emucore/FSNode.cxx @@ -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(Cartridge::maxSize()); ifstream in(getPath(), std::ios::binary); if (in) { in.seekg(0, std::ios::end); - std::streampos length = in.tellg(); + sizeRead = static_cast(in.tellg()); in.seekg(0, std::ios::beg); - if (length == 0) + if (sizeRead == 0) throw runtime_error("Zero-byte file"); - size = std::min(length, Cartridge::maxSize()); - in.read(reinterpret_cast(buffer.get()), size); + buffer = make_unique(sizeRead); + in.read(reinterpret_cast(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(in.tellg()); in.seekg(0, std::ios::beg); - if (length == 0) + if (sizeRead == 0) throw runtime_error("Zero-byte file"); - size = std::min(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(buffer.get()), size); + + out.seekp(0, std::ios::end); + sizeWritten = static_cast(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(out.tellp()); + out.seekp(0, std::ios::beg); + } + else + throw runtime_error("File open/write error"); + + return sizeWritten; } diff --git a/src/emucore/FSNode.hxx b/src/emucore/FSNode.hxx index 66a80ead1..f965eaa10 100644 --- a/src/emucore/FSNode.hxx +++ b/src/emucore/FSNode.hxx @@ -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 + #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 - -#include "bspf.hxx" - class FilesystemNode; class AbstractFSNode; using AbstractFSNodePtr = shared_ptr; @@ -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 diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx index 8a5a16965..f6f13f05e 100644 --- a/src/emucore/OSystem.cxx +++ b/src/emucore/OSystem.cxx @@ -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 ..."); } diff --git a/src/emucore/PropsSet.cxx b/src/emucore/PropsSet.cxx index ce5a34840..65bae06bd 100644 --- a/src/emucore/PropsSet.cxx +++ b/src/emucore/PropsSet.cxx @@ -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; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/PropsSet.hxx b/src/emucore/PropsSet.hxx index 6ecd993b3..387b79bda 100644 --- a/src/emucore/PropsSet.hxx +++ b/src/emucore/PropsSet.hxx @@ -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.