DatPatcher.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. #include <DatSubsystems/DatPatcher.h>
  2. #include <SubfileData.h>
  3. #include <DatFile.h>
  4. #include <yaml-cpp/yaml.h>
  5. namespace LOTRO_DAT {
  6. DatPatcher::DatPatcher(DatFile *datFilePtr) : dat(datFilePtr) {
  7. }
  8. /*!
  9. * \Author Gi1dor
  10. * \date 07.07.2018
  11. * Обновление файла. Записывает данные файла в dat контейнер, используя информацию в SubfileData. Если ранее файл не существовал - он не будет создан (вернёт ошибку).
  12. * \warning В процессе применения локаль будет автоматически переключена в положение PATCHED
  13. * \param[in] data Новые данные файла
  14. * \param[in] single_file Флаг, который означает, что применяемый патч состоит из одного файла.
  15. * Если true, то функция будет управлять состоянием DatStatus и обновлять его.
  16. * Иначе эта обязанность делегируется вызывающей функции
  17. */
  18. DatOperationResult<> DatPatcher::PatchFile(const SubfileData &data, bool single_file) {
  19. if (!data.options["did"]) {
  20. // Subfile data does not contain any Dat ID number, so skipping it
  21. return DatOperationResult<>();
  22. }
  23. auto dat_id = data.options["did"].as<long long>();
  24. if (dat_id != dat->GetDatID()) {
  25. // Subfile Dat ID does not match host DAT ID, so we shouldn't patch it
  26. return DatOperationResult<>();
  27. }
  28. auto file_id = data.options["fid"].as<long long>();
  29. if (single_file) {
  30. dat->GetStatusModule().SetStatus(DatStatus::E_PATCHING);
  31. dat->GetStatusModule().SetFinishedParts(0);
  32. dat->GetStatusModule().SetTotalParts(1);
  33. }
  34. dat->GetStatusModule().SetDebugMessage("Patching file with id " + std::to_string(file_id));
  35. auto getfile_operation = dat->GetFileSystem().GetFile(file_id);
  36. if (getfile_operation.result == ERROR) {
  37. if (single_file) {
  38. dat->GetStatusModule().SetDefaultStatus();
  39. }
  40. return DatOperationResult<>(ERROR,
  41. "PATCHSUBFILEDATA: Unable to find file with id " + std::to_string(file_id));
  42. }
  43. auto &file = getfile_operation.value;
  44. // If file has inactive category, then we should set it to patched state in order to commit patch and
  45. // then in ApplyFilePatch() function, if new category is still inactive, return dictionary to its original state;
  46. if (dat->GetLocaleManager().CategoryIsInactive(file->category) != 0) {
  47. auto operation = dat->GetLocaleManager().GetLocaleFile(file->file_id(), DatLocaleManager::PATCHED);
  48. if (operation.result == SUCCESS)
  49. dat->GetFileSystem().UpdateFileInfo(operation.value);
  50. }
  51. if (data.options["cat"].IsDefined())
  52. file->category = data.options["cat"].as<long long>();
  53. else
  54. file->category = 1;
  55. auto getdata_operation = dat->GetFileSystem().GetFileData(*file, 0);
  56. if (getdata_operation.result == ERROR) {
  57. if (single_file) {
  58. dat->GetStatusModule().SetDefaultStatus();
  59. }
  60. return DatOperationResult<>(ERROR,
  61. "PATCHSUBFILEDATA: can't get file data for id = " + std::to_string(file_id));
  62. }
  63. auto &file_data = getdata_operation.value;
  64. BinaryData patch_data = file->MakeForImport(file_data, data);
  65. auto result = ApplyFilePatch(file, patch_data);
  66. if (single_file) {
  67. dat->GetStatusModule().SetDefaultStatus();
  68. }
  69. if (result.result == ERROR) {
  70. return DatOperationResult<>(ERROR,
  71. "PATCHSUBFILEDATA: applyfilepatch failed for id = " + std::to_string(file_id));
  72. }
  73. return DatOperationResult<>(SUCCESS);
  74. }
  75. /*!
  76. * \Author Gi1dor
  77. * \date 07.07.2018
  78. * Обновление всех файлов в Database.
  79. * \warning В процессе применения локаль будет автоматически переключена в положение PATCHED
  80. * \param[in] db Указатель на базу данных. База должна быть проинициализирована
  81. */
  82. DatOperationResult<int> DatPatcher::PatchAllDatabase(Database *db) {
  83. if (!db)
  84. return DatOperationResult<int>(0, ERROR, "PATCHALLDATABASE: db is nullptr");
  85. dat->GetStatusModule().SetStatus(DatStatus::E_PATCHING);
  86. dat->GetStatusModule().SetFinishedParts(0);
  87. dat->GetStatusModule().SetDebugMessage("Patching database...");
  88. SubfileData data;
  89. data = db->GetNextFile();
  90. int successfully_patched = 0;
  91. unsigned db_rows = db->CountRows();
  92. dat->GetStatusModule().SetTotalParts(db_rows);
  93. for (unsigned i = 0; i < db_rows; ++i) {
  94. auto operation = PatchFile(data, false);
  95. if (operation.result == SUCCESS)
  96. successfully_patched++;
  97. dat->GetStatusModule().SetFinishedParts(i);
  98. data = db->GetNextFile();
  99. }
  100. LOG(INFO) << "Database import success: patched " + std::to_string(successfully_patched) + " out of " +
  101. std::to_string(db_rows) + " files";
  102. dat->GetStatusModule().SetDefaultStatus();
  103. return DatOperationResult<int>(successfully_patched, SUCCESS);
  104. }
  105. /*!
  106. * \Author Gi1dor
  107. * \date 07.07.2018
  108. * Функция, вызываемая функцией PatchFile, отвечающая за корректную запись файла в dat контейнер и обновление
  109. * информации о нём в файловой системе
  110. * \param[in] file Указатель на объект файла в файловой системе (полученный через DatFileSystem::GetFileInfo)
  111. * \param[in] data Бинарные данные собранного нового файла, готовые к записи в dat
  112. */
  113. DatOperationResult<> DatPatcher::ApplyFilePatch(std::shared_ptr<SubFile> file, BinaryData &data) {
  114. long long file_id = file->file_id();
  115. if (dat->GetLocaleManager().GetCurrentLocale() != DatLocaleManager::PATCHED) {
  116. LOG(INFO) << "Changing locale to PATCHED(RU) in order to patch file";
  117. dat->GetLocaleManager().SetLocale(DatLocaleManager::PATCHED);
  118. }
  119. if (dat->GetLocaleManager().CategoryIsInactive(file->category) &&
  120. dat->GetLocaleManager().GetLocaleFile(file_id, DatLocaleManager::PATCHED).result == SUCCESS)
  121. {
  122. dat->GetFileSystem().UpdateFileInfo(
  123. dat->GetLocaleManager().GetLocaleFile(file_id, DatLocaleManager::PATCHED).value);
  124. }
  125. //LOG(INFO) << "Patching file with id = " << file_id;
  126. if (dat->GetLocaleManager().GetLocaleFile(file_id, DatLocaleManager::ORIGINAL).result == ERROR)
  127. dat->GetLocaleManager().UpdateLocaleFile(DatLocaleManager::ORIGINAL, SubFile(*file));
  128. SubFile new_file = SubFile(*file);
  129. if (dat->GetLocaleManager().GetLocaleFile(file_id, DatLocaleManager::PATCHED).result == ERROR
  130. || data.size() > file->block_size()) {
  131. new_file.file_offset_ = dat->GetFileSystem().patched_file_end;
  132. new_file.block_size_ = std::max(data.size(), 256u);
  133. dat->GetFileSystem().patched_file_end += new_file.block_size_ + 8;
  134. }
  135. new_file.file_size_ = data.size() - 8;
  136. data.Append(BinaryData::FromNumber<4>(0), 0); // set additional fragments count to zero
  137. if (file_id != data.ToNumber<4>(8))
  138. LOG(WARNING) << "Created data's file_id " << file_id << "doesn't match to original: "
  139. << data.ToNumber<4>(8);
  140. auto operation = dat->GetIO().WriteData(data, data.size(), new_file.file_offset());
  141. if (operation.result == ERROR)
  142. return DatOperationResult<>(ERROR, "APPLYPATCHFILE: Unable to write data for file with id " +
  143. std::to_string(file_id));
  144. dat->GetFileSystem().UpdateFileInfo(new_file);
  145. dat->GetLocaleManager().UpdateLocaleFile(DatLocaleManager::PATCHED, new_file);
  146. // If file category is inactive, then return file header data in dictionary to original state
  147. dat->GetLocaleManager().UpdateCategory(file_id, new_file.category);
  148. if (dat->GetLocaleManager().CategoryIsInactive(new_file.category))
  149. dat->GetFileSystem().UpdateFileInfo(
  150. dat->GetLocaleManager().GetLocaleFile(file_id, DatLocaleManager::ORIGINAL).value);
  151. //LOG(INFO) << "Successfully patched file with id = " << file_id;
  152. return DatOperationResult<>(SUCCESS);
  153. }
  154. };