DatLocaleManager.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. #include <DatSubsystems/DatLocaleManager.h>
  2. #include <EasyLogging++/easylogging++.h>
  3. #include <DatFile.h>
  4. #include <SubFile.h>
  5. #define DAT_LOCALE_DICT_VERSION 103
  6. namespace LOTRO_DAT {
  7. DatLocaleManager::DatLocaleManager(DatFile *datFilePtr) : dat(datFilePtr), current_locale_(ORIGINAL) {
  8. }
  9. /*!
  10. * \Author Gi1dor
  11. * \date 06.07.2018
  12. * Инициализация модуля. Должна происходить после инициализации модуля ввода-вывода и файловой системы.
  13. * Считывает словарь и проверяет корректность (если структура словаря нарушена и основной является локаль PATHCED,
  14. * то dat файл считается некорреткным (любое обновлене dat файла с активной локалью PATCHED нарушает структуру файла
  15. * и делает использование в игре невозможным.
  16. *
  17. * \warning Не должна вызываться вручную! Автоматически вызывается в функции Initialise класса DatFile
  18. *
  19. * Структура словарей локализации:
  20. * ======== LOCALE DICT STRUCTURE =========
  21. * 4 bytes for dict size (in bytes)
  22. * 4 bytes for locale version
  23. * 4 bytes for .dat file size (with patches)
  24. * 4 bytes for header hash
  25. * 15 bytes for "Hi from Gi1dor"
  26. * 4 bytes for LOCALE mark ("PATC" or "ORIG")
  27. * 4 bytes for orig_dict.size()
  28. * (32 + 4) * orig_dict.size() bytes for orig_dict data
  29. * 4 bytes for patch_dict.size()
  30. * (32 + 4) * patch_dict.size() bytes for patch_dict data
  31. * 4 bytes for inactive_categories dict
  32. * 4 * inactive_categories.size() bytes for inactive_categories data
  33. * ========================================
  34. * Помимо этого:
  35. * 0x128-0x12C - Флаг, установлена ли альтернативная версия (flag)
  36. * 0x12C-0x130 - Офсет начала словаря локализации (offset)
  37. *
  38. * \warning Если flag != 0 и offset == 0 - то dat файл считается повреждённым. Проверка осуществляется функцией CheckLocaleCorrectж
  39. */
  40. DatOperationResult<> DatLocaleManager::Init() {
  41. if (!dat)
  42. return DatOperationResult<>(ERROR, "LOCALEINIT: no connection with Dat (dat is nullptr)");
  43. orig_dict_.clear();
  44. patch_dict_.clear();
  45. inactive_categories.clear();
  46. current_locale_ = ORIGINAL;
  47. LOG(INFO) << "Initialising locales...";
  48. BinaryData locale_offset_data(4);
  49. dat->GetIO().ReadData(locale_offset_data, 4, 300);
  50. long long locale_offset = locale_offset_data.ToNumber<4>(0);
  51. if (locale_offset == 0 || locale_offset + 8 >= dat->GetIO().GetActualDatSize().value) {
  52. if (CheckLocaleCorrect()) {
  53. LOG(INFO) << "Dictionary offset is empty or incorrect. Passing.";
  54. return DatOperationResult<>();
  55. } else {
  56. return DatOperationResult<>(ERROR,
  57. "Locale dict is incorrect, through patched mark is standing. Dat file may be corrupted");
  58. }
  59. }
  60. BinaryData locale_info(16);
  61. dat->GetIO().ReadData(locale_info, 16, locale_offset);
  62. long long dict_size = locale_info.ToNumber<4>(0);
  63. long long dict_version = locale_info.ToNumber<4>(4);
  64. long long patched_file_end = locale_info.ToNumber<4>(8);
  65. long long dat_file_header_hash = locale_info.ToNumber<4>(12);
  66. if (dict_version != DAT_LOCALE_DICT_VERSION) {
  67. dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
  68. if (CheckLocaleCorrect())
  69. return DatOperationResult<>(SUCCESS);
  70. else
  71. return DatOperationResult<>(ERROR,
  72. "Version of locales' dictionary is incorrect, through patched mark is standing. Dat file may be corrupted");
  73. }
  74. if (dat_file_header_hash != dat->GetIO().GetHeaderHash()) {
  75. LOG(INFO) << "Header hash, written after last patch apply doesn't match current hash. Seems like dat-file was modified somewhere else, so removing all patch data";
  76. dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
  77. if (CheckLocaleCorrect())
  78. return DatOperationResult<>(SUCCESS);
  79. else
  80. return DatOperationResult<>(ERROR,
  81. "Version of locales' dictionary is incorrect, through patched mark is standing. Dat file may be corrupted");
  82. }
  83. BinaryData dicts_data = BinaryData((unsigned) dict_size);
  84. dat->GetIO().ReadData(dicts_data, dict_size - 16, locale_offset + 16);
  85. if (dicts_data.size() < 15) {
  86. dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
  87. return DatOperationResult<>(ERROR, "INITLOCALE: Data in locales' dictionary is incorrect.");
  88. }
  89. BinaryData hi_data = dicts_data.CutData(0, 15) + BinaryData("\0", 1);
  90. std::string hi = std::string((char *) (hi_data.data()));
  91. if (hi != "Hi from Gi1dor!")
  92. return DatOperationResult<>(ERROR,
  93. "INITLOCALE: Data in locales' dictionary is incorrect (couldn't receive 'Hello').");
  94. int offset = 15;
  95. BinaryData current_locale_data = dicts_data.CutData(offset, offset + 4) + BinaryData("\0", 1);
  96. std::string locale((char *) (current_locale_data.data()));
  97. offset += 4;
  98. if (locale != "PATC" && locale != "ORIG")
  99. return DatOperationResult<>(ERROR,
  100. "INITLOCALE: Data in locales' dictionary is incorrect (current locale mark is invalid).");
  101. current_locale_ = (locale == "PATC" ? PATCHED : ORIGINAL);
  102. size_t orig_dict_size = size_t(dicts_data.CutData(offset, offset + 4).ToNumber<4>(0));
  103. offset += 4;
  104. for (size_t i = 0; i < orig_dict_size; i++) {
  105. auto file = SubFile(*dat, dicts_data.CutData(offset, offset + 32));
  106. file.category = dicts_data.ToNumber<4>(offset + 32);
  107. orig_dict_[file.file_id()] = file;
  108. offset += 36;
  109. }
  110. size_t patch_dict_size = size_t(dicts_data.CutData(offset, offset + 4).ToNumber<4>(0));
  111. offset += 4;
  112. for (size_t i = 0; i < patch_dict_size; i++) {
  113. auto file = SubFile(*dat, dicts_data.CutData(offset, offset + 32));
  114. file.category = dicts_data.ToNumber<4>(offset + 32);
  115. patch_dict_[file.file_id()] = file;
  116. offset += 36;
  117. }
  118. size_t inactive_categories_set_size = size_t(dicts_data.ToNumber<4>(offset));
  119. offset += 4;
  120. for (size_t i = 0; i < inactive_categories_set_size; i++) {
  121. size_t category_id = size_t(dicts_data.ToNumber<4>(offset));
  122. inactive_categories.insert(category_id);
  123. offset += 4;
  124. }
  125. LOG(INFO) << "There are " << patch_dict_.size() << " files in patch locale dictionary";
  126. LOG(INFO) << "There are " << orig_dict_.size() << " files in original locale dictionary";
  127. LOG(INFO) << "There are " << inactive_categories.size() << " categories inactive: ";
  128. LOG(INFO) << "Finished initialising locales";
  129. if (CheckLocaleCorrect()) {
  130. dat->GetFileSystem().patched_file_end = patched_file_end;
  131. LOG(INFO) << "Locales initialisation success. Dictionary size is " << dict_size << ". Version is "
  132. << dict_version << ". Localed .dat size = " << patched_file_end;
  133. return DatOperationResult<>(SUCCESS);
  134. } else {
  135. orig_dict_.clear();
  136. patch_dict_.clear();
  137. inactive_categories.clear();
  138. current_locale_ = ORIGINAL;
  139. return DatOperationResult<>(ERROR,
  140. "Locale dict is incorrect, through patched mark is standing. Dat file may be corrupted");
  141. }
  142. }
  143. /*!
  144. * \Author Gi1dor
  145. * \date 06.07.2018
  146. * Смена активной локали. Меняет информацию о файлах в файловой системе на соответствующую информацию в локали.
  147. * \param[in] locale Устанавливаемая локаль
  148. * \warning Изменения в файловую систему вносятся локально! В dat файл изменения будут записаны во время деинициализации модуля файловой системы
  149. */
  150. DatOperationResult<> DatLocaleManager::SetLocale(DatLocaleManager::LOCALE locale) {
  151. if (dat->GetStatusModule().GetStatus() == DatStatus::E_FREE)
  152. dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
  153. dat->GetStatusModule().SetDebugMessage("Changing locale to " +
  154. (locale == PATCHED ? std::string(" patched version")
  155. : std::string(" original version")));
  156. LOG(INFO) << "Setting locale to " << (locale == PATCHED ? " PATCHED" : " ORIGINAL");
  157. if (!dat)
  158. return DatOperationResult<>(ERROR, "SETLOCALE: no connection with Dat (dat is nullptr)");
  159. if (current_locale_ == locale) {
  160. LOG(INFO) << "Locale is already " << locale << ", nothing to do.";
  161. if (dat->GetStatusModule().GetStatus() == DatStatus::E_COMMITING)
  162. dat->GetStatusModule().SetDefaultStatus();
  163. return DatOperationResult<>(SUCCESS);
  164. }
  165. std::map<long long, SubFile> &dict = GetLocaleDictReference(locale);
  166. size_t files_total = dict.size();
  167. size_t files_proceeded = 0;
  168. dat->GetStatusModule().SetTotalParts(files_total);
  169. for (const auto &file : dict) {
  170. ++files_proceeded;
  171. dat->GetStatusModule().SetFinishedParts(files_proceeded);
  172. if (CategoryIsInactive(file.second.category) && locale == PATCHED)
  173. continue;
  174. long long file_id = file.first;
  175. auto dict_file_result = dat->GetFileSystem().GetFile(file_id);
  176. if (dict_file_result.result != SUCCESS) {
  177. LOG(WARNING) << "Unable to get file with id = " << file_id << "from datFileSystem!";
  178. dict.erase(file_id);
  179. continue;
  180. }
  181. std::shared_ptr<SubFile> dict_file = dict_file_result.value;
  182. if (dict_file->MakeHeaderData().CutData(8, 16) == file.second.MakeHeaderData().CutData(8, 16))
  183. continue;
  184. dat->GetFileSystem().UpdateFileInfo(file.second);
  185. }
  186. current_locale_ = locale;
  187. if (dat->GetStatusModule().GetStatus() == DatStatus::E_COMMITING)
  188. dat->GetStatusModule().SetDefaultStatus();
  189. return DatOperationResult<>(SUCCESS);
  190. }
  191. /*!
  192. * \Author Gi1dor
  193. * \date 06.07.2018
  194. * Получение текущей активной локали. Если локали не существуют, понимается, что используется оригинальная (ORIGINAL)
  195. */
  196. DatLocaleManager::LOCALE DatLocaleManager::GetCurrentLocale() {
  197. return current_locale_;
  198. }
  199. /*!
  200. * \Author Gi1dor
  201. * \date 06.07.2018
  202. * Деинициализация модуля. Должна происходить перед деинициализацией модулей ввода-вывода и файловой системы.
  203. */
  204. DatOperationResult<> DatLocaleManager::DeInit() {
  205. LOG(INFO) << "Committing locales...";
  206. if (!dat)
  207. return DatOperationResult<>(ERROR, "LOCALEDEINIT: no connection with Dat (dat is nullptr)");
  208. auto result = CommitLocales();
  209. ClearData();
  210. return result;
  211. }
  212. /*!
  213. * \Author Gi1dor
  214. * \date 06.07.2018
  215. * Обновление данных файла в локали
  216. * \param[in] locale Локаль, для которой файл данных будет обновлён
  217. * \param[in] file Новые данные файла. Старые данные будут перезаписаны
  218. */
  219. void DatLocaleManager::UpdateLocaleFile(DatLocaleManager::LOCALE locale, const SubFile &file) {
  220. std::map<long long, SubFile> &dict = GetLocaleDictReference(locale);
  221. dict[file.file_id()] = file;
  222. }
  223. /*!
  224. * \Author Gi1dor
  225. * \date 06.07.2018
  226. * Получение данных файла в локали. Вернёт DatOperationResult.result = ERROR, если файла с таким id не существует.
  227. * \param[in] file_id id файла, для которого необходимо получить данных
  228. * \param[in] locale Локаль, данные которой будут получены
  229. */
  230. DatOperationResult<SubFile> DatLocaleManager::GetLocaleFile(long long file_id, DatLocaleManager::LOCALE locale) {
  231. std::map<long long, SubFile> &dict = GetLocaleDictReference(locale);
  232. if (dict.count(file_id) == 0)
  233. return DatOperationResult<SubFile>(SubFile(), ERROR,
  234. "GETLOCFILE: cannot get file with id = " + std::to_string(file_id) +
  235. " from dict " + std::to_string(locale));
  236. return DatOperationResult<SubFile>(dict[file_id], SUCCESS);
  237. }
  238. /*!
  239. * \Author Gi1dor
  240. * \date 06.07.2018
  241. * Получение словаря файлов локали
  242. * \param[in] locale Локаль, данные которой будут получены
  243. */
  244. std::map<long long, SubFile> &DatLocaleManager::GetLocaleDictReference(DatLocaleManager::LOCALE locale) {
  245. return locale == PATCHED ? patch_dict_ : orig_dict_;
  246. }
  247. /*!
  248. * \Author Gi1dor
  249. * \date 06.07.2018
  250. * Выводит информацию о состоянии модуля в файл
  251. * \param[in] file указатель на объект файла, в конец которого будет напечатана информация
  252. * \warning Файл file должен быть открыт для записи. После завершения работы функции файл остаётся открытым
  253. */
  254. void DatLocaleManager::PrintInformaion(FILE *file) {
  255. fprintf(file, "========= Locales info ========\n");
  256. BinaryData locale_offset_data(4);
  257. dat->GetIO().ReadData(locale_offset_data, 4, 300);
  258. long long locale_offset = locale_offset_data.ToNumber<4>(0);
  259. fprintf(file, "Locales' dictionary offset = %lld\n", locale_offset);
  260. BinaryData locale_status_data(4);
  261. dat->GetIO().ReadData(locale_status_data, 4, 296);
  262. long long locale_status = locale_offset_data.ToNumber<4>(0);
  263. fprintf(file, "Locale status = %lld\n", locale_status);
  264. if (locale_offset != 0) {
  265. BinaryData locale_info(16);
  266. dat->GetIO().ReadData(locale_info, 16, locale_offset);
  267. long long dict_size = locale_info.ToNumber<4>(0);
  268. long long dict_version = locale_info.ToNumber<4>(4);
  269. fprintf(file, "Locales' dictionary size = %lld, version = %lld\n", dict_size, dict_version);
  270. }
  271. fprintf(file, "Current locale id = %d\n", current_locale_);
  272. fprintf(file, "Patch dictionary size = %d\n", patch_dict_.size());
  273. fprintf(file, "Original dictionary size = %d\n", orig_dict_.size());
  274. }
  275. /*!
  276. * \Author Gi1dor
  277. * \date 06.07.2018
  278. * Осуществляет проверку корректности dat файла с позиции локалей
  279. * Файл считается некорректным, если активной является альтернативная локаль, причём нет возможности вернуть оригинальную (блок локалей неверный/повреждён)
  280. * Байты 0x128-0x12C равны 0, если на момент пользования патчером активна оригинальная локаль, и DatIO::file_size, если нет
  281. * Отличие значения в 0x128 от 0 и значения в 0x148 => файл ресурсов мог быть повреждён
  282. */
  283. bool DatLocaleManager::CheckLocaleCorrect() {
  284. BinaryData locale_info(4);
  285. dat->GetIO().ReadData(locale_info, 4, 296);
  286. long long locale_status = locale_info.ToNumber<4>(0);
  287. BinaryData locale_offset_data(4);
  288. dat->GetIO().ReadData(locale_offset_data, 4, 300);
  289. long long locale_offset = locale_offset_data.ToNumber<4>(0);
  290. if (locale_offset == 0 || locale_offset + 8 >= dat->GetIO().GetActualDatSize().value)
  291. return locale_status == 0;
  292. BinaryData dicts_data = BinaryData(4);
  293. auto operation = dat->GetIO().ReadData(dicts_data, 4, locale_offset + 16 + 15);
  294. if (operation.result == ERROR)
  295. return locale_status == 0;
  296. BinaryData locale_data = dicts_data + BinaryData("\0", 1);
  297. std::string locale((char *) (locale_data.data()));
  298. LOCALE dat_locale = (locale == "PATC" ? PATCHED : ORIGINAL);
  299. if ((locale_status == 0 && dat_locale == PATCHED) || (locale_status != 0 && dat_locale == ORIGINAL)) {
  300. LOG(ERROR) << "CHCKLOCALECORRECT: Locale status and current_locale doesn't match!";
  301. return false;
  302. }
  303. if (locale_status != 0 && locale_status != dat->GetIO().GetHeaderHash()){
  304. LOG(ERROR) << "CHCKLOCALECORRECT: Locale hash doesn't match!";
  305. return false;
  306. }
  307. return true;
  308. }
  309. /*!
  310. * \Author Gi1dor
  311. * \date 11.07.2018
  312. * Проверка активности категории
  313. * \returns true, если категория активна и false - если нет
  314. */
  315. bool DatLocaleManager::CategoryIsInactive(long long category) {
  316. return inactive_categories.count(category) > 0;
  317. }
  318. /*!
  319. * \Author Gi1dor
  320. * \date 11.07.2018
  321. * Обновляет категорию у файлов в словарях локалей
  322. */
  323. void DatLocaleManager::UpdateCategory(long long file_id, long long category) {
  324. if (orig_dict_.count(file_id))
  325. orig_dict_[file_id].category = category;
  326. if (patch_dict_.count(file_id))
  327. patch_dict_[file_id].category = category;
  328. }
  329. /*!
  330. * \Author Gi1dor
  331. * \date 21.10.2018
  332. * Возвращает словарь, состоящий из id категорий, помеченных как неактивные
  333. */
  334. const std::set<long long> &DatLocaleManager::GetInactiveCategories() {
  335. return inactive_categories;
  336. }
  337. /*!
  338. * \Author Gi1dor
  339. * \date 21.10.2018
  340. * Записывает словарь. Если ранее словарь не существовал - новый будет записан в конец файла.
  341. * Для словаря выделяется блок не менее 20мб
  342. *
  343. * \warning Не должна вызываться вручную! Автоматически вызывается в функции Deinitialise класса DatFile
  344. *
  345. * Структура словарей локализации:
  346. * ======== LOCALE DICT STRUCTURE =========
  347. * 4 bytes for dict size (in bytes)
  348. * 4 bytes for locale version
  349. * 4 bytes for .dat file size (with patches)
  350. * 4 bytes for dat file header hash
  351. * 15 bytes for "Hi from Gi1dor"
  352. * 4 bytes for LOCALE
  353. * 4 bytes for orig_dict.size()
  354. * (32 + 4) * orig_dict.size() bytes for orig_dict data
  355. * 4 bytes for patch_dict.size()
  356. * (32 + 4) * patch_dict.size() bytes for patch_dict data
  357. * 4 bytes for inactive_categories dict
  358. * 4 * inactive_categories.size() bytes for inactive_categories data
  359. * ========================================
  360. * Помимо этого:
  361. * 0x128-0x12C - 0, если выбрана локаль ORIGINAL и обновление клиентом игры не испортит .dat file
  362. * хэш хедера файла в противном случае.
  363. * Отличие значения в 0x128 от 0 и значения в 0x148 => файл ресурсов мог быть повреждён
  364. *
  365. * 0x12C-0x130 - Офсет начала словаря локализации
  366. */
  367. DatOperationResult<> DatLocaleManager::CommitLocales() {
  368. if (!dat->Initialized())
  369. return DatOperationResult<>(SUCCESS);
  370. if (patch_dict_.empty()) {
  371. dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
  372. dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
  373. return DatOperationResult<>(SUCCESS);
  374. }
  375. dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
  376. dat->GetStatusModule().SetTotalParts(orig_dict_.size() + patch_dict_.size());
  377. dat->GetStatusModule().SetFinishedParts(0);
  378. BinaryData binary_data = BinaryData(4 + 4 + 4 + 4 + 14 + 15 + 4
  379. + 4 + (32 + 4) * orig_dict_.size()
  380. + 4 + (32 + 4) * patch_dict_.size()
  381. + 4 + 4 * inactive_categories.size());
  382. // First 16 bytes will be filled just before writing data to file
  383. size_t current_size = 16;
  384. binary_data.Append(BinaryData("Hi from Gi1dor!", 15), current_size);
  385. current_size += 15;
  386. binary_data.Append(BinaryData((current_locale_ == ORIGINAL ? "ORIG" : "PATC"), 4), current_size);
  387. current_size += 4;
  388. binary_data.Append(BinaryData::FromNumber<4>(orig_dict_.size()), current_size);
  389. current_size += 4;
  390. unsigned long long processed_files = 0;
  391. for (const auto &file : orig_dict_) {
  392. binary_data.Append(file.second.MakeHeaderData(), current_size);
  393. current_size += 32;
  394. binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size);
  395. current_size += 4;
  396. dat->GetStatusModule().SetFinishedParts(++processed_files);
  397. }
  398. binary_data.Append(BinaryData::FromNumber<4>(patch_dict_.size()), current_size);
  399. current_size += 4;
  400. for (const auto &file : patch_dict_) {
  401. binary_data.Append(file.second.MakeHeaderData(), current_size);
  402. current_size += 32;
  403. binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size);
  404. current_size += 4;
  405. dat->GetStatusModule().SetFinishedParts(++processed_files);
  406. }
  407. binary_data.Append(BinaryData::FromNumber<4>(inactive_categories.size()), current_size);
  408. current_size += 4;
  409. for (auto patch_id : inactive_categories) {
  410. binary_data.Append(BinaryData::FromNumber<4>(patch_id), current_size);
  411. current_size += 4;
  412. }
  413. BinaryData dicts_data(4);
  414. dat->GetIO().ReadData(dicts_data, 4, 300);
  415. long long dict_offset = dicts_data.ToNumber<4>(0);
  416. dat->GetIO().ReadData(dicts_data, 4, dict_offset);
  417. long long dict_size = dicts_data.ToNumber<4>(0);
  418. if (binary_data.size() > dict_size || dict_offset == 0) {
  419. long long new_dict_offset = dat->GetFileSystem().patched_file_end + 16;
  420. // Updating first 16 bytes
  421. binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)), 0);
  422. binary_data.Append(BinaryData::FromNumber<4>(DAT_LOCALE_DICT_VERSION), 4);
  423. binary_data.Append(
  424. BinaryData::FromNumber<4>(dat->GetFileSystem().patched_file_end + binary_data.size() + 20 * 1024 * 1024), 8);
  425. binary_data.Append(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 12);
  426. auto operation = dat->GetIO().WriteData(binary_data, binary_data.size(), new_dict_offset);
  427. if (operation.result != SUCCESS) {
  428. return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales");
  429. }
  430. dat->GetIO().WriteData(BinaryData::FromNumber<4>(new_dict_offset), 4, 300);
  431. if (current_locale_ == PATCHED) {
  432. dat->GetIO().WriteData(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 4, 296);
  433. } else {
  434. dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
  435. }
  436. dat->GetFileSystem().patched_file_end += binary_data.size();
  437. LOG(INFO) << "Writing 20 mbytes to " << dat->GetFileSystem().patched_file_end;
  438. BinaryData nulls(unsigned(20 * 1024 * 1024));
  439. dat->GetIO().WriteData(nulls, nulls.size(), dat->GetFileSystem().patched_file_end);
  440. dat->GetFileSystem().patched_file_end += nulls.size();
  441. } else {
  442. binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)), 0);
  443. binary_data.Append(BinaryData::FromNumber<4>(DAT_LOCALE_DICT_VERSION), 4);
  444. binary_data.Append(BinaryData::FromNumber<4>(dat->GetFileSystem().patched_file_end), 8);
  445. binary_data.Append(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 12);
  446. if (current_locale_ == PATCHED) {
  447. dat->GetIO().WriteData(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 4, 296);
  448. } else {
  449. dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
  450. }
  451. auto operation = dat->GetIO().WriteData(binary_data, binary_data.size(), dict_offset);
  452. if (operation.result != SUCCESS) {
  453. return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales. ERRMSG: " + operation.msg);
  454. }
  455. }
  456. dat->GetStatusModule().SetDefaultStatus();
  457. LOG(INFO) << "Locales committed successfully";
  458. return DatOperationResult<>(SUCCESS);
  459. }
  460. void DatLocaleManager::ClearData() {
  461. orig_dict_.clear();
  462. patch_dict_.clear();
  463. inactive_categories.clear();
  464. current_locale_ = LOCALE(-1);
  465. }
  466. DatOperationResult<> DatLocaleManager::EnableCategory(long long category) {
  467. inactive_categories.erase(category);
  468. dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
  469. size_t files_count = patch_dict_.size();
  470. size_t files_processed = 0;
  471. dat->GetStatusModule().SetTotalParts(files_count);
  472. for (const auto& entry : patch_dict_) {
  473. SubFile file = entry.second;
  474. ++files_processed;
  475. dat->GetStatusModule().SetFinishedParts(files_processed);
  476. if (file.category == category) {
  477. dat->GetFileSystem().UpdateFileInfo(file);
  478. }
  479. }
  480. dat->GetStatusModule().SetDefaultStatus();
  481. return DatOperationResult<>(SUCCESS);
  482. }
  483. DatOperationResult<> DatLocaleManager::DisableCategory(long long category) {
  484. inactive_categories.insert(category);
  485. dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
  486. dat->GetStatusModule().SetFinishedParts(0);
  487. size_t files_count = orig_dict_.size();
  488. size_t files_processed = 0;
  489. dat->GetStatusModule().SetTotalParts(files_count);
  490. for (const auto& entry : orig_dict_) {
  491. SubFile file = entry.second;
  492. ++files_processed;
  493. dat->GetStatusModule().SetFinishedParts(files_processed);
  494. if (file.category == category) {
  495. dat->GetFileSystem().UpdateFileInfo(file);
  496. }
  497. }
  498. dat->GetStatusModule().SetDefaultStatus();
  499. return DatOperationResult<>(SUCCESS);
  500. }
  501. }