stella/src/emucore/PropsSet.cxx

224 lines
6.5 KiB
C++

//============================================================================
//
// 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-2011 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.
//
// $Id$
//============================================================================
#include <fstream>
#include <sstream>
#include <map>
#include "bspf.hxx"
#include "DefProps.hxx"
#include "OSystem.hxx"
#include "Props.hxx"
#include "Settings.hxx"
#include "PropsSet.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertiesSet::PropertiesSet(OSystem* osystem)
: myOSystem(osystem),
mySize(0)
{
const string& props = myOSystem->propertiesFile();
load(props);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertiesSet::~PropertiesSet()
{
myExternalProps.clear();
myTempProps.clear();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PropertiesSet::load(const string& filename)
{
ifstream in(filename.c_str(), ios::in);
// Loop reading properties
for(;;)
{
// Make sure the stream is still good or we're done
if(!in)
break;
// Get the property list associated with this profile
Properties prop;
prop.load(in);
// If the stream is still good then insert the properties
if(in)
insert(prop);
}
if(in)
in.close();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PropertiesSet::save(const string& filename) const
{
ofstream out(filename.c_str(), ios::out);
if(!out)
return false;
// Only save those entries in the external list
for(PropsList::const_iterator i = myExternalProps.begin();
i != myExternalProps.end(); ++i)
i->second.save(out);
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PropertiesSet::getMD5(const string& md5, Properties& properties,
bool useDefaults) const
{
properties.setDefaults();
bool found = false;
// There are three lists to search when looking for a properties entry,
// which must be done in the following order
// If 'useDefaults' is specified, only use the built-in list
//
// 'save': entries previously inserted that are saved on program exit
// 'temp': entries previously inserted that are discarded
// 'builtin': the defaults compiled into the program
// First check properties from external file
if(!useDefaults)
{
// Check external list
PropsList::const_iterator iter = myExternalProps.find(md5);
if(iter != myExternalProps.end())
{
properties = iter->second;
found = true;
}
else // Search temp list
{
iter = myTempProps.find(md5);
if(iter != myTempProps.end())
{
properties = iter->second;
found = true;
}
}
}
// Otherwise, search the internal database using binary search
if(!found)
{
int low = 0, high = DEF_PROPS_SIZE - 1;
while(low <= high)
{
int i = (low + high) / 2;
int cmp = strncmp(md5.c_str(), DefProps[i][Cartridge_MD5], 32);
if(cmp == 0) // found it
{
for(int p = 0; p < LastPropType; ++p)
if(DefProps[i][p][0] != 0)
properties.set((PropertyType)p, DefProps[i][p]);
found = true;
break;
}
else if(cmp < 0)
high = i - 1; // look at lower range
else
low = i + 1; // look at upper range
}
}
return found;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PropertiesSet::insert(const Properties& properties, bool save)
{
// 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,
// as the properties are built-in)
// If an item does exist, it will be removed and insertion done again
// This shouldn't be a speed issue, as insertions will only fail with
// duplicates when you're changing the current ROM properties, which
// most people tend not to do
// Since the PropSet is keyed by md5, we can't insert without a valid one
const string& md5 = properties.get(Cartridge_MD5);
if(md5 == "")
return;
// The status of 'save' determines which list to save to
PropsList& list = save ? myExternalProps : myTempProps;
pair<PropsList::iterator,bool> ret;
ret = list.insert(make_pair(md5, properties));
if(ret.second == false)
{
// Remove old item and insert again
list.erase(ret.first);
list.insert(make_pair(md5, properties));
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PropertiesSet::removeMD5(const string& md5)
{
// We only remove from the external list
myExternalProps.erase(md5);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PropertiesSet::print() const
{
// We only look at the external properties and the built-in ones;
// the temp properties are ignored
// Also, any properties entries in the external file override the built-in
// ones
// The easiest way to merge the lists is to create another temporary one
// This isn't fast, but I suspect this method isn't used too often (or at all)
PropsList list;
// First insert all external props
list = myExternalProps;
// Now insert all the built-in ones
// Note that if we try to insert a duplicate, the insertion will fail
// This is fine, since a duplicate in the built-in list means it should
// be overrided anyway (and insertion shouldn't be done)
Properties properties;
for(int i = 0; i < DEF_PROPS_SIZE; ++i)
{
properties.setDefaults();
for(int p = 0; p < LastPropType; ++p)
if(DefProps[i][p][0] != 0)
properties.set((PropertyType)p, DefProps[i][p]);
list.insert(make_pair(DefProps[i][Cartridge_MD5], properties));
}
// Now, print the resulting list
Properties::printHeader();
for(PropsList::const_iterator i = list.begin(); i != list.end(); ++i)
i->second.print();
}