|
@@ -0,0 +1,382 @@
|
|
|
|
|
+#include "CmPreferences.h"
|
|
|
|
|
+#include "CmException.h"
|
|
|
|
|
+#include "CmDebug.h"
|
|
|
|
|
+#include "CmFileSystem.h"
|
|
|
|
|
+#include "CmDataStream.h"
|
|
|
|
|
+
|
|
|
|
|
+namespace CamelotEditor
|
|
|
|
|
+{
|
|
|
|
|
+ void Preferences::setValue(const String& key, const String& value, UINT32 arrayIdx)
|
|
|
|
|
+ {
|
|
|
|
|
+ if(!isKeyValid(key))
|
|
|
|
|
+ CM_EXCEPT(InvalidParametersException, "Invalid key name. Please don't use any special character in the key name: " + key);
|
|
|
|
|
+
|
|
|
|
|
+ auto findIter = mKeys.find(key);
|
|
|
|
|
+
|
|
|
|
|
+ if(findIter == mKeys.end() || findIter->second.mapType != VMT_String)
|
|
|
|
|
+ {
|
|
|
|
|
+ KeyMapIndex kmi;
|
|
|
|
|
+ kmi.mapType = VMT_String;
|
|
|
|
|
+ kmi.entryIdx = (UINT32)mStringEntries.size();
|
|
|
|
|
+ mStringEntries.push_back(PrefStringEntries());
|
|
|
|
|
+
|
|
|
|
|
+ mKeys[key] = kmi;
|
|
|
|
|
+
|
|
|
|
|
+ findIter = mKeys.find(key);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ PrefStringEntries& strEntry = mStringEntries[findIter->second.entryIdx];
|
|
|
|
|
+ if(strEntry.entries.size() <= arrayIdx)
|
|
|
|
|
+ strEntry.entries.resize(arrayIdx + 1);
|
|
|
|
|
+
|
|
|
|
|
+ strEntry.entries[arrayIdx] = value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void Preferences::addValue(const String& key, const String& value)
|
|
|
|
|
+ {
|
|
|
|
|
+ UINT32 arrayIdx = getNumValues(key);
|
|
|
|
|
+ setValue(key, value, arrayIdx);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const String& Preferences::getValue(const String& key, const String& defaultValue, UINT32 arrayIdx)
|
|
|
|
|
+ {
|
|
|
|
|
+ auto findIter = mKeys.find(key);
|
|
|
|
|
+
|
|
|
|
|
+ if(findIter == mKeys.end() || findIter->second.mapType != VMT_String)
|
|
|
|
|
+ return defaultValue;
|
|
|
|
|
+
|
|
|
|
|
+ PrefStringEntries& entry = mStringEntries[findIter->second.entryIdx];
|
|
|
|
|
+ if(arrayIdx < entry.entries.size())
|
|
|
|
|
+ return entry.entries[arrayIdx];
|
|
|
|
|
+
|
|
|
|
|
+ return defaultValue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void Preferences::setValue(const String& key, const int value, UINT32 arrayIdx)
|
|
|
|
|
+ {
|
|
|
|
|
+ if(!isKeyValid(key))
|
|
|
|
|
+ CM_EXCEPT(InvalidParametersException, "Invalid key name. Please don't use any special character in the key name: " + key);
|
|
|
|
|
+
|
|
|
|
|
+ auto findIter = mKeys.find(key);
|
|
|
|
|
+
|
|
|
|
|
+ if(findIter == mKeys.end() || findIter->second.mapType != VMT_Int)
|
|
|
|
|
+ {
|
|
|
|
|
+ KeyMapIndex kmi;
|
|
|
|
|
+ kmi.mapType = VMT_Int;
|
|
|
|
|
+ kmi.entryIdx = (UINT32)mIntEntries.size();
|
|
|
|
|
+ mIntEntries.push_back(PrefIntEntries());
|
|
|
|
|
+
|
|
|
|
|
+ mKeys[key] = kmi;
|
|
|
|
|
+
|
|
|
|
|
+ findIter = mKeys.find(key);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ PrefIntEntries& intEntry = mIntEntries[findIter->second.entryIdx];
|
|
|
|
|
+ if(intEntry.entries.size() <= arrayIdx)
|
|
|
|
|
+ intEntry.entries.resize(arrayIdx + 1);
|
|
|
|
|
+
|
|
|
|
|
+ intEntry.entries[arrayIdx] = value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void Preferences::addValue(const String& key, const int value)
|
|
|
|
|
+ {
|
|
|
|
|
+ UINT32 arrayIdx = getNumValues(key);
|
|
|
|
|
+ setValue(key, value, arrayIdx);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int Preferences::getValue(const String& key, int defaultValue, UINT32 arrayIdx)
|
|
|
|
|
+ {
|
|
|
|
|
+ auto findIter = mKeys.find(key);
|
|
|
|
|
+
|
|
|
|
|
+ if(findIter == mKeys.end() || findIter->second.mapType != VMT_Int)
|
|
|
|
|
+ return defaultValue;
|
|
|
|
|
+
|
|
|
|
|
+ PrefIntEntries& entry = mIntEntries[findIter->second.entryIdx];
|
|
|
|
|
+ if(arrayIdx < entry.entries.size())
|
|
|
|
|
+ return entry.entries[arrayIdx];
|
|
|
|
|
+
|
|
|
|
|
+ return defaultValue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void Preferences::setValue(const String& key, float value, UINT32 arrayIdx)
|
|
|
|
|
+ {
|
|
|
|
|
+ if(!isKeyValid(key))
|
|
|
|
|
+ CM_EXCEPT(InvalidParametersException, "Invalid key name. Please don't use any special character in the key name: " + key);
|
|
|
|
|
+
|
|
|
|
|
+ auto findIter = mKeys.find(key);
|
|
|
|
|
+
|
|
|
|
|
+ if(findIter == mKeys.end() || findIter->second.mapType != VMT_Float)
|
|
|
|
|
+ {
|
|
|
|
|
+ KeyMapIndex kmi;
|
|
|
|
|
+ kmi.mapType = VMT_Float;
|
|
|
|
|
+ kmi.entryIdx = (UINT32)mFloatEntries.size();
|
|
|
|
|
+ mFloatEntries.push_back(PrefFloatEntries());
|
|
|
|
|
+
|
|
|
|
|
+ mKeys[key] = kmi;
|
|
|
|
|
+
|
|
|
|
|
+ findIter = mKeys.find(key);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ PrefFloatEntries& floatEntry = mFloatEntries[findIter->second.entryIdx];
|
|
|
|
|
+ if(floatEntry.entries.size() <= arrayIdx)
|
|
|
|
|
+ floatEntry.entries.resize(arrayIdx + 1);
|
|
|
|
|
+
|
|
|
|
|
+ floatEntry.entries[arrayIdx] = value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void Preferences::addValue(const String& key, float value)
|
|
|
|
|
+ {
|
|
|
|
|
+ UINT32 arrayIdx = getNumValues(key);
|
|
|
|
|
+ setValue(key, value, arrayIdx);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ float Preferences::getValue(const String& key, float defaultValue, UINT32 arrayIdx)
|
|
|
|
|
+ {
|
|
|
|
|
+ auto findIter = mKeys.find(key);
|
|
|
|
|
+
|
|
|
|
|
+ if(findIter == mKeys.end() || findIter->second.mapType != VMT_Float)
|
|
|
|
|
+ return defaultValue;
|
|
|
|
|
+
|
|
|
|
|
+ PrefFloatEntries& entry = mFloatEntries[findIter->second.entryIdx];
|
|
|
|
|
+ if(arrayIdx < entry.entries.size())
|
|
|
|
|
+ return entry.entries[arrayIdx];
|
|
|
|
|
+
|
|
|
|
|
+ return defaultValue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ UINT32 Preferences::getNumValues(const String& key)
|
|
|
|
|
+ {
|
|
|
|
|
+ auto findIter = mKeys.find(key);
|
|
|
|
|
+
|
|
|
|
|
+ if(findIter == mKeys.end())
|
|
|
|
|
+ return 0;
|
|
|
|
|
+
|
|
|
|
|
+ switch(findIter->second.mapType)
|
|
|
|
|
+ {
|
|
|
|
|
+ case VMT_String:
|
|
|
|
|
+ return (UINT32)mStringEntries[findIter->second.entryIdx].entries.size();
|
|
|
|
|
+ case VMT_Float:
|
|
|
|
|
+ return (UINT32)mFloatEntries[findIter->second.entryIdx].entries.size();
|
|
|
|
|
+ case VMT_Int:
|
|
|
|
|
+ return (UINT32)mIntEntries[findIter->second.entryIdx].entries.size();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool Preferences::isKeyValid(const String& key) const
|
|
|
|
|
+ {
|
|
|
|
|
+ String::size_type foundComma = key.find(",");
|
|
|
|
|
+
|
|
|
|
|
+ return foundComma == String::npos;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void Preferences::save(const String& path, bool overwrite) const
|
|
|
|
|
+ {
|
|
|
|
|
+ if(FileSystem::fileExists(path))
|
|
|
|
|
+ {
|
|
|
|
|
+ if(!overwrite)
|
|
|
|
|
+ {
|
|
|
|
|
+ CM_EXCEPT(FileNotFoundException, "File already exists at this location.");
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ FileSystem::remove(path);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ map<String, vector<EntryToSave>::type>::type groups;
|
|
|
|
|
+ for(auto iter = mKeys.begin(); iter != mKeys.end(); ++iter)
|
|
|
|
|
+ {
|
|
|
|
|
+ vector<String>::type splitStr = StringUtil::split(iter->first, ".", 1);
|
|
|
|
|
+
|
|
|
|
|
+ String groupName;
|
|
|
|
|
+ String paramName;
|
|
|
|
|
+ if(splitStr.size() > 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ groupName = splitStr[0];
|
|
|
|
|
+ paramName = splitStr[1];
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ groupName = "Global";
|
|
|
|
|
+ paramName = splitStr[0];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ switch(iter->second.mapType)
|
|
|
|
|
+ {
|
|
|
|
|
+ case VMT_String:
|
|
|
|
|
+ {
|
|
|
|
|
+ const PrefStringEntries& entry = mStringEntries[iter->second.entryIdx];
|
|
|
|
|
+
|
|
|
|
|
+ if(entry.entries.size() > 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ for(size_t i = 0; i < entry.entries.size(); i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ EntryToSave newEntry;
|
|
|
|
|
+ newEntry.name = paramName + "," + toString(i);
|
|
|
|
|
+ newEntry.value = "\"" + entry.entries[i] + "\"";
|
|
|
|
|
+
|
|
|
|
|
+ groups[groupName].push_back(newEntry);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ EntryToSave newEntry;
|
|
|
|
|
+ newEntry.name = paramName;
|
|
|
|
|
+ newEntry.value = "\"" + entry.entries[0] + "\"";
|
|
|
|
|
+
|
|
|
|
|
+ groups[groupName].push_back(newEntry);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case VMT_Float:
|
|
|
|
|
+ {
|
|
|
|
|
+ const PrefFloatEntries& entry = mFloatEntries[iter->second.entryIdx];
|
|
|
|
|
+
|
|
|
|
|
+ if(entry.entries.size() > 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ for(size_t i = 0; i < entry.entries.size(); i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ EntryToSave newEntry;
|
|
|
|
|
+ newEntry.name = paramName + "," + toString(i);
|
|
|
|
|
+ newEntry.value = toString(entry.entries[i], 12) + "f";
|
|
|
|
|
+
|
|
|
|
|
+ groups[groupName].push_back(newEntry);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ EntryToSave newEntry;
|
|
|
|
|
+ newEntry.name = paramName;
|
|
|
|
|
+ newEntry.value = toString(entry.entries[0], 12) + "f";
|
|
|
|
|
+
|
|
|
|
|
+ groups[groupName].push_back(newEntry);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case VMT_Int:
|
|
|
|
|
+ {
|
|
|
|
|
+ const PrefIntEntries& entry = mIntEntries[iter->second.entryIdx];
|
|
|
|
|
+
|
|
|
|
|
+ if(entry.entries.size() > 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ for(size_t i = 0; i < entry.entries.size(); i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ EntryToSave newEntry;
|
|
|
|
|
+ newEntry.name = paramName + "," + toString(i);
|
|
|
|
|
+ newEntry.value = toString(entry.entries[i]);
|
|
|
|
|
+
|
|
|
|
|
+ groups[groupName].push_back(newEntry);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ EntryToSave newEntry;
|
|
|
|
|
+ newEntry.name = paramName;
|
|
|
|
|
+ newEntry.value = toString(entry.entries[0]);
|
|
|
|
|
+
|
|
|
|
|
+ groups[groupName].push_back(newEntry);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ stringstream strStream;
|
|
|
|
|
+ for(auto iter = groups.begin(); iter != groups.end(); ++iter)
|
|
|
|
|
+ {
|
|
|
|
|
+ strStream<<"["<<iter->first<<"]\r\n";
|
|
|
|
|
+
|
|
|
|
|
+ for(auto iter2 = iter->second.begin(); iter2 != iter->second.end(); ++iter2)
|
|
|
|
|
+ {
|
|
|
|
|
+ strStream<<iter2->name<<" = "<<iter2->value<<"\r\n";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ strStream<<"\r\n";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String outputString = strStream.str();
|
|
|
|
|
+
|
|
|
|
|
+ DataStreamPtr stream = FileSystem::create(path);
|
|
|
|
|
+ stream->write(outputString.data(), outputString.size());
|
|
|
|
|
+ stream->close();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void Preferences::load(const String& path)
|
|
|
|
|
+ {
|
|
|
|
|
+ if(!FileSystem::fileExists(path))
|
|
|
|
|
+ CM_EXCEPT(FileNotFoundException, "Specified file: " + path + " does not exist.");
|
|
|
|
|
+
|
|
|
|
|
+ DataStreamPtr stream = FileSystem::open(path);
|
|
|
|
|
+ char buffer[4096];
|
|
|
|
|
+
|
|
|
|
|
+ String currentGroupName = "Global";
|
|
|
|
|
+ bool groupIsDefault = true;
|
|
|
|
|
+ int foundGroups = 0;
|
|
|
|
|
+ while(!stream->eof())
|
|
|
|
|
+ {
|
|
|
|
|
+ stream->readLine(buffer, sizeof(buffer));
|
|
|
|
|
+ String line(buffer);
|
|
|
|
|
+
|
|
|
|
|
+ String::size_type bracketsStart = line.find("[");
|
|
|
|
|
+ String::size_type bracketsEnd = line.find("]");
|
|
|
|
|
+ if (bracketsStart != String::npos && bracketsEnd != String::npos)
|
|
|
|
|
+ {
|
|
|
|
|
+ currentGroupName = line.substr(bracketsStart + 1, bracketsEnd - (bracketsStart + 1));
|
|
|
|
|
+ groupIsDefault = foundGroups == 0;
|
|
|
|
|
+ foundGroups++;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ vector<String>::type paramSplit = StringUtil::split(line, "=");
|
|
|
|
|
+ if(paramSplit.size() < 2)
|
|
|
|
|
+ {
|
|
|
|
|
+ LOGWRN("Cannot parse line in file " + path + ". Invalid number of elements: " + line);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String paramName = paramSplit[0];
|
|
|
|
|
+ StringUtil::trim(paramName);
|
|
|
|
|
+
|
|
|
|
|
+ vector<String>::type arrayElemSplit = StringUtil::split(paramName, ",");
|
|
|
|
|
+ UINT32 arrayIdx = 0;
|
|
|
|
|
+ if(arrayElemSplit.size() > 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ String arrayElemStr = arrayElemSplit[1];
|
|
|
|
|
+ StringUtil::trim(arrayElemStr);
|
|
|
|
|
+ arrayIdx = parseInt(arrayElemStr);
|
|
|
|
|
+
|
|
|
|
|
+ paramName = arrayElemSplit[0];
|
|
|
|
|
+ StringUtil::trim(paramName);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(!groupIsDefault)
|
|
|
|
|
+ paramName = currentGroupName + "." + paramName;
|
|
|
|
|
+
|
|
|
|
|
+ String paramValue = paramSplit[1];
|
|
|
|
|
+ StringUtil::trim(paramValue);
|
|
|
|
|
+
|
|
|
|
|
+ // Float
|
|
|
|
|
+ if(StringUtil::endsWith(paramValue, "f"))
|
|
|
|
|
+ {
|
|
|
|
|
+ paramValue.erase(paramValue.begin() + paramValue.size() - 1);
|
|
|
|
|
+
|
|
|
|
|
+ float fltValue = parseFloat(paramValue);
|
|
|
|
|
+ setValue(paramName, fltValue, arrayIdx);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if(StringUtil::endsWith(paramValue, "\"")) // String
|
|
|
|
|
+ {
|
|
|
|
|
+ paramValue.erase(paramValue.begin());
|
|
|
|
|
+ paramValue.erase(paramValue.begin() + paramValue.size() - 1);
|
|
|
|
|
+
|
|
|
|
|
+ setValue(paramName, paramValue, arrayIdx);
|
|
|
|
|
+ }
|
|
|
|
|
+ else // Assuming int
|
|
|
|
|
+ {
|
|
|
|
|
+ int intValue = parseInt(paramValue);
|
|
|
|
|
+ setValue(paramName, intValue, arrayIdx);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ stream->close();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|