BsStringTable.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #include "BsStringTable.h"
  2. #include "BsException.h"
  3. #include "BsResources.h"
  4. #include "BsStringTableRTTI.h"
  5. namespace BansheeEngine
  6. {
  7. const Language StringTable::DEFAULT_LANGUAGE = Language::EnglishUS;
  8. LocalizedStringData::LocalizedStringData()
  9. :parameterOffsets(nullptr), numParameters(0)
  10. {
  11. }
  12. LocalizedStringData::~LocalizedStringData()
  13. {
  14. if(parameterOffsets != nullptr)
  15. bs_deleteN(parameterOffsets, numParameters);
  16. }
  17. void LocalizedStringData::concatenateString(WString& outputString, WString* parameters, UINT32 numParameterValues) const
  18. {
  19. // A safeguard in case translated strings have different number of parameters
  20. UINT32 actualNumParameters = std::min(numParameterValues, numParameters);
  21. if(parameters != nullptr)
  22. {
  23. UINT32 totalNumChars = 0;
  24. UINT32 prevIdx = 0;
  25. for(UINT32 i = 0; i < actualNumParameters; i++)
  26. {
  27. totalNumChars += (parameterOffsets[i].location - prevIdx) + (UINT32)parameters[parameterOffsets[i].paramIdx].size();;
  28. prevIdx = parameterOffsets[i].location;
  29. }
  30. totalNumChars += (UINT32)string.size() - prevIdx;
  31. outputString.resize(totalNumChars);
  32. wchar_t* strData = &outputString[0]; // String contiguity required by C++11, but this should work elsewhere as well
  33. prevIdx = 0;
  34. for(UINT32 i = 0; i < actualNumParameters; i++)
  35. {
  36. UINT32 strSize = parameterOffsets[i].location - prevIdx;
  37. memcpy(strData, &string[prevIdx], strSize * sizeof(wchar_t));
  38. strData += strSize;
  39. WString& param = parameters[parameterOffsets[i].paramIdx];
  40. memcpy(strData, &param[0], param.size() * sizeof(wchar_t));
  41. strData += param.size();
  42. prevIdx = parameterOffsets[i].location;
  43. }
  44. memcpy(strData, &string[prevIdx], (string.size() - prevIdx) * sizeof(wchar_t));
  45. }
  46. else
  47. {
  48. outputString.resize(string.size());
  49. wchar_t* strData = &outputString[0]; // String contiguity required by C++11, but this should work elsewhere as well
  50. memcpy(strData, &string[0], string.size() * sizeof(wchar_t));
  51. }
  52. }
  53. void LocalizedStringData::updateString(const WString& _string)
  54. {
  55. if(parameterOffsets != nullptr)
  56. bs_deleteN(parameterOffsets, numParameters);
  57. Vector<ParamOffset> paramOffsets;
  58. INT32 lastBracket = -1;
  59. WStringStream bracketChars;
  60. WStringStream cleanString;
  61. bool escaped = false;
  62. UINT32 numRemovedChars = 0;
  63. for(UINT32 i = 0; i < (UINT32)_string.size(); i++)
  64. {
  65. if(_string[i] == '\\' && !escaped)
  66. {
  67. numRemovedChars++;
  68. escaped = true;
  69. continue;
  70. }
  71. if(lastBracket == -1)
  72. {
  73. // If current char is non-escaped opening bracket start parameter definition
  74. if(_string[i] == '{' && !escaped)
  75. lastBracket = i;
  76. else
  77. cleanString<<_string[i];
  78. }
  79. else
  80. {
  81. if(isdigit(_string[i]))
  82. bracketChars<<_string[i];
  83. else
  84. {
  85. // If current char is non-escaped closing bracket end parameter definition
  86. UINT32 numParamChars = (UINT32)bracketChars.tellp();
  87. if(_string[i] == '}' && numParamChars > 0 && !escaped)
  88. {
  89. numRemovedChars += numParamChars + 2; // +2 for open and closed brackets
  90. UINT32 paramIdx = parseUnsignedInt(bracketChars.str());
  91. paramOffsets.push_back(ParamOffset(paramIdx, i + 1 - numRemovedChars));
  92. }
  93. else
  94. {
  95. // Last bracket wasn't really a parameter
  96. for(UINT32 j = lastBracket; j <= i; j++)
  97. cleanString<<_string[j];
  98. }
  99. lastBracket = -1;
  100. bracketChars.str(L"");
  101. bracketChars.clear();
  102. }
  103. }
  104. escaped = false;
  105. }
  106. string = cleanString.str();
  107. numParameters = (UINT32)paramOffsets.size();
  108. // Try to find out of order param offsets and fix them
  109. std::sort(begin(paramOffsets), end(paramOffsets),
  110. [&] (const ParamOffset& a, const ParamOffset& b) { return a.paramIdx < b.paramIdx; } );
  111. if(paramOffsets.size() > 0)
  112. {
  113. UINT32 sequentialIdx = 0;
  114. UINT32 lastParamIdx = paramOffsets[0].paramIdx;
  115. for(UINT32 i = 0; i < numParameters; i++)
  116. {
  117. if(paramOffsets[i].paramIdx == lastParamIdx)
  118. {
  119. paramOffsets[i].paramIdx = sequentialIdx;
  120. continue;
  121. }
  122. lastParamIdx = paramOffsets[i].paramIdx;
  123. sequentialIdx++;
  124. paramOffsets[i].paramIdx = sequentialIdx;
  125. }
  126. }
  127. // Re-sort based on location since we find that more useful at runtime
  128. std::sort(begin(paramOffsets), end(paramOffsets),
  129. [&] (const ParamOffset& a, const ParamOffset& b) { return a.location < b.location; } );
  130. parameterOffsets = bs_newN<ParamOffset>(numParameters);
  131. for(UINT32 i = 0; i < numParameters; i++)
  132. parameterOffsets[i] = paramOffsets[i];
  133. }
  134. StringTable::StringTable()
  135. :Resource(false), mActiveLanguageData(nullptr), mDefaultLanguageData(nullptr), mAllLanguages(nullptr)
  136. {
  137. mAllLanguages = bs_newN<LanguageData>((UINT32)Language::Count);
  138. mDefaultLanguageData = &(mAllLanguages[(UINT32)DEFAULT_LANGUAGE]);
  139. mActiveLanguageData = mDefaultLanguageData;
  140. mActiveLanguage = DEFAULT_LANGUAGE;
  141. }
  142. StringTable::~StringTable()
  143. {
  144. bs_deleteN(mAllLanguages, (UINT32)Language::Count);
  145. }
  146. void StringTable::setActiveLanguage(Language language)
  147. {
  148. if(language == mActiveLanguage)
  149. return;
  150. mActiveLanguageData = &(mAllLanguages[(UINT32)language]);
  151. mActiveLanguage = language;
  152. }
  153. void StringTable::setString(const WString& identifier, Language language, const WString& string)
  154. {
  155. LanguageData* curLanguage = &(mAllLanguages[(UINT32)language]);
  156. auto iterFind = curLanguage->strings.find(identifier);
  157. SPtr<LocalizedStringData> stringData;
  158. if(iterFind == curLanguage->strings.end())
  159. {
  160. stringData = bs_shared_ptr<LocalizedStringData>();
  161. curLanguage->strings[identifier] = stringData;
  162. }
  163. else
  164. {
  165. stringData = iterFind->second;
  166. }
  167. mIdentifiers.insert(identifier);
  168. stringData->updateString(string);
  169. }
  170. WString StringTable::getString(const WString& identifier, Language language)
  171. {
  172. LanguageData* curLanguage = &(mAllLanguages[(UINT32)language]);
  173. auto iterFind = curLanguage->strings.find(identifier);
  174. if (iterFind != curLanguage->strings.end())
  175. return iterFind->second->string;
  176. return identifier;
  177. }
  178. void StringTable::removeString(const WString& identifier)
  179. {
  180. for(UINT32 i = 0; i < (UINT32)Language::Count; i++)
  181. {
  182. mAllLanguages[i].strings.erase(identifier);
  183. }
  184. mIdentifiers.erase(identifier);
  185. }
  186. SPtr<LocalizedStringData> StringTable::getStringData(const WString& identifier, bool insertIfNonExisting)
  187. {
  188. return getStringData(identifier, mActiveLanguage, insertIfNonExisting);
  189. }
  190. SPtr<LocalizedStringData> StringTable::getStringData(const WString& identifier, Language language, bool insertIfNonExisting)
  191. {
  192. LanguageData* curLanguage = &(mAllLanguages[(UINT32)language]);
  193. auto iterFind = curLanguage->strings.find(identifier);
  194. if(iterFind != curLanguage->strings.end())
  195. return iterFind->second;
  196. auto defaultIterFind = mDefaultLanguageData->strings.find(identifier);
  197. if(defaultIterFind != mDefaultLanguageData->strings.end())
  198. return defaultIterFind->second;
  199. if(insertIfNonExisting)
  200. {
  201. setString(identifier, DEFAULT_LANGUAGE, identifier);
  202. auto defaultIterFind = mDefaultLanguageData->strings.find(identifier);
  203. if(defaultIterFind != mDefaultLanguageData->strings.end())
  204. return defaultIterFind->second;
  205. }
  206. BS_EXCEPT(InvalidParametersException, "There is no string data for the provided identifier.");
  207. }
  208. HStringTable StringTable::create()
  209. {
  210. return static_resource_cast<StringTable>(gResources()._createResourceHandle(_createPtr()));
  211. }
  212. SPtr<StringTable> StringTable::_createPtr()
  213. {
  214. SPtr<StringTable> scriptCodePtr = bs_core_ptr<StringTable, PoolAlloc>(
  215. new (bs_alloc<StringTable, PoolAlloc>()) StringTable());
  216. scriptCodePtr->_setThisPtr(scriptCodePtr);
  217. scriptCodePtr->initialize();
  218. return scriptCodePtr;
  219. }
  220. RTTITypeBase* StringTable::getRTTIStatic()
  221. {
  222. return StringTableRTTI::instance();
  223. }
  224. RTTITypeBase* StringTable::getRTTI() const
  225. {
  226. return StringTable::getRTTIStatic();
  227. }
  228. }