Browse Source

Added preferences for easy handling of .ini files

Marko Pintera 13 years ago
parent
commit
16547b8381

+ 2 - 0
CamelotEditor/CamelotEditor.vcxproj

@@ -157,6 +157,7 @@
     </ClCompile>
     <ClCompile Include="Source\CmEditorApplication.cpp" />
     <ClCompile Include="Source\CmEditorPrefs.cpp" />
+    <ClCompile Include="Source\CmPreferences.cpp" />
     <ClCompile Include="Source\CmQtEditor.cpp" />
     <ClCompile Include="Source\CmQtNewProject.cpp" />
     <ClCompile Include="Source\CmQtProjectSelection.cpp" />
@@ -207,6 +208,7 @@
     <ClInclude Include="Include\CmEditorPrefs.h" />
     <ClInclude Include="Include\CmEditorPrerequisites.h" />
     <ClInclude Include="Include\CmLayoutManager.h" />
+    <ClInclude Include="Include\CmPreferences.h" />
     <CustomBuild Include="Include\CmQtNewProject.h">
       <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\moc_%(Filename).cpp"  -DQT_NO_KEYWORDS -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL  "-I.\GeneratedFiles" "-I.\Include" "-I.\Dependencies\Include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\CamelotCore\Include" "-I.\..\CamelotUtility\Include" "-I.\..\Dependencies\Include"</Command>
       <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Filename)...</Message>

+ 6 - 0
CamelotEditor/CamelotEditor.vcxproj.filters

@@ -57,6 +57,9 @@
     <ClCompile Include="GeneratedFiles\moc_CmQtNewProject.cpp">
       <Filter>Generated Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\CmPreferences.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="CmEditor.qrc">
@@ -85,5 +88,8 @@
     <ClInclude Include="Include\CmEditorPrerequisites.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmPreferences.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>

+ 69 - 0
CamelotEditor/Include/CmPreferences.h

@@ -0,0 +1,69 @@
+#pragma once
+
+#include "CmEditorPrerequisites.h"
+
+namespace CamelotEditor
+{
+	class Preferences
+	{
+	public:
+		void setValue(const String& key, const String& value, CamelotEngine::UINT32 arrayIdx = 0);
+		void addValue(const String& key, const String& value);
+		const String& getValue(const String& key, const String& defaultValue = StringUtil::BLANK, CamelotEngine::UINT32 arrayIdx = 0);
+
+		void setValue(const String& key, const int value, CamelotEngine::UINT32 arrayIdx = 0);
+		void addValue(const String& key, const int value);
+		int getValue(const String& key, int defaultValue = 0, CamelotEngine::UINT32 arrayIdx = 0);
+
+		void setValue(const String& key, float value, CamelotEngine::UINT32 arrayIdx = 0);
+		void addValue(const String& key, float value);
+		float getValue(const String& key, float defaultValue = 0.0f, CamelotEngine::UINT32 arrayIdx = 0);
+
+		CamelotEngine::UINT32 getNumValues(const String& key);
+
+		void save(const String& path, bool overwrite = true) const;
+		void load(const String& path);
+
+	private:
+		struct PrefStringEntries
+		{
+			vector<String>::type entries;
+		};
+
+		struct PrefIntEntries
+		{
+			vector<int>::type entries;
+		};
+
+		struct PrefFloatEntries
+		{
+			vector<float>::type entries;
+		};
+
+		enum ValueMapType
+		{
+			VMT_String,
+			VMT_Int,
+			VMT_Float
+		};
+
+		struct KeyMapIndex
+		{
+			ValueMapType mapType;
+			CamelotEngine::UINT32 entryIdx;
+		};
+
+		struct EntryToSave
+		{
+			String name;
+			String value;
+		};
+
+		map<String, KeyMapIndex>::type	mKeys;
+		vector<PrefStringEntries>::type mStringEntries;
+		vector<PrefIntEntries>::type	mIntEntries;
+		vector<PrefFloatEntries>::type	mFloatEntries;
+
+		bool isKeyValid(const String& key) const;
+	};
+}

+ 382 - 0
CamelotEditor/Source/CmPreferences.cpp

@@ -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();
+	}
+}

+ 17 - 0
CamelotEditor/Source/main.cpp

@@ -1,6 +1,8 @@
 #include <windows.h>
 #include "CmEditorApplication.h"
 
+#include "CmPreferences.h"
+
 using namespace CamelotEditor;
 
 int CALLBACK WinMain(
@@ -10,6 +12,21 @@ int CALLBACK WinMain(
 	_In_  int nCmdShow
 	)
 {
+	Preferences prefs;
+	prefs.setValue("testA", "Haloo sta ima!?");
+	prefs.setValue("testA", "Haloo sta ima 222!?", 1);
+	prefs.addValue("testFlt", 10.534536346f);
+	prefs.addValue("testFlt", 5.2423423423f);
+	prefs.setValue("testInt", 10);
+	prefs.setValue("SomeGroup.testInt1", 10);
+	prefs.setValue("SomeGroup.testInt2", 11);
+	prefs.setValue("SomeGroup.testInt3", 12);
+
+	prefs.save("D:\\testPrefs.ini");
+
+	prefs.load("D:\\testPrefs.ini");
+	prefs.save("D:\\testPrefs2.ini");
+
 	gEditorApp().startUp();
 
 	gEditorApp().run();