Browse Source

Support for multiple StringTables
Made StringTable a resource and added its RTTI

Marko Pintera 10 years ago
parent
commit
d293414daf
38 changed files with 1116 additions and 325 deletions
  1. 7 0
      BansheeCore/BansheeCore.vcxproj
  2. 27 0
      BansheeCore/BansheeCore.vcxproj.filters
  3. 8 1
      BansheeCore/Include/BsCorePrerequisites.h
  4. 1 3
      BansheeCore/Include/BsHString.h
  5. 53 16
      BansheeCore/Include/BsStringTable.h
  6. 49 0
      BansheeCore/Include/BsStringTableManager.h
  7. 200 0
      BansheeCore/Include/BsStringTableRTTI.h
  8. 3 3
      BansheeCore/Source/BsCoreApplication.cpp
  9. 7 6
      BansheeCore/Source/BsHString.cpp
  10. 42 1
      BansheeCore/Source/BsStringTable.cpp
  11. 46 0
      BansheeCore/Source/BsStringTableManager.cpp
  12. 2 2
      BansheeEngine/Include/BsScriptCode.h
  13. 0 4
      BansheeUtility/BansheeUtility.vcxproj
  14. 0 15
      BansheeUtility/BansheeUtility.vcxproj.filters
  15. 0 1
      BansheeUtility/Include/BsEvent.h
  16. 2 1
      BansheeUtility/Include/BsFwdDeclUtil.h
  17. 2 1
      BansheeUtility/Include/BsPrerequisitesUtil.h
  18. 89 8
      BansheeUtility/Include/BsRTTIPrerequisites.h
  19. 1 4
      BansheeUtility/Include/BsString.h
  20. 1 0
      MBansheeEngine/MBansheeEngine.csproj
  21. 47 214
      MBansheeEngine/StringTable.cs
  22. 237 0
      MBansheeEngine/StringTables.cs
  23. 17 0
      SBansheeEditor/Source/BsGUIResourceField.cpp
  24. 1 0
      SBansheeEngine/Include/BsManagedSerializableObjectInfo.h
  25. 3 1
      SBansheeEngine/Include/BsScriptAssemblyManager.h
  26. 1 0
      SBansheeEngine/Include/BsScriptEnginePrerequisites.h
  27. 17 0
      SBansheeEngine/Include/BsScriptResourceManager.h
  28. 21 8
      SBansheeEngine/Include/BsScriptStringTable.h
  29. 25 0
      SBansheeEngine/Include/BsScriptStringTableManager.h
  30. 2 0
      SBansheeEngine/SBansheeEngine.vcxproj
  31. 6 0
      SBansheeEngine/SBansheeEngine.vcxproj.filters
  32. 28 0
      SBansheeEngine/Source/BsManagedSerializableField.cpp
  33. 2 0
      SBansheeEngine/Source/BsManagedSerializableObjectInfo.cpp
  34. 12 1
      SBansheeEngine/Source/BsScriptAssemblyManager.cpp
  35. 27 0
      SBansheeEngine/Source/BsScriptResourceManager.cpp
  36. 66 17
      SBansheeEngine/Source/BsScriptStringTable.cpp
  37. 60 0
      SBansheeEngine/Source/BsScriptStringTableManager.cpp
  38. 4 18
      TODO.txt

+ 7 - 0
BansheeCore/BansheeCore.vcxproj

@@ -280,6 +280,7 @@
   <ItemGroup>
     <ClInclude Include="Include\BsCoreObjectCore.h" />
     <ClInclude Include="Include\BsDrawList.h" />
+    <ClInclude Include="Include\BsHString.h" />
     <ClInclude Include="Include\BsMeshImportOptions.h" />
     <ClInclude Include="Include\BsMeshImportOptionsRTTI.h" />
     <ClInclude Include="Include\BsMeshUtility.h" />
@@ -363,6 +364,9 @@
     <ClInclude Include="Include\BsPixelBuffer.h" />
     <ClInclude Include="Include\BsShaderIncludeImporter.h" />
     <ClInclude Include="Include\BsShaderManager.h" />
+    <ClInclude Include="Include\BsStringTable.h" />
+    <ClInclude Include="Include\BsStringTableManager.h" />
+    <ClInclude Include="Include\BsStringTableRTTI.h" />
     <ClInclude Include="Include\BsSubMesh.h" />
     <ClInclude Include="Include\BsTextureImportOptions.h" />
     <ClInclude Include="Include\BsTextureImportOptionsRTTI.h" />
@@ -438,6 +442,7 @@
     <ClCompile Include="Source\BsCoreObjectCore.cpp" />
     <ClCompile Include="Source\BsCoreThread.cpp" />
     <ClCompile Include="Source\BsDrawList.cpp" />
+    <ClCompile Include="Source\BsHString.cpp" />
     <ClCompile Include="Source\BsIResourceListener.cpp" />
     <ClCompile Include="Source\BsMaterialParam.cpp" />
     <ClCompile Include="Source\BsMeshImportOptions.cpp" />
@@ -495,6 +500,8 @@
     <ClCompile Include="Source\BsResourceManifest.cpp" />
     <ClCompile Include="Source\BsResourceMetaData.cpp" />
     <ClCompile Include="Source\BsShaderManager.cpp" />
+    <ClCompile Include="Source\BsStringTable.cpp" />
+    <ClCompile Include="Source\BsStringTableManager.cpp" />
     <ClCompile Include="Source\BsTextureImportOptions.cpp" />
     <ClCompile Include="Source\BsTextureView.cpp" />
     <ClCompile Include="Source\BsTextData.cpp" />

+ 27 - 0
BansheeCore/BansheeCore.vcxproj.filters

@@ -84,6 +84,12 @@
     <Filter Include="Source Files\Utility">
       <UniqueIdentifier>{0d63b345-0a58-4df2-9d01-f4da53fc40c9}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Header Files\Localization">
+      <UniqueIdentifier>{df01dcc2-a0b0-48a8-a6fd-59a556cb67f9}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\Localization">
+      <UniqueIdentifier>{f8c05475-0bc9-44d9-9702-985ec016f0ba}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Include\BsRenderStats.h">
@@ -551,6 +557,18 @@
     <ClInclude Include="Include\BsRendererMeshData.h">
       <Filter>Header Files\Renderer</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsHString.h">
+      <Filter>Header Files\Localization</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsStringTable.h">
+      <Filter>Header Files\Localization</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsStringTableManager.h">
+      <Filter>Header Files\Localization</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsStringTableRTTI.h">
+      <Filter>Header Files\RTTI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsCoreApplication.cpp">
@@ -874,5 +892,14 @@
     <ClCompile Include="Source\BsRendererMeshData.cpp">
       <Filter>Source Files\Renderer</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsHString.cpp">
+      <Filter>Source Files\Localization</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsStringTable.cpp">
+      <Filter>Source Files\Localization</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsStringTableManager.cpp">
+      <Filter>Source Files\Localization</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 8 - 1
BansheeCore/Include/BsCorePrerequisites.h

@@ -57,6 +57,8 @@
 
 #endif
 
+#include "BsHString.h"
+
 namespace BansheeEngine 
 {
 	static const StringID RenderAPIAny = "AnyRenderAPI";
@@ -186,6 +188,7 @@ namespace BansheeEngine
 	class Font;
 	class ResourceMetaData;
 	class OSDropTarget;
+	class StringTable;
 	// Scene
 	class SceneObject;
 	class Component;
@@ -342,7 +345,10 @@ namespace BansheeEngine
 		TID_PrefabObjectDiff = 1079,
 		TID_PrefabComponentDiff = 1080,
 		TID_GUIWidget = 1081,
-		TID_ProfilerOverlay = 1082
+		TID_ProfilerOverlay = 1082,
+		TID_StringTable = 1083,
+		TID_LanguageData = 1084,
+		TID_LocalizedStringData = 1085
 	};
 }
 
@@ -363,6 +369,7 @@ namespace BansheeEngine
 	typedef ResourceHandle<Font> HFont;
 	typedef ResourceHandle<Shader> HShader;
 	typedef ResourceHandle<Prefab> HPrefab;
+	typedef ResourceHandle<StringTable> HStringTable;
 }
 
 namespace BansheeEngine

+ 1 - 3
BansheeUtility/Include/BsHString.h → BansheeCore/Include/BsHString.h

@@ -1,7 +1,5 @@
 #pragma once
 
