// // Created by kikab on 04.06.2018. // #include #include #include #include namespace LOTRO_DAT { DatLocaleManager::DatLocaleManager(DatFile *datFilePtr) : dat(datFilePtr), current_locale_(ORIGINAL) { } DatOperationResult<> DatLocaleManager::Init() { if (!dat) return DatOperationResult<>(ERROR, "LOCALEINIT: no connection with Dat (dat is nullptr)"); orig_dict_.clear(); patch_dict_.clear(); LOG(INFO) << "Initialising locales..."; BinaryData locale_offset_data(4); dat->getIO().ReadData(locale_offset_data, 4, 300); long long locale_offset = locale_offset_data.ToNumber<4>(0); if (locale_offset == 0 || locale_offset + 8 >= dat->getIO().GetActualDatSize()) { LOG(INFO) << "Dictionary offset is empty or incorrect. Passing."; return DatOperationResult<>(); } BinaryData locale_info(12); dat->getIO().ReadData(locale_info, 12, locale_offset); long long dict_size = locale_info.ToNumber<4>(0); long long dict_version = locale_info.ToNumber<4>(4); dat->getIO().file_size = locale_info.ToNumber<4>(8); LOG(INFO) << "Dictionary size is " << dict_size << ". Version is " << dict_version << ". Localed .dat size = " << dat->getIO().file_size; if (dict_version != 101) { LOG(WARNING) << "DICTIONARY IS OLD! Version = " << dict_version; dat->getIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300); return DatOperationResult<>(ERROR, "Version of locales' dictionary is old (ver = " + std::to_string(dict_version) + "), cannot work with this type of .dat file."); } BinaryData dicts_data = BinaryData((unsigned)dict_size); dat->getIO().ReadData(dicts_data, dict_size, locale_offset + 12); if (dicts_data.size() < 15) { dat->getIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300); return DatOperationResult<>(ERROR, "INITLOCALE: Data in locales' dictionary is incorrect."); } BinaryData hi_data = dicts_data.CutData(0, 15) + BinaryData("\0", 1); std::string hi = std::string((char *) (hi_data.data())); if (hi != "Hi from Gi1dor!") return DatOperationResult<>(ERROR, "INITLOCALE: Data in locales' dictionary is incorrect (couldn't receive 'Hello')."); int offset = 15; BinaryData current_locale_data = dicts_data.CutData(offset, offset + 4) + BinaryData("\0", 1); std::string locale((char *) (current_locale_data.data())); offset += 4; if (locale != "PATC" && locale != "ORIG") return DatOperationResult<>(ERROR, "INITLOCALE: Data in locales' dictionary is incorrect (current locale mark is invalid)."); current_locale_ = (locale == "PATC" ? PATCHED : ORIGINAL); size_t orig_dict_size = size_t(dicts_data.CutData(offset, offset + 4).ToNumber<4>(0)); offset += 4; for (size_t i = 0; i < orig_dict_size; i++) { auto file = SubFile(*dat, dicts_data.CutData(offset, offset + 32)); file.category = dicts_data.ToNumber<4>(offset); orig_dict_[file.file_id()] = file; offset += 36; } size_t patch_dict_size = size_t(dicts_data.CutData(offset, offset + 4).ToNumber<4>(0)); offset += 4; for (size_t i = 0; i < patch_dict_size; i++) { auto file = SubFile(*dat, dicts_data.CutData(offset, offset + 32)); file.category = dicts_data.ToNumber<4>(offset); orig_dict_[file.file_id()] = file; offset += 36; } LOG(INFO) << "There are " << patch_dict_.size() << " files in patch locale dictionary"; LOG(INFO) << "There are " << orig_dict_.size() << " files in original locale dictionary"; LOG(INFO) << "Finished initialising locales"; return DatOperationResult<>(SUCCESS); } DatOperationResult<> DatLocaleManager::SetLocale(DatLocaleManager::LOCALE locale) { LOG(INFO) << "Setting locale to " << (locale == PATCHED ? " PATCHED" : " ORIGINAL"); if (!dat) return DatOperationResult<>(ERROR, "SETLOCALE: no connection with Dat (dat is nullptr)"); if (current_locale_ == locale) { LOG(INFO) << "Locale is already " << locale << ", nothing to do."; return DatOperationResult<>(SUCCESS); } auto dict = GetLocaleDictReference(locale); for (const auto &file : dict) { long long file_id = file.first; auto dict_file_result = dat->getFileSystem().GetFile(file_id); if (dict_file_result.result != SUCCESS) { LOG(WARNING) << "Unable to get file with id = " << file_id << "from datFileSystem!"; dict.erase(file_id); continue; } std::shared_ptr dict_file = dict_file_result.value; if (dict_file->MakeHeaderData().CutData(8, 16) == file.second.MakeHeaderData().CutData(8, 16)) continue; dat->getFileSystem().UpdateFileInfo(file.second); } current_locale_ = locale; return DatOperationResult<>(SUCCESS); } DatLocaleManager::LOCALE DatLocaleManager::GetCurrentLocale() { return current_locale_; } DatOperationResult<> DatLocaleManager::DeInit() { LOG(INFO) << "Committing locales..."; if (!dat) return DatOperationResult<>(ERROR, "LOCALEDEINIT: no connection with Dat (dat is nullptr)"); BinaryData binary_data = BinaryData(4 + 4 + 4 + 14 + 15 + 4 + 4 + (32 + 4) * orig_dict_.size() + 4 + (32 + 4) * patch_dict_.size() + 4 + 4 * inactive_categories.size()); size_t current_size = 0; binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u))); binary_data.Append(BinaryData::FromNumber<4>(101)); binary_data.Append(BinaryData::FromNumber<4>(dat->getIO().file_size + binary_data.size() + 12 + 20 * 1024 * 1024)); current_size = 12; binary_data.Append(BinaryData("Hi from Gi1dor!", 15), current_size); current_size += 15; binary_data.Append(BinaryData((current_locale_ == ORIGINAL ? "ORIG" : "PATC"), 4), current_size); current_size += 4; binary_data.Append(BinaryData::FromNumber<4>(orig_dict_.size()), current_size); current_size += 4; for (const auto &file : orig_dict_) { binary_data.Append(file.second.MakeHeaderData(), current_size); current_size += 32; binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size); current_size += 4; } binary_data.Append(BinaryData::FromNumber<4>(patch_dict_.size()), current_size); current_size += 4; for (const auto &file : patch_dict_) { binary_data.Append(file.second.MakeHeaderData(), current_size); current_size += 32; binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size); current_size += 4; } binary_data.Append(BinaryData::FromNumber<4>(inactive_categories.size()), current_size); current_size += 4; for (auto patch_id : inactive_categories) { binary_data.Append(BinaryData::FromNumber<4>(patch_id), current_size); current_size += 4; } BinaryData dicts_data(4); dat->getIO().ReadData(dicts_data, 4, 300); long long dict_offset = dicts_data.ToNumber<4>(0); dat->getIO().ReadData(dicts_data, 4, dict_offset); long long dict_size = dicts_data.ToNumber<4>(0); if (binary_data.size() > dict_size || dict_offset == 0) { auto operation = dat->getIO().WriteData(binary_data, binary_data.size(), dat->getIO().file_size + 12); if (operation.result != SUCCESS) return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales. ERRMSG: " + operation.msg); dat->getIO().WriteData(BinaryData::FromNumber<4>(dat->getIO().file_size), 4, 300); dat->getIO().file_size += binary_data.size(); // Adding space for 20 megabytes locales file in total. BinaryData nulls(unsigned(20 * 1024 * 1024)); dat->getIO().WriteData(nulls, nulls.size(), dat->getIO().file_size); dat->getIO().file_size += nulls.size(); } else { auto operation = dat->getIO().WriteData(binary_data, binary_data.size(), dict_offset); if (operation.result != SUCCESS) return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales. ERRMSG: " + operation.msg); } LOG(INFO) << "Locales commited successfully"; return DatOperationResult<>(SUCCESS); } void DatLocaleManager::UpdateLocaleFile(DatLocaleManager::LOCALE locale, const SubFile &file) { auto dict = GetLocaleDictReference(locale); dict[file.file_id()] = file; } DatOperationResult DatLocaleManager::GetLocaleFile(long long file_id, DatLocaleManager::LOCALE locale) { auto dict = GetLocaleDictReference(locale); if (dict.count(file_id) != 0) return DatOperationResult(SubFile(), ERROR, "GETLOCFILE: cannot get file with id = " + std::to_string(file_id) + " from dict " + std::to_string(locale)); return DatOperationResult(dict[file_id], SUCCESS); } std::map &DatLocaleManager::GetLocaleDictReference(DatLocaleManager::LOCALE locale) { return locale == PATCHED ? patch_dict_ : orig_dict_; } void DatLocaleManager::PrintInformaion(FILE *file) { fprintf(file, "========= Locales info ========\n"); BinaryData locale_offset_data(4); dat->getIO().ReadData(locale_offset_data, 4, 300); long long locale_offset = locale_offset_data.ToNumber<4>(0); fprintf(file, "Locales' dictionary offset = %lld\n", locale_offset); if (locale_offset != 0) { BinaryData locale_info(12); dat->getIO().ReadData(locale_info, 12, locale_offset); long long dict_size = locale_info.ToNumber<4>(0); long long dict_version = locale_info.ToNumber<4>(4); fprintf(file, "Locales' dictionary size = %lld, version = %lld\n", dict_size, dict_version); dat->getIO().file_size = locale_info.ToNumber<4>(8); } fprintf(file, "Current locale id = %d\n", current_locale_); fprintf(file, "Patch dictionary size = %d\n", patch_dict_.size()); fprintf(file, "Original dictionary size = %d\n", orig_dict_.size()); } }