DatLocaleManager.cpp 30 KB

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