Ivan Arkhipov 6 роки тому
батько
коміт
49c7759d42

+ 7 - 2
CHANGELOG

@@ -51,6 +51,11 @@ Version 5.1.0
     * Improved stability and safety while reading files' data.
 ----------------------------------------------------------------------
 Version 5.1.1
-    * Fixed problem, when game update caused .dat file corruption ("Unable to send request" error after update).
     * Improved locales' file commit process. Now fixed place in .dat file is reserved for locales, they won't 'eat' more data after every patch apply.
-----------------------------------------------------------------------
+----------------------------------------------------------------------
+Version 5.2.0
+    * Fixed (maybe) problem, when game update caused .dat file corruption ("Unable to send request" error after update).
+    * Fixed (maybe) incorrect work with iterations list
+    * 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.
+----------------------------------------------------------------------

+ 1 - 1
CMakeLists.txt

@@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD 14)
 set(PROJECT_BINARY_DIR bin)
 set(PROJECT_VERSION 0.1.0)
 
-SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS} -O9 -pipe -fomit-frame-pointer -ffast-math -Wall -Wextra" )
+SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS} -O2 -Wall -Wextra")
 SET(CMAKE_EXE_LINKER_FLAGS  "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
 
 if (MSVS)

BIN
bin/LotRO_dat_extractor.exe


BIN
bin/LotRO_dat_patcher.exe


+ 0 - 71
include/DatException.h

@@ -1,71 +0,0 @@
-//
-// Created by Иван_Архипов on 31.10.2017.
-//
-#ifndef LOTRO_DAT_PATCHER_DATEXCEPTION_H
-#define LOTRO_DAT_PATCHER_DATEXCEPTION_H
-
-//#define DAT_DEBUG
-
-#include <string>
-#include <iostream>
-#include <cstring>
-#include "EasyLogging++/easylogging++.h"
-
-extern  "C++"
-{
-namespace LOTRO_DAT
-{
-    enum DAT_EXCEPTION_TYPE {
-        READ_EXCEPTION,
-        WRITE_EXCEPTION,
-        SUBDIR_EXCEPTION,
-        SUBFILE_EXCEPTION,
-        IMPORT_EXCEPTION,
-        EXPORT_EXCEPTION,
-        DATA_EXCEPTION,
-        DATABASE_EXCEPTION,
-        UNKNOWN_EXCEPTION
-    };
-
-    class DatException : public std::exception {
-    public:
-        DatException()
-                : msg_(const_cast<char *>("Unknown DatException has been raised!"))
-                , type_(UNKNOWN_EXCEPTION)
-        {
-            LOG(ERROR) << "THROWN DatException " << std::string(msg_);
-        }
-
-        explicit DatException(const char *msg, DAT_EXCEPTION_TYPE type = UNKNOWN_EXCEPTION) noexcept
-                : std::exception()
-                , type_(type)
-        {
-            msg_ = new char[strlen(msg) + 1];
-            memcpy(msg_, msg, strlen(msg) + 1);
-            LOG(ERROR) << "THROWN DatException " << std::string(msg_);
-            #ifdef DAT_DEBUG
-                fprintf(stderr, "%s\n", msg_);
-            #endif
-        }
-
-        ~DatException() override
-        {
-            delete[] msg_;
-        }
-
-        const char* what() const noexcept override
-        {
-            return (std::string("DatException ") + std::string(msg_)).c_str();
-        }
-
-        DAT_EXCEPTION_TYPE type() const
-        {
-            return type_;
-        }
-    private:
-        char *msg_;
-        const DAT_EXCEPTION_TYPE type_;
-    };
-}
-}
-#endif //LOTRO_DAT_PATCHER_DATEXCEPTION_H

+ 18 - 8
include/DatFile.h

@@ -49,9 +49,6 @@ namespace LOTRO_DAT {
         //----BASE----///
         FAILED = 0,
         SUCCESS = 1,
-        //----TRUE/FALSE----//
-        FALSE = 0,
-        TRUE = 1,
 
         //----WARNINGS----//
         CORRUPTED_FILE_WARNING = 2,
@@ -68,7 +65,9 @@ namespace LOTRO_DAT {
         DAT_PATCH_FILE_ERROR = -10,
         DAT_READ_ERROR = -11,
         DAT_WRITE_ERROR = -12,
-        CRITICAL_DAT_ERROR = -14
+        CRITICAL_DAT_ERROR = -14,
+        NO_BACKUP_ERROR = -15,
+        REMOVE_FILE_ERROR = -16
     };
 
     enum DAT_STATE {
@@ -136,6 +135,9 @@ namespace LOTRO_DAT {
         DAT_RESULT MakeDictionary();
 
         DAT_RESULT ClearFragmentationJournal();
+
+        DAT_RESULT ModifyFragmentationJournal();
+
         // READ-WRITE SECTION
 
         DAT_RESULT ReadData(BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);
@@ -149,8 +151,8 @@ namespace LOTRO_DAT {
     private:
         long long free_buffered_size_;
 
-        const long long MAX_BUFFERED_SIZE = 50 * 1024 * 1024; // 50 megabytes;
-        const long long MIN_BUFFERED_SIZE = 5 * 1024 * 1024; // 5 megabytes;
+        const long long MAX_BUFFERED_SIZE = 10 * 1024 * 1024; // 50 megabytes;
+        const long long MIN_BUFFERED_SIZE = 1 * 1024 * 1024; // 5 megabytes;
 
         void AddBufferedSize();
 
@@ -197,6 +199,13 @@ namespace LOTRO_DAT {
 
         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, Subfile*>* GetLocaleDictReference(LOCALE locale);
 
@@ -215,8 +224,8 @@ namespace LOTRO_DAT {
 
         SubDirectory *root_directory_;
 
-        std::unordered_set<long long> pending_dictionary_;
-        std::unordered_map<long long, Subfile *> dictionary_;
+        std::set<long long> pending_dictionary_;
+        std::map<long long, Subfile *> dictionary_;
 
         long long constant1_;
         long long constant2_;
@@ -227,6 +236,7 @@ namespace LOTRO_DAT {
         long long fragmentation_journal_end_;
         long long root_directory_offset_;
         long long fragmentation_journal_offset_;
+        long long free_dat_size_;
 
         DAT_STATE dat_state_;
 

+ 0 - 1
include/LotroDat.h

@@ -5,7 +5,6 @@
 #include "DatFile.h"
 #include "Database.h"
 #include "SubfileData.h"
-#include "DatException.h"
 
 #include "yaml-cpp/yaml.h"
 #include "zlib.h"

+ 8 - 3
include/SubDirectory.h

@@ -9,6 +9,8 @@
 #include <map>
 #include <unordered_map>
 #include <unordered_set>
+#include "Subfile.h"
+
 
 extern "C++"
 {
@@ -26,19 +28,22 @@ namespace LOTRO_DAT
         SubDirectory();
         SubDirectory(long long offset, DatFile *dat, long long max_subdirs = 63);
         ~SubDirectory();
-        void MakeDictionary(std::unordered_map<long long, Subfile*> &dict);
+        void MakeDictionary(std::map<long long, Subfile*> &dict);
+
+        static std::set<long long> visited_subdirectories_;
     private:
         bool MakeSubDirectories();
-        bool MakeSubFiles();
 
+        bool MakeSubFiles();
         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;
 
+        FILE_TYPE GetSubfileType(long long file_id, long long file_offset) const;
         DatFile *dat_;
         long long offset_;
         long long max_subdirs_;
         std::vector<SubDirectory *> subdirs_;
+
         std::vector<Subfile *> subfiles_;
     };
 }

+ 4 - 0
include/Subfile.h

@@ -30,6 +30,7 @@ namespace LOTRO_DAT
 		Subfile(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);
+        Subfile& operator=(const Subfile &b);
 
         virtual FILE_TYPE FileType() const;
         virtual std::string Extension() const;
@@ -50,6 +51,9 @@ namespace LOTRO_DAT
         long long block_size() const;
 		long long unknown2() const;
 
+		bool operator ==(const Subfile &b) const;
+		bool operator !=(const Subfile &b) const;
+
         long long category;
 
 	protected:

BIN
lib/libLotroDat.dll.a


BIN
lib/libLotroDat_static.a


+ 51 - 48
src/BinaryData.cpp

@@ -3,8 +3,8 @@
 //
 
 #include "BinaryData.h"
-#include "DatException.h"
 #include "zlib.h"
+#include "EasyLogging++/easylogging++.h"
 
 #include <algorithm>
 
@@ -37,13 +37,13 @@ namespace LOTRO_DAT {
     }
 
     BinaryData::~BinaryData() {
-        if (size_ != 0 && data_ != nullptr)
-            delete[] data_;
+        delete[] data_;
     }
 
     unsigned char&  BinaryData::operator[](const unsigned int &pos) {
         if (pos >= size_)
-            throw DatException("Bad BinaryData::operator[]. Position is out of range.");
+            LOG(ERROR) << "Bad BinaryData::operator[]. Position " << pos
+                       << " is out of range in BinaryData with size " << size_;
         return data_[pos];
     }
 
@@ -59,67 +59,61 @@ 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 {
-        try {
-            long long ans = 0;
+        long long ans = 0;
 
-            if (pos + T > size_)
-                throw DatException("Bad BinaryData::ToNumber(). Reached end of BinaryData!", DATA_EXCEPTION);
+        if (pos + T > size_) {
+            LOG(ERROR) << "Reading " << T << " bytes from " << pos << " offset with BinaryData size " << size_
+                       << " Reached end of BinaryData!";
+            return 0;
+        }
 
-            for (int i = T - 1; i >= 0; i--)
-                ans = ((ans << 8ll) | data_[pos + i]);
+        for (int i = T - 1; i >= 0; i--)
+            ans = ((ans << 8ll) | data_[pos + i]);
 
-            return ans;
-        } catch (...) {
-            throw DatException("Bad BinaryData::ToNumber(). Error while using template function", DATA_EXCEPTION);
-        }
+        return ans;
     }
 
     // Translates T bytes from data into number in raw format
     template<unsigned int T>
     long long BinaryData::ToNumberRAW(const long long &pos) const {
-        try {
-            long long ans = 0;
+        long long ans = 0;
 
-            if (pos + T >= size_)
-                throw DatException("Bad BinaryData::ToNumber(). Reached end of BinaryData!", DATA_EXCEPTION);
+        if (pos + T > size_) {
+            LOG(ERROR) << "Reading " << T << " bytes from " << pos << " offset with BinaryData size " << size_
+                       << " Reached end of BinaryData!";
+            return 0;
+        }
 
-            for (unsigned i = 0; i < T; i++)
-                ans = ((ans << 8ll) | data_[pos + i]);
+        for (unsigned i = 0; i < T; i++)
+            ans = ((ans << 8ll) | data_[pos + i]);
 
-            return ans;
-        } catch (...) {
-            throw DatException("Bad BinaryData::ToNumber(). Error while using template function", DATA_EXCEPTION);
-        }
+        return ans;
     }
 
     // Makes data from specified T bytes of number in Little Endian encoding
     template<unsigned int T>
     BinaryData BinaryData::FromNumber(const long long &number) {
-        if (T < 0)
-            throw DatException("Bad BinaryData::FromNumber() - trying to make data from amount of bytes < 0");
-        BinaryData data(T);
-        try {
-            for (size_t i = 0; i < T; i++)
-                data.data_[i] = (unsigned char)((number >> (8 * i)) & 0xFF);
-        } catch (...) {
-            throw DatException("Bad BinaryData::ToNumber(). Error in using template function", DATA_EXCEPTION);
+        if (T <= 0) {
+            LOG(ERROR) << "Trying to make data from amount of bytes < 0";
+            return BinaryData(0);
         }
+
+        BinaryData data(T);
+        for (size_t i = 0; i < T; i++)
+            data.data_[i] = (unsigned char)((number >> (8 * i)) & 0xFF);
         return data;
     }
 
     // Makes data from specified T bytes of number in raw
     template<unsigned int T>
     BinaryData BinaryData::FromNumberRAW(const long long &number) {
-        if (T <= 0)
-            throw DatException("Bad BinaryData::FromNumber() - trying to make data from amount of bytes <= 0");
+        if (T <= 0) {
+            LOG(ERROR) << "Trying to make data from amount of bytes < 0";
+            return BinaryData(0);
+        }
 
         BinaryData data = FromNumber<T>(number);
-
-        try {
-            std::reverse(data.data_, data.data_ + data.size());
-        } catch (...) {
-            throw DatException("Bad BinaryData::ToNumber(). Error in using template function", DATA_EXCEPTION);
-        }
+        std::reverse(data.data_, data.data_ + data.size());
         return data;
     }
 
@@ -153,7 +147,8 @@ namespace LOTRO_DAT {
         FILE *f;
         f = fopen64(filename, "wb");
         if (f == nullptr) {
-            throw DatException("Bad BinaryData::WriteToFile() - unable to open output file", EXPORT_EXCEPTION);
+            LOG(ERROR) << "File " << std::string(filename) << " doesn't exist or is unreachable.. Cannot write data";
+            return false;
         }
 
         fwrite(data(), size(), 1, f);
@@ -169,7 +164,10 @@ namespace LOTRO_DAT {
         FILE *f;
         fopen_s(&f, filename, "rb");
         if (f == nullptr) {
-            throw DatException("Bad BinaryData::WriteToFile() - unable to open output file", EXPORT_EXCEPTION);
+            LOG(ERROR) << "File " << std::string(filename) << " doesn't exist.. Retuning null data";
+            size_ = 0;
+            delete[] data_;
+            return;
         }
 
         _fseeki64(f, 0, SEEK_END);
@@ -203,7 +201,8 @@ namespace LOTRO_DAT {
         int res = uncompress(decompressed.data_, &new_size, data_ + offset, size_ - offset);
 
         if (res != 0) {
-            throw DatException("Bad BinaryData::DecompressData() - uncompress() failed!", DATA_EXCEPTION);
+            LOG(ERROR) << "Failed to decompress. Function returned " << res;
+            return BinaryData(0);
         }
 
         decompressed.size_ = (unsigned int)new_size;
@@ -219,7 +218,8 @@ namespace LOTRO_DAT {
         int res = compress2(compressed.data_, &new_size, data_ + offset, size_ - offset, 9);
 
         if (res != 0) {
-            throw DatException("Bad BinaryData::CompressData() - compress failed!", DATA_EXCEPTION);
+            LOG(ERROR) << "Failed to compress. Function returned " << res;
+            return BinaryData(0);
         }
 
         compressed.size_ = (unsigned int)new_size;
@@ -230,8 +230,9 @@ namespace LOTRO_DAT {
         if (last < 0)
             last = size();
 
-        if (last > size())
-            throw DatException("Bad BinaryData::CutData() - parameter 'last' is out of range");
+        if (last > size()) {
+            LOG(ERROR) << "Unable to cut data - parameter last is out of range";
+        }
 
         BinaryData newdata(unsigned(last - first));
         memcpy(newdata.data(), data() + first, newdata.size());
@@ -256,8 +257,10 @@ namespace LOTRO_DAT {
     }
 
     void BinaryData::Append(const BinaryData &b, size_t append_offset) {
-        if (append_offset + b.size() > size())
-            throw DatException("Bad Binary:Data::Append() - data for appending has more bytes than BinaryData size!", DATA_EXCEPTION);
+        if (append_offset + b.size() > size()) {
+            LOG(ERROR) << "data for appending has more bytes than BinaryData size!";
+            return;
+        }
         memcpy(data_ + append_offset, b.data_, b.size_);
     }
 

+ 140 - 120
src/DatFile.cpp

@@ -5,14 +5,14 @@
 #include "DatFile.h"
 #include "BinaryData.h"
 
-#include "DatException.h"
 #include "SubDirectory.h"
 #include "Subfile.h"
 #include "SubfileData.h"
 
 #include <EasyLogging++/easylogging++.h>
 #include <unistd.h>
-
+#include <algorithm>
+#include <iterator>
 #include <locale>
 
 #define ELPP_FEATURE_CRASH_LOG
@@ -33,6 +33,10 @@ namespace LOTRO_DAT {
         file_handler_ = nullptr;
         free_buffered_size_ = 0;
 
+        orig_dict_.clear();
+        patch_dict_.clear();
+        dictionary_.clear();
+
         el::Configurations defaultConf;
         defaultConf.setToDefault();
         defaultConf.setGlobally(el::ConfigurationType::Format,
@@ -125,6 +129,8 @@ namespace LOTRO_DAT {
         LOG(INFO) << "Making last preparations...";
         return_value = std::max(return_value, result);
 
+        CheckIfUpdatedByGame();
+
         if (return_value >= 2) {
             LOG(WARNING) << "Dat file could be corrupted. Trying to delete corrupted dictionary rows";
             if (RepairDatFile() != SUCCESS)
@@ -143,14 +149,6 @@ namespace LOTRO_DAT {
         CloseDatFile();
     }
 
-    /// Extracts file with file_id.
-    /// If path is undefined then it will be recognised as current working directory
-    /// Output file path consists of "path + file_id + file_extension";
-    /// NOTICE: The directory, mentioned in "std::string path" variable SHOULD BE ALREADY CREATED;
-    /// Otherwise DatException() will be thrown.
-    /// Returns true, if file was successfully extracted;
-    /// Throws DatException() if undefined behaviour happened
-
     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) {
@@ -179,13 +177,6 @@ namespace LOTRO_DAT {
         return SUCCESS;
     }
 
-    /// Extracts file with file_id to database "db".
-    /// DATABASE SHOULD BE ALREADY CREATED; Otherwise DatException will be called.
-    /// NOTICE: The directory, mentioned in "std::string path" variable SHOULD BE ALREADY CREATED;
-    /// Otherwise DatException() will be thrown.
-    /// Returns true, if file was successfully extracted;
-    /// Throws DatException() if undefined behaviour happened
-
     DAT_RESULT DatFile::ExtractFile(long long file_id, Database *db) {
         LOG(DEBUG) << "Extracting file " << file_id << " to database.";
 
@@ -228,13 +219,6 @@ namespace LOTRO_DAT {
         return SUCCESS;
     }
 
-    /// Extracts all files with specific type to "path + type + file_id + file_part + extension" files;
-    /// If path is undefined then it will be recognised as current working directory
-    /// NOTICE: The directory, mentioned in "std::string path" variable SHOULD BE ALREADY CREATED;
-    /// Otherwise DatException() will be thrown.
-    /// Returns number of successfully extracted files
-    /// Throws DatException() if undefined behaviour happened
-
     int DatFile::ExtractAllFilesByType(FILE_TYPE type, std::string path) {
         LOG(INFO) << "Extracting all files to path " << path;
         if (dat_state_ < READY) {
@@ -254,11 +238,6 @@ namespace LOTRO_DAT {
         return success;
     }
 
-    /// Extracts all files with specific type to database "db";
-    /// DATABASE SHOULD BE ALREADY CREATED; Otherwise DatException will be called.
-    /// Returns number of successfully extracted files
-    /// Throws DatException() if undefined behaviour happened
-
     int DatFile::ExtractAllFilesByType(FILE_TYPE type, Database *db) {
         LOG(INFO) << "Extracting all files to database...";
 
@@ -278,7 +257,6 @@ namespace LOTRO_DAT {
         return success;
     }
 
-    // TODO: Write description and make asserts
     DAT_RESULT DatFile::PatchFile(const SubfileData &data) {
         LOG(DEBUG) << "Patching file with id = " << data.options["fid"].as<long long>() << ".";
 
@@ -304,11 +282,7 @@ namespace LOTRO_DAT {
         // 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]->file_offset_ = patch_dict_[file_id]->file_offset_;
-            dictionary_[file_id]->file_size_ = patch_dict_[file_id]->file_size_;
-            dictionary_[file_id]->block_size_ = patch_dict_[file_id]->block_size_;
-            dictionary_[file_id]->timestamp_ = patch_dict_[file_id]->timestamp_;
-            dictionary_[file_id]->version_ = patch_dict_[file_id]->version_;
+            *dictionary_[file_id] = *patch_dict_[file_id];
         }
 
         if (data.options["cat"].IsDefined()) {
@@ -327,12 +301,13 @@ namespace LOTRO_DAT {
         DAT_RESULT result = ApplyFilePatch(file, patch_data);
         if (result != SUCCESS)
             return result;
+        file->timestamp_ = std::time(0);
+        patch_dict_[file->file_id_]->timestamp_ =  file->timestamp_;
 
         LOG(DEBUG) << "Patched successfully file " << data.options["fid"].as<long long>() << ".";
         return SUCCESS;
     }
 
-    // TODO: Write description
     DAT_RESULT DatFile::PatchAllDatabase(Database *db) {
         LOG(INFO) << "Patching all database";
         if (dat_state_ < READY) {
@@ -353,10 +328,6 @@ namespace LOTRO_DAT {
         return SUCCESS;
     }
 
-    /// DatFile::WriteUnorderedDictionary(...);
-    /// Prints list of all found files with some information about them to file.
-    /// Gets std::string path - path to directory, where the file will be written with name "dict.txt"
-
     DAT_RESULT DatFile::WriteUnorderedDictionary(std::string path) const {
         LOG(INFO) << "Writing unordered dictionary to " << path << "dict.txt";
         FILE *f = nullptr;
@@ -378,16 +349,10 @@ namespace LOTRO_DAT {
         return SUCCESS;
     }
 
-    /// DatFile::files_number();
-    /// Returns amount of files, found in dictionaries of DatFile. Some if them may be empty or erased.
-
     long long DatFile::files_number() const {
         return dictionary_.size();
     }
 
-    /// DatFile::GetFileData()
-    /// Returns BinaryData, which contains of subfile data, made from parts of file in DatFile
-    // TODO: ASSERTS
     BinaryData DatFile::GetFileData(const Subfile *file, long long int offset) {
         LOG(DEBUG) << "Getting file " << file->file_id() << " data";
         BinaryData mfile_id(20);
@@ -436,9 +401,6 @@ namespace LOTRO_DAT {
         return data;
     }
 
-    /// DatFile special functions for opening and reading/writing raw data.
-    /// Shouldn't be used by any external classes except Subfile and Subdirectory.
-
     DAT_RESULT DatFile::OpenDatFile(const char *dat_name) {
         LOG(DEBUG) << "Started opening DatFile";
         if (dat_state_ != CLOSED) {
@@ -479,6 +441,7 @@ namespace LOTRO_DAT {
         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);
         auto size1 = data.ToNumber<4>(0x148);
 
         if (constant1_ != 0x4C5000) {
@@ -492,9 +455,9 @@ namespace LOTRO_DAT {
 
         if (file_size_ != size1) {
             LOG(ERROR) << "variable at 0x148 position is not equal to .dat file size!";
-            file_size_ = size1;
+            //file_size_ = size1;
             dat_state_ = SUCCESS_SUPERBLOCK;
-            return CORRUPTED_FILE_WARNING;
+            //return CORRUPTED_FILE_WARNING;
         }
 
         dat_state_ = SUCCESS_SUPERBLOCK;
@@ -576,9 +539,6 @@ namespace LOTRO_DAT {
         return SUCCESS;
     }
 
-    /// Special functions used by patch process.
-    /// Shouldn't be used by any external class.
-
     DAT_RESULT DatFile::ApplyFilePatch(Subfile *file, BinaryData &data) {
         LOG(DEBUG) << "Applying " << file->file_id() << " patch.";
 
@@ -657,27 +617,22 @@ namespace LOTRO_DAT {
         return SUCCESS;
     }
 
-    DAT_RESULT DatFile::ClearFragmentationJournal() {
-        LOG(DEBUG) << "Clearing fragmentation journal";
+    DAT_RESULT DatFile::ModifyFragmentationJournal() {
+        LOG(DEBUG) << "Modifying fragmentation journal";
 
-        long long offset = 0;
-        BinaryData data(32);
-        DAT_RESULT res = ReadData(data, 32, fragmentation_journal_offset_ + 8 + offset);
+        long long free_size = 128256;
+        long long free_offset = file_size_;
 
-        if (res != SUCCESS) {
-            LOG(ERROR) << "Error " << res << " while reading data";
-            return FAILED;
-        }
+        BinaryData nulldata = BinaryData(unsigned(free_size));
+        WriteData(nulldata, nulldata.size(), file_size_);
+        file_size_ += nulldata.size();
 
-        BinaryData nulls = BinaryData(32);
+        WriteData(BinaryData::FromNumber<4>(free_size), 4, fragmentation_journal_offset_ + 8);
+        WriteData(BinaryData::FromNumber<4>(free_offset), 4, fragmentation_journal_offset_ + 12);
 
-        while (data != nulls && !data.Empty()) {
-            WriteData(nulls, 32, fragmentation_journal_offset_ + 8 + offset);
-            offset += 32;
-            ReadData(data, 32, fragmentation_journal_offset_ + 8 + offset);
-        }
-        //fragmentation_journal_.emplace_back(std::make_pair(data.ToNumber<4>(0), data.ToNumber<4>(4)));
-        LOG(DEBUG) << "Finished getting fragmentation journal";
+        nulldata = BinaryData(8);
+        WriteData(nulldata, nulldata.size(), fragmentation_journal_offset_ + 16);
+        LOG(DEBUG) << "Finished modifying fragmentation journal";
         return SUCCESS;
     }
 
@@ -685,13 +640,15 @@ namespace LOTRO_DAT {
         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>(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;
     }
@@ -705,33 +662,56 @@ namespace LOTRO_DAT {
 
         // Commiting changes and updating/writing locales and header info
 
-        if (!pending_dictionary_.empty()) {
+        if (!pending_dictionary_.empty() || dat_state_ == UPDATED) {
             CommitLocales();
             CommitDirectories();
+            ModifyFragmentationJournal();
+            free_dat_size_ = 128254;
             fragmentation_journal_end_ = 0;
-            fragmentation_journal_size_ = 0;
+            fragmentation_journal_size_ = 1;
             UpdateHeader();
         }
-        ClearFragmentationJournal();
-
-        orig_dict_.clear();
-        pending_patch_.clear();
 
         current_locale_ = ORIGINAL;
 
         if (file_handler_ != nullptr) {
             fclose(file_handler_);
         }
-
+        SubDirectory::visited_subdirectories_.clear();
         delete root_directory_;
+        truncate64(filename_.c_str(), file_size_);
 
-        dictionary_.clear();
         free_buffered_size_ = 0;
 
-        truncate64(filename_.c_str(), file_size_);
         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;
     }
@@ -878,11 +858,7 @@ namespace LOTRO_DAT {
             if (orig_dict_.count(file_id) == 0 || subfile->file_offset() == orig_dict_[file_id]->file_offset())
                 return CRITICAL_DAT_ERROR;
 
-            dictionary_[file_id]->file_offset_ = orig_dict_[file_id]->file_offset_;
-            dictionary_[file_id]->file_size_ = orig_dict_[file_id]->file_size_;
-            dictionary_[file_id]->block_size_ = orig_dict_[file_id]->block_size_;
-            dictionary_[file_id]->timestamp_ = orig_dict_[file_id]->timestamp_;
-            dictionary_[file_id]->version_ = orig_dict_[file_id]->version_;
+            *dictionary_[file_id] = *orig_dict_[file_id];
             patch_dict_.erase(file_id);
             orig_dict_.erase(file_id);
         }
@@ -903,7 +879,10 @@ namespace LOTRO_DAT {
         dat_state_ = UPDATED;
         auto dict = GetLocaleDictReference(locale);
         for (auto file : *dict) {
-            if (dictionary_[file.first] == nullptr) {
+            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);
@@ -917,11 +896,7 @@ namespace LOTRO_DAT {
             long long file_id = file.first;
             Subfile *new_file = file.second;
 
-            dictionary_[file_id]->file_offset_ = new_file->file_offset_;
-            dictionary_[file_id]->file_size_ = new_file->file_size_;
-            dictionary_[file_id]->block_size_ = new_file->block_size_;
-            dictionary_[file_id]->timestamp_ = new_file->timestamp_;
-            dictionary_[file_id]->version_ = new_file->version_;
+            *dictionary_[file_id] = *new_file;
 
             pending_dictionary_.insert(file_id);
             dat_state_ = UPDATED;
@@ -936,20 +911,16 @@ namespace LOTRO_DAT {
         LOG(INFO) << "Checking if DatFile was updated by LotRO";
         if (!pending_patch_.empty())
             return true;
-        if (current_locale_ == ORIGINAL)
-            return false;
 
         bool updated = false;
 
         for (auto i : dictionary_) {
             long long file_id = i.first;
             Subfile *subfile = i.second;
-            if (inactive_categories.count(subfile->category) > 0)
+            if (patch_dict_.count(file_id) == 0)
                 continue;
-            if (patch_dict_.count(file_id) > 0
-                && (subfile->file_size() != patch_dict_[file_id]->file_size()
-                    || subfile->file_offset() != patch_dict_[file_id]->file_offset()
-                    || subfile->block_size() != patch_dict_[file_id]->block_size())) {
+
+            if (*subfile != *patch_dict_[file_id] && *subfile != *orig_dict_[file_id]) {
                 orig_dict_.erase(file_id);
                 patch_dict_.erase(file_id);
                 pending_patch_.insert(file_id);
@@ -957,7 +928,6 @@ namespace LOTRO_DAT {
                 dat_state_ = UPDATED;
             }
         }
-        LOG(INFO) << "Dat file " << (updated ? "WAS " : "WASN'T ") << "updated by game.";
         return updated;
     }
 
@@ -1082,11 +1052,7 @@ namespace LOTRO_DAT {
         for (auto file : dictionary_) {
             auto file_id = file.first;
             if (patch_dict_.count(file_id) > 0 && patch_dict_[file_id]->category == category) {
-                file.second->file_offset_ = patch_dict_[file_id]->file_offset_;
-                file.second->file_size_ = patch_dict_[file_id]->file_size_;
-                file.second->block_size_ = patch_dict_[file_id]->block_size_;
-                file.second->timestamp_ = patch_dict_[file_id]->timestamp_;
-                file.second->version_ = patch_dict_[file_id]->version_;
+                *file.second = *patch_dict_[file_id];
                 pending_dictionary_.insert(file_id);
             }
         }
@@ -1104,11 +1070,7 @@ namespace LOTRO_DAT {
         for (auto file : dictionary_) {
             auto file_id = file.first;
             if (orig_dict_.count(file_id) && orig_dict_[file_id]->category == category) {
-                file.second->file_offset_ = orig_dict_[file_id]->file_offset_;
-                file.second->file_size_ = orig_dict_[file_id]->file_size_;
-                file.second->block_size_ = orig_dict_[file_id]->block_size_;
-                file.second->timestamp_ = orig_dict_[file_id]->timestamp_;
-                file.second->version_ = orig_dict_[file_id]->version_;
+                *file.second = *orig_dict_[file_id];
                 pending_dictionary_.insert(file_id);
             }
         }
@@ -1137,15 +1099,6 @@ namespace LOTRO_DAT {
     }
 
     DAT_RESULT DatFile::CommitDirectories() {
-//        for (auto i : dictionary_) {
-//            if (i.second == nullptr) {
-//                LOG(WARNING) << "WHAT?? " << i.first;
-//                continue;
-//            }
-//            //i.second->block_size_ = 8;
-//            //WriteData(i.second->MakeHeaderData(), 32, i.second->dictionary_offset());
-//        }
-
         for (auto file_id : pending_dictionary_) {
             if (dictionary_[file_id] == nullptr)
                 continue;
@@ -1162,5 +1115,72 @@ namespace LOTRO_DAT {
         WriteData(nulls, MAX_BUFFERED_SIZE, file_size_);
         free_buffered_size_ = MAX_BUFFERED_SIZE;
     }
+
+    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;
+    }
 }
 }

+ 81 - 124
src/Database.cpp

@@ -3,7 +3,6 @@
 //
 
 #include "Database.h"
-#include "DatException.h"
 #include "BinaryData.h"
 #include "SubfileData.h"
 #include "EasyLogging++/easylogging++.h"
@@ -17,20 +16,17 @@ namespace LOTRO_DAT {
 
     bool Database::CloseDatabase() {
         LOG(DEBUG) << "Closing database.";
-        try {
-            if (db_ != nullptr) {
-                ExecSql("COMMIT TRANSACTION");
-                sqlite3_finalize(insert_request_);
-                sqlite3_finalize(fetch_one_request_);
-                sqlite3_finalize(get_rows_number_request_);
-                if (sqlite3_close_v2(db_) != SQLITE_OK)
-                    LOG(ERROR) << "Database error when closing: " << sqlite3_errmsg(db_);
-                db_ = nullptr;
-            }
-        } catch (std::exception &e) {
-            LOG(ERROR) << "caught " << e.what() << " exception";
-            return false;
+
+        if (db_ != nullptr) {
+            ExecSql("COMMIT TRANSACTION");
+            sqlite3_finalize(insert_request_);
+            sqlite3_finalize(fetch_one_request_);
+            sqlite3_finalize(get_rows_number_request_);
+            if (sqlite3_close_v2(db_) != SQLITE_OK)
+                LOG(ERROR) << "Database error when closing: " << sqlite3_errmsg(db_);
+            db_ = nullptr;
         }
+
         LOG(DEBUG) << "Database successfully closed.";
         return true;
     }
@@ -41,33 +37,30 @@ namespace LOTRO_DAT {
 
     bool Database::InitDatabase(const std::string &filename) {
         LOG(DEBUG) << "Initializing database " << filename;
-        try {
-            CloseDatabase();
-            if (sqlite3_open(filename.c_str(), &db_) != SQLITE_OK) {
-                sqlite3_close(db_);
-                LOG(ERROR) << "sqlite3_open returned an error.";
-                throw DatException("Bad Database::InitDatabase() - sqlite3_open returned an error...",
-                                   DATABASE_EXCEPTION);
-            }
-
-            ExecSql("PRAGMA synchronous = OFF");
-            ExecSql("PRAGMA count_changes = OFF");
-            ExecSql("PRAGMA journal_mode = MEMORY");
-            ExecSql("PRAGMA temp_store = MEMORY");
-            ExecSql("PRAGMA encoding = \"UTF-8\";");
-
-            ExecSql(CreateTableCommand_);
-
-            sqlite3_prepare_v2(db_, InsertFileCommand_.c_str(), InsertFileCommand_.length(), &insert_request_, nullptr);
-            sqlite3_prepare_v2(db_, FetchOneCommand.c_str(), FetchOneCommand.length(), &fetch_one_request_, nullptr);
-            sqlite3_prepare_v2(db_, GetRowsNumberCommand_.c_str(), GetRowsNumberCommand_.length(),
-                               &get_rows_number_request_, nullptr);
-
-            ExecSql("BEGIN TRANSACTION");
-        } catch (std::exception &e) {
-            LOG(ERROR) << "Caught " << e.what() << " exception.";
+
+        CloseDatabase();
+        if (sqlite3_open(filename.c_str(), &db_) != SQLITE_OK) {
+            sqlite3_close(db_);
+            db_ = nullptr;
+            LOG(ERROR) << "sqlite3_open returned an error. Unable to open file " << filename;
             return false;
         }
+
+        ExecSql("PRAGMA synchronous = OFF");
+        ExecSql("PRAGMA count_changes = OFF");
+        ExecSql("PRAGMA journal_mode = MEMORY");
+        ExecSql("PRAGMA temp_store = MEMORY");
+        ExecSql("PRAGMA encoding = \"UTF-8\";");
+
+        ExecSql(CreateTableCommand_);
+
+        sqlite3_prepare_v2(db_, InsertFileCommand_.c_str(), InsertFileCommand_.length(), &insert_request_, nullptr);
+        sqlite3_prepare_v2(db_, FetchOneCommand.c_str(), FetchOneCommand.length(), &fetch_one_request_, nullptr);
+        sqlite3_prepare_v2(db_, GetRowsNumberCommand_.c_str(), GetRowsNumberCommand_.length(),
+                           &get_rows_number_request_, nullptr);
+
+        ExecSql("BEGIN TRANSACTION");
+
         LOG(DEBUG) << "Database " << filename << " successfully initialized";
         return true;
     }
@@ -88,114 +81,78 @@ namespace LOTRO_DAT {
 
     bool Database::PushFile(const SubfileData &data) {
         LOG(DEBUG) << "Pushing to database file with file_id " << data.options["fid"].as<long long>();
-        try {
-            if (db_ == nullptr) {
-                LOG(WARNING) << "Trying to execute sql query to db, which hasn't been opened yet.";
-                return false;
-            }
-
-            std::stringstream options_;
-            options_ << data.options;
-
-            sqlite3_bind_blob(insert_request_, 1, data.binary_data.data(), data.binary_data.size(), SQLITE_TRANSIENT);
-            sqlite3_bind_text16(insert_request_, 2, data.text_data.c_str(), -1, SQLITE_TRANSIENT);
-            sqlite3_bind_text(insert_request_, 3, options_.str().c_str(), -1, SQLITE_TRANSIENT);
-
-            if (sqlite3_step(insert_request_) != SQLITE_DONE) {
-                LOG(ERROR) << "SQLite3 error: " << sqlite3_errmsg(db_);
-                return false;
-            }
-
-            sqlite3_reset(insert_request_);
-        } catch (std::exception &e) {
-            LOG(ERROR) << "Caught " << e.what() << " exception";
+
+        if (db_ == nullptr) {
+            LOG(WARNING) << "Trying to push file to db, which hasn't been opened yet.";
             return false;
         }
+
+        std::stringstream options_;
+        options_ << data.options;
+
+        sqlite3_bind_blob(insert_request_, 1, data.binary_data.data(), data.binary_data.size(), SQLITE_TRANSIENT);
+        sqlite3_bind_text16(insert_request_, 2, data.text_data.c_str(), -1, SQLITE_TRANSIENT);
+        sqlite3_bind_text(insert_request_, 3, options_.str().c_str(), -1, SQLITE_TRANSIENT);
+
+        if (sqlite3_step(insert_request_) != SQLITE_DONE) {
+            LOG(ERROR) << "SQLite3 error: " << sqlite3_errmsg(db_);
+            return false;
+        }
+
+        sqlite3_reset(insert_request_);
+
         LOG(DEBUG) << "File with file_id " << data.options["fid"].as<long long>() << " pushed to database successfully.";
         return true;
     }
 
     SubfileData Database::GetNextFile() {
         LOG(DEBUG) << "Getting next file from database..";
-        try {
-            SubfileData data;
-            if (db_ == nullptr) {
-                LOG(WARNING) << "Trying to execute sql query to db, which hasn't been opened yet.";
-                return data;
-            }
-
-            int result = sqlite3_step(fetch_one_request_);
 
-            if (result == SQLITE_ROW) {
-                data.binary_data = BinaryData((char *) sqlite3_column_blob(fetch_one_request_, 0),
-                                              unsigned(sqlite3_column_bytes(fetch_one_request_, 0)));
+        if (db_ == nullptr) {
+            LOG(WARNING) << "Trying to get next file from db, which hasn't been opened yet.";
+            return SubfileData();
+        }
 
-                data.text_data = std::u16string((char16_t *) sqlite3_column_text16(fetch_one_request_, 1));
+        SubfileData data;
+        int result = sqlite3_step(fetch_one_request_);
 
-                std::string _options = std::string((char *) sqlite3_column_text(fetch_one_request_, 2),
-                                                   unsigned(sqlite3_column_bytes(fetch_one_request_, 2)));
-                data.options = YAML::Load(_options);
-                return data;
-            }
+        if (result == SQLITE_ROW) {
+            data.binary_data = BinaryData((char *) sqlite3_column_blob(fetch_one_request_, 0),
+                                          unsigned(sqlite3_column_bytes(fetch_one_request_, 0)));
 
-            if (result == SQLITE_DONE) {
-                LOG(DEBUG) << "Next file fetched successfully.";
-                return data;
-            }
+            data.text_data = std::u16string((char16_t *) sqlite3_column_text16(fetch_one_request_, 1));
 
-            LOG(ERROR) << "SQLite3 fetch_one request returned " << result << " code. SQLite message is: "<< sqlite3_errmsg(db_);
+            std::string _options = std::string((char *) sqlite3_column_text(fetch_one_request_, 2),
+                                               unsigned(sqlite3_column_bytes(fetch_one_request_, 2)));
+            data.options = YAML::Load(_options);
             return data;
-        } catch (std::exception &e) {
-            LOG(ERROR) << "Caught " << e.what() << " exception.";
-            return SubfileData();
         }
-    }
 
-    bool Database::RemoveDatabase() {
-        try {
-            if (db_ == nullptr)
-                throw DatException("Bad Database::RemoveDatabase() - database hasn't been opened!", DATABASE_EXCEPTION);
-            throw DatException("Database::RemoveDatabase() haven't been implemented yet...", DATABASE_EXCEPTION);
-            // TODO: Implement function
-        } catch (std::exception &e) {
-            fprintf(stderr, "Bad Database::RemoveDatabase() - caught exception %s\n", e.what());
-            return false;
+        if (result == SQLITE_DONE) {
+            LOG(DEBUG) << "Next file fetched successfully.";
+            return data;
         }
-    }
 
-    bool Database::ClearDatabase() {
-        try {
-            if (db_ == nullptr)
-                throw DatException("Bad Database::ClearDatabase() - database hasn't been opened!", DATABASE_EXCEPTION);
-            ExecSql(ClearTableCommand_);
-            return true;
-        }  catch (std::exception &e) {
-            fprintf(stderr, "Bad Database::ClearDatabase() - caught exception %s\n", e.what());
-            return false;
-        }
+        LOG(ERROR) << "SQLite3 fetch_one request returned " << result << " code. SQLite message is: "<< sqlite3_errmsg(db_);
+        return data;
     }
 
     size_t Database::CountRows() {
         LOG(INFO) << "Counting rows in database...";
-        try {
-            if (db_ == nullptr) {
-                LOG(WARNING) << "Trying to execute sql query to db, which hasn't been opened yet.";
-                return 0;
-            }
-
-            int result = sqlite3_step(get_rows_number_request_);
-
-            if (result == SQLITE_ERROR) {
-                LOG(ERROR) << "Error when counting rows " << sqlite3_errmsg(db_);
-                return 0;
-            }
-            long long res = sqlite3_column_int64(get_rows_number_request_, 0);
-            sqlite3_reset(get_rows_number_request_);
-            LOG(INFO) << "Counted " << size_t(res) << " rows in database.";
-            return size_t(res);
-        } catch (std::exception &e) {
-            LOG(ERROR) << "Caught " << e.what() << "exception.";
+        if (db_ == nullptr) {
+            LOG(WARNING) << "Trying to execute sql query (Count rows) to db, which hasn't been opened yet.";
+            return 0;
+        }
+
+        int result = sqlite3_step(get_rows_number_request_);
+
+        if (result == SQLITE_ERROR) {
+            LOG(ERROR) << "Error when counting rows " << sqlite3_errmsg(db_);
             return 0;
         }
+        long long res = sqlite3_column_int64(get_rows_number_request_, 0);
+        sqlite3_reset(get_rows_number_request_);
+        LOG(INFO) << "Counted " << size_t(res) << " rows in database.";
+        return size_t(res);
     }
 }

+ 22 - 2
src/Examples/patcher_example.cpp

@@ -76,10 +76,11 @@ int main() {
     while (true) {
         std::cout << "Please, choose, what should I do. I can patch datfile from database to .dat file (enter 1), "
                 "change locale (enter 2), print current locale (enter 3), enable category (enter 4), disable category (enter 5), "
-                "print disabled categories (enter 6) or exit (enter -1)\n";
+                "print disabled categories (enter 6), create backup (enter 7), remove backup (enter 8), "
+                "restore .dat file from backup (enter 9), check if backup exists (enter 10) or exit (enter -1)\n";
 
         int cmd = 0;
-        std::cout << "Enter number of command (1-6): ";
+        std::cout << "Enter number of command (1-10): ";
         std::cin >> cmd;
 
         std::string tmp;
@@ -177,6 +178,25 @@ int main() {
                 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;
+        }
+
+        if (cmd == 8) {
+            std::cout << "Removing backup..." << std::endl;
+            std::cout << "Remove backup function returned " << file.RemoveBackup("cli_local_En.backup") << 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;
+        }
+
+        if (cmd == 10) {
+            std::cout << "Backup file " << (file.CheckIfBackupExists("clie_local_En.backup") ? "exists!" : "doesn't exist.") << std::endl;
+        }
     }
     file.CloseDatFile();
 

+ 27 - 32
src/SubDirectory.cpp

@@ -4,7 +4,6 @@
 #include "SubDirectory.h"
 
 #include "DatFile.h"
-#include "DatException.h"
 #include "Subfile.h"
 #include "BinaryData.h"
 #include "EasyLogging++/easylogging++.h"
@@ -18,6 +17,8 @@
 #include "Subfiles/UnknownSubfile.h"
 
 namespace LOTRO_DAT {
+    std::set<long long> SubDirectory::visited_subdirectories_ = std::set<long long>();
+
     SubDirectory::SubDirectory() {
         offset_ = 0;
     }
@@ -65,6 +66,12 @@ namespace LOTRO_DAT {
             if (data.ToNumber<4>(i) == 0 || data.ToNumber<4>(i + 4) == 0)
                 break;
 
+            if (visited_subdirectories_.count(data.ToNumber<4>(i + 4)) > 0) {
+                LOG(DEBUG) << "Visiting subdirectory at offset " << data.ToNumber<4>(i + 4) << " more than one time. Passing.";
+                continue;
+            }
+            visited_subdirectories_.insert(data.ToNumber<4>(i + 4));
+
             SubDirectory *subdir = new SubDirectory(data.ToNumber<4>(i + 4), dat_);
 
             if (subdir->subfiles_.empty() && subdir->subdirs_.empty()) {
@@ -103,26 +110,20 @@ namespace LOTRO_DAT {
             if (header.ToNumber<4>(20) == 0 || header.ToNumber<4>(28) != 0)
                 continue;
 
-            Subfile *subfile;
-            try {
-                subfile = 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??
-                );
-            } catch (std::exception &e) {
-                LOG(ERROR) << "Caught " << e.what() << " exception while making SubFile. Passing incorrect file.";
-                break;
-            }
+            Subfile *subfile = 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??
+            );
 
             if (dat_->CorrectSubfile(subfile)) {
-                subfiles_.push_back(subfile);
+                subfiles_.emplace_back(subfile);
             } else {
                 LOG(WARNING) << "Incorrect Subfile in directory at offset " << offset_ + 63 * 8 + 4 + 32 * i << " (id = " << subfile->file_id() << ");";
                 break;
@@ -131,7 +132,7 @@ namespace LOTRO_DAT {
         return true;
     }
 
-    void SubDirectory::MakeDictionary(std::unordered_map<long long, Subfile *> &dict) {
+    void SubDirectory::MakeDictionary(std::map<long long, Subfile *> &dict) {
         for (Subfile *i : subfiles_) {
             if (dict.count(i->file_id() != 0)) {
                 LOG(WARNING) << "Found multiple instances of file " << i->file_id() << " at dictionary offset "
@@ -181,20 +182,14 @@ namespace LOTRO_DAT {
             return FONT;
 
         BinaryData header(64);
-        try {
-            dat_->ReadData(header, 64, (unsigned) file_offset + 8);
-        }catch (DatException &e) {
-            if (e.type() == READ_EXCEPTION) {
-                LOG(ERROR) << "Unable to read file header. file_id = " << file_id << ", offset = " << offset_;
-                std::string err =
-                        "Bad Subfile::getExtension() - unable to read header of file with id = " +
-                        std::to_string(file_id) +
-                        " and offset = " + std::to_string(file_offset);
-                throw DatException(err.c_str(), SUBFILE_EXCEPTION);
-            } else
-                throw e;
+        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 ((file_id >> 24ll) == 0x41ll) {
             long long soi = header.ToNumber<2>(24);

+ 48 - 8
src/Subfile.cpp

@@ -5,8 +5,8 @@
 #include "Subfile.h"
 #include "BinaryData.h"
 #include "DatFile.h"
-#include "DatException.h"
 #include "SubfileData.h"
+#include "EasyLogging++/easylogging++.h"
 
 #include <algorithm>
 
@@ -40,9 +40,12 @@ namespace LOTRO_DAT {
             file_offset_(file_offset),
             file_size_(file_size), timestamp_(timestamp), version_(version), block_size_(block_size), unknown2_(unknown2) {
 
-        if (file_size_ > MAXSIZE)
-            throw DatException("Bad Subfile::Subfile() - File size is too much... Maybe it's incorrect..?",
-                               SUBFILE_EXCEPTION);
+        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..?";
+            file_id_ = -1;
+            return;
+        }
     }
 
     /// Typical getters of private fields, if there's need for getting information about SubFile from outside class.
@@ -89,7 +92,8 @@ namespace LOTRO_DAT {
     /// Returns enum FILE_TYPE value, which is declared in DatFile.h
 
     FILE_TYPE Subfile::FileType() const {
-        throw DatException("Bad Subfile::FileType() - function is not implemented for this type.", SUBFILE_EXCEPTION);
+        LOG(ERROR) << "INCORRECT IMPLEMENTATION!";
+        return UNKNOWN;
     }
 
     /// std::string Subfile::Extension()
@@ -97,7 +101,8 @@ namespace LOTRO_DAT {
     /// Returns std::string with extension, beggined with '.', ex. ".jpg", ".txt" and so on;
 
     std::string Subfile::Extension() const {
-        throw DatException("Bad Subfile::Extension() - function is not implemented for this type.", SUBFILE_EXCEPTION);
+        LOG(ERROR) << "INCORRECT IMPLEMENTATION!";
+        return ".subfile";
     }
 
     /// bool Subfile::PrepareForExport(...);
@@ -110,7 +115,8 @@ namespace LOTRO_DAT {
     /// Returns true if preparation was success. Otherwise returns false;
 
     SubfileData Subfile::PrepareForExport(const BinaryData &file_data) {
-        throw DatException("Bad Subfile::PrepareForExport() - function is not implemented for this type.", EXPORT_EXCEPTION);
+        LOG(ERROR) << "INCORRECT IMPLEMENTATION!";
+        return SubfileData();
     }
 
 
@@ -123,7 +129,8 @@ namespace LOTRO_DAT {
     /// Returns BinaryData - bytes array, prepared for writing in .dat file
 
     BinaryData Subfile::MakeForImport(const BinaryData &old_data, const SubfileData &data) {
-        throw DatException("Bad Subfile::MakeForImport() - function is not implemented for this type.", IMPORT_EXCEPTION);
+        LOG(ERROR) << "INCORRECT IMPLEMENTATION!";
+        return BinaryData(0);
     }
 
     BinaryData Subfile::MakeHeaderData() const {
@@ -137,4 +144,37 @@ namespace LOTRO_DAT {
                             + BinaryData::FromNumber<4>(unknown2_);
         return header;
     }
+
+
+    bool Subfile::operator==(const Subfile &b) const {
+        return unknown1_ == b.unknown1_
+               && file_id_ == b.file_id_
+               && file_offset_ == b.file_offset_
+               && file_size_ == b.file_size_
+               && timestamp_ == b.timestamp_
+               && version_ == b.version_
+               && block_size_ == b.block_size_
+               && unknown2_ == b.unknown2_;
+    }
+
+    bool Subfile::operator!=(const Subfile &b) const {
+        return !(*this == b);
+    }
+
+    Subfile &Subfile::operator=(const Subfile &b) {
+        if (*this == b)
+            return *this;
+
+        category = b.category;
+        dat_ = b.dat_;
+        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;
+    }
 };

+ 5 - 3
src/Subfiles/DdsSubfile.cpp

@@ -6,8 +6,8 @@
 
 #include "BinaryData.h"
 #include "DatFile.h"
-#include "DatException.h"
 #include "SubfileData.h"
+#include "EasyLogging++/easylogging++.h"
 
 namespace LOTRO_DAT {
     DdsSubfile::DdsSubfile() = default;
@@ -132,7 +132,8 @@ namespace LOTRO_DAT {
                 ddsData[109] = 16;
                 break;
             default:
-                throw DatException("Bad DdsSubfile::PrepareAsBinary() - unknown header format.", EXPORT_EXCEPTION);
+                LOG(ERROR) << "unknown header format.";
+                return SubfileData();
         }
 
         SubfileData result;
@@ -145,7 +146,8 @@ namespace LOTRO_DAT {
     BinaryData DdsSubfile::MakeForImport(const BinaryData &old_data, const SubfileData &data) {
         if (!data.options["ext"] || data.options["ext"].as<std::string>() != Extension() ||
             !data.options["fid"] || data.options["fid"].as<long long>() != file_id()) {
-            throw DatException("Bad DdsSubfile::MakeForImport() - invalid options data!", IMPORT_EXCEPTION);
+            LOG(ERROR) << "invalid options data!";
+            return BinaryData(0);
         }
         // TODO: COMPRESSED TEXTURES
         if (old_data.CheckCompression())

+ 3 - 2
src/Subfiles/FontSubfile.cpp

@@ -6,8 +6,8 @@
 
 #include "BinaryData.h"
 #include "DatFile.h"
-#include "DatException.h"
 #include "SubfileData.h"
+#include "EasyLogging++/easylogging++.h"
 
 namespace LOTRO_DAT {
     FontSubfile::FontSubfile() = default;
@@ -38,7 +38,8 @@ namespace LOTRO_DAT {
     BinaryData FontSubfile::MakeForImport(const BinaryData &old_data, const SubfileData &data) {
         if (!data.options["ext"] || data.options["ext"].as<std::string>() != Extension() ||
             !data.options["fid"] || data.options["fid"].as<long long>() != file_id()) {
-            throw DatException("Bad DdsSubfile::MakeForImport() - invalid options data!", IMPORT_EXCEPTION);
+            LOG(ERROR) << "invalid options data!";
+            return BinaryData(0);
         }
         return old_data.CutData(0, 8) + data.binary_data;
     }

+ 3 - 3
src/Subfiles/JpgSubfile.cpp

@@ -3,10 +3,9 @@
 //
 
 #include "Subfiles/JpgSubfile.h"
-
+#include "EasyLogging++/easylogging++.h"
 #include "BinaryData.h"
 #include "DatFile.h"
-#include "DatException.h"
 #include "SubfileData.h"
 
 namespace LOTRO_DAT {
@@ -38,7 +37,8 @@ namespace LOTRO_DAT {
     BinaryData JpgSubfile::MakeForImport(const BinaryData &old_data, const SubfileData &data) {
         if (!data.options["ext"] || data.options["ext"].as<std::string>() != Extension() ||
             !data.options["fid"] || data.options["fid"].as<long long>() != file_id()) {
-            throw DatException("Bad DdsSubfile::MakeForImport() - invalid options data!", IMPORT_EXCEPTION);
+            LOG(ERROR) << "invalid options data!";
+            return BinaryData(0);
         }
         BinaryData file_size = BinaryData::FromNumber<4>(data.binary_data.size());
         return old_data.CutData(0, 28) + file_size + data.binary_data;

+ 3 - 2
src/Subfiles/OggSubfile.cpp

@@ -6,8 +6,8 @@
 
 #include "BinaryData.h"
 #include "DatFile.h"
-#include "DatException.h"
 #include "SubfileData.h"
+#include "EasyLogging++/easylogging++.h"
 
 namespace LOTRO_DAT {
     OggSubfile::OggSubfile() = default;
@@ -38,7 +38,8 @@ namespace LOTRO_DAT {
     BinaryData OggSubfile::MakeForImport(const BinaryData &old_data, const SubfileData &data) {
         if (!data.options["ext"] || data.options["ext"].as<std::string>() != Extension() ||
             !data.options["fid"] || data.options["fid"].as<long long>() != file_id()) {
-            throw DatException("Bad DdsSubfile::MakeForImport() - invalid options data!", IMPORT_EXCEPTION);
+            LOG(ERROR) << "invalid options data!";
+            return BinaryData(0);
         }
         BinaryData file_size = BinaryData::FromNumber<4>(data.binary_data.size() - 8);
         BinaryData file_id = BinaryData::FromNumber<4>(this->file_id());

+ 8 - 5
src/Subfiles/TextSubfile.cpp

@@ -6,7 +6,6 @@
 
 #include "BinaryData.h"
 #include "DatFile.h"
-#include "DatException.h"
 #include "SubfileData.h"
 #include "EasyLogging++/easylogging++.h"
 
@@ -164,8 +163,10 @@ namespace LOTRO_DAT {
         while (pointer < text.length()) {
             // Parsing fragment_id
             size_t pointer1 = text.find(u":::", pointer);
-            if (pointer1 == std::u16string::npos)
-                throw DatException("Bad TextSubfile::ParsePatchFragments() - Unable to parse fragment id! Cannot find '...' divider");
+            if (pointer1 == std::u16string::npos) {
+                LOG(ERROR) << "Unable to parse fragment id! Cannot find '...' divider. File_id = " << file_id_;
+                return res;
+            }
             long long fragment_id = from_utf16(text.substr(pointer, pointer1 - pointer));
             pointer = pointer1 + 3;
             res[fragment_id] = SubfileData();
@@ -173,8 +174,10 @@ namespace LOTRO_DAT {
 
             // Parsing arguments
             pointer1 = text.find(u":::", pointer);
-            if (pointer1 == std::u16string::npos)
-                throw DatException("Bad TextSubfile::ParsePatchFragments() - Unable to parse arguments! Cannot find '...' divider");
+            if (pointer1 == std::u16string::npos) {
+                LOG(ERROR) << "Unable to parse arguments! Cannot find '...' divider. File_id = " << file_id_;
+                return res;
+            }
             std::u16string arguments = text.substr(pointer, pointer1 - pointer);
             pointer = pointer1 + 3;
             if (arguments.length() > 0) {

+ 3 - 2
src/Subfiles/UnknownSubfile.cpp

@@ -6,8 +6,8 @@
 
 #include "BinaryData.h"
 #include "DatFile.h"
-#include "DatException.h"
 #include "SubfileData.h"
+#include "EasyLogging++/easylogging++.h"
 
 namespace LOTRO_DAT {
     UnknownSubfile::UnknownSubfile() = default;
@@ -38,7 +38,8 @@ namespace LOTRO_DAT {
     BinaryData UnknownSubfile::MakeForImport(const BinaryData &old_data, const SubfileData &data) {
         if (!data.options["ext"] || data.options["ext"].as<std::string>() != Extension() ||
             !data.options["fid"] || data.options["fid"].as<long long>() != file_id()) {
-            throw DatException("Bad DdsSubfile::MakeForImport() - invalid options data!", IMPORT_EXCEPTION);
+            LOG(ERROR) << "invalid options data!";
+            return BinaryData(0);
         }
         return data.binary_data;
     }

+ 3 - 2
src/Subfiles/WavSubfile.cpp

@@ -6,8 +6,8 @@
 
 #include "BinaryData.h"
 #include "DatFile.h"
-#include "DatException.h"
 #include "SubfileData.h"
+#include "EasyLogging++/easylogging++.h"
 
 namespace LOTRO_DAT {
     WavSubfile::WavSubfile() = default;
@@ -38,7 +38,8 @@ namespace LOTRO_DAT {
     BinaryData WavSubfile::MakeForImport(const BinaryData &old_data, const SubfileData &data) {
         if (!data.options["ext"] || data.options["ext"].as<std::string>() != Extension() ||
             !data.options["fid"] || data.options["fid"].as<long long>() != file_id()) {
-            throw DatException("Bad DdsSubfile::MakeForImport() - invalid options data!", IMPORT_EXCEPTION);
+            LOG(ERROR) << "invalid options data!";
+            return BinaryData(0);
         }
         return old_data.CutData(0, 24) + data.binary_data;
     }