Browse Source

Merge branch 'v7' of LotRO_Legacy/LotroDat into master

Ivan Arkhipov 4 years ago
parent
commit
fa3e639883
57 changed files with 3236 additions and 1927 deletions
  1. 3 6
      .gitignore
  2. 17 0
      CHANGELOG
  3. 37 23
      CMakeLists.txt
  4. 6 0
      Third_party/EasyLogging++/easylogging++.h
  5. BIN
      Third_party/lib/libyaml-cpp.a
  6. BIN
      Third_party/lib/libyaml-cppd.a
  7. BIN
      Third_party/lib/libyaml-cppmt.lib
  8. BIN
      Third_party/lib/libyaml-cppmtd.lib
  9. BIN
      Third_party/lib/libzlibstatic.a
  10. BIN
      Third_party/lib/libzlibstaticd.a
  11. BIN
      bin/LotRO_dat_extractor.exe
  12. BIN
      bin/LotRO_dat_patcher.exe
  13. 0 0
      docs/readme.md
  14. 2 1
      include/BinaryData.h
  15. 42 212
      include/DatFile.h
  16. 86 0
      include/DatOperationResult.h
  17. 56 0
      include/DatSubsystems/DatBackupManager.h
  18. 50 0
      include/DatSubsystems/DatExporter.h
  19. 92 0
      include/DatSubsystems/DatFileSystem.h
  20. 105 0
      include/DatSubsystems/DatIO.h
  21. 92 0
      include/DatSubsystems/DatLocaleManager.h
  22. 51 0
      include/DatSubsystems/DatPatcher.h
  23. 69 0
      include/DatSubsystems/DatStatus.h
  24. 5 2
      include/LotroDat.h
  25. 12 44
      include/SubDirectory.h
  26. 19 9
      include/SubFile.h
  27. 2 2
      include/SubfileData.h
  28. 2 4
      include/Subfiles/DdsSubFile.h
  29. 2 4
      include/Subfiles/FontSubFile.h
  30. 2 4
      include/Subfiles/JpgSubFile.h
  31. 2 4
      include/Subfiles/OggSubFile.h
  32. 6 9
      include/Subfiles/TextSubFile.h
  33. 2 4
      include/Subfiles/UnknownSubFile.h
  34. 2 4
      include/Subfiles/WavSubFile.h
  35. BIN
      lib/libLotroDat.dll.a
  36. BIN
      lib/libLotroDat_static.a
  37. 11 3
      src/BinaryData.cpp
  38. 100 1203
      src/DatFile.cpp
  39. 145 0
      src/DatSubsystems/DatBackupManager.cpp
  40. 216 0
      src/DatSubsystems/DatExporter.cpp
  41. 497 0
      src/DatSubsystems/DatFileSystem.cpp
  42. 419 0
      src/DatSubsystems/DatIO.cpp
  43. 612 0
      src/DatSubsystems/DatLocaleManager.cpp
  44. 192 0
      src/DatSubsystems/DatPatcher.cpp
  45. 42 0
      src/DatSubsystems/DatStatus.cpp
  46. 44 45
      src/Examples/extractor_example.cpp
  47. 46 0
      src/Examples/info_gatherer.cpp
  48. 22 43
      src/Examples/patcher_example.cpp
  49. 5 221
      src/SubDirectory.cpp
  50. 93 24
      src/SubFile.cpp
  51. 1 5
      src/Subfiles/DdsSubFile.cpp
  52. 1 5
      src/Subfiles/FontSubFile.cpp
  53. 1 5
      src/Subfiles/JpgSubFile.cpp
  54. 1 5
      src/Subfiles/OggSubFile.cpp
  55. 22 26
      src/Subfiles/TextSubFile.cpp
  56. 1 5
      src/Subfiles/UnknownSubFile.cpp
  57. 1 5
      src/Subfiles/WavSubFile.cpp

+ 3 - 6
.gitignore

@@ -1,6 +1,6 @@
 # cmake build directory
-cmake-build-debug 
-cmake-build-release
+cmake-build*
+cmake-build*
 # ide project directory
 .idea
 
@@ -21,7 +21,4 @@ debug_build/*
 release_build/*
 bin/*
 install/*
-
-
-!bin/LotRO_dat_extractor.exe
-!bin/LotRO_dat_patcher.exe
+lib/*

+ 17 - 0
CHANGELOG

@@ -59,3 +59,20 @@ Version 5.2.0
     * Improved speed of opening .dat file by visiting each directory only once.
     * Removed DatException class and all it's calls. Throwing exceptions changed to returning special null data.
 ----------------------------------------------------------------------
+Version 7.0.0
+    * Everything except some simpliest modules or structures was rewritten
+    * Logic part divided into submodules (DatIO, DatFileSystem, DatBackupManager, DatExporter, DatLocaleManager, DatPatcher, DatStatus)
+    * Slightly improved stability, added extra features (normal backup, operation status & percentage and so on)
+----------------------------------------------------------------------
+Version 7.1.0
+    * Improved security, added special fields, which help to recognize whether .dat file was broken
+----------------------------------------------------------------------
+Version 7.2.0
+    * Minor security fixes
+    * Reimplemented categories support
+----------------------------------------------------------------------
+Version 7.2.1
+    * Bug fixes (Text files used incorrect fragments during import, if there was no fragment in patch with specified id)
+----------------------------------------------------------------------
+Version 7.2.2
+    * Small fixes, added one more protection layer: locales dictionary now contains hash of header at the moment of last patch applying. So that system can easily detect if file was modified and there's a need to update patches.

+ 37 - 23
CMakeLists.txt

@@ -1,16 +1,16 @@
 cmake_minimum_required(VERSION 3.8)
-project(LotRO_dat_library)
+project(LotroDat)
 
 set(CMAKE_CXX_STANDARD 14)
 set(PROJECT_BINARY_DIR bin)
-set(PROJECT_VERSION 5.2.0)
+set(PROJECT_VERSION 7.2.2)
 
-SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS} -O3 -Wall -Wextra")
-SET(CMAKE_EXE_LINKER_FLAGS  "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS} -Wall -Wextra -O2")
+SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
 
 if (MSVS)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_RELEASE} /MT /SAFESEH:NO")
-endif(MSVS)
+endif (MSVS)
 
 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)
 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)
@@ -40,7 +40,14 @@ set(HEADER_FILES
         ${CMAKE_SOURCE_DIR}/include/Subfiles/FontSubfile.h
         ${CMAKE_SOURCE_DIR}/include/Subfiles/WavSubfile.h
         ${CMAKE_SOURCE_DIR}/include/Subfiles/UnknownSubfile.h
-)
+
+        ${CMAKE_SOURCE_DIR}/include/DatSubsystems/DatBackupManager.h
+        ${CMAKE_SOURCE_DIR}/include/DatSubsystems/DatExporter.h
+        ${CMAKE_SOURCE_DIR}/include/DatSubsystems/DatFileSystem.h
+        ${CMAKE_SOURCE_DIR}/include/DatSubsystems/DatIO.h
+        ${CMAKE_SOURCE_DIR}/include/DatSubsystems/DatLocaleManager.h
+        ${CMAKE_SOURCE_DIR}/include/DatSubsystems/DatPatcher.h
+        ${CMAKE_SOURCE_DIR}/include/DatSubsystems/DatStatus.h)
 
 set(SOURCE_FILES
         ${CMAKE_SOURCE_DIR}/src/DatFile.cpp
@@ -59,7 +66,14 @@ set(SOURCE_FILES
 
         ${CMAKE_SOURCE_DIR}/Third_party/SQLite/sqlite3.c
         ${CMAKE_SOURCE_DIR}/Third_party/EasyLogging++/easylogging++.cc
-        )
+
+        ${CMAKE_SOURCE_DIR}/src/DatSubsystems/DatBackupManager.cpp
+        ${CMAKE_SOURCE_DIR}/src/DatSubsystems/DatExporter.cpp
+        ${CMAKE_SOURCE_DIR}/src/DatSubsystems/DatFileSystem.cpp
+        ${CMAKE_SOURCE_DIR}/src/DatSubsystems/DatIO.cpp
+        ${CMAKE_SOURCE_DIR}/src/DatSubsystems/DatLocaleManager.cpp
+        ${CMAKE_SOURCE_DIR}/src/DatSubsystems/DatPatcher.cpp
+        ${CMAKE_SOURCE_DIR}/src/DatSubsystems/DatStatus.cpp)
 
 set(CompilerFlags
         CMAKE_CXX_FLAGS
@@ -74,27 +88,27 @@ set(CompilerFlags
         CMAKE_C_FLAGS_RELWITHDEBINFO
         )
 
-foreach(CompilerFlag ${CompilerFlags})
+foreach (CompilerFlag ${CompilerFlags})
     string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")
-endforeach()
+endforeach ()
 
 # STATIC LIBRARY
 add_library(LotroDat_static STATIC ${SOURCE_FILES} ${HEADER_FILES})
 # SHARED LIBRARY
 add_library(LotroDat SHARED ${SOURCE_FILES} ${HEADER_FILES})
 # CONSOLE EXTRACTOR TOOL
-add_executable(LotRO_dat_extractor ${SOURCE_FILES} ${HEADER_FILES} ${CMAKE_SOURCE_DIR}/src/Examples/extractor_example.cpp)
+add_executable(LotRO_dat_extractor ${CMAKE_SOURCE_DIR}/src/Examples/extractor_example.cpp ${SOURCE_FILES} ${HEADER_FILES})
 # CONSOLE PATCHER TOOL
-add_executable(LotRO_dat_patcher ${SOURCE_FILES} ${HEADER_FILES} ${CMAKE_SOURCE_DIR}/src/Examples/patcher_example.cpp)
-
-foreach(TARGET LotroDat_static LotroDat LotRO_dat_extractor LotRO_dat_patcher)
-    #if(MSVC)
-    #    target_link_libraries(${TARGET} ${CMAKE_SOURCE_DIR}/Third_Party/Zlib/zlibstatic.lib)
-    #    target_link_libraries(${TARGET} ${CMAKE_SOURCE_DIR}/Third_Party/Yaml-cpp/libyaml-cppmt.lib)
-    #    target_link_libraries(${TARGET} vcruntime.lib MSVCRT.lib)
-    #else(MSVC)
-        target_link_libraries(${TARGET} ${CMAKE_SOURCE_DIR}/Third_Party/lib/libzlibstatic.a)
-        target_link_libraries(${TARGET} ${CMAKE_SOURCE_DIR}/Third_Party/lib/libyaml-cpp.a)
-        target_link_libraries(${TARGET} -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic)
-    #endif(MSVC)
-endforeach(TARGET LotroDat_static LotroDat LotRO_dat_extractor LotRO_dat_patcher)
+add_executable(LotRO_dat_patcher ${CMAKE_SOURCE_DIR}/src/Examples/patcher_example.cpp ${SOURCE_FILES} ${HEADER_FILES})
+# FILE INFO GATHERER
+add_executable(LotRO_dat_info_gatherer ${SOURCE_FILES} ${HEADER_FILES} ${CMAKE_SOURCE_DIR}/src/Examples/info_gatherer.cpp)
+
+foreach (TARGET LotroDat_static LotroDat LotRO_dat_extractor LotRO_dat_patcher LotRO_dat_info_gatherer)
+    target_link_libraries(${TARGET} optimized ${CMAKE_SOURCE_DIR}/Third_Party/lib/libzlibstatic.a
+                                    debug ${CMAKE_SOURCE_DIR}/Third_Party/lib/libzlibstaticd.a
+            )
+    target_link_libraries(${TARGET} optimized ${CMAKE_SOURCE_DIR}/Third_Party/lib/libyaml-cpp.a
+                                    debug ${CMAKE_SOURCE_DIR}/Third_Party/lib/libyaml-cppd.a)
+
+    target_link_libraries(${TARGET} -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic)
+endforeach (TARGET LotroDat_static LotroDat LotRO_dat_extractor LotRO_dat_patcher)

+ 6 - 0
Third_party/EasyLogging++/easylogging++.h

@@ -15,6 +15,12 @@
 //
 #ifndef EASYLOGGINGPP_H
 #define EASYLOGGINGPP_H
+
+#define ELPP_FEATURE_CRASH_LOG
+#define ELPP_HANDLE_SIGABRT
+#define ELPP_DEFAULT_LOG_FILE "dat_library.log"
+#define ELPP_DEBUG_ERRORS
+
 // Compilers and C++0x/C++11 Evaluation
 #if __cplusplus >= 201103L
 #  define ELPP_CXX11 1

BIN
Third_party/lib/libyaml-cpp.a


BIN
Third_party/lib/libyaml-cppd.a


BIN
Third_party/lib/libyaml-cppmt.lib


BIN
Third_party/lib/libyaml-cppmtd.lib


BIN
Third_party/lib/libzlibstatic.a


BIN
Third_party/lib/libzlibstaticd.a


BIN
bin/LotRO_dat_extractor.exe


BIN
bin/LotRO_dat_patcher.exe


+ 0 - 0
docs/readme.md


+ 2 - 1
include/BinaryData.h

@@ -21,7 +21,8 @@ namespace LOTRO_DAT
     public:
         BinaryData();
         BinaryData(const BinaryData &d);
-        explicit BinaryData(const char* data, unsigned int size);
+        BinaryData(BinaryData&& other) noexcept;
+        BinaryData(const char* data, unsigned int size);
         explicit BinaryData(unsigned int size);
         ~BinaryData();
 

+ 42 - 212
include/DatFile.h

@@ -11,236 +11,66 @@
 #define LOTRO_DAT_API __declspec(dllimport)
 #endif
 
-#include <fstream>
-#include <map>
-#include <unordered_map>
-#include <set>
-#include <vector>
-#include <yaml-cpp/node/node.h>
-#include <unordered_set>
-#include "Database.h"
+#include <bits/unique_ptr.h>
+#include "DatSubsystems/DatBackupManager.h"
+#include "DatSubsystems/DatExporter.h"
+#include "DatSubsystems/DatFileSystem.h"
+#include "DatSubsystems/DatIO.h"
+#include "DatSubsystems/DatLocaleManager.h"
+#include "DatSubsystems/DatPatcher.h"
+#include "DatSubsystems/DatStatus.h"
 
 // Dat file names definitions
 
 extern "C++"
 {
 namespace LOTRO_DAT {
-    class BinaryData;
-
-    class DatException;
-
-    class SubDirectory;
-
-    class SubFile;
-
-    class SubfileData;
-
     enum FILE_TYPE : int {
-        TEXT,
-        JPG,
-        DDS,
-        WAV,
-        OGG,
-        FONT,
-        UNKNOWN
-    };
-
-    enum DAT_RESULT : int {
-        //----BASE----///
-        FAILED = 0,
-        SUCCESS = 1,
-
-        //----WARNINGS----//
-        CORRUPTED_FILE_WARNING = 2,
-
-        //----ERRORS----//
-        INCORRECT_STATE_ERROR = -1,
-        NO_FILE_ERROR = -2,
-        WRITE_TO_FILE_ERROR = -3,
-        INCORRECT_DAT_ID = -4,
-        INCORRECT_SUPERBLOCK_ERROR = -5,
-        INIT_ERROR = -6,
-        DUBLICATE_PATCH_FILES_ERROR = -8,
-        INCORRECT_PATCH_FILE = -9,
-        DAT_PATCH_FILE_ERROR = -10,
-        DAT_READ_ERROR = -11,
-        DAT_WRITE_ERROR = -12,
-        CRITICAL_DAT_ERROR = -14,
-        NO_BACKUP_ERROR = -15,
-        REMOVE_FILE_ERROR = -16
-    };
-
-    enum DAT_STATE {
-        CLOSED = 1,
-        SUCCESS_OPENED = 2,
-        SUCCESS_SUPERBLOCK = 3,
-        SUCCESS_DIRECTORIES = 4,
-        SUCCESS_DICTIONARY = 5,
-        READY = 6,
-        UPDATED = 7
-    };
-
-    enum LOCALE : unsigned {
-        ORIGINAL = 0,
-        PATCHED = 1
+        NO_TYPE = 0,
+        TEXT = 1,
+        JPG = 2,
+        DDS = 4,
+        WAV = 8,
+        OGG = 16,
+        FONT = 32,
+        UNKNOWN = 64
     };
 
     class DatFile {
-        friend class SubDirectory;
 
     public:
         DatFile();
-        explicit DatFile(const DatFile &other) = delete;
-        DatFile& operator=(const DatFile &other) = delete;
-
-        DAT_RESULT InitDatFile(const std::string &filename, int dat_id);
-
-        DAT_STATE DatFileState() const;
-
-        DAT_RESULT PerformDictionaryCheck();
-
+        DatFile(const DatFile &other) = delete;
+        DatFile(DatFile &&other) = default;
+        DatFile &operator=(const DatFile &other) = delete;
+        DatFile &operator=(DatFile &&other) = default;
         ~DatFile();
 
-        // EXTRACT BASE SECTION
-
-        DAT_RESULT ExtractFile(long long file_id, const std::string &path = "");
-
-        DAT_RESULT ExtractFile(long long file_id, Database *db);
-
-        int ExtractAllFilesByType(FILE_TYPE type, std::string path = "");
-
-        int ExtractAllFilesByType(FILE_TYPE type, Database *db);
-
-        // PATCH BASE SECTION
-
-        //DAT_RESULT PatchFile(const char *filename, YAML::Node options);
-
-        DAT_RESULT PatchFile(const SubfileData &data);
-
-        DAT_RESULT PatchAllDatabase(Database *db);
-
-
-        long long files_number() const;
-
-        const std::string& filename() const;
-
-        BinaryData GetFileData(const std::shared_ptr<SubFile>& file, long long offset = 0);
-
-        DAT_RESULT CloseDatFile();
-
-    private:
-        // INIT SECTION
-        DAT_RESULT OpenDatFile(const char *dat_name);
-
-        DAT_RESULT ReadSuperBlock();
-
-        DAT_RESULT MakeDirectories();
-
-        DAT_RESULT MakeDictionary();
-
-        DAT_RESULT ModifyFragmentationJournal();
-
-        // READ-WRITE SECTION
-
-        DAT_RESULT ReadData(BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);
-
-        DAT_RESULT WriteData(const BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);
+        DatLocaleManager &GetLocaleManager();
+        DatExporter &GetExporter();
+        DatPatcher &GetPatcher();
+        DatBackupManager &GetBackupManager();
+        DatIO &GetIO();
+        DatFileSystem &GetFileSystem();
+        DatStatus &GetStatusModule();
 
-        // PATCH SECTION
+        DatOperationResult<> Initialise(const std::string &filename, long long dat_id);
+        DatOperationResult<> GatherInformation(const std::string &output_filename);
+        DatOperationResult<> Deinitialize();
+        bool Initialized();
+        long long GetDatID();
 
-        DAT_RESULT ApplyFilePatch(std::shared_ptr<SubFile> file, BinaryData &data);
-
-    private:
-        long long free_buffered_size_;
-
-        const unsigned MAX_BUFFERED_SIZE = 10 * 1024 * 1024; // 50 megabytes;
-        const unsigned MIN_BUFFERED_SIZE = 1 * 1024 * 1024; // 5 megabytes;
-
-        void AddBufferedSize();
-
-        // COMMIT UPDATE SECTION
-
-        DAT_RESULT UpdateHeader();
-
-        // LOCALE MANAGING SECTION
     private:
-        DAT_RESULT InitLocales();
-
-        DAT_RESULT CommitLocales();
-
-        DAT_RESULT CommitDirectories();
-
-    public:
-        DAT_RESULT SetLocale(LOCALE locale);
-
-        LOCALE current_locale();
-
-        // CATEGORY MANAGEMENT SECTION
-
-        DAT_RESULT EnableCategory(int category);
-
-        DAT_RESULT DisableCategory(int category);
-
-        const std::set<long long>& GetInactiveCategoriesList();
-
-        // SOME PRIOR TOOLS
-
-        DAT_RESULT RepairDatFile();
-
-        bool CorrectSubfile(std::shared_ptr<SubFile> file);
-
-        bool CheckIfUpdatedByGame();
-
-        DAT_RESULT WriteUnorderedDictionary(std::string path) const;
-
-        bool CheckIfNotPatched();
-
-        bool CheckIfPatchedByOldLauncher();
-
-        bool CheckIfBackupExists(const std::string &backup_datname);
-
-        DAT_RESULT CreateBackup(const std::string &backup_datname);
-
-        DAT_RESULT RestoreFromBackup(const std::string &backup_datname);
-
-        DAT_RESULT RemoveBackup(const std::string &backup_datname);
-    private:
-        std::map<long long, std::shared_ptr<SubFile>>& GetLocaleDictReference(LOCALE locale);
-
-    private:
-        LOCALE current_locale_;
-
-        std::string filename_;
-
-        std::map<long long, std::shared_ptr<SubFile>> orig_dict_;
-        std::map<long long, std::shared_ptr<SubFile>> patch_dict_;
-        std::set<long long> pending_patch_;
-        std::set<long long> inactive_categories;
-
-    private:
-        FILE *file_handler_;
-
-        std::shared_ptr<SubDirectory> root_directory_;
-
-        std::set<long long> pending_dictionary_;
-        std::map<long long, std::shared_ptr<SubFile> > dictionary_;
-
-        long long constant1_;
-        long long constant2_;
-        long long file_size_;
-        long long version1_;
-        long long version2_;
-        long long fragmentation_journal_size_;
-        long long fragmentation_journal_end_;
-        long long root_directory_offset_;
-        long long fragmentation_journal_offset_;
-        long long free_dat_size_;
-
-        long long actual_dat_size_;
-        DAT_STATE dat_state_;
-
-        int dat_id_;
-        bool dat_without_patches_;
+        std::unique_ptr<DatIO> io_;
+        std::unique_ptr<DatFileSystem> fileSystem_;
+        std::unique_ptr<DatLocaleManager> localeManager_;
+        std::unique_ptr<DatPatcher> patcher_;
+        std::unique_ptr<DatExporter> exporter_;
+        std::unique_ptr<DatBackupManager> backupManager_;
+        std::unique_ptr<DatStatus> status_;
+
+        bool initialized_;
+        long long dat_id_;
     };
 }
 }

+ 86 - 0
include/DatOperationResult.h

@@ -0,0 +1,86 @@
+//
+// Created by kikab on 04.06.2018.
+//
+
+#ifndef LOTRO_DAT_LIBRARY_DATOPERATIONRESULT_H
+#define LOTRO_DAT_LIBRARY_DATOPERATIONRESULT_H
+
+#include <string>
+#include <memory>
+#include <utility>
+#include "BinaryData.h"
+#include <EasyLogging++/easylogging++.h>
+
+extern "C++"
+{
+namespace LOTRO_DAT {
+    enum DAT_OPERATION_RESULT {
+        SUCCESS = 1,
+        ERROR = 0
+    };
+
+    template<typename... OutputData>
+    class DatOperationResult {
+        typedef DAT_OPERATION_RESULT RESULT;
+    };
+
+    class __DatOperationResult_base {
+    public:
+        typedef DAT_OPERATION_RESULT RESULT;
+
+        __DatOperationResult_base() : result(SUCCESS), msg("No message") {}
+
+        __DatOperationResult_base(const __DatOperationResult_base &other) = default;
+
+        __DatOperationResult_base(__DatOperationResult_base &&other) noexcept = default;
+
+        __DatOperationResult_base(RESULT result_, std::string msg_) : result(result_), msg(std::move(msg_)) {
+            if (result_ == ERROR)
+                LOG(ERROR) << msg;
+        }
+
+        __DatOperationResult_base &operator=(const __DatOperationResult_base &other) = default;
+
+        __DatOperationResult_base &operator=(__DatOperationResult_base &&other) = default;
+
+        RESULT result;
+        std::string msg;
+    };
+
+    template<>
+    class DatOperationResult<> : public __DatOperationResult_base {
+    public:
+
+        DatOperationResult() : __DatOperationResult_base() {}
+
+        explicit DatOperationResult(RESULT result_) : __DatOperationResult_base(result_, "No message") {}
+
+        DatOperationResult(RESULT result_, std::string msg_) : __DatOperationResult_base(result_, std::move(msg_)) {}
+    };
+
+
+    template<typename Output>
+    class DatOperationResult<Output> : public __DatOperationResult_base {
+    public:
+        typedef DAT_OPERATION_RESULT RESULT;
+
+        DatOperationResult() = delete;
+
+        DatOperationResult(const DatOperationResult<Output> &other) : __DatOperationResult_base(), value(other.value) {}
+
+        DatOperationResult(DatOperationResult<Output> &&other) noexcept
+                : __DatOperationResult_base(), value(std::move(other.value)) {}
+
+        DatOperationResult(Output &&output_, RESULT result_, const std::string &msg_ = std::string("No message provided")) : __DatOperationResult_base(
+                result_, msg_), value(output_) {}
+
+        DatOperationResult(const Output &output_, RESULT result_, const std::string &msg_ = std::string("No message provided")) : __DatOperationResult_base(
+                result_, msg_), value(output_) {}
+
+        Output value;
+    };
+
+}
+}
+
+#endif //LOTRO_DAT_LIBRARY_DATOPERATIONRESULT_H

+ 56 - 0
include/DatSubsystems/DatBackupManager.h

@@ -0,0 +1,56 @@
+//
+// Created by kikab on 04.06.2018.
+//
+
+#ifndef LOTRO_DAT_LIBRARY_DATBACKUP_H
+#define LOTRO_DAT_LIBRARY_DATBACKUP_H
+
+#include <string>
+#include "../DatOperationResult.h"
+
+extern "C++" {
+namespace LOTRO_DAT {
+    class DatFile;
+
+    /*!
+     * \brief Модуль резервного копирования файлов
+     * \author Gi1dor
+     * \date 30.06.2018
+     *
+     * Класс для осуществления резервного копирования без деинициализации модулей и потери данных dat файла.
+     *
+     * \warning Объекты этого класса не должны создаваться отдельно! Созданием и управлением ими занимается класс DatFile
+     */
+
+    class DatBackupManager {
+    public:
+        DatBackupManager() = delete;
+
+        DatBackupManager(const DatBackupManager &other) = delete;
+
+        DatBackupManager &operator=(const DatBackupManager &other) = delete;
+
+        ~DatBackupManager() = default;
+
+        explicit DatBackupManager(DatFile *datFilePtr);
+
+        bool CheckIfBackupAvailable(const std::string &backup_datname);
+
+        DatOperationResult<> CreateBackup(const std::string &backup_datname);
+
+        DatOperationResult<> RestoreFromBackup(const std::string &backup_datname);
+
+        DatOperationResult<> RemoveBackup(const std::string &backup_datname);
+
+    private:
+
+        DatOperationResult<> CopyDatFile(DatFile &source, FILE* target);
+
+        DatFile *dat;
+
+        const unsigned COPY_BLOCK_SIZE = 16 * 1024 * 1024; // 16 megabytes
+    };
+}
+};
+
+#endif //LOTRO_DAT_LIBRARY_DATBACKUP_H

+ 50 - 0
include/DatSubsystems/DatExporter.h