-#include "BsEvent.h"
-
 namespace BansheeEngine
 {
 	/**
@@ -14,7 +12,7 @@ namespace BansheeEngine
 	 *			
 	 *			Use {0}, {1}, etc. in the string value for values that might change dynamically.
 	 */
-	class BS_UTILITY_EXPORT HString
+	class BS_CORE_EXPORT HString
 	{
 	public:
 		/**

+ 53 - 16
BansheeUtility/Include/BsStringTable.h → BansheeCore/Include/BsStringTable.h

@@ -1,8 +1,7 @@
 #pragma once
 
-#include "BsPrerequisitesUtil.h"
-#include "BsModule.h"
-#include "BsEvent.h"
+#include "BsCorePrerequisites.h"
+#include "BsResource.h"
 
 namespace BansheeEngine
 {
@@ -230,37 +229,42 @@ namespace BansheeEngine
 		void updateString(const WString& string);
 	};
 
+	/**
+	 * @brief	Data for a single language in the string table.
+	 */
+	struct LanguageData
+	{
+		UnorderedMap<WString, SPtr<LocalizedStringData>> strings;
+	};
+
 	/**
 	 * @brief	Class that handles string localization. Stores strings and their translations
 	 * 			in various languages, along with the ability to switch currently active language.
 	 */
-	class BS_UTILITY_EXPORT StringTable : public Module<StringTable>
+	class BS_CORE_EXPORT StringTable : public Resource
 	{
 		// TODO - When editing string table I will need to ensure that all languages of the same string have the same number of parameters
 
-		struct LanguageData
-		{
-			UnorderedMap<WString, SPtr<LocalizedStringData>> strings;
-		};
+
 	public:
 		StringTable();
 		~StringTable();
 
 		/**
-		 * @brief	Gets the currently active language.
+		 * @brief	Returns all identifiers in the table.
 		 */
-		Language getActiveLanguage() const { return mActiveLanguage; }
+		const UnorderedSet<WString>& getIdentifiers() const { return mIdentifiers; }
 
 		/**
-		 * @brief	Changes the currently active language.
-		 * 			This will update any localized string instances.
+		 * @brief	Adds or modifies string translation for the specified language.
 		 */
-		void setActiveLanguage(Language language);
+		void setString(const WString& identifier, Language language, const WString& string);
 
 		/**
-		 * @brief	Adds or modifies string translation for the specified language.
+		 * @brief	Returns a string translation for the specified language. Returns
+		 *			the identifier itself if one doesn't exist.
 		 */
-		void setString(const WString& identifier, Language language, const WString& string);
+		WString getString(const WString& identifier, Language language);
 
 		/**
 		 * @brief	Removes the string described by identifier, from all languages.
@@ -292,15 +296,48 @@ namespace BansheeEngine
 		 */
 		SPtr<LocalizedStringData> getStringData(const WString& identifier, Language language, bool insertIfNonExisting = true);
 
+		/**
+		 * @brief	Creates a new empty string table resource.
+		 */
+		static HStringTable create();
+
+		/**
+		 * @brief	Creates a new empty string table resource.
+		 *
+		 * @note	Internal method. Use "create" for normal use.
+		 */
+		static SPtr<StringTable> _createPtr();
+
+		static const Language DEFAULT_LANGUAGE;
 	private:
 		friend class HString;
+		friend class StringTableManager;
 
-		static const Language DEFAULT_LANGUAGE;
+		/**
+		 * @brief	Gets the currently active language.
+		 */
+		Language getActiveLanguage() const { return mActiveLanguage; }
+
+		/**
+		 * @brief	Changes the currently active language.
+		 * 			Any newly created strings will use this value.
+		 */
+		void setActiveLanguage(Language language);
 
 		Language mActiveLanguage;
 		LanguageData* mActiveLanguageData;
 		LanguageData* mDefaultLanguageData;
 
 		LanguageData* mAllLanguages;
+
+		UnorderedSet<WString> mIdentifiers;
+
+		/************************************************************************/
+		/* 								SERIALIZATION                      		*/
+		/************************************************************************/
+	public:
+		friend class StringTableRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		virtual RTTITypeBase* getRTTI() const override;
 	};
 }

+ 49 - 0
BansheeCore/Include/BsStringTableManager.h

@@ -0,0 +1,49 @@
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsModule.h"
+#include "BsStringTable.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Manages string tables used for localizing text.
+	 */
+	class BS_CORE_EXPORT StringTableManager : public Module<StringTableManager>
+	{
+	public:
+		StringTableManager();
+
+		/**
+		 * @brief	Gets the currently active language.
+		 */
+		Language getActiveLanguage() const { return mActiveLanguage; }
+
+		/**
+		 * @brief	Changes the currently active language.
+		 *			Any newly created strings will use this value.
+		 */
+		void setActiveLanguage(Language language);
+
+		/**
+		 * @brief	Returns the string table with the specified id.
+		 *			If the table doesn't exist new one is created.
+		 */
+		HStringTable getTable(UINT32 id);
+
+		/**
+		 * @brief	Removes the string table with the specified id.
+		 */
+		void removeTable(UINT32 id);
+
+		/**
+		 * @brief	Registers a new, or replaces an old string table at
+		 *			the specified id.
+		 */
+		void setTable(UINT32 id, HStringTable table);
+
+	private:
+		Language mActiveLanguage;
+		UnorderedMap<UINT32, HStringTable> mTables;
+	};
+}

+ 200 - 0
BansheeCore/Include/BsStringTableRTTI.h

