Move properties to sqlite.

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

View File

@ -99,6 +99,8 @@
"regex": "cpp",
"valarray": "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
#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

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 string& key, const Variant& value) = 0;
virtual void remove(const string& key) = 0;
};
#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 remove(const string& key) override {}
private:
FilesystemNode myFile;

View File

@ -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

View File

@ -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());
}
}

View File

@ -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;
};

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()
{
@ -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()
{

View File

@ -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:

View File

@ -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` = ?");
}

View File

@ -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:

View File

@ -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;
};

View File

@ -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;

View File

@ -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.

View File

@ -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
{

View File

@ -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

View File

@ -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;

View File

@ -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;

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)
{
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
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -