DatLocaleManager.cpp 29 KB

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