@@ -0,0 +1,200 @@
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsRTTIType.h"
+#include "BsStringTable.h"
+
+namespace BansheeEngine
+{
+	class BS_CORE_EXPORT StringTableRTTI : public RTTIType<StringTable, IReflectable, StringTableRTTI>
+	{
+	private:
+		Language& getActiveLanguage(StringTable* obj) { return obj->mActiveLanguage; }
+		void setActiveLanguage(StringTable* obj, Language& val) { obj->mActiveLanguage = val; }
+
+		LanguageData& getLanguageData(StringTable* obj, UINT32 idx) { return obj->mAllLanguages[idx]; }
+		void setLanguageData(StringTable* obj, UINT32 idx, LanguageData& val) { obj->mAllLanguages[idx] = val; }
+		UINT32 getNumLanguages(StringTable* obj) { return (UINT32)Language::Count; }
+		void setNumLanguages(StringTable* obj, UINT32 val) { /* Do nothing */ }
+
+		UnorderedSet<WString>& getIdentifiers(StringTable* obj) { return obj->mIdentifiers; }
+		void setIdentifiers(StringTable* obj, UnorderedSet<WString>& val) { obj->mIdentifiers = val; }
+
+	public:
+		StringTableRTTI()
+		{
+			addPlainField("mActiveLanguage", 0, &StringTableRTTI::getActiveLanguage, &StringTableRTTI::setActiveLanguage);
+			addPlainArrayField("mLanguageData", 1, &StringTableRTTI::getLanguageData, &StringTableRTTI::getNumLanguages, 
+				&StringTableRTTI::setLanguageData, &StringTableRTTI::setNumLanguages);
+			addPlainField("mIdentifiers", 2, &StringTableRTTI::getIdentifiers, &StringTableRTTI::setIdentifiers);
+		}
+
+		virtual void onDeserializationEnded(IReflectable* obj) override
+		{
+			StringTable* stringTable = static_cast<StringTable*>(obj);
+			stringTable->setActiveLanguage(stringTable->mActiveLanguage);
+		}
+
+		virtual const String& getRTTIName() override
+		{
+			static String name = "StringTable";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId() override
+		{
+			return TID_StringTable;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject() override
+		{
+			return StringTable::_createPtr();
+		}
+	};
+
+	/**
+	 * @brief	RTTIPlainType for LanguageData.
+	 * 			
+	 * @see		RTTIPlainType
+	 */
+	template<>
+	struct RTTIPlainType<LanguageData>
+	{	
+		enum { id = TID_LanguageData }; enum { hasDynamicSize = 1 };
+
+		/**
+		 * @copydoc		RTTIPlainType::toMemory
+		 */
+		static void toMemory(const LanguageData& data, char* memory)
+		{ 
+			UINT32 size = sizeof(UINT32);
+			char* memoryStart = memory;
+			memory += sizeof(UINT32);
+
+			UINT32 numElements = (UINT32)data.strings.size();
+			memory = rttiWriteElem(numElements, memory, size);
+
+			for (auto& entry : data.strings)
+			{
+				memory = rttiWriteElem(entry.first, memory, size);
+				memory = rttiWriteElem(*entry.second, memory, size);
+			}
+			
+			memcpy(memoryStart, &size, sizeof(UINT32));
+		}
+
+		/**
+		 * @copydoc		RTTIPlainType::fromMemory
+		 */
+		static UINT32 fromMemory(LanguageData& data, char* memory)
+		{ 
+			UINT32 size = 0;
+			memory = rttiReadElem(size, memory);
+
+			UINT32 numElements = 0;
+			memory = rttiReadElem(numElements, memory);
+
+			data.strings.clear();
+			for (UINT32 i = 0; i < numElements; i++)
+			{
+				WString identifier;
+				memory = rttiReadElem(identifier, memory);
+
+				SPtr<LocalizedStringData> entryData = bs_shared_ptr<LocalizedStringData>();
+				memory = rttiReadElem(*entryData, memory);
+
+				data.strings[identifier] = entryData;
+			}
+
+			return size;
+		}
+
+		/**
+		 * @copydoc		RTTIPlainType::getDynamicSize
+		 */
+		static UINT32 getDynamicSize(const LanguageData& data)
+		{ 
+			UINT64 dataSize = sizeof(UINT32) * 2;
+
+			for (auto& entry : data.strings)
+			{
+				dataSize += rttiGetElemSize(entry.first);
+				dataSize += rttiGetElemSize(*entry.second);
+			}
+
+			assert(dataSize <= std::numeric_limits<UINT32>::max());
+
+			return (UINT32)dataSize;
+		}	
+	}; 
+
+	/**
+	 * @brief	RTTIPlainType for LocalizedStringData.
+	 * 			
+	 * @see		RTTIPlainType
+	 */
+	template<>
+	struct RTTIPlainType<LocalizedStringData>
+	{	
+		enum { id = TID_LocalizedStringData }; enum { hasDynamicSize = 1 };
+
+		/**
+		 * @copydoc		RTTIPlainType::toMemory
+		 */
+		static void toMemory(const LocalizedStringData& data, char* memory)
+		{ 
+			UINT32 size = sizeof(UINT32);
+			char* memoryStart = memory;
+			memory += sizeof(UINT32);
+
+			memory = rttiWriteElem(data.string, memory, size);
+			memory = rttiWriteElem(data.numParameters, memory, size);
+
+			for (UINT32 i = 0; i < data.numParameters; i++)
+				memory = rttiWriteElem(data.parameterOffsets[i], memory, size);
+
+			memcpy(memoryStart, &size, sizeof(UINT32));
+		}
+
+		/**
+		 * @copydoc		RTTIPlainType::fromMemory
+		 */
+		static UINT32 fromMemory(LocalizedStringData& data, char* memory)
+		{ 
+			if (data.parameterOffsets != nullptr)
+				bs_deleteN(data.parameterOffsets, data.numParameters);
+
+			UINT32 size = 0;
+			memory = rttiReadElem(size, memory);
+
+			memory = rttiReadElem(data.string, memory);
+			memory = rttiReadElem(data.numParameters, memory);
+
+			data.parameterOffsets = bs_newN<LocalizedStringData::ParamOffset>(data.numParameters);
+			for (UINT32 i = 0; i < data.numParameters; i++)
+				memory = rttiReadElem(data.parameterOffsets[i], memory);
+
+			return size;
+		}
+
+		/**
+		 * @copydoc		RTTIPlainType::getDynamicSize
+		 */
+		static UINT32 getDynamicSize(const LocalizedStringData& data)
+		{ 
+			UINT64 dataSize = sizeof(UINT32);
+
+			dataSize += rttiGetElemSize(data.string);
+			dataSize += rttiGetElemSize(data.numParameters);
+
+			for (UINT32 i = 0; i < data.numParameters; i++)
+				dataSize = rttiGetElemSize(data.parameterOffsets[i]);
+
+			assert(dataSize <= std::numeric_limits<UINT32>::max());
+
+			return (UINT32)dataSize;
+		}	
+	}; 
+
+	BS_ALLOW_MEMCPY_SERIALIZATION(LocalizedStringData::ParamOffset);
+}

+ 3 - 3
BansheeCore/Source/BsCoreApplication.cpp

@@ -29,7 +29,7 @@
 #include "BsCoreRenderer.h"
 #include "BsDeferredCallManager.h"
 #include "BsCoreThread.h"
-#include "BsStringTable.h"
+#include "BsStringTableManager.h"
 #include "BsProfilingManager.h"
 #include "BsProfilerCPU.h"
 #include "BsProfilerGPU.h"
@@ -101,7 +101,7 @@ namespace BansheeEngine
 		DynLibManager::shutDown();
 		Time::shutDown();
 		DeferredCallManager::shutDown();
-		StringTable::shutDown();
+		StringTableManager::shutDown();
 
 		CoreThread::shutDown();
 		RenderStats::shutDown();
@@ -134,7 +134,7 @@ namespace BansheeEngine
 		TaskScheduler::instance().removeWorker();
 		RenderStats::startUp();
 		CoreThread::startUp();
-		StringTable::startUp();
+		StringTableManager::startUp();
 		DeferredCallManager::startUp();
 		Time::startUp();
 		DynLibManager::startUp();

+ 7 - 6
BansheeUtility/Source/BsHString.cpp → BansheeCore/Source/BsHString.cpp

@@ -1,13 +1,13 @@
-#include "BsPrerequisitesUtil.h"
+#include "BsCorePrerequisites.h"
 #include "BsHString.h"
-#include "BsStringTable.h"
+#include "BsStringTableManager.h"
 
 namespace BansheeEngine
 {
 	HString::HString(UINT32 stringTableId)
 		:mParameters(nullptr), mIsDirty(true), mStringPtr(nullptr)
 	{
-		mStringData = StringTable::instance().getStringData(L"");
+		mStringData = StringTableManager::instance().getTable(stringTableId)->getStringData(L"");
 
 		if(mStringData->numParameters > 0)
 			mParameters = bs_newN<WString>(mStringData->numParameters);
@@ -16,7 +16,7 @@ namespace BansheeEngine
 	HString::HString(const WString& identifierString, UINT32 stringTableId)
 		:mParameters(nullptr), mIsDirty(true), mStringPtr(nullptr)
 	{
-		mStringData = StringTable::instance().getStringData(identifierString);
+		mStringData = StringTableManager::instance().getTable(stringTableId)->getStringData(identifierString);
 
 		if(mStringData->numParameters > 0)
 			mParameters = bs_newN<WString>(mStringData->numParameters);
@@ -25,9 +25,10 @@ namespace BansheeEngine
 	HString::HString(const WString& identifierString, const WString& defaultString, UINT32 stringTableId)
 		:mParameters(nullptr), mIsDirty(true), mStringPtr(nullptr)
 	{
-		StringTable::instance().setString(identifierString, StringTable::DEFAULT_LANGUAGE, defaultString);
+		HStringTable table = StringTableManager::instance().getTable(stringTableId);
+		table->setString(identifierString, StringTable::DEFAULT_LANGUAGE, defaultString);
 
-		mStringData = StringTable::instance().getStringData(identifierString);
+		mStringData = table->getStringData(identifierString);
 
 		if (mStringData->numParameters > 0)
 			mParameters = bs_newN<WString>(mStringData->numParameters);

+ 42 - 1
BansheeUtility/Source/BsStringTable.cpp → BansheeCore/Source/BsStringTable.cpp

@@ -1,5 +1,7 @@
 #include "BsStringTable.h"
 #include "BsException.h"
+#include "BsResources.h"
+#include "BsStringTableRTTI.h"
 
 namespace BansheeEngine
 {
@@ -160,7 +162,7 @@ namespace BansheeEngine
 	}
 
 	StringTable::StringTable()
-		:mActiveLanguageData(nullptr), mDefaultLanguageData(nullptr), mAllLanguages(nullptr)
+		:Resource(false), mActiveLanguageData(nullptr), mDefaultLanguageData(nullptr), mAllLanguages(nullptr)
 	{
 		mAllLanguages = bs_newN<LanguageData>((UINT32)Language::Count);
 
@@ -200,15 +202,29 @@ namespace BansheeEngine
 			stringData = iterFind->second;
 		}
 
+		mIdentifiers.insert(identifier);
 		stringData->updateString(string);
 	}
 
+	WString StringTable::getString(const WString& identifier, Language language)
+	{
+		LanguageData* curLanguage = &(mAllLanguages[(UINT32)language]);
+
+		auto iterFind = curLanguage->strings.find(identifier);
+		if (iterFind != curLanguage->strings.end())
+			return iterFind->second->string;
+			
+		return identifier;
+	}
+
 	void StringTable::removeString(const WString& identifier)
 	{
 		for(UINT32 i = 0; i < (UINT32)Language::Count; i++)
 		{
 			mAllLanguages[i].strings.erase(identifier);
 		}
+
+		mIdentifiers.erase(identifier);
 	}
 
 	SPtr<LocalizedStringData> StringTable::getStringData(const WString& identifier, bool insertIfNonExisting)
@@ -239,4 +255,29 @@ namespace BansheeEngine
 
 		BS_EXCEPT(InvalidParametersException, "There is no string data for the provided identifier.");
 	}
+
+	HStringTable StringTable::create()
+	{
+		return static_resource_cast<StringTable>(gResources()._createResourceHandle(_createPtr()));
+	}
+
+	SPtr<StringTable> StringTable::_createPtr()
+	{
+		SPtr<StringTable> scriptCodePtr = bs_core_ptr<StringTable, PoolAlloc>(
+			new (bs_alloc<StringTable, PoolAlloc>()) StringTable());
+		scriptCodePtr->_setThisPtr(scriptCodePtr);
+		scriptCodePtr->initialize();
+
+		return scriptCodePtr;
+	}
+
+	RTTITypeBase* StringTable::getRTTIStatic()
+	{
+		return StringTableRTTI::instance();
+	}
+
+	RTTITypeBase* StringTable::getRTTI() const
+	{
+		return StringTable::getRTTIStatic();
+	}
 }

+ 46 - 0
BansheeCore/Source/BsStringTableManager.cpp

@@ -0,0 +1,46 @@
+#include "BsStringTableManager.h"
+
+namespace BansheeEngine
+{
+	StringTableManager::StringTableManager()
+		:mActiveLanguage(StringTable::DEFAULT_LANGUAGE)
+	{
+
+	}
+
+	void StringTableManager::setActiveLanguage(Language language)
+	{
+		if (language != mActiveLanguage)
+		{
+			mActiveLanguage = language;
+
+			for (auto& tablePair : mTables)
+				tablePair.second->setActiveLanguage(language);
+		}
+	}
+
+	HStringTable StringTableManager::getTable(UINT32 id)
+	{
+		auto iterFind = mTables.find(id);
+		if (iterFind != mTables.end())
+			return iterFind->second;
+
+		HStringTable newTable = StringTable::create();
+		setTable(id, newTable);
+
+		return newTable;
+	}
+
+	void StringTableManager::removeTable(UINT32 id)
+	{
+		mTables.erase(id);
+	}
+
+	void StringTableManager::setTable(UINT32 id, HStringTable table)
+	{
+		mTables[id] = table;
+
+		if (table != nullptr)
+			table->setActiveLanguage(mActiveLanguage);
+	}
+}

+ 2 - 2
BansheeEngine/Include/BsScriptCode.h

@@ -27,7 +27,7 @@ namespace BansheeEngine
 		static HScriptCode create(const WString& data, bool editorScript = false);
 
 		/**
-		 * @brief	Creates an include file resource with the specified include string.
+		 * @brief	Creates a new scriptcode resource with the specified source string.
 		 *
 		 * @note	Internal method. Use "create" for normal use.
 		 */
@@ -44,6 +44,6 @@ namespace BansheeEngine
 	public:
 		friend class ScriptCodeRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const;
+		virtual RTTITypeBase* getRTTI() const override;
 	};
 }

+ 0 - 4
BansheeUtility/BansheeUtility.vcxproj

@@ -284,7 +284,6 @@
     <ClCompile Include="Source\BsRay.cpp" />
     <ClCompile Include="Source\BsRect2I.cpp" />
     <ClCompile Include="Source\BsSphere.cpp" />
-    <ClCompile Include="Source\BsStringTable.cpp" />
     <ClCompile Include="Source\BsTexAtlasGenerator.cpp" />
     <ClCompile Include="Source\ThirdParty\md5.cpp" />
     <ClCompile Include="Source\Win32\BsFileSystem.cpp" />
@@ -326,7 +325,6 @@
     <ClInclude Include="Include\BsFrameAlloc.h" />
     <ClInclude Include="Include\BsMemorySerializer.h" />
     <ClInclude Include="Include\BsRect2.h" />
-    <ClInclude Include="Include\BsHString.h" />
     <ClInclude Include="Include\BsTorus.h" />
     <ClInclude Include="Include\BsVector2I.h" />
     <ClInclude Include="Include\BsIReflectable.h" />
@@ -347,7 +345,6 @@
     <ClInclude Include="Include\BsRTTIType.h" />
     <ClInclude Include="Include\BsMemStack.h" />
     <ClInclude Include="Include\BsString.h" />
-    <ClInclude Include="Include\BsStringTable.h" />
     <ClInclude Include="Include\BsThreadDefines.h" />
     <ClInclude Include="Include\BsTime.h" />
     <ClInclude Include="Include\BsTimer.h" />
@@ -376,7 +373,6 @@
     <ClCompile Include="Source\BsRTTIField.cpp" />
     <ClCompile Include="Source\BsRTTIType.cpp" />
     <ClInclude Include="Include\BsTexAtlasGenerator.h" />
-    <ClCompile Include="Source\BsHString.cpp" />
     <ClInclude Include="Include\BsVectorNI.h" />
     <ClInclude Include="Include\BsGlobalFrameAlloc.h" />
     <ClInclude Include="Include\ThirdParty\md5.h" />

+ 0 - 15
BansheeUtility/BansheeUtility.vcxproj.filters

@@ -42,9 +42,6 @@
     <Filter Include="Source Files\Threading">
       <UniqueIdentifier>{cb0d2667-8d73-4d4c-9b2b-bc18fbd7fd70}</UniqueIdentifier>
     </Filter>
-    <Filter Include="Header Files\Localization">
-      <UniqueIdentifier>{bbd99250-46cb-4299-bbfa-34addc87b878}</UniqueIdentifier>
-    </Filter>
     <Filter Include="Header Files\ThirdParty">
       <UniqueIdentifier>{e0b74f43-dbb4-4e2a-8000-6d9f29dda203}</UniqueIdentifier>
     </Filter>
@@ -83,9 +80,6 @@
     <ClInclude Include="Include\BsTexAtlasGenerator.h">
       <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="Include\BsStringTable.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
     <ClInclude Include="Include\BsString.h">
       <Filter>Header Files</Filter>
     </ClInclude>
@@ -233,9 +227,6 @@
     <ClInclude Include="Include\BsVector4.h">
       <Filter>Header Files\Math</Filter>
     </ClInclude>
-    <ClInclude Include="Include\BsHString.h">
-      <Filter>Header Files\Localization</Filter>
-    </ClInclude>
     <ClInclude Include="Include\BsBitmapWriter.h">
       <Filter>Header Files\Debug</Filter>
     </ClInclude>
@@ -328,9 +319,6 @@
     <ClCompile Include="Source\BsTexAtlasGenerator.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="Source\BsStringTable.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
     <ClCompile Include="Source\BsString.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
@@ -349,9 +337,6 @@
     <ClCompile Include="Source\BsLog.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="Source\BsHString.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
     <ClCompile Include="Source\BsFrameAlloc.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>

+ 0 - 1
BansheeUtility/Include/BsEvent.h

@@ -1,7 +1,6 @@
 #pragma once
 
 #include "BsPrerequisitesUtil.h"
-#include "BsModule.h"
 
 namespace BansheeEngine
 {

+ 2 - 1
BansheeUtility/Include/BsFwdDeclUtil.h

@@ -101,6 +101,7 @@ namespace BansheeEngine
 		TID_SerializedArray = 62,
 		TID_SerializedEntry = 63,
 		TID_SerializedArrayEntry = 64,
-		TID_SerializedSubObject = 65
+		TID_SerializedSubObject = 65,
+		TID_UnorderedSet = 66,
 	};
 }