@@ -0,0 +1,50 @@
+#ifndef LOTRO_DAT_LIBRARY_DATEXPORTER_H
+#define LOTRO_DAT_LIBRARY_DATEXPORTER_H
+
+#include "../DatOperationResult.h"
+#include "../Database.h"
+
+extern "C++" {
+namespace LOTRO_DAT {
+
+    enum FILE_TYPE : int;
+
+    /*!
+     * \brief Модуль экспорта файлов
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Класс для экспорта файлов из dat контейнера. Позволяет извлекать файлы по их типам в базу данных или в папку.
+     * Также позволяет извлекать отдельные файлы по их id.
+     * \warning Объекты этого класса не должны создаваться отдельно! Созданием и управлением ими занимается класс DatFile
+     */
+
+    class DatExporter {
+    public:
+        DatExporter() = delete;
+
+        DatExporter(const DatExporter &other) = delete;
+
+        DatExporter &operator=(const DatExporter &other) = delete;
+
+        ~DatExporter() = default;
+
+        explicit DatExporter(DatFile *datFilePtr);
+
+        DatOperationResult<int> ExtractAllFilesByType(const FILE_TYPE &type, std::string output_directory_path);
+
+        DatOperationResult<> ExtractFileById(long long file_id, std::string output_filename);
+
+        DatOperationResult<int> ExtractAllFilesByType(const FILE_TYPE &type, Database *db);
+
+        DatOperationResult<> ExtractFileById(long long file_id, Database *db);
+
+    private:
+        DatOperationResult<> WriteStringToFile(const std::u16string &str, const std::string &path);
+
+        DatFile *dat;
+
+    };
+}
+};
+
+#endif //LOTRO_DAT_LIBRARY_DATEXPORTER_H

+ 92 - 0
include/DatSubsystems/DatFileSystem.h

@@ -0,0 +1,92 @@
+//
+// Created by kikab on 04.06.2018.
+//
+
+#ifndef LOTRO_DAT_LIBRARY_DATFILESYSTEM_H
+#define LOTRO_DAT_LIBRARY_DATFILESYSTEM_H
+
+#include <set>
+#include <map>
+#include <unordered_set>
+#include <unordered_map>
+#include <utility>
+#include <memory>
+
+#include "../DatOperationResult.h"
+#include "../SubFile.h"
+#include "../SubDirectory.h"
+
+extern "C++" {
+namespace LOTRO_DAT {
+    class DatFile;
+    class BinaryData;
+    class SubDirectory;
+
+
+    /*!
+     * \brief Модуль файловой системы
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Класс для работы с внутренними файлами в dat-контейнере. Позволяет найти и извлечь данные необходимого файла
+     * или изменить информацию о нём
+     * \warning Объекты этого класса не должны создаваться отдельно! Созданием и управлением ими занимается класс DatFile
+     */
+
+    class DatFileSystem {
+
+    public:
+        DatFileSystem() = delete;
+        DatFileSystem(const DatFileSystem &other) = delete;
+        DatFileSystem& operator=(const DatFileSystem &other) = delete;
+        ~DatFileSystem() = default;
+
+        explicit DatFileSystem(DatFile *datFilePtr);
+
+        DatOperationResult<> Init();
+
+        DatOperationResult<> InitAllDirectories();
+
+        DatOperationResult<> InitAllFiles();
+
+        DatOperationResult<BinaryData> GetFileData(const SubFile& file, long long int offset);
+
+        DatOperationResult<std::shared_ptr<SubFile>> GetFile(long long file_id);
+
+        DatOperationResult<bool> CheckCorrectSubfile(const SubFile& file);
+
+        DatOperationResult<> UpdateFileInfo(const SubFile& file);
+
+        int GetInitialisedFilesNumber() const;
+
+        void PrintInformaion(FILE* file);
+
+        DatOperationResult<> DeInit();
+
+        DatOperationResult<> PerformOperationOnAllFiles(const std::function<void (std::shared_ptr<SubFile>&)>& function);
+
+        DatOperationResult<> CommitDirectories();
+
+    private:
+        DatOperationResult<> InitSubFile(long long file_id);
+
+        DatOperationResult<> InitSubDirectory(SubDirectory dir);
+
+    private:
+        DatFile *dat;
+        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_map_;
+        std::set<SubFile, SubFile::SubFileOffsetComparator> subfile_init_queue_;
+    public:
+        long long patched_file_end;
+    };
+}
+};
+
+#endif //LOTRO_DAT_LIBRARY_DATFILESYSTEM_H

+ 105 - 0
include/DatSubsystems/DatIO.h

@@ -0,0 +1,105 @@
+//
+// Created by kikab on 04.06.2018.
+//
+
+#ifndef LOTRO_DAT_LIBRARY_DATIO_H
+#define LOTRO_DAT_LIBRARY_DATIO_H
+
+#include <utility>
+#include "../DatOperationResult.h"
+
+extern "C++" {
+namespace LOTRO_DAT {
+    class BinaryData;
+
+    class DatFile;
+
+    class SubDirectory;
+
+    class SubFile;
+
+    /*!
+     * \brief Модуль ввода/вывода
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Класс для работы с вводом-выводом данных в dat-контейнере. Предоставляет функции чтения/записи данных, а также
+     * информацию об основных параметрах dat файла
+     * \warning Объекты этого класса не должны создаваться отдельно! Созданием и управлением ими занимается класс DatFile
+     */
+
+    class DatIO {
+    public:
+        DatIO() = delete;
+        DatIO(const DatIO &other) = delete;
+        DatIO& operator=(const DatIO &other) = delete;
+        ~DatIO();
+
+        explicit DatIO(DatFile *datFilePtr);
+
+        DatOperationResult<> Init(const std::string &filename);
+
+        DatOperationResult<> ReadData(BinaryData &data, long long size, long long offset = 0, long long data_offset = 0) const;
+
+        DatOperationResult<> WriteData(const BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);
+
+        DatOperationResult<long long> GetActualDatSize();
+
+        DatOperationResult<std::string> GetFilename();
+
+        void PrintInformaion(FILE* file);
+
+        DatOperationResult<> DeInit();
+
+        unsigned int GetHeaderHash();
+    private:
+
+        void ClearData();
+
+        //------------------------------------------------//
+        // PRIVATE INIT SECTION
+        //------------------------------------------------//
+
+        DatOperationResult<> OpenDatFile();
+
+        DatOperationResult<> ReadSuperBlock();
+
+        //------------------------------------------------//
+        // PRIVATE READ-WRITE SECTION
+        //------------------------------------------------//
+
+        void UpdateBufferIfNeeded(long long size_to_write);
+
+        //------------------------------------------------//
+        // PRIVATE DEINIT SECTION
+        //------------------------------------------------//
+
+        DatOperationResult<> ModifyFragmentationJournal();
+
+        DatOperationResult<> UpdateHeader();
+
+    private:
+        DatFile *dat;
+        FILE *file_handler_;
+        std::string filename_;
+
+    public:
+        /// Header values
+        long long constant1;
+        long long constant2;
+        long long file_size;
+        long long version1;
+        long long version2;
+        long long fragmentation_journal_size;
+        long long fragmentation_journal_end;
+        long long root_directory_offset;
+        long long fragmentation_journal_offset;
+        long long free_dat_size;
+
+    private:
+        long long actual_dat_size_;
+        const unsigned MAX_EOF_BUFFER = 15 * 1024 * 1024; /// Maximal size of buffer, which is written to the end of dat file to improve write speed of small fragments
+    };
+}
+};
+#endif //LOTRO_DAT_LIBRARY_DATIO_H
+

+ 92 - 0
include/DatSubsystems/DatLocaleManager.h

@@ -0,0 +1,92 @@
+#ifndef LOTRO_DAT_LIBRARY_DATLOCALEMANAGER_H
+#define LOTRO_DAT_LIBRARY_DATLOCALEMANAGER_H
+
+#include <memory>
+#include <map>
+#include <set>
+
+#include "../DatOperationResult.h"
+
+extern "C++" {
+namespace LOTRO_DAT {
+    class DatFile;
+
+    class SubFile;
+
+    /*!
+     * \brief Модуль работы с локалями
+     * \author Gi1dor
+     * \date 06.07.2018
+     *
+     * Класс для работы с искуственно внедряемымыми в dat контейнер копиями файлов. Позволяет независимо хранить
+     * информацию о двух версиях каждого файла, которую можно подставлять в файловую систему.
+     * В рамках русификации хранит информацию об оригинальной и русифицированной версии тех файлов, для которых
+     * существует русификация.
+     *
+     * \warning Данные локалей чувствительны к обновлениям игры и могут быть стёрты после них!
+     * \warning Объекты этого класса не должны создаваться отдельно! Созданием и управлением ими занимается класс DatFile
+     */
+
+    class DatLocaleManager {
+        friend class DatStatus;
+    public:
+        enum LOCALE : int {
+            ORIGINAL = 0,
+            PATCHED = 1
+        };
+
+        DatLocaleManager() = delete;
+
+        DatLocaleManager(const DatLocaleManager &other) = delete;
+
+        DatLocaleManager &operator=(const DatLocaleManager &other) = delete;
+
+        ~DatLocaleManager() = default;
+
+        explicit DatLocaleManager(DatFile *datFilePtr);
+
+        DatOperationResult<> Init();
+
+        DatOperationResult<> SetLocale(LOCALE locale);
+
+        DatOperationResult<> DeInit();
+
+        DatOperationResult<> CommitLocales();
+
+        LOCALE GetCurrentLocale();
+
+        bool CheckLocaleCorrect();
+
+        void UpdateLocaleFile(LOCALE locale, const SubFile &file);
+
+        DatOperationResult<SubFile> GetLocaleFile(long long file_id, LOCALE locale);
+
+        void PrintInformaion(FILE *file);
+
+        bool CategoryIsInactive(long long category);
+
+        void UpdateCategory(long long file_id, long long category);
+
+        DatOperationResult<> EnableCategory(long long category);
+
+        DatOperationResult<> DisableCategory(long long category);
+
+        const std::set<long long>& GetInactiveCategories();
+
+    private:
+        std::map<long long, SubFile> &GetLocaleDictReference(LOCALE locale);
+
+        void ClearData();
+
+    private:
+        DatFile *dat;
+        std::map<long long, SubFile> orig_dict_;
+        std::map<long long, SubFile> patch_dict_;
+        std::set<long long> inactive_categories;
+        LOCALE current_locale_;
+    };
+}
+};
+
+
+#endif //LOTRO_DAT_LIBRARY_DATLOCALEMANAGER_H

+ 51 - 0
include/DatSubsystems/DatPatcher.h

@@ -0,0 +1,51 @@
+//
+// Created by kikab on 04.06.2018.
+//
+
+#ifndef LOTRO_DAT_LIBRARY_DATPATCHER_H
+#define LOTRO_DAT_LIBRARY_DATPATCHER_H
+
+#include <yaml-cpp/yaml.h>
+#include "../DatOperationResult.h"
+
+extern "C++"
+{
+namespace LOTRO_DAT {
+    class DatFile;
+    class SubfileData;
+    class SubFile;
+    class Database;
+    class BinaryData;
+
+    /*!
+     * \brief Модуль экспорта файлов
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Класс для изменения файлов в dat контейнере. Позволяет обновлять файлы, сохраняя их оригинальные версии
+     *
+     * \warning Объекты этого класса не должны создаваться отдельно! Созданием и управлением ими занимается класс DatFile
+     */
+
+    class DatPatcher {
+    public:
+        DatPatcher() = delete;
+        DatPatcher(const DatPatcher &other) = delete;
+        DatPatcher& operator=(const DatPatcher &other) = delete;
+        ~DatPatcher() = default;
+
+        explicit DatPatcher(DatFile *datFilePtr);
+
+        DatOperationResult<> PatchFile(const SubfileData &data, bool single_file = true);
+
+        DatOperationResult<int> PatchAllDatabase(Database *db);
+
+    private:
+        DatOperationResult<> ApplyFilePatch(std::shared_ptr<SubFile> file, BinaryData &data);
+
+    private:
+        DatFile *dat;
+    };
+}
+}
+
+#endif //LOTRO_DAT_LIBRARY_DATFILEPATCHER_H

+ 69 - 0
include/DatSubsystems/DatStatus.h

@@ -0,0 +1,69 @@
+#ifndef LOTRO_DAT_LIBRARY_DATSTATUS_H
+#define LOTRO_DAT_LIBRARY_DATSTATUS_H
+
+#include <string>
+
+namespace LOTRO_DAT {
+    class DatFile;
+
+    /*!
+     * \brief Модуль статуса dat-файла
+     * \author Gi1dor
+     * \date 06.07.2018
+     *
+     * Класс для хранения информации о выполняемых процессах в dat-файле. Позволяет отслеживать прогресс выполнения
+     * во время операций создания резервных копий, применения патчей или извлечения файлов
+     *
+     * \warning Объекты этого класса не должны создаваться отдельно! Созданием и управлением ими занимается класс DatFile
+     */
+
+    class DatStatus {
+    public:
+        enum DAT_STATUS: int {
+            E_INITIALISING,
+            E_EXTRACTING,
+            E_PATCHING,
+            E_COMMITING,
+            E_BACKUP_CREATING,
+            E_BACKUP_RESTORING,
+            E_BACKUP_REMOVING,
+            E_GATHERING_INFO,
+            E_FREE
+        };
+
+        DatStatus() = delete;
+
+        DatStatus(const DatStatus &other) = delete;
+
+        DatStatus &operator=(const DatStatus &other) = delete;
+
+        ~DatStatus() = default;
+
+        explicit DatStatus(DatFile *datFilePtr);
+
+        void SetPercentage(unsigned percent);
+
+        unsigned GetPercentage();
+
+        void SetDebugMessage(const std::string &message);
+
+        std::string GetDebugMessage();
+
+        void SetStatus(DAT_STATUS status);
+
+        DAT_STATUS GetStatus();
+
+        bool CheckIfNotPatched();
+
+        void ClearAll();
+
+    private:
+        DatFile *dat;
+        unsigned percentage_;
+        DAT_STATUS status_;
+
+        std::string debug_message;
+    };
+}
+
+#endif //LOTRO_DAT_LIBRARY_DATSTATUS_H

+ 5 - 2
include/LotroDat.h

@@ -2,9 +2,12 @@
 // Created by Иван_Архипов on 01.11.2017.
 //
 
+#define LOTRO_DAT_VERSION "7.2.2"
+
 #include "DatFile.h"
 #include "Database.h"
 #include "SubfileData.h"
+#include "DatOperationResult.h"
 
-#include "yaml-cpp/yaml.h"
-#include "ZLib/zlib.h"
+#include <yaml-cpp/yaml.h>
+#include <ZLib/zlib.h>

+ 12 - 44
include/SubDirectory.h

@@ -5,63 +5,31 @@
 #ifndef LOTRO_DAT_PATCHER_SUBDIRECTORY_H
 #define LOTRO_DAT_PATCHER_SUBDIRECTORY_H
 
