1 //////////////////////////////////////////////////////////////////////////////////////// 2 // 3 // Nestopia - NES/Famicom emulator written in C++ 4 // 5 // Copyright (C) 2003-2008 Martin Freij 6 // 7 // This file is part of Nestopia. 8 // 9 // Nestopia is free software; you can redistribute it and/or modify 10 // it under the terms of the GNU General Public License as published by 11 // the Free Software Foundation; either version 2 of the License, or 12 // (at your option) any later version. 13 // 14 // Nestopia is distributed in the hope that it will be useful, 15 // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 // GNU General Public License for more details. 18 // 19 // You should have received a copy of the GNU General Public License 20 // along with Nestopia; if not, write to the Free Software 21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 // 23 //////////////////////////////////////////////////////////////////////////////////////// 24 25 #include <map> 26 #include "NstIoLog.hpp" 27 #include "NstIoStream.hpp" 28 #include "NstWindowUser.hpp" 29 #include "NstApplicationInstance.hpp" 30 #include "../core/NstXml.hpp" 31 32 namespace Nestopia 33 { 34 namespace Application 35 { 36 struct Configuration::Node 37 { 38 Node(); 39 40 typedef Nes::Core::Xml Xml; 41 42 Node& Get(const HeapString&); 43 const Node* Find(cstring) const; 44 45 void Load(Xml::Node); 46 void Save(Xml::Node,wcstring=NULL) const; 47 void CheckReference(wcstring) const; 48 49 typedef HeapString Key; 50 typedef std::multimap<Key,Node> Map; 51 52 Map map; 53 HeapString string; 54 55 struct 56 { 57 Map* map; 58 Map::iterator item; 59 } parent; 60 61 mutable bool referenced; 62 }; 63 Configuration()64 Configuration::Configuration() 65 : root(*new Node), save(false) 66 { 67 //const Path path( Instance::GetExePath(L"nestopia.xml") ); 68 const Path path( Instance::GetConfigPath(L"nestopia.xml") ); //bg 69 70 if (path.FileExists()) 71 { 72 try 73 { 74 Nes::Core::Xml xml; 75 76 { 77 Io::Stream::In file( path ); 78 xml.Read( file ); 79 } 80 81 if (!xml.GetRoot().IsType( L"configuration" )) 82 throw 1; 83 84 if (HeapString(Instance::GetVersion()) != xml.GetRoot().GetAttribute(L"version").GetValue()) 85 Window::User::Warn( L"The configuration file is old and may not work reliably with this version of Nestopia!", L"Configuration file version conflict!" ); 86 87 root.Load( xml.GetRoot().GetFirstChild() ); 88 } 89 catch (...) 90 { 91 Reset( false ); 92 Window::User::Warn( L"Configuration file load error! Default settings will be used!" ); 93 } 94 } 95 96 if (wcstring ptr = ::GetCommandLine()) 97 { 98 for (uint quote=0; (*ptr > ' ') || (*ptr && quote); ++ptr) 99 { 100 if (*ptr == '\"') 101 quote ^= 1; 102 } 103 104 while (*ptr && *ptr <= ' ') 105 ++ptr; 106 107 if (*ptr) 108 { 109 if (*ptr != '-') 110 { 111 wcstring const offset = ptr; 112 113 for (uint quote=0; (*ptr > ' ') || (*ptr && quote); ++ptr) 114 { 115 if (*ptr == '\"') 116 quote ^= 1; 117 } 118 119 startupFile.Assign( offset, ptr - offset ); 120 startupFile.Remove( '\"' ); 121 startupFile.Trim(); 122 123 // Win98/ME/2k fix 124 if (startupFile.Length()) 125 startupFile = Instance::GetLongPath( startupFile.Ptr() ); 126 } 127 } 128 } 129 } 130 ~Configuration()131 Configuration::~Configuration() 132 { 133 if (save) 134 { 135 try 136 { 137 Nes::Core::Xml xml; 138 xml.Create( L"configuration" ).AddAttribute( L"version", HeapString(Instance::GetVersion()).Ptr() ); 139 root.Save( xml.GetRoot() ); 140 141 //Io::Stream::Out file( Instance::GetExePath(L"nestopia.xml") ); 142 Io::Stream::Out file( Instance::GetConfigPath(L"nestopia.xml") );//bg 143 xml.Write( xml.GetRoot(), file ); 144 } 145 catch (...) 146 { 147 Window::User::Warn( L"Couldn't save the configuration!" ); 148 } 149 } 150 151 delete &root; 152 } 153 Reset(bool notify)154 void Configuration::Reset(bool notify) 155 { 156 if (notify) 157 root.CheckReference( L"" ); 158 159 root.map.clear(); 160 } 161 EnableSaving(bool enable)162 void Configuration::EnableSaving(bool enable) 163 { 164 save = enable; 165 } 166 GetStartupFile() const167 const Path& Configuration::GetStartupFile() const 168 { 169 return startupFile; 170 } 171 operator [](cstring key) const172 Configuration::ConstSection Configuration::operator [] (cstring key) const 173 { 174 return root.Find( key ); 175 } 176 operator [](cstring key)177 Configuration::Section Configuration::operator [] (cstring key) 178 { 179 return &root.Get( key ); 180 } 181 operator [](cstring key) const182 Configuration::ConstSection Configuration::ConstSection::operator [] (cstring key) const 183 { 184 return node ? node->Find( key ) : NULL; 185 } 186 operator [](uint i) const187 Configuration::ConstSection Configuration::ConstSection::operator [] (uint i) const 188 { 189 if (node) 190 { 191 if (i) 192 { 193 Node::Map::iterator it( node->parent.item ); 194 195 while (++it != node->parent.map->end() && it->first == node->parent.item->first) 196 { 197 if (!--i) 198 { 199 it->second.referenced = true; 200 return &it->second; 201 } 202 } 203 } 204 else 205 { 206 return *this; 207 } 208 } 209 210 return NULL; 211 } 212 operator [](cstring key)213 Configuration::Section Configuration::Section::operator [] (cstring key) 214 { 215 return &node->Get( key ); 216 } 217 operator [](uint i)218 Configuration::Section Configuration::Section::operator [] (uint i) 219 { 220 if (i) 221 { 222 Node::Map::iterator it( node->parent.item ); 223 224 while (++it != node->parent.map->end() && it->first == node->parent.item->first) 225 { 226 if (!--i) 227 return &it->second; 228 } 229 230 const Node::Map::value_type item( node->parent.item->first,Node() ); 231 232 do 233 { 234 it = node->parent.map->insert( it, item ); 235 it->second.parent.map = node->parent.map; 236 it->second.parent.item = it; 237 } 238 while (--i); 239 240 return &it->second; 241 } 242 else 243 { 244 return *this; 245 } 246 } 247 Str()248 HeapString& Configuration::Section::Str() 249 { 250 return node->string; 251 } 252 Str() const253 GenericString Configuration::ConstSection::Str() const 254 { 255 return node ? GenericString(node->string) : GenericString(); 256 } 257 Int() const258 ulong Configuration::ConstSection::Int() const 259 { 260 ulong i; 261 return node && (node->string >> i) ? i : 0; 262 } 263 Int(ulong d) const264 ulong Configuration::ConstSection::Int(ulong d) const 265 { 266 ulong i; 267 return node && (node->string >> i) ? i : d; 268 } 269 operator =(ulong i)270 void Configuration::Section::IntProxy::operator = (ulong i) 271 { 272 node.string << i; 273 } 274 operator =(bool yes)275 void Configuration::Section::YesNoProxy::operator = (bool yes) 276 { 277 node.string.Assign( yes ? L"yes" : L"no", yes ? 3 : 2 ); 278 } 279 Yes() const280 bool Configuration::ConstSection::Yes() const 281 { 282 return node && node->string == L"yes"; 283 } 284 No() const285 bool Configuration::ConstSection::No() const 286 { 287 return node && node->string == L"no"; 288 } 289 Node()290 Configuration::Node::Node() 291 : referenced(false) {} 292 Get(const HeapString & key)293 Configuration::Node& Configuration::Node::Get(const HeapString& key) 294 { 295 Map::iterator it(map.lower_bound( key )); 296 297 if (it == map.end() || it->first != key) 298 { 299 it = map.insert( it, Map::value_type(key,Node()) ); 300 it->second.parent.map = ↦ 301 it->second.parent.item = it; 302 } 303 304 return it->second; 305 } 306 Find(cstring key) const307 const Configuration::Node* Configuration::Node::Find(cstring key) const 308 { 309 Map::const_iterator it(map.find( key )); 310 311 if (it != map.end()) 312 { 313 it->second.referenced = true; 314 return &it->second; 315 } 316 317 return NULL; 318 } 319 Load(Xml::Node xmlNode)320 void Configuration::Node::Load(Xml::Node xmlNode) 321 { 322 do 323 { 324 Map::iterator it(map.insert( Map::value_type(xmlNode.GetType(),Node()) )); 325 326 it->second.parent.map = ↦ 327 it->second.parent.item = it; 328 329 if (const Xml::Node xmlChild=xmlNode.GetFirstChild()) 330 it->second.Load( xmlChild ); 331 else 332 it->second.string = xmlNode.GetValue(); 333 334 xmlNode = xmlNode.GetNextSibling(); 335 } 336 while (xmlNode); 337 } 338 Save(Xml::Node node,wcstring const key) const339 void Configuration::Node::Save(Xml::Node node,wcstring const key) const 340 { 341 NST_ASSERT( node && (!key || *key) ); 342 343 if (map.empty()) 344 { 345 if (key) 346 node.AddChild( key, string.Ptr() ); 347 } 348 else 349 { 350 if (key) 351 node = node.AddChild( key ); 352 353 for (Map::const_iterator it(map.begin()), end(map.end()); it != end; ++it) 354 it->second.Save( node, it->first.Ptr() ); 355 } 356 } 357 CheckReference(wcstring const key) const358 void Configuration::Node::CheckReference(wcstring const key) const 359 { 360 NST_ASSERT( key ); 361 362 if (map.empty()) 363 { 364 if (*key && !referenced) 365 Io::Log() << "Configuration: warning, unused/invalid parameter: \"" << key << "\"\r\n"; 366 } 367 else for (Map::const_iterator it(map.begin()), end(map.end()); it != end; ++it) 368 { 369 it->second.CheckReference( it->first.Ptr() ); 370 } 371 } 372 } 373 } 374