mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
373 lines
8.1 KiB
C++
373 lines
8.1 KiB
C++
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Nestopia - NES/Famicom emulator written in C++
|
|
//
|
|
// Copyright (C) 2003-2008 Martin Freij
|
|
//
|
|
// This file is part of Nestopia.
|
|
//
|
|
// Nestopia is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Nestopia is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Nestopia; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <map>
|
|
#include "NstIoLog.hpp"
|
|
#include "NstIoStream.hpp"
|
|
#include "NstWindowUser.hpp"
|
|
#include "NstApplicationInstance.hpp"
|
|
#include "../core/NstXml.hpp"
|
|
|
|
namespace Nestopia
|
|
{
|
|
namespace Application
|
|
{
|
|
struct Configuration::Node
|
|
{
|
|
Node();
|
|
|
|
typedef Nes::Core::Xml Xml;
|
|
|
|
Node& Get(const HeapString&);
|
|
const Node* Find(cstring) const;
|
|
|
|
void Load(Xml::Node);
|
|
void Save(Xml::Node,wcstring=NULL) const;
|
|
void CheckReference(wcstring) const;
|
|
|
|
typedef HeapString Key;
|
|
typedef std::multimap<Key,Node> Map;
|
|
|
|
Map map;
|
|
HeapString string;
|
|
|
|
struct
|
|
{
|
|
Map* map;
|
|
Map::iterator item;
|
|
} parent;
|
|
|
|
mutable bool referenced;
|
|
};
|
|
|
|
Configuration::Configuration()
|
|
: root(*new Node), save(false)
|
|
{
|
|
//const Path path( Instance::GetExePath(L"nestopia.xml") );
|
|
const Path path( Instance::GetConfigPath(L"nestopia.xml") ); //bg
|
|
|
|
if (path.FileExists())
|
|
{
|
|
try
|
|
{
|
|
Nes::Core::Xml xml;
|
|
|
|
{
|
|
Io::Stream::In file( path );
|
|
xml.Read( file );
|
|
}
|
|
|
|
if (!xml.GetRoot().IsType( L"configuration" ))
|
|
throw 1;
|
|
|
|
if (HeapString(Instance::GetVersion()) != xml.GetRoot().GetAttribute(L"version").GetValue())
|
|
Window::User::Warn( L"The configuration file is old and may not work reliably with this version of Nestopia!", L"Configuration file version conflict!" );
|
|
|
|
root.Load( xml.GetRoot().GetFirstChild() );
|
|
}
|
|
catch (...)
|
|
{
|
|
Reset( false );
|
|
Window::User::Warn( L"Configuration file load error! Default settings will be used!" );
|
|
}
|
|
}
|
|
|
|
if (wcstring ptr = ::GetCommandLine())
|
|
{
|
|
for (uint quote=0; (*ptr > ' ') || (*ptr && quote); ++ptr)
|
|
{
|
|
if (*ptr == '\"')
|
|
quote ^= 1;
|
|
}
|
|
|
|
while (*ptr && *ptr <= ' ')
|
|
++ptr;
|
|
|
|
if (*ptr)
|
|
{
|
|
if (*ptr != '-')
|
|
{
|
|
wcstring const offset = ptr;
|
|
|
|
for (uint quote=0; (*ptr > ' ') || (*ptr && quote); ++ptr)
|
|
{
|
|
if (*ptr == '\"')
|
|
quote ^= 1;
|
|
}
|
|
|
|
startupFile.Assign( offset, ptr - offset );
|
|
startupFile.Remove( '\"' );
|
|
startupFile.Trim();
|
|
|
|
// Win98/ME/2k fix
|
|
if (startupFile.Length())
|
|
startupFile = Instance::GetLongPath( startupFile.Ptr() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Configuration::~Configuration()
|
|
{
|
|
if (save)
|
|
{
|
|
try
|
|
{
|
|
Nes::Core::Xml xml;
|
|
xml.Create( L"configuration" ).AddAttribute( L"version", HeapString(Instance::GetVersion()).Ptr() );
|
|
root.Save( xml.GetRoot() );
|
|
|
|
//Io::Stream::Out file( Instance::GetExePath(L"nestopia.xml") );
|
|
Io::Stream::Out file( Instance::GetConfigPath(L"nestopia.xml") );//bg
|
|
xml.Write( xml.GetRoot(), file );
|
|
}
|
|
catch (...)
|
|
{
|
|
Window::User::Warn( L"Couldn't save the configuration!" );
|
|
}
|
|
}
|
|
|
|
delete &root;
|
|
}
|
|
|
|
void Configuration::Reset(bool notify)
|
|
{
|
|
if (notify)
|
|
root.CheckReference( L"" );
|
|
|
|
root.map.clear();
|
|
}
|
|
|
|
void Configuration::EnableSaving(bool enable)
|
|
{
|
|
save = enable;
|
|
}
|
|
|
|
const Path& Configuration::GetStartupFile() const
|
|
{
|
|
return startupFile;
|
|
}
|
|
|
|
Configuration::ConstSection Configuration::operator [] (cstring key) const
|
|
{
|
|
return root.Find( key );
|
|
}
|
|
|
|
Configuration::Section Configuration::operator [] (cstring key)
|
|
{
|
|
return &root.Get( key );
|
|
}
|
|
|
|
Configuration::ConstSection Configuration::ConstSection::operator [] (cstring key) const
|
|
{
|
|
return node ? node->Find( key ) : NULL;
|
|
}
|
|
|
|
Configuration::ConstSection Configuration::ConstSection::operator [] (uint i) const
|
|
{
|
|
if (node)
|
|
{
|
|
if (i)
|
|
{
|
|
Node::Map::iterator it( node->parent.item );
|
|
|
|
while (++it != node->parent.map->end() && it->first == node->parent.item->first)
|
|
{
|
|
if (!--i)
|
|
{
|
|
it->second.referenced = true;
|
|
return &it->second;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return *this;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Configuration::Section Configuration::Section::operator [] (cstring key)
|
|
{
|
|
return &node->Get( key );
|
|
}
|
|
|
|
Configuration::Section Configuration::Section::operator [] (uint i)
|
|
{
|
|
if (i)
|
|
{
|
|
Node::Map::iterator it( node->parent.item );
|
|
|
|
while (++it != node->parent.map->end() && it->first == node->parent.item->first)
|
|
{
|
|
if (!--i)
|
|
return &it->second;
|
|
}
|
|
|
|
const Node::Map::value_type item( node->parent.item->first,Node() );
|
|
|
|
do
|
|
{
|
|
it = node->parent.map->insert( it, item );
|
|
it->second.parent.map = node->parent.map;
|
|
it->second.parent.item = it;
|
|
}
|
|
while (--i);
|
|
|
|
return &it->second;
|
|
}
|
|
else
|
|
{
|
|
return *this;
|
|
}
|
|
}
|
|
|
|
HeapString& Configuration::Section::Str()
|
|
{
|
|
return node->string;
|
|
}
|
|
|
|
GenericString Configuration::ConstSection::Str() const
|
|
{
|
|
return node ? GenericString(node->string) : GenericString();
|
|
}
|
|
|
|
ulong Configuration::ConstSection::Int() const
|
|
{
|
|
ulong i;
|
|
return node && (node->string >> i) ? i : 0;
|
|
}
|
|
|
|
ulong Configuration::ConstSection::Int(ulong d) const
|
|
{
|
|
ulong i;
|
|
return node && (node->string >> i) ? i : d;
|
|
}
|
|
|
|
void Configuration::Section::IntProxy::operator = (ulong i)
|
|
{
|
|
node.string << i;
|
|
}
|
|
|
|
void Configuration::Section::YesNoProxy::operator = (bool yes)
|
|
{
|
|
node.string.Assign( yes ? L"yes" : L"no", yes ? 3 : 2 );
|
|
}
|
|
|
|
bool Configuration::ConstSection::Yes() const
|
|
{
|
|
return node && node->string == L"yes";
|
|
}
|
|
|
|
bool Configuration::ConstSection::No() const
|
|
{
|
|
return node && node->string == L"no";
|
|
}
|
|
|
|
Configuration::Node::Node()
|
|
: referenced(false) {}
|
|
|
|
Configuration::Node& Configuration::Node::Get(const HeapString& key)
|
|
{
|
|
Map::iterator it(map.lower_bound( key ));
|
|
|
|
if (it == map.end() || it->first != key)
|
|
{
|
|
it = map.insert( it, Map::value_type(key,Node()) );
|
|
it->second.parent.map = ↦
|
|
it->second.parent.item = it;
|
|
}
|
|
|
|
return it->second;
|
|
}
|
|
|
|
const Configuration::Node* Configuration::Node::Find(cstring key) const
|
|
{
|
|
Map::const_iterator it(map.find( key ));
|
|
|
|
if (it != map.end())
|
|
{
|
|
it->second.referenced = true;
|
|
return &it->second;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void Configuration::Node::Load(Xml::Node xmlNode)
|
|
{
|
|
do
|
|
{
|
|
Map::iterator it(map.insert( Map::value_type(xmlNode.GetType(),Node()) ));
|
|
|
|
it->second.parent.map = ↦
|
|
it->second.parent.item = it;
|
|
|
|
if (const Xml::Node xmlChild=xmlNode.GetFirstChild())
|
|
it->second.Load( xmlChild );
|
|
else
|
|
it->second.string = xmlNode.GetValue();
|
|
|
|
xmlNode = xmlNode.GetNextSibling();
|
|
}
|
|
while (xmlNode);
|
|
}
|
|
|
|
void Configuration::Node::Save(Xml::Node node,wcstring const key) const
|
|
{
|
|
NST_ASSERT( node && (!key || *key) );
|
|
|
|
if (map.empty())
|
|
{
|
|
if (key)
|
|
node.AddChild( key, string.Ptr() );
|
|
}
|
|
else
|
|
{
|
|
if (key)
|
|
node = node.AddChild( key );
|
|
|
|
for (Map::const_iterator it(map.begin()), end(map.end()); it != end; ++it)
|
|
it->second.Save( node, it->first.Ptr() );
|
|
}
|
|
}
|
|
|
|
void Configuration::Node::CheckReference(wcstring const key) const
|
|
{
|
|
NST_ASSERT( key );
|
|
|
|
if (map.empty())
|
|
{
|
|
if (*key && !referenced)
|
|
Io::Log() << "Configuration: warning, unused/invalid parameter: \"" << key << "\"\r\n";
|
|
}
|
|
else for (Map::const_iterator it(map.begin()), end(map.end()); it != end; ++it)
|
|
{
|
|
it->second.CheckReference( it->first.Ptr() );
|
|
}
|
|
}
|
|
}
|
|
}
|