123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "util/settings.h"
- #include "console/engineAPI.h"
- #include "console/consoleTypes.h"
- #include "console/SimXMLDocument.h"
- IMPLEMENT_CONOBJECT(Settings);
- ConsoleDocClass( Settings,
- "@brief Class used for writing out preferences and settings for editors\n\n"
- "Not intended for game development, for editors or internal use only.\n\n "
- "@internal");
- Settings::Settings()
- {
- mFile = "";
- mSearchPos = 0;
- }
- Settings::~Settings()
- {
-
- }
- void Settings::initPersistFields()
- {
- addField("file", TypeStringFilename, Offset(mFile, Settings), "The file path and name to be saved to and loaded from.");
- Parent::initPersistFields();
- }
- void Settings::setDefaultValue(const UTF8 *settingName, const UTF8 *settingValue, const UTF8 *settingType)
- {
- String baseName;
- buildGroupString(baseName, settingName);
- String name = baseName + "_default";
- StringTableEntry nameEntry = StringTable->insert(name.c_str());
- String type = baseName + "_type";
- StringTableEntry typeEntry = StringTable->insert(type.c_str());
- setModStaticFields(false);
- setDataField(nameEntry, NULL, settingValue);
- setDataField(typeEntry, NULL, settingType);
- setModStaticFields(true);
- }
- void Settings::setValue(const UTF8 *settingName, const UTF8 *settingValue)
- {
- String name;
- buildGroupString(name, settingName);
- StringTableEntry nameEntry = StringTable->insert(name.c_str());
- setModStaticFields(false);
- setDataField(nameEntry, NULL, settingValue);
- setModStaticFields(true);
- }
- const UTF8 *Settings::value(const UTF8 *settingName, const UTF8 *defaultValue)
- {
- String name;
- buildGroupString(name, settingName);
- StringTableEntry nameEntry = StringTable->insert(name.c_str());
- name += "_default";
- StringTableEntry defaultNameEntry = StringTable->insert(name.c_str());
- // we do this setModStaticFields call to make sure our get/set calls
- // don't grab a regular field, don't want to stomp anything
- setModStaticFields(false);
- const UTF8 *value = getDataField(nameEntry, NULL);
- const UTF8 *storedDefaultValue = getDataField(defaultNameEntry, NULL);
- setModStaticFields(true);
- if(String::compare(value, "") != 0)
- return value;
- else if(String::compare(storedDefaultValue, "") != 0)
- return storedDefaultValue;
- else
- return defaultValue;
- }
- void Settings::remove(const UTF8 *settingName, bool includeDefaults)
- {
- // Fetch Dynamic-Field Dictionary.
- SimFieldDictionary* pFieldDictionary = getFieldDictionary();
- // Any Field Dictionary?
- if ( pFieldDictionary == NULL )
- {
- // No, so we're done.
- return;
- }
- String name;
- buildGroupString(name, settingName);
- StringTableEntry nameEntry = StringTable->insert(name.c_str());
- StringTableEntry nameEntryDefault = StringTable->insert( String::ToString("%s%s",name.c_str(), "_default") );
- // Iterate fields.
- for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr )
- {
- // Fetch Field Entry.
- SimFieldDictionary::Entry* fieldEntry = *itr;
- // is this a field of our current group
- if ( (String::compare(nameEntry, "") == 0) ||
- String::compare( nameEntry, fieldEntry->slotName ) == 0 ||
- (includeDefaults && String::compare( nameEntryDefault, fieldEntry->slotName ) == 0) )
- {
- // Yes, so remove it.
- pFieldDictionary->setFieldValue( fieldEntry->slotName, "" );
- }
- }
- }
- void Settings::buildGroupString(String &name, const UTF8 *settingName)
- {
- // here we want to loop through the stack and build a "/" seperated string
- // representing the entire current group stack that gets pre-pended to the
- // setting name passed in
- if(mGroupStack.size() > 0)
- {
- for(S32 i=0; i < mGroupStack.size(); i++)
- {
- S32 pos = 0;
- if(name.size() > 0)
- pos = name.size()-1;
- // tack on the "/" in front if this isn't the first
- if(i == 0)
- {
- name.insert(pos, mGroupStack[i]);
- }
- else
- {
- name.insert(pos, "/");
- name.insert(pos+1, mGroupStack[i]);
- }
- }
- // tack on a final "/"
- name.insert(name.size()-1, "/");
- if(dStrlen(settingName) > 0)
- name.insert(name.size()-1, settingName);
- }
- else
- {
- name = settingName;
- }
- }
- void Settings::clearAllFields()
- {
- // Fetch Dynamic-Field Dictionary.
- SimFieldDictionary* pFieldDictionary = getFieldDictionary();
- // Any Field Dictionary?
- if ( pFieldDictionary == NULL )
- {
- // No, so we're done.
- return;
- }
- // Iterate fields.
- for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr )
- {
- // Fetch Field Entry.
- SimFieldDictionary::Entry* fieldEntry = *itr;
- // don't remove default field values
- if (dStrEndsWith(fieldEntry->slotName, "_default"))
- continue;
- // remove it.
- pFieldDictionary->setFieldValue( fieldEntry->slotName, "" );
- }
- }
- bool Settings::write()
- {
- // Fetch Dynamic-Field Dictionary.
- SimFieldDictionary* pFieldDictionary = getFieldDictionary();
- // Any Field Dictionary?
- if ( pFieldDictionary == NULL )
- {
- // No, so we're done.
- return false;
- }
- /*
- // Iterate fields.
- for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr )
- {
- // Fetch Field Entry.
- SimFieldDictionary::Entry* fieldEntry = *itr;
- String check(fieldEntry->slotName);
- String::SizeType pos = check.find("_default");
- if(pos != String::NPos)
- continue;
- // let's build our XML doc
- document->pushNewElement("dynamicField");
- document->setAttribute("name", fieldEntry->slotName);
- document->addText(fieldEntry->value);
- document->popElement();
- }
- */
- SimXMLDocument *document = new SimXMLDocument();
- document->registerObject();
- document->addHeader();
- document->pushNewElement(getName());
- SettingSaveNode *node = new SettingSaveNode();
- // Iterate fields.
- for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr )
- {
- // Fetch Field Entry.
- SimFieldDictionary::Entry* fieldEntry = *itr;
- String check(fieldEntry->slotName);
- if(check.find("_default") != String::NPos || check.find("_type") != String::NPos)
- continue;
- node->addValue(fieldEntry->slotName, fieldEntry->value);
- }
- node->buildDocument(document, true);
- node->clear();
- delete node;
- bool saved = document->saveFile(mFile);
- document->deleteObject();
- if(saved)
- return true;
- else
- return false;
- }
- bool Settings::read()
- {
- SimXMLDocument *document = new SimXMLDocument();
- document->registerObject();
- bool success = true;
- if(document->loadFile(mFile))
- {
- clearAllFields();
- // set our base element
- if(document->pushFirstChildElement(getName()))
- {
- setModStaticFields(false);
- readLayer(document);
- setModStaticFields(true);
- }
- else
- success = false;
- }
- else
- success = false;
- document->deleteObject();
- return success;
- }
- void Settings::readLayer(SimXMLDocument *document, String groupStack)
- {
- for(S32 i=0; document->pushChildElement(i); i++)
- {
- const UTF8 *type = document->elementValue();
- const UTF8 *name = document->attribute("name");
- const UTF8 *value = document->getText();
-
- if(String::compare(type, "Group") == 0)
- {
- String newStack = groupStack;
- if(!groupStack.isEmpty())
- newStack += "/";
- newStack += name;
- readLayer(document, newStack);
- } else if(String::compare(type, "Setting") == 0)
- {
- String nameString = groupStack;
-
- if(!groupStack.isEmpty())
- nameString += "/";
- nameString += name;
- setDataField(StringTable->insert(nameString.c_str()), NULL, value);
- }
-
- document->popElement();
- }
- }
- void Settings::beginGroup(const UTF8 *groupName, bool fromStart)
- {
- // check if we want to clear the stack
- if(fromStart)
- clearGroups();
- mGroupStack.push_back(String(groupName));
- }
- void Settings::endGroup()
- {
- if(mGroupStack.size() > 0)
- mGroupStack.pop_back();
- }
- void Settings::clearGroups()
- {
- mGroupStack.clear();
- }
- const UTF8 *Settings::getCurrentGroups()
- {
- // we want to return a string with our group setup
- String returnString;
- for(S32 i=0; i<mGroupStack.size(); i++)
- {
- S32 pos = returnString.size() - 1;
- if(pos < 0)
- pos = 0;
- if(i == 0)
- {
- returnString.insert(pos, mGroupStack[i]);
- } else
- {
- returnString.insert(pos, "/");
- returnString.insert(pos+1, mGroupStack[i]);
- }
- }
- return StringTable->insert(returnString.c_str());
- }
- /*
- S32 Settings::buildSearchList( const char* pattern, bool deepSearch, bool includeDefaults )
- {
- mSearchResults.clear();
- SimFieldDictionary* fieldDictionary = getFieldDictionary();
- // Get the dynamic field count
- if ( !fieldDictionary )
- return -1;
- for (SimFieldDictionaryIterator itr(fieldDictionary); *itr; ++itr)
- {
- // Fetch Field Entry.
- SimFieldDictionary::Entry* fieldEntry = *itr;
-
- // Compare strings, store proper results in vector
- String extendedPath = String::ToString(fieldEntry->slotName);
- String::SizeType start(0);
- String::SizeType slashPos = extendedPath.find('/', 0, String::Right);
- String shortPath = extendedPath.substr( start, slashPos );
- if( deepSearch )
- {
- if( shortPath.find( pattern ) != -1 )
- {
- if( !includeDefaults && extendedPath.find("_default") != -1 )
- continue;
- String listMember = String::ToString(fieldEntry->value);
- listMember.insert(start, " " );
- listMember.insert(start, String::ToString(fieldEntry->slotName) );
- mSearchResults.push_back( listMember );
- }
- }
- else
- {
- if( shortPath.compare( pattern ) == 0 )
- {
- if( !includeDefaults && extendedPath.find("_default") != -1 )
- continue;
- String listMember = String::ToString(fieldEntry->value);
- listMember.insert(start, " " );
- listMember.insert(start, String::ToString(fieldEntry->slotName) );
- mSearchResults.push_back( listMember );
- }
- }
- }
- return mSearchResults.size();
- }
- */
- const char* Settings::findFirstValue( const char* pattern, bool deepSearch, bool includeDefaults )
- {
- mSearchResults.clear();
- SimFieldDictionary* fieldDictionary = getFieldDictionary();
- // Get the dynamic field count
- if ( !fieldDictionary )
- return "";
- for (SimFieldDictionaryIterator itr(fieldDictionary); *itr; ++itr)
- {
- // Fetch Field Entry.
- SimFieldDictionary::Entry* fieldEntry = *itr;
-
- // Compare strings, store proper results in vector
- String extendedPath = String::ToString(fieldEntry->slotName);
- String::SizeType start(0);
- String::SizeType slashPos = extendedPath.find('/', 0, String::Right);
- String shortPath = extendedPath.substr( start, slashPos );
- if( deepSearch )
- {
- if( shortPath.find( pattern ) != -1 )
- {
- if( !includeDefaults && extendedPath.find("_default") != -1 )
- continue;
- String listMember = String::ToString(fieldEntry->slotName);
- //listMember.insert(start, " " );
- //listMember.insert(start, String::ToString(fieldEntry->slotName) );
- mSearchResults.push_back( listMember );
- }
- }
- else
- {
- if( shortPath.compare( pattern ) == 0 )
- {
- if( !includeDefaults && extendedPath.find("_default") != -1 )
- continue;
- String listMember = String::ToString(fieldEntry->slotName);
- //listMember.insert(start, " " );
- //listMember.insert(start, String::ToString(fieldEntry->slotName) );
- mSearchResults.push_back( listMember );
- }
- }
- }
-
- if( mSearchResults.size() < 1 )
- {
- Con::errorf("findFirstValue() : Pattern not found");
- return "";
- }
- mSearchPos = 0;
- return mSearchResults[mSearchPos];
- }
- const char* Settings::findNextValue()
- {
- if ( mSearchPos + 1 >= mSearchResults.size() )
- return "";
- mSearchPos++;
- return mSearchResults[mSearchPos];
- }
- // make sure to replace the strings
- DefineEngineMethod(Settings, findFirstValue, const char*, ( const char* pattern, bool deepSearch, bool includeDefaults ), ("", false, false), "settingObj.findFirstValue();")
- {
- return object->findFirstValue( pattern, deepSearch, includeDefaults );
- }
- DefineEngineMethod(Settings, findNextValue, const char*, (), , "settingObj.findNextValue();")
- {
- return object->findNextValue();
- }
- /*
- ConsoleMethod(Settings, buildSearchList, void, 2, 2, "settingObj.buildSearchList();")
- {
- object->buildSearchList( "foobar" );
- }
- */
- void SettingSaveNode::addValue(const UTF8 *name, const UTF8 *value)
- {
- String nameString(name);
- S32 groupCount = getGroupCount(nameString);
- SettingSaveNode *parentNode = this;
- // let's check to make sure all these groups exist already
- for(S32 i=0; i<groupCount; i++)
- {
- String groupName = getGroup(nameString, i);
- if(!groupName.isEmpty())
- {
- bool found = false;
- // loop through all of our nodes to find if this one exists,
- // if it does we want to use it
- for(S32 j=0; j<parentNode->mGroupNodes.size(); j++)
- {
- SettingSaveNode *node = parentNode->mGroupNodes[j];
- if(!node->mIsGroup)
- continue;
- if(node->mName.compare(groupName) == 0)
- {
- parentNode = node;
- found = true;
- break;
- }
- }
-
- // not found, so we create it
- if(!found)
- {
- SettingSaveNode *node = new SettingSaveNode(groupName, true);
- parentNode->mGroupNodes.push_back(node);
- parentNode = node;
- }
- }
- }
- // now we can properly set our actual value
- String settingNameString = getSettingName(name);
- String valueString(value);
- SettingSaveNode *node = new SettingSaveNode(settingNameString, valueString);
- parentNode->mSettingNodes.push_back(node);
- }
- S32 SettingSaveNode::getGroupCount(const String &name)
- {
- String::SizeType pos = 0;
- S32 count = 0;
- // loop through and count our exiting groups
- while(pos != String::NPos)
- {
- pos = name.find("/", pos + 1);
- if(pos != String::NPos)
- count++;
- }
- return count;
- }
- String SettingSaveNode::getGroup(const String &name, S32 num)
- {
- String::SizeType pos = 0;
- String::SizeType lastPos = 0;
- S32 count = 0;
- while(pos != String::NPos)
- {
- lastPos = pos;
- pos = name.find("/", pos + 1);
- if(count == num)
- {
- String::SizeType startPos = lastPos;
- if(count > 0)
- startPos++;
- if(pos == String::NPos)
- return name.substr(startPos, name.length() - (startPos));
- else
- return name.substr(startPos, pos - startPos);
- }
- count++;
- }
- return String("");
- }
- String SettingSaveNode::getSettingName(const String &name)
- {
- String::SizeType pos = name.find("/", 0, String::Right);
- if(pos == String::NPos)
- return String(name);
- else
- return name.substr(pos+1, name.length() - (pos+1));
- }
- void SettingSaveNode::clear()
- {
- for( U32 i = 0, num = mGroupNodes.size(); i < num; ++ i )
- delete mGroupNodes[ i ];
- for( U32 i = 0, num = mSettingNodes.size(); i < num; ++ i )
- delete mSettingNodes[ i ];
- mGroupNodes.clear();
- mSettingNodes.clear();
- }
- void SettingSaveNode::buildDocument(SimXMLDocument *document, bool skipWrite)
- {
- // let's build our XML doc
- if(mIsGroup && !skipWrite)
- {
- document->pushNewElement("Group");
- document->setAttribute("name", mName);
- }
- if(!mIsGroup && !skipWrite)
- {
- document->pushNewElement("Setting");
- document->setAttribute("name", mName);
- document->addText(mValue);
- }
- else
- {
- mSettingNodes.sort(_NodeCompare);
- mGroupNodes.sort(_NodeCompare);
- for(S32 i=0; i<mSettingNodes.size(); i++)
- {
- SettingSaveNode *node = mSettingNodes[i];
- node->buildDocument(document);
- }
- for(S32 i=0; i<mGroupNodes.size(); i++)
- {
- SettingSaveNode *node = mGroupNodes[i];
- node->buildDocument(document);
- }
- }
-
- if(!skipWrite)
- document->popElement();
- }
- S32 QSORT_CALLBACK SettingSaveNode::_NodeCompare(SettingSaveNode* const* a, SettingSaveNode* const* b)
- {
- S32 result = dStrnatcasecmp((*a)->mName.c_str(), (*b)->mName.c_str());
- return result;
- }
- DefineEngineMethod(Settings, setValue, void, (const char * settingName, const char * value), (""), "settingObj.setValue(settingName, value);")
- {
- StringTableEntry fieldName = StringTable->insert( settingName );
-
- if (!String::isEmpty(value))
- object->setValue( fieldName, value );
- else
- object->setValue( fieldName );
- }
- DefineEngineMethod(Settings, setDefaultValue, void, (const char * settingName, const char * value), , "settingObj.setDefaultValue(settingName, value);")
- {
- StringTableEntry fieldName = StringTable->insert( settingName );
- object->setDefaultValue( fieldName, value );
- }
- DefineEngineMethod(Settings, value, const char*, (const char * settingName, const char * defaultValue), (""), "settingObj.value(settingName, defaultValue);")
- {
- StringTableEntry fieldName = StringTable->insert( settingName );
-
- if (String::compare(defaultValue, "") != 0)
- return object->value( fieldName, defaultValue );
- else if (String::compare(settingName, "") != 0)
- return object->value( fieldName );
- return "";
- }
- DefineEngineMethod(Settings, remove, void, (const char * settingName, bool includeDefaults), (false), "settingObj.remove(settingName, includeDefaults = false);")
- {
- // there's a problem with some fields not being removed properly, but works if you run it twice,
- // a temporary solution for now is simply to call the remove twice
- object->remove( settingName, includeDefaults );
- object->remove( settingName, includeDefaults );
- }
- DefineEngineMethod(Settings, write, bool, (),, "%success = settingObj.write();")
- {
- return object->write();
- }
- DefineEngineMethod(Settings, read, bool, (), , "%success = settingObj.read();")
- {
- return object->read();
- }
- DefineEngineMethod(Settings, beginGroup, void, (const char * groupName, bool includeDefaults), (false), "settingObj.beginGroup(groupName, fromStart = false);")
- {
- object->beginGroup( groupName, includeDefaults );
- }
- DefineEngineMethod(Settings, endGroup, void, (), , "settingObj.endGroup();")
- {
- object->endGroup();
- }
- DefineEngineMethod(Settings, clearGroups, void, (), , "settingObj.clearGroups();")
- {
- object->clearGroups();
- }
- DefineEngineMethod(Settings, getCurrentGroups, const char*, (), , "settingObj.getCurrentGroups();")
- {
- return object->getCurrentGroups();
- }
|