mirror of https://github.com/stella-emu/stella.git
Move properties to sqlite.
This commit is contained in:
parent
f496f8b74a
commit
5c70a3d3ec
|
@ -99,6 +99,8 @@
|
|||
"regex": "cpp",
|
||||
"valarray": "cpp",
|
||||
"ranges": "cpp",
|
||||
"stop_token": "cpp"
|
||||
"stop_token": "cpp",
|
||||
"version": "cpp",
|
||||
"shared_mutex": "cpp"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#ifndef COMPOSITE_KEY_VALUE_REPOSITORY_HXX
|
||||
#define COMPOSITE_KEY_VALUE_REPOSITORY_HXX
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "KeyValueRepository.hxx"
|
||||
#include "bspf.hxx"
|
||||
|
||||
|
@ -30,6 +32,8 @@ class CompositeKeyValueRepository
|
|||
virtual shared_ptr<KeyValueRepository> get(const string& key) = 0;
|
||||
|
||||
virtual bool has(const string& key) = 0;
|
||||
|
||||
virtual void remove(const string& key) = 0;
|
||||
};
|
||||
|
||||
#endif // COMPOSITE_KEY_VALUE_REPOSITORY_HXX
|
||||
|
|
|
@ -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
|
|
@ -34,6 +34,8 @@ class KeyValueRepository
|
|||
virtual void save(const std::map<string, Variant>& values) = 0;
|
||||
|
||||
virtual void save(const string& key, const Variant& value) = 0;
|
||||
|
||||
virtual void remove(const string& key) = 0;
|
||||
};
|
||||
|
||||
#endif // KEY_VALUE_REPOSITORY_HXX
|
||||
|
|
|
@ -33,6 +33,8 @@ class KeyValueRepositoryConfigfile : public KeyValueRepository
|
|||
|
||||
void save(const string& key, const Variant& value) override {}
|
||||
|
||||
void remove(const string& key) override {}
|
||||
|
||||
private:
|
||||
|
||||
FilesystemNode myFile;
|
||||
|
|
|
@ -31,6 +31,8 @@ class KeyValueRepositoryNoop : public KeyValueRepository
|
|||
void save(const std::map<string, Variant>& values) override {}
|
||||
|
||||
void save(const string& key, const Variant& value) override {}
|
||||
|
||||
void remove(const string& key) override {}
|
||||
};
|
||||
|
||||
#endif // KEY_VALUE_REPOSITORY_NOOP_HXX
|
||||
|
|
|
@ -66,11 +66,7 @@ void AbstractKeyValueRepositorySqlite::save(const string& key, const Variant& va
|
|||
try {
|
||||
SqliteStatement& stmt{stmtInsert(key, value.toString())};
|
||||
|
||||
stmt
|
||||
.bind(1, key.c_str())
|
||||
.bind(2, value.toCString())
|
||||
.step();
|
||||
|
||||
stmt.step();
|
||||
stmt.reset();
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,13 @@ class AbstractKeyValueRepositorySqlite : public KeyValueRepository
|
|||
|
||||
void save(const string& key, const Variant& value) override;
|
||||
|
||||
void remove(const string& key) override;
|
||||
|
||||
protected:
|
||||
|
||||
virtual SqliteStatement& stmtInsert(const string& key, const string& value) = 0;
|
||||
virtual SqliteStatement& stmtSelect() = 0;
|
||||
virtual SqliteStatement& stmtDelete(const string& key) = 0;
|
||||
virtual SqliteDatabase& database() = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
@ -63,6 +79,8 @@ void CompositeKeyValueRepositorySqlite::initialize()
|
|||
myStmtInsert = make_unique<SqliteStatement>(myDb, "INSERT OR REPLACE INTO `" + myTableName + "` VALUES (?, ?, ?)");
|
||||
myStmtSelect = make_unique<SqliteStatement>(myDb, "SELECT `key2`, `VALUE` 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());
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -33,6 +33,8 @@ class CompositeKeyValueRepositorySqlite : public CompositeKeyValueRepository {
|
|||
|
||||
bool has(const string& key) override;
|
||||
|
||||
void remove(const string& key) override;
|
||||
|
||||
void initialize();
|
||||
|
||||
private:
|
||||
|
@ -46,6 +48,7 @@ class CompositeKeyValueRepositorySqlite : public CompositeKeyValueRepository {
|
|||
|
||||
SqliteStatement& stmtInsert(const string& key, const string& value) override;
|
||||
SqliteStatement& stmtSelect() override;
|
||||
SqliteStatement& stmtDelete(const string& key) override;
|
||||
SqliteDatabase& database() override;
|
||||
|
||||
private:
|
||||
|
@ -69,6 +72,8 @@ class CompositeKeyValueRepositorySqlite : public CompositeKeyValueRepository {
|
|||
unique_ptr<SqliteStatement> myStmtInsert;
|
||||
unique_ptr<SqliteStatement> myStmtSelect;
|
||||
unique_ptr<SqliteStatement> myStmtCount;
|
||||
unique_ptr<SqliteStatement> myStmtDelete;
|
||||
unique_ptr<SqliteStatement> myStmtDeleteSet;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -44,6 +44,15 @@ SqliteStatement& KeyValueRepositorySqlite::stmtSelect()
|
|||
return *myStmtSelect;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
SqliteStatement& KeyValueRepositorySqlite::stmtDelete(const string& key)
|
||||
{
|
||||
myStmtDelete->reset();
|
||||
|
||||
return (*myStmtDelete)
|
||||
.bind(1, key.c_str());
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
SqliteDatabase& KeyValueRepositorySqlite::database()
|
||||
{
|
||||
|
@ -59,4 +68,5 @@ void KeyValueRepositorySqlite::initialize()
|
|||
|
||||
myStmtInsert = make_unique<SqliteStatement>(myDb, "INSERT OR REPLACE INTO `" + myTableName + "` VALUES (?, ?)");
|
||||
myStmtSelect = make_unique<SqliteStatement>(myDb, "SELECT `key`, `VALUE` FROM `" + myTableName + "`");
|
||||
myStmtDelete = make_unique<SqliteStatement>(myDb, "DELETE FROM `" + myTableName + "` WHERE `key` = ?");
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ class KeyValueRepositorySqlite : public AbstractKeyValueRepositorySqlite
|
|||
|
||||
SqliteStatement& stmtInsert(const string& key, const string& value) override;
|
||||
SqliteStatement& stmtSelect() override;
|
||||
SqliteStatement& stmtDelete(const string& key) override;
|
||||
SqliteDatabase& database() override;
|
||||
|
||||
private:
|
||||
|
@ -44,6 +45,7 @@ class KeyValueRepositorySqlite : public AbstractKeyValueRepositorySqlite
|
|||
|
||||
unique_ptr<SqliteStatement> myStmtInsert;
|
||||
unique_ptr<SqliteStatement> myStmtSelect;
|
||||
unique_ptr<SqliteStatement> myStmtDelete;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class SettingsDb
|
|||
string myDatabaseDirectory;
|
||||
string myDatabaseName;
|
||||
|
||||
unique_ptr<SqliteDatabase> myDb;
|
||||
shared_ptr<SqliteDatabase> myDb;
|
||||
unique_ptr<KeyValueRepositorySqlite> mySettingsRepository;
|
||||
unique_ptr<CompositeKeyValueRepositorySqlite> myPropertyRepository;
|
||||
};
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
#include "EmulationWorker.hxx"
|
||||
#include "AudioSettings.hxx"
|
||||
#include "repository/KeyValueRepositoryNoop.hxx"
|
||||
#include "repository/CompositeKeyValueRepositoryNoop.hxx"
|
||||
#include "M6532.hxx"
|
||||
|
||||
#include "OSystem.hxx"
|
||||
|
@ -192,8 +193,6 @@ bool OSystem::create()
|
|||
myPNGLib = make_unique<PNGLibrary>(*this);
|
||||
#endif
|
||||
|
||||
myPropSet->load(myPropertiesFile);
|
||||
|
||||
// Detect serial port for AtariVox-USB
|
||||
// If a previously set port is defined, use it;
|
||||
// otherwise use the first one found (if any)
|
||||
|
@ -230,6 +229,7 @@ void OSystem::loadConfig(const Settings::Options& options)
|
|||
myConfigFile = FilesystemNode(mySettingsDb->databaseFileName());
|
||||
|
||||
mySettings->setRepository(createSettingsRepository());
|
||||
myPropSet->setRepository(createPropertyRepository());
|
||||
mySettings->load(options);
|
||||
|
||||
// userDir is NOT affected by '-baseDir'and '-basedirinapp' params
|
||||
|
@ -260,9 +260,6 @@ void OSystem::saveConfig()
|
|||
Logger::debug("Saving config options ...");
|
||||
mySettings->save();
|
||||
}
|
||||
|
||||
if(myPropSet && myPropSet->save(myPropertiesFile))
|
||||
Logger::debug("Saving properties set ...");
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -890,6 +887,14 @@ shared_ptr<KeyValueRepository> OSystem::createSettingsRepository()
|
|||
: make_shared<KeyValueRepositoryNoop>();
|
||||
}
|
||||
|
||||
shared_ptr<CompositeKeyValueRepository> OSystem::createPropertyRepository()
|
||||
{
|
||||
return mySettingsDb
|
||||
? shared_ptr<CompositeKeyValueRepository>(mySettingsDb, &mySettingsDb->propertyRepository())
|
||||
: make_shared<CompositeKeyValueRepositoryNoop>();
|
||||
}
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string OSystem::ourOverrideBaseDir = "";
|
||||
bool OSystem::ourOverrideBaseDirWithApp = false;
|
||||
|
|
|
@ -60,6 +60,7 @@ class AudioSettings;
|
|||
#include "Logger.hxx"
|
||||
#include "bspf.hxx"
|
||||
#include "repository/KeyValueRepository.hxx"
|
||||
#include "repository/CompositeKeyValueRepository.hxx"
|
||||
|
||||
/**
|
||||
This class provides an interface for accessing operating system specific
|
||||
|
@ -450,6 +451,8 @@ class OSystem
|
|||
|
||||
virtual shared_ptr<KeyValueRepository> createSettingsRepository();
|
||||
|
||||
virtual shared_ptr<CompositeKeyValueRepository> createPropertyRepository();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// The following methods are system-specific and *must* be
|
||||
// implemented in derived classes.
|
||||
|
|
|
@ -15,10 +15,11 @@
|
|||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "Props.hxx"
|
||||
#include "Variant.hxx"
|
||||
#include "bspf.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Properties::Properties()
|
||||
|
@ -32,6 +33,32 @@ Properties::Properties(const Properties& 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)
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#ifndef PROPERTIES_HXX
|
||||
#define PROPERTIES_HXX
|
||||
|
||||
#include "repository/KeyValueRepository.hxx"
|
||||
#include "bspf.hxx"
|
||||
|
||||
enum class PropType : uInt8 {
|
||||
|
@ -81,6 +82,10 @@ class Properties
|
|||
Properties(const Properties& properties);
|
||||
|
||||
public:
|
||||
void load(KeyValueRepository& repo);
|
||||
|
||||
void save(KeyValueRepository& repo) const;
|
||||
|
||||
/**
|
||||
Get the value assigned to the specified key. If the key does
|
||||
not exist then the empty string is returned.
|
||||
|
@ -101,22 +106,6 @@ class Properties
|
|||
*/
|
||||
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
|
||||
*/
|
||||
|
@ -169,24 +158,6 @@ class 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
|
||||
|
||||
|
|
|
@ -23,51 +23,15 @@
|
|||
#include "DefProps.hxx"
|
||||
#include "Props.hxx"
|
||||
#include "PropsSet.hxx"
|
||||
#include "repository/CompositeKeyValueRepositoryNoop.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void PropertiesSet::load(const FilesystemNode& file, bool save)
|
||||
{
|
||||
try
|
||||
{
|
||||
stringstream in;
|
||||
if(file.exists() && file.read(in) > 0)
|
||||
{
|
||||
Properties prop;
|
||||
while(in >> prop)
|
||||
insert(prop, save);
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
}
|
||||
}
|
||||
PropertiesSet::PropertiesSet() : myRepository{make_shared<CompositeKeyValueRepositoryNoop>()} {}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool PropertiesSet::save(const FilesystemNode& file) const
|
||||
void PropertiesSet::setRepository(shared_ptr<CompositeKeyValueRepository> repository)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 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;
|
||||
myRepository = repository;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -88,11 +52,9 @@ bool PropertiesSet::getMD5(const string& md5, Properties& properties,
|
|||
// First check properties from external file
|
||||
if(!useDefaults)
|
||||
{
|
||||
// Check external list
|
||||
auto ext = myExternalProps.find(md5);
|
||||
if(ext != myExternalProps.end())
|
||||
{
|
||||
properties = ext->second;
|
||||
if (myRepository->has(md5)) {
|
||||
properties.load(*myRepository->get(md5));
|
||||
|
||||
found = true;
|
||||
}
|
||||
else // Search temp list
|
||||
|
@ -138,6 +100,7 @@ bool PropertiesSet::getMD5(const string& md5, Properties& properties,
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void PropertiesSet::insert(const Properties& properties, bool save)
|
||||
{
|
||||
// TODO
|
||||
// Note that the following code is optimized for insertion when an item
|
||||
// doesn't already exist, and when the external properties file is
|
||||
// relatively small (which is the case with current versions of Stella,
|
||||
|
@ -158,19 +121,21 @@ void PropertiesSet::insert(const Properties& properties, bool save)
|
|||
return;
|
||||
else if(getMD5(md5, defaultProps, true) && defaultProps == properties)
|
||||
{
|
||||
myExternalProps.erase(md5);
|
||||
cerr << "DELETE" << endl << std::flush;
|
||||
myRepository->remove(md5);
|
||||
return;
|
||||
}
|
||||
|
||||
// The status of 'save' determines which list to save to
|
||||
PropsList& list = save ? myExternalProps : myTempProps;
|
||||
|
||||
auto ret = list.emplace(md5, properties);
|
||||
if(ret.second == false)
|
||||
{
|
||||
// Remove old item and insert again
|
||||
list.erase(ret.first);
|
||||
list.emplace(md5, properties);
|
||||
if (save) {
|
||||
properties.save(*myRepository->get(md5));
|
||||
} else {
|
||||
auto ret = myTempProps.emplace(md5, properties);
|
||||
if(ret.second == false)
|
||||
{
|
||||
// Remove old item and insert again
|
||||
myTempProps.erase(ret.first);
|
||||
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?
|
||||
// If so, load it into the database
|
||||
FilesystemNode propsNode(rom.getPathWithExt(".pro"));
|
||||
if(propsNode.exists())
|
||||
load(propsNode, false);
|
||||
// CSTODO
|
||||
#if 0
|
||||
if(propsNode.exists())
|
||||
load(propsNode, false);
|
||||
#endif
|
||||
|
||||
// Next, make sure we have a valid md5 and name
|
||||
Properties props;
|
||||
|
|
|
@ -25,6 +25,7 @@ class OSystem;
|
|||
|
||||
#include "bspf.hxx"
|
||||
#include "Props.hxx"
|
||||
#include "repository/CompositeKeyValueRepository.hxx"
|
||||
|
||||
/**
|
||||
This class maintains an ordered collection of properties, maintained
|
||||
|
@ -39,32 +40,10 @@ class OSystem;
|
|||
class PropertiesSet
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Trivial constructor.
|
||||
*/
|
||||
PropertiesSet() = default;
|
||||
|
||||
/**
|
||||
Load properties from the specified file, and create an internal
|
||||
searchable list.
|
||||
PropertiesSet();
|
||||
|
||||
@param file The node representing the input file to use
|
||||
@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;
|
||||
void setRepository(shared_ptr<CompositeKeyValueRepository> repository);
|
||||
|
||||
/**
|
||||
Get the property from the set with the given MD5.
|
||||
|
@ -120,6 +99,8 @@ class PropertiesSet
|
|||
// be discarded when the program ends
|
||||
PropsList myTempProps;
|
||||
|
||||
shared_ptr<CompositeKeyValueRepository> myRepository;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
PropertiesSet(const PropertiesSet&) = delete;
|
||||
|
|
|
@ -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"
|
||||
};
|
|
@ -1333,6 +1333,9 @@ void GameInfoDialog::setAddressVal(EditTextWidget* addressWidget, EditTextWidget
|
|||
void GameInfoDialog::exportCurrentPropertiesToDisk(const FilesystemNode& node)
|
||||
{
|
||||
saveProperties();
|
||||
|
||||
// CSTODO
|
||||
#if 0
|
||||
stringstream out;
|
||||
out << myGameProperties;
|
||||
|
||||
|
@ -1345,6 +1348,8 @@ void GameInfoDialog::exportCurrentPropertiesToDisk(const FilesystemNode& node)
|
|||
{
|
||||
instance().frameBuffer().showTextMessage("Error exporting ROM properties");
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
Loading…
Reference in New Issue