LocStrings.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. //============================================================================================
  2. // Spirenkov Maxim, 2006
  3. //============================================================================================
  4. // LocStrings
  5. //============================================================================================
  6. #include "LocStrings.h"
  7. #include "..\..\common_h\data_swizzle.h"
  8. #include "..\..\common_h\StringUtils\Win1250.h"
  9. #include "..\..\common_h\StringUtils\Win1251.h"
  10. #include "..\..\common_h\StringUtils\Win1252.h"
  11. #include "..\..\common_h\sound.h"
  12. #ifdef _XBOX
  13. CREATE_SERVICE(LocStrings, 23)
  14. #else
  15. CREATE_SERVICE(LocStrings, 3)
  16. #endif
  17. #define DELIMETER '\t'
  18. LocStrings::LocStrings() : strings(_FL_, 256)
  19. {
  20. strings.Reserve(1024);
  21. }
  22. LocStrings::~LocStrings()
  23. {
  24. }
  25. //Инициализация
  26. bool LocStrings::Init()
  27. {
  28. LoadAll();
  29. return true;
  30. }
  31. //Получить строку по идентификатору
  32. const char * LocStrings::GetString(long id)
  33. {
  34. long eindex = id & (ARRSIZE(entryTable) - 1);
  35. for(long i = entryTable[eindex]; i >= 0; i = strings[i].next)
  36. {
  37. if(strings[i].id == id)
  38. {
  39. strings[i].flags |= f_use;
  40. return strings[i].str;
  41. }
  42. }
  43. return null;
  44. }
  45. //Вывести в лог список неиспользуемых строк
  46. void LocStrings::TraceUnuse()
  47. {
  48. api->Trace("-------------------------------------------\nUnuse strings table\n-------------------------------------------\n\n");
  49. for(dword i = 0; i < strings.Size(); i++)
  50. {
  51. StringElement & se = strings[i];
  52. if((se.flags & f_use) == 0)
  53. {
  54. api->Trace("%.5i : %s", se.id, se.str.c_str());
  55. }
  56. }
  57. api->Trace("-------------------------------------------\n\n");
  58. }
  59. void LocStrings::LoadTable(IFileService * fs, const char * szFileName)
  60. {
  61. //Загружаем файл
  62. Assert(fs);
  63. ILoadBuffer * loadBuffer = fs->LoadData(szFileName, _FL_);
  64. if(!loadBuffer)
  65. {
  66. api->Trace("LocStrings ERROR: Can't find or open language file '%s' !!!", szFileName);
  67. return;
  68. }
  69. api->Trace("LocStrings message: Load language file '%s'", szFileName);
  70. dword dwFileSize = loadBuffer->Size();
  71. word* header = (word *)loadBuffer->Buffer();
  72. LPCWSTR unicodeData = (LPCWSTR)(loadBuffer->Buffer() + sizeof(word));
  73. //Верно только для UTF-16 UCS2
  74. dword unicodeCharCount = (dwFileSize - sizeof(word)) >> 1;
  75. if (*header == 0xFFFE)
  76. {
  77. //inverse byte order unicode (swizzle need)
  78. short* swizzleArray = (short*)unicodeData;
  79. for (dword i = 0; i < unicodeCharCount; i++)
  80. {
  81. swizzleArray[i] = __DataSwizzler(swizzleArray[i]);
  82. }
  83. } else
  84. {
  85. if (*header == 0xFEFF)
  86. {
  87. //platform byte order unicode
  88. } else
  89. {
  90. api->Trace("LocStrings ERROR: Language is not unicode file !!!");
  91. return;
  92. }
  93. }
  94. dword ansiDataSize = dwFileSize + 1;
  95. array<char> ansiTextData(_FL_);
  96. ansiTextData.AddElements(ansiDataSize);
  97. char * ansiText = ansiTextData.GetBuffer();
  98. memset (ansiText, 0, ansiDataSize);
  99. int ansiCharsResult = ConvertUnicodeToCP1251(unicodeData, ansiText, ansiDataSize);
  100. if (ansiCharsResult == 0)
  101. {
  102. api->Trace("LocStrings ERROR: Can't convert localization text from UNICODE to ANSI !!");
  103. return;
  104. }
  105. //Разбираем текст
  106. StringElement se;
  107. const char * text = (const char *)ansiText;
  108. const char * end = (const char *)(ansiText + ansiCharsResult);
  109. //Пропускаем первую строчку, там шапка с названиями языков...
  110. for(; text < end; text++)
  111. {
  112. if(*text == '\r' || *text == '\n') break;
  113. }
  114. //ansiText ... text - тут шапка с названиям языков, что бы выбрать колонку правильную
  115. //text ... end - тут csv таблица, которую надо парсить
  116. //Строим список языков, список что бы потом можно было к примеру наружу этот список отдать
  117. //Если нужно будет...
  118. array<string> languages(_FL_);
  119. for (dword lang = 0; lang < 512; lang++)
  120. {
  121. const char * headerText = (const char *)ansiText;
  122. bool parseResult = ParseString(headerText, text, se, lang);
  123. if(parseResult)
  124. {
  125. //api->Trace("LocStrings message: Found language : '%s'", se.str.c_str());
  126. languages.Add(se.str);
  127. } else
  128. {
  129. //Строчка не парсится - используем по умолчанию, первый столбец
  130. break;
  131. }
  132. }
  133. if (languages.Size() <= 0)
  134. {
  135. api->Trace("LocStrings ERROR: No languages file found !!!");
  136. return;
  137. }
  138. long localizationColumn = 0;
  139. for (dword lang = 0; lang < languages.Size(); lang++)
  140. {
  141. if (locId == languages[lang])
  142. {
  143. //Нашли нужный язык...
  144. localizationColumn = lang;
  145. break;
  146. }
  147. }
  148. //api->Trace("LocStrings message: Selected language : '%s'", languages[localizationColumn].c_str());
  149. while(text < end)
  150. {
  151. //Выделяем строку
  152. const char * str = text;
  153. const char * str1 = text;
  154. //Ищем окончание строки
  155. for(; text < end; text++)
  156. {
  157. if(*text == '\r' || *text == '\n') break;
  158. }
  159. //Разбираем строку
  160. if(ParseString(str, text++, se, localizationColumn))
  161. {
  162. if(se.id >= 0)
  163. {
  164. AddString(se);
  165. }else{
  166. api->Trace("LocStrings ERROR: Found line without identificator!!! -> %s", str1);
  167. }
  168. }
  169. }
  170. loadBuffer->Release();
  171. }
  172. void LocStrings::LoadAll()
  173. {
  174. //Очищаем текущее содержимое
  175. for(long i = 0; i < ARRSIZE(entryTable); i++)
  176. {
  177. entryTable[i] = -1;
  178. }
  179. strings.DelAll();
  180. //Очищаем идентификатор языка
  181. locId.Empty();
  182. //Файловый сервис
  183. IFileService * fs = (IFileService *)api->GetService("FileService");
  184. Assert(fs);
  185. //Получаем текущий язык...
  186. #ifndef GAME_RUSSIAN
  187. const char* currentLanguage = api->Storage().GetString(ILocStrings_StorageLocalizationPath, null);
  188. if(!currentLanguage)
  189. {
  190. IIniFile * iniFile = fs->SystemIni();
  191. const char * loc = iniFile->GetString(null, "localization", null);
  192. if(!string::IsEmpty(loc))
  193. {
  194. currentLanguage = loc;
  195. }else{
  196. currentLanguage = ILocStrings_LocalizationDefaultValue;
  197. }
  198. }
  199. locId = currentLanguage;
  200. #else
  201. locId = "";
  202. locId += "r";
  203. locId += "";
  204. locId += "us";
  205. #endif
  206. //Перебераем локализационные файлы
  207. #ifndef _XBOX
  208. IPackFile * pack = fs->LoadPack("Resource\\text.pkx", _FL_);
  209. #else
  210. IPackFile * pack = fs->LoadPack("text.pkx", _FL_);
  211. #endif
  212. IFinder* fileFinder = fs->CreateFinder("Resource\\Text\\", "*.csv", find_all_files, _FL_);
  213. for (dword i = 0; i < fileFinder->Count(); i++)
  214. {
  215. const char * fileName = fileFinder->FilePath(i);
  216. LoadTable(fs, fileName);
  217. }
  218. fileFinder->Release();
  219. //Загружаем звуковые банки
  220. RELEASE(pack);
  221. }
  222. //Получить идентификатор языка
  223. const char * LocStrings::GetLocId()
  224. {
  225. return locId.c_str();
  226. }
  227. //Получить количество строк
  228. dword LocStrings::GetStringsCount()
  229. {
  230. return strings.Size();
  231. }
  232. //Получить строку по индексу
  233. const char * LocStrings::GetStringByIndex(dword index)
  234. {
  235. return strings[index].str.c_str();
  236. }
  237. //Получить идентификатор по индексу
  238. long LocStrings::GetIdByIndex(dword index)
  239. {
  240. return strings[index].id;
  241. }
  242. //Разбираем строку
  243. bool LocStrings::ParseString(const char * & str, const char * end, StringElement & se, long column)
  244. {
  245. se.id = -1;
  246. se.next = -1;
  247. se.str.Empty();
  248. //Пропускаем свободное место
  249. for(; str < end; str++)
  250. {
  251. if(*str == DELIMETER) return false;
  252. if(*str >= '0' || *str <= '9') break;
  253. }
  254. if(str >= end) return false;
  255. //Получаем идентификатор
  256. bool isFindNumber = false;
  257. for(long id = 0, count = 0; str < end && count < 10; str++, count++)
  258. {
  259. if(*str < '0' || *str > '9') break;
  260. isFindNumber = true;
  261. id = id*10 + *str - '0';
  262. }
  263. if(!isFindNumber)
  264. {
  265. id = -1;
  266. }
  267. //Ищем разделитель (пропускаем, столько разделителей, сколько заданно в column)
  268. long skipCount = column;
  269. for(; str < end; str++)
  270. {
  271. //Такого столбца нет !!!
  272. if(*str == '\r' || *str == '\n')
  273. {
  274. return false;
  275. }
  276. if(*str == DELIMETER)
  277. {
  278. if (skipCount <= 0)
  279. {
  280. break;
  281. } else
  282. {
  283. skipCount--;
  284. }
  285. }
  286. }
  287. if(str >= end) return false;
  288. //Теперь начало столбца в str
  289. const char * columnEnd = str;
  290. columnEnd++;
  291. for(; columnEnd < end; columnEnd++)
  292. {
  293. if(*columnEnd == '\r' || *columnEnd == '\n')
  294. {
  295. columnEnd--;
  296. break;
  297. }
  298. if(*columnEnd == DELIMETER)
  299. {
  300. break;
  301. }
  302. }
  303. //columnEnd теперь это последний символ в столбце...
  304. //Если конец столбца раньше начала, он пустой к примеру, то на выход
  305. if(columnEnd < str) return false;
  306. end = columnEnd;
  307. //Ищем начало строки
  308. for(str++; str < end; str++)
  309. {
  310. if(*str != ' ') break;
  311. }
  312. if(str >= end) return false;
  313. //Модифицируем конец строки
  314. for(end--; end >= str; end--)
  315. {
  316. if(*end != ' ') break;
  317. }
  318. //Добавляем запись
  319. se.id = id;
  320. for(; str <= end; str++)
  321. {
  322. se.str += *str;
  323. }
  324. str++;
  325. return true;
  326. }
  327. //Добавить запись
  328. void LocStrings::AddString(StringElement & se)
  329. {
  330. //api->Trace("%d = '%s'", se.id, se.str.c_str());
  331. const char* szStringExist = GetString(se.id);
  332. if(szStringExist)
  333. {
  334. api->Trace("string with id = %d (in table: \"%s\", add: \"%s\") have duplicated !!\n", se.id, szStringExist, se.str.c_str());
  335. szStringExist = null;
  336. }
  337. Assert(se.id >= 0);
  338. //Assert(szStringExist == null);
  339. Assert(strings.Size() < 0x7fff);
  340. dword index = strings.Add(se);
  341. StringElement & st = strings[index];
  342. st.next = -1;
  343. st.flags = 0;
  344. long eindex = se.id & (ARRSIZE(entryTable) - 1);
  345. if(entryTable[eindex] >= 0)
  346. {
  347. for(long i = entryTable[eindex]; strings[i].next >= 0; i = strings[i].next);
  348. strings[i].next = index;
  349. }else{
  350. entryTable[eindex] = index;
  351. }
  352. //Фиксим кавычки
  353. char * src = st.str.GetDataBuffer();
  354. if(src)
  355. {
  356. char * dst = src;
  357. while(*src)
  358. {
  359. if(*src != '"')
  360. {
  361. *dst++ = *src++;
  362. }else{
  363. if(src[1] != '"')
  364. {
  365. src++;
  366. }else{
  367. *dst++ = '"';
  368. for(src += 2; *src == '"'; src++);
  369. }
  370. }
  371. }
  372. *dst++ = 0;
  373. st.str.SetDataSize(dst - st.str.GetDataBuffer());
  374. }
  375. //api->Trace("string %s", st.str.c_str());
  376. }