Move properties to sqlite.

This commit is contained in:
Christian Speckner 2020-12-30 22:37:41 +01:00
parent 05eefdb112
commit 8dba817906
21 changed files with 592 additions and 243 deletions

View File

@ -99,6 +99,8 @@
"regex": "cpp", "regex": "cpp",
"valarray": "cpp", "valarray": "cpp",
"ranges": "cpp", "ranges": "cpp",
"stop_token": "cpp" "stop_token": "cpp",
"version": "cpp",
"shared_mutex": "cpp"
} }
} }

View File

@ -18,6 +18,8 @@
#ifndef COMPOSITE_KEY_VALUE_REPOSITORY_HXX #ifndef COMPOSITE_KEY_VALUE_REPOSITORY_HXX
#define COMPOSITE_KEY_VALUE_REPOSITORY_HXX #define COMPOSITE_KEY_VALUE_REPOSITORY_HXX
#include <vector>
#include "KeyValueRepository.hxx" #include "KeyValueRepository.hxx"
#include "bspf.hxx" #include "bspf.hxx"
@ -30,6 +32,8 @@ class CompositeKeyValueRepository
virtual shared_ptr<KeyValueRepository> get(const string& key) = 0; virtual shared_ptr<KeyValueRepository> get(const string& key) = 0;
virtual bool has(const string& key) = 0; virtual bool has(const string& key) = 0;
virtual void remove(const string& key) = 0;
}; };
#endif // COMPOSITE_KEY_VALUE_REPOSITORY_HXX #endif // COMPOSITE_KEY_VALUE_REPOSITORY_HXX

View File

@ -0,0 +1,36 @@
//============================================================================
//
// 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 COMPOSITE_KEY_VALUE_REPOSITORY_NOOP_HXX
#define COMPOSITE_KEY_VALUE_REPOSITORY_NOOP_HXX
#include "repository/CompositeKeyValueRepository.hxx"
#include "repository/KeyValueRepositoryNoop.hxx"
#include "bspf.hxx"
class CompositeKeyValueRepositoryNoop : public CompositeKeyValueRepository
{
public:
shared_ptr<KeyValueRepository> get(const string& key) { return make_shared<KeyValueRepositoryNoop>(); }
bool has(const string& key) { return false; }
void remove(const string& key) {}
};
#endif // COMPOSITE_KEY_VALUE_REPOSITORY_NOOP_HXX

View File

@ -34,6 +34,8 @@ class KeyValueRepository
virtual void save(const std::map<string, Variant>& values) = 0; virtual void save(const std::map<string, Variant>& values) = 0;
virtual void save(const string& key, const Variant& value) = 0; virtual void save(const string& key, const Variant& value) = 0;
virtual void remove(const string& key) = 0;
}; };
#endif // KEY_VALUE_REPOSITORY_HXX #endif // KEY_VALUE_REPOSITORY_HXX

View File

@ -33,6 +33,8 @@ class KeyValueRepositoryConfigfile : public KeyValueRepository
void save(const string& key, const Variant& value) override {} void save(const string& key, const Variant& value) override {}
void remove(const string& key) override {}
private: private:
FilesystemNode myFile; FilesystemNode myFile;

View File

@ -31,6 +31,8 @@ class KeyValueRepositoryNoop : public KeyValueRepository
void save(const std::map<string, Variant>& values) override {} void save(const std::map<string, Variant>& values) override {}
void save(const string& key, const Variant& value) override {} void save(const string& key, const Variant& value) override {}
void remove(const string& key) override {}
}; };
#endif // KEY_VALUE_REPOSITORY_NOOP_HXX #endif // KEY_VALUE_REPOSITORY_NOOP_HXX

View File