+ 2 - 1
BansheeUtility/Include/BsPrerequisitesUtil.h

@@ -64,4 +64,5 @@
 #include "BsMessageHandlerFwd.h"
 #include "BsUtil.h"
 #include "BsPath.h"
-#include "BsStringID.h"
+#include "BsStringID.h"
+#include "BsEvent.h"

+ 89 - 8
BansheeUtility/Include/BsRTTIPrerequisites.h

@@ -403,10 +403,10 @@ namespace BansheeEngine
 	}; 
 
 	/**
-	* @brief	RTTIPlainType for std::unordered_map.
-	*
-	* @see		RTTIPlainType
-	*/
+	 * @brief	RTTIPlainType for std::unordered_map.
+	 *
+	 * @see		RTTIPlainType
+	 */
 	template<class Key, class Value> 
 	struct RTTIPlainType<std::unordered_map<Key, Value, std::hash<Key>, std::equal_to<Key>, StdAlloc<std::pair<const Key, Value>>>>
 	{
@@ -447,8 +447,8 @@ namespace BansheeEngine
 		}
 
 		/**
-		* @copydoc		RTTIPlainType::fromMemory
-		*/
+		 * @copydoc		RTTIPlainType::fromMemory
+		 */
 		static UINT32 fromMemory(MapType& data, char* memory)
 		{
 			UINT32 size = 0;
@@ -476,8 +476,8 @@ namespace BansheeEngine
 		}
 
 		/**
-		* @copydoc		RTTIPlainType::getDynamicSize
-		*/
+		 * @copydoc		RTTIPlainType::getDynamicSize
+		 */
 		static UINT32 getDynamicSize(const MapType& data)
 		{
 			UINT64 dataSize = sizeof(UINT32)* 2;
@@ -494,6 +494,87 @@ namespace BansheeEngine
 		}
 	};
 
