diff --git a/src/common/module.mk b/src/common/module.mk index fa36dda88..b7a425d98 100644 --- a/src/common/module.mk +++ b/src/common/module.mk @@ -32,6 +32,7 @@ MODULE_OBJS := \ src/common/sdl_blitter/BilinearBlitter.o \ src/common/sdl_blitter/QisBlitter.o \ src/common/sdl_blitter/BlitterFactory.o \ + src/common/repository/KeyValueRepositoryPropertyFile.o \ MODULE_DIRS += \ src/common diff --git a/src/common/repository/KeyValueRepository.hxx b/src/common/repository/KeyValueRepository.hxx index b90f0fe73..885c6e185 100644 --- a/src/common/repository/KeyValueRepository.hxx +++ b/src/common/repository/KeyValueRepository.hxx @@ -31,9 +31,9 @@ class KeyValueRepository virtual std::map load() = 0; - virtual void save(const std::map& values) = 0; + virtual bool save(const std::map& values) = 0; - virtual void save(const string& key, const Variant& value) = 0; + virtual bool save(const string& key, const Variant& value) = 0; virtual void remove(const string& key) = 0; }; diff --git a/src/common/repository/KeyValueRepositoryConfigfile.cxx b/src/common/repository/KeyValueRepositoryConfigfile.cxx index 1e325a08c..4b8cfbeb5 100644 --- a/src/common/repository/KeyValueRepositoryConfigfile.cxx +++ b/src/common/repository/KeyValueRepositoryConfigfile.cxx @@ -72,7 +72,7 @@ std::map KeyValueRepositoryConfigfile::load() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void KeyValueRepositoryConfigfile::save(const std::map& values) +bool KeyValueRepositoryConfigfile::save(const std::map& values) { stringstream out; out << "; Stella configuration file" << endl @@ -97,9 +97,13 @@ void KeyValueRepositoryConfigfile::save(const std::map& values) try { myFile.write(out); + + return true; } catch(...) { Logger::error("ERROR: Couldn't save to settings file " + myFile.getShortPath()); + + return false; } } diff --git a/src/common/repository/KeyValueRepositoryConfigfile.hxx b/src/common/repository/KeyValueRepositoryConfigfile.hxx index da6d3d4fd..248b7db1a 100644 --- a/src/common/repository/KeyValueRepositoryConfigfile.hxx +++ b/src/common/repository/KeyValueRepositoryConfigfile.hxx @@ -29,9 +29,9 @@ class KeyValueRepositoryConfigfile : public KeyValueRepository std::map load() override; - void save(const std::map& values) override; + bool save(const std::map& values) override; - void save(const string& key, const Variant& value) override {} + bool save(const string& key, const Variant& value) override { return false; } void remove(const string& key) override {} diff --git a/src/common/repository/KeyValueRepositoryNoop.hxx b/src/common/repository/KeyValueRepositoryNoop.hxx index 99156d7d6..6025556bb 100644 --- a/src/common/repository/KeyValueRepositoryNoop.hxx +++ b/src/common/repository/KeyValueRepositoryNoop.hxx @@ -28,9 +28,9 @@ class KeyValueRepositoryNoop : public KeyValueRepository return std::map(); } - void save(const std::map& values) override {} + bool save(const std::map& values) override { return false; } - void save(const string& key, const Variant& value) override {} + bool save(const string& key, const Variant& value) override { return false; } void remove(const string& key) override {} }; diff --git a/src/common/repository/KeyValueRepositoryPropertyFile.cxx b/src/common/repository/KeyValueRepositoryPropertyFile.cxx new file mode 100644 index 000000000..deddf6441 --- /dev/null +++ b/src/common/repository/KeyValueRepositoryPropertyFile.cxx @@ -0,0 +1,163 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#include +#include +#include +#include + +#include "repository/KeyValueRepositoryPropertyFile.hxx" +#include "Logger.hxx" + +namespace { + + string readQuotedString(istream& in) + { + // Read characters until we see a quote + char c; + while(in.get(c)) + if(c == '"') + break; + + // Read characters until we see the close quote + string s; + while(in.get(c)) + { + if((c == '\\') && (in.peek() == '"')) + in.get(c); + else if((c == '\\') && (in.peek() == '\\')) + in.get(c); + else if(c == '"') + break; + else if(c == '\r') + continue; + + s += c; + } + + return s; + } + + void writeQuotedString(ostream& out, const string& s) + { + out.put('"'); + for(uInt32 i = 0; i < s.length(); ++i) + { + if(s[i] == '\\') + { + out.put('\\'); + out.put('\\'); + } + else if(s[i] == '\"') + { + out.put('\\'); + out.put('"'); + } + else + out.put(s[i]); + } + out.put('"'); + } + +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +KeyValueRepositoryPropertyFile::KeyValueRepositoryPropertyFile(const FilesystemNode& node) + : myNode(node) +{} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +std::map KeyValueRepositoryPropertyFile::load() +{ + std::map map; + + if (!myNode.exists()) return map; + + stringstream in; + + try { + myNode.read(in); + } + catch (const runtime_error& err) { + Logger::error(err.what()); + + return map; + } + catch (...) { + return map; + } + + // Loop reading properties + string key, value; + for(;;) + { + // Get the key associated with this property + key = readQuotedString(in); + + // Make sure the stream is still okay + if(!in) return map; + + // A null key signifies the end of the property list + if(key == "") + break; + + // Get the value associated with this property + value = readQuotedString(in); + + // Make sure the stream is still okay + if(!in) + return map; + + map[key] = value; + } + + return map; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool KeyValueRepositoryPropertyFile::save(const std::map& values) +{ + if (values.size() == 0) return true; + + stringstream out; + + for (auto& [key, value]: values) { + writeQuotedString(out, key); + out.put(' '); + writeQuotedString(out, value.toString()); + out.put('\n'); + } + + writeQuotedString(out, ""); + out.put('\n'); + out.put('\n'); + + try { + myNode.write(out); + + return true; + } + catch (const runtime_error& err) { + Logger::error(err.what()); + + return false; + } + catch (...) + { + return false; + } +} diff --git a/src/common/repository/KeyValueRepositoryPropertyFile.hxx b/src/common/repository/KeyValueRepositoryPropertyFile.hxx new file mode 100644 index 000000000..bf8405f05 --- /dev/null +++ b/src/common/repository/KeyValueRepositoryPropertyFile.hxx @@ -0,0 +1,42 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef KEY_VALUE_REPOSITORY_PROPERTY_FILE_HXX +#define KEY_VALUE_REPOSITORY_PROPERTY_FILE_HXX + +#include "repository/KeyValueRepository.hxx" +#include "FSNode.hxx" +#include "bspf.hxx" + +class KeyValueRepositoryPropertyFile : public KeyValueRepository { + public: + KeyValueRepositoryPropertyFile(const FilesystemNode& node); + + std::map load() override; + + bool save(const std::map& values) override; + + bool save(const string& key, const Variant& value) override { return false; } + + void remove(const string& key) override {} + + private: + + const FilesystemNode& myNode; +}; + +#endif diff --git a/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.cxx b/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.cxx index 5a97716aa..abdb8755e 100644 --- a/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.cxx +++ b/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.cxx @@ -34,14 +34,14 @@ std::map AbstractKeyValueRepositorySqlite::load() stmt.reset(); } catch (const SqliteError& err) { - Logger::info(err.what()); + Logger::error(err.what()); } return values; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void AbstractKeyValueRepositorySqlite::save(const std::map& values) +bool AbstractKeyValueRepositorySqlite::save(const std::map& values) { try { SqliteTransaction tx{database()}; @@ -54,23 +54,31 @@ void AbstractKeyValueRepositorySqlite::save(const std::map& val } tx.commit(); + + return true; } catch (const SqliteError& err) { - Logger::info(err.what()); + Logger::error(err.what()); + + return false; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void AbstractKeyValueRepositorySqlite::save(const string& key, const Variant& value) +bool AbstractKeyValueRepositorySqlite::save(const string& key, const Variant& value) { try { SqliteStatement& stmt{stmtInsert(key, value.toString())}; stmt.step(); stmt.reset(); + + return true; } catch (const SqliteError& err) { - Logger::info(err.what()); + Logger::error(err.what()); + + return false; } } @@ -84,6 +92,6 @@ void AbstractKeyValueRepositorySqlite::remove(const string& key) stmt.reset(); } catch (const SqliteError& err) { - Logger::info(err.what()); + Logger::error(err.what()); } } diff --git a/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.hxx b/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.hxx index 2597bd6e8..c3beb5179 100644 --- a/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.hxx +++ b/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.hxx @@ -12,9 +12,9 @@ class AbstractKeyValueRepositorySqlite : public KeyValueRepository std::map load() override; - void save(const std::map& values) override; + bool save(const std::map& values) override; - void save(const string& key, const Variant& value) override; + bool save(const string& key, const Variant& value) override; void remove(const string& key) override; diff --git a/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.cxx b/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.cxx index 66451bc8e..d500fb26e 100644 --- a/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.cxx +++ b/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.cxx @@ -47,7 +47,7 @@ bool CompositeKeyValueRepositorySqlite::has(const string& key) return rowCount > 0; } catch (const SqliteError& err) { - Logger::info(err.what()); + Logger::error(err.what()); return false; } @@ -64,7 +64,7 @@ void CompositeKeyValueRepositorySqlite::remove(const string& key) .step(); } catch (const SqliteError& err) { - Logger::info(err.what()); + Logger::error(err.what()); } } diff --git a/src/common/repository/sqlite/SettingsDb.cxx b/src/common/repository/sqlite/SettingsDb.cxx index c71fdb35c..788bceeb3 100644 --- a/src/common/repository/sqlite/SettingsDb.cxx +++ b/src/common/repository/sqlite/SettingsDb.cxx @@ -40,7 +40,7 @@ bool SettingsDb::initialize() myPropertyRepository->initialize(); } catch (const SqliteError& err) { - Logger::info("sqlite DB " + databaseFileName() + " failed to initialize: " + err.what()); + Logger::error("sqlite DB " + databaseFileName() + " failed to initialize: " + err.what()); myDb.reset(); mySettingsRepository.reset(); diff --git a/src/common/repository/sqlite/SqliteDatabase.cxx b/src/common/repository/sqlite/SqliteDatabase.cxx index 9d8e239d8..4b9912a5a 100644 --- a/src/common/repository/sqlite/SqliteDatabase.cxx +++ b/src/common/repository/sqlite/SqliteDatabase.cxx @@ -75,11 +75,11 @@ void SqliteDatabase::initialize() break; case SQLITE_MISUSE: - Logger::info("failed to checkpoint WAL on " + myDatabaseFile + " - WAL probably unavailable"); + Logger::error("failed to checkpoint WAL on " + myDatabaseFile + " - WAL probably unavailable"); break; default: - Logger::info("failed to checkpoint WAL on " + myDatabaseFile + " : " + sqlite3_errmsg(myHandle)); + Logger::error("failed to checkpoint WAL on " + myDatabaseFile + " : " + sqlite3_errmsg(myHandle)); break; } } diff --git a/src/emucore/Props.cxx b/src/emucore/Props.cxx index fa99b55a8..ff8fd496a 100644 --- a/src/emucore/Props.cxx +++ b/src/emucore/Props.cxx @@ -45,7 +45,7 @@ void Properties::load(KeyValueRepository& repo) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Properties::save(KeyValueRepository& repo) const +bool Properties::save(KeyValueRepository& repo) const { std::map props; @@ -56,7 +56,7 @@ void Properties::save(KeyValueRepository& repo) const props[ourPropertyNames[i]] = myProperties[i]; } - repo.save(props); + return repo.save(props); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/Props.hxx b/src/emucore/Props.hxx index bdb7230af..35e773b75 100644 --- a/src/emucore/Props.hxx +++ b/src/emucore/Props.hxx @@ -84,7 +84,7 @@ class Properties public: void load(KeyValueRepository& repo); - void save(KeyValueRepository& repo) const; + bool save(KeyValueRepository& repo) const; /** Get the value assigned to the specified key. If the key does diff --git a/src/emucore/PropsSet.cxx b/src/emucore/PropsSet.cxx index 5bdd45cb2..e6dc3bbe6 100644 --- a/src/emucore/PropsSet.cxx +++ b/src/emucore/PropsSet.cxx @@ -24,6 +24,7 @@ #include "Props.hxx" #include "PropsSet.hxx" #include "repository/CompositeKeyValueRepositoryNoop.hxx" +#include "repository/KeyValueRepositoryPropertyFile.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PropertiesSet::PropertiesSet() : myRepository{make_shared()} {} @@ -142,6 +143,8 @@ void PropertiesSet::insert(const Properties& properties, bool save) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PropertiesSet::loadPerROM(const FilesystemNode& rom, const string& md5) { + Properties props; + // Handle ROM properties, do some error checking // Only add to the database when necessary bool toInsert = false; @@ -149,14 +152,14 @@ 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")); - // CSTODO - #if 0 - if(propsNode.exists()) - load(propsNode, false); - #endif + if (propsNode.exists()) { + KeyValueRepositoryPropertyFile repo(propsNode); + props.load(repo); + + insert(props, false); + } // Next, make sure we have a valid md5 and name - Properties props; if(!getMD5(md5, props)) { props.set(PropType::Cart_MD5, md5); diff --git a/src/gui/GameInfoDialog.cxx b/src/gui/GameInfoDialog.cxx index aab12f45a..44e0ca7fe 100644 --- a/src/gui/GameInfoDialog.cxx +++ b/src/gui/GameInfoDialog.cxx @@ -36,6 +36,7 @@ #include "Widget.hxx" #include "Font.hxx" +#include "repository/KeyValueRepositoryPropertyFile.hxx" #include "FrameBuffer.hxx" #include "TIASurface.hxx" #include "TIA.hxx" @@ -1334,22 +1335,12 @@ void GameInfoDialog::exportCurrentPropertiesToDisk(const FilesystemNode& node) { saveProperties(); - // CSTODO - #if 0 - stringstream out; - out << myGameProperties; + KeyValueRepositoryPropertyFile repo(node); - try - { - node.write(out); + if (myGameProperties.save(repo)) instance().frameBuffer().showTextMessage("ROM properties exported"); - } - catch(...) - { + else instance().frameBuffer().showTextMessage("Error exporting ROM properties"); - } - - #endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -