nestopia/source/win32/NstApplicationConfiguration.cpp

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