+	/**
+	 * @brief	RTTIPlainType for std::unordered_set.
+	 *
+	 * @see		RTTIPlainType
+	 */
+	template<class Key> 
+	struct RTTIPlainType<std::unordered_set<Key, std::hash<Key>, std::equal_to<Key>, StdAlloc<Key>>>
+	{
+		enum { id = TID_UnorderedSet }; enum { hasDynamicSize = 1 };
+
+		typedef std::unordered_set<Key, std::hash<Key>, std::equal_to<Key>, StdAlloc<Key>> MapType;
+
+		/**
+		* @copydoc		RTTIPlainType::toMemory
+		*/
+		static void toMemory(MapType& data, char* memory)
+		{
+			UINT32 size = sizeof(UINT32);
+			char* memoryStart = memory;
+			memory += sizeof(UINT32);
+
+			UINT32 numElements = (UINT32)data.size();
+			memcpy(memory, &numElements, sizeof(UINT32));
+			memory += sizeof(UINT32);
+			size += sizeof(UINT32);
+
+			for (auto iter = data.begin(); iter != data.end(); ++iter)
+			{
+				UINT32 keySize = RTTIPlainType<Key>::getDynamicSize(*iter);
+				RTTIPlainType<Key>::toMemory(*iter, memory);
+
+				memory += keySize;
+				size += keySize;
+			}
+
+			memcpy(memoryStart, &size, sizeof(UINT32));
+		}
+
+		/**
+		 * @copydoc		RTTIPlainType::fromMemory
+		 */
+		static UINT32 fromMemory(MapType& data, char* memory)
+		{
+			UINT32 size = 0;
+			memcpy(&size, memory, sizeof(UINT32));
+			memory += sizeof(UINT32);
+
+			UINT32 numElements;
+			memcpy(&numElements, memory, sizeof(UINT32));
+			memory += sizeof(UINT32);
+
+			for (UINT32 i = 0; i < numElements; i++)
+			{
+				Key key;
+				UINT32 keySize = RTTIPlainType<Key>::fromMemory(key, memory);
+				memory += keySize;
+
+				data.insert(key);
+			}
+
+			return size;
+		}
+
+		/**
+		 * @copydoc		RTTIPlainType::getDynamicSize
+		 */
+		static UINT32 getDynamicSize(const MapType& data)
+		{
+			UINT64 dataSize = sizeof(UINT32)* 2;
+
+			for (auto iter = data.begin(); iter != data.end(); ++iter)
+			{
+				dataSize += RTTIPlainType<Key>::getDynamicSize(*iter);
+			}
+
+			assert(dataSize <= std::numeric_limits<UINT32>::max());
+
+			return (UINT32)dataSize;
+		}
+	};
+
 	/**
 	 * @brief	RTTIPlainType for std::pair.
 	 * 			

+ 1 - 4
BansheeUtility/Include/BsString.h

@@ -961,7 +961,4 @@ struct std::hash<BansheeEngine::WString>
 			hash = 65599 * hash + string[i];
 		return hash ^ (hash >> 16);
 	}
-};
-
-
-#include "BsHString.h"
+};

+ 1 - 0
MBansheeEngine/MBansheeEngine.csproj

@@ -135,6 +135,7 @@
     <Compile Include="Sphere.cs" />
     <Compile Include="SpriteTexture.cs" />
     <Compile Include="StringTable.cs" />
+    <Compile Include="StringTables.cs" />
     <Compile Include="Texture.cs" />
     <Compile Include="Texture2D.cs" />
     <Compile Include="Math\Vector2.cs" />

+ 47 - 214
MBansheeEngine/StringTable.cs

@@ -1,239 +1,72 @@
 using System;
+using System.Collections.Generic;
+using System.Linq;
 using System.Runtime.CompilerServices;
+using System.Text;
 
 namespace BansheeEngine
 {
-    public sealed class StringTable
+    public sealed class StringTable : Resource
     {
-        public Language GetActiveLanguage()
+        // For internal use by the runtime
+        private StringTable()
+        { }
+
+        public int GetNumStrings()
         {
-            Language value;
-            Internal_GetActiveLanguage(out value);
-            return value;
+            return Internal_GetNumStrings(mCachedPtr);
         }
 
-		public void SetActiveLanguage(Language language)
-		{
-            Internal_SetActiveLanguage(language);
-		}
+        public string[] GetIdentifiers()
+        {
+            return Internal_GetIdentifiers(mCachedPtr);
+        }
 
-		public void SetString(string identifier, Language language, string value)
-		{
-		    Internal_SetString(identifier, language, value);
-		}
+        public void SetString(string identifier, Language language, string value)
+        {
+            Internal_SetString(mCachedPtr, identifier, language, value);
+        }
 
-		public void RemoveString(string identifier)
-		{
-		    Internal_RemoveString(identifier);
-		}
+        public void SetString(string identifier, string value)
+        {
+            Internal_SetStringDefault(mCachedPtr, identifier, value);
+        }
+
+        public void RemoveString(string identifier)
+        {
+            Internal_RemoveString(mCachedPtr, identifier);
+        }
 
-        public string GetLocalizedString(string identifier)
+        public string GetString(string identifier, Language language)
         {
             string value;
-            Internal_GetLocalizedString(identifier, out value);
+            Internal_GetString(mCachedPtr, identifier, language, out value);
+            return value;
+        }
+
+        public string GetString(string identifier)
+        {
+            string value;
+            Internal_GetStringDefault(mCachedPtr, identifier, out value);
             return value;
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_GetActiveLanguage(out Language value);
+        private static extern int Internal_GetNumStrings(IntPtr thisPtr);
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetActiveLanguage(Language value);
+        private static extern string[] Internal_GetIdentifiers(IntPtr thisPtr);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetString(string identifier, Language language, string value);
+        private static extern void Internal_SetString(IntPtr thisPtr, string identifier, Language language, string value);
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_RemoveString(string identifier);
+        private static extern void Internal_SetStringDefault(IntPtr thisPtr, string identifier, string value);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_GetLocalizedString(string identifier, out string value);
-    }
+        private static extern void Internal_RemoveString(IntPtr thisPtr, string identifier);
 
-    public enum Language
-	{
-		Afar, 
-		Abkhazian, 
-		Avestan, 
-		Afrikaans, 
-		Akan, 
-		Amharic, 
-		Aragonese, 
-		Arabic, 
-		Assamese, 
-		Avaric, 
-		Aymara, 
-		Azerbaijani, 
-		Bashkir, 
-		Belarusian, 
-		Bulgarian, 
-		Bihari, 
-		Bislama, 
-		Bambara, 
-		Bengali, 
-		Tibetan, 
-		Breton, 
-		Bosnian, 
-		Catalan, 
-		Chechen, 
-		Chamorro, 
-		Corsican, 
-		Cree, 
-		Czech, 
-		ChurchSlavic,
-		Chuvash, 
-		Welsh, 
-		Danish, 
-		German, 
-		Maldivian, 
-		Bhutani, 
-		Ewe, 		
-		Greek, 
-		EnglishUK, 
-		EnglishUS,
-		Esperanto, 
-		Spanish, 
-		Estonian, 
-		Basque, 
-		Persian, 
-		Fulah, 
-		Finnish, 
-		Fijian, 
-		Faroese, 
-		French, 
-		WesternFrisian, 
-		Irish, 
-		ScottishGaelic, 
-		Galician, 
-		Guarani, 
-		Gujarati, 
-		Manx, 
-		Hausa, 
-		Hebrew, 
-		Hindi, 
-		HiriMotu, 
-		Croatian, 
-		Haitian, 
-		Hungarian, 
-		Armenian, 
-		Herero, 
-		Interlingua, 
-		Indonesian, 
-		Interlingue, 
-		Igbo, 
-		SichuanYi, 
-		Inupiak, 
-		Ido, 
-		Icelandic, 
-		Italian, 
-		Inuktitut, 
-		Japanese, 
-		Javanese, 
-		Georgian, 
-		Kongo, 
-		Kikuyu, 
-		Kuanyama, 
-		Kazakh, 
-		Kalaallisut, 
-		Cambodian, 
-		Kannada, 
-		Korean, 
-		Kanuri, 
-		Kashmiri, 
-		Kurdish, 
-		Komi, 
-		Cornish, 
-		Kirghiz, 
-		Latin, 
-		Luxembourgish, 
-		Ganda, 
-		Limburgish,
-		Lingala, 
-		Laotian, 
-		Lithuanian, 
-		LubaKatanga, 
-		Latvian,
-		Malagasy, 
-		Marshallese, 
-		Maori, 
-		Macedonian, 
-		Malayalam, 
-		Mongolian, 
-		Moldavian, 
-		Marathi, 
-		Malay, 
-		Maltese, 
-		Burmese, 
-		Nauru, 
-		NorwegianBokmal, 
-		Ndebele, 
-		Nepali, 
-		Ndonga, 
-		Dutch, 
-		NorwegianNynorsk, 
-		Norwegian, 
-		Navaho, 
-		Nyanja, 
-		Provençal, 
-		Ojibwa, 
-		Oromo, 
-		Oriya, 
-		Ossetic, 
-		Punjabi, 
-		Pali, 
-		Polish, 
-		Pushto, 
-		Portuguese, 
-		Quechua, 
-		Romansh, 
-		Kirundi, 
-		Romanian, 
-		Russian, 
-		Kinyarwanda, 
-		Sanskrit, 
-		Sardinian, 
-		Sindhi, 
-		NorthernSami, 
-		Sangro, 
-		Sinhalese, 
-		Slovak, 
-		Slovenian, 
-		Samoan, 
-		Shona, 
-		Somali, 
-		Albanian, 
-		Serbian, 
-		Swati,
-		Sesotho,
-		Sundanese, 
-		Swedish, 
-		Swahili, 
-		Tamil, 
-		Telugu, 
-		Tajik, 
-		Thai, 
-		Tigrinya, 
-		Turkmen, 
-		Tagalog, 
-		Setswana, 
-		Tonga, 
-		Turkish, 
-		Tsonga, 
-		Tatar,
-		Twi, 
-		Tahitian, 
-		Uighur, 
-		Ukrainian, 
-		Urdu, 
-		Uzbek, 
-		Venda, 
-		Vietnamese, 
-		Volapuk, 
-		Walloon, 
-		Wolof, 
-		Xhosa, 
-		Yiddish, 
-		Yoruba, 
-		Zhuang,
-		Chinese,
-		Zulu,
-		Count // Number of entries
-	};
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_GetString(IntPtr thisPtr, string identifier, Language language, out string value);
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_GetStringDefault(IntPtr thisPtr, string identifier, out string value);
+    }
 }

+ 237 - 0
MBansheeEngine/StringTables.cs

@@ -0,0 +1,237 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace BansheeEngine
+{
+    public sealed class StringTables
+    {
+        public Language GetActiveLanguage()
+        {
+            Language value;
+            Internal_GetActiveLanguage(out value);
+            return value;
+        }
+
+		public void SetActiveLanguage(Language language)
+		{
+            Internal_SetActiveLanguage(language);
+		}
+
+        public StringTable GetTable(int id)
+        {
+            return Internal_GetTable(id);
+        }
+
+        public void RegisterTable(int id, StringTable table)
+        {
+            Internal_SetTable(id, table);
+        }
+
+        public void UnregisterTable(int id)
+        {
+            Internal_RemoveTable(id);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_GetActiveLanguage(out Language value);
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetActiveLanguage(Language value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern StringTable Internal_GetTable(int id);
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetTable(int id, StringTable table);
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_RemoveTable(int id);
+    }
+
+    public enum Language
+	{
+		Afar, 
+		Abkhazian, 
+		Avestan, 
+		Afrikaans, 
+		Akan, 
+		Amharic, 
+		Aragonese, 
+		Arabic, 
+		Assamese, 
+		Avaric, 
+		Aymara, 
+		Azerbaijani, 
+		Bashkir, 
+		Belarusian, 
+		Bulgarian, 
+		Bihari, 
+		Bislama, 
+		Bambara, 
+		Bengali, 
+		Tibetan, 
+		Breton, 
+		Bosnian, 
+		Catalan, 
+		Chechen, 
+		Chamorro, 
+		Corsican, 
+		Cree, 
+		Czech, 
+		ChurchSlavic,
+		Chuvash, 
+		Welsh, 
+		Danish, 
+		German, 
+		Maldivian, 
+		Bhutani, 
+		Ewe, 		
+		Greek, 
+		EnglishUK, 
+		EnglishUS,
+		Esperanto, 
+		Spanish, 
+		Estonian, 
+		Basque, 
+		Persian, 
+		Fulah, 
+		Finnish, 
+		Fijian, 
+		Faroese, 
+		French, 
+		WesternFrisian, 
+		Irish, 
+		ScottishGaelic, 
+		Galician, 
+		Guarani, 
+		Gujarati, 
+		Manx, 
+		Hausa, 
+		Hebrew, 
+		Hindi, 
+		HiriMotu, 
+		Croatian, 
+		Haitian, 
+		Hungarian, 
+		Armenian, 
+		Herero, 
+		Interlingua, 
+		Indonesian, 
+		Interlingue, 
+		Igbo, 
+		SichuanYi, 
+		Inupiak, 
+		Ido, 
+		Icelandic, 
+		Italian, 
+		Inuktitut, 
+		Japanese, 
+		Javanese, 
+		Georgian, 
+		Kongo, 
+		Kikuyu, 
+		Kuanyama, 
+		Kazakh, 
+		Kalaallisut, 
+		Cambodian, 
+		Kannada, 
+		Korean, 
+		Kanuri, 
+		Kashmiri, 
+		Kurdish, 
+		Komi, 
+		Cornish, 
+		Kirghiz, 
+		Latin, 
+		Luxembourgish, 
+		Ganda, 
+		Limburgish,
+		Lingala, 
+		Laotian, 
+		Lithuanian, 
+		LubaKatanga, 
+		Latvian,
+		Malagasy, 
+		Marshallese, 
+		Maori, 
+		Macedonian, 
+		Malayalam, 
+		Mongolian, 
+		Moldavian, 
+		Marathi, 
+		Malay, 
+		Maltese, 
+		Burmese, 
+		Nauru, 
+		NorwegianBokmal, 
+		Ndebele, 
+		Nepali, 
+		Ndonga, 
+		Dutch, 
+		NorwegianNynorsk, 
+		Norwegian, 
+		Navaho, 
+		Nyanja, 
+		Provençal, 
+		Ojibwa, 
+		Oromo, 
+		Oriya, 
+		Ossetic, 
+		Punjabi, 
+		Pali, 
+		Polish, 
+		Pushto, 
+		Portuguese, 
+		Quechua, 
+		Romansh, 
+		Kirundi, 
+		Romanian, 
+		Russian, 
+		Kinyarwanda, 
+		Sanskrit, 
+		Sardinian, 
+		Sindhi, 
+		NorthernSami, 
+		Sangro, 
+		Sinhalese, 
+		Slovak, 
+		Slovenian, 
+		Samoan, 
+		Shona, 
+		Somali, 
+		Albanian, 
+		Serbian, 
+		Swati,
+		Sesotho,
+		Sundanese, 
+		Swedish, 
+		Swahili, 
+		Tamil, 
+		Telugu, 
+		Tajik, 
+		Thai, 
+		Tigrinya, 
+		Turkmen, 
+		Tagalog, 
+		Setswana, 
+		Tonga, 
+		Turkish, 
+		Tsonga, 
+		Tatar,
+		Twi, 
+		Tahitian, 
+		Uighur, 
+		Ukrainian, 
+		Urdu, 
+		Uzbek, 
+		Venda, 
+		Vietnamese, 
+		Volapuk, 
+		Walloon, 
+		Wolof, 
+		Xhosa, 
+		Yiddish, 
+		Yoruba, 
+		Zhuang,
+		Chinese,
+		Zulu,
+		Count // Number of entries
+	};
+}

+ 17 - 0
SBansheeEditor/Source/BsGUIResourceField.cpp

@@ -311,6 +311,23 @@ namespace BansheeEngine
 				}
 			}
 				break;
+			case TID_Prefab:
+			{
+				if (ScriptAssemblyManager::instance().getPrefabClass()->isSubClassOf(acceptedClass))
+				{
+					setUUID(uuid);
+					found = true;
+				}
+			}
+			case TID_StringTable:
+			{
+				if (ScriptAssemblyManager::instance().getStringTableClass()->isSubClassOf(acceptedClass))
+				{
+					setUUID(uuid);
+					found = true;
+				}
+			}
+				break;
 			case TID_ManagedResource:
 			{
 				ManagedResourceMetaDataPtr managedResMetaData = std::static_pointer_cast<ManagedResourceMetaData>(meta->getResourceMetaData());

+ 1 - 0
SBansheeEngine/Include/BsManagedSerializableObjectInfo.h

@@ -32,6 +32,7 @@ namespace BansheeEngine
 		MaterialRef,
 		MeshRef,
 		PrefabRef,
+		StringTableRef,
 		SceneObjectRef,
 		ComponentRef
 	};

+ 3 - 1
SBansheeEngine/Include/BsScriptAssemblyManager.h

@@ -35,7 +35,8 @@ namespace BansheeEngine
 		MonoClass* getMaterialClass() const { return mMaterialClass; }
 		MonoClass* getMeshClass() const { return mMeshClass; }
 		MonoClass* getFontClass() const { return mFontClass; }
-		MonoClass* getPrefabClass() const { return mPrefabClass;  }
+		MonoClass* getPrefabClass() const { return mPrefabClass; }
+		MonoClass* getStringTableClass() const { return mStringTableClass; }
 		MonoClass* getPlainTextClass() const { return mPlainTextClass; }
 		MonoClass* getScriptCodeClass() const { return mScriptCodeClass; }
 
@@ -62,6 +63,7 @@ namespace BansheeEngine
 		MonoClass* mManagedResourceClass;
 		MonoClass* mFontClass;
 		MonoClass* mPrefabClass;
+		MonoClass* mStringTableClass;
 		MonoClass* mPlainTextClass;
 		MonoClass* mScriptCodeClass;
 

+ 1 - 0
SBansheeEngine/Include/BsScriptEnginePrerequisites.h

@@ -34,6 +34,7 @@ namespace BansheeEngine
 	class ScriptMaterial;
 	class ScriptMesh;
 	class ScriptPrefab;
+	class ScriptStringTable;
 	class ScriptGUIElementStyle;
 	class ScriptGUIElementStateStyle;
 	class ScriptGUILayout;

+ 17 - 0
SBansheeEngine/Include/BsScriptResourceManager.h

@@ -143,6 +143,18 @@ namespace BansheeEngine
 		 */
 		ScriptPrefab* createScriptPrefab(MonoObject* existingInstance, const HPrefab& resourceHandle);
 