-#include <vector>
-#include <map>
-#include <queue>
-#include <unordered_map>
-#include <unordered_set>
-#include "SubFile.h"
-
-
 extern "C++"
 {
-namespace LOTRO_DAT
-{
-    enum FILE_TYPE : int;
-    class DatFile;
-    class BinaryData;
-    class SubFile;
-
-    class SubDirectory
-    {
+namespace LOTRO_DAT {
+    class SubDirectory {
     public:
-        struct SubDirectoryOffsetComp {
-            bool operator() (const std::shared_ptr<SubDirectory> &f, const std::shared_ptr<SubDirectory> &s){
-                if (!f || !s)
-                    return false;
-                return f->offset_ < s->offset_;
+        struct SubDirectoryOffsetComparator {
+            bool operator()(const SubDirectory &f, const SubDirectory &s) {
+                return f.offset_ < s.offset_;
             }
         };
 
-        SubDirectory() = delete;
-        explicit SubDirectory(const SubDirectory& other) = delete;
-        SubDirectory& operator =(const SubDirectory &other) = delete;
+        SubDirectory() = default;
 
-        SubDirectory(long long offset, DatFile &dat, long long max_subdirs = 63);
-        ~SubDirectory();
-        void MakeDictionary(std::map<long long, std::shared_ptr<SubFile>> &dict);
+        SubDirectory(const SubDirectory &other) = default;
 
-        bool MakeSubDirectories();
-        bool MakeSubFiles();
+        SubDirectory &operator=(const SubDirectory &other) = default;
 
-        void clear();
+        SubDirectory(long long unknown, long long offset);
 
-        static std::unordered_set<long long> visited_subdirectories_;
-        static std::unordered_set<long long> visited_subfiles_;
-        static std::set<std::shared_ptr<SubDirectory>, SubDirectoryOffsetComp> subdir_init_queue_;
-        static std::set<std::shared_ptr<SubDirectory>, SubDirectoryOffsetComp> subfile_init_queue_;
+    public:
+        long long offset();
 
     private:
-        std::shared_ptr<SubFile> MakeSubfile(long long dictionary_offset, long long unknown1, long long file_id, long long file_offset,
-                             long long file_size, long long timestamp, long long version, long long block_size, long long unknown2);
-
-        FILE_TYPE GetSubfileType(long long file_id, long long file_offset) const;
-        DatFile &dat_;
+        long long unknown_;
         long long offset_;
-        long long max_subdirs_;
-
-        std::vector<std::shared_ptr<SubDirectory>> subdirs_;
-        std::vector<std::shared_ptr<SubFile>> subfiles_;
     };
 }
 };

+ 19 - 9
include/SubFile.h

@@ -7,7 +7,7 @@
 
 #include <string>
 #include <vector>
-#include "yaml-cpp/yaml.h"
+#include <yaml-cpp/yaml.h>
 
 extern "C++"
 {
@@ -22,12 +22,20 @@ namespace LOTRO_DAT
 
     class SubFile
     {
-        friend class DatFile;
-		friend class SubDirectory;
+        friend class DatFileSystem;
+		friend class DatPatcher;
+
     public:
-        SubFile() = delete;
-        explicit SubFile(const SubFile &other) = delete;
-		SubFile &operator =(const SubFile &other);
+        struct SubFileOffsetComparator {
+            bool operator() (const SubFile &f, const SubFile &s){
+                return f.file_offset_ < s.file_offset_;
+            }
+        };
+
+
+        SubFile();
+        SubFile(const SubFile &other) = default;
+		SubFile &operator =(const SubFile &other) = default;
 
         SubFile(DatFile &dat, const BinaryData &header);
 		SubFile(DatFile &dat, long long dictionary_offset, long long fragments_count, long long unknown1,
@@ -39,9 +47,7 @@ namespace LOTRO_DAT
         virtual std::string Extension() const;
 
         virtual SubfileData PrepareForExport(const BinaryData &file_data);
-
         virtual BinaryData MakeForImport(const BinaryData &old_data, const SubfileData &data);
-
 		BinaryData MakeHeaderData() const;
 
 		long long dictionary_offset() const;
@@ -57,10 +63,14 @@ namespace LOTRO_DAT
 		bool operator ==(const SubFile &b) const;
 		bool operator !=(const SubFile &b) const;
 
+
+        static FILE_TYPE GetSubfileType(DatFile& dat, SubFile preinit_file);
+        static std::shared_ptr<SubFile> MakeSubfile(DatFile& dat, SubFile preinit_file);
+
         long long category;
 
 	protected:
-		DatFile &dat_;
+		DatFile *dat_;
         long long dictionary_offset_;
 
         long long unknown1_;

+ 2 - 2
include/SubfileData.h

@@ -10,10 +10,10 @@
 #include "BinaryData.h"
 
 namespace LOTRO_DAT {
-    struct SubfileData {
+    class SubfileData {
     public:
         SubfileData() {
-            binary_data = BinaryData();
+            binary_data = BinaryData(0);
             text_data = std::u16string();
             options = YAML::Node();
         }

+ 2 - 4
include/Subfiles/DdsSubFile.h

@@ -5,7 +5,7 @@
 #ifndef LOTRO_DAT_LIBRARY_DDSSUBFILE_H
 #define LOTRO_DAT_LIBRARY_DDSSUBFILE_H
 
-#include "SubFile.h"
+#include "../SubFile.h"
 
 namespace LOTRO_DAT {
     class DdsSubFile : public SubFile {
@@ -14,9 +14,7 @@ namespace LOTRO_DAT {
         explicit DdsSubFile(const DdsSubFile &other) = delete;
         DdsSubFile &operator =(const DdsSubFile &other) = delete;
 
-        DdsSubFile(DatFile &dat, long long dictionary_offset, long long fragments_count, long long unknown1,
-                   long long file_id, long long file_offset, long long file_size, long long timestamp,
-                   long long version, long long block_size);
+        explicit DdsSubFile(SubFile preinit_file);
 
         FILE_TYPE FileType() const override;
 

+ 2 - 4
include/Subfiles/FontSubFile.h

@@ -6,7 +6,7 @@
 #define LOTRO_DAT_LIBRARY_FONTSUBFILE_H
 
 
-#include "SubFile.h"
+#include "../SubFile.h"
 
 namespace LOTRO_DAT {
     class FontSubFile : public SubFile {
@@ -15,9 +15,7 @@ namespace LOTRO_DAT {
         explicit FontSubFile(const FontSubFile &other) = delete;
         FontSubFile &operator =(const FontSubFile &other) = delete;
 
-        FontSubFile(DatFile &dat, long long dictionary_offset, long long fragments_count, long long unknown1,
-                    long long file_id, long long file_offset, long long file_size, long long timestamp,
-                    long long version, long long block_size);
+        explicit FontSubFile(SubFile preinit_file);
 
         FILE_TYPE FileType() const override;
 

+ 2 - 4
include/Subfiles/JpgSubFile.h

@@ -6,7 +6,7 @@
 #define LOTRO_DAT_LIBRARY_JPGSUBFILE_H
 
 
-#include "SubFile.h"
+#include "../SubFile.h"
 
 namespace LOTRO_DAT {
     class JpgSubFile : public SubFile {
@@ -15,9 +15,7 @@ namespace LOTRO_DAT {
         explicit JpgSubFile(const JpgSubFile &other) = delete;
         JpgSubFile &operator =(const JpgSubFile &other) = delete;
 
-        JpgSubFile(DatFile &dat, long long dictionary_offset, long long fragments_count, long long unknown1,
-                   long long file_id, long long file_offset, long long file_size, long long timestamp,
-                   long long version, long long block_size);
+        explicit JpgSubFile(SubFile preinit_file);
 
 
         FILE_TYPE FileType() const override;

+ 2 - 4
include/Subfiles/OggSubFile.h

@@ -5,7 +5,7 @@
 #ifndef LOTRO_DAT_LIBRARY_OGGSUBFILE_H
 #define LOTRO_DAT_LIBRARY_OGGSUBFILE_H
 
-#include "SubFile.h"
+#include "../SubFile.h"
 
 namespace LOTRO_DAT {
     class OggSubFile : public SubFile {
@@ -14,9 +14,7 @@ namespace LOTRO_DAT {
         explicit OggSubFile(const OggSubFile &other) = delete;
         OggSubFile &operator =(const OggSubFile &other) = delete;
 
-        OggSubFile(DatFile &dat, long long dictionary_offset,  long long fragments_count, long long unknown1,
-                   long long file_id, long long file_offset, long long file_size, long long timestamp,
-                   long long version, long long block_size);
+        explicit OggSubFile(SubFile preinit_file);
 
         FILE_TYPE FileType() const override;
         std::string Extension() const override;

+ 6 - 9
include/Subfiles/TextSubFile.h

@@ -5,9 +5,9 @@
 #ifndef LOTRO_DAT_LIBRARY_TEXTSUBFILE_H
 #define LOTRO_DAT_LIBRARY_TEXTSUBFILE_H
 
-#include "SubFile.h"
-#include "SubfileData.h"
-#include "BinaryData.h"
+#include "../SubFile.h"
+#include "../SubfileData.h"
+#include "../BinaryData.h"
 
 #include <vector>
 #include <unordered_map>
@@ -18,8 +18,8 @@ namespace LOTRO_DAT {
         std::u16string text;
         std::string args;
 
-        bool operator < (const TextFragment &other) const {
-            return fragment_id < other.fragment_id;
+        friend bool operator < (const TextFragment &first, const TextFragment &second) {
+            return first.fragment_id < second.fragment_id;
         }
     };
 
@@ -29,10 +29,7 @@ namespace LOTRO_DAT {
         explicit TextSubFile(const TextSubFile &other) = delete;
         SubFile &operator =(const TextSubFile &other) = delete;
 
-
-        TextSubFile(DatFile &dat, long long dictionary_offset, long long fragments_count, long long unknown1,
-                    long long file_id, long long file_offset, long long file_size, long long timestamp,
-                    long long version, long long block_size);
+        explicit TextSubFile(SubFile preinit_file);
 
         FILE_TYPE FileType() const override;
 

+ 2 - 4
include/Subfiles/UnknownSubFile.h

@@ -5,7 +5,7 @@
 #ifndef LOTRO_DAT_LIBRARY_UNKNOWNSUBFILE_H
 #define LOTRO_DAT_LIBRARY_UNKNOWNSUBFILE_H
 
-#include "SubFile.h"
+#include "../SubFile.h"
 
 namespace LOTRO_DAT {
     class UnknownSubFile : public SubFile {
@@ -14,9 +14,7 @@ namespace LOTRO_DAT {
         explicit UnknownSubFile(const UnknownSubFile &other) = delete;
         SubFile &operator =(const UnknownSubFile &other) = delete;
 
-        UnknownSubFile(DatFile &dat, long long dictionary_offset, long long fragments_count, long long unknown1,
-                       long long file_id, long long file_offset, long long file_size, long long timestamp,
-                       long long version, long long block_size);
+        explicit UnknownSubFile(SubFile preinit_file);
 
         FILE_TYPE FileType() const override;
         std::string Extension() const override;

+ 2 - 4
include/Subfiles/WavSubFile.h

@@ -5,7 +5,7 @@
 #ifndef LOTRO_DAT_LIBRARY_WAVSUBFILE_H
 #define LOTRO_DAT_LIBRARY_WAVSUBFILE_H
 
-#include "SubFile.h"
+#include "../SubFile.h"
 
 namespace LOTRO_DAT {
     class WavSubFile : public SubFile {
@@ -14,9 +14,7 @@ namespace LOTRO_DAT {
         explicit WavSubFile(const WavSubFile &other) = delete;
         WavSubFile &operator =(const WavSubFile &other) = delete;
 
-        WavSubFile(DatFile &dat, long long dictionary_offset, long long fragments_count, long long unknown1,
-                   long long file_id, long long file_offset, long long file_size, long long timestamp,
-                   long long version, long long block_size);
+        explicit WavSubFile(SubFile preinit_file);
 
 
         FILE_TYPE FileType() const override;

BIN
lib/libLotroDat.dll.a


BIN
lib/libLotroDat_static.a


+ 11 - 3
src/BinaryData.cpp

@@ -7,6 +7,8 @@
 #include "EasyLogging++/easylogging++.h"
 
 #include <algorithm>
+#include <BinaryData.h>
+
 
 extern  "C++"
 {
@@ -23,6 +25,13 @@ namespace LOTRO_DAT {
         memcpy(data_, d.data_, size_);
     }
 
+    BinaryData::BinaryData(BinaryData &&other) noexcept {
+        size_ = other.size_;
+        data_ = other.data_;
+        other.size_ = 0;
+        other.data_ = nullptr;
+    }
+
     BinaryData::BinaryData(const char* data, unsigned int size) {
         size_ = size;
         data_ = new unsigned char[size_];
@@ -59,7 +68,7 @@ namespace LOTRO_DAT {
     // Translates T bytes from data into number using UTF-16LE encoding of the .dat file
     template<unsigned int T>
     long long BinaryData::ToNumber(const long long &pos) const {
-        long long ans = 0;
+        unsigned long long ans = 0;
 
         if (pos + T > size_) {
             LOG(ERROR) << "Reading " << T << " bytes from " << pos << " offset with BinaryData size " << size_
@@ -68,7 +77,7 @@ namespace LOTRO_DAT {
         }
 
         for (int i = T - 1; i >= 0; i--)
-            ans = ((ans << 8ll) | data_[pos + i]);
+            ans = ((ans << 8ull) | data_[pos + i]);
 
         return ans;
     }
@@ -266,7 +275,6 @@ namespace LOTRO_DAT {
         memcpy(data_ + append_offset, b.data_, b.size_);
     }
 
-
     template long long BinaryData::ToNumber<1>(long long const&) const;
     template long long BinaryData::ToNumber<2>(long long const&) const;
     template long long BinaryData::ToNumber<4>(long long const&) const;

+ 100 - 1203
src/DatFile.cpp

@@ -2,27 +2,20 @@
 // Created by Иван_Архипов on 31.10.2017.
 //
 #define NOMINMAX
-#include "DatFile.h"
-#include "BinaryData.h"
-
-#include "SubDirectory.h"
-#include "SubFile.h"
-#include "SubfileData.h"
 
+#include <DatFile.h>
+#include <DatOperationResult.h>
 #include <EasyLogging++/easylogging++.h>
-//#include <unistd.h>
-#include <algorithm>
-#include <iterator>
-#include <locale>
-
-#define ELPP_FEATURE_CRASH_LOG
-INITIALIZE_EASYLOGGINGPP
 
 #ifdef WIN32
 #define fseek _fseeki64
 #define ftell _ftelli64
 #endif
 
+#define VERSION "7.1.0"
+
+INITIALIZE_EASYLOGGINGPP
+
 extern "C++"
 {
 namespace LOTRO_DAT {
@@ -32,1262 +25,166 @@ namespace LOTRO_DAT {
     //------------------------------------------------//
 
     DatFile::DatFile() {
-        dat_state_ = CLOSED;
-        root_directory_ = nullptr;
-        file_handler_ = nullptr;
-        free_buffered_size_ = 0;
-
-        orig_dict_.clear();
-        patch_dict_.clear();
-        dictionary_.clear();
-
         el::Configurations defaultConf;
+        el::Loggers::addFlag(el::LoggingFlag::LogDetailedCrashReason);
+        el::Loggers::addFlag(el::LoggingFlag::ImmediateFlush);
+        el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck);
+
         defaultConf.setToDefault();
-        defaultConf.setGlobally(el::ConfigurationType::Format,
-                                "%datetime %level %fbase (line %line) : %msg (function: %func)");
+        defaultConf.setGlobally(el::ConfigurationType::Format, "%datetime %level : %msg (function: %func)");
+
+
         defaultConf.setGlobally(el::ConfigurationType::ToFile, "true");
         defaultConf.setGlobally(el::ConfigurationType::Filename, "dat_library.log");
         defaultConf.setGlobally(el::ConfigurationType::ToStandardOutput, "false");
         defaultConf.setGlobally(el::ConfigurationType::PerformanceTracking, "true");
         defaultConf.setGlobally(el::ConfigurationType::MaxLogFileSize, "5242880"); // 5MB
-        defaultConf.setGlobally(el::ConfigurationType::LogFlushThreshold, "1"); // Flush after every one log
 
         defaultConf.set(el::Level::Debug, el::ConfigurationType::Enabled, "false");
         defaultConf.set(el::Level::Debug, el::ConfigurationType::Filename, "dat_library_debug.log");
 
         el::Loggers::reconfigureAllLoggers(defaultConf);
+
         LOG(INFO) << "==================================================================";
         LOG(INFO) << "Starting new DatFile class instance";
-    }
-
-    DAT_RESULT DatFile::InitDatFile(const std::string &filename, int dat_id) {
-        LOG(DEBUG) << "Started initialisation of DatFile " << filename;
-        if (dat_state_ != CLOSED && filename == filename_) {
-            LOG(DEBUG) << "Trying to reopen the same file: " << filename << ". Doing nothing.";
-            return SUCCESS;
-        }
-
-        if (dat_state_ != CLOSED && filename != filename_) {
-            LOG(DEBUG) << "DatFile wasn't closed yet. Closing in order to reopen.";
-            if (CloseDatFile() != SUCCESS) {
-                LOG(ERROR) << "Unable to perform CloseDatFile()! Aborting initialization!";
-                return FAILED;
-            }
-        }
-
-        dat_id_ = dat_id;
-        dat_state_ = CLOSED;
-        current_locale_ = ORIGINAL;
-        root_directory_ = nullptr;
-        file_handler_ = nullptr;
-        free_buffered_size_ = 0;
-        filename_ = "none";
-
-        DAT_RESULT result;
-        DAT_RESULT return_value = SUCCESS;
-
-        LOG(INFO) << "Opening .dat file " << filename;
-        result = OpenDatFile(filename.c_str());
-        if (result != SUCCESS) {
-            LOG(ERROR) << "Unable to perform opening file. Aborting.";
-            CloseDatFile();
-            return result;
-        }
-        return_value = std::max(return_value, result);
-
-        LOG(INFO) << "Starting ReadSuperBlock";
-
-        result = ReadSuperBlock();
-        if (result <= 0) {
-            LOG(ERROR) << "Unable to read super block. Aborting.";
-            CloseDatFile();
-            return result;
-        }
-        return_value = std::max(return_value, result);
-
-        LOG(INFO) << "Starting MakeDirectories";
-
-        result = MakeDirectories();
-        if (result <= 0) {
-            LOG(ERROR) << "Unable to make directories. Aborting.";
-            CloseDatFile();
-            return result;
-        }
-        return_value = std::max(return_value, result);
-
-        LOG(INFO) << "Starting MakeDictionary";
-
-        result = MakeDictionary();
-        if (result <= 0) {
-            LOG(ERROR) << "Unable to make dictionary. Aborting.";
-            CloseDatFile();
-            return result;
-        }
-        return_value = std::max(return_value, result);
-
-        LOG(INFO) << "Starting InitLocales";
-
-        result = InitLocales();
-        if (result <= 0) {
-            LOG(ERROR) << "Unable to initialize locales. Aborting.";
-            CloseDatFile();
-            return result;
-        }
-        return_value = std::max(return_value, result);
-
-        LOG(INFO) << "File " << filename << " opened successfully!";
-        filename_ = filename;
-        dat_state_ = READY;
-
-        LOG(INFO) << "Making last preparations...";
-        return_value = std::max(return_value, result);
-
-        PerformDictionaryCheck();
-
-        if (return_value >= 2) {
-            LOG(WARNING) << "Dat file could be corrupted. Trying to delete corrupted dictionary rows";
-            if (RepairDatFile() != SUCCESS)
-                return CRITICAL_DAT_ERROR;
-        }
-
-        if (CheckIfUpdatedByGame()) {
-            LOG(INFO) << ".dat file was updated by game! Need to reinitialize files and directories!";
-            CloseDatFile();
-            InitDatFile(filename, dat_id);
-        }
-
-        std::cout << "Visited subdirs: " << SubDirectory::visited_subdirectories_.size() << std::endl;
-        std::cout << "Visited files: " << SubDirectory::visited_subfiles_.size() << std::endl;
-        dat_without_patches_ = CheckIfNotPatched();
-        LOG(INFO) << "Preparations made successfully! Init return value = " << return_value;
-        return return_value;
-    }
 
-    DAT_RESULT DatFile::OpenDatFile(const char *dat_name) {
-        LOG(DEBUG) << "Started opening DatFile";
-        if (dat_state_ != CLOSED) {
-            CloseDatFile();
-        }
-
-        file_handler_ = fopen(dat_name, "r+b");
-
-        if (file_handler_ == nullptr) {
-            LOG(ERROR) << "Unable to open file " << dat_name;
-            return NO_FILE_ERROR;
-        }
-
-        fseek(file_handler_, 0, SEEK_END);
-        actual_dat_size_ = ftell(file_handler_);
-        fseek(file_handler_, 0, SEEK_SET);
-
-        dat_state_ = SUCCESS_OPENED;
-        LOG(DEBUG) << "Successfully opened DatFile";
-        return SUCCESS;
-    }
-
-    DAT_RESULT DatFile::ReadSuperBlock() {
-        LOG(DEBUG) << "Started reading superblock";
-        if (dat_state_ != SUCCESS_OPENED) {
-            LOG(ERROR) << "Dat state isn't SUCCESS_OPENED. Cannot perform extraction.";
-            return INCORRECT_STATE_ERROR;
-        }
-
-        BinaryData data(1024);
-        ReadData(data, 1024);
-
-        constant1_ = data.ToNumber<4>(0x100);
-        constant2_ = data.ToNumber<4>(0x140);
-        version1_ = data.ToNumber<4>(0x14C);
-        file_size_ = data.ToNumber<4>(0x148);
-        version2_ = data.ToNumber<4>(0x150);
-        fragmentation_journal_offset_ = data.ToNumber<4>(0x154);
-        fragmentation_journal_end_ = data.ToNumber<4>(0x158);
-        fragmentation_journal_size_ = data.ToNumber<4>(0x15C);
-        root_directory_offset_ = data.ToNumber<4>(0x160);
-        free_dat_size_ = data.ToNumber<4>(0x19C);
-
-        if (constant1_ != 0x4C5000) {
-            LOG(ERROR) << "variable at position 0x100 is not equal to .dat file constant!";
-            return INCORRECT_SUPERBLOCK_ERROR;
-        }
-        if (constant2_ != 0x5442) {
-            LOG(ERROR) << "variable at position 0x140 is not equal to .dat file constant!";
-            return INCORRECT_SUPERBLOCK_ERROR;
-        }
-
-        if (file_size_ != actual_dat_size_) {
-            LOG(ERROR) << "variable at 0x148 position is not equal to .dat file size!";
-            //return CORRUPTED_FILE_WARNING;
-        }
-
-        dat_state_ = SUCCESS_SUPERBLOCK;
-        LOG(DEBUG) << "Superblock read successfully";
-        return SUCCESS;
+        io_ = std::make_unique<DatIO>(this);
+        fileSystem_ = std::make_unique<DatFileSystem>(this);
+        localeManager_ = std::make_unique<DatLocaleManager>(this);
+        exporter_ = std::make_unique<DatExporter>(this);
+        patcher_ = std::make_unique<DatPatcher>(this);
+        backupManager_ = std::make_unique<DatBackupManager>(this);
+        status_ = std::make_unique<DatStatus>(this);
     }
 
-    DAT_RESULT DatFile::MakeDirectories() {
-        LOG(DEBUG) << "Started making directories";
-        if (dat_state_ != SUCCESS_SUPERBLOCK) {
-            LOG(ERROR) << "Dat state isn't SUCCESS_SUPERBLOCK. Cannot make directories.";
-            return INCORRECT_STATE_ERROR;
-        }
-
-        root_directory_ = std::make_shared<SubDirectory>((unsigned) root_directory_offset_, *this);
-        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();
-        }
-
-        dat_state_ = SUCCESS_DIRECTORIES;
-
-        LOG(DEBUG) << "Directories made successfully";
-        return SUCCESS;
-    }
-
-    DAT_RESULT DatFile::MakeDictionary() {
-        LOG(DEBUG) << "Started making dictionary";
-        if (dat_state_ != SUCCESS_DIRECTORIES) {
-            LOG(ERROR) << "Dat state isn't SUCCESS_DIRECTORIES. Cannot make directories.";
-            return INCORRECT_STATE_ERROR;
-        }
-
-        if (root_directory_ == nullptr) {
-            LOG(ERROR) << "root_directory is nullptr!!";
-            return INIT_ERROR;
-        }
-
-        root_directory_->MakeDictionary(dictionary_);
-        dat_state_ = SUCCESS_DICTIONARY;
-        LOG(DEBUG) << "Dictionary made successfull";
-        return SUCCESS;
-    }
-
-    DAT_RESULT DatFile::InitLocales() {
-        LOG(INFO) << "Initialising locales...";
-        BinaryData dicts_data(4);
-
-        ReadData(dicts_data, 4, 300);
-        long long dict_offset = dicts_data.ToNumber<4>(0);
-
-        if (dict_offset == 0 || dict_offset + 8 >= actual_dat_size_) {
-            LOG(INFO) << "Dictionary offset is empty or incorrect. Passing.";
-            return SUCCESS;
-        }
-
-        ReadData(dicts_data, 4, dict_offset);
-        long long dict_size = dicts_data.ToNumber<4>(0);
-
-        ReadData(dicts_data, 4, dict_offset + 4);
-        long long dict_version = dicts_data.ToNumber<4>(0);
-
-        ReadData(dicts_data, 4, dict_offset + 8);
-        file_size_ = dicts_data.ToNumber<4>(0);
-
-        LOG(INFO) << "Dictionary size is " << dict_size << ". Version is " << dict_version << ". Localed .dat size = " << file_size_;
-
-        if (dict_version != 101) {
-            LOG(WARNING) << "DICTIONARY IS OLD!!!";
-            orig_dict_.clear();
-            patch_dict_.clear();
-            WriteData(BinaryData::FromNumber<4>(0), 4, 300);
-            dat_state_ = UPDATED;
-            dat_without_patches_ = true;
-            return SUCCESS;
-        }
-
-        dicts_data = BinaryData((unsigned)dict_size);
-        ReadData(dicts_data, dict_size, dict_offset + 12);
-
-        if (dicts_data.size() < 15) {
-            LOG(ERROR) << "Incorrect dictionary. Passing without it.";
-            orig_dict_.clear();
-            patch_dict_.clear();
-            WriteData(BinaryData::FromNumber<4>(0), 4, 300);
-            dat_state_ = UPDATED;
-            dat_without_patches_ = true;
-            return SUCCESS;
-        }
-
-        BinaryData hi_data = dicts_data.CutData(0, 15) + BinaryData("\0", 1);
-        std::string hi = std::string((char *) (hi_data.data()));
-        LOG(DEBUG) << "hi info is " << hi;
-
-        if (hi != "Hi from Gi1dor!") {
-            LOG(WARNING) << "Didn't receive 'hi' from Gi1dor... Initialising locale dicts as empty";
-            LOG(INFO) << "Could't init locales' file... Continuing without them";
-            return SUCCESS;
-        }
-
-        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;
-        LOG(DEBUG) << "current locale:" << locale;
-
-        if (locale != "PATC" && locale != "ORIG") {
-            LOG(WARNING) << "Incorrect locale... Initialising locale dicts as empty";
-            LOG(INFO) << "Could't recognize locale... Continuing without locales";
-            return SUCCESS;
-        }
-        current_locale_ = (locale == "PATC" ? PATCHED : ORIGINAL);
-
-        // 15 bytes for "Hi from Gi1dor"
-        // 4 bytes for LOCALE
-        // 4 bytes for orig_dict.size()
-        // (32 + 4) * orig_dict.size() bytes for orig_dict data
-        // 4 bytes for patch_dict.size()
-        // (32 + 4) * patch_dict.size() bytes for patch_dict data
-        // 4 bytes for inactive_categories dict
-        // 4 * inactive_categories.size() bytes for inactive_categories data
-
-        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 = std::make_shared<SubFile>(*this, dicts_data.CutData(offset, offset + 32));
-            orig_dict_[file->file_id()] = file;
-            offset += 32;
-            orig_dict_[file->file_id()]->category = dicts_data.ToNumber<4>(offset);
-            offset += 4;
-
-            if (orig_dict_[file->file_id()]->category == 0)
-                LOG(DEBUG) << "file category is undefined (0)!";
-        }
-
-        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 = std::make_shared<SubFile>(*this, dicts_data.CutData(offset, offset + 32));
-            patch_dict_[file->file_id()] = file;
-            offset += 32;
-            patch_dict_[file->file_id()]->category = dicts_data.ToNumber<4>(offset);
-            offset += 4;
-            if (patch_dict_[file->file_id()]->category == 0)
-                LOG(DEBUG) << "file category is undefined (0)!";
-
-        }
-
-        size_t active_patches_dict_size = size_t(dicts_data.CutData(offset, offset + 4).ToNumber<4>(0));
-        offset += 4;
-        for (size_t i = 0; i < active_patches_dict_size; i++) {
-            inactive_categories.insert(dicts_data.ToNumber<4>(offset));
-            offset += 4;
-        }
-
-        LOG(INFO) << "There are " << patch_dict_.size() << " files in patch locale dictionary";
-        LOG(INFO) << "There are " << orig_dict_.size() << " files in original locale dictionary";
-        std::string inactive_cat_s;
-        for (auto i : inactive_categories) {
-            inactive_cat_s += std::to_string(i) + " ";
-        }
-        LOG(INFO) << "Unactive patches now: " << inactive_cat_s;
-        LOG(INFO) << "Finished initialising locales";
-        return SUCCESS;
-    }
-
-    DAT_RESULT DatFile::PerformDictionaryCheck() {
-        for (const auto& mpair : dictionary_) {
-            auto file = mpair.second;
-            auto file_id = mpair.first;
-            if (CorrectSubfile(file))
-                continue;
-
-            if (current_locale_ == PATCHED && orig_dict_.count(file_id) > 0) {
-                LOG(WARNING) << "Potential incorrect patched version of file " << file_id << ". Switching to original.";
-                dictionary_[file_id] = orig_dict_[file_id];
-            }
-
-            if (!CorrectSubfile(file)) {
-                LOG(ERROR) << "Incorrect file " << file_id << ". It's offset is said as " << file->file_offset()
-                           << ". Erasing it from dictionary.";
-                dictionary_.erase(file_id);
-            }
-        }
-        return SUCCESS;
-    }
-
-    //------------------------------------------------//
-    // CLOSE SECTION
-    //------------------------------------------------//
 
     DatFile::~DatFile() {
-        CloseDatFile();
-    }
-
-    DAT_RESULT DatFile::CloseDatFile() {
-        LOG(INFO) << "Closing DatFile";
-        if (dat_state_ == CLOSED) {
-            LOG(INFO) << "DatFile is already closed. Nothing to do";
-            return SUCCESS;
-        }
-
-        // Committing changes and updating/writing locales and header info
-
-        if (!pending_dictionary_.empty() || dat_state_ == UPDATED) {
-            CommitLocales();
-            CommitDirectories();
-            //ModifyFragmentationJournal();
-            //free_dat_size_ = 128248;
-            //fragmentation_journal_end_ = 0;
-            //fragmentation_journal_size_ = 1;
-            //UpdateHeader();
-        }
-
-        current_locale_ = ORIGINAL;
-
-        if (file_handler_ != nullptr) {
-            fclose(file_handler_);
-        }
-        SubDirectory::visited_subdirectories_.clear();
-        //truncate64(filename_.c_str(), file_size_);
-
-        free_buffered_size_ = 0;
-
-        filename_ = "none";
-
-        orig_dict_.clear();
-        patch_dict_.clear();
-        pending_patch_.clear();
-        inactive_categories.clear();
-
-        file_handler_ = nullptr;
-        root_directory_ = nullptr;
-
-
-        pending_dictionary_.clear();
-        dictionary_.clear();
-
-        constant1_ = 0;
-        constant2_ = 0;
-        file_size_ = 0;
-        version1_ = 0;
-        version2_ = 0;
-        fragmentation_journal_size_ = 0;
-        fragmentation_journal_end_ = 0;
-        root_directory_offset_ = 0;
-        fragmentation_journal_offset_ = 0;
-
-        dat_state_ = CLOSED;
-
-        dat_id_ = -1;
-
-
-        LOG(INFO) << "File closed successfully.";
-        return SUCCESS;
-    }
-
-    DAT_RESULT DatFile::CommitLocales() {
-        LOG(INFO) << "Committing locales...";
-        // 15 bytes for "Hi from Gi1dor"
-        // 4 bytes for LOCALE
-        // 4 bytes for orig_dict.size()
-        // (32 + 4) * orig_dict.size() bytes for orig_dict data
-        // 4 bytes for patch_dict.size()
-        // (32 + 4) * patch_dict.size() bytes for patch_dict data
-        // 4 bytes for inactive_categories list
-        // 4 * inactive_categories.size() bytes for inactive_categories data
-
-        BinaryData binary_data = BinaryData(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("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);
-        ReadData(dicts_data, 4, 300);
-        long long dict_offset = dicts_data.ToNumber<4>(0);
-        ReadData(dicts_data, 4, dict_offset);
-        long long dict_size = dicts_data.ToNumber<4>(0);
-
-        if (binary_data.size() > dict_size || dict_offset == 0) {
-            WriteData(BinaryData::FromNumber<4>(file_size_), 4, 300);
-
-            WriteData(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)), 4, file_size_);
-            WriteData(BinaryData::FromNumber<4>(101), 4, file_size_ + 4);
-            WriteData(BinaryData::FromNumber<4>(file_size_ + binary_data.size() + 12 + 20 * 1024 * 1024), 4, file_size_ + 8); // Writing current file size;
-
-            WriteData(binary_data, binary_data.size(), file_size_ + 12);
-            file_size_ += binary_data.size() + 12;
-
-            // Adding space for 25 megabytes locales file in total.
-            BinaryData nulls(unsigned(20 * 1024 * 1024));
-            WriteData(nulls, nulls.size(), file_size_);
-            file_size_ += nulls.size();
-
-        } else {
-            WriteData(BinaryData::FromNumber<4>(std::max(binary_data.size(), 20u * 1024u * 1024u)), 4, dict_offset);
-            WriteData(BinaryData::FromNumber<4>(101), 4, dict_offset + 4);
-            WriteData(BinaryData::FromNumber<4>(file_size_), 4, dict_offset + 8); // Writing current file size;
-            WriteData(binary_data, binary_data.size(), dict_offset + 12);
-        }
-        LOG(INFO) << "Locales commited successfully";
-        return SUCCESS;
-    }
-
-    DAT_RESULT DatFile::CommitDirectories() {
-        for (auto file_id : pending_dictionary_) {
-            if (dictionary_[file_id] == nullptr || !CorrectSubfile(dictionary_[file_id]))
-                continue;
-            WriteData(dictionary_[file_id]->MakeHeaderData(), 32, dictionary_[file_id]->dictionary_offset());
-        }
-        pending_dictionary_.clear();
-        return SUCCESS;
-    }
-
-    DAT_RESULT DatFile::ModifyFragmentationJournal() {
-        if (fragmentation_journal_size_ == 0)
-            return SUCCESS;
-        LOG(DEBUG) << "Modifying fragmentation journal";
-        BinaryData data(4);
-        ReadData(data, 4, fragmentation_journal_offset_ + 8 * fragmentation_journal_size_);
-        LOG(INFO) << "FREE_SIZE BLOCK = " << data.ToNumber<4>(0);
-
-        long long free_size = data.ToNumber<4>(0);
-        long long free_offset = file_size_;
-
-        BinaryData nulldata = BinaryData(unsigned(free_size));
-        WriteData(nulldata, nulldata.size(), file_size_);
-        file_size_ += nulldata.size();
-
-        WriteData(BinaryData::FromNumber<4>(free_size), 4, fragmentation_journal_offset_ + 8 * fragmentation_journal_size_);
-        WriteData(BinaryData::FromNumber<4>(free_offset), 4, fragmentation_journal_offset_ + 8 * fragmentation_journal_size_ + 4);
-
-        //nulldata = BinaryData(8);
-        //WriteData(nulldata, nulldata.size(), fragmentation_journal_offset_ + 16);
-        LOG(DEBUG) << "Finished modifying fragmentation journal";
-        return SUCCESS;
-    }
-
-    DAT_RESULT DatFile::UpdateHeader() {
-        LOG(DEBUG) << "Updating header";
-        WriteData(BinaryData::FromNumber<4>(constant1_), 4, 0x100);
-        WriteData(BinaryData::FromNumber<4>(constant2_), 4, 0x140);
-        //WriteData(BinaryData::FromNumber<4>(    0     ), 4, 0x144);
-        WriteData(BinaryData::FromNumber<4>(file_size_), 4, 0x148);
-        WriteData(BinaryData::FromNumber<4>(version1_ ), 4, 0x14C);
-        WriteData(BinaryData::FromNumber<4>(version2_ ), 4, 0x150);
-        WriteData(BinaryData::FromNumber<4>(fragmentation_journal_offset_), 4, 0x154);
-        WriteData(BinaryData::FromNumber<4>(fragmentation_journal_end_), 4, 0x158);
-        WriteData(BinaryData::FromNumber<4>(fragmentation_journal_size_), 4, 0x15C);
-        WriteData(BinaryData::FromNumber<4>(root_directory_offset_), 4, 0x160);
-        WriteData(BinaryData::FromNumber<4>(free_dat_size_), 4, 0x19C);
-        LOG(DEBUG) << "Finished updating header";
-        return SUCCESS;
-    }
-
-    DAT_RESULT DatFile::RepairDatFile() {
-        for (const auto& file : dictionary_) {
-            auto subfile = file.second;
-            auto file_id = file.first;
-
-            if (CorrectSubfile(subfile))
-                continue;
-
-            if (orig_dict_.count(file_id) == 0 || subfile->file_offset() == orig_dict_[file_id]->file_offset())
-                return CRITICAL_DAT_ERROR;
-
-            *dictionary_[file_id] = *orig_dict_[file_id];
-            patch_dict_.erase(file_id);
-            orig_dict_.erase(file_id);
-        }
-        return SUCCESS;
+        if (Initialized())
+            Deinitialize();
     }
 
-    //------------------------------------------------//
-    // DAT INFO SECTION
-    //------------------------------------------------//
-
-    DAT_STATE DatFile::DatFileState() const {
-        return dat_state_;
+    LOTRO_DAT::DatLocaleManager &DatFile::GetLocaleManager() {
+        return *localeManager_;
     }
 
-    long long DatFile::files_number() const {
-        return dictionary_.size();
+    DatExporter &DatFile::GetExporter() {
+        return *exporter_;
     }
 
-    //------------------------------------------------//
-    // EXTRACT SECTION
-    //------------------------------------------------//
-
-    DAT_RESULT DatFile::ExtractFile(long long file_id, const std::string &path) {
-        LOG(DEBUG) << "Extracting file " << file_id << " to path " << path;
-        if (dat_state_ < READY) {
-            LOG(ERROR) << "Dat state isn't READY. Cannot perform extraction.";
-            return INCORRECT_STATE_ERROR;
-        }
-        BinaryData file_data = GetFileData(dictionary_[file_id], 8);
-
-        if (file_data.size() == 0) {
-            LOG(ERROR) << "File data is empty. Aborting extraction.";
-            return NO_FILE_ERROR;
-        }
-
-        SubfileData export_data = dictionary_[file_id]->PrepareForExport(file_data);
-
-        if (export_data.Empty()) {
-            LOG(ERROR) << "Export data is empty. Aborting extraction.";
-            return NO_FILE_ERROR;
-        }
-
-        if (export_data.binary_data.WriteToFile(path + export_data.options["ext"].as<std::string>()) != SUCCESS) {
-            LOG(ERROR) << "Cannot write to file.";
-            return WRITE_TO_FILE_ERROR;
-        }
-        LOG(DEBUG) << "File " << file_id << " extracted successfully";
-        return SUCCESS;
+    LOTRO_DAT::DatPatcher &DatFile::GetPatcher() {
+        return *patcher_;
     }
 
-    DAT_RESULT DatFile::ExtractFile(long long file_id, Database *db) {
-        LOG(DEBUG) << "Extracting file " << file_id << " to database.";
-
-        if (dat_state_ < READY) {
-            LOG(ERROR) << "Dat state isn't READY. Cannot perform extraction.";
-            return INCORRECT_STATE_ERROR;
-        }
-
-        BinaryData file_data = GetFileData(dictionary_[file_id], 8);
-
-        if (file_data.Empty()) {
-            LOG(WARNING) << "File with id " << dictionary_[file_id]->file_id() << " is empty. Passing it.";
-            return SUCCESS;
-        }
-
-        SubfileData export_data;
-        export_data = dictionary_[file_id]->PrepareForExport(file_data);
-        export_data.options["did"] = dat_id_;
-
-        if (export_data == SubfileData()) {
-            LOG(WARNING) << "File with id " << dictionary_[file_id]->file_id() << " is empty or incorrect.";
-            return SUCCESS;
-        }
-
-        try {
-            db->PushFile(export_data);
-        } catch (std::exception &e) {
-            LOG(ERROR) << "Caught " << e.what() << " exception.";
-            return FAILED;
-        }
-        LOG(DEBUG) << "File " << file_id << " extracted successfully";
-        return SUCCESS;
+    DatBackupManager &DatFile::GetBackupManager() {
+        return *backupManager_;
     }
 
-    int DatFile::ExtractAllFilesByType(FILE_TYPE type, std::string path) {
-        LOG(INFO) << "Extracting all files to path " << path;
-        if (dat_state_ < READY) {
-            LOG(ERROR) << "Dat state isn't READY. Cannot perform extraction.";
-            return INCORRECT_STATE_ERROR;
-        }
-
-        int success = 0;
-        for (const auto& i : dictionary_) {
-            FILE_TYPE file_type = i.second->FileType();
-            if (file_type == type) {
-                success += (ExtractFile(i.second->file_id(), (path + std::to_string(i.second->file_id()))) == SUCCESS
-                            ? 1 : 0);
-            }
-        }
-        LOG(INFO) << "Successfully extracted " << success << " files";
-        return success;
-    }
-
-    int DatFile::ExtractAllFilesByType(FILE_TYPE type, Database *db) {
-        LOG(INFO) << "Extracting all files to database...";
-
-        if (dat_state_ < READY) {
-            LOG(ERROR) << "Dat state isn't READY. Cannot perform extraction.";
-            return INCORRECT_STATE_ERROR;
-        }
-
-        int success = 0;
-        for (const auto& i : dictionary_) {
-            FILE_TYPE file_type = i.second->FileType();
-            if (file_type == type) {
-                success += (ExtractFile(i.second->file_id(), db) == SUCCESS ? 1 : 0);
-            }
-        }
-        LOG(INFO) << "Extracted " << success << " files";
-        return success;
-    }
-
-    //------------------------------------------------//
-    // PATCH SECTION
-    //------------------------------------------------//
-
-    DAT_RESULT DatFile::PatchFile(const SubfileData &data) {
-        LOG(DEBUG) << "Patching file with id = " << data.options["fid"].as<long long>() << ".";
-        actual_dat_size_ = std::max(file_size_, actual_dat_size_);
-
-        if (!dat_without_patches_) {
-            file_size_ = actual_dat_size_;
-        }
-
-        if (dat_state_ < READY) {
-            LOG(ERROR) << "Dat state isn't READY. Cannot patch.";
-            return INCORRECT_STATE_ERROR;
-        }
-
-        auto file_id = data.options["fid"].as<long long>();
-
-        if (dictionary_.count(file_id) == 0) {
-            LOG(ERROR) << "Cannot patch file - there is no file in dictionary with file_id = " << file_id;
-            return NO_FILE_ERROR;
-        }
-        std::shared_ptr<SubFile> file = dictionary_[file_id];
-
-//        if (!CorrectSubfile(file)) {
-//            if (current_locale_ == PATCHED && patch_dict_.count(file_id) > 0) {
-//                LOG(WARNING) << "Patched subfile header with id = " << file->file_id() << " differs from original version...";
-//            } else {
-//                LOG(ERROR) << "Incorrect subfile with id " << file->file_id()
-//                           << " (headers do not match). Cannot patch it";
-//                return FAILED;
-//            }
-//        }
-
-        // If file has inactive category, then we should set it to patched state in order to commit patch and
-        // then in ApplyFilePatch(), if new category is still inactive, return dictionary to its original state;
-
-        if (inactive_categories.count(file->category) != 0 && patch_dict_.count(file_id) != 0 && file_id != 2013266257) {
-            *dictionary_[file_id] = *patch_dict_[file_id];
-        }
-
-        if (data.options["cat"].IsDefined()) {
-            file->category = data.options["cat"].as<long long>();
-        } else {
-            file->category = 1;
-        }
-
-        BinaryData old_data = GetFileData(orig_dict_.count(file->file_id()) == 0 ? file : orig_dict_[file->file_id_]);
-        if (old_data.Empty()) {
-            LOG(ERROR) << "GetFileData returned empty data. Aborting.";
-            return DAT_PATCH_FILE_ERROR;
-        }
-
-        BinaryData patch_data = file->MakeForImport(old_data, data);
-        DAT_RESULT result = ApplyFilePatch(file, patch_data);
-        if (result != SUCCESS)
-            return result;
-
-        LOG(DEBUG) << "Patched successfully file " << data.options["fid"].as<long long>() << ".";
-        return SUCCESS;
+    DatIO &DatFile::GetIO() {
+        return *io_;
     }
 
-    DAT_RESULT DatFile::PatchAllDatabase(Database *db) {
-        LOG(INFO) << "Patching all database";
-        if (dat_state_ < READY) {
-            LOG(ERROR) << "Dat state isn't READY. Cannot patch.";
-            return INCORRECT_STATE_ERROR;
-        }
-
-        SubfileData data;
-        data = db->GetNextFile();
-
-        while (!data.Empty()) {
-            DAT_RESULT result = PatchFile(data);
-            if (result != SUCCESS)
-                LOG(ERROR) << "Cannot patch file " << data.options["fid"].as<long long>() << " continuing";
-            data = db->GetNextFile();
-        }
-        LOG(INFO) << "Successfully patched whole database";
-        return SUCCESS;
+    DatFileSystem &DatFile::GetFileSystem() {
+        return *fileSystem_;
     }
 
-    DAT_RESULT DatFile::WriteUnorderedDictionary(std::string path) const {
-        LOG(INFO) << "Writing unordered dictionary to " << path << "dict.txt";
-        FILE *f = nullptr;
-        fopen_s(&f, (path + "dict.txt").c_str(), "w");
-
-        if (f == nullptr) {
-            LOG(ERROR) << "Cannot open file " << path + "dict.txt";
-            return WRITE_TO_FILE_ERROR;
-        }
-
-        fprintf(f, "unk1 file_id offset size1 timestamp version size2 unknown2 type\n");
-        for (const auto& i : dictionary_) {
-            fprintf(f, "%lld %lld %lld %lld %lld %lld %lld %lld %s\n", i.second->unknown1(), i.second->file_id(),
-                    i.second->file_offset(), i.second->file_size(), i.second->timestamp(), i.second->version(),
-                    i.second->block_size(), i.second->unknown2(), i.second->Extension().c_str());
-        }
-        fclose(f);
-        LOG(INFO) << "Unordered dictionary was written successfully to " << path << "dict.txt";
-        return SUCCESS;
+    DatStatus &DatFile::GetStatusModule() {
+        return *status_;
     }
 
-    DAT_RESULT DatFile::ApplyFilePatch(std::shared_ptr<SubFile> file, BinaryData &data) {
-        LOG(DEBUG) << "Applying " << file->file_id() << " patch.";
-
-//        if (patch_dict_.size() == 0 && pending_dictionary_.size() == 0) {
-//            BinaryData nulls(50 * 1024 * 1024);
-//            WriteData(nulls, nulls.size(), file_size_);
-//            file_size_ += 50 * 1024 * 1024;
-//        }
-
-        if (data.Empty()) {
-            LOG(ERROR) << "Error caused during making file for import. Cannot patch file " << file->file_id();
-            return FAILED;
-        }
-
-        auto file_id = file->file_id();
-
-        if (current_locale() != PATCHED && file_id != 2013266257) {
-            LOG(INFO) << "Changing locale to PATCHED(RU) in order to patch file";
-            SetLocale(PATCHED);
-        }
-
-        dat_state_ = UPDATED;
-
-        if (orig_dict_.count(file_id) == 0 && file_id != 2013266257) {
-            orig_dict_[file_id] = std::make_shared<SubFile>(*this, file->MakeHeaderData());
-        }
-
-
-        if ((patch_dict_.count(file_id) == 0 && file_id != 2013266257) || data.size() > file->block_size()
-            || file->file_size() + 8 > file->block_size()) {
-
-            file->file_offset_ = file_size_;
-            file->block_size_ = std::max((long long)data.size(), file->block_size_);
-
-            free_buffered_size_ = std::max(0ll, free_buffered_size_ - file->block_size_ - 8);
-            AddBufferedSize();
-
-            this->file_size_ += file->block_size_ + 8;
-        }
-
-        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(ERROR) << "Created data's file_id doesn't match to original! Patch wasn't written to .dat file";
-            return INCORRECT_PATCH_FILE;
-        }
-
-        //data.ProtectData();
-        //BinaryData data1(data.size());
-        WriteData(data, data.size(), file->file_offset());
-        //data.DeprotectData();
-
-        patch_dict_.erase(file_id); // Удалили старое значение в русском словаре
-        if (file_id != 2013266257) {
-            patch_dict_[file_id] = std::make_shared<SubFile>(*this, file->MakeHeaderData()); // Создали новое значение
-        }
+    DatOperationResult<> DatFile::Initialise(const std::string &filename, long long dat_id) {
+        status_->SetStatus(DatStatus::E_INITIALISING);
 
-        // If category is forbidden, then return file header data to original state
-        if (inactive_categories.count(file->category) != 0) {
-            file->file_offset_ = orig_dict_[file_id]->file_offset_;
-            file->file_size_ = orig_dict_[file_id]->file_size_;
-            file->block_size_ = orig_dict_[file_id]->block_size_;
-            file->timestamp_ = orig_dict_[file_id]->timestamp_;
-            file->version_ = orig_dict_[file_id]->version_;
-        }
-
-        if (orig_dict_.count(file_id) != 0 && file_id != 2013266257)
-            orig_dict_[file_id]->category = file->category;
-        if (patch_dict_.count(file_id) != 0 && file_id != 2013266257)
-            patch_dict_[file_id]->category = file->category;
-
-
-        // Applying file info in directory
-        pending_dictionary_.insert(file_id);
-
-        LOG(DEBUG) << "Successfully applied file " << file->file_id() << " patch.";
-        return SUCCESS;
-    }
+        dat_id_ = dat_id;
 
-    //------------------------------------------------//
-    // INPUT-OUTPUT SECTION
-    //------------------------------------------------//
+        if (initialized_ && io_->GetFilename().result == SUCCESS && io_->GetFilename().value == filename)
+            return DatOperationResult<>();
 
-    BinaryData DatFile::GetFileData(const std::shared_ptr<SubFile>& file, long long int offset) {
-        LOG(DEBUG) << "Getting file " << file->file_id() << " data";
+        initialized_ = false;
 
-        BinaryData mfile_id(20);
-        ReadData(mfile_id, 20, file->file_offset() + 8);
-        if (mfile_id.Empty()) {
-            LOG(ERROR) << "Error while reading file " << file->file_id() << " header (offset = "
-                       << file->file_offset() << "); Aborting.";
-            return BinaryData(0);
+        auto operation = io_->Init(filename);
+        if (operation.result != SUCCESS) {
+            Deinitialize();
+            status_->ClearAll();
+            return DatOperationResult<>(ERROR, "DATINIT: Error, cannot initialize dat due to internal IO error");
         }
 
-        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 BinaryData(0);
+        operation = fileSystem_->Init();
+        if (operation.result != SUCCESS) {
+            Deinitialize();
+            status_->ClearAll();
+            return DatOperationResult<>(ERROR, "DATINIT: Error, cannot initialize dat due to filesystem parsing error");
         }
 
-        BinaryData data((unsigned)(file->file_size() + (8 - offset)));
-        if (file->block_size() >= file->file_size() + 8) {
-            ReadData(data, file->file_size() + (8 - offset), file->file_offset() + offset);
-            return data;
+        operation = localeManager_->Init();
+        if (operation.result != SUCCESS) {
+            Deinitialize();
+            status_->ClearAll();
+            return DatOperationResult<>(ERROR, "DATINIT: Error, cannot initialize dat due to locale manager initialisation error");
         }
 
-        BinaryData fragments_count(4);
-        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;
+        initialized_ = true;
 
-        ReadData(data, current_block_size, file->file_offset() + offset);
-
-        BinaryData FragmentsDictionary(8 * unsigned(fragments_number));
-        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);
-            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 data;
+        status_->ClearAll();
+        return DatOperationResult<>();
     }
 
-    DAT_RESULT DatFile::ReadData(BinaryData &data, long long size, long long offset, long long data_offset) {
-        if (dat_state_ == CLOSED) {
-            LOG(ERROR) << "Dat state is CLOSED. Cannot read data.";
-            data = BinaryData(0);
-            return INIT_ERROR;
-        }
+    DatOperationResult<> DatFile::GatherInformation(const std::string &output_filename) {
+        FILE *out = fopen(output_filename.c_str(), "w");
+        if (!out)
+            return DatOperationResult<>(ERROR, "GATHERDATINFO: Cannot open file " + output_filename);
 
-        if (data_offset + size > data.size()) {
-            LOG(ERROR) << "Trying to read more than BinaryData size: Reading " << size << " bytes from " << offset
-                       << " position.";
-            data = BinaryData(0);
-            return DAT_READ_ERROR;
-        }
+        status_->SetStatus(DatStatus::E_GATHERING_INFO);
 
+        fprintf(out, "########################################################################################\n"
+                     "# LOTRO Dat library version: %8s                                                  #\n"
+                     "# Author: Gi1dor (e1.gildor@gmail.com)                                                 #\n"
+                     "# Is part of LOTRO Legacy project (http://translate.lotros.ru/)                        #\n"
+                     "# Last version is available on http://git.gi1dor.ru/LotRO_Legacy/Universal_dat_library #\n"
+                     "########################################################################################\n\n\n",
+                VERSION);
 
+        io_->PrintInformaion(out);
+        fileSystem_->PrintInformaion(out);
+        localeManager_->PrintInformaion(out);
 
-        if (offset + size > actual_dat_size_) {
-            LOG(ERROR) << "Trying to read more than DatFile size elapsed: Reading " << size << " bytes from " << offset
-                       << " position.";
-            data = BinaryData(0);
-            return DAT_READ_ERROR;
-        }
-
-        if (offset != ftell(file_handler_))
-            fseek(file_handler_, offset, SEEK_SET);
-        fread(data.data() + data_offset, unsigned(size), 1, file_handler_);
-        return SUCCESS;
+        fclose(out);
+        status_->ClearAll();
+        return DatOperationResult<>(SUCCESS);
     }
 
-    DAT_RESULT DatFile::WriteData(const BinaryData &data, long long size, long long offset, long long data_offset) {
-        if (dat_state_ < SUCCESS_DICTIONARY) {
-            LOG(ERROR) << "Dat state isn't READY. Cannot write data.";
-            return INCORRECT_STATE_ERROR;
-        }
+    DatOperationResult<> DatFile::Deinitialize() {
+        status_->SetStatus(DatStatus::E_COMMITING);
 
-        if (offset != ftell(file_handler_))
-            fseek(file_handler_, offset, SEEK_SET);
-
-        if (data_offset + size > data.size()) {
-            LOG(ERROR) << "Trying to write more than BinaryData size";
-            return DAT_WRITE_ERROR;
+        auto operation = localeManager_->DeInit();
+        if (operation.result != SUCCESS) {
+            status_->ClearAll();
+            return DatOperationResult<>(ERROR, "DATDEINIT: Error, cannot deinitialize. msg: " + operation.msg);
         }
 
-        fwrite(data.data() + data_offset, unsigned(size), 1, file_handler_);
-        actual_dat_size_ = std::max(file_size_, actual_dat_size_);
-        return SUCCESS;
-    }
-
-    //------------------------------------------------//
-    // LOCALE SECTION
-    //------------------------------------------------//
-
-    DAT_RESULT DatFile::SetLocale(LOCALE locale) {
-        LOG(INFO) << "Setting locale to " << (locale == PATCHED ? " PATCHED" : " ORIGINAL");
-        if (dat_state_ < READY) {
-            LOG(ERROR) << "Dat state isn't READY. Cannot set locale.";
-            return INCORRECT_STATE_ERROR;
+        operation = fileSystem_->DeInit();
+        if (operation.result != SUCCESS) {
+            status_->ClearAll();
+            return DatOperationResult<>(ERROR, "DATDEINIT: Error, cannot deinitialize. msg: " + operation.msg);
         }
 
-        if (current_locale_ == locale) {
-            return SUCCESS;
+        operation = io_->DeInit();
+        if (operation.result != SUCCESS) {
+            status_->ClearAll();
+            return DatOperationResult<>(ERROR, "DATDEINIT: Error, cannot deinitialize. msg: " + operation.msg);
         }
 
-        dat_state_ = UPDATED;
-        auto dict = GetLocaleDictReference(locale);
-        for (const auto& file : dict) {
-            if (file.second == nullptr)
-                continue;
-
-            if (dictionary_.count(file.first) == 0) {
-                LOG(WARNING) << "In locale dictionary there is file with file_id = " << file.first
-                             << "which is not in .dat file! Passing it and removing from locale dictionary";
-                dict.erase(file.first);
-                continue;
-            }
-            if (dictionary_[file.first]->MakeHeaderData().CutData(8, 16) ==
-                file.second->MakeHeaderData().CutData(8, 16) ||
-                inactive_categories.count(orig_dict_[file.first]->category) != 0)
-                continue;
-
-            long long file_id = file.first;
-            std::shared_ptr<SubFile> new_file = file.second;
-
-            *dictionary_[file_id] = *new_file;
-
-            pending_dictionary_.insert(file_id);
-            dat_state_ = UPDATED;
-        }
-
-        current_locale_ = locale;
-        LOG(INFO) << "Locale set successfull";
-        return SUCCESS;
+        initialized_ = false;
+        status_->ClearAll();
+        return DatOperationResult<>();
     }
 
-    LOCALE DatFile::current_locale() {
-        if (dat_state_ < READY) {
-            LOG(ERROR) << "dat_file is in incorrect state!";
-            return ORIGINAL;
-        }
-        if (current_locale_ != PATCHED && current_locale_ != ORIGINAL) {
-            LOG(ERROR) << "locale has incorrect value. Setting it to original";
-            current_locale_ = ORIGINAL;
-        }
-        return current_locale_;
-    }
-
-    std::map<long long, std::shared_ptr<SubFile> > &DatFile::GetLocaleDictReference(LOCALE locale) {
-        switch (locale) {
-            case PATCHED:
-                return patch_dict_;
-            case ORIGINAL:
-                return orig_dict_;
-            default:
-                LOG(ERROR) << "Unknown locale! Returning original";
-                return orig_dict_;
-        }
-    }
 
-    //------------------------------------------------//
-    // CHECKERS SECTION
-    //------------------------------------------------//
-
-    bool DatFile::CorrectSubfile(std::shared_ptr<SubFile> file) {
-        BinaryData mfile_id(20);
-        ReadData(mfile_id, 20, file->file_offset() + 8);
-        if (mfile_id.Empty())
-            return false;
-
-        return (mfile_id.CheckCompression() || file->file_id() == mfile_id.ToNumber<4>(0)) && file->file_size() < 50ll * 1024ll * 1024ll;
+    bool DatFile::Initialized() {
+        return initialized_;
     }
 
-    bool DatFile::CheckIfUpdatedByGame() {
-//        LOG(INFO) << "Checking if DatFile was updated by LotRO";
-//        if (!pending_patch_.empty())
-//            return true;
-
-//        bool updated = false;
-
-//        for (const auto& i : dictionary_) {
-//            long long file_id = i.first;
-//            std::shared_ptr<SubFile> subfile = i.second;
-//            if (patch_dict_.count(file_id) == 0)
-//                continue;
-//
-//            if (*subfile != *patch_dict_[file_id] && *subfile != *orig_dict_[file_id]) {
-//                //orig_dict_.clear();
-//                //patch_dict_.clear();
-//                LOG(INFO) << "DAT FILE WAS UPDATED!!!! CLEARING PATCH DATA";
-//                pending_patch_.insert(file_id);
-//                WriteData(BinaryData::FromNumber<4>(0), 4, 300);
-//                return true;
-//            }
-//        }
-//        return updated;
-        return false;
-    }
-
-    bool DatFile::CheckIfNotPatched() {
-        LOG(INFO) << "DatFile " << (patch_dict_.empty() ? "HASN'T " : "HAS already")
-                  << " been patched by LEGACY launcher!";
-        return patch_dict_.empty();
-    }
-
-    bool DatFile::CheckIfPatchedByOldLauncher() {
-        LOG(INFO) << "DatFile " << (dictionary_.count(620750000) == 0 ? "HASN'T " : "HAS already")
-                  << " been patched by OLD LAUNCHER!";
-        return dictionary_.count(620750000) > 0;
-    }
-
-    //------------------------------------------------//
-    // CATEGORY SECTION
-    //------------------------------------------------//
-
-    DAT_RESULT DatFile::EnableCategory(int category) {
-        LOG(INFO) << "Enabling category " << category;
-        if (inactive_categories.count(category) == 0)
-            return SUCCESS;
-
-        inactive_categories.erase(category);
-        dat_state_ = UPDATED;
-
-        for (auto &file : dictionary_) {
-            auto file_id = file.first;
-            if (patch_dict_.count(file_id) > 0 && patch_dict_[file_id]->category == category) {
-                *file.second = *patch_dict_[file_id];
-                pending_dictionary_.insert(file_id);
-            }
-        }
-        LOG(INFO) << "Category " << category << " enabled successfully";
-        return SUCCESS;
-    }
-
-    DAT_RESULT DatFile::DisableCategory(int category) {
-        LOG(INFO) << "Disabling category " << category;
-        if (inactive_categories.count(category) != 0)
-            return SUCCESS;
-        inactive_categories.insert(category);
-        dat_state_ = UPDATED;
-
-        for (auto &file : dictionary_) {
-            auto file_id = file.first;
-            if (orig_dict_.count(file_id) && orig_dict_[file_id]->category == category) {
-                *file.second = *orig_dict_[file_id];
-                pending_dictionary_.insert(file_id);
-            }
-        }
-        LOG(INFO) << "Category " << category << " disabled successfully";
-        return SUCCESS;
-    }
-
-    const std::set<long long> &DatFile::GetInactiveCategoriesList() {
-        return inactive_categories;
-    }
-
-    const std::string &DatFile::filename() const {
-        return filename_;
-    }
-
-    void DatFile::AddBufferedSize() {
-        if (free_buffered_size_ >= MIN_BUFFERED_SIZE)
-            return;
-        BinaryData nulls(MAX_BUFFERED_SIZE);
-        WriteData(nulls, MAX_BUFFERED_SIZE, file_size_);
-        free_buffered_size_ = MAX_BUFFERED_SIZE;
-    }
-
-    //------------------------------------------------//
-    // BACKUP SECTION
-    //------------------------------------------------//
-
-    bool DatFile::CheckIfBackupExists(const std::string &backup_datname) {
-        std::ifstream dst("DAT_LIBRARY_BACKUP/" + backup_datname, std::ios::binary);
-        return !dst.fail();
-    }
-
-    DAT_RESULT DatFile::RemoveBackup(const std::string &backup_datname) {
-        if (!CheckIfBackupExists(backup_datname))
-            return SUCCESS;
-        if (remove(("DAT_LIBRARY_BACKUP/" + backup_datname).c_str()) == 0)
-            return SUCCESS;
-        return REMOVE_FILE_ERROR;
-    }
-
-    DAT_RESULT DatFile::CreateBackup(const std::string &backup_datname) {
-        auto filename = filename_;
-        auto dat_id = dat_id_;
-        LOG(INFO) << "Restoring .dat file " << filename << " from backup " << backup_datname;
-        LOG(INFO) << "    Closing DatFile...";
-        CloseDatFile();
-        LOG(INFO) << "    Copying " << filename << " to " << backup_datname;
-        mkdir("DAT_LIBRARY_BACKUP");
-        std::ifstream  src(filename, std::ios::binary);
-        std::ofstream  dst("DAT_LIBRARY_BACKUP/" + backup_datname, std::ios::binary);
-
-        std::istreambuf_iterator<char> begin_source(src);
-        std::istreambuf_iterator<char> end_source;
-        std::ostreambuf_iterator<char> begin_dest(dst);
-        std::copy(begin_source, end_source, begin_dest);
-
-        src.close();
-        dst.close();
-
-        LOG(INFO) << "    Done copying. Initializing restored" << filename << " DatFile...";
-        InitDatFile(filename, dat_id);
-        LOG(INFO) << "Restoring .dat file success!";
-        return SUCCESS;
-    }
-
-    DAT_RESULT DatFile::RestoreFromBackup(const std::string &backup_datname) {
-        auto filename = filename_;
-        auto dat_id = dat_id_;
-        LOG(INFO) << "Restoring .dat file " << filename << " from backup " << backup_datname;
-        LOG(INFO) << "    Closing DatFile...";
-        CloseDatFile();
-        LOG(INFO) << "    Copying " << filename << " to " << backup_datname;
-        mkdir("DAT_LIBRARY_BACKUP");
-        std::ifstream  src("DAT_LIBRARY_BACKUP/" + backup_datname, std::ios::binary);
-        std::ofstream  dst(filename, std::ios::binary);
-        if (src.fail()) {
-            LOG(ERROR) << "CANNOT RESTORE FILE FROM BACKUP - no backup specified with name " << backup_datname;
-            return NO_BACKUP_ERROR;
-        }
-
-        std::istreambuf_iterator<char> begin_source(src);
-        std::istreambuf_iterator<char> end_source;
-        std::ostreambuf_iterator<char> begin_dest(dst);
-        std::copy(begin_source, end_source, begin_dest);
-
-        src.close();
-        dst.close();
-
-        LOG(INFO) << "    Done copying. Initializing restored" << filename << " DatFile...";
-        InitDatFile(filename, dat_id);
-        LOG(INFO) << "Restoring .dat file success!";
-        return SUCCESS;
+    long long DatFile::GetDatID() {
+        return dat_id_;
     }
 }
 }

+ 145 - 0
src/DatSubsystems/DatBackupManager.cpp

@@ -0,0 +1,145 @@
+//
+// Created by kikab on 04.06.2018.
+//
+
+#include <DatFile.h>
+#include <DatSubsystems/DatBackupManager.h>
+
+#include "DatSubsystems/DatBackupManager.h"
+
+#include "EasyLogging++/easylogging++.h"
+
+namespace LOTRO_DAT {
+    DatBackupManager::DatBackupManager(DatFile *datFilePtr): dat(datFilePtr) {}
+
+
+    /*!
+     * \author Gi1dor
+     * \date 11.07.2018
+     * Проверка доступности и корректности бэкапа dat файла
+     * \param[in] backup_datname путь к файлу бэкапа
+     * \returns true - если файл доступен для чтения его структура корректна. Иначе false
+     */
+
+    bool DatBackupManager::CheckIfBackupAvailable(const std::string &backup_datname) {
+        DatFile file;
+        bool result = file.Initialise(backup_datname, 0).result == SUCCESS && file.Initialized();
+        file.Deinitialize();
+        return result;
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 11.07.2018
+     * Создание резервной копии инициализированного Dat файла.
+     *
+     * \param[in] backup_datname путь к файлу создаваемого бэкапа
+     */
+
+    DatOperationResult<> DatBackupManager::CreateBackup(const std::string &backup_datname) {
+        FILE* file = fopen(backup_datname.c_str(), "w+b");
+        if (!file)
+            return DatOperationResult<>(ERROR, "CREATEBACKUP: cannot open file " + backup_datname);
+
+        dat->GetStatusModule().SetStatus(DatStatus::E_BACKUP_CREATING);
+        dat->GetStatusModule().SetPercentage(0);
+        dat->GetStatusModule().SetDebugMessage("Creating backup " + backup_datname + " from " + dat->GetIO().GetFilename().value);
+        auto operation = CopyDatFile(*dat, file);
+        dat->GetStatusModule().ClearAll();
+
+        fclose(file);
+
+        if (operation.result != SUCCESS)
+            return DatOperationResult<>(ERROR, "CREATEBACKUP: Error in copy dat file");
+        return DatOperationResult<>(SUCCESS);
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 11.07.2018
+     * Восстановление dat файла из резервной копии. Если восстановление не удалось, DatFile будет деинициализирован.
+     * \param[in] backup_datname путь к файлу резервной копии
+     */
+
+    DatOperationResult<> DatBackupManager::RestoreFromBackup(const std::string &backup_datname) {
+        if (!CheckIfBackupAvailable(backup_datname))
+            return DatOperationResult<>(ERROR, "RESTOREFROMBACKUP: incorrect backup file " + backup_datname);
+
+        DatFile backup_file;
+        backup_file.Initialise(backup_datname, 0);
+
+        std::string dat_filename = dat->GetIO().GetFilename().value;
+        dat->Deinitialize();
+
+        FILE* file = fopen(backup_datname.c_str(), "w+b");
+        if (!file)
+            return DatOperationResult<>(ERROR, "RESTOREFROMBACKUP: cannot open file " + backup_datname);
+
+        dat->GetStatusModule().SetStatus(DatStatus::E_BACKUP_RESTORING);
+        dat->GetStatusModule().SetPercentage(0);
+        dat->GetStatusModule().SetDebugMessage("Restoring file " + dat->GetIO().GetFilename().value +  " from backup " + backup_datname);
+        auto operation = CopyDatFile(backup_file, file);
+        dat->GetStatusModule().ClearAll();
+
+        fclose(file);
+        if (operation.result == ERROR)
+            return DatOperationResult<>(ERROR, "RESTOREFROMBACKUP: error in copy dat file");
+
+        dat->Initialise(dat_filename, dat->GetDatID());
+        return DatOperationResult<>(SUCCESS);
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 11.07.2018
+     * Удаление резервной копии.
+     * \param[in] backup_datname путь к файлу резервной копии
+     */
+
+    DatOperationResult<> DatBackupManager::RemoveBackup(const std::string &backup_datname) {
+        dat->GetStatusModule().SetStatus(DatStatus::E_BACKUP_REMOVING);
+        dat->GetStatusModule().SetPercentage(0);
+        dat->GetStatusModule().SetDebugMessage("Removing backup file " + backup_datname);
+
+        if (remove(backup_datname.c_str()) != 0)
+            LOG(INFO) << "Removed backup file " << backup_datname;
+
+        dat->GetStatusModule().ClearAll();
+        return DatOperationResult<>(SUCCESS);
+    }
+
+    /*!
+     * \author Gi1dor
+     * \date 11.07.2018
+     * Копирует содержимое dat файла в файл target
+     * \param[in] source dat файл для копирования. Должен быть проинициализирован
+     * \param[in] target файл для записи. Должен быть открыт на запись.
+     * \warning Файл target после выполнения функции НЕ будет закрыт. Требуется закрыть командой fclose;
+     */
+
+    DatOperationResult<> DatBackupManager::CopyDatFile(DatFile &source, FILE *target) {
+        long long parts_count =
+                source.GetIO().GetActualDatSize().value / COPY_BLOCK_SIZE + (source.GetIO().GetActualDatSize().value % COPY_BLOCK_SIZE != 0);
+        long long newfile_size = 0;
+        unsigned elapsed_size = unsigned(source.GetIO().GetActualDatSize().value - newfile_size);
+
+        BinaryData data(COPY_BLOCK_SIZE);
+        for (unsigned i = 0; i < parts_count; i++) {
+            dat->GetStatusModule().SetPercentage(i * 100 / (unsigned)parts_count);
+
+            auto operation = source.GetIO().ReadData(data, std::min(COPY_BLOCK_SIZE, elapsed_size), newfile_size);
+            if (operation.result == ERROR)
+                return DatOperationResult<>(ERROR, "Copy failed. Read data error");
+            fwrite(data.data(), std::min(COPY_BLOCK_SIZE, elapsed_size), 1, target);
+
+            newfile_size += std::min(COPY_BLOCK_SIZE, elapsed_size);
+            elapsed_size -= std::min(COPY_BLOCK_SIZE, elapsed_size);
+        }
+
+        return DatOperationResult<>(SUCCESS);
+    }
+
+}

+ 216 - 0
src/DatSubsystems/DatExporter.cpp

@@ -0,0 +1,216 @@
+#include <DatFile.h>
+#include <DatSubsystems/DatExporter.h>
+#include <DatSubsystems/DatFileSystem.h>
+#include <BinaryData.h>
+#include <SubfileData.h>
+#include <SubFile.h>
+
+#include <string>
+#include <fstream>
+#include <algorithm>
+
+namespace LOTRO_DAT {
+    DatExporter::DatExporter(DatFile *datFilePtr) : dat(datFilePtr) {
+    }
+
+    /*!
+     * \author Gi1dor
+     * \date 05.07.2018
+     * Извлечение в папку всех файлов, имеющих заданный тип
+     * \param[in] type Тип файлов
+     * \param[in] output_directory_path путь к папке, куда будут записаны файлы. Имя экспортируемого файла - file_id.ext, где file_id - id файла, ext - его расширение
+     *
+     * \returns DatOperationResult.value - количество извлечённых файлов
+     */
+
+
+    DatOperationResult<int> DatExporter::ExtractAllFilesByType(const FILE_TYPE &type, std::string output_directory_path) {
+        int success_exported = 0;
+        int iterated_files = 0;
+
+        dat->GetStatusModule().SetStatus(DatStatus::E_EXTRACTING);
+        dat->GetStatusModule().SetPercentage(0);
+        dat->GetStatusModule().SetDebugMessage("Starting extracting files of type " + std::to_string(type) + ", this may take long time, please be patient!");
+        LOG(INFO) << "Extracting files by type " + std::to_string(type) + "to database...";
+
+        auto operation = dat->GetFileSystem().PerformOperationOnAllFiles([&iterated_files, &success_exported, this, type, &output_directory_path](std::shared_ptr<SubFile>& file) -> void {
+            iterated_files++;
+            dat->GetStatusModule().SetPercentage(iterated_files * 100u / dat->GetFileSystem().GetInitialisedFilesNumber());
+            if ((file->FileType() & type) == NO_TYPE)
+                return;
+
+            SubfileData export_file = file->PrepareForExport(dat->GetFileSystem().GetFileData(*file, 8).value);
+            dat->GetStatusModule().SetDebugMessage("Extracting file " + std::to_string(file->file_id()) + " (Files by type -> directory)");
+
+            if (export_file.Empty())
+                return;
+
+            bool result;
+
+            if (type == TEXT)
+                result = WriteStringToFile(export_file.text_data, output_directory_path + std::to_string(file->file_id()) + file->Extension()).result;
+            else
+                result = export_file.binary_data.WriteToFile(output_directory_path + std::to_string(file->file_id()) + file->Extension());
+
+            if (result)
+                success_exported++;
+        });
+
+        LOG(INFO) << "Extracting files: successfully exported " << success_exported << " files";
+        dat->GetStatusModule().ClearAll();
+        return DatOperationResult<int>(success_exported, SUCCESS);
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 05.07.2018
+     * Извлечение файла по его id
+     * \param[in] file_id id файла
+     * \param[in] output_filename - имя файла, куда будет записаны данные.
+     */
+
+    DatOperationResult<> DatExporter::ExtractFileById(long long file_id, std::string output_filename) {
+        dat->GetStatusModule().SetStatus(DatStatus::E_EXTRACTING);
+        dat->GetStatusModule().SetPercentage(0);
+        dat->GetStatusModule().SetDebugMessage("Extracting file " + std::to_string(file_id) + " to file " + output_filename);
+
+        auto operation_GetFile = dat->GetFileSystem().GetFile(file_id);
+        if (operation_GetFile.result == ERROR) {
+            dat->GetStatusModule().ClearAll();
+            return DatOperationResult<>(ERROR, "EXTRACTFILEBYID: File not found! id = " + std::to_string(file_id));
+        }
+        std::shared_ptr<SubFile> file = operation_GetFile.value;
+
+        auto operation_GetFileData = dat->GetFileSystem().GetFileData(*file, 8);
+        if (operation_GetFileData.result == ERROR) {
+            dat->GetStatusModule().ClearAll();
+            return DatOperationResult<>(ERROR, "EXTRACTFILEBYID: Can't get data for id = " + std::to_string(file_id));
+        }
+
+        SubfileData export_data = file->PrepareForExport(operation_GetFileData.value);
+
+        bool result;
+
+        if (file->FileType() == TEXT)
+            result = WriteStringToFile(export_data.text_data, output_filename  + file->Extension()).result;
+        else
+            result = export_data.binary_data.WriteToFile(output_filename + file->Extension());
+
+        dat->GetStatusModule().ClearAll();
+        if (!result)
+            return DatOperationResult<>(ERROR, "EXTRACTFILEBYID: Cannot write to file" + output_filename + file->Extension());
+
+        return DatOperationResult<>(SUCCESS);
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 05.07.2018
+     * Извлечение в базу данных всех файлов, имеющих заданный тип
+     * \param[in] type Тип файлов
+     * \param[in] db Указатель на объект базы данных
+     *
+     * \returns DatOperationResult.value - количество извлечённых файлов
+     */
+
+
+    DatOperationResult<int> DatExporter::ExtractAllFilesByType(const FILE_TYPE &type, Database *db) {
+        if (!db)
+            return DatOperationResult<int>(0, ERROR, "EXTRACTALLBYTYPETODB: database is nullptr");
+
+        dat->GetStatusModule().SetStatus(DatStatus::E_EXTRACTING);
+        dat->GetStatusModule().SetPercentage(0);
+        dat->GetStatusModule().SetDebugMessage("Initialising extraction of files by type " + std::to_string(type) + "to database... This may take long time, please be patient!\"");
+        LOG(INFO) << "Extracting files by type " + std::to_string(type) + "to database...";
+
+        int success_exported = 0;
+        int iterated_files = 0;
+
+        auto operation = dat->GetFileSystem().PerformOperationOnAllFiles([&success_exported, &iterated_files, this, type, db](std::shared_ptr<SubFile>& file) {
+            iterated_files++;
+            dat->GetStatusModule().SetPercentage(iterated_files * 100u / dat->GetFileSystem().GetInitialisedFilesNumber());
+
+            if ((file->FileType() & type) == NO_TYPE)
+                return;
+
+            dat->GetStatusModule().SetDebugMessage("Extracting file " + std::to_string(file->file_id()) + " (Files by type -> database)");
+
+            SubfileData export_file = file->PrepareForExport(dat->GetFileSystem().GetFileData(*file, 8).value);
+
+            bool result = db->PushFile(export_file);
+
+            if (result)
+                success_exported++;
+        });
+
+        LOG(INFO) << "Extracting files: successfully exported " << success_exported << " files";
+        dat->GetStatusModule().ClearAll();
+        return DatOperationResult<int>(success_exported, SUCCESS);
+    }
+
+
+
+    /*!
+     * \author Ivan Arkhipov
+     * \date 21.10.2018
+     * Извлечение в базу данных файла с заданным id
+     * \param[in] file_id ID файла
+     * \param[in] db Указатель на объект базы данных
+     */
+
+    DatOperationResult<> DatExporter::ExtractFileById(long long file_id, Database *db) {
+        if (!db)
+            return DatOperationResult<>(ERROR, "EXTRACTFILEBYIDTODB: database is nullptr");
+
+        dat->GetStatusModule().SetStatus(DatStatus::E_EXTRACTING);
+        dat->GetStatusModule().SetPercentage(50);
+        LOG(INFO) << "Extracting file with id = " << file_id << "to database...";
+
+        auto operation_GetFile = dat->GetFileSystem().GetFile(file_id);
+        if (operation_GetFile.result == ERROR) {
+            dat->GetStatusModule().ClearAll();
+            return DatOperationResult<>(ERROR, "EXTRACTFILEBYIDTODB: File not found! id = " + std::to_string(file_id));
+        }
+        std::shared_ptr<SubFile> file = operation_GetFile.value;
+
+        auto operation_GetFileData = dat->GetFileSystem().GetFileData(*file, 8);
+        if (operation_GetFileData.result == ERROR) {
+            dat->GetStatusModule().ClearAll();
+            return DatOperationResult<>(ERROR, "EXTRACTFILEBYIDTODB: Can't get data for id = " + std::to_string(file_id));
+        }
+
+        SubfileData export_data = file->PrepareForExport(operation_GetFileData.value);
+
+        bool result = db->PushFile(export_data);
+
+        dat->GetStatusModule().ClearAll();
+        if (!result)
+            return DatOperationResult<>(ERROR, "EXTRACTFILEBYIDTODB: Cannot export file" + std::to_string(file_id));
+
+        LOG(INFO) << "Successfully exported file " << file_id << " to database";
+        return DatOperationResult<>(SUCCESS);
+    }
+
+    /*!
+     * \author Gi1dor
+     * \date 05.07.2018
+     * Запись строки в формате UTF-16 в файл
+     * \param[in] str Строка для записи в файл
+     * \param[in] path Путь к файлу, в который строка будет записана
+     *
+     * \warning Путь к файлу должен быть корректным (папки должны существовать). Если и сам файл существует, то он будет перезаписан
+     */
+
+    DatOperationResult<> DatExporter::WriteStringToFile(const std::u16string &str, const std::string &path) {
+        std::basic_ofstream<char16_t> output_stream(path, std::ios::out);
+        if (!output_stream.is_open())
+            return DatOperationResult<>(ERROR, "WRITESTRINGTOFILE: cant open file " + path);
+
+        output_stream << str;
+        output_stream.close();
+
+        return DatOperationResult<>();
+    }
+};

+ 497 - 0
src/DatSubsystems/DatFileSystem.cpp

@@ -0,0 +1,497 @@
+#include <DatSubsystems/DatFileSystem.h>
+#include <EasyLogging++/easylogging++.h>
+#include <BinaryData.h>
+#include <DatFile.h>
+#include <SubFile.h>
+#include <SubDirectory.h>
+
+#include <functional>
+
+namespace LOTRO_DAT {
+
+
+    /*!
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Конструктор объекта модуля. Вызывается только из класса DatFile. Создаёт указатель но объект DatFile, но
+     * не инициализирует модуль. Инизиализация происходит в функции Init
+     * \param[in] datFilePtr Указатель на объект управляющего класса DatFile
+     */
+
+    DatFileSystem::DatFileSystem(DatFile *datFilePtr) : dat(datFilePtr) {}
+
+    /*!
+     * \author Gi1dor
+     * \date 29.06.2018
+     * Возвращает бинарные данные файла. Помимо самого файла, в бинарном содержимом находится также метаинформация о нём.
+     * Изменение параметра offset позволяет игнорировать метаинформацию и извлечь только непосредственно содержимое файла
+     * \param[in] file_id Идентификатор получаемого файла
+     * \param[in] offset Отступ от начала файла. Данные до отступа не извлекаются.
+     * \return Возвращает result = ERROR и value = BinaryData, если файл не найден или информаци в словаре некорректная
+     */
+
+    DatOperationResult<BinaryData> DatFileSystem::GetFileData(const SubFile& file, long long int offset) {
+        if (!dat)
+            return DatOperationResult<BinaryData>(BinaryData(), ERROR,
+                                                  "DatFileSystem error: no connection with Dat (dat is nullptr)");
+
+        BinaryData mfile_id(20);
+        auto operation = CheckCorrectSubfile(file);
+        if (operation.result == ERROR || !operation.value)
+            return DatOperationResult<BinaryData>(BinaryData(), ERROR,
+                                                  "DATFSGETFILEDATA: Incorrect file" + std::to_string(file.file_id()));
+
+
+        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);
+        }
+
+        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;
+        }
+
+        return DatOperationResult<BinaryData>(data, SUCCESS);
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 29.06.2018
+     * Возвращает указатель на объект файла в словаре. Через этот указатель можно патчить/извлекать файл
+     * \param[in] file_id идентификатор получаемого файла
+     * \return Возвращает result = ERROR и value = nullptr, если файл не найден
+     */
+
+    DatOperationResult<std::shared_ptr<SubFile>> DatFileSystem::GetFile(long long file_id) {
+        if (!dat)
+            return DatOperationResult<std::shared_ptr<SubFile>>(nullptr, ERROR,
+                                                                "DATFSGETFILE: no connection with Dat (dat is nullptr)");
+
+        if (dictionary_.count(file_id) == 0) {
+            auto operation = InitSubFile(file_id);
+            if (operation.result == ERROR)
+                return DatOperationResult<std::shared_ptr<SubFile>>(nullptr, ERROR);
+        }
+
+        return DatOperationResult<std::shared_ptr<SubFile>>(dictionary_[file_id], 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();
+        subfile_pending_update.insert(preinit_file.file_id());
+
+        return DatOperationResult<>();
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 29.06.2018
+     * Проверяет корректность файла, полученного из словаря
+     *
+     * \return Объект DatOperationResult<bool> для которого value = true, если файл корректный, и false - иначе.
+     */
+
+    DatOperationResult<bool> DatFileSystem::CheckCorrectSubfile(const SubFile& file) {
+        if (!dat)
+            return DatOperationResult<bool>(false, ERROR,
+                                            "DATFSCORRECTSUBFILE: no connection with Dat (dat is nullptr)");
+        if (file.file_size() < 16)
+            return DatOperationResult<bool>(false, SUCCESS);
+
+        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");
+
+        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, "DATFSCOMMITDIRS: no connection with Dat (dat is nullptr)");
+
+        for (auto file_id : subfile_pending_update) {
+            if (dictionary_.count(file_id) == 0)
+                continue;
+
+//            auto operation = CheckCorrectSubfile(dictionary_[file_id]);
+//            if (operation.result == ERROR) {
+//                LOG(ERROR) << "Check subfile correctness failed";
+//                continue;
+//            }
+
+//            if (!operation.value) {
+//                LOG(ERROR) << "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;
+        }
+        subfile_pending_update.clear();
+        return DatOperationResult<>(SUCCESS);
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 29.06.2018
+     * Инициализация модуля. Подготавливает файловую систему для работы
+     */
+
+    DatOperationResult<> DatFileSystem::Init() {
+        if (!dat)
+            return DatOperationResult<>(ERROR, "DATFSINIT: no connection with Dat (dat is nullptr)");
+
+        DeInit();
+        patched_file_end = dat->GetIO().file_size;
+        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();
+
+        subdir_init_queue_.clear();
+        subfile_init_map_.clear();
+        subfile_init_queue_.clear();
+
+        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();
+
+        fprintf(file, "============= FS INFO SECTION =============\n");
+        fprintf(file, "Files in dictionary number: %d\n", dictionary_.size());
+        fprintf(file, "Files visited: %d\n", visited_subfiles_ids_.size());
+        fprintf(file, "Folders visited: %d\n", visited_subdirectories_offsets_.size());
+        fprintf(file, "File patched size: %lld\n", patched_file_end);
+
+        std::map<FILE_TYPE, size_t> filetypes_count{{TEXT, 0},
+                                                    {JPG, 0},
+                                                    {DDS, 0},
+                                                    {WAV, 0},
+                                                    {OGG, 0},
+                                                    {FONT, 0},
+                                                    {UNKNOWN, 0}};
+
+        for (const auto &datfile : dictionary_) {
+            filetypes_count[datfile.second->FileType()]++;
+        }
+
+        fprintf(file, "TEXT files number = %d\n", filetypes_count[TEXT]);
+        fprintf(file, "JPG files number = %d\n", filetypes_count[JPG]);
+        fprintf(file, "DDS files number = %d\n", filetypes_count[DDS]);
+        fprintf(file, "WAV files number = %d\n", filetypes_count[WAV]);
+        fprintf(file, "OGG files number = %d\n", filetypes_count[OGG]);
+        fprintf(file, "FONT files number = %d\n", filetypes_count[FONT]);
+        fprintf(file, "UNKNOWN files number = %d\n", filetypes_count[UNKNOWN]);
+    }
+
+    /*!
+     * \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_map_.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();
+            subdir_init_queue_.erase(subdir_init_queue_.begin());
+            InitSubDirectory(dir);
+        }
+
+        SubFile file = subfile_init_map_[file_id];
+        subfile_init_queue_.erase(file);
+        subfile_init_map_.erase(file_id);
+
+        auto initialised_file = SubFile::MakeSubfile(*dat, file);
+
+        if (!initialised_file)
+            return DatOperationResult<>(ERROR, "DATFSINITSUBFILE: initialised subfile pointer is empty");
+
+        if (!CheckCorrectSubfile(*initialised_file).value)
+            return DatOperationResult<>(ERROR, "DATFSINITSUBFILE: initialised file " + std::to_string(file.file_id()) + " is incorrect");
+
+        if (dictionary_.count(file_id) > 0) {
+            LOG(WARNING) << "Dublicate files id = " << file_id << "dictionary offsets = "
+                         << dictionary_[file_id]->dictionary_offset() << " and "
+                         << initialised_file->dictionary_offset();
+            if (CheckCorrectSubfile(*dictionary_[file_id]).value)
+                LOG(ERROR) << "    FILE IN DICTIONARY IS ALREADY CORRECT!";
+
+            dictionary_[file_id] = initialised_file;
+        }
+
+        if (dictionary_.count(file_id) == 0)
+            dictionary_[file_id] = initialised_file;
+
+        return DatOperationResult<>();
+    }
+
+
+    /*!
+     * \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();
+            subdir_init_queue_.erase(subdir_init_queue_.begin());
+            InitSubDirectory(dir);
+        }
+        return DatOperationResult<>();
+    }
+
+
+    /*!
+     * \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()) {
+            SubFile file = *subfile_init_queue_.begin();
+            InitSubFile(file.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 объект папки SubDirectory
+     * \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 == ERROR)
+            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");
+
+        // 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");
+
+        // 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 (unknown == 0 || offset == 0)
+                break;
+
+            if (visited_subdirectories_offsets_.count(offset) != 0)
+                continue;
+
+            visited_subdirectories_offsets_.insert(offset);
+            subdir_init_queue_.insert(SubDirectory(unknown, offset));
+        }
+
+        // Subfiles section
+
+        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()) || file.file_id() == -1)
+                break;
+
+            visited_subfiles_ids_.insert(file.file_id());
+            subfile_init_map_[file.file_id_] = file;
+            subfile_init_queue_.insert(file);
+        }
+        return DatOperationResult<>();
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 03.07.2018
+     * Выполнение функции function для всех файлов в dat контейнере.
+     *
+     * \param[in] function Функтор, принимающий в качестве аргумента ссылку на объект-указатель std::shared_ptr<SubFile>
+     */
+
+    DatOperationResult<> DatFileSystem::PerformOperationOnAllFiles(const std::function<void (std::shared_ptr<SubFile>&)>& function) {
+        InitAllFiles();
+        for (auto& file_pair: dictionary_)
+            function(file_pair.second);
+        return DatOperationResult<>();
+    }
+
+    int DatFileSystem::GetInitialisedFilesNumber() const {
+        return dictionary_.size();
+    }
+}

+ 419 - 0
src/DatSubsystems/DatIO.cpp

@@ -0,0 +1,419 @@
+#include "BinaryData.h"
+#include "DatFile.h"
+#include "EasyLogging++/easylogging++.h"
+#include <DatOperationResult.h>
+
+#include <algorithm>
+#include <locale>
+#include <DatSubsystems/DatIO.h>
+#include <zconf.h>
+
+
+#ifdef WIN32
+#define fseek _fseeki64
+#define ftell _ftelli64
+#endif
+
+unsigned int RSHash(const std::string& str) {
+    unsigned int b    = 378551;
+    unsigned int a    = 63689;
+    unsigned int hash = 0;
+
+    for (char i : str) {
+        hash = hash * a + i;
+        a    = a * b;
+    }
+
+    return hash;
+}
+
+extern "C++"
+{
+namespace LOTRO_DAT {
+
+    /*!
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Конструктор объекта модуля. Вызывается только из класса DatFile. Создаёт указатель но объект DatFile, но
+     * не инициализирует модуль. Инизиализация происходит в функции Init
+     * \param[in] datFile Указатель на объект управляющего класса DatFile
+     */
+
+    DatIO::DatIO(DatFile *datFile) : dat(datFile), file_handler_(nullptr), filename_("none"), actual_dat_size_(0) {
+    }
+
+
+    DatIO::~DatIO() {
+        DeInit();
+    }
+
+    //------------------------------------------------//
+    // INIT SECTION
+    //------------------------------------------------//
+
+    /*!
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Инициализация модуля. Открывает файл на чтение и блокирует его для доступа из других программ.
+     * Читает заголовочную информацию, необходимую для инициализации остальных модулей. Не должна вызываться где-либо,
+     * кроме как из управляющего объекта DatFile.
+     * \param[in] filename имя файла для открытия (включая путь)
+     * \return Возвращает result = ERROR в случае, если не удалось открыть файл или файл некорректный
+     */
+
+
+    DatOperationResult<> DatIO::Init(const std::string &filename) {
+        LOG(INFO) << "Initializing IO: " << filename;
+
+        filename_ = filename;
+
+        auto result = OpenDatFile();
+        if (result.result == ERROR) {
+            LOG(ERROR) << "Error in OpenDatFile: " << result.msg;
+            DeInit();
+            return result;
+        }
+
+        result = ReadSuperBlock();
+        if (result.result == ERROR) {
+            LOG(ERROR) << "Error in ReadSuperBlock: " << result.msg;
+            DeInit();
+            return result;
+        }
+
+        LOG(INFO) << "Successfull initializing IO: " << filename;
+        return DatOperationResult<>(SUCCESS, "DatIO initialized successfully");
+    }
+
+    /*!
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Открытие файла, имя которого записано в переменной file_handler_
+     * \return Возвращает result = ERROR в случае, если не удалось открыть файл
+     */
+
+    DatOperationResult<> DatIO::OpenDatFile() {
+        LOG(DEBUG) << "DatIO: Started opening DatFile";
+
+        file_handler_ = fopen(filename_.c_str(), "r+b");
+
+        if (file_handler_ == nullptr) {
+            LOG(ERROR) << "DatIO: Unable to open file " << filename_ << ". Presumably - no file found...";
+            return DatOperationResult<>(ERROR, std::string("Unable to locate and open file " + filename_));
+        }
+
+        fseek(file_handler_, 0, SEEK_END);
+        actual_dat_size_ = ftell(file_handler_);
+        fseek(file_handler_, 0, SEEK_SET);
+
+        LOG(INFO) << "DatIO: file opened";
+        return DatOperationResult<>(SUCCESS, std::string("Successfully opened file " + filename_));
+    }
+
+    /*!
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Инициализация основных переменных файла, располагающихся в начале.
+     *
+     * Известные переменные (каждая по 4 байта):
+     *
+     * Офсет - описание переменной
+     * =========
+     * 0x100 - константа, одинаковая для всех .dat файлов
+     * 0x140 - ещё одна константа, одинаковая для всех .dat файлов
+     * 0x14C - Первая переменная версии .dat файла
+     * 0x148 - размер .dat файла. Указывает на начало свободного места. Необязательно должен являться физическим концом
+     * 0x150 - Вторая переменная версии dat файла
+     * 0x154 - Положение журнала фрагментации (который представляет собой список свободных блоков в dat-файле, куда можно писать новые данные
+     * 0x158 - Позиция окончания журнала фрагментации (?)
+     * 0x15C - Кол-во записей в журнале фрагментации (?)
+     * 0x160 - Офсет корневой папки
+     * 0x19C - Свободное место в dat файле (?)
+     *
+     * \return Возвращает result = ERROR в случае, если константы не совпадают с требуемыми значениями
+     */
+
+
+    DatOperationResult<> DatIO::ReadSuperBlock() {
+        LOG(INFO) << "DatIO: Started reading superblock";
+
+        BinaryData data(1024);
+        ReadData(data, 1024);
+
+        constant1 = data.ToNumber<4>(0x100);
+        constant2 = data.ToNumber<4>(0x140);
+        version1 = data.ToNumber<4>(0x14C);
+        file_size = data.ToNumber<4>(0x148);
+        version2 = data.ToNumber<4>(0x150);
+        fragmentation_journal_offset = data.ToNumber<4>(0x154);
+        fragmentation_journal_end = data.ToNumber<4>(0x158);
+        fragmentation_journal_size = data.ToNumber<4>(0x15C);
+        root_directory_offset = data.ToNumber<4>(0x160);
+        free_dat_size = data.ToNumber<4>(0x19C);
+
+        if (constant1 != 0x4C5000)
+            return DatOperationResult<>(ERROR, "Variable at position 0x100 is not equal to .dat file constant!");
+
+        if (constant2 != 0x5442)
+            return DatOperationResult<>(ERROR, "Variable at position 0x140 is not equal to .dat file constant!");
+
+        LOG(INFO) << "DatIO: Superblock read successfully";
+        return DatOperationResult<>(SUCCESS, "Superblock read successfully.");
+    }
+
+
+    //------------------------------------------------//
+    // PUBLIC READ/WRITE SECTION
+    //------------------------------------------------//
+
+
+    /*!
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Чтение бинарных данных из файла. Считает size байт dat файла, начиная с положения offset, и запишет их в
+     * объект data, причём положение первого байта считанных данных будет равно значению data_offset
+     *
+     * \param[in, out] data Объект класса BinaryData, в который будут считаны данные
+     * \param[in] size Количество байт, которые будут считаны
+     * \param[in] offset Положение начала данных в dat файле, откуда считать
+     * \param[in] data_offset Положение записи. Считанные данные будут записаны с отступом data_offset в data
+     *
+     * \return Возвращает result = ERROR в случае, если считать не удалось (неверные входные данные)
+     */
+
+    DatOperationResult<>
+    DatIO::ReadData(BinaryData &data, long long size, long long offset, long long data_offset) const {
+        if (file_handler_ == nullptr)
+            return DatOperationResult<>(ERROR, "IOREADDATA: file handler is null pointer on reading data.");
+
+        if (data_offset + size > data.size())
+            return DatOperationResult<>(ERROR, "IOREADDATA: Parameters: offset, size are out of range.");
+
+        if (offset + size > actual_dat_size_)
+            return DatOperationResult<>(ERROR, "IOREADDATA: Reading more than DatFile size elapsed.");
+
+        if (offset != ftell(file_handler_))
+            fseek(file_handler_, offset, SEEK_SET);
+
+        fread(data.data() + data_offset, unsigned(size), 1, file_handler_);
+        return DatOperationResult<>(SUCCESS, "Read data successful.");
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 10.07.2018
+     *
+     * Проверка буфера перед записью новых size_to_write байт в конец файла. Если буфер мал - то дозаписывается
+     * новый чистый блок размера MAX_EOF_BUFFER (константное поле класса DatIO);
+     *
+     * \param[in] size_to_write Размер данных в байтах, которые будут сейчас записаны в конец файла
+     *
+     */
+
+    void DatIO::UpdateBufferIfNeeded(long long size_to_write) {
+        BinaryData empty_buffer(std::max(MAX_EOF_BUFFER, unsigned(size_to_write)));
+        fseek(file_handler_, 0, SEEK_END);
+        fwrite(empty_buffer.data(), empty_buffer.size(), 1, file_handler_);
+        actual_dat_size_ += empty_buffer.size();
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Запись бинарных данных в dat файл. Запишет size байт данных из data, начиная с позиции data_offset,
+     * в позицию offset .dat файла.
+     *
+     * \param[in] data Объект класса BinaryData, откуда будут взяты данные для записи
+     * \param[in] size Количество байт для записи
+     * \param[in] offset Положение начала данных в dat файле, куда записывать
+     * \param[in] data_offset Положение начала данных в data, от которого будет взято size байт и записано в dat файл
+     *
+     * \return Возвращает result = ERROR в случае, если записать не удалось (неверные входные данные)
+     */
+
+    DatOperationResult<>
+    DatIO::WriteData(const BinaryData &data, long long size, long long offset, long long data_offset) {
+        if (file_handler_ == nullptr)
+            return DatOperationResult<>(ERROR, "IOWRITEDATA: file handler is null pointer on writing data.");
+
+        if (data_offset + size > data.size())
+            return DatOperationResult<>(ERROR, "IOWRITEDATA: writing more than BinaryData size.");
+
+        if (offset + size > actual_dat_size_)
+            UpdateBufferIfNeeded(offset + size - actual_dat_size_);
+
+        if (offset != ftell(file_handler_))
+            fseek(file_handler_, offset, SEEK_SET);
+
+        fwrite(data.data() + data_offset, unsigned(size), 1, file_handler_);
+
+        return DatOperationResult<>(SUCCESS, "Data writing successful.");
+    }
+
+
+    //------------------------------------------------//
+    // DEINIT SECTION
+    //------------------------------------------------//
+
+    /*!
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Деинициализация модуля. Не должна вызываться где-либо, кроме как из управляющего объекта DatFile.
+     */
+
+    DatOperationResult<> DatIO::DeInit() {
+        if (!dat->Initialized()) {
+            ClearData();
+            return DatOperationResult<>(SUCCESS);
+        }
+
+        if (file_handler_ != nullptr)
+            fclose(file_handler_);
+
+        truncate64(filename_.c_str(), actual_dat_size_);
+
+        filename_ = "none";
+        file_handler_ = nullptr;
+
+        constant1 = 0;
+        constant2 = 0;
+        file_size = 0;
+        version1 = 0;
+        version2 = 0;
+        fragmentation_journal_size = 0;
+        fragmentation_journal_end = 0;
+        root_directory_offset = 0;
+        fragmentation_journal_offset = 0;
+        return DatOperationResult<>(SUCCESS, "File deinitialisation successfull");
+    }
+
+    DatOperationResult<> DatIO::ModifyFragmentationJournal() {
+        if (fragmentation_journal_size == 0)
+            return DatOperationResult<>(SUCCESS,
+                                      "DatIO: Fragmentation journal is empty. Nothing to do.");
+        LOG(DEBUG) << "Modifying fragmentation journal";
+        BinaryData data(4);
+        ReadData(data, 4, fragmentation_journal_offset + 8 * fragmentation_journal_size);
+        LOG(INFO) << "FREE_SIZE BLOCK = " << data.ToNumber<4>(0);
+
+        long long free_size = data.ToNumber<4>(0);
+        long long free_offset = file_size;
+
+        BinaryData nulldata = BinaryData(unsigned(free_size));
+        WriteData(nulldata, nulldata.size(), file_size);
+        file_size += nulldata.size();
+
+        WriteData(BinaryData::FromNumber<4>(free_size), 4,
+                  fragmentation_journal_offset + 8 * fragmentation_journal_size);
+        WriteData(BinaryData::FromNumber<4>(free_offset), 4,
+                  fragmentation_journal_offset + 8 * fragmentation_journal_size + 4);
+
+        //nulldata = BinaryData(8);
+        //WriteData(nulldata, nulldata.size(), fragmentation_journal_offset + 16);
+        LOG(DEBUG) << "Finished modifying fragmentation journal";
+        return DatOperationResult<>(SUCCESS, "Fragmentation journal patched successfully!");
+    }
+
+    DatOperationResult<> DatIO::UpdateHeader() {
+        LOG(DEBUG) << "Updating header";
+        WriteData(BinaryData::FromNumber<4>(constant1), 4, 0x100);
+        WriteData(BinaryData::FromNumber<4>(constant2), 4, 0x140);
+        //WriteData(BinaryData::FromNumber<4>(    0     ), 4, 0x144);
+        WriteData(BinaryData::FromNumber<4>(file_size), 4, 0x148);
+        WriteData(BinaryData::FromNumber<4>(version1), 4, 0x14C);
+        WriteData(BinaryData::FromNumber<4>(version2), 4, 0x150);
+        WriteData(BinaryData::FromNumber<4>(fragmentation_journal_offset), 4, 0x154);
+        WriteData(BinaryData::FromNumber<4>(fragmentation_journal_end), 4, 0x158);
+        WriteData(BinaryData::FromNumber<4>(fragmentation_journal_size), 4, 0x15C);
+        WriteData(BinaryData::FromNumber<4>(root_directory_offset), 4, 0x160);
+        WriteData(BinaryData::FromNumber<4>(free_dat_size), 4, 0x19C);
+        LOG(DEBUG) << "Finished updating header";
+        return DatOperationResult<>(SUCCESS, "File header patched successfully");;
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Возвращает размер dat файла в байтах (может отличаться от значения внутренней переменной file_size dat файла
+     */
+
+    DatOperationResult<long long> DatIO::GetActualDatSize() {
+        return DatOperationResult<long long>(actual_dat_size_, SUCCESS);
+    }
+
+
+    /*!
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Возвращает имя dat файла, которое было передано при инициализации
+     */
+
+    DatOperationResult<std::string> DatIO::GetFilename() {
+        if (filename_ == "none")
+            return DatOperationResult<std::string>("", ERROR, "DATIOGETFILENAME: not initialised");
+        return DatOperationResult<std::string>(filename_, SUCCESS);
+    }
+
+    /*!
+     * \author Gi1dor
+     * \date 30.06.2018
+     * Выводит информацию о состоянии модуля в файл
+     * \param[in] file указатель на объект файла, в конец которого будет напечатана информация
+     * \warning Файл file должен быть открыт для записи. После завершения работы функции файл остаётся открытым
+     */
+
+    void DatIO::PrintInformaion(FILE *file) {
+        if (!file)
+            return;
+
+        fprintf(file, "========== IO info ==========\n");
+        fprintf(file, "Filename = %s\n", filename_.c_str());
+        fprintf(file, "File header data:\n");
+        fprintf(file, "constant1 = %lld\n", constant1);
+        fprintf(file, "constant2 = %lld\n", constant2);
+        fprintf(file, "file_size = %lld\n", file_size);
+        fprintf(file, "version1 = %lld\n", version1);
+        fprintf(file, "version2 = %lld\n", version2);
+        fprintf(file, "fragment_journal_offset = %lld\n", fragmentation_journal_offset);
+        fprintf(file, "fragment_journal_end = %lld\n", fragmentation_journal_end);
+        fprintf(file, "fragment_journal_size = %lld\n", fragmentation_journal_size);
+        fprintf(file, "root_dir_offset = %lld\n", root_directory_offset);
+        fprintf(file, "free_dat_size = %lld\n", free_dat_size);
+        fprintf(file, "DatLibrary: EOF buffer size constant = %d\n", MAX_EOF_BUFFER);
+    }
+
+    unsigned int DatIO::GetHeaderHash() {
+        std::string values =
+                std::to_string(constant1) +
+                std::to_string(constant2) +
+                std::to_string(file_size) +
+                std::to_string(version1) +
+                std::to_string(version2) +
+                std::to_string(fragmentation_journal_offset) +
+                std::to_string(fragmentation_journal_end) +
+                std::to_string(fragmentation_journal_size) +
+                std::to_string(root_directory_offset) +
+                std::to_string(free_dat_size);
+
+        return RSHash(values);
+    }
+
+    void DatIO::ClearData() {
+        constant1 = -1;
+        constant2 = -1;
+        file_size = -1;
+        version1 = -1;
+        version2 = -1;
+        fragmentation_journal_size = -1;
+        fragmentation_journal_end = -1;
+        root_directory_offset = -1;
+        fragmentation_journal_offset = -1;
+        free_dat_size = -1;
+        actual_dat_size_ = -1;
+    }
+
+}
+}

+ 612 - 0
src/DatSubsystems/DatLocaleManager.cpp

@@ -0,0 +1,612 @@
+#include <DatSubsystems/DatLocaleManager.h>
+#include <EasyLogging++/easylogging++.h>
+#include <DatFile.h>
+#include <SubFile.h>
+
+#define DAT_LOCALE_DICT_VERSION 102
+
+namespace LOTRO_DAT {
+    DatLocaleManager::DatLocaleManager(DatFile *datFilePtr) : dat(datFilePtr), current_locale_(ORIGINAL) {
+    }
+
+    /*!
+     * \Author Gi1dor
+     * \date 06.07.2018
+     * Инициализация модуля. Должна происходить после инициализации модуля ввода-вывода и файловой системы.
+     * Считывает словарь и проверяет корректность (если структура словаря нарушена и основной является локаль PATHCED,
+     * то dat файл считается некорреткным (любое обновлене dat файла с активной локалью PATCHED нарушает структуру файла
+     * и делает использование в игре невозможным.
+     *
+     * \warning Не должна вызываться вручную! Автоматически вызывается в функции Initialise класса DatFile
+     *
+     * Структура словарей локализации:
+     * ======== LOCALE DICT STRUCTURE =========
+     * 4                                bytes for dict size (in bytes)
+     * 4                                bytes for locale version
+     * 4                                bytes for .dat file size (with patches)
+     * 4                                bytes for header hash
+     * 15                               bytes for "Hi from Gi1dor"
+     * 4                                bytes for LOCALE mark ("PATC" or "ORIG")
+     * 4                                bytes for orig_dict.size()
+     * (32 + 4) * orig_dict.size()      bytes for orig_dict data
+     * 4                                bytes for patch_dict.size()
+     * (32 + 4) * patch_dict.size()     bytes for patch_dict data
+     * 4                                bytes for inactive_categories dict
+     * 4 * inactive_categories.size()   bytes for inactive_categories data
+     * ========================================
+     * Помимо этого:
+     * 0x128-0x12C - Флаг, установлена ли альтернативная версия (flag)
+     * 0x12C-0x130 - Офсет начала словаря локализации (offset)
+     *
+     * \warning Если flag != 0 и offset == 0 - то dat файл считается повреждённым. Проверка осуществляется функцией CheckLocaleCorrectж
+     */
+
+    DatOperationResult<> DatLocaleManager::Init() {
+        if (!dat)
+            return DatOperationResult<>(ERROR, "LOCALEINIT: no connection with Dat (dat is nullptr)");
+
+        orig_dict_.clear();
+        patch_dict_.clear();
+        inactive_categories.clear();
+        current_locale_ = ORIGINAL;
+
+        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().value) {
+            if (CheckLocaleCorrect()) {
+                LOG(INFO) << "Dictionary offset is empty or incorrect. Passing.";
+                return DatOperationResult<>();
+            } else {
+                return DatOperationResult<>(ERROR,
+                                            "Locale dict is incorrect, through patched mark is standing. Dat file may be corrupted");
+            }
+        }
+
+        BinaryData locale_info(16);
+        dat->GetIO().ReadData(locale_info, 16, locale_offset);
+
+        long long dict_size = locale_info.ToNumber<4>(0);
+        long long dict_version = locale_info.ToNumber<4>(4);
+        long long patched_file_end = locale_info.ToNumber<4>(8);
+        long long dat_file_header_hash = locale_info.ToNumber<4>(12);
+
+        if (dict_version != DAT_LOCALE_DICT_VERSION) {
+            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
+            if (CheckLocaleCorrect())
+                return DatOperationResult<>(SUCCESS);
+            else
+                return DatOperationResult<>(ERROR,
+                                            "Version of locales' dictionary is incorrect, through patched mark is standing. Dat file may be corrupted");
+        }
+
+        if (dat_file_header_hash != dat->GetIO().GetHeaderHash()) {
+            LOG(INFO) << "Header hash, written after last patch apply doesn't match current hash. Seems like dat-file was modified somewhere else, so removing all patch data";
+            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
+            if (CheckLocaleCorrect())
+                return DatOperationResult<>(SUCCESS);
+            else
+                return DatOperationResult<>(ERROR,
+                                            "Version of locales' dictionary is incorrect, through patched mark is standing. Dat file may be corrupted");
+        }
+
+        BinaryData dicts_data = BinaryData((unsigned) dict_size);
+        dat->GetIO().ReadData(dicts_data, dict_size - 16, locale_offset + 16);
+
+        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 + 32);
+            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 + 32);
+            patch_dict_[file.file_id()] = file;
+            offset += 36;
+        }
+
+        size_t inactive_categories_set_size = size_t(dicts_data.ToNumber<4>(offset));
+        offset += 4;
+        for (size_t i = 0; i < inactive_categories_set_size; i++) {
+            size_t category_id = size_t(dicts_data.ToNumber<4>(offset));
+            inactive_categories.insert(category_id);
+            offset += 4;
+        }
+
+        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) << "There are " << inactive_categories.size() << " categories inactive: ";
+        LOG(INFO) << "Finished initialising locales";
+
+        if (CheckLocaleCorrect()) {
+            dat->GetFileSystem().patched_file_end = patched_file_end;
+            LOG(INFO) << "Locales initialisation success. Dictionary size is " << dict_size << ". Version is "
+                      << dict_version << ". Localed .dat size = " << patched_file_end;
+            return DatOperationResult<>(SUCCESS);
+        } else {
+            orig_dict_.clear();
+            patch_dict_.clear();
+            inactive_categories.clear();
+            current_locale_ = ORIGINAL;
+            return DatOperationResult<>(ERROR,
+                                        "Locale dict is incorrect, through patched mark is standing. Dat file may be corrupted");
+        }
+    }
+
+    /*!
+     * \Author Gi1dor
+     * \date 06.07.2018
+     * Смена активной локали. Меняет информацию о файлах в файловой системе на соответствующую информацию в локали.
+     * \param[in] locale Устанавливаемая локаль
+     * \warning Изменения в файловую систему вносятся локально! В dat файл изменения будут записаны во время деинициализации модуля файловой системы
+     */
+
+    DatOperationResult<> DatLocaleManager::SetLocale(DatLocaleManager::LOCALE locale) {
+        if (dat->GetStatusModule().GetStatus() == DatStatus::E_FREE)
+            dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
+
+        dat->GetStatusModule().SetDebugMessage("Changing locale to " +
+                                               (locale == PATCHED ? std::string(" patched version")
+                                                                  : std::string(" original version")));
+
+        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.";
+
+            if (dat->GetStatusModule().GetStatus() == DatStatus::E_COMMITING)
+                dat->GetStatusModule().ClearAll();
+
+            return DatOperationResult<>(SUCCESS);
+        }
+
+        std::map<long long, SubFile> &dict = GetLocaleDictReference(locale);
+
+        size_t files_total = dict.size();
+        size_t files_proceeded = 0;
+
+        for (const auto &file : dict) {
+            ++files_proceeded;
+            dat->GetStatusModule().SetPercentage(files_proceeded * 100 / files_total);
+
+            if (CategoryIsInactive(file.second.category) && locale == PATCHED)
+                continue;
+
+            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<SubFile> 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;
+
+        if (dat->GetStatusModule().GetStatus() == DatStatus::E_COMMITING)
+            dat->GetStatusModule().ClearAll();
+
+        return DatOperationResult<>(SUCCESS);
+    }
+
+
+    /*!
+     * \Author Gi1dor
+     * \date 06.07.2018
+     * Получение текущей активной локали. Если локали не существуют, понимается, что используется оригинальная (ORIGINAL)
+     */
+
+    DatLocaleManager::LOCALE DatLocaleManager::GetCurrentLocale() {
+        return current_locale_;
+    }
+
+
+    /*!
+     * \Author Gi1dor
+     * \date 06.07.2018
+     * Деинициализация модуля. Должна происходить перед деинициализацией модулей ввода-вывода и файловой системы.
+     */
+
+    DatOperationResult<> DatLocaleManager::DeInit() {
+        LOG(INFO) << "Committing locales...";
+        if (!dat)
+            return DatOperationResult<>(ERROR, "LOCALEDEINIT: no connection with Dat (dat is nullptr)");
+
+        auto result = CommitLocales();
+        ClearData();
+        return result;
+    }
+
+
+    /*!
+     * \Author Gi1dor
+     * \date 06.07.2018
+     * Обновление данных файла в локали
+     * \param[in] locale Локаль, для которой файл данных будет обновлён
+     * \param[in] file Новые данные файла. Старые данные будут перезаписаны
+     */
+
+    void DatLocaleManager::UpdateLocaleFile(DatLocaleManager::LOCALE locale, const SubFile &file) {
+        std::map<long long, SubFile> &dict = GetLocaleDictReference(locale);
+        dict[file.file_id()] = file;
+    }
+
+    /*!
+     * \Author Gi1dor
+     * \date 06.07.2018
+     * Получение данных файла в локали. Вернёт DatOperationResult.result = ERROR, если файла с таким id не существует.
+     * \param[in] file_id id файла, для которого необходимо получить данных
+     * \param[in] locale Локаль, данные которой будут получены
+     */
+
+    DatOperationResult<SubFile> DatLocaleManager::GetLocaleFile(long long file_id, DatLocaleManager::LOCALE locale) {
+        std::map<long long, SubFile> &dict = GetLocaleDictReference(locale);
+        if (dict.count(file_id) == 0)
+            return DatOperationResult<SubFile>(SubFile(), ERROR,
+                                               "GETLOCFILE: cannot get file with id = " + std::to_string(file_id) +
+                                               " from dict " + std::to_string(locale));
+        return DatOperationResult<SubFile>(dict[file_id], SUCCESS);
+    }
+
+
+    /*!
+     * \Author Gi1dor
+     * \date 06.07.2018
+     * Получение словаря файлов локали
+     * \param[in] locale Локаль, данные которой будут получены
+     */
+
+    std::map<long long, SubFile> &DatLocaleManager::GetLocaleDictReference(DatLocaleManager::LOCALE locale) {
+        return locale == PATCHED ? patch_dict_ : orig_dict_;
+    }
+
+
+    /*!
+     * \Author Gi1dor
+     * \date 06.07.2018
+     * Выводит информацию о состоянии модуля в файл
+     * \param[in] file указатель на объект файла, в конец которого будет напечатана информация
+     * \warning Файл file должен быть открыт для записи. После завершения работы функции файл остаётся открытым
+     */
+
+    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);
+
+        BinaryData locale_status_data(4);
+        dat->GetIO().ReadData(locale_status_data, 4, 296);
+        long long locale_status = locale_offset_data.ToNumber<4>(0);
+
+        fprintf(file, "Locale status = %lld\n", locale_status);
+
+        if (locale_offset != 0) {
+            BinaryData locale_info(16);
+            dat->GetIO().ReadData(locale_info, 16, 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);
+        }
+
+        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());
+    }
+
+
+    /*!
+     * \Author Gi1dor
+     * \date 06.07.2018
+     * Осуществляет проверку корректности dat файла с позиции локалей
+     * Файл считается некорректным, если активной является альтернативная локаль, причём нет возможности вернуть оригинальную (блок локалей неверный/повреждён)
+     * Байты 0x128-0x12C равны 0, если на момент пользования патчером активна оригинальная локаль, и DatIO::file_size, если нет
+     * Отличие значения в 0x128 от 0 и значения в 0x148 => файл ресурсов мог быть повреждён
+     */
+
+    bool DatLocaleManager::CheckLocaleCorrect() {
+        BinaryData locale_info(4);
+        dat->GetIO().ReadData(locale_info, 4, 296);
+        long long locale_status = locale_info.ToNumber<4>(0);
+
+        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().value)
+            return locale_status == 0;
+
+        BinaryData dicts_data = BinaryData(4);
+        auto operation = dat->GetIO().ReadData(dicts_data, 4, locale_offset + 16 + 15);
+        if (operation.result == ERROR)
+            return locale_status == 0;
+
+        BinaryData locale_data = dicts_data + BinaryData("\0", 1);
+        std::string locale((char *) (locale_data.data()));
+
+        LOCALE dat_locale = (locale == "PATC" ? PATCHED : ORIGINAL);
+
+        if ((locale_status == 0 && dat_locale == PATCHED) || (locale_status != 0 && dat_locale == ORIGINAL)) {
+            LOG(ERROR) << "CHCKLOCALECORRECT: Locale status and current_locale doesn't match!";
+            return false;
+        }
+
+        if (locale_status != 0 && locale_status != dat->GetIO().GetHeaderHash()){
+            LOG(ERROR) << "CHCKLOCALECORRECT: Locale hash doesn't match!";
+            return false;
+        }
+
+        return true;
+    }
+
+
+    /*!
+     * \Author Gi1dor
+     * \date 11.07.2018
+     * Проверка активности категории
+     * \returns true, если категория активна и false - если нет
+     */
+
+    bool DatLocaleManager::CategoryIsInactive(long long category) {
+        return inactive_categories.count(category) > 0;
+    }
+
+
+    /*!
+    * \Author Gi1dor
+    * \date 11.07.2018
+    * Обновляет категорию у файлов в словарях локалей
+    */
+
+    void DatLocaleManager::UpdateCategory(long long file_id, long long category) {
+        if (orig_dict_.count(file_id))
+            orig_dict_[file_id].category = category;
+        if (patch_dict_.count(file_id))
+            patch_dict_[file_id].category = category;
+    }
+
+    /*!
+    * \Author Gi1dor
+    * \date 21.10.2018
+    * Возвращает словарь, состоящий из id категорий, помеченных как неактивные
+    */
+
+    const std::set<long long> &DatLocaleManager::GetInactiveCategories() {
+        return inactive_categories;
+    }
+
+    /*!
+     * \Author Gi1dor
+     * \date 21.10.2018
+     * Записывает словарь. Если ранее словарь не существовал - новый будет записан в конец файла.
+     * Для словаря выделяется блок не менее 20мб
+     *
+     * \warning Не должна вызываться вручную! Автоматически вызывается в функции Deinitialise класса DatFile
+     *
+     * Структура словарей локализации:
+     * ======== LOCALE DICT STRUCTURE =========
+     * 4                                bytes for dict size (in bytes)
+     * 4                                bytes for locale version
+     * 4                                bytes for .dat file size (with patches)
+     * 4                                bytes for dat file header hash
+     * 15                               bytes for "Hi from Gi1dor"
+     * 4                                bytes for LOCALE
+     * 4                                bytes for orig_dict.size()
+     * (32 + 4) * orig_dict.size()      bytes for orig_dict data
+     * 4                                bytes for patch_dict.size()
+     * (32 + 4) * patch_dict.size()     bytes for patch_dict data
+     * 4                                bytes for inactive_categories dict
+     * 4 * inactive_categories.size()   bytes for inactive_categories data
+     * ========================================
+     * Помимо этого:
+     * 0x128-0x12C - 0, если выбрана локаль ORIGINAL и обновление клиентом игры не испортит .dat file
+     *               хэш хедера файла в противном случае.
+     *               Отличие значения в 0x128 от 0 и значения в 0x148 => файл ресурсов мог быть повреждён
+     *
+     * 0x12C-0x130 - Офсет начала словаря локализации
+     */
+
+    DatOperationResult<> DatLocaleManager::CommitLocales() {
+        if (!dat->Initialized())
+            return DatOperationResult<>(SUCCESS);
+
+        if (patch_dict_.empty()) {
+            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
+            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
+            return DatOperationResult<>(SUCCESS);
+        }
+
+        BinaryData binary_data = BinaryData(4 + 4 + 4 + 4 + 14 + 15 + 4
+                                            + 4 + (32 + 4) * orig_dict_.size()
+                                            + 4 + (32 + 4) * patch_dict_.size()
+                                            + 4 + 4 * inactive_categories.size());
+
+        // First 16 bytes will be filled just before writing data to file
+        size_t current_size = 16;
+
+        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) {
+            long long new_dict_offset = dat->GetFileSystem().patched_file_end + 16;
+
+            // Updating first 16 bytes
+            binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)), 0);
+            binary_data.Append(BinaryData::FromNumber<4>(DAT_LOCALE_DICT_VERSION), 4);
+            binary_data.Append(
+                    BinaryData::FromNumber<4>(dat->GetFileSystem().patched_file_end + binary_data.size() + 20 * 1024 * 1024), 8);
+            binary_data.Append(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 12);
+            auto operation = dat->GetIO().WriteData(binary_data, binary_data.size(), new_dict_offset);
+            if (operation.result != SUCCESS) {
+                return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales");
+            }
+
+            dat->GetIO().WriteData(BinaryData::FromNumber<4>(new_dict_offset), 4, 300);
+
+            if (current_locale_ == PATCHED) {
+                dat->GetIO().WriteData(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 4, 296);
+            } else {
+                dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
+            }
+            dat->GetFileSystem().patched_file_end += binary_data.size();
+
+            LOG(INFO) << "Writing 20 mbytes to " << dat->GetFileSystem().patched_file_end;
+
+            BinaryData nulls(unsigned(20 * 1024 * 1024));
+            dat->GetIO().WriteData(nulls, nulls.size(), dat->GetFileSystem().patched_file_end);
+            dat->GetFileSystem().patched_file_end += nulls.size();
+        } else {
+            binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)), 0);
+            binary_data.Append(BinaryData::FromNumber<4>(DAT_LOCALE_DICT_VERSION), 4);
+            binary_data.Append(BinaryData::FromNumber<4>(dat->GetFileSystem().patched_file_end), 8);
+            binary_data.Append(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 12);
+
+            if (current_locale_ == PATCHED) {
+                dat->GetIO().WriteData(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 4, 296);
+            } else {
+                dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
+            }
+
+            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 committed successfully";
+        return DatOperationResult<>(SUCCESS);
+    }
+
+    void DatLocaleManager::ClearData() {
+        orig_dict_.clear();
+        patch_dict_.clear();
+        inactive_categories.clear();
+        current_locale_ = LOCALE(-1);
+    }
+
+    DatOperationResult<> DatLocaleManager::EnableCategory(long long category) {
+        inactive_categories.erase(category);
+        dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
+        dat->GetStatusModule().SetPercentage(0);
+
+        size_t files_count = patch_dict_.size();
+        size_t files_processed = 0;
+
+        for (const auto& entry : patch_dict_) {
+            SubFile file = entry.second;
+            ++files_processed;
+            dat->GetStatusModule().SetPercentage(files_processed * 100 / files_count);
+
+            if (file.category == category) {
+                dat->GetFileSystem().UpdateFileInfo(file);
+            }
+        }
+
+        dat->GetStatusModule().ClearAll();
+        return DatOperationResult<>(SUCCESS);
+    }
+
+    DatOperationResult<> DatLocaleManager::DisableCategory(long long category) {
+        inactive_categories.insert(category);
+        dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
+        dat->GetStatusModule().SetPercentage(0);
+
+        size_t files_count = orig_dict_.size();
+        size_t files_processed = 0;
+
+        for (const auto& entry : orig_dict_) {
+            SubFile file = entry.second;
+            ++files_processed;
+            dat->GetStatusModule().SetPercentage(files_processed * 100 / files_count);
+
+            if (file.category == category) {
+                dat->GetFileSystem().UpdateFileInfo(file);
+            }
+        }
+
+        dat->GetStatusModule().ClearAll();
+        return DatOperationResult<>(SUCCESS);
+    }
+}

+ 192 - 0
src/DatSubsystems/DatPatcher.cpp

@@ -0,0 +1,192 @@
+#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);
+        }
+
+        if (dat->GetLocaleManager().CategoryIsInactive(file->category) &&
+            dat->GetLocaleManager().GetLocaleFile(file_id, DatLocaleManager::PATCHED).result == SUCCESS)
+        {
+            dat->GetFileSystem().UpdateFileInfo(
+                    dat->GetLocaleManager().GetLocaleFile(file_id, DatLocaleManager::PATCHED).value);
+        }
+
+        //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->GetFileSystem().patched_file_end;
+            new_file.block_size_ = std::max(data.size(), 256u);
+            dat->GetFileSystem().patched_file_end += 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
+        dat->GetLocaleManager().UpdateCategory(file_id, new_file.category);
+
+        if (dat->GetLocaleManager().CategoryIsInactive(new_file.category))
+            dat->GetFileSystem().UpdateFileInfo(
+                    dat->GetLocaleManager().GetLocaleFile(file_id, DatLocaleManager::ORIGINAL).value);
+
+        //LOG(INFO) << "Successfully patched file with id = " << file_id;
+
+        return DatOperationResult<>(SUCCESS);
+    }
+};

+ 42 - 0
src/DatSubsystems/DatStatus.cpp

@@ -0,0 +1,42 @@
+#include <DatSubsystems/DatStatus.h>
+#include <DatFile.h>
+
+namespace LOTRO_DAT {
+    void DatStatus::SetPercentage(unsigned percent) {
+        percentage_ = std::min(100u, percent);
+    }
+
+    unsigned DatStatus::GetPercentage() {
+        return percentage_;
+    }
+
+    bool DatStatus::CheckIfNotPatched() {
+        return dat->GetLocaleManager().patch_dict_.empty();
+    }
+
+    DatStatus::DatStatus(DatFile *datFilePtr) : dat(datFilePtr), percentage_(0), status_(E_FREE) {
+    }
+
+    void DatStatus::ClearAll() {
+        debug_message = "";
+        status_ = E_FREE;
+    }
+
+    void DatStatus::SetStatus(DatStatus::DAT_STATUS status) {
+        status_ = status;
+
+    }
+
+    DatStatus::DAT_STATUS DatStatus::GetStatus() {
+        return status_;
+    }
+
+    void DatStatus::SetDebugMessage(const std::string &message) {
+        debug_message = message;
+
+    }
+
+    std::string DatStatus::GetDebugMessage() {
+        return debug_message;
+    }
+}

+ 44 - 45
src/Examples/extractor_example.cpp

@@ -15,6 +15,7 @@ using namespace LOTRO_DAT;
 using namespace std;
 
 // Change these variables to true if you want export category to files.
+bool exportTextsToFiles = false;
 bool exportImagesToFiles = false;
 bool exportFontsToFiles = false;
 bool exportSoundsToFiles = false;
@@ -31,7 +32,7 @@ bool exportUnknownToDb = false;
 // There is no need to change anything else
 
 int main() {
-    std::cout << "Gi1dor's LotRO .dat extractor ver. 5.2.0" << std::endl;
+    std::cout << "Gi1dor's LotRO .dat extractor ver. " << LOTRO_DAT_VERSION << std::endl;
 
     std::cout << "Hello! I'm a basic shell version of .dat file extractor. I can open .dat file directly, "
             "if you write path to it (with name of file) in file \"dat_file_path.txt\"\n";
@@ -45,14 +46,12 @@ int main() {
         std::cout << "Using .dat file from dat_file_path.txt...\n";
         std::cout << "Opening file " << filename << std::endl;
 
-        if (file.InitDatFile(filename, 0) == false) {
-            std::cout << "Dat file path from dat_file_path.txt - " << filename << " may be incorrect (cannot open "
-                    "DatFile due to error. See it in errors.log)\n";
-            file.CloseDatFile();
-        }
+        auto operation = file.Initialise(filename, 0);
+        if (operation.result == false)
+            std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\n";
     }
 
-    while (file.DatFileState() == CLOSED) {
+    while (!file.Initialized()) {
         std::cout << "Please, tell, where the .dat file is\n";
         std::cout << "Enter path to file (including filename): ";
         std::string filename;
@@ -60,24 +59,14 @@ int main() {
 
         std::cout << "Opening file " << filename << std::endl;
 
-        if (file.InitDatFile(filename, 0) == false) {
-            std::cout << "Some error caused while opening the file... "
-                    "Could you enter .dat filename once more?" << std::endl;
-            file.CloseDatFile();
-        }
+        auto operation = file.Initialise(filename, 0);
+        if (operation.result == false)
+            std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\nTo try again enter path to .dat file\n";
     }
 
     std::cout << "Great! File initialised successfully!\n";
-    if (file.CheckIfNotPatched())
-        std::cout << "MESSAGE: Dat file is new and haven't been patched yet\n";
-
-    if (file.CheckIfPatchedByOldLauncher())
-        std::cout << "MESSAGE: Dat file was patched by old launcher. Capability isn't guaranteed! Some functions may not work properly!!!\n";
-
-    if (file.CheckIfUpdatedByGame())
-        std::cout << "MESSAGE: .dat file was updated by game! Need to repair patches with functions RepairPatches() and FinishRepairingPatches()\n";
-
-    std::cout << "Files number: " << file.files_number() << std::endl;
+    std::cout << "Gathering file information...";
+    file.GatherInformation("dat_info.log");
 
     int cmd = 0;
     while (true) {
@@ -91,6 +80,7 @@ int main() {
             std::string tmp;
             std::getline(std::cin, tmp);
         }
+
         if (cmd == -1) {
             std::cout << "Exiting. Thanks for using me!\n";
             break;
@@ -107,13 +97,13 @@ int main() {
             memcpy(out_time, ttime, 24);
             out_time[24] = '\0';
 
-            std::string output_dir = std::string("Extracted data\\") + file.filename().substr(file.filename().length() - 16, 16) + " " + std::string(out_time) + "\\";
+            std::string filename = file.GetIO().GetFilename().value;
+            std::cout << "FILENAME = " << filename << std::endl;
+            std::string output_dir = std::string("Extracted data\\") + filename.substr(std::max(filename.length() - 16, 0u), 16) + " " + std::string(out_time) + "\\";
             std::replace(output_dir.begin(), output_dir.end(), ':', '-');
 
             mkdir(output_dir.c_str(), 744);
             
-            std::cout << "Total files found: " << file.files_number() << std::endl << std::flush;
-            file.WriteUnorderedDictionary(output_dir);
             std::cout << "Beginning unpacking... Please, wait for some minutes."
                     "\nMaybe it's a good idea to have a cup of tea, while unpacker is working...\n" << std::flush;
 
@@ -121,63 +111,66 @@ int main() {
 
             if (exportImagesToDb) {
                 output_db.InitDatabase(output_dir + std::string("Images.db"));
-                std::cout << "Extracted " << file.ExtractAllFilesByType(JPG, &output_db) << " .jpg files to Images.db" << std::endl << std::flush;
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(JPG, &output_db).value << " .jpg files to Images.db" << std::endl << std::flush;
                 output_db.CloseDatabase();
             }
 
             if (exportSoundsToDb) {
                 output_db.InitDatabase(output_dir + std::string("Sounds.db"));
-                std::cout << "Extracted " << file.ExtractAllFilesByType(WAV, &output_db) << " .wav files to Sounds.db" << std::endl << std::flush;
-                std::cout << "Extracted " << file.ExtractAllFilesByType(OGG, &output_db) << " .ogg files to Sounds.db" << std::endl << std::flush;
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(WAV, &output_db).value << " .wav files to Sounds.db" << std::endl << std::flush;
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(OGG, &output_db).value << " .ogg files to Sounds.db" << std::endl << std::flush;
                 output_db.CloseDatabase();
             }
 
             if (exportTextsToDb) {
                 output_db.InitDatabase(output_dir + std::string("Texts.db"));
-                std::cout << "Extracted " << file.ExtractAllFilesByType(TEXT, &output_db) << " text files to Texts.db" << std::endl << std::flush;
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(TEXT, &output_db).value << " text files to Texts.db" << std::endl << std::flush;
                 output_db.CloseDatabase();
             }
 
             if (exportFontsToDb) {
                 output_db.InitDatabase(output_dir + std::string("Fonts.db"));
-                std::cout << "Extracted " << file.ExtractAllFilesByType(FONT, &output_db) << " font files to Fonts.db" << std::endl << std::flush;
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(FONT, &output_db).value << " font files to Fonts.db" << std::endl << std::flush;
                 output_db.CloseDatabase();
             }
 
             if (exportTexturesToDb) {
                 output_db.InitDatabase(output_dir + std::string("Textures.db"));
-                std::cout << "Extracted " << file.ExtractAllFilesByType(DDS, &output_db) << " .dds files to Textures.db" << std::endl << std::flush;
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(DDS, &output_db).value << " .dds files to Textures.db" << std::endl << std::flush;
                 output_db.CloseDatabase();
             }
 
             if (exportImagesToFiles) {
                 mkdir((output_dir + "jpg").c_str(), 744);
-                std::cout << "Extracted " << file.ExtractAllFilesByType(JPG, output_dir + "jpg\\") << " .jpg files to directory" << std::endl << std::flush;
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(JPG, output_dir + "jpg\\").value << " .jpg files to directory" << std::endl << std::flush;
             }
 
             if (exportTexturesToFiles) {
                 mkdir((output_dir + "dds").c_str(), 744);
-                std::cout << "Extracted " << file.ExtractAllFilesByType(DDS, output_dir + "dds\\") << " .dds files to directory" << std::endl << std::flush;
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(DDS, output_dir + "dds\\").value << " .dds files to directory" << std::endl << std::flush;
             }
 
             if (exportSoundsToFiles) {
                 mkdir((output_dir + "wav").c_str(), 744);
-                std::cout << "Extracted " << file.ExtractAllFilesByType(WAV, output_dir + "wav\\") << " .wav files to directory" << std::endl << std::flush;
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(WAV, output_dir + "wav\\").value << " .wav files to directory" << std::endl << std::flush;
                 mkdir((output_dir + "ogg").c_str(), 744);
-                std::cout << "Extracted " << file.ExtractAllFilesByType(OGG, output_dir + "ogg\\") << " .ogg files to directory" << std::endl << std::flush;
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(OGG, output_dir + "ogg\\").value << " .ogg files to directory" << std::endl << std::flush;
             }
 
             if (exportFontsToFiles) {
                 mkdir((output_dir + "fonts").c_str(), 744);
-                std::cout << "Extracted " << file.ExtractAllFilesByType(FONT, output_dir + "fonts\\") << " font files to directory" << std::endl << std::flush;
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(FONT, output_dir + "fonts\\").value << " font files to directory" << std::endl << std::flush;
             }
 
             if (exportUnknownToFiles) {
                 mkdir((output_dir + "unknown").c_str(), 744);
-                std::cout << "Extracted " << file.ExtractAllFilesByType(UNKNOWN, output_dir + "unknown\\") << " unknown files to directory" << std::endl << std::flush;
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(UNKNOWN, output_dir + "unknown\\").value << " unknown files to directory" << std::endl << std::flush;
             }
 
-            file.CloseDatFile();
+            if (exportTextsToFiles) {
+                mkdir((output_dir + "texts").c_str(), 744);
+                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(TEXT, output_dir + "texts\\").value << " text files to directory" << std::endl << std::flush;
+            }
 
             fprintf(stdout, "Spent %f seconds on running unpacker! Thank you for your patience!\n",
                     float(clock() - begin_time) / CLOCKS_PER_SEC);
@@ -185,10 +178,9 @@ int main() {
 
         if (cmd == 2) {
             std::cout << "Closing file...\n";
-            if (!file.CloseDatFile())
-                std::cout << "An error occured while closing the file!!!\n";
+            file.Deinitialize();
 
-            while (file.DatFileState() == CLOSED) {
+            while (!file.Initialized()) {
                 std::cout << "Please, tell, where the .dat file is\n";
                 std::cout << "Enter path to file (including filename): ";
                 std::string filename;
@@ -196,17 +188,20 @@ int main() {
 
                 std::cout << "Opening file " << filename << std::endl;
 
-                if (file.InitDatFile(filename, 0) == false) {
+                auto operation = file.Initialise(filename, 0);
+                if (operation.result == false) {
                     std::cout << "Some error caused while opening the file... "
                             "Could you enter .dat filename once more?" << std::endl;
-                    file.CloseDatFile();
+                    std::cout << "Error message: " << operation.msg << std::endl;
+                    file.Deinitialize();
                 }
             }
         }
 
         if (cmd == 3) {
             std::cout << "Current parameters:\n";
-            std::cout << "(01) Export texts to database - " << (exportTextsToDb ? "true\n" : "false\n");
+            std::cout << "(01) Export texts to files - " << (exportTextsToFiles ? "true\n" : "false\n");
+            std::cout << "(02) Export texts to database - " << (exportTextsToDb ? "true\n" : "false\n");
 
             std::cout << "(11) Export images to files - " << (exportImagesToFiles ? "true\n" : "false\n");
             std::cout << "(12) Export images to database - " << (exportImagesToDb ? "true\n" : "false\n");
@@ -237,6 +232,9 @@ int main() {
             } else {
                 switch (number) {
                     case 01:
+                        exportTextsToFiles = !exportTextsToFiles;
+                        break;
+                    case 02:
                         exportTextsToDb = !exportTextsToDb;
                         break;
                     case 11:
@@ -275,7 +273,8 @@ int main() {
             }
         }
     }
-    file.CloseDatFile();
+
+    file.Deinitialize();
     system("pause");
     return 0;
 }

+ 46 - 0
src/Examples/info_gatherer.cpp

@@ -0,0 +1,46 @@
+//
+// Created by kikab on 10.06.2018.
+//
+
+#include <LotroDat.h>
+using namespace LOTRO_DAT;
+
+#include <iostream>
+#include <fstream>
+#include <cstdlib>
+#include <ctime>
+
+int main() {
+    std::cout << "Gi1dor's LotRO .dat patcher ver. " << LOTRO_DAT_VERSION << std::endl;
+    freopen("patcher_errors.log", "w", stderr);
+
+    setbuf(stdout, nullptr);
+    setbuf(stderr, nullptr);
+
+    setvbuf (stdout, nullptr, _IONBF, BUFSIZ);
+    setvbuf (stderr, nullptr, _IONBF, BUFSIZ);
+
+    DatFile file;
+    std::ifstream in("dat_file_path.txt");
+
+    if (in.fail())
+        return 0;
+
+    std::string filename;
+    std::getline(in, filename);
+    std::cout << "Opening file " << filename << std::endl;
+    auto operation = file.Initialise(filename, 0);
+    if (operation.result != SUCCESS) {
+        std::cout << "Unable to initialize file. DatFile message: " << operation.msg;
+        system("pause");
+        return 0;
+    }
+
+    operation = file.GatherInformation("info.txt");
+    if (operation.result != SUCCESS)
+        std::cout << "Gather information wasn't successfully completed. DatFile message: " << operation.msg;
+    else
+        std::cout << "DatFile information gathered successfully!";
+    system("pause");
+    return 0;
+}

+ 22 - 43
src/Examples/patcher_example.cpp

@@ -15,7 +15,7 @@ using namespace LOTRO_DAT;
 using namespace std;
 
 int main() {
-    std::cout << "Gi1dor's LotRO .dat patcher ver. 6.0.0" << std::endl;
+    std::cout << "Gi1dor's LotRO .dat patcher ver. " << LOTRO_DAT_VERSION << std::endl;
     freopen("patcher_errors.log", "w", stderr);
 
     setbuf(stdout, nullptr);
@@ -35,18 +35,12 @@ int main() {
 
         std::cout << "Using .dat file from dat_file_path.txt...\n";
         std::cout << "Opening file " << filename << std::endl;
-        DAT_RESULT result = file.InitDatFile(filename, 0);
-        if (result >= 2) {
-            std::cout << "Dat file may be corrupted! It's opened, but all functions MAY WORK INCORRECTLY! It's better to DOWNLOAD CLEAR .DAT FILE\n";
-        }
-        if (result <= 0) {
-            std::cout << "Dat file path from dat_file_path.txt - " << filename << " may be incorrect (cannot open "
-                    "DatFile due to error. See it in errors.log)\n";
-            file.CloseDatFile();
-        }
+        auto operation = file.Initialise(filename, 0);
+        if (operation.result == ERROR)
+            std::cout << "Cannot initialise dat file " << filename << " \n";
     }
 
-    while (file.DatFileState() == CLOSED) {
+    while (!file.Initialized()) {
         std::cout << "Please, tell, where the .dat file is\n";
         std::cout << "Enter path to file (including filename): ";
         std::string filename;
@@ -54,26 +48,12 @@ int main() {
 
         std::cout << "Opening file " << filename << std::endl;
 
-        if (file.InitDatFile(filename, 0) == false) {
-                std::cout << "Some error caused while opening the file... "
-                        "Could you enter .dat filename once more?" << std::endl;
-            file.CloseDatFile();
-        }
+        auto operation = file.Initialise(filename, 0);
+        if (operation.result == ERROR)
+            std::cout << "Cannot initialise dat file " << filename << ", please, try again\n";
     }
 
     std::cout << "Great! File initialised successfully!\n";
-    file.WriteUnorderedDictionary("");
-
-    if (file.CheckIfNotPatched())
-        std::cout << "MESSAGE: Dat file is new and haven't been patched yet\n";
-
-    if (file.CheckIfPatchedByOldLauncher())
-        std::cout << "MESSAGE: Dat file was patched by old launcher. Capability isn't guaranteed! Some functions may not work properly!!!\n";
-
-    if (file.CheckIfUpdatedByGame())
-        std::cout << "MESSAGE: .dat file was updated by game! Need to repair patches with functions RepairPatches() and FinishRepairingPatches()\n";
-
-    std::cout << "Files number: " << file.files_number() << std::endl;
 
     while (true) {
         std::cout << "Please, choose, what should I do. I can patch datfile from database to .dat file (enter 1), "
@@ -90,6 +70,7 @@ int main() {
 
         if (cmd == -1) {
             std::cout << "Exiting. Thanks for using me!\n";
+            file.Deinitialize();
             break;
         }
 
@@ -130,7 +111,7 @@ int main() {
 
                 SubfileData subfile = db.GetNextFile();
                 while (!subfile.Empty()) {
-                    if (!file.PatchFile(subfile)) {
+                    if (file.GetPatcher().PatchFile(subfile).result == ERROR) {
                         fprintf(stderr, "Error! Caught exception while patching file! Passing it\n");
                     }
 
@@ -150,21 +131,21 @@ int main() {
             }
         }
         if (cmd == 2) {
-            std::cout << "Old locale is " << (file.current_locale() == PATCHED ? "RU" : "Original") << endl;
+            std::cout << "Old locale is " << (file.GetLocaleManager().GetCurrentLocale() == DatLocaleManager::PATCHED ? "RU" : "Original") << endl;
             std::cout << "Changing locale..." << std::endl;
-            file.SetLocale(file.current_locale() == PATCHED ? ORIGINAL : PATCHED);
-            std::cout << "New locale is " << (file.current_locale() == PATCHED ? "RU" : "Original") << endl << endl;
+            file.GetLocaleManager().SetLocale(file.GetLocaleManager().GetCurrentLocale() == DatLocaleManager::PATCHED  ? DatLocaleManager::ORIGINAL : DatLocaleManager::PATCHED);
+            std::cout << "New locale is " << (file.GetLocaleManager().GetCurrentLocale() == DatLocaleManager::PATCHED ? "RU" : "Original") << endl;
         }
 
         if (cmd == 3) {
-            std::cout << "Current locale is " << (file.current_locale() == PATCHED ? "RU" : "Original") << endl << endl;
+            std::cout << "Current locale is " << (file.GetLocaleManager().GetCurrentLocale() == DatLocaleManager::PATCHED ? "RU" : "Original") << endl;
         }
 
         if (cmd == 4) {
             int category_id = 0;
             std::cout << "Enter category id: ";
             std::cin >> category_id;
-            file.EnableCategory(category_id);
+            file.GetLocaleManager().EnableCategory(category_id);
             std::cout << "Category successfully enabled!" << std::endl;
         }
 
@@ -172,39 +153,37 @@ int main() {
             int category_id = 0;
             std::cout << "Enter category id: ";
             std::cin >> category_id;
-            file.DisableCategory(category_id);
+            file.GetLocaleManager().DisableCategory(category_id);
             std::cout << "Category successfully disabled!" << std::endl;
         }
 
         if (cmd == 6) {
             std::cout << "Disabled categories: ";
-            for (auto i : file.GetInactiveCategoriesList())
+            for (auto i : file.GetLocaleManager().GetInactiveCategories())
                 std::cout << i << " ";
             std::cout << endl;
         }
 
         if (cmd == 7) {
             std::cout << "Creating backup..." << std::endl;
-            std::cout << "Create backup function returned " << file.CreateBackup("cli_local_En.backup") << std::endl;
+            std::cout << "Create backup function returned " << file.GetBackupManager().CreateBackup("cli_local_En.backup").result << std::endl;
         }
 
         if (cmd == 8) {
             std::cout << "Removing backup..." << std::endl;
-            std::cout << "Remove backup function returned " << file.RemoveBackup("cli_local_En.backup") << std::endl;
+            std::cout << "Remove backup function returned " << file.GetBackupManager().RemoveBackup("cli_local_En.backup").result << std::endl;
         }
 
         if (cmd == 9) {
             std::cout << "Restoring file from backup..." << std::endl;
-            std::cout << "Restore file function returned " << file.RestoreFromBackup("cli_local_En.backup") << std::endl;
+            std::cout << "Restore file function returned " << file.GetBackupManager().RestoreFromBackup("cli_local_En.backup").result << std::endl;
         }
 
         if (cmd == 10) {
-            std::cout << "Backup file " << (file.CheckIfBackupExists("clie_local_En.backup") ? "exists!" : "doesn't exist.") << std::endl;
+            std::cout << "Backup file " << (file.GetBackupManager().CheckIfBackupAvailable("clie_local_En.backup") ? "exists!" : "doesn't exist.") << std::endl;
         }
     }
-    file.CloseDatFile();
-
-    system("pause");
+    //system("pause");
     return 0;
 }
 

+ 5 - 221
src/SubDirectory.cpp

@@ -1,230 +1,14 @@
 //
 // Created by Иван_Архипов on 07.11.2017.
 //
-#include "SubDirectory.h"
-
-#include "DatFile.h"
-#include "SubFile.h"
-#include "BinaryData.h"
-#include "SubfileData.h"
-#include "EasyLogging++/easylogging++.h"
-
-#include "Subfiles/TextSubFile.h"
-#include "Subfiles/DdsSubFile.h"
-#include "Subfiles/FontSubFile.h"
-#include "Subfiles/JpgSubFile.h"
-#include "Subfiles/OggSubFile.h"
-#include "Subfiles/WavSubFile.h"
-#include "Subfiles/UnknownSubFile.h"
-
+#include <SubDirectory.h>
 
 namespace LOTRO_DAT {
-    std::unordered_set<long long> SubDirectory::visited_subdirectories_ = std::unordered_set<long long>();
-
-    std::unordered_set<long long> SubDirectory::visited_subfiles_ = std::unordered_set<long long>();
-
-    std::set<std::shared_ptr<SubDirectory>, SubDirectory::SubDirectoryOffsetComp> SubDirectory::subdir_init_queue_
-           = std::set<std::shared_ptr<SubDirectory>, SubDirectory::SubDirectoryOffsetComp>();
-
-    std::set<std::shared_ptr<SubDirectory>, SubDirectory::SubDirectoryOffsetComp> SubDirectory::subfile_init_queue_
-            = std::set<std::shared_ptr<SubDirectory>, SubDirectory::SubDirectoryOffsetComp>();
-
-
-    SubDirectory::~SubDirectory() {
-        subfiles_.clear();
-        subdirs_.clear();
-    }
-
-    SubDirectory::SubDirectory(long long offset, DatFile &dat, long long max_subdirs) :
-            dat_(dat), offset_(offset), max_subdirs_(max_subdirs) {
-    }
-
-    bool SubDirectory::MakeSubDirectories() {
-        BinaryData data(1024);
-        dat_.ReadData(data, 63 * 8, offset_);
-
-        if (data.Empty()) {
-            LOG(ERROR) << "(READ ERROR) Incorrect directory at offset " << offset_;
-            return false;
-        }
-
-        if (data.ToNumber<4>(0) != 0 || data.ToNumber<4>(4) != 0) {
-            LOG(WARNING) << "first 8 bytes are not equal to 0 at offset " << offset_;
-            return false;
-        }
-
-        for (unsigned int i = 8; i < 63 * 8; i += 8) {
-            if (data.ToNumber<4>(i) == 0 || data.ToNumber<4>(i + 4) == 0)
-                break;
-
-//            if (visited_subdirectories_.count(data.ToNumber<4>(i + 4)) > 0) {
-//                LOG(WARNING) << "Visiting subdirectory at offset " << data.ToNumber<4>(i + 4) << " more than one time. Passing.";
-//                continue;
-//            }
-            visited_subdirectories_.insert(data.ToNumber<4>(i + 4));
-
-            std::shared_ptr<SubDirectory> subdir = std::make_shared<SubDirectory>(data.ToNumber<4>(i + 4), dat_);
-
-            //if (subdir->subfiles_.empty() && subdir->subdirs_.empty()) {
-            //    LOG(WARNING) << "Sub-subdirectory is empty or made empty... Dictionary offset = " << offset_ + i << "; Passing others";
-            //    break;
-            //} else {
-                subdirs_.push_back(subdir);
-                subdir_init_queue_.insert(subdir);
-            //}
-        }
-        return true;
-    }
-
-    bool SubDirectory::MakeSubFiles() {
-        BinaryData data = BinaryData(4);
-        dat_.ReadData(data, 4, offset_ + 63 * 8);
-
-        if (data.Empty()) {
-            LOG(ERROR) << "(READ ERROR) Incorrect directory at offset " << offset_;
-            return false;
-        }
-
-        auto subfiles_number = data.ToNumber<4>(0);
-        if (subfiles_number >= 64) {
-            LOG(ERROR) << "Incorrect directory (subfiles_num >= 64) at offset " << offset_;
-            return false;
-        }
-
-        subfiles_.resize(unsigned(subfiles_number), nullptr);
-        BinaryData headers(32 * unsigned(subfiles_number));
-        dat_.ReadData(headers, 32 * subfiles_number, offset_ + 63 * 8 + 4);
-
-        // LOG(INFO) << "============== DIRECTORY offset = " << offset_ <<  "=======================";
-
-        for (int i = 0; i < subfiles_number; i++) {
-            BinaryData header = headers.CutData(32 * i, 32 * (i + 1));
-
-            if (header.Empty()) {
-                LOG(ERROR) << "(READ ERROR) Incorrect directory (unable to read subfile data) at offset " << offset_;
-                return false;
-            }
-
-            if (header.ToNumber<4>(20) == 0 || header.ToNumber<4>(28) != 0)
-                continue;
-
-            if (visited_subfiles_.count(header.ToNumber<4>(4))) {
-                LOG(ERROR) << "Found duplicate file with id " << header.ToNumber<4>(4);
-                break;
-            }
-
-            subfiles_[i] = MakeSubfile(
-                    offset_ + 63 * 8 + 4 + 32 * i,
-                    header.ToNumber<4>(0), // unknown1
-                    header.ToNumber<4>(4), // file_id
-                    header.ToNumber<4>(8), // file_offset
-                    header.ToNumber<4>(12), // file_size
-                    header.ToNumber<4>(16), // timestamp
-                    header.ToNumber<4>(20), // version
-                    header.ToNumber<4>(24), // block_size
-                    header.ToNumber<4>(28) // unknown2 - must be zero??
-            );
-
-            // LOG(INFO) << subfiles_[i]->file_id();
-
-
-            //if (dat_.CorrectSubfile(subfiles_[i])) {
-                visited_subfiles_.insert(subfiles_[i]->file_id());
-//            } else {
-//                LOG(WARNING) << "Incorrect SubFile in directory at offset " << offset_ + 63 * 8 + 4 + 32 * i << " (id = " << subfiles_[i]->file_id() << ");";
-//                subfiles_[i] = nullptr;
-//                break;
-//            }
-        }
-        return true;
-    }
-
-    void SubDirectory::MakeDictionary(std::map<long long, std::shared_ptr<SubFile> > &dict) {
-        for (const std::shared_ptr<SubFile> &file: subfiles_) {
-            if (!file)
-                continue;
-            if (dict.count(file->file_id() != 0)) {
-                LOG(WARNING) << "Found multiple instances of file " << file->file_id() << " at dictionary offset "
-                             << file->dictionary_offset() << ". Base offset = " << dict[file->file_id()]->dictionary_offset();
-                if (!dat_.CorrectSubfile(file) || dat_.CorrectSubfile(dict[file->file_id_]))
-                    continue;
-            }
-            dict[file->file_id()] = file;
-        }
-
-        for (const auto &dir : subdirs_) {
-            dir->MakeDictionary(dict);
-        }
-    }
-
-    std::shared_ptr<SubFile> SubDirectory::MakeSubfile(long long dictionary_offset, long long unknown1, long long file_id,
-                                       long long file_offset, long long file_size, long long timestamp,
-                                       long long version, long long block_size, long long unknown2) {
-
-        FILE_TYPE type = GetSubfileType(file_id, file_offset);
-
-        switch (type) {
-            case TEXT:
-                return std::dynamic_pointer_cast<SubFile>(std::make_shared<TextSubFile>(dat_, dictionary_offset, unknown1, file_id, file_offset, file_size, timestamp, version, block_size, unknown2));
-            case JPG:
-                return std::dynamic_pointer_cast<SubFile>(std::make_shared<JpgSubFile>(dat_, dictionary_offset, unknown1, file_id, file_offset, file_size, timestamp, version, block_size, unknown2));
-            case DDS:
-                return std::dynamic_pointer_cast<SubFile>(std::make_shared<DdsSubFile>(dat_, dictionary_offset, unknown1, file_id, file_offset, file_size, timestamp, version, block_size, unknown2));
-            case WAV:
-                return std::dynamic_pointer_cast<SubFile>(std::make_shared<WavSubFile>(dat_, dictionary_offset, unknown1, file_id, file_offset, file_size, timestamp, version, block_size, unknown2));
-            case OGG:
-                return std::dynamic_pointer_cast<SubFile>(std::make_shared<OggSubFile>(dat_, dictionary_offset, unknown1, file_id, file_offset, file_size, timestamp, version, block_size, unknown2));
-            case FONT:
-                return std::dynamic_pointer_cast<SubFile>(std::make_shared<FontSubFile>(dat_, dictionary_offset, unknown1, file_id, file_offset, file_size, timestamp, version, block_size, unknown2));
-            case UNKNOWN:
-                return std::dynamic_pointer_cast<SubFile>(std::make_shared<UnknownSubFile>(dat_, dictionary_offset, unknown1, file_id, file_offset, file_size, timestamp, version, block_size, unknown2));
-        }
-        LOG(ERROR) << "Incorrect file type..";
-        return std::dynamic_pointer_cast<SubFile>(std::make_shared<UnknownSubFile>(dat_, dictionary_offset, unknown1, file_id, file_offset, file_size, timestamp, version, block_size, unknown2));
-    }
-
-    FILE_TYPE SubDirectory::GetSubfileType(long long file_id, long long file_offset) const {
-        // Text check based on file_id
-        if (((unsigned long long) file_id >> 24ull) == 0x25ull)
-            return TEXT;
-
-        // Font check based on file_id
-        if (((unsigned long long) file_id >> 24ull) == 0x42ull)
-            return FONT;
-
-        BinaryData header(64);
-        dat_.ReadData(header, 64, (unsigned) file_offset + 8);
-
-        if (header.Empty()) {
-            LOG(ERROR) << "Unable to read file header. file_id = " << file_id << ", offset = " << offset_;
-            return UNKNOWN;
-        }
-
-
-        // jpeg / dds check
-        if (((unsigned long long) file_id >> 24ull) == 0x41ull) {
-            long long soi = header.ToNumber<2>(24);
-            long long marker = header.ToNumber<2>(26);
-
-            //auto markerSize = header.ToNumber<short>(28);
-            //auto four = header.ToNumber<int>(30);
-
-            if ((soi == 0xD8FFll && marker == 0xE0FFll) || marker == 0xE1FFll)
-                return JPG;
-            return DDS;
-        }
-
-        // Ogg and Wav check
-        if (header[8] == 0x4F && header[9] == 0x67 && header[10] == 0x67 && header[11] == 0x53)
-            return OGG;
-        if (header[8] == 0x52 && header[9] == 0x49 && header[10] == 0x46 && header[11] == 0x46)
-            return WAV;
-
-        return UNKNOWN;
+    SubDirectory::SubDirectory(long long unknown, long long offset) :
+            unknown_(unknown), offset_(offset) {
     }
 
-    void SubDirectory::clear() {
-        subfiles_.clear();
-        subdirs_.clear();
+    long long SubDirectory::offset() {
+        return offset_;
     }
 };

+ 93 - 24
src/SubFile.cpp

@@ -8,15 +8,31 @@
 #include "SubfileData.h"
 #include "EasyLogging++/easylogging++.h"
 
+#include "DatOperationResult.h"
+
+#include "Subfiles/TextSubFile.h"
+#include "Subfiles/DdsSubFile.h"
+#include "Subfiles/FontSubFile.h"
+#include "Subfiles/JpgSubFile.h"
+#include "Subfiles/OggSubFile.h"
+#include "Subfiles/WavSubFile.h"
+#include "Subfiles/UnknownSubFile.h"
+
+
 #include <algorithm>
 
+
 const long long MAXSIZE = 50ll * 1024ll * 1024ll; // Setting maximal file size 50 MB; Files with size more than 50 mb
-                                                  // will be recognized as incorrect and passed.
-                                                  // This should be done because of not completely correct implementation
-                                                  // of Subfiles and Subdirectories search in DatFile.
+// will be recognized as incorrect and passed.
+// This should be done because of not completely correct implementation
+// of Subfiles and Subdirectories search in DatFile.
+
 namespace LOTRO_DAT {
+    SubFile::SubFile() : category(0), dat_(nullptr), unknown1_(0), file_id_(0), file_offset_(0), file_size_(0),
+                         timestamp_(0), version_(0), block_size_(0), unknown2_(0) {
+    }
 
-    SubFile::SubFile(DatFile &dat, const BinaryData &header) : dat_(dat) {
+    SubFile::SubFile(DatFile &dat, const BinaryData &header) : dat_(&dat) {
         category = 0;
 
         unknown1_ = header.ToNumber<4>(0); // unknown1
@@ -29,7 +45,7 @@ namespace LOTRO_DAT {
         unknown2_ = header.ToNumber<4>(28); // unknown2
 
         if (file_size_ > MAXSIZE) {
-            LOG(ERROR) << "Bad SubFile::SubFile() - File size of file " << file_id << " with offset " << file_offset
+            LOG(ERROR) << "Bad SubFile::SubFile() - File size of file " << file_id_ << " with offset " << file_offset_
                        << " is too much... Maybe it's incorrect..?";
             file_id_ = -1;
             return;
@@ -38,15 +54,15 @@ namespace LOTRO_DAT {
     }
 
     SubFile::SubFile(DatFile &dat, long long dictionary_offset, long long unknown1, long long file_id,
-                                long long file_offset,
-                                long long file_size, long long timestamp, long long version, long long block_size, long long unknown2) :
-            category(0), dat_(dat), dictionary_offset_(dictionary_offset), unknown1_(unknown1), file_id_(file_id),
-            file_offset_(file_offset),
-            file_size_(file_size), timestamp_(timestamp), version_(version), block_size_(block_size), unknown2_(unknown2) {
+                     long long file_offset, long long file_size, long long timestamp, long long version,
+                     long long block_size, long long unknown2) :
+            category(0), dat_(&dat), dictionary_offset_(dictionary_offset), unknown1_(unknown1), file_id_(file_id),
+            file_offset_(file_offset), file_size_(file_size), timestamp_(timestamp), version_(version),
+            block_size_(block_size), unknown2_(unknown2) {
 
         if (file_size_ > MAXSIZE) {
             LOG(ERROR) << "Bad SubFile::SubFile() - File size of file " << file_id << " with offset " << file_offset
-                       <<" is too much... Maybe it's incorrect..?";
+                       << " is too much... Maybe it's incorrect..?";
             file_id_ = -1;
             return;
         }
@@ -165,19 +181,72 @@ namespace LOTRO_DAT {
         return !(*this == b);
     }
 
-    SubFile &SubFile::operator=(const SubFile &b) {
-        if (*this == b || &dat_ != &b.dat_)
-            return *this;
 
-        category = b.category;
-        unknown1_ = b.unknown1_; // unknown1
-        file_id_ = b.file_id_; // file_id
-        file_offset_ = b.file_offset_; // file_offset
-        file_size_ = b.file_size_; // block_size
-        timestamp_ = b.timestamp_; // timestamp
-        version_ = b.version_; // version
-        block_size_ = b.block_size_; // block_size
-        unknown2_ = b.unknown2_; // unknown2
-        return *this;
+    std::shared_ptr<SubFile> SubFile::MakeSubfile(DatFile &dat, SubFile preinit_file) {
+        FILE_TYPE type = SubFile::GetSubfileType(dat, preinit_file);
+
+        switch (type) {
+            case TEXT:
+                return std::dynamic_pointer_cast<SubFile>(std::make_shared<TextSubFile>(preinit_file));
+            case JPG:
+                return std::dynamic_pointer_cast<SubFile>(std::make_shared<JpgSubFile>(preinit_file));
+            case DDS:
+                return std::dynamic_pointer_cast<SubFile>(std::make_shared<DdsSubFile>(preinit_file));
+            case WAV:
+                return std::dynamic_pointer_cast<SubFile>(std::make_shared<WavSubFile>(preinit_file));
+            case OGG:
+                return std::dynamic_pointer_cast<SubFile>(std::make_shared<OggSubFile>(preinit_file));
+            case FONT:
+                return std::dynamic_pointer_cast<SubFile>(std::make_shared<FontSubFile>(preinit_file));
+            case UNKNOWN:
+                return std::dynamic_pointer_cast<SubFile>(std::make_shared<UnknownSubFile>(preinit_file));
+            case NO_TYPE:
+                return std::shared_ptr<SubFile>();
+        }
+        LOG(ERROR) << "Incorrect file type..";
+        return std::dynamic_pointer_cast<SubFile>(std::make_shared<UnknownSubFile>(preinit_file));
     }
+
+    FILE_TYPE SubFile::GetSubfileType(DatFile &dat, SubFile preinit_file) {
+        // Text check based on file_id
+        if (((unsigned long long) preinit_file.file_id_ >> 24ull) == 0x25ull)
+            return TEXT;
+
+        // Font check based on file_id
+        if (((unsigned long long) preinit_file.file_id_ >> 24ull) == 0x42ull)
+            return FONT;
+
+        BinaryData header(64);
+        dat.GetIO().ReadData(header, 64, (unsigned) preinit_file.file_offset_ + 8);
+
+        if (header.Empty()) {
+            LOG(ERROR) << "Unable to read file header. file_id = " << preinit_file.file_id_ << ", offset = "
+                       << preinit_file.file_offset_;
+            return UNKNOWN;
+        }
+
+
+        // jpeg / dds check
+        if (((unsigned long long) preinit_file.file_id_ >> 24ull) == 0x41ull) {
+            long long soi = header.ToNumber<2>(24);
+            long long marker = header.ToNumber<2>(26);
+
+            //auto markerSize = header.ToNumber<short>(28);
+            //auto four = header.ToNumber<int>(30);
+
+            if ((soi == 0xD8FFll && marker == 0xE0FFll) || marker == 0xE1FFll)
+                return JPG;
+            return DDS;
+        }
+
+        // Ogg and Wav check
+        if (header[8] == 0x4F && header[9] == 0x67 && header[10] == 0x67 && header[11] == 0x53)
+            return OGG;
+        if (header[8] == 0x52 && header[9] == 0x49 && header[10] == 0x46 && header[11] == 0x46)
+            return WAV;
+
+        return UNKNOWN;
+    }
+
+
 };

+ 1 - 5
src/Subfiles/DdsSubFile.cpp

@@ -10,11 +10,7 @@
 #include "EasyLogging++/easylogging++.h"
 
 namespace LOTRO_DAT {
-    DdsSubFile::DdsSubFile(DatFile &dat, long long dictionary_offset, long long unknown1,
-                           long long file_id, long long file_offset, long long file_size,
-                           long long timestamp, long long version, long long block_size, long long unknown2)
-            : SubFile(dat, dictionary_offset, unknown1, file_id, file_offset, file_size,
-                      timestamp, version, block_size, unknown2) {
+    DdsSubFile::DdsSubFile(SubFile preinit_file) : SubFile(preinit_file) {
     }
 
     FILE_TYPE DdsSubFile::FileType() const {

+ 1 - 5
src/Subfiles/FontSubFile.cpp

@@ -10,11 +10,7 @@
 #include "EasyLogging++/easylogging++.h"
 
 namespace LOTRO_DAT {
-    FontSubFile::FontSubFile(DatFile &dat, long long dictionary_offset, long long unknown1,
-                             long long file_id, long long file_offset, long long file_size,
-                             long long timestamp, long long version, long long block_size, long long unknown2)
-            : SubFile(dat, dictionary_offset, unknown1, file_id, file_offset, file_size,
-                      timestamp, version, block_size, unknown2) {
+    FontSubFile::FontSubFile(SubFile preinit_file) : SubFile(preinit_file) {
     }
 
     FILE_TYPE FontSubFile::FileType() const {

+ 1 - 5
src/Subfiles/JpgSubFile.cpp

@@ -9,11 +9,7 @@
 #include "SubfileData.h"
 
 namespace LOTRO_DAT {
-    JpgSubFile::JpgSubFile(DatFile &dat, long long dictionary_offset, long long unknown1,
-                           long long file_id, long long file_offset, long long file_size,
-                           long long timestamp, long long version, long long block_size, long long unknown2)
-            : SubFile(dat, dictionary_offset, unknown1, file_id, file_offset, file_size,
-                      timestamp, version, block_size, unknown2) {
+    JpgSubFile::JpgSubFile(SubFile preinit_file) : SubFile(preinit_file) {
     }
 
     FILE_TYPE JpgSubFile::FileType() const {

+ 1 - 5
src/Subfiles/OggSubFile.cpp

@@ -10,11 +10,7 @@
 #include "EasyLogging++/easylogging++.h"
 
 namespace LOTRO_DAT {
-    OggSubFile::OggSubFile(DatFile &dat, long long dictionary_offset, long long unknown1,
-                           long long file_id, long long file_offset, long long file_size,
-                           long long timestamp, long long version, long long block_size, long long unknown2)
-            : SubFile(dat, dictionary_offset, unknown1, file_id, file_offset, file_size,
-                      timestamp, version, block_size, unknown2) {
+    OggSubFile::OggSubFile(SubFile preinit_file) : SubFile(preinit_file) {
     }
 
     FILE_TYPE OggSubFile::FileType() const {

+ 22 - 26
src/Subfiles/TextSubFile.cpp

@@ -51,11 +51,7 @@ std::string argumentsFromUtf16(const std::u16string &args) {
 namespace LOTRO_DAT {
     BinaryData TextSubFile::buffer_ = BinaryData(10 * 1024 * 1024);
 
-    TextSubFile::TextSubFile(DatFile &dat, long long dictionary_offset, long long unknown1,
-                             long long file_id, long long file_offset, long long file_size,
-                             long long timestamp, long long version, long long block_size, long long unknown2)
-            : SubFile(dat, dictionary_offset, unknown1, file_id, file_offset, file_size,
-                      timestamp, version, block_size, unknown2) {
+    TextSubFile::TextSubFile(SubFile preinit_file) : SubFile(preinit_file) {
     }
 
     FILE_TYPE TextSubFile::FileType() const {
@@ -141,7 +137,7 @@ namespace LOTRO_DAT {
         // first 4 bytes - file_id, then 4 bytes - unknown, then 1 byte - unknown
 
         if (old_data.ToNumber<4>(8) != file_id_) {
-            std::cout << "Error: file_id mismatch. Found " << old_data.ToNumber<4>(8) << ", expected: " << file_id_
+            LOG(WARNING) << "Error: file_id mismatch. Found " << old_data.ToNumber<4>(8) << ", expected: " << file_id_
                       << "\n";
         }
 
@@ -165,7 +161,7 @@ namespace LOTRO_DAT {
 
             auto fragment_iterator = std::lower_bound(patch_fragments_.begin(), patch_fragments_.end(), id_comp);
 
-            if (fragment_iterator == patch_fragments_.end()) {
+            if (fragment_iterator == patch_fragments_.end() || fragment_iterator->fragment_id != id_comp.fragment_id) {
                 // Retrieving old pieces
                 new_data = new_data + GetPieceData(old_data, offset);
                 // Retrieving old references
@@ -195,11 +191,12 @@ namespace LOTRO_DAT {
         LOG(DEBUG) << "Started parsing patch fragments";
 
         size_t pointer = 0;
+        patch_fragments_.clear();
         while (pointer < data.text_data.length()) {
             // Parsing fragment_id
             size_t pointer1 = data.text_data.find(u":::", pointer);
             if (pointer1 == std::u16string::npos) {
-                LOG(ERROR) << "Unable to parse fragment id! Cannot find '...' divider. File_id = " << file_id_;
+                LOG(ERROR) << "Unable to parse fragment id! Cannot find ':::' divider. File_id = " << file_id_;
                 return;
             }
             long long fragment_id = from_utf16(data.text_data.substr(pointer, pointer1 - pointer));
@@ -212,7 +209,7 @@ namespace LOTRO_DAT {
             // Parsing arguments
             pointer1 = data.text_data.find(u":::", pointer);
             if (pointer1 == std::u16string::npos) {
-                LOG(ERROR) << "Unable to parse arguments! Cannot find '...' divider. File_id = " << file_id_;
+                LOG(ERROR) << "Unable to parse arguments! Cannot find ':::' divider. File_id = " << file_id_;
                 return;
             }
             std::u16string arguments = data.text_data.substr(pointer, pointer1 - pointer);
@@ -316,13 +313,13 @@ namespace LOTRO_DAT {
                                    + u" gid:" + to_utf16(new_data.fragment_id);
 
         // Deleting '[' and ']' brackets
-        std::u16string text_data = new_data.text.substr(1, new_data.text.size() - 2) + file_data;
+        std::u16string text_data = new_data.text.substr(1, new_data.text.size() - 2);// + file_data;
 
 
-        if (file_id_ == 620757423 && new_data.fragment_id == 96627013) {
-            std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> codecvt;
-            std::cout << "AAAAAAAAAAAAA: " << codecvt.to_bytes(text_data) << std::endl;
-        }
+//        if (file_id_ == 620757423 && new_data.fragment_id == 96627013) {
+//            std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> codecvt;
+//            std::cout << "AAAAAAAAAAAAA: " << codecvt.to_bytes(text_data) << std::endl;
+//        }
         text_pieces_.clear();
 
         const std::u16string DNT = u"<--DO_NOT_TOUCH!-->";
@@ -338,14 +335,13 @@ namespace LOTRO_DAT {
 
         text_pieces_.push_back(text_data.substr(prev));
 
-        if (file_id_ == 620757423 && new_data.fragment_id == 96627013) {
-            std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> codecvt;
-            std::cout << "PIECES: SIZE = " << text_pieces_.size() << "..." << std::endl;
-            for (const auto &i : text_pieces_) {
-                std::cout << "   PIECE='" << codecvt.to_bytes(i) << "'\n";
-            }
-        }
-
+//        if (file_id_ == 620757423 && new_data.fragment_id == 96627013) {
+//            std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> codecvt;
+//            std::cout << "PIECES: SIZE = " << text_pieces_.size() << "..." << std::endl;
+//            for (const auto &i : text_pieces_) {
+//                std::cout << "   PIECE='" << codecvt.to_bytes(i) << "'\n";
+//            }
+//        }
 
         // Building BinaryData from pieces
         unsigned buffer_offset = 0;
@@ -378,10 +374,10 @@ namespace LOTRO_DAT {
         // Moving &offset pointer in &data
         GetArgumentReferenceData(data, offset);
 
-        if (file_id_ == 620757423 && new_data.fragment_id == 96627013) {
-            std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> codecvt;
-            std::cout << "ARG_REFS: " << new_data.args << std::endl;
-        }
+//        if (file_id_ == 620757423 && new_data.fragment_id == 96627013) {
+//            std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> codecvt;
+//            std::cout << "ARG_REFS: " << new_data.args << std::endl;
+//        }
 
         // If there are no args - making 4 null-bytes and return;
         if (new_data.args.empty()) {

+ 1 - 5
src/Subfiles/UnknownSubFile.cpp

@@ -10,11 +10,7 @@
 #include "EasyLogging++/easylogging++.h"
 
 namespace LOTRO_DAT {
-    UnknownSubFile::UnknownSubFile(DatFile &dat, long long dictionary_offset, long long unknown1,
-                                   long long file_id, long long file_offset, long long file_size,
-                                   long long timestamp, long long version, long long block_size, long long unknown2)
-            : SubFile(dat, dictionary_offset, unknown1, file_id, file_offset, file_size,
-                      timestamp, version, block_size, unknown2) {
+    UnknownSubFile::UnknownSubFile(SubFile preinit_file) : SubFile(preinit_file) {
     }
 
     FILE_TYPE UnknownSubFile::FileType() const {

+ 1 - 5
src/Subfiles/WavSubFile.cpp

@@ -10,11 +10,7 @@
 #include "EasyLogging++/easylogging++.h"
 
 namespace LOTRO_DAT {
-    WavSubFile::WavSubFile(DatFile &dat, long long dictionary_offset, long long unknown1,
-                           long long file_id, long long file_offset, long long file_size,
-                           long long timestamp, long long version, long long block_size, long long unknown2)
-            : SubFile(dat, dictionary_offset, unknown1, file_id, file_offset, file_size,
-                      timestamp, version, block_size, unknown2) {
+    WavSubFile::WavSubFile(SubFile preinit_file) : SubFile(preinit_file) {
     }
 
     FILE_TYPE WavSubFile::FileType() const {