| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- //============================================================================================
- // Spirenkov Maxim, 2006
- //============================================================================================
- // LocStrings
- //============================================================================================
- #include "LocStrings.h"
- #include "..\..\common_h\data_swizzle.h"
- #include "..\..\common_h\StringUtils\Win1250.h"
- #include "..\..\common_h\StringUtils\Win1251.h"
- #include "..\..\common_h\StringUtils\Win1252.h"
- #include "..\..\common_h\sound.h"
- #ifdef _XBOX
- CREATE_SERVICE(LocStrings, 23)
- #else
- CREATE_SERVICE(LocStrings, 3)
- #endif
- #define DELIMETER '\t'
- LocStrings::LocStrings() : strings(_FL_, 256)
- {
- strings.Reserve(1024);
- }
- LocStrings::~LocStrings()
- {
- }
- //Инициализация
- bool LocStrings::Init()
- {
- LoadAll();
- return true;
- }
- //Получить строку по идентификатору
- const char * LocStrings::GetString(long id)
- {
- long eindex = id & (ARRSIZE(entryTable) - 1);
- for(long i = entryTable[eindex]; i >= 0; i = strings[i].next)
- {
- if(strings[i].id == id)
- {
- strings[i].flags |= f_use;
- return strings[i].str;
- }
- }
- return null;
- }
- //Вывести в лог список неиспользуемых строк
- void LocStrings::TraceUnuse()
- {
- api->Trace("-------------------------------------------\nUnuse strings table\n-------------------------------------------\n\n");
- for(dword i = 0; i < strings.Size(); i++)
- {
- StringElement & se = strings[i];
- if((se.flags & f_use) == 0)
- {
- api->Trace("%.5i : %s", se.id, se.str.c_str());
- }
- }
- api->Trace("-------------------------------------------\n\n");
- }
- void LocStrings::LoadTable(IFileService * fs, const char * szFileName)
- {
- //Загружаем файл
- Assert(fs);
- ILoadBuffer * loadBuffer = fs->LoadData(szFileName, _FL_);
- if(!loadBuffer)
- {
- api->Trace("LocStrings ERROR: Can't find or open language file '%s' !!!", szFileName);
- return;
- }
-
- api->Trace("LocStrings message: Load language file '%s'", szFileName);
- dword dwFileSize = loadBuffer->Size();
- word* header = (word *)loadBuffer->Buffer();
- LPCWSTR unicodeData = (LPCWSTR)(loadBuffer->Buffer() + sizeof(word));
-
- //Верно только для UTF-16 UCS2
- dword unicodeCharCount = (dwFileSize - sizeof(word)) >> 1;
- if (*header == 0xFFFE)
- {
- //inverse byte order unicode (swizzle need)
- short* swizzleArray = (short*)unicodeData;
- for (dword i = 0; i < unicodeCharCount; i++)
- {
- swizzleArray[i] = __DataSwizzler(swizzleArray[i]);
- }
- } else
- {
- if (*header == 0xFEFF)
- {
- //platform byte order unicode
- } else
- {
- api->Trace("LocStrings ERROR: Language is not unicode file !!!");
- return;
- }
- }
- dword ansiDataSize = dwFileSize + 1;
- array<char> ansiTextData(_FL_);
- ansiTextData.AddElements(ansiDataSize);
- char * ansiText = ansiTextData.GetBuffer();
- memset (ansiText, 0, ansiDataSize);
-
- int ansiCharsResult = ConvertUnicodeToCP1251(unicodeData, ansiText, ansiDataSize);
- if (ansiCharsResult == 0)
- {
- api->Trace("LocStrings ERROR: Can't convert localization text from UNICODE to ANSI !!");
- return;
- }
-
- //Разбираем текст
- StringElement se;
- const char * text = (const char *)ansiText;
- const char * end = (const char *)(ansiText + ansiCharsResult);
- //Пропускаем первую строчку, там шапка с названиями языков...
- for(; text < end; text++)
- {
- if(*text == '\r' || *text == '\n') break;
- }
- //ansiText ... text - тут шапка с названиям языков, что бы выбрать колонку правильную
- //text ... end - тут csv таблица, которую надо парсить
- //Строим список языков, список что бы потом можно было к примеру наружу этот список отдать
- //Если нужно будет...
- array<string> languages(_FL_);
- for (dword lang = 0; lang < 512; lang++)
- {
- const char * headerText = (const char *)ansiText;
- bool parseResult = ParseString(headerText, text, se, lang);
- if(parseResult)
- {
- //api->Trace("LocStrings message: Found language : '%s'", se.str.c_str());
- languages.Add(se.str);
- } else
- {
- //Строчка не парсится - используем по умолчанию, первый столбец
- break;
- }
- }
- if (languages.Size() <= 0)
- {
- api->Trace("LocStrings ERROR: No languages file found !!!");
- return;
- }
- long localizationColumn = 0;
- for (dword lang = 0; lang < languages.Size(); lang++)
- {
- if (locId == languages[lang])
- {
- //Нашли нужный язык...
- localizationColumn = lang;
- break;
- }
- }
- //api->Trace("LocStrings message: Selected language : '%s'", languages[localizationColumn].c_str());
- while(text < end)
- {
- //Выделяем строку
- const char * str = text;
- const char * str1 = text;
- //Ищем окончание строки
- for(; text < end; text++)
- {
- if(*text == '\r' || *text == '\n') break;
- }
- //Разбираем строку
- if(ParseString(str, text++, se, localizationColumn))
- {
- if(se.id >= 0)
- {
- AddString(se);
- }else{
- api->Trace("LocStrings ERROR: Found line without identificator!!! -> %s", str1);
- }
- }
- }
- loadBuffer->Release();
- }
- void LocStrings::LoadAll()
- {
- //Очищаем текущее содержимое
- for(long i = 0; i < ARRSIZE(entryTable); i++)
- {
- entryTable[i] = -1;
- }
- strings.DelAll();
- //Очищаем идентификатор языка
- locId.Empty();
- //Файловый сервис
- IFileService * fs = (IFileService *)api->GetService("FileService");
- Assert(fs);
- //Получаем текущий язык...
- #ifndef GAME_RUSSIAN
- const char* currentLanguage = api->Storage().GetString(ILocStrings_StorageLocalizationPath, null);
- if(!currentLanguage)
- {
- IIniFile * iniFile = fs->SystemIni();
- const char * loc = iniFile->GetString(null, "localization", null);
- if(!string::IsEmpty(loc))
- {
- currentLanguage = loc;
- }else{
- currentLanguage = ILocStrings_LocalizationDefaultValue;
- }
- }
- locId = currentLanguage;
- #else
- locId = "";
- locId += "r";
- locId += "";
- locId += "us";
- #endif
-
- //Перебераем локализационные файлы
- #ifndef _XBOX
- IPackFile * pack = fs->LoadPack("Resource\\text.pkx", _FL_);
- #else
- IPackFile * pack = fs->LoadPack("text.pkx", _FL_);
- #endif
- IFinder* fileFinder = fs->CreateFinder("Resource\\Text\\", "*.csv", find_all_files, _FL_);
- for (dword i = 0; i < fileFinder->Count(); i++)
- {
- const char * fileName = fileFinder->FilePath(i);
- LoadTable(fs, fileName);
- }
- fileFinder->Release();
- //Загружаем звуковые банки
- RELEASE(pack);
- }
- //Получить идентификатор языка
- const char * LocStrings::GetLocId()
- {
- return locId.c_str();
- }
- //Получить количество строк
- dword LocStrings::GetStringsCount()
- {
- return strings.Size();
- }
- //Получить строку по индексу
- const char * LocStrings::GetStringByIndex(dword index)
- {
- return strings[index].str.c_str();
- }
- //Получить идентификатор по индексу
- long LocStrings::GetIdByIndex(dword index)
- {
- return strings[index].id;
- }
- //Разбираем строку
- bool LocStrings::ParseString(const char * & str, const char * end, StringElement & se, long column)
- {
- se.id = -1;
- se.next = -1;
- se.str.Empty();
- //Пропускаем свободное место
- for(; str < end; str++)
- {
- if(*str == DELIMETER) return false;
- if(*str >= '0' || *str <= '9') break;
- }
- if(str >= end) return false;
- //Получаем идентификатор
- bool isFindNumber = false;
- for(long id = 0, count = 0; str < end && count < 10; str++, count++)
- {
- if(*str < '0' || *str > '9') break;
- isFindNumber = true;
- id = id*10 + *str - '0';
- }
- if(!isFindNumber)
- {
- id = -1;
- }
- //Ищем разделитель (пропускаем, столько разделителей, сколько заданно в column)
- long skipCount = column;
- for(; str < end; str++)
- {
- //Такого столбца нет !!!
- if(*str == '\r' || *str == '\n')
- {
- return false;
- }
- if(*str == DELIMETER)
- {
- if (skipCount <= 0)
- {
- break;
- } else
- {
- skipCount--;
- }
- }
- }
- if(str >= end) return false;
- //Теперь начало столбца в str
- const char * columnEnd = str;
- columnEnd++;
- for(; columnEnd < end; columnEnd++)
- {
- if(*columnEnd == '\r' || *columnEnd == '\n')
- {
- columnEnd--;
- break;
- }
- if(*columnEnd == DELIMETER)
- {
- break;
- }
- }
- //columnEnd теперь это последний символ в столбце...
- //Если конец столбца раньше начала, он пустой к примеру, то на выход
- if(columnEnd < str) return false;
- end = columnEnd;
- //Ищем начало строки
- for(str++; str < end; str++)
- {
- if(*str != ' ') break;
- }
- if(str >= end) return false;
- //Модифицируем конец строки
- for(end--; end >= str; end--)
- {
- if(*end != ' ') break;
- }
- //Добавляем запись
- se.id = id;
- for(; str <= end; str++)
- {
- se.str += *str;
- }
- str++;
- return true;
- }
- //Добавить запись
- void LocStrings::AddString(StringElement & se)
- {
- //api->Trace("%d = '%s'", se.id, se.str.c_str());
- const char* szStringExist = GetString(se.id);
- if(szStringExist)
- {
- api->Trace("string with id = %d (in table: \"%s\", add: \"%s\") have duplicated !!\n", se.id, szStringExist, se.str.c_str());
- szStringExist = null;
- }
- Assert(se.id >= 0);
- //Assert(szStringExist == null);
- Assert(strings.Size() < 0x7fff);
- dword index = strings.Add(se);
- StringElement & st = strings[index];
- st.next = -1;
- st.flags = 0;
- long eindex = se.id & (ARRSIZE(entryTable) - 1);
- if(entryTable[eindex] >= 0)
- {
- for(long i = entryTable[eindex]; strings[i].next >= 0; i = strings[i].next);
- strings[i].next = index;
- }else{
- entryTable[eindex] = index;
- }
- //Фиксим кавычки
- char * src = st.str.GetDataBuffer();
- if(src)
- {
- char * dst = src;
- while(*src)
- {
- if(*src != '"')
- {
- *dst++ = *src++;
- }else{
- if(src[1] != '"')
- {
- src++;
- }else{
- *dst++ = '"';
- for(src += 2; *src == '"'; src++);
- }
- }
- }
- *dst++ = 0;
- st.str.SetDataSize(dst - st.str.GetDataBuffer());
- }
- //api->Trace("string %s", st.str.c_str());
- }
|