|
@@ -1,7 +1,3 @@
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
#include <DatSubsystems/DatLocaleManager.h>
|
|
|
#include <EasyLogging++/easylogging++.h>
|
|
|
#include <DatFile.h>
|
|
@@ -11,12 +7,45 @@ namespace LOTRO_DAT {
|
|
|
DatLocaleManager::DatLocaleManager(DatFile *datFilePtr) : dat(datFilePtr), current_locale_(ORIGINAL) {
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ * \Author Gi1dor
|
|
|
+ * \date 06.07.2018
|
|
|
+ * Инициализация модуля. Должна происходить после инициализации модуля ввода-вывода и файловой системы.
|
|
|
+ * Считывает словарь и проверяет корректность (если структура словаря нарушена и основной является локаль PATHCED,
|
|
|
+ * то dat файл считается некорреткным (любое обновлене dat файла с активной локалью PATCHED нарушает структуру файла
|
|
|
+ * и делает использование в игре невозможным.
|
|
|
+ *
|
|
|
+ * \warning Не должна вызываться вручную! Автоматически вызывается в функции Initialise класса DatFile
|
|
|
+ *
|
|
|
+ * Структура словарей локализации:
|
|
|
+ * ======== LOCALE DICT STRUCTURE =========
|
|
|
+ * 4 bytes for dict size (in bytes)
|
|
|
+ * 4 bytes for locale version
|
|
|
+ * 4 bytes for .dat file size (with patches)
|
|
|
+ * 15 bytes for "Hi from Gi1dor"
|
|
|
+ * 4 bytes for LOCALE
|
|
|
+ * 4 bytes for orig_dict.size()
|
|
|
+ * (32 + 4) * orig_dict.size() bytes for orig_dict data
|
|
|
+ * 4 bytes for patch_dict.size()
|
|
|
+ * (32 + 4) * patch_dict.size() bytes for patch_dict data
|
|
|
+ * 4 bytes for inactive_categories dict
|
|
|
+ * 4 * inactive_categories.size() bytes for inactive_categories data
|
|
|
+ * ========================================
|
|
|
+ * Помимо этого:
|
|
|
+ * 0x128-0x12C - Флаг, установлена ли альтернативная версия (flag)
|
|
|
+ * 0x12C-0x130 - Офсет начала словаря локализации (offset)
|
|
|
+ *
|
|
|
+ * \warning Если flag != 0 и offset == 0 - то dat файл считается повреждённым. Проверка осуществляется функцией CheckLocaleCorrectж
|
|
|
+ */
|
|
|
+
|
|
|
DatOperationResult<> DatLocaleManager::Init() {
|
|
|
if (!dat)
|
|
|
return DatOperationResult<>(ERROR, "LOCALEINIT: no connection with Dat (dat is nullptr)");
|
|
|
|
|
|
orig_dict_.clear();
|
|
|
patch_dict_.clear();
|
|
|
+ inactive_categories.clear();
|
|
|
+ current_locale_ = ORIGINAL;
|
|
|
|
|
|
LOG(INFO) << "Initialising locales...";
|
|
|
BinaryData locale_offset_data(4);
|
|
@@ -39,11 +68,14 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
if (dict_version != 101) {
|
|
|
dat->getIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
|
|
|
- return DatOperationResult<>(SUCCESS, "Version of locales' dictionary is old (ver = " + std::to_string(dict_version) + "), cannot initialize locales.");
|
|
|
+ if (CheckLocaleCorrect())
|
|
|
+ return DatOperationResult<>(SUCCESS);
|
|
|
+ else
|
|
|
+ return DatOperationResult<>(ERROR, "Version of locales' dictionary is incorrect, through patched mark is standing. Dat file may be corrupted");
|
|
|
}
|
|
|
|
|
|
BinaryData dicts_data = BinaryData((unsigned)dict_size);
|
|
|
- dat->getIO().ReadData(dicts_data, dict_size, locale_offset + 12);
|
|
|
+ dat->getIO().ReadData(dicts_data, dict_size - 12, locale_offset + 12);
|
|
|
|
|
|
if (dicts_data.size() < 15) {
|
|
|
dat->getIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
|
|
@@ -87,9 +119,22 @@ namespace LOTRO_DAT {
|
|
|
LOG(INFO) << "There are " << patch_dict_.size() << " files in patch locale dictionary";
|
|
|
LOG(INFO) << "There are " << orig_dict_.size() << " files in original locale dictionary";
|
|
|
LOG(INFO) << "Finished initialising locales";
|
|
|
- return DatOperationResult<>(SUCCESS);
|
|
|
+
|
|
|
+ if (CheckLocaleCorrect())
|
|
|
+ return DatOperationResult<>(SUCCESS);
|
|
|
+ else
|
|
|
+ return DatOperationResult<>(ERROR, "Locale dict is incorrect, through patched mark is standing. Dat file may be corrupted");
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+ * \Author Gi1dor
|
|
|
+ * \date 06.07.2018
|
|
|
+ * Смена активной локали. Меняет информацию о файлах в файловой системе на соответствующую информацию в локали.
|
|
|
+ * \param[in] locale Устанавливаемая локаль
|
|
|
+ * \warning Изменения в файловую систему вносятся локально! В dat файл изменения будут записаны во время деинициализации модуля файловой системы
|
|
|
+ */
|
|
|
+
|
|
|
DatOperationResult<> DatLocaleManager::SetLocale(DatLocaleManager::LOCALE locale) {
|
|
|
LOG(INFO) << "Setting locale to " << (locale == PATCHED ? " PATCHED" : " ORIGINAL");
|
|
|
if (!dat)
|
|
@@ -122,10 +167,46 @@ namespace LOTRO_DAT {
|
|
|
return DatOperationResult<>(SUCCESS);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+ * \Author Gi1dor
|
|
|
+ * \date 06.07.2018
|
|
|
+ * Получение текущей активной локали. Если локали не существуют, понимается, что используется оригинальная (ORIGINAL)
|
|
|
+ */
|
|
|
+
|
|
|
DatLocaleManager::LOCALE DatLocaleManager::GetCurrentLocale() {
|
|
|
return current_locale_;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+ * \Author Gi1dor
|
|
|
+ * \date 06.07.2018
|
|
|
+ * Деинициализация модуля. Должна происходить перед деинициализацией модулей ввода-вывода и файловой системы.
|
|
|
+ * Записывает словарь. Если ранее словарь не существовал - новый будет записан в конец файла.
|
|
|
+ * Для словаря выделяется блок не менее 20мб
|
|
|
+ *
|
|
|
+ * \warning Не должна вызываться вручную! Автоматически вызывается в функции Deinitialise класса DatFile
|
|
|
+ *
|
|
|
+ * Структура словарей локализации:
|
|
|
+ * ======== LOCALE DICT STRUCTURE =========
|
|
|
+ * 4 bytes for dict size (in bytes)
|
|
|
+ * 4 bytes for locale version
|
|
|
+ * 4 bytes for .dat file size (with patches)
|
|
|
+ * 15 bytes for "Hi from Gi1dor"
|
|
|
+ * 4 bytes for LOCALE
|
|
|
+ * 4 bytes for orig_dict.size()
|
|
|
+ * (32 + 4) * orig_dict.size() bytes for orig_dict data
|
|
|
+ * 4 bytes for patch_dict.size()
|
|
|
+ * (32 + 4) * patch_dict.size() bytes for patch_dict data
|
|
|
+ * 4 bytes for inactive_categories dict
|
|
|
+ * 4 * inactive_categories.size() bytes for inactive_categories data
|
|
|
+ * ========================================
|
|
|
+ * Помимо этого:
|
|
|
+ * 0x128-0x12C - Флаг, является ли активной альтернативная версия (PATCHED)
|
|
|
+ * 0x12C-0x130 - Офсет начала словаря локализации
|
|
|
+ */
|
|
|
+
|
|
|
DatOperationResult<> DatLocaleManager::DeInit() {
|
|
|
LOG(INFO) << "Committing locales...";
|
|
|
if (!dat)
|
|
@@ -191,8 +272,10 @@ namespace LOTRO_DAT {
|
|
|
if (binary_data.size() > dict_size || dict_offset == 0) {
|
|
|
auto operation = dat->getIO().WriteData(binary_data, binary_data.size(), dat->getIO().file_size + 12);
|
|
|
if (operation.result != SUCCESS)
|
|
|
- return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales. ERRMSG: " + operation.msg);
|
|
|
+ return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales");
|
|
|
+
|
|
|
dat->getIO().WriteData(BinaryData::FromNumber<4>(dat->getIO().file_size), 4, 300);
|
|
|
+ dat->getIO().WriteData(BinaryData::FromNumber<4>(current_locale_), 4, 296, 0);
|
|
|
|
|
|
dat->getIO().file_size += binary_data.size();
|
|
|
|
|
@@ -201,6 +284,7 @@ namespace LOTRO_DAT {
|
|
|
dat->getIO().WriteData(nulls, nulls.size(), dat->getIO().file_size);
|
|
|
dat->getIO().file_size += nulls.size();
|
|
|
} else {
|
|
|
+ dat->getIO().WriteData(BinaryData::FromNumber<4>(current_locale_), 4, 296, 0);
|
|
|
auto operation = dat->getIO().WriteData(binary_data, binary_data.size(), dict_offset);
|
|
|
if (operation.result != SUCCESS)
|
|
|
return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales. ERRMSG: " + operation.msg);
|
|
@@ -211,11 +295,27 @@ namespace LOTRO_DAT {
|
|
|
}
|
|
|
|
|
|
|
|
|
+
|
|
|
+ * \Author Gi1dor
|
|
|
+ * \date 06.07.2018
|
|
|
+ * Обновление данных файла в локали
|
|
|
+ * \param[in] locale Локаль, для которой файл данных будет обновлён
|
|
|
+ * \param[in] file Новые данные файла. Старые данные будут перезаписаны
|
|
|
+ */
|
|
|
+
|
|
|
void DatLocaleManager::UpdateLocaleFile(DatLocaleManager::LOCALE locale, const SubFile &file) {
|
|
|
auto dict = GetLocaleDictReference(locale);
|
|
|
dict[file.file_id()] = file;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ * \Author Gi1dor
|
|
|
+ * \date 06.07.2018
|
|
|
+ * Получение данных файла в локали. Вернёт DatOperationResult.result = ERROR, если файла с таким id не существует.
|
|
|
+ * \param[in] file_id id файла, для которого необходимо получить данных
|
|
|
+ * \param[in] locale Локаль, данные которой будут получены
|
|
|
+ */
|
|
|
+
|
|
|
DatOperationResult<SubFile> DatLocaleManager::GetLocaleFile(long long file_id, DatLocaleManager::LOCALE locale) {
|
|
|
auto dict = GetLocaleDictReference(locale);
|
|
|
if (dict.count(file_id) != 0)
|
|
@@ -223,10 +323,27 @@ namespace LOTRO_DAT {
|
|
|
return DatOperationResult<SubFile>(dict[file_id], SUCCESS);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+ * \Author Gi1dor
|
|
|
+ * \date 06.07.2018
|
|
|
+ * Получение словаря файлов локали
|
|
|
+ * \param[in] locale Локаль, данные которой будут получены
|
|
|
+ */
|
|
|
+
|
|
|
std::map<long long, SubFile> &DatLocaleManager::GetLocaleDictReference(DatLocaleManager::LOCALE locale) {
|
|
|
return locale == PATCHED ? patch_dict_ : orig_dict_;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+ * \Author Gi1dor
|
|
|
+ * \date 06.07.2018
|
|
|
+ * Выводит информацию о состоянии модуля в файл
|
|
|
+ * \param[in] file указатель на объект файла, в конец которого будет напечатана информация
|
|
|
+ * \warning Файл file должен быть открыт для записи. После завершения работы функции файл остаётся открытым
|
|
|
+ */
|
|
|
+
|
|
|
void DatLocaleManager::PrintInformaion(FILE *file) {
|
|
|
fprintf(file, "========= Locales info ========\n");
|
|
|
BinaryData locale_offset_data(4);
|
|
@@ -235,6 +352,12 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
fprintf(file, "Locales' dictionary offset = %lld\n", locale_offset);
|
|
|
|
|
|
+ BinaryData locale_status_data(4);
|
|
|
+ dat->getIO().ReadData(locale_status_data, 4, 296);
|
|
|
+ long long locale_status = locale_offset_data.ToNumber<4>(0);
|
|
|
+
|
|
|
+ fprintf(file, "Locale status = %lld\n", locale_status);
|
|
|
+
|
|
|
if (locale_offset != 0) {
|
|
|
BinaryData locale_info(12);
|
|
|
dat->getIO().ReadData(locale_info, 12, locale_offset);
|
|
@@ -251,4 +374,40 @@ namespace LOTRO_DAT {
|
|
|
fprintf(file, "Patch dictionary size = %d\n", patch_dict_.size());
|
|
|
fprintf(file, "Original dictionary size = %d\n", orig_dict_.size());
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * \Author Gi1dor
|
|
|
+ * \date 06.07.2018
|
|
|
+ * Осуществляет проверку корректности dat файла с позиции локалей
|
|
|
+ * Файл считается некорректным, если активной является альтернативная локаль, причём нет возможности вернуть оригинальную (блок локалей неверный/повреждён)
|
|
|
+ * Байты 0x128-0x12C показывают активную локаль на момент закрытия dat файла.
|
|
|
+ */
|
|
|
+
|
|
|
+ bool DatLocaleManager::CheckLocaleCorrect() {
|
|
|
+ BinaryData locale_info(4);
|
|
|
+ dat->getIO().ReadData(locale_info, 4, 296);
|
|
|
+ long long locale_status = locale_info.ToNumber<4>(0);
|
|
|
+
|
|
|
+ if (locale_status == ORIGINAL)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ BinaryData locale_offset_data(4);
|
|
|
+ dat->getIO().ReadData(locale_offset_data, 4, 300);
|
|
|
+ long long locale_offset = locale_offset_data.ToNumber<4>(0);
|
|
|
+
|
|
|
+ if (locale_offset == 0 || locale_offset + 8 >= dat->getIO().GetActualDatSize().value)
|
|
|
+ return locale_status == ORIGINAL;
|
|
|
+
|
|
|
+ BinaryData dicts_data = BinaryData(4);
|
|
|
+ auto operation = dat->getIO().ReadData(dicts_data, 4, locale_offset + 12 + 15);
|
|
|
+ if (operation.result == ERROR)
|
|
|
+ return locale_status == ORIGINAL;
|
|
|
+
|
|
|
+ BinaryData locale_data = dicts_data + BinaryData("\0", 1);
|
|
|
+ std::string locale((char *)(locale_data.data()));
|
|
|
+
|
|
|
+ LOCALE dat_locale = (locale == "PATC" ? PATCHED : ORIGINAL);
|
|
|
+ return locale_status == dat_locale;
|
|
|
+ }
|
|
|
}
|