123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- #include <DatSubsystems/DatPatcher.h>
- #include <SubfileData.h>
- #include <DatFile.h>
- #include <yaml-cpp/yaml.h>
- namespace LOTRO_DAT {
- DatPatcher::DatPatcher(DatFile *datFilePtr) : dat(datFilePtr) {
- }
- /*!
- * \Author Gi1dor
- * \date 07.07.2018
- * Обновление файла. Записывает данные файла в dat контейнер, используя информацию в SubfileData. Если ранее файл не существовал - он не будет создан (вернёт ошибку).
- * \warning В процессе применения локаль будет автоматически переключена в положение PATCHED
- * \param[in] data Новые данные файла
- * \param[in] single_file Флаг, который означает, что применяемый патч состоит из одного файла.
- * Если true, то функция будет управлять состоянием DatStatus и обновлять его.
- * Иначе эта обязанность делегируется вызывающей функции
- */
- DatOperationResult<> DatPatcher::PatchFile(const SubfileData &data, bool single_file) {
- auto file_id = data.options["fid"].as<long long>();
- if (single_file) {
- dat->GetStatusModule().SetStatus(DatStatus::E_PATCHING);
- dat->GetStatusModule().SetPercentage(0);
- }
- dat->GetStatusModule().SetDebugMessage("Patching file with id " + std::to_string(file_id));
- auto getfile_operation = dat->GetFileSystem().GetFile(file_id);
- if (getfile_operation.result == ERROR) {
- if (single_file)
- dat->GetStatusModule().ClearAll();
- return DatOperationResult<>(ERROR,
- "PATCHSUBFILEDATA: Unable to find file with id " + std::to_string(file_id));
- }
- auto &file = getfile_operation.value;
- // If file has inactive category, then we should set it to patched state in order to commit patch and
- // then in ApplyFilePatch() function, if new category is still inactive, return dictionary to its original state;
- if (dat->GetLocaleManager().CategoryIsInactive(file->category) != 0) {
- auto operation = dat->GetLocaleManager().GetLocaleFile(file->file_id(), DatLocaleManager::PATCHED);
- if (operation.result == SUCCESS)
- dat->GetFileSystem().UpdateFileInfo(operation.value);
- }
- if (data.options["cat"].IsDefined())
- file->category = data.options["cat"].as<long long>();
- else
- file->category = 1;
- auto getdata_operation = dat->GetFileSystem().GetFileData(*file, 0);
- if (getdata_operation.result == ERROR) {
- if (single_file)
- dat->GetStatusModule().ClearAll();
- return DatOperationResult<>(ERROR,
- "PATCHSUBFILEDATA: can't get file data for id = " + std::to_string(file_id));
- }
- auto &file_data = getdata_operation.value;
- BinaryData patch_data = file->MakeForImport(file_data, data);
- auto result = ApplyFilePatch(file, patch_data);
- if (single_file)
- dat->GetStatusModule().ClearAll();
- if (result.result == ERROR)
- return DatOperationResult<>(ERROR,
- "PATCHSUBFILEDATA: applyfilepatch failed for id = " + std::to_string(file_id));
- return DatOperationResult<>(SUCCESS);
- }
- /*!
- * \Author Gi1dor
- * \date 07.07.2018
- * Обновление всех файлов в Database.
- * \warning В процессе применения локаль будет автоматически переключена в положение PATCHED
- * \param[in] db Указатель на базу данных. База должна быть проинициализирована
- */
- DatOperationResult<int> DatPatcher::PatchAllDatabase(Database *db) {
- if (!db)
- return DatOperationResult<int>(0, ERROR, "PATCHALLDATABASE: db is nullptr");
- dat->GetStatusModule().SetStatus(DatStatus::E_PATCHING);
- dat->GetStatusModule().SetPercentage(0);
- dat->GetStatusModule().SetDebugMessage("Patching database...");
- SubfileData data;
- data = db->GetNextFile();
- int successfully_patched = 0;
- unsigned db_rows = db->CountRows();
- for (unsigned i = 0; i < db_rows; ++i) {
- auto operation = PatchFile(data, false);
- if (operation.result == SUCCESS)
- successfully_patched++;
- dat->GetStatusModule().SetPercentage(i * 100 / db_rows);
- data = db->GetNextFile();
- }
- LOG(INFO) << "Database import success: patched " + std::to_string(successfully_patched) + " out of " +
- std::to_string(db_rows) + " files";
- dat->GetStatusModule().ClearAll();
- return DatOperationResult<int>(successfully_patched, SUCCESS);
- }
- /*!
- * \Author Gi1dor
- * \date 07.07.2018
- * Функция, вызываемая функцией PatchFile, отвечающая за корректную запись файла в dat контейнер и обновление
- * информации о нём в файловой системе
- * \param[in] file Указатель на объект файла в файловой системе (полученный через DatFileSystem::GetFileInfo)
- * \param[in] data Бинарные данные собранного нового файла, готовые к записи в dat
- */
- DatOperationResult<> DatPatcher::ApplyFilePatch(std::shared_ptr<SubFile> file, BinaryData &data) {
- long long file_id = file->file_id();
- if (dat->GetLocaleManager().GetCurrentLocale() != DatLocaleManager::PATCHED) {
- LOG(INFO) << "Changing locale to PATCHED(RU) in order to patch file";
- dat->GetLocaleManager().SetLocale(DatLocaleManager::PATCHED);
- }
- //LOG(INFO) << "Patching file with id = " << file_id;
- if (dat->GetLocaleManager().GetLocaleFile(file_id, DatLocaleManager::ORIGINAL).result == ERROR)
- dat->GetLocaleManager().UpdateLocaleFile(DatLocaleManager::ORIGINAL, SubFile(*file));
- SubFile new_file = SubFile(*file);
- if (dat->GetLocaleManager().GetLocaleFile(file_id, DatLocaleManager::PATCHED).result == ERROR
- || data.size() > file->block_size()) {
- new_file.file_offset_ = dat->GetIO().file_size;
- new_file.block_size_ = std::max(data.size(), 256u);
- dat->GetIO().file_size += new_file.block_size_ + 8;
- }
- new_file.file_size_ = data.size() - 8;
- data.Append(BinaryData::FromNumber<4>(0), 0); // set additional fragments count to zero
- if (file_id != data.ToNumber<4>(8))
- LOG(WARNING) << "Created data's file_id " << file_id << "doesn't match to original: "
- << data.ToNumber<4>(8);
- auto operation = dat->GetIO().WriteData(data, data.size(), new_file.file_offset());
- if (operation.result == ERROR)
- return DatOperationResult<>(ERROR, "APPLYPATCHFILE: Unable to write data for file with id " +
- std::to_string(file_id));
- dat->GetFileSystem().UpdateFileInfo(new_file);
- dat->GetLocaleManager().UpdateLocaleFile(DatLocaleManager::PATCHED, new_file);
- // If file category is inactive, then return file header data in dictionary to original state
- if (dat->GetLocaleManager().CategoryIsInactive(new_file.category))
- dat->GetFileSystem().UpdateFileInfo(
- dat->GetLocaleManager().GetLocaleFile(file_id, DatLocaleManager::ORIGINAL).value);
- dat->GetLocaleManager().UpdateCategory(file_id, new_file.category);
- //LOG(INFO) << "Successfully patched file with id = " << file_id;
- return DatOperationResult<>(SUCCESS);
- }
- };
|