// // Created by kikab on 04.06.2018. // #include #include #include #include #include #include namespace LOTRO_DAT{ DatFileSystem::DatFileSystem(DatFile *datFilePtr) : dat(datFilePtr) { LOG(INFO) << "Initialization of empty DatFileSystem"; } DatOperationResult DatFileSystem::GetFileData(long long file_id, long long int offset) { if (!dat) return DatOperationResult(BinaryData(), ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)"); if (dictionary_.count(file_id)) return DatOperationResult(BinaryData(), ERROR, "DatFileSystem error: no file found with id = " + std::to_string(file_id)); auto file = dictionary_[file_id]; LOG(DEBUG) << "Getting file " << file->file_id() << " data"; BinaryData mfile_id(20); auto operation = dat->getIO().ReadData(mfile_id, 20, file->file_offset() + 8); if (operation.result == ERROR) { LOG(ERROR) << "Error while reading file " << file->file_id() << " header (offset = " << file->file_offset() << "); Message: " << operation.msg << "Aborting."; return DatOperationResult(BinaryData(), ERROR, std::string("DatFileSystem error: Read ") + std::to_string(file_id) + " error." + operation.msg); } if (!mfile_id.CheckCompression() && file->file_id() != mfile_id.ToNumber<4>(0)) { LOG(ERROR) << "Bad DatFile::GetFileData() - file_id in SubFile (" << file->file_id() << ") doesn't match to file_id (" << mfile_id.ToNumber<4>(0) << ")in DatFile."; return DatOperationResult(BinaryData(), ERROR, std::string("DatFileSystem error: Read ") + std::to_string(file_id) + " error: SubFile value differs from value in dict"); } BinaryData data((unsigned)(file->file_size() + (8 - offset))); if (file->block_size() >= file->file_size() + 8) { dat->getIO().ReadData(data, file->file_size() + (8 - offset), file->file_offset() + offset); return DatOperationResult(data, SUCCESS); } BinaryData fragments_count(4); dat->getIO().ReadData(fragments_count, 4, file->file_offset()); long long fragments_number = fragments_count.ToNumber<4>(0); long long current_block_size = file->block_size() - offset - 8 * fragments_number; dat->getIO().ReadData(data, current_block_size, file->file_offset() + offset); BinaryData FragmentsDictionary(8 * unsigned(fragments_number)); dat->getIO().ReadData(FragmentsDictionary, 8 * unsigned(fragments_number), file->file_offset() + file->block_size() - 8 * fragments_number); for (long long i = 0; i < fragments_number; i++) { long long fragment_size = FragmentsDictionary.ToNumber<4>(8 * i); long long fragment_offset = FragmentsDictionary.ToNumber<4>(8 * i + 4); dat->getIO().ReadData(data, std::min(fragment_size, file->file_size() - current_block_size), fragment_offset, current_block_size); current_block_size += fragment_size; } LOG(DEBUG) << "Successfully got file " << file->file_id() << " data"; return DatOperationResult(data, SUCCESS); } DatOperationResult DatFileSystem::GetFile(long long file_id) { if (!dat) return DatOperationResult(SubFile(), ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)"); if (dictionary_.count(file_id) == 0) return DatOperationResult(SubFile(), ERROR, "No file with id = " + std::to_string(file_id) + " found in dictionary"); return DatOperationResult(*dictionary_[file_id].get(), SUCCESS); } DatOperationResult<> DatFileSystem::UpdateFile(const SubFile &file) { if (!dat) return DatOperationResult<>(ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)"); if (dictionary_.count(file.file_id()) == 0) return DatOperationResult<>(ERROR, "No file with id = " + std::to_string(file.file_id()) + " found in dictionary. Cannot update file"); *dictionary_[file.file_id()] = file; return DatOperationResult<>(SUCCESS); } DatOperationResult<> DatFileSystem::MakeDirectories() { LOG(DEBUG) << "Started making directories"; if (!dat) return DatOperationResult<>(ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)"); root_directory_ = std::make_shared((unsigned)dat->getIO().root_directory_offset, *dat); SubDirectory::subdir_init_queue_.insert(root_directory_); while (!SubDirectory::subdir_init_queue_.empty()) { std::shared_ptr dir = *SubDirectory::subdir_init_queue_.begin(); SubDirectory::subdir_init_queue_.erase(SubDirectory::subdir_init_queue_.begin()); if (dir->MakeSubDirectories()) SubDirectory::subfile_init_queue_.insert(dir); else dir->clear(); } while (!SubDirectory::subfile_init_queue_.empty()) { std::shared_ptr dir = *SubDirectory::subfile_init_queue_.begin(); SubDirectory::subfile_init_queue_.erase(SubDirectory::subfile_init_queue_.begin()); if (!dir->MakeSubFiles()) dir->clear(); } LOG(DEBUG) << "Directories made successfully"; return DatOperationResult<>(SUCCESS); } DatOperationResult<> DatFileSystem::MakeDictionary() { LOG(DEBUG) << "Started making dictionary"; if (!dat) return DatOperationResult<>(ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)"); if (root_directory_ == nullptr) { return DatOperationResult<>(ERROR, "DatFileSystem error: making dictionary from nullptr root directory"); } root_directory_->MakeDictionary(dictionary_); LOG(DEBUG) << "Dictionary made successfull"; return DatOperationResult<>(SUCCESS); } DatOperationResult<> DatFileSystem::CommitDirectories() { if (!dat) return DatOperationResult<>(ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)"); for (auto file_id : subfile_pending_update) { if (dictionary_[file_id] == nullptr) continue; auto operation = CheckCorrectSubfile(*dictionary_[file_id].get()); if (operation.result == ERROR) { LOG(ERROR) << "Check subfile correctness failed. Error message: " << operation.msg; continue; } if (!operation.value) { LOG(DEBUG) << "Incorrect SubFile " << file_id << " data: doesn't match"; continue; } auto operation1 = dat->getIO().WriteData(dictionary_[file_id]->MakeHeaderData(), 32, dictionary_[file_id]->dictionary_offset()); if (operation1.result == ERROR) { LOG(ERROR) << "Unable to write data to dictionary for file " << file_id << ". Returned message: " << operation.msg; } } subfile_pending_update.clear(); return DatOperationResult<>(SUCCESS); } DatOperationResult DatFileSystem::CheckCorrectSubfile(const SubFile &file) { if (!dat) return DatOperationResult(false, ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)"); BinaryData mfile_id(20); dat->getIO().ReadData(mfile_id, 20, file.file_offset() + 8); if (mfile_id.Empty()) return DatOperationResult(false, SUCCESS); return DatOperationResult((mfile_id.CheckCompression() || file.file_id() == mfile_id.ToNumber<4>(0)) && file.file_size() < 50ll * 1024ll * 1024ll, SUCCESS); } DatOperationResult<> DatFileSystem::Init() { auto operation = MakeDirectories(); if (operation.result == ERROR) { LOG(ERROR) << "Unable to make direcories. Error msg: " << operation.msg; return DatOperationResult<>(ERROR, "Unable to init Dat file system due to fail while making directories"); } operation = MakeDictionary(); if (operation.result == ERROR) { LOG(ERROR) << "Unable to make dictionary. Error msg: " << operation.msg; return DatOperationResult<>(ERROR, "Unable to init Dat file system due to fail while making dictionary"); } return DatOperationResult<>(SUCCESS); } DatOperationResult<> DatFileSystem::DeInit() { auto operation = CommitDirectories(); if (operation.result == ERROR) { LOG(ERROR) << "Unable to commit direcories. Error msg: " << operation.msg; return DatOperationResult<>(ERROR, "Unable to deinit Dat file system due to fail while making directories"); } return DatOperationResult<>(SUCCESS); } }