mirror of https://github.com/stella-emu/stella.git
212 lines
6.6 KiB
C++
212 lines
6.6 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-2021 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 <map>
|
|
|
|
#include "bspf.hxx"
|
|
#include "FSNode.hxx"
|
|
#include "Logger.hxx"
|
|
#include "DefProps.hxx"
|
|
#include "Props.hxx"
|
|
#include "PropsSet.hxx"
|
|
#include "repository/CompositeKeyValueRepositoryNoop.hxx"
|
|
#include "repository/KeyValueRepositoryPropertyFile.hxx"
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
PropertiesSet::PropertiesSet() : myRepository{make_shared<CompositeKeyValueRepositoryNoop>()} {}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void PropertiesSet::setRepository(shared_ptr<CompositeKeyValueRepository> repository)
|
|
{
|
|
myRepository = repository;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
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)
|
|
{
|
|
if (myRepository->has(md5)) {
|
|
properties.load(*myRepository->get(md5));
|
|
|
|
found = true;
|
|
}
|
|
else // Search temp list
|
|
{
|
|
auto tmp = myTempProps.find(md5);
|
|
if(tmp != myTempProps.end())
|
|
{
|
|
properties = tmp->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 = BSPF::compareIgnoreCase(md5,
|
|
DefProps[i][static_cast<uInt8>(PropType::Cart_MD5)]);
|
|
|
|
if(cmp == 0) // found it
|
|
{
|
|
for(uInt8 p = 0; p < static_cast<uInt8>(PropType::NumTypes); ++p)
|
|
if(DefProps[i][p][0] != 0)
|
|
properties.set(PropType(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)
|
|
{
|
|
// 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,
|
|
// 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(PropType::Cart_MD5);
|
|
if(md5 == "")
|
|
return;
|
|
|
|
// Make sure the exact entry isn't already in any list
|
|
Properties defaultProps;
|
|
if(getMD5(md5, defaultProps, false) && defaultProps == properties)
|
|
return;
|
|
else if(getMD5(md5, defaultProps, true) && defaultProps == properties)
|
|
{
|
|
cerr << "DELETE" << endl << std::flush;
|
|
myRepository->remove(md5);
|
|
return;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void PropertiesSet::loadPerROM(const FilesystemNode& rom, const string& md5)
|
|
{
|
|
Properties props;
|
|
|
|
// Handle ROM properties, do some error checking
|
|
// Only add to the database when necessary
|
|
bool toInsert = false;
|
|
|
|
// 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()) {
|
|
KeyValueRepositoryPropertyFile repo(propsNode);
|
|
props.load(repo);
|
|
|
|
insert(props, false);
|
|
}
|
|
|
|
// Next, make sure we have a valid md5 and name
|
|
if(!getMD5(md5, props))
|
|
{
|
|
props.set(PropType::Cart_MD5, md5);
|
|
toInsert = true;
|
|
}
|
|
if(toInsert || props.get(PropType::Cart_Name) == EmptyString)
|
|
{
|
|
props.set(PropType::Cart_Name, rom.getNameWithExt(""));
|
|
toInsert = true;
|
|
}
|
|
|
|
// Finally, insert properties if any info was missing
|
|
if(toInsert)
|
|
insert(props, false);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
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)
|
|
|
|
// First insert all external props
|
|
PropsList 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(uInt32 i = 0; i < DEF_PROPS_SIZE; ++i)
|
|
{
|
|
properties.setDefaults();
|
|
for(uInt8 p = 0; p < static_cast<uInt8>(PropType::NumTypes); ++p)
|
|
if(DefProps[i][p][0] != 0)
|
|
properties.set(PropType(p), DefProps[i][p]);
|
|
|
|
list.emplace(DefProps[i][static_cast<uInt8>(PropType::Cart_MD5)], properties);
|
|
}
|
|
|
|
// Now, print the resulting list
|
|
Properties::printHeader();
|
|
for(const auto& i: list)
|
|
i.second.print();
|
|
}
|