|
@@ -9,38 +9,41 @@
|
|
|
#include <SubFile.h>
|
|
|
#include <SubDirectory.h>
|
|
|
|
|
|
-namespace LOTRO_DAT{
|
|
|
+namespace LOTRO_DAT {
|
|
|
DatFileSystem::DatFileSystem(DatFile *datFilePtr) : dat(datFilePtr) {
|
|
|
LOG(INFO) << "Initialization of empty DatFileSystem";
|
|
|
}
|
|
|
|
|
|
+ /*!
|
|
|
+ * \author Gi1dor
|
|
|
+ * \date 29.06.2018
|
|
|
+ * Возвращает бинарные данные файла. Помимо самого файла, в бинарном содержимом находится также метаинформация о нём.
|
|
|
+ * Изменение параметра offset позволяет игнорировать метаинформацию и извлечь только непосредственно содержимое файла
|
|
|
+ * \param[in] file_id Идентификатор получаемого файла
|
|
|
+ * \param[in] offset Отступ от начала файла. Данные до отступа не извлекаются.
|
|
|
+ * \return Возвращает result = ERROR и value = BinaryData, если файл не найден или информаци в словаре некорректная
|
|
|
+ */
|
|
|
|
|
|
DatOperationResult<BinaryData> DatFileSystem::GetFileData(long long file_id, long long int offset) {
|
|
|
if (!dat)
|
|
|
- return DatOperationResult<BinaryData>(BinaryData(), ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
|
|
|
+ return DatOperationResult<BinaryData>(BinaryData(), ERROR,
|
|
|
+ "DatFileSystem error: no connection with Dat (dat is nullptr)");
|
|
|
|
|
|
- if (dictionary_.count(file_id))
|
|
|
- return DatOperationResult<BinaryData>(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";
|
|
|
+ auto getfile_operation = GetFile(file_id);
|
|
|
+ if (getfile_operation.result == ERROR)
|
|
|
+ return DatOperationResult<BinaryData>(BinaryData(), ERROR, "DATFSGETFILEDATA: no file with id = " + std::to_string(file_id));
|
|
|
+
|
|
|
+ auto file = getfile_operation.value;
|
|
|
+
|
|
|
|
|
|
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>(BinaryData(), ERROR, std::string("DatFileSystem error: Read ") + std::to_string(file_id) + " error." + operation.msg);
|
|
|
- }
|
|
|
+ auto operation = CheckCorrectSubfile(file);
|
|
|
+ if (operation.result == ERROR || !operation.value)
|
|
|
+ return DatOperationResult<BinaryData>(BinaryData(), ERROR, "DATFSGETFILEDATA: Incorrect file" + std::to_string(file_id));
|
|
|
|
|
|
- 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>(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)));
|
|
|
+ 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<BinaryData>(data, SUCCESS);
|
|
@@ -57,155 +60,382 @@ namespace LOTRO_DAT{
|
|
|
|
|
|
BinaryData FragmentsDictionary(8 * unsigned(fragments_number));
|
|
|
dat->getIO().ReadData(FragmentsDictionary, 8 * unsigned(fragments_number),
|
|
|
- file->file_offset() + file->block_size() - 8 * 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);
|
|
|
+ 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<BinaryData>(data, SUCCESS);
|
|
|
}
|
|
|
|
|
|
|
|
|
- DatOperationResult<SubFile> DatFileSystem::GetFile(long long file_id) {
|
|
|
- if (!dat)
|
|
|
- return DatOperationResult<SubFile>(SubFile(), ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
|
|
|
-
|
|
|
- if (dictionary_.count(file_id) == 0)
|
|
|
- return DatOperationResult<SubFile>(SubFile(), ERROR, "No file with id = " + std::to_string(file_id) + " found in dictionary");
|
|
|
-
|
|
|
- return DatOperationResult<SubFile>(*dictionary_[file_id].get(), SUCCESS);
|
|
|
- }
|
|
|
-
|
|
|
+ /*!
|
|
|
+ * \author Gi1dor
|
|
|
+ * \date 29.06.2018
|
|
|
+ * Возвращает указатель на объект файла в словаре. Через этот указатель можно патчить/извлекать файл
|
|
|
+ * \param[in] file_id идентификатор получаемого файла
|
|
|
+ * \return Возвращает result = ERROR и value = nullptr, если файл не найден
|
|
|
+ */
|
|
|
|
|
|
- DatOperationResult<> DatFileSystem::UpdateFile(const SubFile &file) {
|
|
|
+ DatOperationResult<std::shared_ptr<SubFile>> DatFileSystem::GetFile(long long file_id) {
|
|
|
if (!dat)
|
|
|
- return DatOperationResult<>(ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
|
|
|
+ return DatOperationResult<std::shared_ptr<SubFile>>(nullptr, ERROR,
|
|
|
+ "DATFSGETFILE: 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");
|
|
|
+ if (dictionary_.count(file_id) == 0) {
|
|
|
+ auto operation = InitSubFile(file_id);
|
|
|
+ if (operation.result == ERROR)
|
|
|
+ return DatOperationResult<std::shared_ptr<SubFile>>(nullptr, ERROR);
|
|
|
+ }
|
|
|
|
|
|
- *dictionary_[file.file_id()] = file;
|
|
|
- return DatOperationResult<>(SUCCESS);
|
|
|
+ return DatOperationResult<std::shared_ptr<SubFile>>(dictionary_[file_id], 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<SubDirectory>((unsigned)dat->getIO().root_directory_offset, *dat);
|
|
|
- SubDirectory::subdir_init_queue_.insert(root_directory_);
|
|
|
-
|
|
|
- while (!SubDirectory::subdir_init_queue_.empty()) {
|
|
|
- std::shared_ptr<SubDirectory> 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<SubDirectory> 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);
|
|
|
+ /*!
|
|
|
+ * \author Gi1dor
|
|
|
+ * \date 29.06.2018
|
|
|
+ * Обновляет данные файла
|
|
|
+ * \param[in] preinit_file неинициализированный объект файла, данные которого обновляются в словаре
|
|
|
+ * \warning Новые данные не будут записаны в dat файле, пока не будет вызвана функция CommitDirectories()
|
|
|
+ */
|
|
|
+
|
|
|
+ DatOperationResult<> DatFileSystem::UpdateFileInfo(const SubFile &preinit_file) {
|
|
|
+ auto operation = GetFile(preinit_file.file_id());
|
|
|
+
|
|
|
+ if (operation.result == ERROR)
|
|
|
+ return DatOperationResult<>(ERROR, "DATFS: Unable to update file info - no file in dict with id = " +
|
|
|
+ std::to_string(preinit_file.file_id()));
|
|
|
+ auto file = operation.value;
|
|
|
+
|
|
|
+ file->unknown1_ = preinit_file.unknown1();
|
|
|
+ file->file_id_ = preinit_file.file_id();
|
|
|
+ file->file_offset_ = preinit_file.file_offset();
|
|
|
+ file->file_size_ = preinit_file.file_size();
|
|
|
+ file->timestamp_ = preinit_file.timestamp();
|
|
|
+ file->version_ = preinit_file.version();
|
|
|
+ file->block_size_ = preinit_file.block_size();
|
|
|
+ file->unknown2_ = preinit_file.unknown2();
|
|
|
+ return DatOperationResult<>();
|
|
|
}
|
|
|
|
|
|
|
|
|
- DatOperationResult<> DatFileSystem::MakeDictionary() {
|
|
|
- LOG(DEBUG) << "Started making dictionary";
|
|
|
+ /*!
|
|
|
+ * \author Gi1dor
|
|
|
+ * \date 29.06.2018
|
|
|
+ * Проверяет корректность файла, полученного из словаря
|
|
|
+ *
|
|
|
+ * \return Объект DatOperationResult<bool> для которого value = true, если файл корректный, и false - иначе.
|
|
|
+ */
|
|
|
+
|
|
|
+ DatOperationResult<bool> DatFileSystem::CheckCorrectSubfile(const std::shared_ptr<SubFile> &file) {
|
|
|
if (!dat)
|
|
|
- return DatOperationResult<>(ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
|
|
|
+ return DatOperationResult<bool>(false, ERROR, "DATFSCORRECTSUBFILE: no connection with Dat (dat is nullptr)");
|
|
|
|
|
|
- if (root_directory_ == nullptr) {
|
|
|
- return DatOperationResult<>(ERROR, "DatFileSystem error: making dictionary from nullptr root directory");
|
|
|
- }
|
|
|
+ BinaryData mfile_id(20);
|
|
|
+ auto operation = dat->getIO().ReadData(mfile_id, 20, file->file_offset() + 8);
|
|
|
+ if (operation.result == ERROR)
|
|
|
+ return DatOperationResult<bool>(false, ERROR, "DATFSCORRECTSUBFILE: cannot read file header data");
|
|
|
|
|
|
- root_directory_->MakeDictionary(dictionary_);
|
|
|
- LOG(DEBUG) << "Dictionary made successfull";
|
|
|
- return DatOperationResult<>(SUCCESS);
|
|
|
+ return DatOperationResult<bool>((mfile_id.CheckCompression() || file->file_id() == mfile_id.ToNumber<4>(0)) &&
|
|
|
+ file->file_size() < 50ll * 1024ll * 1024ll, SUCCESS);
|
|
|
}
|
|
|
|
|
|
|
|
|
+ /*!
|
|
|
+ * \author Gi1dor
|
|
|
+ * \date 29.06.2018
|
|
|
+ * Записывает все изменения структуры файловой системы в dat-файл.
|
|
|
+ */
|
|
|
+
|
|
|
DatOperationResult<> DatFileSystem::CommitDirectories() {
|
|
|
if (!dat)
|
|
|
- return DatOperationResult<>(ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
|
|
|
+ return DatOperationResult<>(ERROR, "DATFSCOMMITDIRS: 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());
|
|
|
+ auto operation = CheckCorrectSubfile(dictionary_[file_id]);
|
|
|
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;
|
|
|
- }
|
|
|
+ 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;
|
|
|
}
|
|
|
subfile_pending_update.clear();
|
|
|
return DatOperationResult<>(SUCCESS);
|
|
|
}
|
|
|
|
|
|
- DatOperationResult<bool> DatFileSystem::CheckCorrectSubfile(const SubFile &file) {
|
|
|
+
|
|
|
+ /*!
|
|
|
+ * \author Gi1dor
|
|
|
+ * \date 29.06.2018
|
|
|
+ * Подготавливает файловую систему для работы
|
|
|
+ */
|
|
|
+
|
|
|
+ DatOperationResult<> DatFileSystem::Init() {
|
|
|
if (!dat)
|
|
|
- return DatOperationResult<bool>(false, ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
|
|
|
+ return DatOperationResult<>(ERROR, "DATFSINIT: 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<bool>(false, SUCCESS);
|
|
|
+ DeInit();
|
|
|
+ SubDirectory root_directory(0, (unsigned) dat->getIO().root_directory_offset);
|
|
|
+ subdir_init_queue_.insert(root_directory);
|
|
|
+ return DatOperationResult<>(SUCCESS);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /*!
|
|
|
+ * \author Gi1dor
|
|
|
+ * \date 29.06.2018
|
|
|
+ * Завершает работу файловой системы и записывает изменения в dat файл
|
|
|
+ *
|
|
|
+ * \return Вернёт DatOperationResult<>(ERROR), если случилась ошибка записи структуры файлов в dat файл
|
|
|
+ */
|
|
|
+
|
|
|
+ DatOperationResult<> DatFileSystem::DeInit() {
|
|
|
+ if (!dat)
|
|
|
+ return DatOperationResult<>(ERROR, "DATFSDEINIT: no connection with Dat (dat is nullptr)");
|
|
|
+
|
|
|
+ auto operation = CommitDirectories();
|
|
|
+ dictionary_.clear();
|
|
|
+
|
|
|
+ visited_subdirectories_offsets_.clear();
|
|
|
+ visited_subfiles_ids_.clear();
|
|
|
+
|
|
|
+ subfile_init_queue_.clear();
|
|
|
+ subdir_init_queue_.clear();
|
|
|
|
|
|
- return DatOperationResult<bool>((mfile_id.CheckCompression() || file.file_id() == mfile_id.ToNumber<4>(0)) && file.file_size() < 50ll * 1024ll * 1024ll, SUCCESS);
|
|
|
+ subfile_pending_update.clear();
|
|
|
+
|
|
|
+ if (operation.result == ERROR)
|
|
|
+ return DatOperationResult<>(ERROR, "DEINITFS: unable to commit directories.");
|
|
|
+ return DatOperationResult<>(SUCCESS);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ /*!
|
|
|
+ * \author Gi1dor
|
|
|
+ * \date 29.06.2018
|
|
|
+ * Записывает общую информацию о файловой системе. Требует полной инициализации всех папок и файлов.
|
|
|
+ *
|
|
|
+ * \param[in] file Объект файла, в конец которого будут записываться данные.
|
|
|
+ */
|
|
|
+
|
|
|
+ void DatFileSystem::PrintInformaion(FILE *file) {
|
|
|
+ if (!dat) {
|
|
|
+ LOG(ERROR) << "DATFSPRINTINFO: no connection with Dat (dat is nullptr)";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ InitAllFiles();
|
|
|
|
|
|
- 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");
|
|
|
+ fprintf(file, "============= FS INFO SECTION =============\n");
|
|
|
+ fprintf(file, "Files number: %d\n", dictionary_.size());
|
|
|
+
|
|
|
+ std::vector<size_t> filetypes_count(7, 0);
|
|
|
+
|
|
|
+ for (const auto &datfile : dictionary_) {
|
|
|
+ filetypes_count[datfile.second->FileType()]++;
|
|
|
}
|
|
|
|
|
|
- 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");
|
|
|
+ fprintf(file, "TEXT files number = %d\n", filetypes_count[0]);
|
|
|
+ fprintf(file, "JPG files number = %d\n", filetypes_count[1]);
|
|
|
+ fprintf(file, "DDS files number = %d\n", filetypes_count[2]);
|
|
|
+ fprintf(file, "WAV files number = %d\n", filetypes_count[3]);
|
|
|
+ fprintf(file, "OGG files number = %d\n", filetypes_count[4]);
|
|
|
+ fprintf(file, "FONT files number = %d\n", filetypes_count[5]);
|
|
|
+ fprintf(file, "UNKNOWN files number = %d\n", filetypes_count[6]);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /*!
|
|
|
+ * \author Gi1dor
|
|
|
+ * \date 29.06.2018
|
|
|
+ * Инициализация файла с заданным id. Ищет информацию о файле, инициализирует его в соответствии с типом файла и
|
|
|
+ * помещает в словарь. Работает быстрее, если все папки уже были проинициализированы. В противном случае - пытается
|
|
|
+ * инициализировать каждую из папок, пока не найдёт в ней файла с требуем id.
|
|
|
+ *
|
|
|
+ * \param[in] file_id Id файла, который нужно проинициализировать.
|
|
|
+ * \return Объект DatOperationResult. Если файл не удалось проинициализировать - поле result будет равно ERROR
|
|
|
+ */
|
|
|
+
|
|
|
+ DatOperationResult<> DatFileSystem::InitSubFile(long long file_id) {
|
|
|
+ if (!dat)
|
|
|
+ return DatOperationResult<>(ERROR, "DATFSINITSUBFILE: no connection with Dat (dat is nullptr)");
|
|
|
+
|
|
|
+
|
|
|
+ while (subfile_init_queue_.count(file_id) == 0) {
|
|
|
+ if (subdir_init_queue_.empty())
|
|
|
+ return DatOperationResult<>(ERROR,
|
|
|
+ "DATFSINITSUBFILE: Cannot init file with id = "
|
|
|
+ + std::to_string(file_id) + " (NOT FOUND)");
|
|
|
+
|
|
|
+ SubDirectory dir = *subdir_init_queue_.begin();
|
|
|
+ InitSubDirectory(dir);
|
|
|
+ subdir_init_queue_.erase(subdir_init_queue_.begin());
|
|
|
}
|
|
|
|
|
|
- return DatOperationResult<>(SUCCESS);
|
|
|
+ SubFile file = subfile_init_queue_[file_id];
|
|
|
+ subfile_init_queue_.erase(file_id);
|
|
|
+
|
|
|
+
|
|
|
+ auto initialised_file = SubFile::MakeSubfile(*dat, file);
|
|
|
+ dictionary_[initialised_file->file_id()] = initialised_file;
|
|
|
+ return DatOperationResult<>();
|
|
|
}
|
|
|
|
|
|
|
|
|
- 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");
|
|
|
+ /*!
|
|
|
+ * \author Gi1dor
|
|
|
+ * \date 29.06.2018
|
|
|
+ * Инициализация всех папок. Собирает информацию обо всех файлах в .dat файле для быстрого доступа к ним.
|
|
|
+ * \warning Сами файлы не инициализируются. Инициализация файла выполнится при первом вызове GetFile() или GetFileData()
|
|
|
+ *
|
|
|
+ * \return Объект DatOperationResult
|
|
|
+ */
|
|
|
+
|
|
|
+ DatOperationResult<> DatFileSystem::InitAllDirectories() {
|
|
|
+ if (!dat)
|
|
|
+ return DatOperationResult<>(ERROR, "DATFSINITALLDIRS: no connection with Dat (dat is nullptr)");
|
|
|
+
|
|
|
+ while (!subdir_init_queue_.empty()) {
|
|
|
+ SubDirectory dir = *subdir_init_queue_.begin();
|
|
|
+ InitSubDirectory(dir);
|
|
|
+ subdir_init_queue_.erase(subdir_init_queue_.begin());
|
|
|
}
|
|
|
- return DatOperationResult<>(SUCCESS);
|
|
|
+ return DatOperationResult<>();
|
|
|
}
|
|
|
|
|
|
- bool DatFileSystem::FileExists(long long file_id) {
|
|
|
- return dictionary_.count(file_id) != 0;
|
|
|
+
|
|
|
+ /*!
|
|
|
+ * \author Gi1dor
|
|
|
+ * \date 29.06.2018
|
|
|
+ * Инициализация всех файлов. Полностью инициализирует все внутренние папки и файлы и помещает последние в словарь.
|
|
|
+ *
|
|
|
+ * \return Объект DatOperationResult
|
|
|
+ */
|
|
|
+
|
|
|
+ DatOperationResult<> DatFileSystem::InitAllFiles() {
|
|
|
+ if (!dat)
|
|
|
+ return DatOperationResult<>(ERROR, "DATFSINITALLFILES: no connection with Dat (dat is nullptr)");
|
|
|
+
|
|
|
+ InitAllDirectories();
|
|
|
+ while (!subfile_init_queue_.empty()) {
|
|
|
+ long long file_id = (*subfile_init_queue_.begin()).first;
|
|
|
+ InitSubFile(file_id);
|
|
|
+ subfile_init_queue_.erase(file_id);
|
|
|
+ }
|
|
|
+ return DatOperationResult<>();
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ /*!
|
|
|
+ * \author Gi1dor
|
|
|
+ * \date 29.06.2018
|
|
|
+ * Инициализация папки. Читает внутреннюю структуру папки и получает информацию о файлах и подпапках.
|
|
|
+ *
|
|
|
+ * Структура папки:
|
|
|
+ * 63 * 8 байт под описание подпапок. По 8 байт на папку.
|
|
|
+ * Из них по 4 байта на 2 поля:
|
|
|
+ * unknown - неизвестное поле
|
|
|
+ * offset - положение подпапки в .dat файле
|
|
|
+ *
|
|
|
+ * 4 байта - количество файлов в папке. В каждой папке не более 64 Файлов.
|
|
|
+ *
|
|
|
+ * 32 * 64 байта под описание файлов. По 32 байта на каждый файл.
|
|
|
+ *
|
|
|
+ * Из них по 4 байта на каждое из 8 полей:
|
|
|
+ * unknown1 - неизвестное поле
|
|
|
+ * file_id - уникальный идентификатор файла
|
|
|
+ * file_offset - положение содержимого файла
|
|
|
+ * file_size - размер файла в байтах
|
|
|
+ * timestamp - timestamp последнего редактирования
|
|
|
+ * version - версия файла
|
|
|
+ * block_size - размер блока данных, выделенного под файл
|
|
|
+ * unknown2 - неизвестное поле
|
|
|
+ *
|
|
|
+ * \param[in] dir Указатель на объект папки
|
|
|
+ * \return Объект DatOperationResult
|
|
|
+ */
|
|
|
+
|
|
|
+ DatOperationResult<> DatFileSystem::InitSubDirectory(SubDirectory dir) {
|
|
|
+ if (!dat)
|
|
|
+ return DatOperationResult<>(ERROR, "DATFSINITSUBDIR: no connection with Dat (dat is nullptr)");
|
|
|
+
|
|
|
+ BinaryData subdir_data(63 * 8 + 4 + 32 * 64);
|
|
|
+ auto operation = dat->getIO().ReadData(subdir_data, 63 * 8 + 4 + 32 * 64, dir.offset());
|
|
|
+ if (!operation.result == SUCCESS)
|
|
|
+ return DatOperationResult<>(ERROR, "DATFSINITSUBDIR: cannot init dir (read error) with offset = " +
|
|
|
+ std::to_string((dir.offset())));
|
|
|
+
|
|
|
+ if (subdir_data.ToNumber<4>(0) != 0 || subdir_data.ToNumber<4>(4) != 0)
|
|
|
+ return DatOperationResult<>(ERROR, "DATFSINITSUBDIR: incorrect dir. Nulls must be at first 8 bytes of dir");
|
|
|
+
|
|
|
+ // Subdirs section
|
|
|
+ for (unsigned i = 8; i < 63 * 8; i += 8) {
|
|
|
+ if (subdir_data.ToNumber<4>(i) == 0 || subdir_data.ToNumber<4>(i + 4) == 0)
|
|
|
+ break;
|
|
|
+
|
|
|
+ long long unknown = subdir_data.ToNumber<4>(i);
|
|
|
+ long long offset = subdir_data.ToNumber<4>(i + 4);
|
|
|
+
|
|
|
+ if (visited_subdirectories_offsets_.count(offset) != 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ visited_subdirectories_offsets_.insert(offset);
|
|
|
+ subdir_init_queue_.insert(SubDirectory(unknown, offset));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Subfiles section
|
|
|
+ long long subfiles_number = subdir_data.ToNumber<4>(63 * 8);
|
|
|
+ if (subfiles_number >= 64)
|
|
|
+ return DatOperationResult<>(ERROR, "DATINITSUBDIR: incorrect subdir: files number > 64");
|
|
|
+
|
|
|
+
|
|
|
+ for (int i = 0; i < subfiles_number; i++) {
|
|
|
+ long long header_start = 63 * 8 + 4 + 32 * i;
|
|
|
+
|
|
|
+ SubFile file = SubFile(
|
|
|
+ *dat,
|
|
|
+ dir.offset() + header_start,
|
|
|
+ subdir_data.ToNumber<4>(header_start + 0), // unknown1
|
|
|
+ subdir_data.ToNumber<4>(header_start + 4), // file_id
|
|
|
+ subdir_data.ToNumber<4>(header_start + 8), // file_offset
|
|
|
+ subdir_data.ToNumber<4>(header_start + 12), // file_size
|
|
|
+ subdir_data.ToNumber<4>(header_start + 16), // timestamp
|
|
|
+ subdir_data.ToNumber<4>(header_start + 20), // version
|
|
|
+ subdir_data.ToNumber<4>(header_start + 24), // block_size
|
|
|
+ subdir_data.ToNumber<4>(header_start + 28) // unknown2
|
|
|
+ );
|
|
|
+
|
|
|
+ if (file.version() == 0 || file.unknown2() != 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (visited_subfiles_ids_.count(file.file_id()))
|
|
|
+ break;
|
|
|
+
|
|
|
+ visited_subfiles_ids_.insert(file.file_id());
|
|
|
+ subfile_init_queue_.insert(file);
|
|
|
+ }
|
|
|
+ return DatOperationResult<>();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
}
|