+		/**
+		 * @note Throws an exception if resource for the handle already exists.
+		 * 		 Creates a new managed instance of the object.
+		 */
+		ScriptStringTable* createScriptStringTable(const HStringTable& resourceHandle);
+
+		/**
+		 * @note Throws an exception if resource for the handle already exists.
+		 * 		 Initializes the ScriptResource with an existing managed instance.
+		 */
+		ScriptStringTable* createScriptStringTable(MonoObject* existingInstance, const HStringTable& resourceHandle);
+
 		/**
 		* @note  Throws an exception if resource for the handle already exists.
 		* 		 Initializes the ScriptResource with an existing managed instance.
@@ -204,6 +216,11 @@ namespace BansheeEngine
 		 */
 		ScriptPrefab* getScriptPrefab(const HPrefab& resourceHandle);
 
+		/**
+		 * @note Returns nullptr if script resource doesn't exist.
+		 */
+		ScriptStringTable* getScriptStringTable(const HStringTable& resourceHandle);
+
 		/**
 		 * @note Returns nullptr if script resource doesn't exist.
 		 */

+ 21 - 8
SBansheeEngine/Include/BsScriptStringTable.h

@@ -1,24 +1,37 @@
 #pragma once
 
 #include "BsScriptEnginePrerequisites.h"
-#include "BsScriptObject.h"
+#include "BsScriptResource.h"
 #include "BsStringTable.h"
 
 namespace BansheeEngine
 {
-	class BS_SCR_BE_EXPORT ScriptStringTable : public ScriptObject<ScriptStringTable>
+	class BS_SCR_BE_EXPORT ScriptStringTable : public ScriptObject <ScriptStringTable, ScriptResourceBase>
 	{
 	public:
 		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "StringTable")
 
+		ScriptStringTable(MonoObject* instance, const HStringTable& table);
+
+		HResource getNativeHandle() const override { return mTable; }
+		void setNativeHandle(const HResource& resource) override;
+
+		HStringTable getStringTableHandle() const { return mTable; }
+
 	private:
-		static void internal_GetActiveLanguage(Language* value);
-		static void internal_SetActiveLanguage(Language value);
+		static UINT32 internal_GetNumStrings(ScriptStringTable* thisPtr);
+		static MonoArray* internal_GetIdentifiers(ScriptStringTable* thisPtr);
+
+		static void internal_SetString(ScriptStringTable* thisPtr, MonoString* identifier, Language language, MonoString* value);
+		static void internal_SetStringDefault(ScriptStringTable* thisPtr, MonoString* identifier, MonoString* value);
+
+		static void internal_RemoveString(ScriptStringTable* thisPtr, MonoString* identifier);
+
+		static void internal_GetString(ScriptStringTable* thisPtr, MonoString* identifier, Language language, MonoString** value);
+		static void internal_GetStringDefault(ScriptStringTable* thisPtr, MonoString* identifier, MonoString** value);
 
-		static void internal_SetString(MonoString* identifier, Language language, MonoString* value);
-		static void internal_RemoveString(MonoString* identifier);
-		static void internal_GetLocalizedString(MonoString* identifier, MonoString** value);
+		void _onManagedInstanceDeleted() override;
 
-		ScriptStringTable(MonoObject* instance);
+		HStringTable mTable;
 	};
 }

+ 25 - 0
SBansheeEngine/Include/BsScriptStringTableManager.h

@@ -0,0 +1,25 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsScriptObject.h"
+#include "BsStringTable.h"
+#include "BsStringTableManager.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ScriptStringTableManager : public ScriptObject <ScriptStringTableManager>
+	{
+	public:
+		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "StringTables")
+
+	private:
+		static void internal_GetActiveLanguage(Language* value);
+		static void internal_SetActiveLanguage(Language value);
+
+		static MonoObject* internal_GetTable(UINT32 id);
+		static void internal_SetTable(UINT32 id, MonoObject* table);
+		static void internal_RemoveTable(UINT32 id);
+
+		ScriptStringTableManager(MonoObject* instance);
+	};
+}

