Просмотр исходного кода

Updated DatFileSystem class in order to improve initialisation speed

Ivan Arkhipov 6 лет назад
Родитель
Сommit
2a86038f18
2 измененных файлов с 359 добавлено и 115 удалено
  1. 24 10
      include/DatSubsystems/DatFileSystem.h
  2. 335 105
      src/DatSubsystems/DatFileSystem.cpp

+ 24 - 10
include/DatSubsystems/DatFileSystem.h

@@ -7,18 +7,23 @@
 
 #include <set>
 #include <map>
+#include <unordered_set>
+#include <unordered_map>
 #include <utility>
 #include <memory>
-#include "DatOperationResult.h"
+
+#include <DatOperationResult.h>
+#include <SubFile.h>
+#include <SubDirectory.h>
 
 extern "C++" {
 namespace LOTRO_DAT {
     class DatFile;
     class BinaryData;
     class SubDirectory;
-    class SubFile;
 
     class DatFileSystem {
+
     public:
         DatFileSystem() = delete;
         DatFileSystem(const DatFileSystem &other) = delete;
@@ -31,13 +36,13 @@ namespace LOTRO_DAT {
 
         DatOperationResult<BinaryData> GetFileData(long long file_id, long long int offset);
 
-        DatOperationResult<SubFile> GetFile(long long file_id);
+        DatOperationResult<std::shared_ptr<SubFile>> GetFile(long long file_id);
 
-        DatOperationResult<bool> CheckCorrectSubfile(const SubFile &file);
+        DatOperationResult<bool> CheckCorrectSubfile(const std::shared_ptr<SubFile> &file);
 
-        bool FileExists(long long file_id);
+        DatOperationResult<> UpdateFileInfo(const SubFile& file);
 
-        DatOperationResult<> UpdateFile(const SubFile& file);
+        void PrintInformaion(FILE* file);
 
         DatOperationResult<> DeInit();
 
@@ -46,9 +51,13 @@ namespace LOTRO_DAT {
         // PRIVATE INIT SECTION
         //------------------------------------------------//
 
-        DatOperationResult<> MakeDirectories();
+        DatOperationResult<> InitSubFile(long long file_id);
+
+        DatOperationResult<> InitSubDirectory(SubDirectory dir);
+
+        DatOperationResult<> InitAllDirectories();
 
-        DatOperationResult<> MakeDictionary();
+        DatOperationResult<> InitAllFiles();
 
         //------------------------------------------------//
         // PRIVATE DEINIT SECTION
@@ -58,10 +67,15 @@ namespace LOTRO_DAT {
 
     private:
         DatFile *dat;
-        std::shared_ptr<SubDirectory> root_directory_;
-
         std::set<long long> subfile_pending_update;
+
         std::map<long long, std::shared_ptr<SubFile> > dictionary_;
+
+        std::unordered_set<long long> visited_subdirectories_offsets_;
+        std::unordered_set<long long> visited_subfiles_ids_;
+
+        std::set<SubDirectory, SubDirectory::SubDirectoryOffsetComparator> subdir_init_queue_;
+        std::unordered_map<long long, SubFile> subfile_init_queue_;
     };
 }
 };

+ 335 - 105
src/DatSubsystems/DatFileSystem.cpp

@@ -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<>();
+    }
+
+
 }