1 #include "optionswrapper.h"
2
3 #include "unitsync.h"
4 #include <lslutils/conversion.h>
5 #include <lslutils/debug.h>
6 #include <lslutils/logging.h>
7
8 #include <stdexcept>
9 #include <clocale>
10
11 namespace LSL {
12
OptionsWrapper()13 OptionsWrapper::OptionsWrapper()
14 {
15 unLoadOptions();
16 loadOptions( EngineOption, "" );
17 loadOptions( PrivateOptions,"" );
18 }
19
unLoadOptions()20 void OptionsWrapper::unLoadOptions()
21 {
22 for (int i = 0; i < LastOption; ++i)
23 {
24 unLoadOptions( (GameOption)i );
25 }
26 }
27
unLoadOptions(GameOption i)28 void OptionsWrapper::unLoadOptions(GameOption i)
29 {
30 GameOptions empty;
31 m_opts[i] = empty;
32
33 mmSectionTreeMap::iterator itor = m_sections.find( i );
34 if ( itor != m_sections.end() ) m_sections.erase( itor );
35 }
36
~OptionsWrapper()37 OptionsWrapper::~OptionsWrapper()
38 {
39 }
40
loadMapOptions(const std::string & mapname)41 bool OptionsWrapper::loadMapOptions( const std::string& mapname)
42 {
43 return loadOptions(MapOption,mapname);
44 }
45
GetSingleOptionType(const std::string & key) const46 Enum::OptionType OptionsWrapper::GetSingleOptionType ( const std::string& key) const
47 {
48 Enum::OptionType type = Enum::opt_undefined;
49 for ( int g = 0; g < LastOption; g++ )
50 {
51 if (keyExists(key,(GameOption)g,false,type))
52 return type;
53 }
54 return Enum::opt_undefined;
55 }
56
loadAIOptions(const std::string & modname,int aiindex,const std::string & ainame)57 bool OptionsWrapper::loadAIOptions( const std::string& modname, int aiindex,const std::string& ainame )
58 {
59 int mapindex = m_ais_indexes[ainame];
60 if ( mapindex == 0 ) mapindex = m_ais_indexes.size() + LastOption;
61 m_ais_indexes[ainame] = mapindex;
62 unLoadOptions((GameOption)mapindex);
63 try
64 {
65 GameOptions opt = usync().GetAIOptions( modname, aiindex );
66 ParseSectionMap( m_sections[mapindex], opt.section_map );
67 m_opts[mapindex] = opt;
68 } catch (...)
69 {
70 return false;
71 }
72 return true;
73 }
74
GetAIOptionIndex(const std::string & nick) const75 int OptionsWrapper::GetAIOptionIndex( const std::string& nick ) const
76 {
77 std::map<std::string,int>::const_iterator itor = m_ais_indexes.find(nick);
78 int pos = -1;
79 if ( itor != m_ais_indexes.end() ) pos = itor->second;
80 return pos;
81 }
82
loadOptions(GameOption modmapFlag,const std::string & name,const std::string & extra_filename)83 bool OptionsWrapper::loadOptions( GameOption modmapFlag, const std::string& name, const std::string& extra_filename )
84 {
85 unLoadOptions(modmapFlag);
86 GameOptions opt;
87 switch (modmapFlag)
88 {
89 default:
90 break;
91 case MapOption:
92 try
93 {
94 opt = usync().GetMapOptions(name);
95 ParseSectionMap( m_sections[modmapFlag], opt.section_map );
96 }
97 catch(...)
98 {
99 LslError("Could not load map options");
100 return false;
101 }
102 break;
103
104 case ModOption:
105 try
106 {
107 opt = usync().GetModOptions(name);
108 ParseSectionMap( m_sections[modmapFlag], opt.section_map );
109 }
110 catch(...)
111 {
112 LslError("Could not load game options");
113 return false;
114 }
115 break;
116
117 case EngineOption: {
118 //TODO Fixed,random and so forth are intls
119 mmOptionList startpos( "Start Position Type", "startpostype", "How players will select where to be spawned in the map\n0: fixed map positions\n1: random map positions\n2: choose in game\n3: choose in the lobby before starting", "0" );
120 startpos.addItem( "0", "Fixed", "Use the start positions defined in the map, the positions will be assigned incrementally from the team with lowest number to highest");
121 startpos.addItem( "1", "Random", "Use the start positions defined in the map, the positions will be assigned randomly");
122 startpos.addItem( "2", "Choose in-game", "Players will be able to pick their own starting point right before the game starts, optionally limited by a bounding box defined by the host");
123 startpos.addItem( "3", "Choose before game", "The host will place each player's start position in the map preview before the game is launched");
124 opt.list_map["startpostype"] = startpos;
125 break;
126 }
127
128 case PrivateOptions: {
129 opt.string_map["restrictions"] = mmOptionString("List of restricted units", "restrictedunits", "Units in this list won't be available in game", "", 0 ); // tab separated list
130 opt.string_map["mapname"] = mmOptionString("Map name", "mapname", "Map name", "", 0 );
131 break;
132 }
133
134 case ModCustomizations: {
135 try {
136 opt = usync().GetModCustomizations( name );
137 }
138 catch(...) {
139 LslError("Could not load mod customizations");
140 return false;
141 }
142 break;
143 }
144
145 case SkirmishOptions: {
146 try {
147 opt = usync().GetSkirmishOptions( name, extra_filename );
148 }
149 catch(...) {
150 LslError("Could not load skirmish options");
151 return false;
152 }
153 break;
154 }
155 }
156 m_opts[modmapFlag] = opt;
157 return true;
158 }
159
GetSection(const std::string & key) const160 OptionsWrapper::GameOption OptionsWrapper::GetSection( const std::string& key ) const
161 {
162 GameOption ret = LastOption;
163 bool found = false;
164 for ( int flag = 0; flag < PrivateOptions; flag++ )
165 {
166 Enum::OptionType optType = Enum::opt_undefined;
167 found = keyExists( key, (GameOption)flag, false, optType );
168 if ( found )
169 {
170 ret = (GameOption)flag;
171 break;
172 }
173 }
174 return ret;
175 }
176
keyExists(const std::string & key) const177 bool OptionsWrapper::keyExists( const std::string& key ) const
178 {
179 bool found = false;
180 for ( int flag = 0; flag < PrivateOptions; flag++ )
181 {
182 Enum::OptionType optType = Enum::opt_undefined;
183 found = keyExists( key, (GameOption)flag, false, optType );
184 if ( found ) break;
185 }
186 return found;
187 }
188
keyExists(const std::string & key,const GameOption modmapFlag,bool showError,Enum::OptionType & optType) const189 bool OptionsWrapper::keyExists( const std::string& key, const GameOption modmapFlag, bool showError, Enum::OptionType& optType ) const
190 {
191 //std::string duplicateKeyError = "Please contact the game's author and tell him\nto use unique keys in his ModOptions.lua";
192 bool exists = false;
193 optType = Enum::opt_undefined;
194 GameOptionsMap::const_iterator optIt = m_opts.find((int)modmapFlag);
195 if ( optIt == m_opts.end() )
196 return false;
197 const GameOptions& gameoptions = optIt->second;
198 if ( gameoptions.list_map.find(key) != gameoptions.list_map.end())
199 {
200 optType = Enum::opt_list;
201 exists = true;
202 }
203 else if ( gameoptions.string_map.find(key) != gameoptions.string_map.end())
204 {
205 optType = Enum::opt_string;
206 exists = true;
207 }
208 else if ( gameoptions.bool_map.find(key) != gameoptions.bool_map.end())
209 {
210 optType = Enum::opt_bool;
211 exists = true;
212 }
213 else if ( gameoptions.float_map.find(key)!= gameoptions.float_map.end())
214 {
215 optType = Enum::opt_float;
216 exists = true;
217 }
218 else if ( gameoptions.section_map.find(key)!= gameoptions.section_map.end())
219 {
220 optType = Enum::opt_section;
221 exists = true;
222 }
223 if (exists && showError)
224 {
225 //TODO STH
226 // customMessageBoxNoModal(SL_MAIN_ICON,duplicateKeyError, "Mod/map option error",wxOK);
227 LslWarning("duplicate key in mapmodoptions");
228 return false;
229 }
230 else if ( exists && !showError )
231 {
232 return true;
233 }
234 else
235 return false;
236 }
237
setOptions(stringPairVec * options,GameOption modmapFlag)238 bool OptionsWrapper::setOptions(stringPairVec* options, GameOption modmapFlag)
239 {
240 for (stringPairVec::const_iterator it = options->begin(); it != options->end(); ++it)
241 {
242 std::string key = it->first;
243 std::string value = it->second;
244
245 //we don't want to add a key that doesn't already exists
246 Enum::OptionType optType = Enum::opt_undefined;
247 if(!keyExists(key,modmapFlag,false,optType))
248 return false;
249 else
250 {
251 if ( !setSingleOptionTypeSwitch( key, value, modmapFlag, optType) )
252 return false;
253 }
254 }
255 return true;
256 }
257
getOptions(GameOption modmapFlag) const258 OptionsWrapper::stringTripleVec OptionsWrapper::getOptions( GameOption modmapFlag) const
259 {
260 stringTripleVec list;
261 GameOptionsMapCIter optIt = m_opts.find((int)modmapFlag);
262 if ( optIt != m_opts.end() ) {
263 const GameOptions& gameoptions = optIt->second;
264 for (OptionMapBoolConstIter it = gameoptions.bool_map.begin(); it != gameoptions.bool_map.end(); ++it) {
265 list.push_back( stringTriple( (*it).first, stringPair ( it->second.name , Util::ToString(it->second.value) ) ) );
266 }
267
268 for (OptionMapStringConstIter it = gameoptions.string_map.begin(); it != gameoptions.string_map.end(); ++it) {
269 list.push_back( stringTriple( (*it).first, stringPair ( it->second.name, it->second.value) ) );
270 }
271
272 for (OptionMapFloatConstIter it = gameoptions.float_map.begin(); it != gameoptions.float_map.end(); ++it) {
273 list.push_back( stringTriple( (*it).first, stringPair ( it->second.name, Util::ToString(it->second.value) ) ) );
274 }
275
276 for (OptionMapListConstIter it = gameoptions.list_map.begin(); it != gameoptions.list_map.end(); ++it) {
277 list.push_back( stringTriple( (*it).first, stringPair ( it->second.name, it->second.value ) ) );
278 }
279 }
280 return list;
281 }
282
getOptionsMap(GameOption modmapFlag) const283 std::map<std::string,std::string> LSL::OptionsWrapper::getOptionsMap( GameOption modmapFlag ) const
284 {
285 std::map<std::string,std::string> map;
286 GameOptionsMapCIter optIt = m_opts.find((int)modmapFlag);
287 if ( optIt != m_opts.end() ) {
288 const GameOptions& gameoptions = optIt->second;
289 for (OptionMapBoolConstIter it = gameoptions.bool_map.begin(); it != gameoptions.bool_map.end(); ++it) {
290 map[it->first] = Util::ToString(it->second.value);
291 }
292
293 for (OptionMapStringConstIter it = gameoptions.string_map.begin(); it != gameoptions.string_map.end(); ++it) {
294 map[it->first] = it->second.value;
295 }
296
297 for (OptionMapFloatConstIter it = gameoptions.float_map.begin(); it != gameoptions.float_map.end(); ++it) {
298 map[it->first] = Util::ToString(it->second.value);
299 }
300
301 for (OptionMapListConstIter it = gameoptions.list_map.begin(); it != gameoptions.list_map.end(); ++it) {
302 map[it->first] = it->second.value;
303 }
304 }
305 return map;
306 }
307
setSingleOption(const std::string & key,const std::string & value,GameOption modmapFlag)308 bool OptionsWrapper::setSingleOption( const std::string& key, const std::string& value,GameOption modmapFlag)
309 {
310 Enum::OptionType optType = Enum::opt_undefined;
311 keyExists( key, modmapFlag, false, optType );
312 return setSingleOptionTypeSwitch(key,value,modmapFlag,optType);
313 }
314
setSingleOption(const std::string & key,const std::string & value)315 bool OptionsWrapper::setSingleOption( const std::string& key, const std::string& value )
316 {
317 Enum::OptionType optType = Enum::opt_undefined;
318 if (keyExists(key,ModOption,false,optType))
319 return setSingleOptionTypeSwitch(key,value,ModOption,optType);
320 else if (keyExists(key,MapOption,false,optType))
321 return setSingleOptionTypeSwitch(key,value,MapOption,optType);
322 else
323 return false;
324 }
325
getSingleValue(const std::string & key) const326 std::string OptionsWrapper::getSingleValue( const std::string& key) const
327 {
328 for ( int g = 0; g < LastOption; g++ )
329 {
330 const std::string tmp = getSingleValue(key, (GameOption)g);
331 if (tmp != "")
332 return tmp;
333 }
334 return "";
335 }
336 template < class MapType >
GetItem(const MapType & map,const typename MapType::key_type & key)337 static inline typename MapType::mapped_type GetItem( const MapType& map, const typename MapType::key_type& key )
338 {
339 typename MapType::const_iterator mapIt = map.find(key);
340 if ( mapIt != map.end() )
341 return mapIt->second;
342 else
343 return typename MapType::mapped_type();
344 }
345
getSingleValue(const std::string & key,GameOption modmapFlag) const346 std::string OptionsWrapper::getSingleValue( const std::string& key, GameOption modmapFlag) const
347 {
348 Enum::OptionType optType = Enum::opt_undefined;
349
350 if ( keyExists(key,modmapFlag,false,optType) )
351 {
352 GameOptionsMapCIter optIt = m_opts.find((int)modmapFlag);
353 if ( optIt == m_opts.end() )
354 return "";
355
356 const GameOptions& tempOpt = optIt->second;
357 switch (optType)
358 {
359 case Enum::opt_float:
360 return Util::ToString( GetItem( tempOpt.float_map, key ).value );
361 case Enum::opt_bool:
362 return Util::ToString( GetItem( tempOpt.bool_map, key ).value );
363 case Enum::opt_string:
364 return GetItem( tempOpt.string_map, key ).value ;
365 case Enum::opt_list:
366 return GetItem( tempOpt.list_map, key ).value;
367 case Enum::opt_undefined:
368 default:
369 return "";
370 }
371 }
372 return "";
373 }
374
getDefaultValue(const std::string & key,GameOption modmapFlag) const375 std::string OptionsWrapper::getDefaultValue( const std::string& key, GameOption modmapFlag) const
376 {
377 Enum::OptionType optType = Enum::opt_undefined;
378 std::string ret;
379 if ( keyExists(key,modmapFlag,false,optType) )
380 {
381 //purposefully create a copy, no better idea
382 GameOptionsMapCIter optIt = m_opts.find((int)modmapFlag);
383 if ( optIt == m_opts.end() )
384 return "";
385
386 const GameOptions& tempOpt = optIt->second;
387 switch ( optType )
388 {
389 {
390 case Enum::opt_bool:
391 ret = Util::ToString( GetItem( tempOpt.bool_map, key ).def );
392 break;
393 }
394 case Enum::opt_float:
395 {
396 ret = Util::ToString( GetItem( tempOpt.float_map, key ).def );
397 break;
398 }
399 case Enum::opt_string:
400 {
401 ret = GetItem( tempOpt.string_map, key ).def;
402 break;
403 }
404 case Enum::opt_list:
405 {
406 ret = GetItem( tempOpt.list_map, key ).def;
407 break;
408 }
409 default:
410 {
411 break;
412 }
413 }
414 }
415 return ret;
416 }
417
setSingleOptionTypeSwitch(const std::string & key,const std::string & value,GameOption modmapFlag,Enum::OptionType optType)418 bool LSL::OptionsWrapper::setSingleOptionTypeSwitch( const std::string& key, const std::string& value, GameOption modmapFlag, Enum::OptionType optType)
419 {
420 GameOptions& gameoptions = m_opts[modmapFlag];
421 switch (optType)
422 {
423 case Enum::opt_float :
424 {
425 //temp set to C locale cause we get '.' as decimal seperator over the net
426 const char* old_locale = std::setlocale(LC_NUMERIC, "C");
427 //test if min < val < max
428 const double d_val = Util::FromString<double>( value );
429 std::setlocale(LC_NUMERIC, old_locale);
430 if( d_val < (gameoptions.float_map)[key].min || d_val > (gameoptions.float_map)[key].max )
431 {
432 LslDebug("received number option exceeds boundaries");
433 return false;
434 }
435 else
436 (gameoptions.float_map)[key].value = d_val;
437 break;
438 }
439 case Enum::opt_bool :
440 {
441 const long l_val = Util::FromString<long>( value );
442 if( l_val != 1 && l_val != 0 )
443 {
444 LslDebug("recieved bool option that is neither 0 or 1");
445 return false;
446 }
447 else
448 (gameoptions.bool_map)[key].value = bool(l_val);
449 break;
450 }
451 case Enum::opt_string :
452 {
453 // test if maxlength isn't exceeded
454 unsigned int max_length = (gameoptions.string_map)[key].max_len;
455 if ( ( max_length != 0 ) && ( value.length() > max_length ) )
456 {
457 LslDebug("recieved string option exceeds max_len");
458 return false;
459 }
460 else
461 (gameoptions.string_map)[key].value = value;
462 break;
463 }
464 case Enum::opt_list :
465 {
466 // test if valid value, aka is in list
467 int listitemcount = (gameoptions.list_map)[key].listitems.size();
468 bool valid_string = false;
469 int j = 0;
470 for (; j < listitemcount; ++j)
471 {
472 if ( (gameoptions.list_map)[key].listitems[j].key == value)
473 {
474 valid_string = true;
475 break;
476 }
477 }
478
479 if (valid_string)
480 {
481 //LOOKATME (koshi) if there's a problem with list modoption look here first
482 (gameoptions.list_map)[key].value = (gameoptions.list_map)[key].listitems[j].key;
483 (gameoptions.list_map)[key].cur_choice_index = j;
484 }
485 else
486 {
487 LslDebug("received list option is not valid");
488 return false;
489 }
490 break;
491 }
492 default:
493 return false;
494 }
495 //if we made it here, all is good
496 return true;
497 }
498
GetNameListOptValue(const std::string & key,GameOption flag) const499 std::string OptionsWrapper::GetNameListOptValue( const std::string& key, GameOption flag) const
500 {
501 Enum::OptionType optType;
502 if ( keyExists(key,flag,false, optType) )
503 {
504 if ( optType == Enum::opt_list)
505 {
506 GameOptionsMapCIter optIt = m_opts.find((int)flag);
507 if ( optIt == m_opts.end() )
508 return "";
509
510 GameOptions tempOpt = optIt->second;
511 return ( (tempOpt.list_map)[key].cbx_choices[ (tempOpt.list_map)[key].cur_choice_index ] );
512 }
513 }
514 // at this point retrieval failed
515 return "";
516 }
517
GetNameListOptItemKey(const std::string & optkey,const std::string & itemname,GameOption flag) const518 std::string OptionsWrapper::GetNameListOptItemKey( const std::string& optkey, const std::string& itemname, GameOption flag) const
519 {
520 Enum::OptionType optType;
521 if ( keyExists(optkey,flag,false, optType) )
522 {
523 if ( optType == Enum::opt_list)
524 {
525 GameOptionsMapCIter optIt = m_opts.find((int)flag);
526 if ( optIt == m_opts.end() )
527 return "";
528
529 GameOptions tempOpt = optIt->second;
530 for (ListItemVec::const_iterator it = (tempOpt.list_map)[optkey].listitems.begin(); it != (tempOpt.list_map)[optkey].listitems.end(); ++it)
531 {
532 if (it->name == itemname)
533 return it->key;
534 }
535 }
536 }
537
538 // at this point retrieval failed
539 return "";
540 }
541
MergeOptions(const OptionsWrapper & other,GameOption merge_into)542 bool OptionsWrapper::MergeOptions( const OptionsWrapper& other, GameOption merge_into )
543 {
544 GameOptionsMapCIter other_it = other.m_opts.begin();
545 for ( ; other_it != other.m_opts.end(); ++other_it ) {
546 const GameOptions& other_opts = other_it->second;
547 //const GameOption other_id = (const GameOption)other_it->first; //TODO (koshi) what was this supposed to be used for?
548
549 for (OptionMapBoolConstIter it = other_opts.bool_map.begin(); it != other_opts.bool_map.end();++it ) {
550 m_opts[merge_into].bool_map[it->first] = it->second;
551 }
552
553 for ( OptionMapFloatConstIter it = other_opts.float_map.begin(); it != other_opts.float_map.end(); ++it ) {
554 m_opts[merge_into].float_map[it->first] = it->second;
555 }
556
557 for ( OptionMapListConstIter it = other_opts.list_map.begin(); it != other_opts.list_map.end(); ++it ){
558 m_opts[merge_into].list_map[it->first] = it->second;
559 }
560
561 for ( OptionMapStringConstIter it = other_opts.string_map.begin(); it != other_opts.string_map.end(); ++it ) {
562 m_opts[merge_into].string_map[it->first] = it->second;
563 }
564 }
565 return true;
566 }
567
ParseSectionMap(mmSectionTree & section_tree,const OptionMapSection & section_map)568 void OptionsWrapper::ParseSectionMap( mmSectionTree& section_tree, const OptionMapSection& section_map )
569 {
570
571 // map child-key <-> parent-key
572 typedef std::map<std::string,std::string> RelationMap;
573 typedef std::map<std::string,std::string>::iterator RelationMapIter;
574 RelationMap relation_map;
575
576 //setup relation map
577 for ( OptionMapSectionConstIter it = section_map.begin(); it != section_map.end(); ++it )
578 {
579 relation_map[it->second.key] = it->second.section;
580 }
581
582 RelationMapIter rit = relation_map.begin();
583 // no more items in the map means we've added them all
584 while ( !relation_map.empty() )
585 {
586 RelationMapIter rit_next = rit; // in case we need to delete
587 ++rit_next;
588
589 if ( relation_map.find(rit->second) == relation_map.end() )
590 {
591 //either we already added this sections parent or it's a root section
592 OptionMapSectionConstIter section = section_map.find(rit->first);
593 assert ( section != section_map.end() );
594 section_tree.AddSection( section->second );
595
596
597 //we're done with this section, so remove it
598 relation_map.erase(rit);
599 }
600
601 rit = rit_next;
602
603 //we've reached the end of the map, restart at beginning
604 if ( rit == relation_map.end() )
605 rit = relation_map.begin();
606 }
607
608 }
609
610 const std::string tree_sep = "/";
611
mmSectionTree()612 mmSectionTree::mmSectionTree()
613 : m_tree ( new ConfigType() )
614 {
615 //this class is basically nonfunctional atm
616 //FIXME: assert( false );
617 }
618
~mmSectionTree()619 mmSectionTree::~mmSectionTree()
620 {
621 #ifndef NDEBUG
622 // m_tree->Flush();
623 #else //no need to clutter tempfile directory if we're not debugging
624 // m_tree->DeleteAll();
625 #endif
626 }
627
AddSection(const std::string & parentpath,const mmOptionSection & section)628 void mmSectionTree::AddSection ( const std::string& parentpath, const mmOptionSection& section )
629 {
630 //FIXME
631 // std::string fullpath = parentpath + tree_sep + section.key + tree_sep;
632 // m_tree->Write( fullpath + "key", section.key );
633 #ifndef NDEBUG
634 // m_tree->Flush();
635 #endif
636 }
AddSection(const mmOptionSection & section)637 void mmSectionTree::AddSection( const mmOptionSection& section)
638 {
639 //m_section_map[section.key] = section;
640 std::string name = section.section;
641 if ( section.section == Constants::nosection_name )
642 {
643 AddSection( tree_sep, section );
644 }
645 else
646 {
647 std::string parent = FindParentpath( section.section );
648 AddSection( parent, section );
649 }
650 }
651
FindRecursive(const std::string &,std::string &)652 bool mmSectionTree::FindRecursive( const std::string& /*parent_key*/, std::string& /*path */)
653 {
654 // std::string current;
655 // long cur_index;
656
657 // //search current level first before recursing
658 // bool cont = m_tree->GetFirstGroup( current, cur_index );
659 // while ( cont )
660 // {
661 // if ( current.EndsWith( parent_key ) ) {
662 // path = current;
663 // return true;
664 // }
665 // cont = m_tree->GetNextGroup( current, cur_index );
666 // }
667
668 // //we need to recurse into sub-paths
669 // cont = m_tree->GetFirstGroup( current, cur_index );
670 // while ( cont )
671 // {
672 // std::string old_path = m_tree->GetPath();
673 // m_tree->SetPath( old_path + "/" + current );
674 // if ( FindRecursive( parent_key, path ) )
675 // return true;
676 // m_tree->SetPath( old_path );
677 // cont = m_tree->GetNextGroup( current, cur_index );
678 // }
679 return false;
680 }
681
FindParentpath(const std::string & parent_key)682 std::string mmSectionTree::FindParentpath ( const std::string& parent_key )
683 {
684 std::string path = tree_sep;
685 if ( FindRecursive( parent_key, path ) )
686 return path;
687 else
688 return "";
689 }
690
Clear()691 void mmSectionTree::Clear()
692 {
693 m_section_map.clear();
694 // m_tree->DeleteAll();
695 }
696
697 } // namespace LSL {
698