@ -66,11 +66,7 @@ void AbstractKeyValueRepositorySqlite::save(const string& key, const Variant& va
try { try {
SqliteStatement& stmt{stmtInsert(key, value.toString())}; SqliteStatement& stmt{stmtInsert(key, value.toString())};
stmt stmt.step();
.bind(1, key.c_str())
.bind(2, value.toCString())
.step();
stmt.reset(); stmt.reset();
} }
catch (const SqliteError& err) { catch (const SqliteError& err) {
@ -78,3 +74,16 @@ void AbstractKeyValueRepositorySqlite::save(const string& key, const Variant& va
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AbstractKeyValueRepositorySqlite::remove(const string& key)
{
try {
SqliteStatement& stmt{stmtDelete(key)};
stmt.step();
stmt.reset();
}
catch (const SqliteError& err) {
Logger::info(err.what());
}
}

View File

@ -16,10 +16,13 @@ class AbstractKeyValueRepositorySqlite : public KeyValueRepository
void save(const string& key, const Variant& value) override; void save(const string& key, const Variant& value) override;
void remove(const string& key) override;
protected: protected:
virtual SqliteStatement& stmtInsert(const string& key, const string& value) = 0; virtual SqliteStatement& stmtInsert(const string& key, const string& value) = 0;
virtual SqliteStatement& stmtSelect() = 0; virtual SqliteStatement& stmtSelect() = 0;
virtual SqliteStatement& stmtDelete(const string& key) = 0;
virtual SqliteDatabase& database() = 0; virtual SqliteDatabase& database() = 0;
}; };

View File

@ -53,6 +53,22 @@ bool CompositeKeyValueRepositorySqlite::has(const string& key)
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CompositeKeyValueRepositorySqlite::remove(const string& key)
{
try {
myStmtDeleteSet->reset();
(*myStmtDeleteSet)
.bind(1, key.c_str())
.step();
}
catch (const SqliteError& err) {
Logger::info(err.what());
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CompositeKeyValueRepositorySqlite::initialize() void CompositeKeyValueRepositorySqlite::initialize()
{ {
@ -63,6 +79,8 @@ void CompositeKeyValueRepositorySqlite::initialize()
myStmtInsert = make_unique<SqliteStatement>(myDb, "INSERT OR REPLACE INTO `" + myTableName + "` VALUES (?, ?, ?)"); myStmtInsert = make_unique<SqliteStatement>(myDb, "INSERT OR REPLACE INTO `" + myTableName + "` VALUES (?, ?, ?)");
myStmtSelect = make_unique<SqliteStatement>(myDb, "SELECT `key2`, `VALUE` FROM `" + myTableName + "` WHERE `key1` = ?"); myStmtSelect = make_unique<SqliteStatement>(myDb, "SELECT `key2`, `VALUE` FROM `" + myTableName + "` WHERE `key1` = ?");
myStmtCount = make_unique<SqliteStatement>(myDb, "SELECT COUNT(*) FROM `" + myTableName + "` WHERE `key1` = ?"); myStmtCount = make_unique<SqliteStatement>(myDb, "SELECT COUNT(*) FROM `" + myTableName + "` WHERE `key1` = ?");
myStmtDelete = make_unique<SqliteStatement>(myDb, "DELETE FROM `" + myTableName + "` WHERE `key1` = ? AND `key2` = ?");
myStmtDeleteSet = make_unique<SqliteStatement>(myDb, "DELETE FROM `" + myTableName + "` WHERE `key1` = ?");
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -93,6 +111,17 @@ SqliteStatement& CompositeKeyValueRepositorySqlite::ProxyRepository::stmtSelect(
.bind(1, myKey.c_str()); .bind(1, myKey.c_str());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SqliteStatement& CompositeKeyValueRepositorySqlite::ProxyRepository::stmtDelete(const string& key)
{
myRepo.myStmtDelete->reset();
return (*myRepo.myStmtDelete)
.bind(1, myKey.c_str())
.bind(2, key.c_str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SqliteDatabase& CompositeKeyValueRepositorySqlite::ProxyRepository::database() SqliteDatabase& CompositeKeyValueRepositorySqlite::ProxyRepository::database()
{ {

View File

@ -33,6 +33,8 @@ class CompositeKeyValueRepositorySqlite : public CompositeKeyValueRepository {
bool has(const string& key) override; bool has(const string& key) override;
void remove(const string& key) override;
void initialize(); void initialize();
private: private:
@ -46,6 +48,7 @@ class CompositeKeyValueRepositorySqlite : public CompositeKeyValueRepository {
SqliteStatement& stmtInsert(const string& key, const string& value) override; SqliteStatement& stmtInsert(const string& key, const string& value) override;
SqliteStatement& stmtSelect() override; SqliteStatement& stmtSelect() override;
SqliteStatement& stmtDelete(const string& key) override;
SqliteDatabase& database() override; SqliteDatabase& database() override;
private: private:
@ -69,6 +72,8 @@ class CompositeKeyValueRepositorySqlite : public CompositeKeyValueRepository {
unique_ptr<SqliteStatement> myStmtInsert; unique_ptr<SqliteStatement> myStmtInsert;
unique_ptr<SqliteStatement> myStmtSelect; unique_ptr<SqliteStatement> myStmtSelect;
unique_ptr<SqliteStatement> myStmtCount; unique_ptr<SqliteStatement> myStmtCount;
unique_ptr<SqliteStatement> myStmtDelete;
unique_ptr<SqliteStatement> myStmtDeleteSet;
private: private:

View File

@ -44,6 +44,15 @@ SqliteStatement& KeyValueRepositorySqlite::stmtSelect()
return *myStmtSelect; return *myStmtSelect;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SqliteStatement& KeyValueRepositorySqlite::stmtDelete(const string& key)
{
myStmtDelete->reset();
return (*myStmtDelete)
.bind(1, key.c_str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SqliteDatabase& KeyValueRepositorySqlite::database() SqliteDatabase& KeyValueRepositorySqlite::database()
{ {
@ -59,4 +68,5 @@ void KeyValueRepositorySqlite::initialize()
myStmtInsert = make_unique<SqliteStatement>(myDb, "INSERT OR REPLACE INTO `" + myTableName + "` VALUES (?, ?)"); myStmtInsert = make_unique<SqliteStatement>(myDb, "INSERT OR REPLACE INTO `" + myTableName + "` VALUES (?, ?)");
myStmtSelect = make_unique<SqliteStatement>(myDb, "SELECT `key`, `VALUE` FROM `" + myTableName + "`"); myStmtSelect = make_unique<SqliteStatement>(myDb, "SELECT `key`, `VALUE` FROM `" + myTableName + "`");
myStmtDelete = make_unique<SqliteStatement>(myDb, "DELETE FROM `" + myTableName + "` WHERE `key` = ?");
} }

View File

@ -35,6 +35,7 @@ class KeyValueRepositorySqlite : public AbstractKeyValueRepositorySqlite
SqliteStatement& stmtInsert(const string& key, const string& value) override; SqliteStatement& stmtInsert(const string& key, const string& value) override;
SqliteStatement& stmtSelect() override; SqliteStatement& stmtSelect() override;
SqliteStatement& stmtDelete(const string& key) override;
SqliteDatabase& database() override; SqliteDatabase& database() override;
private: private:
@ -44,6 +45,7 @@ class KeyValueRepositorySqlite : public AbstractKeyValueRepositorySqlite
unique_ptr<SqliteStatement> myStmtInsert; unique_ptr<SqliteStatement> myStmtInsert;
unique_ptr<SqliteStatement> myStmtSelect; unique_ptr<SqliteStatement> myStmtSelect;
unique_ptr<SqliteStatement> myStmtDelete;
private: private:

View File

@ -42,7 +42,7 @@ class SettingsDb
string myDatabaseDirectory; string myDatabaseDirectory;
string myDatabaseName; string myDatabaseName;
unique_ptr<SqliteDatabase> myDb; shared_ptr<SqliteDatabase> myDb;
unique_ptr<KeyValueRepositorySqlite> mySettingsRepository; unique_ptr<KeyValueRepositorySqlite> mySettingsRepository;
unique_ptr<CompositeKeyValueRepositorySqlite> myPropertyRepository; unique_ptr<CompositeKeyValueRepositorySqlite> myPropertyRepository;
}; };

View File

@ -66,6 +66,7 @@
#include "EmulationWorker.hxx" #include "EmulationWorker.hxx"
#include "AudioSettings.hxx" #include "AudioSettings.hxx"
#include "repository/KeyValueRepositoryNoop.hxx" #include "repository/KeyValueRepositoryNoop.hxx"
#include "repository/CompositeKeyValueRepositoryNoop.hxx"
#include "M6532.hxx" #include "M6532.hxx"
#include "OSystem.hxx" #include "OSystem.hxx"
@ -192,8 +193,6 @@ bool OSystem::create()
myPNGLib = make_unique<PNGLibrary>(*this); myPNGLib = make_unique<PNGLibrary>(*this);
#endif #endif
myPropSet->load(myPropertiesFile);
// Detect serial port for AtariVox-USB // Detect serial port for AtariVox-USB
// If a previously set port is defined, use it; // If a previously set port is defined, use it;
// otherwise use the first one found (if any) // otherwise use the first one found (if any)
@ -230,6 +229,7 @@ void OSystem::loadConfig(const Settings::Options& options)
myConfigFile = FilesystemNode(mySettingsDb->databaseFileName()); myConfigFile = FilesystemNode(mySettingsDb->databaseFileName());
mySettings->setRepository(createSettingsRepository()); mySettings->setRepository(createSettingsRepository());
myPropSet->setRepository(createPropertyRepository());
mySettings->load(options); mySettings->load(options);
// userDir is NOT affected by '-baseDir'and '-basedirinapp' params // userDir is NOT affected by '-baseDir'and '-basedirinapp' params
@ -260,9 +260,6 @@ void OSystem::saveConfig()
Logger::debug("Saving config options ..."); Logger::debug("Saving config options ...");
mySettings->save(); mySettings->save();
} }
if(myPropSet && myPropSet->save(myPropertiesFile))
Logger::debug("Saving properties set ...");
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -890,6 +887,14 @@ shared_ptr<KeyValueRepository> OSystem::createSettingsRepository()
: make_shared<KeyValueRepositoryNoop>(); : make_shared<KeyValueRepositoryNoop>();
} }
shared_ptr<CompositeKeyValueRepository> OSystem::createPropertyRepository()
{
return mySettingsDb
? shared_ptr<CompositeKeyValueRepository>(mySettingsDb, &mySettingsDb->propertyRepository())
: make_shared<CompositeKeyValueRepositoryNoop>();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string OSystem::ourOverrideBaseDir = ""; string OSystem::ourOverrideBaseDir = "";
bool OSystem::ourOverrideBaseDirWithApp = false; bool OSystem::ourOverrideBaseDirWithApp = false;

View File

@ -60,6 +60,7 @@ class AudioSettings;
#include "Logger.hxx" #include "Logger.hxx"
#include "bspf.hxx" #include "bspf.hxx"
#include "repository/KeyValueRepository.hxx" #include "repository/KeyValueRepository.hxx"
#include "repository/CompositeKeyValueRepository.hxx"
/** /**
This class provides an interface for accessing operating system specific This class provides an interface for accessing operating system specific
@ -450,6 +451,8 @@ class OSystem
virtual shared_ptr<KeyValueRepository> createSettingsRepository(); virtual shared_ptr<KeyValueRepository> createSettingsRepository();
virtual shared_ptr<CompositeKeyValueRepository> createPropertyRepository();
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// The following methods are system-specific and *must* be // The following methods are system-specific and *must* be
// implemented in derived classes. // implemented in derived classes.

View File

@ -15,10 +15,11 @@
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================ //============================================================================
#include <sstream> #include <map>
#include "bspf.hxx"
#include "Props.hxx" #include "Props.hxx"
#include "Variant.hxx"
#include "bspf.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Properties::Properties() Properties::Properties()
@ -32,6 +33,32 @@ Properties::Properties(const Properties& properties)
copy(properties); copy(properties);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Properties::load(KeyValueRepository& repo)
{
setDefaults();
const auto props = repo.load();
for (const auto& [key, value]: props)
set(getPropType(key), value.toString());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Properties::save(KeyValueRepository& repo) const
{
std::map<string, Variant> props;
for (size_t i = 0; i < static_cast<size_t>(PropType::NumTypes); i++) {
if (myProperties[i] == ourDefaultProperties[i])
repo.remove(ourPropertyNames[i]);
else
props[ourPropertyNames[i]] = myProperties[i];
}
repo.save(props);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Properties::set(PropType key, const string& value) void Properties::set(PropType key, const string& value)
{ {
@ -79,120 +106,6 @@ void Properties::set(PropType key, const string& value)
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
istream& operator>>(istream& is, Properties& p)
{
p.setDefaults();
// Loop reading properties
string key, value;
for(;;)
{
// Get the key associated with this property
key = p.readQuotedString(is);
// Make sure the stream is still okay
if(!is)
return is;
// A null key signifies the end of the property list
if(key == "")
break;
// Get the value associated with this property
value = p.readQuotedString(is);
// Make sure the stream is still okay
if(!is)
return is;
// Set the property
PropType type = Properties::getPropType(key);
p.set(type, value);
}
return is;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ostream& operator<<(ostream& os, const Properties& p)
{
// Write out each of the key and value pairs
bool changed = false;
for(size_t i = 0; i < static_cast<size_t>(PropType::NumTypes); ++i)
{
// Try to save some space by only saving the items that differ from default
if(p.myProperties[i] != Properties::ourDefaultProperties[i])
{
p.writeQuotedString(os, Properties::ourPropertyNames[i]);
os.put(' ');
p.writeQuotedString(os, p.myProperties[i]);
os.put('\n');
changed = true;
}
}
if(changed)
{
// Put a trailing null string so we know when to stop reading
p.writeQuotedString(os, "");
os.put('\n');
os.put('\n');
}
return os;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Properties::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 Properties::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('"');
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Properties::operator==(const Properties& properties) const bool Properties::operator==(const Properties& properties) const
{ {

View File

@ -18,6 +18,7 @@
#ifndef PROPERTIES_HXX #ifndef PROPERTIES_HXX
#define PROPERTIES_HXX #define PROPERTIES_HXX
#include "repository/KeyValueRepository.hxx"
#include "bspf.hxx" #include "bspf.hxx"
enum class PropType : uInt8 { enum class PropType : uInt8 {
@ -81,6 +82,10 @@ class Properties
Properties(const Properties& properties); Properties(const Properties& properties);
public: public:
void load(KeyValueRepository& repo);
void save(KeyValueRepository& repo) const;
/** /**
Get the value assigned to the specified key. If the key does Get the value assigned to the specified key. If the key does
not exist then the empty string is returned. not exist then the empty string is returned.
@ -101,22 +106,6 @@ class Properties
*/ */
void set(PropType key, const string& value); void set(PropType key, const string& value);
/**
Load properties from the specified input stream
@param is The input stream to use
@param p The Properties object to write to
*/
friend istream& operator>>(istream& is, Properties& p);
/**
Save properties to the specified output stream
@param os The output stream to use
@param p The Properties object to read from
*/
friend ostream& operator<<(ostream& os, const Properties& p);
/** /**
Print the attributes of this properties object Print the attributes of this properties object
*/ */
@ -169,24 +158,6 @@ class Properties
*/ */
void copy(const Properties& properties); void copy(const Properties& properties);
/**
Read the next quoted string from the specified input stream
and returns it.
@param in The input stream to use
@return The string inside the quotes
*/
static string readQuotedString(istream& in);
/**
Write the specified string to the given output stream as a
quoted string.
@param out The output stream to use
@param s The string to output
*/
static void writeQuotedString(ostream& out, const string& s);
/** /**
Get the property type associated with the named property Get the property type associated with the named property

View File

@ -23,51 +23,15 @@
#include "DefProps.hxx" #include "DefProps.hxx"
#include "Props.hxx" #include "Props.hxx"
#include "PropsSet.hxx" #include "PropsSet.hxx"
#include "repository/CompositeKeyValueRepositoryNoop.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PropertiesSet::load(const FilesystemNode& file, bool save) PropertiesSet::PropertiesSet() : myRepository{make_shared<CompositeKeyValueRepositoryNoop>()} {}
{
try
{
stringstream in;
if(file.exists() && file.read(in) > 0)
{
Properties prop;
while(in >> prop)
insert(prop, save);
}
}
catch(...)
{
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PropertiesSet::save(const FilesystemNode& file) const void PropertiesSet::setRepository(shared_ptr<CompositeKeyValueRepository> repository)
{ {
try myRepository = repository;
{
// Only save properties when it won't create an empty file
if(!file.exists() && myExternalProps.size() == 0)
return false;
// Only save those entries in the external list
stringstream out;
for(const auto& i: myExternalProps)
out << i.second;
file.write(out);
return true;
}
catch(const runtime_error& e)
{
Logger::error(e.what());
}
catch(...)
{
}
return false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -88,11 +52,9 @@ bool PropertiesSet::getMD5(const string& md5, Properties& properties,
// First check properties from external file // First check properties from external file
if(!useDefaults) if(!useDefaults)
{ {
// Check external list if (myRepository->has(md5)) {
auto ext = myExternalProps.find(md5); properties.load(*myRepository->get(md5));
if(ext != myExternalProps.end())
{
properties = ext->second;
found = true; found = true;
} }
else // Search temp list else // Search temp list
@ -138,6 +100,7 @@ bool PropertiesSet::getMD5(const string& md5, Properties& properties,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PropertiesSet::insert(const Properties& properties, bool save) void PropertiesSet::insert(const Properties& properties, bool save)
{ {
// TODO
// Note that the following code is optimized for insertion when an item // Note that the following code is optimized for insertion when an item
// doesn't already exist, and when the external properties file is // doesn't already exist, and when the external properties file is
// relatively small (which is the case with current versions of Stella, // relatively small (which is the case with current versions of Stella,
@ -158,19 +121,21 @@ void PropertiesSet::insert(const Properties& properties, bool save)
return; return;
else if(getMD5(md5, defaultProps, true) && defaultProps == properties) else if(getMD5(md5, defaultProps, true) && defaultProps == properties)
{ {
myExternalProps.erase(md5); cerr << "DELETE" << endl << std::flush;
myRepository->remove(md5);
return; return;
} }
// The status of 'save' determines which list to save to if (save) {
PropsList& list = save ? myExternalProps : myTempProps; properties.save(*myRepository->get(md5));
} else {
auto ret = list.emplace(md5, properties); auto ret = myTempProps.emplace(md5, properties);
if(ret.second == false) if(ret.second == false)
{ {
// Remove old item and insert again // Remove old item and insert again
list.erase(ret.first); myTempProps.erase(ret.first);
list.emplace(md5, properties); myTempProps.emplace(md5, properties);
}
} }
} }
@ -184,8 +149,11 @@ void PropertiesSet::loadPerROM(const FilesystemNode& rom, const string& md5)
// First, does this ROM have a per-ROM properties entry? // First, does this ROM have a per-ROM properties entry?
// If so, load it into the database // If so, load it into the database
FilesystemNode propsNode(rom.getPathWithExt(".pro")); FilesystemNode propsNode(rom.getPathWithExt(".pro"));
if(propsNode.exists()) // CSTODO
load(propsNode, false); #if 0
if(propsNode.exists())
load(propsNode, false);
#endif
// Next, make sure we have a valid md5 and name // Next, make sure we have a valid md5 and name
Properties props; Properties props;

View File

@ -25,6 +25,7 @@ class OSystem;
#include "bspf.hxx" #include "bspf.hxx"
#include "Props.hxx" #include "Props.hxx"
#include "repository/CompositeKeyValueRepository.hxx"
/** /**
This class maintains an ordered collection of properties, maintained This class maintains an ordered collection of properties, maintained
@ -39,32 +40,10 @@ class OSystem;
class PropertiesSet class PropertiesSet
{ {
public: public:
/**
Trivial constructor.
*/
PropertiesSet() = default;
/** PropertiesSet();
Load properties from the specified file, and create an internal
searchable list.
@param file The node representing the input file to use void setRepository(shared_ptr<CompositeKeyValueRepository> repository);
@param save Indicates whether the properties should be saved
when the program exits
*/
void load(const FilesystemNode& file, bool save = true);
/**
Save properties to the specified file.
@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 FilesystemNode& file) const;
/** /**
Get the property from the set with the given MD5. Get the property from the set with the given MD5.
@ -120,6 +99,8 @@ class PropertiesSet
// be discarded when the program ends // be discarded when the program ends
PropsList myTempProps; PropsList myTempProps;
shared_ptr<CompositeKeyValueRepository> myRepository;
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
PropertiesSet(const PropertiesSet&) = delete; PropertiesSet(const PropertiesSet&) = delete;

397
src/emucore/_props.cxx Normal file
View File

@ -0,0 +1,397 @@
//============================================================================
//
// 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 <sstream>
#include "bspf.hxx"
#include "Props.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Properties::Properties()
{
setDefaults();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Properties::Properties(const Properties& properties)
{
copy(properties);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Properties::set(PropType key, const string& value)
{
size_t pos = static_cast<size_t>(key);
if(pos < myProperties.size())
{
myProperties[pos] = value;
if(BSPF::equalsIgnoreCase(myProperties[pos], "AUTO-DETECT"))
myProperties[pos] = "AUTO";
switch(key)
{
case PropType::Cart_Sound:
case PropType::Cart_Type:
case PropType::Console_LeftDiff:
case PropType::Console_RightDiff:
case PropType::Console_TVType:
case PropType::Console_SwapPorts:
case PropType::Controller_Left:
case PropType::Controller_Left1:
case PropType::Controller_Left2:
case PropType::Controller_Right:
case PropType::Controller_Right1:
case PropType::Controller_Right2:
case PropType::Controller_SwapPaddles:
case PropType::Controller_MouseAxis:
case PropType::Display_Format:
case PropType::Display_Phosphor:
{
BSPF::toUpperCase(myProperties[pos]);
break;
}
case PropType::Display_PPBlend:
{
int blend = BSPF::stringToInt(myProperties[pos]);
if(blend < 0 || blend > 100)
myProperties[pos] = ourDefaultProperties[pos];
break;
}
default:
break;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
istream& operator>>(istream& is, Properties& p)
{
p.setDefaults();
// Loop reading properties
string key, value;
for(;;)
{
// Get the key associated with this property
key = p.readQuotedString(is);
// Make sure the stream is still okay
if(!is)
return is;
// A null key signifies the end of the property list
if(key == "")
break;
// Get the value associated with this property
value = p.readQuotedString(is);
// Make sure the stream is still okay
if(!is)
return is;
// Set the property
PropType type = Properties::getPropType(key);
p.set(type, value);
}
return is;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ostream& operator<<(ostream& os, const Properties& p)
{
// Write out each of the key and value pairs
bool changed = false;
for(size_t i = 0; i < static_cast<size_t>(PropType::NumTypes); ++i)
{
// Try to save some space by only saving the items that differ from default
if(p.myProperties[i] != Properties::ourDefaultProperties[i])
{
p.writeQuotedString(os, Properties::ourPropertyNames[i]);
os.put(' ');
p.writeQuotedString(os, p.myProperties[i]);
os.put('\n');
changed = true;
}
}
if(changed)
{
// Put a trailing null string so we know when to stop reading
p.writeQuotedString(os, "");
os.put('\n');
os.put('\n');
}
return os;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Properties::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 Properties::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('"');
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Properties::operator==(const Properties& properties) const
{
for(size_t i = 0; i < myProperties.size(); ++i)
if(myProperties[i] != properties.myProperties[i])
return false;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Properties::operator!=(const Properties& properties) const
{
return !(*this == properties);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Properties& Properties::operator=(const Properties& properties)
{
// Do the assignment only if this isn't a self assignment
if(this != &properties)
{
// Now, make myself a copy of the given object
copy(properties);
}
return *this;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Properties::setDefault(PropType key, const string& value)
{
ourDefaultProperties[static_cast<size_t>(key)] = value;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Properties::copy(const Properties& properties)
{
// Now, copy each property from properties
for(size_t i = 0; i < myProperties.size(); ++i)
myProperties[i] = properties.myProperties[i];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Properties::print() const
{
cout << get(PropType::Cart_MD5) << "|"
<< get(PropType::Cart_Name) << "|"
<< get(PropType::Cart_Manufacturer) << "|"
<< get(PropType::Cart_ModelNo) << "|"
<< get(PropType::Cart_Note) << "|"
<< get(PropType::Cart_Rarity) << "|"
<< get(PropType::Cart_Sound) << "|"
<< get(PropType::Cart_StartBank) << "|"
<< get(PropType::Cart_Type) << "|"
<< get(PropType::Console_LeftDiff) << "|"
<< get(PropType::Console_RightDiff) << "|"
<< get(PropType::Console_TVType) << "|"
<< get(PropType::Console_SwapPorts) << "|"
<< get(PropType::Controller_Left) << "|"
<< get(PropType::Controller_Left1) << "|"
<< get(PropType::Controller_Left2) << "|"
<< get(PropType::Controller_Right) << "|"
<< get(PropType::Controller_Right1) << "|"
<< get(PropType::Controller_Right2) << "|"
<< get(PropType::Controller_SwapPaddles) << "|"
<< get(PropType::Controller_PaddlesXCenter) << "|"
<< get(PropType::Controller_PaddlesYCenter) << "|"
<< get(PropType::Controller_MouseAxis) << "|"
<< get(PropType::Display_Format) << "|"
<< get(PropType::Display_VCenter) << "|"
<< get(PropType::Display_Phosphor) << "|"
<< get(PropType::Display_PPBlend) << "|"
<< get(PropType::Cart_Highscore)
<< endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Properties::reset(PropType key)
{
size_t pos = static_cast<size_t>(key);
myProperties[pos] = ourDefaultProperties[pos];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Properties::setDefaults()
{
for(size_t i = 0; i < myProperties.size(); ++i)
myProperties[i] = ourDefaultProperties[i];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropType Properties::getPropType(const string& name)
{
for(size_t i = 0; i < NUM_PROPS; ++i)
if(ourPropertyNames[i] == name)
return static_cast<PropType>(i);
// Otherwise, indicate that the item wasn't found
return PropType::NumTypes;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Properties::printHeader()
{
cout << "Cart_MD5|"
<< "Cart_Name|"
<< "Cart_Manufacturer|"
<< "Cart_ModelNo|"
<< "Cart_Note|"
<< "Cart_Rarity|"
<< "Cart_Sound|"
<< "Cart_StartBank|"
<< "Cart_Type|"
<< "Console_LeftDiff|"
<< "Console_RightDiff|"
<< "Console_TVType|"
<< "Console_SwapPorts|"
<< "Controller_Left|"
<< "Controller_Left1|"
<< "Controller_Left2|"
<< "Controller_Right|"
<< "Controller_Right1|"
<< "Controller_Right2|"
<< "Controller_SwapPaddles|"
<< "Controller_PaddlesXCenter|"
<< "Controller_PaddlesYCenter|"
<< "Controller_MouseAxis|"
<< "Display_Format|"
<< "Display_VCenter|"
<< "Display_Phosphor|"
<< "Display_PPBlend|"
<< "Cart_Highscore"
<< endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::array<string, Properties::NUM_PROPS> Properties::ourDefaultProperties =
{
"", // Cart.MD5
"", // Cart.Manufacturer
"", // Cart.ModelNo
"", // Cart.Name
"", // Cart.Note
"", // Cart.Rarity
"MONO", // Cart.Sound
"AUTO", // Cart.StartBank
"AUTO", // Cart.Type
"B", // Console.LeftDiff
"B", // Console.RightDiff
"COLOR", // Console.TVType
"NO", // Console.SwapPorts
"AUTO", // Controller.Left
"", // Controller.Left1
"", // Controller.Left2
"AUTO", // Controller.Right
"", // Controller.Right1
"", // Controller.Right2
"NO", // Controller.SwapPaddles
"0", // Controller.PaddlesXCenter
"0", // Controller.PaddlesYCenter
"AUTO", // Controller.MouseAxis
"AUTO", // Display.Format
"0", // Display.VCenter
"NO", // Display.Phosphor
"0", // Display.PPBlend
"" // Cart.Highscore
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::array<string, Properties::NUM_PROPS> Properties::ourPropertyNames =
{
"Cart.MD5",
"Cart.Manufacturer",
"Cart.ModelNo",
"Cart.Name",
"Cart.Note",
"Cart.Rarity",
"Cart.Sound",
"Cart.StartBank",
"Cart.Type",
"Console.LeftDiff",
"Console.RightDiff",
"Console.TVType",
"Console.SwapPorts",
"Controller.Left",
"Controller.Left1",
"Controller.Left2",
"Controller.Right",
"Controller.Right1",
"Controller.Right2",
"Controller.SwapPaddles",
"Controller.PaddlesXCenter",
"Controller.PaddlesYCenter",
"Controller.MouseAxis",
"Display.Format",
"Display.VCenter",
"Display.Phosphor",
"Display.PPBlend",
"Cart.Highscore"
};

View File

@ -1333,6 +1333,9 @@ void GameInfoDialog::setAddressVal(EditTextWidget* addressWidget, EditTextWidget
void GameInfoDialog::exportCurrentPropertiesToDisk(const FilesystemNode& node) void GameInfoDialog::exportCurrentPropertiesToDisk(const FilesystemNode& node)
{ {
saveProperties(); saveProperties();
// CSTODO
#if 0
stringstream out; stringstream out;
out << myGameProperties; out << myGameProperties;
@ -1345,6 +1348,8 @@ void GameInfoDialog::exportCurrentPropertiesToDisk(const FilesystemNode& node)
{ {
instance().frameBuffer().showTextMessage("Error exporting ROM properties"); instance().frameBuffer().showTextMessage("Error exporting ROM properties");
} }
#endif
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -