Almost all file I/O now goes through FilesystemNode::read/write, instead of raw C++ fstreams.

This allows data to be stored in a ZIP archive and for Stella to use it as if it were a normal file.
Still TODO is add ZIP write support.
This commit is contained in:
Stephen Anthony 2020-07-25 12:57:12 -02:30
parent 02176d1c7e
commit 155839fb0b
28 changed files with 301 additions and 253 deletions

View File

@ -22,9 +22,14 @@
* Allow taking snapshots from within Time Machine dialog * Allow taking snapshots from within Time Machine dialog
* Added ability to load per-ROM properties file from a ZIP file containing * Added ability to access most files that Stella uses from within a ZIP
the ROM. This allows to distribute ROM and properties in one file, file. This includes the following:
which Stella can use directly. - Per-ROM properties file (so one can distribute a ROM and its
associated properties).
- Debugger symbol (.sym) and list (.lst) files, etc.
- Several others, as we extend the support.
Basically, you are now able to put many files that Stella uses inside
one ZIP file, and distribute just that file.
* Replaced "Re-disassemble" with "Disassemble @ current line" in debugger * Replaced "Re-disassemble" with "Disassemble @ current line" in debugger

View File

@ -213,10 +213,9 @@ void CheatManager::enable(const string& code, bool enable)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::loadCheatDatabase() void CheatManager::loadCheatDatabase()
{ {
const string& cheatfile = myOSystem.cheatFile(); stringstream in;
ifstream in(cheatfile); try { myOSystem.cheatFile().read(in); }
if(!in) catch(...) { return; }
return;
string line, md5, cheat; string line, md5, cheat;
string::size_type one, two, three, four; string::size_type one, two, three, four;
@ -253,13 +252,12 @@ void CheatManager::saveCheatDatabase()
if(!myListIsDirty) if(!myListIsDirty)
return; return;
const string& cheatfile = myOSystem.cheatFile(); stringstream out;
ofstream out(cheatfile);
if(!out)
return;
for(const auto& iter: myCheatMap) for(const auto& iter: myCheatMap)
out << "\"" << iter.first << "\" " << "\"" << iter.second << "\"" << endl; out << "\"" << iter.first << "\" " << "\"" << iter.second << "\"" << endl;
try { myOSystem.cheatFile().write(out); }
catch(...) { return; }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -52,7 +52,7 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface)
throw runtime_error(s); throw runtime_error(s);
}; };
ifstream in(filename, std::ios_base::binary); std::ifstream in(filename, std::ios_base::binary);
if(!in.is_open()) if(!in.is_open())
loadImageERROR("No snapshot found"); loadImageERROR("No snapshot found");
@ -125,7 +125,7 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::saveImage(const string& filename, const VariantList& comments) void PNGLibrary::saveImage(const string& filename, const VariantList& comments)
{ {
ofstream out(filename, std::ios_base::binary); std::ofstream out(filename, std::ios_base::binary);
if(!out.is_open()) if(!out.is_open())
throw runtime_error("ERROR: Couldn't create snapshot file"); throw runtime_error("ERROR: Couldn't create snapshot file");
@ -156,7 +156,7 @@ void PNGLibrary::saveImage(const string& filename, const VariantList& comments)
void PNGLibrary::saveImage(const string& filename, const FBSurface& surface, void PNGLibrary::saveImage(const string& filename, const FBSurface& surface,
const Common::Rect& rect, const VariantList& comments) const Common::Rect& rect, const VariantList& comments)
{ {
ofstream out(filename, std::ios_base::binary); std::ofstream out(filename, std::ios_base::binary);
if(!out.is_open()) if(!out.is_open())
throw runtime_error("ERROR: Couldn't create snapshot file"); throw runtime_error("ERROR: Couldn't create snapshot file");
@ -182,7 +182,7 @@ void PNGLibrary::saveImage(const string& filename, const FBSurface& surface,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::saveImageToDisk(ofstream& out, const vector<png_bytep>& rows, void PNGLibrary::saveImageToDisk(std::ofstream& out, const vector<png_bytep>& rows,
png_uint_32 width, png_uint_32 height, const VariantList& comments) png_uint_32 width, png_uint_32 height, const VariantList& comments)
{ {
png_structp png_ptr = nullptr; png_structp png_ptr = nullptr;
@ -297,7 +297,7 @@ void PNGLibrary::takeSnapshot(uInt32 number)
// Figure out the correct snapshot name // Figure out the correct snapshot name
string filename; string filename;
bool showmessage = number == 0; bool showmessage = number == 0;
string sspath = myOSystem.snapshotSaveDir() + string sspath = myOSystem.snapshotSaveDir().getPath() +
(myOSystem.settings().getString("snapname") != "int" ? (myOSystem.settings().getString("snapname") != "int" ?
myOSystem.romFile().getNameWithExt("") myOSystem.romFile().getNameWithExt("")
: myOSystem.console().properties().get(PropType::Cart_Name)); : myOSystem.console().properties().get(PropType::Cart_Name));
@ -456,21 +456,21 @@ void PNGLibrary::writeComments(png_structp png_ptr, png_infop info_ptr,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_read_data(png_structp ctx, png_bytep area, png_size_t size) void PNGLibrary::png_read_data(png_structp ctx, png_bytep area, png_size_t size)
{ {
(static_cast<ifstream*>(png_get_io_ptr(ctx)))->read( (static_cast<std::ifstream*>(png_get_io_ptr(ctx)))->read(
reinterpret_cast<char *>(area), size); reinterpret_cast<char *>(area), size);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_write_data(png_structp ctx, png_bytep area, png_size_t size) void PNGLibrary::png_write_data(png_structp ctx, png_bytep area, png_size_t size)
{ {
(static_cast<ofstream*>(png_get_io_ptr(ctx)))->write( (static_cast<std::ofstream*>(png_get_io_ptr(ctx)))->write(
reinterpret_cast<const char *>(area), size); reinterpret_cast<const char *>(area), size);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_io_flush(png_structp ctx) void PNGLibrary::png_io_flush(png_structp ctx)
{ {
(static_cast<ofstream*>(png_get_io_ptr(ctx)))->flush(); (static_cast<std::ofstream*>(png_get_io_ptr(ctx)))->flush();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -165,7 +165,7 @@ class PNGLibrary
@param height The height of the PNG image @param height The height of the PNG image
@param comments The text comments to add to the PNG image @param comments The text comments to add to the PNG image
*/ */
void saveImageToDisk(ofstream& out, const vector<png_bytep>& rows, void saveImageToDisk(std::ofstream& out, const vector<png_bytep>& rows,
png_uint_32 width, png_uint_32 height, png_uint_32 width, png_uint_32 height,
const VariantList& comments); const VariantList& comments);

View File

@ -85,11 +85,13 @@ void PaletteHandler::showAdjustableMessage()
{ {
const ConsoleTiming timing = myOSystem.console().timing(); const ConsoleTiming timing = myOSystem.console().timing();
const bool isNTSC = timing == ConsoleTiming::ntsc; const bool isNTSC = timing == ConsoleTiming::ntsc;
const float value = myOSystem.console().timing() == ConsoleTiming::pal ? myPhasePAL : myPhaseNTSC; const float value =
myOSystem.console().timing() == ConsoleTiming::pal ? myPhasePAL : myPhaseNTSC;
buf << std::fixed << std::setprecision(1) << value << DEGREE; buf << std::fixed << std::setprecision(1) << value << DEGREE;
myOSystem.frameBuffer().showMessage("Palette phase shift", buf.str(), value, myOSystem.frameBuffer().showMessage(
(isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT) - MAX_SHIFT, "Palette phase shift", buf.str(), value,
(isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT) + MAX_SHIFT); (isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT) - MAX_SHIFT,
(isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT) + MAX_SHIFT);
} }
else else
{ {
@ -328,29 +330,25 @@ void PaletteHandler::loadUserPalette()
if (!myOSystem.checkUserPalette(true)) if (!myOSystem.checkUserPalette(true))
return; return;
const string& palette = myOSystem.paletteFile(); ByteBuffer in;
ifstream in(palette, std::ios::binary); try { myOSystem.paletteFile().read(in); }
catch(...) { return; }
// Now that we have valid data, create the user-defined palettes uInt8* pixbuf = in.get();
std::array<uInt8, 3> pixbuf; // Temporary buffer for one 24-bit pixel for(int i = 0; i < 128; i++, pixbuf += 3) // NTSC palette
for(int i = 0; i < 128; i++) // NTSC palette
{ {
in.read(reinterpret_cast<char*>(pixbuf.data()), 3);
const uInt32 pixel = (int(pixbuf[0]) << 16) + (int(pixbuf[1]) << 8) + int(pixbuf[2]); const uInt32 pixel = (int(pixbuf[0]) << 16) + (int(pixbuf[1]) << 8) + int(pixbuf[2]);
ourUserNTSCPalette[(i<<1)] = pixel; ourUserNTSCPalette[(i<<1)] = pixel;
} }
for(int i = 0; i < 128; i++) // PAL palette for(int i = 0; i < 128; i++, pixbuf += 3) // PAL palette
{ {
in.read(reinterpret_cast<char*>(pixbuf.data()), 3);
const uInt32 pixel = (int(pixbuf[0]) << 16) + (int(pixbuf[1]) << 8) + int(pixbuf[2]); const uInt32 pixel = (int(pixbuf[0]) << 16) + (int(pixbuf[1]) << 8) + int(pixbuf[2]);
ourUserPALPalette[(i<<1)] = pixel; ourUserPALPalette[(i<<1)] = pixel;
} }
std::array<uInt32, 16> secam; // All 8 24-bit pixels, plus 8 colorloss pixels std::array<uInt32, 16> secam; // All 8 24-bit pixels, plus 8 colorloss pixels
for(int i = 0; i < 8; i++) // SECAM palette for(int i = 0; i < 8; i++, pixbuf += 3) // SECAM palette
{ {
in.read(reinterpret_cast<char*>(pixbuf.data()), 3);
const uInt32 pixel = (int(pixbuf[0]) << 16) + (int(pixbuf[1]) << 8) + int(pixbuf[2]); const uInt32 pixel = (int(pixbuf[0]) << 16) + (int(pixbuf[1]) << 8) + int(pixbuf[2]);
secam[(i<<1)] = pixel; secam[(i<<1)] = pixel;
secam[(i<<1)+1] = 0; secam[(i<<1)+1] = 0;

View File

@ -64,8 +64,6 @@ using std::istream;
using std::ostream; using std::ostream;
using std::fstream; using std::fstream;
using std::iostream; using std::iostream;
using std::ifstream;
using std::ofstream;
using std::ostringstream; using std::ostringstream;
using std::istringstream; using std::istringstream;
using std::stringstream; using std::stringstream;

View File

@ -28,8 +28,8 @@ namespace {
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyValueRepositoryConfigfile::KeyValueRepositoryConfigfile(const string& filename) KeyValueRepositoryConfigfile::KeyValueRepositoryConfigfile(const FilesystemNode& file)
: myFilename(filename) : myFile(file)
{ {
} }
@ -41,10 +41,14 @@ std::map<string, Variant> KeyValueRepositoryConfigfile::load()
string line, key, value; string line, key, value;
string::size_type equalPos, garbage; string::size_type equalPos, garbage;
ifstream in(myFilename); stringstream in;
if(!in || !in.is_open()) { try
Logger::error("ERROR: Couldn't load from settings file " + myFilename); {
myFile.read(in);
}
catch(...)
{
Logger::error("ERROR: Couldn't load from settings file " + myFile.getShortPath());
return values; return values;
} }
@ -79,13 +83,7 @@ std::map<string, Variant> KeyValueRepositoryConfigfile::load()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KeyValueRepositoryConfigfile::save(const std::map<string, Variant>& values) void KeyValueRepositoryConfigfile::save(const std::map<string, Variant>& values)
{ {
ofstream out(myFilename); stringstream out;
if(!out || !out.is_open()) {
Logger::error("ERROR: Couldn't save to settings file " + myFilename);
return;
}
out << "; Stella configuration file" << endl out << "; Stella configuration file" << endl
<< ";" << endl << ";" << endl
<< "; Lines starting with ';' are comments and are ignored." << endl << "; Lines starting with ';' are comments and are ignored." << endl
@ -104,4 +102,13 @@ void KeyValueRepositoryConfigfile::save(const std::map<string, Variant>& values)
// Write out each of the key and value pairs // Write out each of the key and value pairs
for(const auto& pair: values) for(const auto& pair: values)
out << pair.first << " = " << pair.second << endl; out << pair.first << " = " << pair.second << endl;
try
{
myFile.write(out);
}
catch(...)
{
Logger::error("ERROR: Couldn't save to settings file " + myFile.getShortPath());
}
} }

View File

@ -18,13 +18,14 @@
#ifndef KEY_VALUE_REPOSITORY_CONFIGFILE_HXX #ifndef KEY_VALUE_REPOSITORY_CONFIGFILE_HXX
#define KEY_VALUE_REPOSITORY_CONFIGFILE_HXX #define KEY_VALUE_REPOSITORY_CONFIGFILE_HXX
#include "FSNode.hxx"
#include "KeyValueRepository.hxx" #include "KeyValueRepository.hxx"
class KeyValueRepositoryConfigfile : public KeyValueRepository class KeyValueRepositoryConfigfile : public KeyValueRepository
{ {
public: public:
explicit KeyValueRepositoryConfigfile(const string& filename); explicit KeyValueRepositoryConfigfile(const FilesystemNode& file);
std::map<string, Variant> load() override; std::map<string, Variant> load() override;
@ -34,7 +35,7 @@ class KeyValueRepositoryConfigfile : public KeyValueRepository
private: private:
const string& myFilename; FilesystemNode myFile;
}; };
#endif // KEY_VALUE_REPOSITORY_CONFIGFILE_HXX #endif // KEY_VALUE_REPOSITORY_CONFIGFILE_HXX

View File

@ -747,7 +747,7 @@ string CartDebug::loadListFile()
try try
{ {
if(lst.read(in) == 0) if(lst.read(in) == 0)
return DebuggerParser::red("list file '" + lst.getShortPath() + "' not readable"); return DebuggerParser::red("list file '" + lst.getShortPath() + "' not found");
} }
catch(...) catch(...)
{ {
@ -811,7 +811,7 @@ string CartDebug::loadSymbolFile()
try try
{ {
if(sym.read(in) == 0) if(sym.read(in) == 0)
return DebuggerParser::red("symbol file '" + sym.getShortPath() + "' not readable"); return DebuggerParser::red("symbol file '" + sym.getShortPath() + "' not found");
} }
catch(...) catch(...)
{ {
@ -862,15 +862,14 @@ string CartDebug::loadConfigFile()
// on the actual ROM filename // on the actual ROM filename
FilesystemNode romNode(myOSystem.romFile().getPathWithExt(".cfg")); FilesystemNode romNode(myOSystem.romFile().getPathWithExt(".cfg"));
FilesystemNode cfg(myOSystem.cfgDir() + romNode.getName()); FilesystemNode cfg = myOSystem.cfgDir(); cfg /= romNode.getName();
if(!cfg.isReadable()) if(!cfg.isReadable())
return DebuggerParser::red("config file \'" + cfg.getShortPath() + "\' not found"); return DebuggerParser::red("config file \'" + cfg.getShortPath() + "\' not found");
stringstream in; stringstream in;
try try
{ {
if(cfg.read(in) == 0) cfg.read(in);
return "Unable to load directives from " + cfg.getPath();
} }
catch(...) catch(...)
{ {
@ -1002,12 +1001,11 @@ string CartDebug::saveConfigFile()
try try
{ {
FilesystemNode romNode(myOSystem.romFile().getPathWithExt(".cfg")); FilesystemNode romNode(myOSystem.romFile().getPathWithExt(".cfg"));
FilesystemNode cfg(myOSystem.cfgDir() + romNode.getName()); FilesystemNode cfg = myOSystem.cfgDir(); cfg /= romNode.getName();
if(!cfg.getParent().isWritable()) if(!cfg.getParent().isWritable())
return DebuggerParser::red("config file \'" + cfg.getShortPath() + "\' not writable"); return DebuggerParser::red("config file \'" + cfg.getShortPath() + "\' not writable");
size_t size = cfg.write(out); if(cfg.write(out) == 0)
if(size == 0)
return "Unable to save directives to " + cfg.getShortPath(); return "Unable to save directives to " + cfg.getShortPath();
if(myConsole.cartridge().romBankCount() > 1) if(myConsole.cartridge().romBankCount() > 1)
@ -1016,7 +1014,7 @@ string CartDebug::saveConfigFile()
} }
catch(const runtime_error& e) catch(const runtime_error& e)
{ {
retVal << e.what(); retVal << "Unable to save directives: " << e.what();
} }
return retVal.str(); return retVal.str();
} }
@ -1331,24 +1329,21 @@ string CartDebug::saveDisassembly()
// And finally, output the disassembly // And finally, output the disassembly
out << buf.str(); out << buf.str();
const string& propsname =
myConsole.properties().get(PropType::Cart_Name) + ".asm";
FilesystemNode node(myOSystem.defaultSaveDir().getPath() + propsname);
stringstream retVal; stringstream retVal;
try try
{ {
const string& propsname = node.write(out);
myConsole.properties().get(PropType::Cart_Name) + ".asm";
FilesystemNode node(myOSystem.defaultSaveDir() + propsname);
size_t size = node.write(out);
if(size == 0)
return "Unable to save disassembly to " + node.getShortPath();
if(myConsole.cartridge().romBankCount() > 1) if(myConsole.cartridge().romBankCount() > 1)
retVal << DebuggerParser::red("disassembly for multi-bank ROM not fully supported\n"); retVal << DebuggerParser::red("disassembly for multi-bank ROM not fully supported\n");
retVal << "saved " << node.getShortPath() << " OK"; retVal << "saved " << node.getShortPath() << " OK";
} }
catch(const runtime_error& e) catch(...)
{ {
retVal << e.what(); retVal << "Unable to save disassembly to " << node.getShortPath();
} }
return retVal.str(); return retVal.str();
} }
@ -1358,7 +1353,7 @@ string CartDebug::saveRom()
{ {
const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".a26"; const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".a26";
FilesystemNode node(myOSystem.defaultSaveDir() + rom); FilesystemNode node(myOSystem.defaultSaveDir().getPath() + rom);
if(myConsole.cartridge().saveROM(node)) if(myConsole.cartridge().saveROM(node))
return "saved ROM as " + node.getShortPath(); return "saved ROM as " + node.getShortPath();
else else
@ -1376,15 +1371,13 @@ string CartDebug::saveAccessFile()
try try
{ {
const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".csv"; const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".csv";
FilesystemNode node(myOSystem.defaultSaveDir() + rom); FilesystemNode node(myOSystem.defaultSaveDir().getPath() + rom);
size_t size = node.write(out); node.write(out);
if(size > 0) return "saved access counters as " + node.getShortPath();
return "saved access counters as " + node.getShortPath();
} }
catch(const runtime_error& e) catch(...)
{ {
return e.what();
} }
return DebuggerParser::red("failed to save access counters file"); return DebuggerParser::red("failed to save access counters file");
} }

View File

@ -167,7 +167,7 @@ string Debugger::autoExec(StringList* history)
ostringstream buf; ostringstream buf;
// autoexec.script is always run // autoexec.script is always run
FilesystemNode autoexec(myOSystem.baseDir() + "autoexec.script"); FilesystemNode autoexec(myOSystem.baseDir().getPath() + "autoexec.script");
buf << "autoExec():" << endl buf << "autoExec():" << endl
<< myParser->exec(autoexec, history) << endl; << myParser->exec(autoexec, history) << endl;
@ -304,7 +304,7 @@ int Debugger::step(bool save)
myOSystem.console().tia().updateScanlineByStep().flushLineCache(); myOSystem.console().tia().updateScanlineByStep().flushLineCache();
lockSystem(); lockSystem();
if(save) if(save)
addState("step"); addState("step");
return int(mySystem.cycles() - startCycle); return int(mySystem.cycles() - startCycle);
} }

View File

@ -132,9 +132,9 @@ string DebuggerParser::exec(const FilesystemNode& file, StringList* history)
{ {
if(file.exists()) if(file.exists())
{ {
ifstream in(file.getPath()); stringstream in;
if(!in.is_open()) try { file.read(in); }
return red("script file \'" + file.getShortPath() + "\' not found"); catch(...) { return red("script file \'" + file.getShortPath() + "\' not found"); }
ostringstream buf; ostringstream buf;
int count = 0; int count = 0;
@ -633,11 +633,7 @@ string DebuggerParser::saveScriptFile(string file)
if(file.find_last_of('.') == string::npos) if(file.find_last_of('.') == string::npos)
file += ".script"; file += ".script";
FilesystemNode node(debugger.myOSystem.defaultSaveDir() + file); stringstream out;
ofstream out(node.getPath());
if(!out.is_open())
return "Unable to save script to " + node.getShortPath();
Debugger::FunctionDefMap funcs = debugger.getFunctionDefMap(); Debugger::FunctionDefMap funcs = debugger.getFunctionDefMap();
for(const auto& f: funcs) for(const auto& f: funcs)
if (!debugger.isBuiltinFunction(f.first)) if (!debugger.isBuiltinFunction(f.first))
@ -678,6 +674,16 @@ string DebuggerParser::saveScriptFile(string file)
out << endl; out << endl;
} }
FilesystemNode node(debugger.myOSystem.defaultSaveDir().getPath() + file);
try
{
node.write(out);
}
catch(...)
{
return "Unable to save script to " + node.getShortPath();
}
return "saved " + node.getShortPath() + " OK"; return "saved " + node.getShortPath() + " OK";
} }
@ -1140,7 +1146,7 @@ void DebuggerParser::executeDump()
file << ".dump"; file << ".dump";
FilesystemNode node(file.str()); FilesystemNode node(file.str());
// cout << "dump " << args[0] << "-" << args[1] << " to " << file.str() << endl; // cout << "dump " << args[0] << "-" << args[1] << " to " << file.str() << endl;
ofstream ofs(node.getPath(), ofstream::out | ofstream::app); std::ofstream ofs(node.getPath(), std::ofstream::out | std::ofstream::app);
if(!ofs.is_open()) if(!ofs.is_open())
{ {
outputCommandError("Unable to append dump to file " + node.getShortPath(), myCommand); outputCommandError("Unable to append dump to file " + node.getShortPath(), myCommand);
@ -1221,9 +1227,8 @@ void DebuggerParser::executeExec()
if(file.find_last_of('.') == string::npos) if(file.find_last_of('.') == string::npos)
file += ".script"; file += ".script";
FilesystemNode node(file); FilesystemNode node(file);
if (!node.exists()) { if (!node.exists())
node = FilesystemNode(debugger.myOSystem.defaultSaveDir() + file); node = FilesystemNode(debugger.myOSystem.defaultSaveDir().getPath() + file);
}
if (argCount == 2) { if (argCount == 2) {
execPrefix = argStrings[1]; execPrefix = argStrings[1];

View File

@ -22,7 +22,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AtariVox::AtariVox(Jack jack, const Event& event, const System& system, AtariVox::AtariVox(Jack jack, const Event& event, const System& system,
const string& portname, const string& eepromfile, const string& portname, const FilesystemNode& eepromfile,
const onMessageCallback& callback) const onMessageCallback& callback)
: SaveKey(jack, event, system, eepromfile, callback, Controller::Type::AtariVox) : SaveKey(jack, event, system, eepromfile, callback, Controller::Type::AtariVox)
{ {

View File

@ -20,6 +20,7 @@
class OSystem; class OSystem;
class SerialPort; class SerialPort;
class FilesystemNode;
#include "Control.hxx" #include "Control.hxx"
#include "SaveKey.hxx" #include "SaveKey.hxx"
@ -47,7 +48,7 @@ class AtariVox : public SaveKey
@param callback Called to pass messages back to the parent controller @param callback Called to pass messages back to the parent controller
*/ */
AtariVox(Jack jack, const Event& event, const System& system, AtariVox(Jack jack, const Event& event, const System& system,
const string& portname, const string& eepromfile, const string& portname, const FilesystemNode& eepromfile,
const onMessageCallback& callback); const onMessageCallback& callback);
virtual ~AtariVox(); virtual ~AtariVox();

View File

@ -192,7 +192,7 @@ Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
myConsoleInfo.BankSwitch = myCart->about(); myConsoleInfo.BankSwitch = myCart->about();
// Some carts have an associated nvram file // Some carts have an associated nvram file
myCart->setNVRamFile(myOSystem.nvramDir(), myConsoleInfo.CartName); myCart->setNVRamFile(myOSystem.nvramDir().getPath(), myConsoleInfo.CartName);
// Let the other devices know about the new console // Let the other devices know about the new console
mySystem->consoleChanged(myConsoleTiming); mySystem->consoleChanged(myConsoleTiming);
@ -824,7 +824,8 @@ unique_ptr<Controller> Console::getControllerPort(const Controller::Type type,
case Controller::Type::AtariVox: case Controller::Type::AtariVox:
{ {
const string& nvramfile = myOSystem.nvramDir() + "atarivox_eeprom.dat"; FilesystemNode nvramfile = myOSystem.nvramDir();
nvramfile /= "atarivox_eeprom.dat";
Controller::onMessageCallback callback = [&os = myOSystem](const string& msg) { Controller::onMessageCallback callback = [&os = myOSystem](const string& msg) {
bool devSettings = os.settings().getBool("dev.settings"); bool devSettings = os.settings().getBool("dev.settings");
if(os.settings().getBool(devSettings ? "dev.eepromaccess" : "plr.eepromaccess")) if(os.settings().getBool(devSettings ? "dev.eepromaccess" : "plr.eepromaccess"))
@ -836,7 +837,8 @@ unique_ptr<Controller> Console::getControllerPort(const Controller::Type type,
} }
case Controller::Type::SaveKey: case Controller::Type::SaveKey:
{ {
const string& nvramfile = myOSystem.nvramDir() + "savekey_eeprom.dat"; FilesystemNode nvramfile = myOSystem.nvramDir();
nvramfile /= "savekey_eeprom.dat";
Controller::onMessageCallback callback = [&os = myOSystem](const string& msg) { Controller::onMessageCallback callback = [&os = myOSystem](const string& msg) {
bool devSettings = os.settings().getBool("dev.settings"); bool devSettings = os.settings().getBool("dev.settings");
if(os.settings().getBool(devSettings ? "dev.eepromaccess" : "plr.eepromaccess")) if(os.settings().getBool(devSettings ? "dev.eepromaccess" : "plr.eepromaccess"))

View File

@ -334,8 +334,9 @@ void EventHandler::handleSystemEvent(SystemEvent e, int, int)
#endif #endif
#if 0 #if 0
case SystemEvent::WINDOW_MINIMIZED: case SystemEvent::WINDOW_MINIMIZED:
if(myState == EventHandlerState::EMULATION) enterMenuMode(EventHandlerState::OPTIONSMENU); if(myState == EventHandlerState::EMULATION)
break; enterMenuMode(EventHandlerState::OPTIONSMENU);
break;
#endif #endif
default: // handle other events as testing requires default: // handle other events as testing requires
// cerr << "handleSystemEvent: " << e << endl; // cerr << "handleSystemEvent: " << e << endl;
@ -343,7 +344,6 @@ void EventHandler::handleSystemEvent(SystemEvent e, int, int)
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandler::AdjustGroup EventHandler::getAdjustGroup() EventHandler::AdjustGroup EventHandler::getAdjustGroup()
{ {

View File

@ -25,15 +25,49 @@ FilesystemNode::FilesystemNode(const AbstractFSNodePtr& realNode)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNode::FilesystemNode(const string& p) FilesystemNode::FilesystemNode(const string& path)
{ {
setPath(path);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FilesystemNode::setPath(const string& path)
{
// Only create a new object when necessary
if (path == getPath())
return;
// Is this potentially a ZIP archive? // Is this potentially a ZIP archive?
#if defined(ZIP_SUPPORT) #if defined(ZIP_SUPPORT)
if (BSPF::containsIgnoreCase(p, ".zip")) if (BSPF::containsIgnoreCase(path, ".zip"))
_realNode = FilesystemNodeFactory::create(p, FilesystemNodeFactory::Type::ZIP); _realNode = FilesystemNodeFactory::create(path, FilesystemNodeFactory::Type::ZIP);
else else
#endif #endif
_realNode = FilesystemNodeFactory::create(p, FilesystemNodeFactory::Type::SYSTEM); _realNode = FilesystemNodeFactory::create(path, FilesystemNodeFactory::Type::SYSTEM);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNode& FilesystemNode::operator/=(const string& path)
{
// This part could probably be put in a virtual function, but it seems like
// a waste since almost every system uses the same separator, except Windows
#ifdef BSPF_WINDOWS
#define PATH_SEPARATOR '\\'
#else
#define PATH_SEPARATOR '/'
#endif
if (path != EmptyString)
{
string newPath = getPath();
if (newPath != EmptyString && newPath[newPath.length()-1] != PATH_SEPARATOR)
newPath += PATH_SEPARATOR;
newPath += path;
setPath(newPath);
}
return *this;
#undef PATH_SEPARATOR
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -234,7 +268,7 @@ size_t FilesystemNode::read(ByteBuffer& buffer) const
return sizeRead; return sizeRead;
// Otherwise, the default behaviour is to read from a normal C++ ifstream // Otherwise, the default behaviour is to read from a normal C++ ifstream
ifstream in(getPath(), std::ios::binary); std::ifstream in(getPath(), std::ios::binary);
if (in) if (in)
{ {
in.seekg(0, std::ios::end); in.seekg(0, std::ios::end);
@ -268,7 +302,7 @@ size_t FilesystemNode::read(stringstream& buffer) const
// Otherwise, the default behaviour is to read from a normal C++ ifstream // Otherwise, the default behaviour is to read from a normal C++ ifstream
// and convert to a stringstream // and convert to a stringstream
ifstream in(getPath(), std::ios::binary); std::ifstream in(getPath(), std::ios::binary);
if (in) if (in)
{ {
in.seekg(0, std::ios::end); in.seekg(0, std::ios::end);
@ -296,7 +330,7 @@ size_t FilesystemNode::write(const ByteBuffer& buffer, size_t size) const
return sizeWritten; return sizeWritten;
// Otherwise, the default behaviour is to write to a normal C++ ofstream // Otherwise, the default behaviour is to write to a normal C++ ofstream
ofstream out(getPath(), std::ios::binary); std::ofstream out(getPath(), std::ios::binary);
if (out) if (out)
{ {
out.write(reinterpret_cast<const char*>(buffer.get()), size); out.write(reinterpret_cast<const char*>(buffer.get()), size);
@ -321,7 +355,7 @@ size_t FilesystemNode::write(const stringstream& buffer) const
return sizeWritten; return sizeWritten;
// Otherwise, the default behaviour is to write to a normal C++ ofstream // Otherwise, the default behaviour is to write to a normal C++ ofstream
ofstream out(getPath(), std::ios::binary); std::ofstream out(getPath(), std::ios::binary);
if (out) if (out)
{ {
out << buffer.rdbuf(); out << buffer.rdbuf();

View File

@ -84,13 +84,19 @@ class FilesystemNode
/** /**
* Compare the name of this node to the name of another, testing for * Compare the name of this node to the name of another, testing for
* equality, * equality.
*/ */
inline bool operator==(const FilesystemNode& node) const inline bool operator==(const FilesystemNode& node) const
{ {
return BSPF::compareIgnoreCase(getName(), node.getName()) == 0; return BSPF::compareIgnoreCase(getName(), node.getName()) == 0;
} }
/**
* Append the given path to the node, adding a directory separator
* when necessary. Modelled on the C++17 fs::path API.
*/
FilesystemNode& operator/=(const string& path);
/** /**
* By default, the output operator simply outputs the fully-qualified * By default, the output operator simply outputs the fully-qualified
* pathname of the node. * pathname of the node.
@ -269,6 +275,7 @@ class FilesystemNode
private: private:
AbstractFSNodePtr _realNode; AbstractFSNodePtr _realNode;
explicit FilesystemNode(const AbstractFSNodePtr& realNode); explicit FilesystemNode(const AbstractFSNodePtr& realNode);
void setPath(const string& path);
}; };

View File

@ -43,27 +43,23 @@
*/ */
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MT24LC256::MT24LC256(const string& filename, const System& system, MT24LC256::MT24LC256(const FilesystemNode& eepromfile, const System& system,
const Controller::onMessageCallback& callback) const Controller::onMessageCallback& callback)
: mySystem(system), : mySystem(system),
myCallback(callback), myCallback(callback),
myDataFile(filename) myDataFile(eepromfile)
{ {
// Load the data from an external file (if it exists) // Load the data from an external file (if it exists)
ifstream in(myDataFile, std::ios_base::binary); try
if(in.is_open())
{ {
// Get length of file; it must be 32768 // Get length of file; it must be 32768
in.seekg(0, std::ios::end); if(myDataFile.read(myData) == FLASH_SIZE)
if(uInt32(in.tellg()) == FLASH_SIZE)
{
in.seekg(0, std::ios::beg);
in.read(reinterpret_cast<char*>(myData.data()), myData.size());
myDataFileExists = true; myDataFileExists = true;
}
} }
else catch(...)
{
myDataFileExists = false; myDataFileExists = false;
}
// Then initialize the I2C state // Then initialize the I2C state
jpee_init(); jpee_init();
@ -77,9 +73,8 @@ MT24LC256::~MT24LC256()
// Save EEPROM data to external file only when necessary // Save EEPROM data to external file only when necessary
if(!myDataFileExists || myDataChanged) if(!myDataFileExists || myDataChanged)
{ {
ofstream out(myDataFile, std::ios_base::binary); try { myDataFile.write(myData, FLASH_SIZE); }
if(out.is_open()) catch(...) { }
out.write(reinterpret_cast<char*>(myData.data()), myData.size());
} }
} }
@ -139,7 +134,7 @@ void MT24LC256::eraseAll()
// Work around a bug in XCode 11.2 with -O0 and -O1 // Work around a bug in XCode 11.2 with -O0 and -O1
const uInt8 initialValue = INITIAL_VALUE; const uInt8 initialValue = INITIAL_VALUE;
myData.fill(initialValue); std::fill_n(myData.get(), FLASH_SIZE, initialValue);
myDataChanged = true; myDataChanged = true;
} }
@ -153,7 +148,7 @@ void MT24LC256::eraseCurrent()
{ {
if(myPageHit[page]) if(myPageHit[page])
{ {
std::fill_n(myData.begin() + page * PAGE_SIZE, PAGE_SIZE, initialValue); std::fill_n(myData.get() + page * PAGE_SIZE, PAGE_SIZE, initialValue);
myDataChanged = true; myDataChanged = true;
} }
} }
@ -182,7 +177,7 @@ void MT24LC256::jpee_init()
jpee_smallmode = 0; jpee_smallmode = 0;
jpee_logmode = -1; jpee_logmode = -1;
if(!myDataFileExists) if(!myDataFileExists)
myData.fill(initialValue); std::fill_n(myData.get(), FLASH_SIZE, initialValue);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -21,6 +21,7 @@
class System; class System;
#include "Control.hxx" #include "Control.hxx"
#include "FSNode.hxx"
#include "bspf.hxx" #include "bspf.hxx"
/** /**
@ -36,11 +37,11 @@ class MT24LC256
/** /**
Create a new 24LC256 with its data stored in the given file Create a new 24LC256 with its data stored in the given file
@param filename Data file containing the EEPROM data @param eepromfile Data file containing the EEPROM data
@param system The system using the controller of this device @param system The system using the controller of this device
@param callback Called to pass messages back to the parent controller @param callback Called to pass messages back to the parent controller
*/ */
MT24LC256(const string& filename, const System& system, MT24LC256(const FilesystemNode& eepromfile, const System& system,
const Controller::onMessageCallback& callback); const Controller::onMessageCallback& callback);
~MT24LC256(); ~MT24LC256();
@ -92,7 +93,7 @@ class MT24LC256
Controller::onMessageCallback myCallback; Controller::onMessageCallback myCallback;
// The EEPROM data // The EEPROM data
std::array<uInt8, FLASH_SIZE> myData; ByteBuffer myData;
// Track which pages are used // Track which pages are used
std::array<bool, PAGE_NUM> myPageHit; std::array<bool, PAGE_NUM> myPageHit;
@ -110,7 +111,7 @@ class MT24LC256
uInt64 myCyclesWhenSDASet{0}, myCyclesWhenSCLSet{0}; uInt64 myCyclesWhenSDASet{0}, myCyclesWhenSCLSet{0};
// The file containing the EEPROM data // The file containing the EEPROM data
string myDataFile; FilesystemNode myDataFile;
// Indicates if a valid EEPROM data file exists/was successfully loaded // Indicates if a valid EEPROM data file exists/was successfully loaded
bool myDataFileExists{false}; bool myDataFileExists{false};

View File

@ -121,22 +121,22 @@ bool OSystem::create()
<< " Features: " << myFeatures << endl << " Features: " << myFeatures << endl
<< " " << myBuildInfo << endl << endl << " " << myBuildInfo << endl << endl
<< "Base directory: '" << "Base directory: '"
<< FilesystemNode(myBaseDir).getShortPath() << "'" << endl << myBaseDir.getShortPath() << "'" << endl
<< "State directory: '" << "State directory: '"
<< FilesystemNode(myStateDir).getShortPath() << "'" << endl << myStateDir.getShortPath() << "'" << endl
<< "NVRam directory: '" << "NVRam directory: '"
<< FilesystemNode(myNVRamDir).getShortPath() << "'" << endl; << myNVRamDir.getShortPath() << "'" << endl;
if(!myConfigFile.empty()) if(myConfigFile.getPath() != EmptyString)
buf << "Configuration file: '" buf << "Configuration file: '"
<< FilesystemNode(myConfigFile).getShortPath() << "'" << endl; << myConfigFile.getShortPath() << "'" << endl;
buf << "Game properties: '" buf << "Game properties: '"
<< myPropertiesFile.getShortPath() << "'" << endl << myPropertiesFile.getShortPath() << "'" << endl
<< "Cheat file: '" << "Cheat file: '"
<< FilesystemNode(myCheatFile).getShortPath() << "'" << endl << myCheatFile.getShortPath() << "'" << endl
<< "Palette file: '" << "Palette file: '"
<< FilesystemNode(myPaletteFile).getShortPath() << "'" << endl; << myPaletteFile.getShortPath() << "'" << endl;
Logger::info(buf.str()); Logger::info(buf.str());
// NOTE: The framebuffer MUST be created before any other object!!! // NOTE: The framebuffer MUST be created before any other object!!!
@ -193,30 +193,27 @@ void OSystem::loadConfig(const Settings::Options& options)
{ {
// Get base directory and config file from derived class // Get base directory and config file from derived class
// It will decide whether it can override its default location // It will decide whether it can override its default location
getBaseDirAndConfig(myBaseDir, myConfigFile, string baseDir, cfgFile, defSaveDir, defLoadDir;
myDefaultSaveDir, myDefaultLoadDir, getBaseDirAndConfig(baseDir, cfgFile, defSaveDir, defLoadDir,
ourOverrideBaseDirWithApp, ourOverrideBaseDir); ourOverrideBaseDirWithApp, ourOverrideBaseDir);
// Get fully-qualified pathnames, and make directories when needed // Get fully-qualified pathnames, and make directories when needed
FilesystemNode node(myBaseDir); myBaseDir = FilesystemNode(baseDir);
if(!node.isDirectory()) if(!myBaseDir.isDirectory())
node.makeDir(); myBaseDir.makeDir();
myBaseDir = node.getPath(); if(!cfgFile.empty())
if(!myConfigFile.empty()) myConfigFile = FilesystemNode(cfgFile);
myConfigFile = FilesystemNode(myConfigFile).getPath();
FilesystemNode save(myDefaultSaveDir); myDefaultSaveDir = FilesystemNode(defSaveDir);
if(!save.isDirectory()) if(!myDefaultSaveDir.isDirectory())
save.makeDir(); myDefaultSaveDir.makeDir();
myDefaultSaveDir = save.getShortPath();
FilesystemNode load(myDefaultLoadDir); myDefaultLoadDir = FilesystemNode(defLoadDir);
if(!load.isDirectory()) if(!myDefaultLoadDir.isDirectory())
load.makeDir(); myDefaultLoadDir.makeDir();
myDefaultLoadDir = load.getShortPath();
#ifdef SQLITE_SUPPORT #ifdef SQLITE_SUPPORT
mySettingsDb = make_shared<SettingsDb>(myBaseDir, "settings"); mySettingsDb = make_shared<SettingsDb>(myBaseDir.getPath(), "settings");
if(!mySettingsDb->initialize()) if(!mySettingsDb->initialize())
mySettingsDb.reset(); mySettingsDb.reset();
#endif #endif
@ -259,38 +256,48 @@ void OSystem::saveConfig()
void OSystem::setConfigPaths() void OSystem::setConfigPaths()
{ {
// Make sure all required directories actually exist // Make sure all required directories actually exist
auto buildDirIfRequired = [](string& path, const string& pathToBuild) auto buildDirIfRequired = [](FilesystemNode& path,
const FilesystemNode& initialPath,
const string& pathToAppend = EmptyString)
{ {
FilesystemNode node(pathToBuild); path = initialPath;
if(!node.isDirectory()) if(pathToAppend != EmptyString)
node.makeDir(); path /= pathToAppend;
if(!path.isDirectory())
path = node.getPath(); path.makeDir();
}; };
buildDirIfRequired(myStateDir, myBaseDir + "state"); buildDirIfRequired(myStateDir, myBaseDir, "state");
buildDirIfRequired(myNVRamDir, myBaseDir + "nvram"); buildDirIfRequired(myNVRamDir, myBaseDir, "nvram");
#ifdef DEBUGGER_SUPPORT #ifdef DEBUGGER_SUPPORT
buildDirIfRequired(myCfgDir, myBaseDir + "cfg"); buildDirIfRequired(myCfgDir, myBaseDir, "cfg");
#endif #endif
#ifdef PNG_SUPPORT #ifdef PNG_SUPPORT
mySnapshotSaveDir = mySettings->getString("snapsavedir"); const string& ssSaveDir = mySettings->getString("snapsavedir");
if(mySnapshotSaveDir == "") mySnapshotSaveDir = defaultSaveDir(); if(ssSaveDir == EmptyString)
buildDirIfRequired(mySnapshotSaveDir, mySnapshotSaveDir); mySnapshotSaveDir = defaultSaveDir();
else
mySnapshotSaveDir = FilesystemNode(ssSaveDir);
if(!mySnapshotSaveDir.isDirectory())
mySnapshotSaveDir.makeDir();
mySnapshotLoadDir = mySettings->getString("snaploaddir"); const string& ssLoadDir = mySettings->getString("snaploaddir");
if(mySnapshotLoadDir == "") mySnapshotLoadDir = defaultLoadDir(); if(ssLoadDir == EmptyString)
buildDirIfRequired(mySnapshotLoadDir, mySnapshotLoadDir); mySnapshotLoadDir = defaultLoadDir();
else
mySnapshotLoadDir = FilesystemNode(ssLoadDir);
if(!mySnapshotLoadDir.isDirectory())
mySnapshotLoadDir.makeDir();
#endif #endif
myCheatFile = FilesystemNode(myBaseDir + "stella.cht").getPath(); myCheatFile = myBaseDir; myCheatFile /= "stella.cht";
myPaletteFile = FilesystemNode(myBaseDir + "stella.pal").getPath(); myPaletteFile = myBaseDir; myPaletteFile /= "stella.pal";
myPropertiesFile = FilesystemNode(myBaseDir + "stella.pro"); myPropertiesFile = myBaseDir; myPropertiesFile /= "stella.pro";
#if 0 #if 0
// Debug code // Debug code
auto dbgPath = [](const string& desc, const string& location) auto dbgPath = [](const string& desc, const FilesystemNode& location)
{ {
cerr << desc << ": " << location << endl; cerr << desc << ": " << location << endl;
}; };
@ -310,21 +317,24 @@ void OSystem::setConfigPaths()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool OSystem::checkUserPalette(bool outputError) const bool OSystem::checkUserPalette(bool outputError) const
{ {
const string& palette = paletteFile(); try
ifstream in(palette, std::ios::binary); {
if (!in) ByteBuffer palette;
return false; size_t size = paletteFile().read(palette);
// Make sure the contains enough data for the NTSC, PAL and SECAM palettes // Make sure the contains enough data for the NTSC, PAL and SECAM palettes
// This means 128 colours each for NTSC and PAL, at 3 bytes per pixel // This means 128 colours each for NTSC and PAL, at 3 bytes per pixel
// and 8 colours for SECAM at 3 bytes per pixel // and 8 colours for SECAM at 3 bytes per pixel
in.seekg(0, std::ios::end); if(size != 128 * 3 * 2 + 8 * 3)
std::streampos length = in.tellg(); {
in.seekg(0, std::ios::beg); if(outputError)
if (length < 128 * 3 * 2 + 8 * 3) cerr << "ERROR: invalid palette file " << paletteFile() << endl;
return false;
}
}
catch(...)
{ {
if (outputError)
cerr << "ERROR: invalid palette file " << palette << endl;
return false; return false;
} }
return true; return true;
@ -457,8 +467,11 @@ string OSystem::createConsole(const FilesystemNode& rom, const string& md5sum,
myConsole->cartridge().detectedType() + ", loading ROM" + id); myConsole->cartridge().detectedType() + ", loading ROM" + id);
} }
buf << "Game console created:" << endl buf << "Game console created:" << endl
<< " ROM file: " << myRomFile.getShortPath() << endl << endl << " ROM file: " << myRomFile.getShortPath() << endl;
<< getROMInfo(*myConsole); FilesystemNode propsFile(myRomFile.getPathWithExt(".pro"));
if(propsFile.exists())
buf << " PRO file: " << propsFile.getShortPath() << endl;
buf << endl << getROMInfo(*myConsole);
Logger::info(buf.str()); Logger::info(buf.str());
myFrameBuffer->setCursorState(); myFrameBuffer->setCursorState();
@ -821,7 +834,7 @@ shared_ptr<KeyValueRepository> OSystem::createSettingsRepository()
? shared_ptr<KeyValueRepository>(mySettingsDb, &mySettingsDb->settingsRepository()) ? shared_ptr<KeyValueRepository>(mySettingsDb, &mySettingsDb->settingsRepository())
: make_shared<KeyValueRepositoryNoop>(); : make_shared<KeyValueRepositoryNoop>();
#else #else
if (myConfigFile.empty()) if (myConfigFile.getPath() == EmptyString)
return make_shared<KeyValueRepositoryNoop>(); return make_shared<KeyValueRepositoryNoop>();
return make_shared<KeyValueRepositoryConfigfile>(myConfigFile); return make_shared<KeyValueRepositoryConfigfile>(myConfigFile);

View File

@ -246,53 +246,48 @@ class OSystem
void setConfigPaths(); void setConfigPaths();
/** /**
Return the default full/complete directory name for storing data. Return the default full/complete path name for storing data.
*/ */
const string& baseDir() const { return myBaseDir; } const FilesystemNode& baseDir() const { return myBaseDir; }
/** /**
Return the full/complete directory name for storing state files. Return the full/complete path name for storing state files.
*/ */
const string& stateDir() const { return myStateDir; } const FilesystemNode& stateDir() const { return myStateDir; }
/** /**
Return the full/complete directory name for storing nvram Return the full/complete path name for storing nvram
(flash/EEPROM) files. (flash/EEPROM) files.
*/ */
const string& nvramDir() const { return myNVRamDir; } const FilesystemNode& nvramDir() const { return myNVRamDir; }
#ifdef CHEATCODE_SUPPORT #ifdef CHEATCODE_SUPPORT
/** /**
This method should be called to get the full path of the cheat file. Return the full/complete path name of the cheat file.
@return String representing the full path of the cheat filename.
*/ */
const string& cheatFile() const { return myCheatFile; } const FilesystemNode& cheatFile() const { return myCheatFile; }
#endif #endif
#ifdef DEBUGGER_SUPPORT #ifdef DEBUGGER_SUPPORT
/** /**
Return the full/complete directory name for storing Distella cfg files. Return the full/complete path name for storing Distella cfg files.
*/ */
const string& cfgDir() const { return myCfgDir; } const FilesystemNode& cfgDir() const { return myCfgDir; }
#endif #endif
#ifdef PNG_SUPPORT #ifdef PNG_SUPPORT
/** /**
Return the full/complete directory name for saving and loading Return the full/complete path name for saving and loading
PNG snapshots. PNG snapshots.
*/ */
const string& snapshotSaveDir() const { return mySnapshotSaveDir; } const FilesystemNode& snapshotSaveDir() const { return mySnapshotSaveDir; }
const string& snapshotLoadDir() const { return mySnapshotLoadDir; } const FilesystemNode& snapshotLoadDir() const { return mySnapshotLoadDir; }
#endif #endif
/** /**
This method should be called to get the full path of the Return the full/complete path name of the (optional) palette file.
(optional) palette file.
@return String representing the full path of the properties filename.
*/ */
const string& paletteFile() const { return myPaletteFile; } const FilesystemNode& paletteFile() const { return myPaletteFile; }
/** /**
Checks if a valid a user-defined palette file exists. Checks if a valid a user-defined palette file exists.
@ -300,10 +295,7 @@ class OSystem
bool checkUserPalette(bool outputError = false) const; bool checkUserPalette(bool outputError = false) const;
/** /**
This method should be called to get the full path of the currently Return the full/complete path name of the currently loaded ROM.
loaded ROM.
@return FSNode object representing the ROM file.
*/ */
const FilesystemNode& romFile() const { return myRomFile; } const FilesystemNode& romFile() const { return myRomFile; }
@ -311,8 +303,8 @@ class OSystem
The default locations for saving and loading various files that The default locations for saving and loading various files that
don't already have a specific location. don't already have a specific location.
*/ */
const string& defaultSaveDir() const { return myDefaultSaveDir; } const FilesystemNode& defaultSaveDir() const { return myDefaultSaveDir; }
const string& defaultLoadDir() const { return myDefaultLoadDir; } const FilesystemNode& defaultLoadDir() const { return myDefaultLoadDir; }
/** /**
Open the given ROM and return an array containing its contents. Open the given ROM and return an array containing its contents.
@ -533,22 +525,10 @@ class OSystem
bool myQuitLoop{false}; bool myQuitLoop{false};
private: private:
string myBaseDir; FilesystemNode myBaseDir, myStateDir, mySnapshotSaveDir, mySnapshotLoadDir,
string myStateDir; myNVRamDir, myCfgDir, myDefaultSaveDir, myDefaultLoadDir;
string mySnapshotSaveDir; FilesystemNode myCheatFile, myConfigFile, myPaletteFile, myPropertiesFile;
string mySnapshotLoadDir; FilesystemNode myRomFile; string myRomMD5;
string myNVRamDir;
string myCfgDir;
string myDefaultSaveDir;
string myDefaultLoadDir;
string myCheatFile;
string myConfigFile;
string myPaletteFile;
FilesystemNode myPropertiesFile;
FilesystemNode myRomFile;
string myRomMD5;
string myFeatures; string myFeatures;
string myBuildInfo; string myBuildInfo;

View File

@ -22,7 +22,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SaveKey::SaveKey(Jack jack, const Event& event, const System& system, SaveKey::SaveKey(Jack jack, const Event& event, const System& system,
const string& eepromfile, const onMessageCallback& callback, const FilesystemNode& eepromfile, const onMessageCallback& callback,
Type type) Type type)
: Controller(jack, event, system, type), : Controller(jack, event, system, type),
myEEPROM(make_unique<MT24LC256>(eepromfile, system, callback)) myEEPROM(make_unique<MT24LC256>(eepromfile, system, callback))
@ -33,7 +33,7 @@ SaveKey::SaveKey(Jack jack, const Event& event, const System& system,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SaveKey::SaveKey(Jack jack, const Event& event, const System& system, SaveKey::SaveKey(Jack jack, const Event& event, const System& system,
const string& eepromfile, const onMessageCallback& callback) const FilesystemNode& eepromfile, const onMessageCallback& callback)
: SaveKey(jack, event, system, eepromfile, callback, Controller::Type::SaveKey) : SaveKey(jack, event, system, eepromfile, callback, Controller::Type::SaveKey)
{ {
} }

View File

@ -20,6 +20,7 @@
class MT24LC256; class MT24LC256;
class OSystem; class OSystem;
class FilesystemNode;
#include "Control.hxx" #include "Control.hxx"
@ -45,7 +46,7 @@ class SaveKey : public Controller
@param callback Called to pass messages back to the parent controller @param callback Called to pass messages back to the parent controller
*/ */
SaveKey(Jack jack, const Event& event, const System& system, SaveKey(Jack jack, const Event& event, const System& system,
const string& eepromfile, const onMessageCallback& callback); const FilesystemNode& eepromfile, const onMessageCallback& callback);
virtual ~SaveKey(); virtual ~SaveKey();
protected: protected:
@ -54,7 +55,8 @@ class SaveKey : public Controller
that inherit from SaveKey (currently, AtariVox) that inherit from SaveKey (currently, AtariVox)
*/ */
SaveKey(Jack jack, const Event& event, const System& system, SaveKey(Jack jack, const Event& event, const System& system,
const string& eepromfile, const onMessageCallback& callback, Type type); const FilesystemNode& eepromfile,
const onMessageCallback& callback, Type type);
public: public:
using Controller::read; using Controller::read;

View File

@ -809,17 +809,22 @@ void GameInfoDialog::eraseEEPROM()
void GameInfoDialog::saveCurrentPropertiesToDisk() void GameInfoDialog::saveCurrentPropertiesToDisk()
{ {
saveProperties(); saveProperties();
stringstream out;
out << myGameProperties;
FilesystemNode propfile(instance().defaultSaveDir() + myGameFile.getNameWithExt(".pro")); try
ofstream out(propfile.getPath());
if(out)
{ {
out << myGameProperties; FilesystemNode propfile = instance().defaultSaveDir();
propfile /= myGameFile.getNameWithExt(".pro");
propfile.write(out);
instance().frameBuffer().showMessage("Properties saved to " + instance().frameBuffer().showMessage("Properties saved to " +
propfile.getShortPath()); propfile.getShortPath());
} }
else catch(...)
{
instance().frameBuffer().showMessage("Error saving properties"); instance().frameBuffer().showMessage("Error saving properties");
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -116,15 +116,18 @@ void LoggerDialog::saveConfig()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LoggerDialog::saveLogFile() void LoggerDialog::saveLogFile()
{ {
ostringstream path; FilesystemNode node = instance().defaultSaveDir();
path << instance().defaultSaveDir() << "stella.log"; node /= "stella.log";
FilesystemNode node(path.str());
ofstream out(node.getPath()); try
if(out.is_open())
{ {
stringstream out;
out << Logger::instance().logMessages(); out << Logger::instance().logMessages();
instance().frameBuffer().showMessage("Saving log file to " + path.str()); instance().frameBuffer().showMessage("Saving log file to " + node.getShortPath());
}
catch(...)
{
instance().frameBuffer().showMessage("Error savin log file to " + node.getShortPath());
} }
} }

View File

@ -104,7 +104,7 @@ void RomInfoWidget::parseProperties(const FilesystemNode& node)
#ifdef PNG_SUPPORT #ifdef PNG_SUPPORT
// Get a valid filename representing a snapshot file for this rom // Get a valid filename representing a snapshot file for this rom
const string& filename = instance().snapshotLoadDir() + const string& filename = instance().snapshotLoadDir().getPath() +
myProperties.get(PropType::Cart_Name) + ".png"; myProperties.get(PropType::Cart_Name) + ".png";
// Read the PNG file // Read the PNG file

View File

@ -131,7 +131,7 @@ void SnapshotDialog::saveConfig()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SnapshotDialog::setDefaults() void SnapshotDialog::setDefaults()
{ {
mySnapSavePath->setText(instance().defaultSaveDir()); mySnapSavePath->setText(instance().defaultSaveDir().getShortPath());
mySnapInterval->setValue(2); mySnapInterval->setValue(2);
mySnapName->setState(false); mySnapName->setState(false);
mySnapSingle->setState(false); mySnapSingle->setState(false);

View File

@ -507,7 +507,7 @@ void UIDialog::setDefaults()
myLauncherHeightSlider->setValue(h); myLauncherHeightSlider->setValue(h);
myLauncherFontPopup->setSelected("medium", ""); myLauncherFontPopup->setSelected("medium", "");
myRomViewerSize->setValue(35); myRomViewerSize->setValue(35);
mySnapLoadPath->setText(instance().defaultLoadDir()); mySnapLoadPath->setText(instance().defaultLoadDir().getShortPath());
myLauncherExitWidget->setState(false); myLauncherExitWidget->setState(false);
break; break;
} }