settings.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "util/settings.h"
  23. #include "console/consoleTypes.h"
  24. #include "console/SimXMLDocument.h"
  25. IMPLEMENT_CONOBJECT(Settings);
  26. ConsoleDocClass( Settings,
  27. "@brief Class used for writing out preferences and settings for editors\n\n"
  28. "Not intended for game development, for editors or internal use only.\n\n "
  29. "@internal");
  30. Settings::Settings()
  31. {
  32. mFile = "";
  33. mSearchPos = 0;
  34. }
  35. Settings::~Settings()
  36. {
  37. }
  38. void Settings::initPersistFields()
  39. {
  40. addField("file", TypeStringFilename, Offset(mFile, Settings), "The file path and name to be saved to and loaded from.");
  41. Parent::initPersistFields();
  42. }
  43. void Settings::setDefaultValue(const UTF8 *settingName, const UTF8 *settingValue, const UTF8 *settingType)
  44. {
  45. String baseName;
  46. buildGroupString(baseName, settingName);
  47. String name = baseName + "_default";
  48. StringTableEntry nameEntry = StringTable->insert(name.c_str());
  49. String type = baseName + "_type";
  50. StringTableEntry typeEntry = StringTable->insert(type.c_str());
  51. setModStaticFields(false);
  52. setDataField(nameEntry, NULL, settingValue);
  53. setDataField(typeEntry, NULL, settingType);
  54. setModStaticFields(true);
  55. }
  56. void Settings::setValue(const UTF8 *settingName, const UTF8 *settingValue)
  57. {
  58. String name;
  59. buildGroupString(name, settingName);
  60. StringTableEntry nameEntry = StringTable->insert(name.c_str());
  61. setModStaticFields(false);
  62. setDataField(nameEntry, NULL, settingValue);
  63. setModStaticFields(true);
  64. }
  65. const UTF8 *Settings::value(const UTF8 *settingName, const UTF8 *defaultValue)
  66. {
  67. String name;
  68. buildGroupString(name, settingName);
  69. StringTableEntry nameEntry = StringTable->insert(name.c_str());
  70. name += "_default";
  71. StringTableEntry defaultNameEntry = StringTable->insert(name.c_str());
  72. // we do this setModStaticFields call to make sure our get/set calls
  73. // don't grab a regular field, don't want to stomp anything
  74. setModStaticFields(false);
  75. const UTF8 *value = getDataField(nameEntry, NULL);
  76. const UTF8 *storedDefaultValue = getDataField(defaultNameEntry, NULL);
  77. setModStaticFields(true);
  78. if(dStrcmp(value, "") != 0)
  79. return value;
  80. else if(dStrcmp(storedDefaultValue, "") != 0)
  81. return storedDefaultValue;
  82. else
  83. return defaultValue;
  84. }
  85. void Settings::remove(const UTF8 *settingName, bool includeDefaults)
  86. {
  87. // Fetch Dynamic-Field Dictionary.
  88. SimFieldDictionary* pFieldDictionary = getFieldDictionary();
  89. // Any Field Dictionary?
  90. if ( pFieldDictionary == NULL )
  91. {
  92. // No, so we're done.
  93. return;
  94. }
  95. String name;
  96. buildGroupString(name, settingName);
  97. StringTableEntry nameEntry = StringTable->insert(name.c_str());
  98. StringTableEntry nameEntryDefault = StringTable->insert( String::ToString("%s%s",name.c_str(), "_default") );
  99. // Iterate fields.
  100. for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr )
  101. {
  102. // Fetch Field Entry.
  103. SimFieldDictionary::Entry* fieldEntry = *itr;
  104. // is this a field of our current group
  105. if ( (dStrcmp(nameEntry, "") == 0) ||
  106. dStrcmp( nameEntry, fieldEntry->slotName ) == 0 ||
  107. (includeDefaults && dStrcmp( nameEntryDefault, fieldEntry->slotName ) == 0) )
  108. {
  109. // Yes, so remove it.
  110. pFieldDictionary->setFieldValue( fieldEntry->slotName, "" );
  111. }
  112. }
  113. }
  114. void Settings::buildGroupString(String &name, const UTF8 *settingName)
  115. {
  116. // here we want to loop through the stack and build a "/" seperated string
  117. // representing the entire current group stack that gets pre-pended to the
  118. // setting name passed in
  119. if(mGroupStack.size() > 0)
  120. {
  121. for(S32 i=0; i < mGroupStack.size(); i++)
  122. {
  123. S32 pos = 0;
  124. if(name.size() > 0)
  125. pos = name.size()-1;
  126. // tack on the "/" in front if this isn't the first
  127. if(i == 0)
  128. {
  129. name.insert(pos, mGroupStack[i]);
  130. } else
  131. {
  132. name.insert(pos, "/");
  133. name.insert(pos+1, mGroupStack[i]);
  134. }
  135. }
  136. // tack on a final "/"
  137. name.insert(name.size()-1, "/");
  138. if(dStrlen(settingName) > 0)
  139. name.insert(name.size()-1, settingName);
  140. } else
  141. name = settingName;
  142. }
  143. void Settings::clearAllFields()
  144. {
  145. // Fetch Dynamic-Field Dictionary.
  146. SimFieldDictionary* pFieldDictionary = getFieldDictionary();
  147. // Any Field Dictionary?
  148. if ( pFieldDictionary == NULL )
  149. {
  150. // No, so we're done.
  151. return;
  152. }
  153. // Iterate fields.
  154. for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr )
  155. {
  156. // Fetch Field Entry.
  157. SimFieldDictionary::Entry* fieldEntry = *itr;
  158. // don't remove default field values
  159. if (dStrEndsWith(fieldEntry->slotName, "_default"))
  160. continue;
  161. // remove it.
  162. pFieldDictionary->setFieldValue( fieldEntry->slotName, "" );
  163. }
  164. }
  165. bool Settings::write()
  166. {
  167. // Fetch Dynamic-Field Dictionary.
  168. SimFieldDictionary* pFieldDictionary = getFieldDictionary();
  169. // Any Field Dictionary?
  170. if ( pFieldDictionary == NULL )
  171. {
  172. // No, so we're done.
  173. return false;
  174. }
  175. /*
  176. // Iterate fields.
  177. for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr )
  178. {
  179. // Fetch Field Entry.
  180. SimFieldDictionary::Entry* fieldEntry = *itr;
  181. String check(fieldEntry->slotName);
  182. String::SizeType pos = check.find("_default");
  183. if(pos != String::NPos)
  184. continue;
  185. // let's build our XML doc
  186. document->pushNewElement("dynamicField");
  187. document->setAttribute("name", fieldEntry->slotName);
  188. document->addText(fieldEntry->value);
  189. document->popElement();
  190. }
  191. */
  192. SimXMLDocument *document = new SimXMLDocument();
  193. document->registerObject();
  194. document->addHeader();
  195. document->pushNewElement(getName());
  196. SettingSaveNode *node = new SettingSaveNode();
  197. // Iterate fields.
  198. for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr )
  199. {
  200. // Fetch Field Entry.
  201. SimFieldDictionary::Entry* fieldEntry = *itr;
  202. String check(fieldEntry->slotName);
  203. if(check.find("_default") != String::NPos || check.find("_type") != String::NPos)
  204. continue;
  205. node->addValue(fieldEntry->slotName, fieldEntry->value);
  206. }
  207. node->buildDocument(document, true);
  208. node->clear();
  209. delete node;
  210. bool saved = document->saveFile(mFile.c_str());
  211. document->deleteObject();
  212. if(saved)
  213. return true;
  214. else
  215. return false;
  216. }
  217. bool Settings::read()
  218. {
  219. SimXMLDocument *document = new SimXMLDocument();
  220. document->registerObject();
  221. bool success = true;
  222. if(document->loadFile(mFile.c_str()))
  223. {
  224. clearAllFields();
  225. // set our base element
  226. if(document->pushFirstChildElement(getName()))
  227. {
  228. setModStaticFields(false);
  229. readLayer(document);
  230. setModStaticFields(true);
  231. }
  232. else
  233. success = false;
  234. }
  235. else
  236. success = false;
  237. document->deleteObject();
  238. return success;
  239. }
  240. void Settings::readLayer(SimXMLDocument *document, String groupStack)
  241. {
  242. for(S32 i=0; document->pushChildElement(i); i++)
  243. {
  244. bool groupCount = 0;
  245. const UTF8 *type = document->elementValue();
  246. const UTF8 *name = document->attribute("name");
  247. const UTF8 *value = document->getText();
  248. if(dStrcmp(type, "Group") == 0)
  249. {
  250. String newStack = groupStack;
  251. if(!groupStack.isEmpty())
  252. newStack += "/";
  253. newStack += name;
  254. readLayer(document, newStack);
  255. groupCount++;
  256. } else if(dStrcmp(type, "Setting") == 0)
  257. {
  258. String nameString = groupStack;
  259. if(!groupStack.isEmpty())
  260. nameString += "/";
  261. nameString += name;
  262. setDataField(StringTable->insert(nameString.c_str()), NULL, value);
  263. }
  264. document->popElement();
  265. }
  266. }
  267. void Settings::beginGroup(const UTF8 *groupName, bool fromStart)
  268. {
  269. // check if we want to clear the stack
  270. if(fromStart)
  271. clearGroups();
  272. mGroupStack.push_back(String(groupName));
  273. }
  274. void Settings::endGroup()
  275. {
  276. if(mGroupStack.size() > 0)
  277. mGroupStack.pop_back();
  278. }
  279. void Settings::clearGroups()
  280. {
  281. mGroupStack.clear();
  282. }
  283. const UTF8 *Settings::getCurrentGroups()
  284. {
  285. // we want to return a string with our group setup
  286. String returnString;
  287. for(S32 i=0; i<mGroupStack.size(); i++)
  288. {
  289. S32 pos = returnString.size() - 1;
  290. if(pos < 0)
  291. pos = 0;
  292. if(i == 0)
  293. {
  294. returnString.insert(pos, mGroupStack[i]);
  295. } else
  296. {
  297. returnString.insert(pos, "/");
  298. returnString.insert(pos+1, mGroupStack[i]);
  299. }
  300. }
  301. return StringTable->insert(returnString.c_str());
  302. }
  303. /*
  304. S32 Settings::buildSearchList( const char* pattern, bool deepSearch, bool includeDefaults )
  305. {
  306. mSearchResults.clear();
  307. SimFieldDictionary* fieldDictionary = getFieldDictionary();
  308. // Get the dynamic field count
  309. if ( !fieldDictionary )
  310. return -1;
  311. for (SimFieldDictionaryIterator itr(fieldDictionary); *itr; ++itr)
  312. {
  313. // Fetch Field Entry.
  314. SimFieldDictionary::Entry* fieldEntry = *itr;
  315. // Compare strings, store proper results in vector
  316. String extendedPath = String::ToString(fieldEntry->slotName);
  317. String::SizeType start(0);
  318. String::SizeType slashPos = extendedPath.find('/', 0, String::Right);
  319. String shortPath = extendedPath.substr( start, slashPos );
  320. if( deepSearch )
  321. {
  322. if( shortPath.find( pattern ) != -1 )
  323. {
  324. if( !includeDefaults && extendedPath.find("_default") != -1 )
  325. continue;
  326. String listMember = String::ToString(fieldEntry->value);
  327. listMember.insert(start, " " );
  328. listMember.insert(start, String::ToString(fieldEntry->slotName) );
  329. mSearchResults.push_back( listMember );
  330. }
  331. }
  332. else
  333. {
  334. if( shortPath.compare( pattern ) == 0 )
  335. {
  336. if( !includeDefaults && extendedPath.find("_default") != -1 )
  337. continue;
  338. String listMember = String::ToString(fieldEntry->value);
  339. listMember.insert(start, " " );
  340. listMember.insert(start, String::ToString(fieldEntry->slotName) );
  341. mSearchResults.push_back( listMember );
  342. }
  343. }
  344. }
  345. return mSearchResults.size();
  346. }
  347. */
  348. const char* Settings::findFirstValue( const char* pattern, bool deepSearch, bool includeDefaults )
  349. {
  350. mSearchResults.clear();
  351. SimFieldDictionary* fieldDictionary = getFieldDictionary();
  352. // Get the dynamic field count
  353. if ( !fieldDictionary )
  354. return "";
  355. for (SimFieldDictionaryIterator itr(fieldDictionary); *itr; ++itr)
  356. {
  357. // Fetch Field Entry.
  358. SimFieldDictionary::Entry* fieldEntry = *itr;
  359. // Compare strings, store proper results in vector
  360. String extendedPath = String::ToString(fieldEntry->slotName);
  361. String::SizeType start(0);
  362. String::SizeType slashPos = extendedPath.find('/', 0, String::Right);
  363. String shortPath = extendedPath.substr( start, slashPos );
  364. if( deepSearch )
  365. {
  366. if( shortPath.find( pattern ) != -1 )
  367. {
  368. if( !includeDefaults && extendedPath.find("_default") != -1 )
  369. continue;
  370. String listMember = String::ToString(fieldEntry->slotName);
  371. //listMember.insert(start, " " );
  372. //listMember.insert(start, String::ToString(fieldEntry->slotName) );
  373. mSearchResults.push_back( listMember );
  374. }
  375. }
  376. else
  377. {
  378. if( shortPath.compare( pattern ) == 0 )
  379. {
  380. if( !includeDefaults && extendedPath.find("_default") != -1 )
  381. continue;
  382. String listMember = String::ToString(fieldEntry->slotName);
  383. //listMember.insert(start, " " );
  384. //listMember.insert(start, String::ToString(fieldEntry->slotName) );
  385. mSearchResults.push_back( listMember );
  386. }
  387. }
  388. }
  389. if( mSearchResults.size() < 1 )
  390. {
  391. Con::errorf("findFirstValue() : Pattern not found");
  392. return "";
  393. }
  394. mSearchPos = 0;
  395. return mSearchResults[mSearchPos];
  396. }
  397. const char* Settings::findNextValue()
  398. {
  399. if ( mSearchPos + 1 >= mSearchResults.size() )
  400. return "";
  401. mSearchPos++;
  402. return mSearchResults[mSearchPos];
  403. }
  404. // make sure to replace the strings
  405. ConsoleMethod(Settings, findFirstValue, const char*, 2, 5, "settingObj.findFirstValue();")
  406. {
  407. if( argc == 3 )
  408. return object->findFirstValue( argv[2] );
  409. else if( argc == 4 )
  410. return object->findFirstValue( argv[2], dAtob(argv[3]) );
  411. else if( argc == 5 )
  412. return object->findFirstValue( argv[2], dAtob(argv[3]), dAtob(argv[4]) );
  413. else
  414. return "";
  415. }
  416. ConsoleMethod(Settings, findNextValue, const char*, 2, 2, "settingObj.findNextValue();")
  417. {
  418. return object->findNextValue();
  419. }
  420. /*
  421. ConsoleMethod(Settings, buildSearchList, void, 2, 2, "settingObj.buildSearchList();")
  422. {
  423. object->buildSearchList( "foobar" );
  424. }
  425. */
  426. void SettingSaveNode::addValue(const UTF8 *name, const UTF8 *value)
  427. {
  428. String nameString(name);
  429. S32 groupCount = getGroupCount(nameString);
  430. SettingSaveNode *parentNode = this;
  431. // let's check to make sure all these groups exist already
  432. for(S32 i=0; i<groupCount; i++)
  433. {
  434. String groupName = getGroup(nameString, i);
  435. if(!groupName.isEmpty())
  436. {
  437. bool found = false;
  438. // loop through all of our nodes to find if this one exists,
  439. // if it does we want to use it
  440. for(S32 j=0; j<parentNode->mGroupNodes.size(); j++)
  441. {
  442. SettingSaveNode *node = parentNode->mGroupNodes[j];
  443. if(!node->mIsGroup)
  444. continue;
  445. if(node->mName.compare(groupName) == 0)
  446. {
  447. parentNode = node;
  448. found = true;
  449. break;
  450. }
  451. }
  452. // not found, so we create it
  453. if(!found)
  454. {
  455. SettingSaveNode *node = new SettingSaveNode(groupName, true);
  456. parentNode->mGroupNodes.push_back(node);
  457. parentNode = node;
  458. }
  459. }
  460. }
  461. // now we can properly set our actual value
  462. String settingNameString = getSettingName(name);
  463. String valueString(value);
  464. SettingSaveNode *node = new SettingSaveNode(settingNameString, valueString);
  465. parentNode->mSettingNodes.push_back(node);
  466. }
  467. S32 SettingSaveNode::getGroupCount(const String &name)
  468. {
  469. String::SizeType pos = 0;
  470. S32 count = 0;
  471. // loop through and count our exiting groups
  472. while(pos != String::NPos)
  473. {
  474. pos = name.find("/", pos + 1);
  475. if(pos != String::NPos)
  476. count++;
  477. }
  478. return count;
  479. }
  480. String SettingSaveNode::getGroup(const String &name, S32 num)
  481. {
  482. String::SizeType pos = 0;
  483. String::SizeType lastPos = 0;
  484. S32 count = 0;
  485. while(pos != String::NPos)
  486. {
  487. lastPos = pos;
  488. pos = name.find("/", pos + 1);
  489. if(count == num)
  490. {
  491. String::SizeType startPos = lastPos;
  492. if(count > 0)
  493. startPos++;
  494. if(pos == String::NPos)
  495. return name.substr(startPos, name.length() - (startPos));
  496. else
  497. return name.substr(startPos, pos - startPos);
  498. }
  499. count++;
  500. }
  501. return String("");
  502. }
  503. String SettingSaveNode::getSettingName(const String &name)
  504. {
  505. String::SizeType pos = name.find("/", 0, String::Right);
  506. if(pos == String::NPos)
  507. return String(name);
  508. else
  509. return name.substr(pos+1, name.length() - (pos+1));
  510. }
  511. void SettingSaveNode::clear()
  512. {
  513. for( U32 i = 0, num = mGroupNodes.size(); i < num; ++ i )
  514. delete mGroupNodes[ i ];
  515. for( U32 i = 0, num = mSettingNodes.size(); i < num; ++ i )
  516. delete mSettingNodes[ i ];
  517. mGroupNodes.clear();
  518. mSettingNodes.clear();
  519. }
  520. void SettingSaveNode::buildDocument(SimXMLDocument *document, bool skipWrite)
  521. {
  522. // let's build our XML doc
  523. if(mIsGroup && !skipWrite)
  524. {
  525. document->pushNewElement("Group");
  526. document->setAttribute("name", mName);
  527. }
  528. if(!mIsGroup && !skipWrite)
  529. {
  530. document->pushNewElement("Setting");
  531. document->setAttribute("name", mName);
  532. document->addText(mValue);
  533. } else
  534. {
  535. for(int i=0; i<mSettingNodes.size(); i++)
  536. {
  537. SettingSaveNode *node = mSettingNodes[i];
  538. node->buildDocument(document);
  539. }
  540. for(int i=0; i<mGroupNodes.size(); i++)
  541. {
  542. SettingSaveNode *node = mGroupNodes[i];
  543. node->buildDocument(document);
  544. }
  545. }
  546. if(!skipWrite)
  547. document->popElement();
  548. }
  549. ConsoleMethod(Settings, setValue, void, 3, 4, "settingObj.setValue(settingName, value);")
  550. {
  551. const char *fieldName = StringTable->insert( argv[2] );
  552. if(argc == 3)
  553. object->setValue( fieldName);
  554. else if(argc == 4)
  555. object->setValue( fieldName, argv[3] );
  556. }
  557. ConsoleMethod(Settings, setDefaultValue, void, 4, 4, "settingObj.setDefaultValue(settingName, value);")
  558. {
  559. const char *fieldName = StringTable->insert( argv[2] );
  560. object->setDefaultValue( fieldName, argv[3] );
  561. }
  562. ConsoleMethod(Settings, value, const char*, 3, 4, "settingObj.value(settingName, defaultValue);")
  563. {
  564. const char *fieldName = StringTable->insert( argv[2] );
  565. if(argc == 3)
  566. return object->value( fieldName );
  567. if(argc == 4)
  568. return object->value( fieldName, argv[3] );
  569. return "";
  570. }
  571. ConsoleMethod(Settings, remove, void, 3, 4, "settingObj.remove(settingName, includeDefaults = false);")
  572. {
  573. // there's a problem with some fields not being removed properly, but works if you run it twice,
  574. // a temporary solution for now is simply to call the remove twice
  575. if(argc == 3)
  576. {
  577. object->remove( argv[2] );
  578. object->remove( argv[2] );
  579. }
  580. else if(argc == 4)
  581. {
  582. object->remove( argv[2], dAtob(argv[3]) );
  583. object->remove( argv[2], dAtob(argv[3]) );
  584. }
  585. }
  586. ConsoleMethod(Settings, write, bool, 2, 2, "%success = settingObj.write();")
  587. {
  588. TORQUE_UNUSED(argc); TORQUE_UNUSED(argv);
  589. return object->write();
  590. }
  591. ConsoleMethod(Settings, read, bool, 2, 2, "%success = settingObj.read();")
  592. {
  593. TORQUE_UNUSED(argc); TORQUE_UNUSED(argv);
  594. return object->read();
  595. }
  596. ConsoleMethod(Settings, beginGroup, void, 3, 4, "settingObj.beginGroup(groupName, fromStart = false);")
  597. {
  598. if(argc == 3)
  599. object->beginGroup( argv[2] );
  600. if(argc == 4)
  601. object->beginGroup( argv[2], dAtob(argv[3]) );
  602. }
  603. ConsoleMethod(Settings, endGroup, void, 2, 2, "settingObj.endGroup();")
  604. {
  605. TORQUE_UNUSED(argc); TORQUE_UNUSED(argv);
  606. object->endGroup();
  607. }
  608. ConsoleMethod(Settings, clearGroups, void, 2, 2, "settingObj.clearGroups();")
  609. {
  610. TORQUE_UNUSED(argc); TORQUE_UNUSED(argv);
  611. object->clearGroups();
  612. }
  613. ConsoleMethod(Settings, getCurrentGroups, const char*, 2, 2, "settingObj.getCurrentGroups();")
  614. {
  615. return object->getCurrentGroups();
  616. }