+ 2 - 0
SBansheeEngine/SBansheeEngine.vcxproj

@@ -319,6 +319,7 @@
     <ClInclude Include="Include\BsScriptShader.h" />
     <ClInclude Include="Include\BsScriptSpriteTexture.h" />
     <ClInclude Include="Include\BsScriptStringTable.h" />
+    <ClInclude Include="Include\BsScriptStringTableManager.h" />
     <ClInclude Include="Include\BsScriptTexture.h" />
     <ClInclude Include="Include\BsScriptTexture2D.h" />
     <ClInclude Include="Include\BsScriptGUIContent.h" />
@@ -410,6 +411,7 @@
     <ClCompile Include="Source\BsScriptShader.cpp" />
     <ClCompile Include="Source\BsScriptSpriteTexture.cpp" />
     <ClCompile Include="Source\BsScriptStringTable.cpp" />
+    <ClCompile Include="Source\BsScriptStringTableManager.cpp" />
     <ClCompile Include="Source\BsScriptTexture.cpp" />
     <ClCompile Include="Source\BsScriptTexture2D.cpp" />
     <ClCompile Include="Source\BsScriptGUIContent.cpp" />

+ 6 - 0
SBansheeEngine/SBansheeEngine.vcxproj.filters

@@ -327,6 +327,9 @@
     <ClInclude Include="Include\BsScriptProfilerOverlayInternal.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsScriptStringTableManager.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsScriptTexture2D.cpp">
@@ -593,5 +596,8 @@
     <ClCompile Include="Source\BsScriptProfilerOverlayInternal.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsScriptStringTableManager.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 28 - 0
SBansheeEngine/Source/BsManagedSerializableField.cpp

@@ -16,6 +16,7 @@
 #include "BsScriptMaterial.h"
 #include "BsScriptMesh.h"
 #include "BsScriptPrefab.h"
+#include "BsScriptStringTable.h"
 #include "BsScriptSceneObject.h"
 #include "BsScriptComponent.h"
 #include "BsManagedSerializableObject.h"
@@ -257,6 +258,18 @@ namespace BansheeEngine
 
 				return fieldData;
 			}
+			case ScriptPrimitiveType::StringTableRef:
+			{
+				auto fieldData = bs_shared_ptr<ManagedSerializableFieldDataResourceRef>();
+
+				if (value != nullptr)
+				{
+					ScriptStringTable* scriptStringTable = ScriptStringTable::toNative(value);
+					fieldData->value = static_resource_cast<StringTable>(scriptStringTable->getNativeHandle());
+				}
+
+				return fieldData;
+			}
 			case ScriptPrimitiveType::ManagedResourceRef:
 				{
 					auto fieldData = bs_shared_ptr<ManagedSerializableFieldDataResourceRef>();
@@ -674,6 +687,21 @@ namespace BansheeEngine
 				else
 					return nullptr;
 			}
+			else if (primitiveTypeInfo->mType == ScriptPrimitiveType::StringTableRef)
+			{
+				if (value)
+				{
+					HStringTable stringTable = static_resource_cast<StringTable>(value);
+					ScriptStringTable* scriptResource = ScriptResourceManager::instance().getScriptStringTable(stringTable);
+					if (scriptResource == nullptr)
+						scriptResource = ScriptResourceManager::instance().createScriptStringTable(stringTable);
+
+					if (scriptResource != nullptr)
+						return scriptResource->getManagedInstance();
+				}
+				else
+					return nullptr;
+			}
 			else if (primitiveTypeInfo->mType == ScriptPrimitiveType::ManagedResourceRef)
 			{
 				if (value)

+ 2 - 0
SBansheeEngine/Source/BsManagedSerializableObjectInfo.cpp

@@ -169,6 +169,8 @@ namespace BansheeEngine
 			return ScriptAssemblyManager::instance().getScriptCodeClass()->_getInternalClass();
 		case ScriptPrimitiveType::PrefabRef:
 			return ScriptAssemblyManager::instance().getPrefabClass()->_getInternalClass();
+		case ScriptPrimitiveType::StringTableRef:
+			return ScriptAssemblyManager::instance().getStringTableClass()->_getInternalClass();
 		case ScriptPrimitiveType::SceneObjectRef:
 			return ScriptAssemblyManager::instance().getSceneObjectClass()->_getInternalClass();
 		case ScriptPrimitiveType::ComponentRef:

+ 12 - 1
SBansheeEngine/Source/BsScriptAssemblyManager.cpp

@@ -19,7 +19,7 @@ namespace BansheeEngine
 		mSerializeFieldAttribute(nullptr), mHideInInspectorAttribute(nullptr), mSystemArrayClass(nullptr), mSystemGenericListClass(nullptr),
 		mSystemGenericDictionaryClass(nullptr), mManagedResourceClass(nullptr), mFontClass(nullptr), mMissingComponentClass(nullptr),
 		mPlainTextClass(nullptr), mScriptCodeClass(nullptr), mShaderClass(nullptr), mMaterialClass(nullptr), mTexture3DClass(nullptr),
-		mTextureCubeClass(nullptr), mMeshClass(nullptr), mPrefabClass(nullptr)
+		mTextureCubeClass(nullptr), mMeshClass(nullptr), mPrefabClass(nullptr), mStringTableClass(nullptr)
 	{
 
 	}
@@ -325,6 +325,12 @@ namespace BansheeEngine
 				typeInfo->mType = ScriptPrimitiveType::PrefabRef;
 				return typeInfo;
 			}
+			else if (monoClass->isSubClassOf(mStringTableClass))
+			{
+				std::shared_ptr<ManagedSerializableTypeInfoPrimitive> typeInfo = bs_shared_ptr<ManagedSerializableTypeInfoPrimitive>();
+				typeInfo->mType = ScriptPrimitiveType::StringTableRef;
+				return typeInfo;
+			}
 			else if(monoClass->isSubClassOf(mSceneObjectClass))
 			{
 				std::shared_ptr<ManagedSerializableTypeInfoPrimitive> typeInfo = bs_shared_ptr<ManagedSerializableTypeInfoPrimitive>();
@@ -436,6 +442,7 @@ namespace BansheeEngine
 		mMaterialClass = nullptr;
 		mMeshClass = nullptr;
 		mPrefabClass = nullptr;
+		mStringTableClass = nullptr;
 		mFontClass = nullptr;
 		mPlainTextClass = nullptr;
 		mScriptCodeClass = nullptr;
@@ -527,6 +534,10 @@ namespace BansheeEngine
 		if (mPrefabClass == nullptr)
 			BS_EXCEPT(InvalidStateException, "Cannot find Prefab managed class.");
 
+		mStringTableClass = bansheeEngineAssembly->getClass("BansheeEngine", "StringTable");
+		if (mStringTableClass == nullptr)
+			BS_EXCEPT(InvalidStateException, "Cannot find StringTable managed class.");
+
 		mPlainTextClass = bansheeEngineAssembly->getClass("BansheeEngine", "PlainText");
 		if (mPlainTextClass == nullptr)
 			BS_EXCEPT(InvalidStateException, "Cannot find PlainText managed class.");

+ 27 - 0
SBansheeEngine/Source/BsScriptResourceManager.cpp

@@ -13,6 +13,7 @@
 #include "BsScriptMesh.h"
 #include "BsScriptFont.h"
 #include "BsScriptPrefab.h"
+#include "BsScriptStringTable.h"
 #include "BsScriptManagedResource.h"
 #include "BsScriptAssemblyManager.h"
 
@@ -253,6 +254,27 @@ namespace BansheeEngine
 		return scriptResource;
 	}
 
+	ScriptStringTable* ScriptResourceManager::createScriptStringTable(const HStringTable& resourceHandle)
+	{
+		MonoClass* stringTableClass = ScriptAssemblyManager::instance().getStringTableClass();
+		MonoObject* monoInstance = stringTableClass->createInstance();
+
+		return createScriptStringTable(monoInstance, resourceHandle);
+	}
+
+	ScriptStringTable* ScriptResourceManager::createScriptStringTable(MonoObject* instance, const HStringTable& resourceHandle)
+	{
+		const String& uuid = resourceHandle.getUUID();
+#if BS_DEBUG_MODE
+		throwExceptionIfInvalidOrDuplicate(uuid);
+#endif
+
+		ScriptStringTable* scriptResource = new (bs_alloc<ScriptStringTable>()) ScriptStringTable(instance, resourceHandle);
+		mScriptResources[uuid] = scriptResource;
+
+		return scriptResource;
+	}
+
 	ScriptManagedResource* ScriptResourceManager::createManagedResource(MonoObject* existingInstance, const HManagedResource& resourceHandle)
 	{
 		const String& uuid = resourceHandle.getUUID();
@@ -319,6 +341,11 @@ namespace BansheeEngine
 		return static_cast<ScriptPrefab*>(getScriptResource(resourceHandle.getUUID()));
 	}
 
+	ScriptStringTable* ScriptResourceManager::getScriptStringTable(const HStringTable& resourceHandle)
+	{
+		return static_cast<ScriptStringTable*>(getScriptResource(resourceHandle.getUUID()));
+	}
+
 	ScriptResourceBase* ScriptResourceManager::getScriptResource(const String& uuid)
 	{
 		if(uuid == "")

+ 66 - 17
SBansheeEngine/Source/BsScriptStringTable.cpp

@@ -4,48 +4,97 @@
 #include "BsMonoClass.h"
 #include "BsMonoManager.h"
 #include "BsMonoUtil.h"
+#include "BsScriptResourceManager.h"
+#include "BsStringTableManager.h"
 
 namespace BansheeEngine
 {
-	ScriptStringTable::ScriptStringTable(MonoObject* instance)
-		:ScriptObject(instance)
+	ScriptStringTable::ScriptStringTable(MonoObject* instance, const HStringTable& table)
+		:ScriptObject(instance), mTable(table)
 	{ }
 
 	void ScriptStringTable::initRuntimeData()
 	{
-		metaData.scriptClass->addInternalCall("Internal_GetActiveLanguage", &ScriptStringTable::internal_GetActiveLanguage);
-		metaData.scriptClass->addInternalCall("Internal_SetActiveLanguage", &ScriptStringTable::internal_SetActiveLanguage);
+		metaData.scriptClass->addInternalCall("Internal_GetNumStrings", &ScriptStringTable::internal_GetNumStrings);
+		metaData.scriptClass->addInternalCall("Internal_GetIdentifiers", &ScriptStringTable::internal_GetIdentifiers);
 
 		metaData.scriptClass->addInternalCall("Internal_SetString", &ScriptStringTable::internal_SetString);
+		metaData.scriptClass->addInternalCall("Internal_SetStringDefault", &ScriptStringTable::internal_SetStringDefault);
 		metaData.scriptClass->addInternalCall("Internal_RemoveString", &ScriptStringTable::internal_RemoveString);
-		metaData.scriptClass->addInternalCall("Internal_GetLocalizedString", &ScriptStringTable::internal_GetLocalizedString);
+		metaData.scriptClass->addInternalCall("Internal_GetString", &ScriptStringTable::internal_GetString);
+		metaData.scriptClass->addInternalCall("Internal_GetStringDefault", &ScriptStringTable::internal_GetStringDefault);
 	}
 
-	void ScriptStringTable::internal_GetActiveLanguage(Language* value)
+	UINT32 ScriptStringTable::internal_GetNumStrings(ScriptStringTable* thisPtr)
 	{
-		*value = StringTable::instance().getActiveLanguage();
+		return (UINT32)thisPtr->getStringTableHandle()->getIdentifiers().size();
 	}
 
-	void ScriptStringTable::internal_SetActiveLanguage(Language value)
+	MonoArray* ScriptStringTable::internal_GetIdentifiers(ScriptStringTable* thisPtr)
 	{
-		StringTable::instance().setActiveLanguage(value);
+		const UnorderedSet<WString>& identifiers = thisPtr->getStringTableHandle()->getIdentifiers();
+		UINT32 numIdentifiers = (UINT32)identifiers.size();
+
+		ScriptArray outArray = ScriptArray::create<WString>(numIdentifiers);
+		UINT32 idx = 0;
+		for (auto identifier : identifiers)
+		{
+			outArray.set(idx, identifier);
+
+			idx++;
+		}
+
+		return outArray.getInternal();
+	}
+
+	void ScriptStringTable::internal_SetString(ScriptStringTable* thisPtr, MonoString* identifier, Language language, MonoString* value)
+	{
+		WString nativeIdentifier = MonoUtil::monoToWString(identifier);
+		WString nativeValue = MonoUtil::monoToWString(value);
+
+		thisPtr->getStringTableHandle()->setString(nativeIdentifier, language, nativeValue);
 	}
 
-	void ScriptStringTable::internal_SetString(MonoString* identifier, Language language, MonoString* value)
+	void ScriptStringTable::internal_SetStringDefault(ScriptStringTable* thisPtr, MonoString* identifier, MonoString* value)
 	{
-		StringTable::instance().setString(MonoUtil::monoToWString(identifier), language, MonoUtil::monoToWString(value));
+		WString nativeIdentifier = MonoUtil::monoToWString(identifier);
+		WString nativeValue = MonoUtil::monoToWString(value);
+
+		thisPtr->getStringTableHandle()->setString(nativeIdentifier, StringTableManager::instance().getActiveLanguage(), nativeValue);
+	}
+
+	void ScriptStringTable::internal_RemoveString(ScriptStringTable* thisPtr, MonoString* identifier)
+	{
+		WString nativeIdentifier = MonoUtil::monoToWString(identifier);
+		thisPtr->getStringTableHandle()->removeString(nativeIdentifier);
+	}
+
+	void ScriptStringTable::internal_GetString(ScriptStringTable* thisPtr, MonoString* identifier, Language language, MonoString** value)
+	{
+		WString nativeIdentifier = MonoUtil::monoToWString(identifier);
+		WString nativeValue = thisPtr->getStringTableHandle()->getString(nativeIdentifier, language);
+
+		*value = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), nativeValue);
+	}
+
+	void ScriptStringTable::internal_GetStringDefault(ScriptStringTable* thisPtr, MonoString* identifier, MonoString** value)
+	{
+		WString nativeIdentifier = MonoUtil::monoToWString(identifier);
+		WString nativeValue = thisPtr->getStringTableHandle()->getString(nativeIdentifier, StringTableManager::instance().getActiveLanguage());
+
+		*value = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), nativeValue);
 	}
 
-	void ScriptStringTable::internal_RemoveString(MonoString* identifier)
+	void ScriptStringTable::setNativeHandle(const HResource& resource)
 	{
-		StringTable::instance().removeString(MonoUtil::monoToWString(identifier));
+		mTable = static_resource_cast<StringTable>(resource);
 	}
 
-	void ScriptStringTable::internal_GetLocalizedString(MonoString* identifier, MonoString** value)
+	void ScriptStringTable::_onManagedInstanceDeleted()
 	{
-		HString stringHandle(MonoUtil::monoToWString(identifier));
-		WString outString = stringHandle;
+		mManagedInstance = nullptr;
 
-		*value = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), outString);
+		if (!mRefreshInProgress)
+			ScriptResourceManager::instance().destroyScriptResource(this);
 	}
 }

+ 60 - 0
SBansheeEngine/Source/BsScriptStringTableManager.cpp

@@ -0,0 +1,60 @@
+#include "BsScriptStringTableManager.h"
+#include "BsScriptMeta.h"
+#include "BsMonoField.h"
+#include "BsMonoClass.h"
+#include "BsMonoManager.h"
+#include "BsMonoUtil.h"
+#include "BsScriptResourceManager.h"
+#include "BsScriptStringTable.h"
+
+namespace BansheeEngine
+{
+	ScriptStringTableManager::ScriptStringTableManager(MonoObject* instance)
+		:ScriptObject(instance)
+	{ }
+
+	void ScriptStringTableManager::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_GetActiveLanguage", &ScriptStringTableManager::internal_GetActiveLanguage);
+		metaData.scriptClass->addInternalCall("Internal_SetActiveLanguage", &ScriptStringTableManager::internal_SetActiveLanguage);
+
+		metaData.scriptClass->addInternalCall("Internal_GetTable", &ScriptStringTableManager::internal_GetTable);
+		metaData.scriptClass->addInternalCall("Internal_SetTable", &ScriptStringTableManager::internal_SetTable);
+		metaData.scriptClass->addInternalCall("Internal_RemoveTable", &ScriptStringTableManager::internal_RemoveTable);
+	}
+
+	void ScriptStringTableManager::internal_GetActiveLanguage(Language* value)
+	{
+		*value = StringTableManager::instance().getActiveLanguage();
+	}
+
+	void ScriptStringTableManager::internal_SetActiveLanguage(Language value)
+	{
+		StringTableManager::instance().setActiveLanguage(value);
+	}
+
+	MonoObject* ScriptStringTableManager::internal_GetTable(UINT32 id)
+	{
+		HStringTable table = StringTableManager::instance().getTable(id);
+
+		ScriptStringTable* scriptStringTable = ScriptResourceManager::instance().getScriptStringTable(table);
+		if (scriptStringTable == nullptr)
+			scriptStringTable = ScriptResourceManager::instance().createScriptStringTable(table);
+
+		return scriptStringTable->getManagedInstance();
+	}
+
+	void ScriptStringTableManager::internal_SetTable(UINT32 id, MonoObject* table)
+	{
+		HStringTable nativeTable;
+		if (table != nullptr)
+			nativeTable = ScriptStringTable::toNative(table)->getStringTableHandle();
+
+		StringTableManager::instance().setTable(id, nativeTable);
+	}
+
+	void ScriptStringTableManager::internal_RemoveTable(UINT32 id)
+	{
+		StringTableManager::instance().removeTable(id);
+	}
+}

+ 4 - 18
TODO.txt

@@ -57,24 +57,10 @@ Code quality improvements:
 ----------------------------------------------------------------------
 Polish stage 1
 
-Add StringTableManager
- - Takes over the functionality of current StringTable module
- - getTable(enum)
- - register(enum, stResource) <- load/Save this in BsApplication or BsCoreApplication
- - unregister(enum, stResource)
-
-Convert current StringTable to a Resource:
- - Add RTTI
- - Also I need methods to enumerate and modify all strings from all languages (so an editor can be built)
-  - getNumStrings() <- might require me to keep a global list of identifiers
-  - getIdentifier(idx)
-  - getValue(language, identifier)
-  - setValue(language, identifier)
-
-Reflect new changes in C#
- - StringTableManager -> StringTables
- - C# StringTable resource
-
+StringTable TODO:
+  - Issue in ScriptStringTable - I should allow it to be created from C# and in that case I need to call Internal_CreateInstance,
+    but it seems I'm using the default constructor for something else so I can't call it there (or can I?)
+  - Need to actually save & load the string tables from somewhere (probably don't need this for now)
 
 Test inspector selection, selecting a resource and adding/removing component updates
 Crash when showing the inspector (invalid index in Layout.InsertElement called from InspectableObject.Update)