Przeglądaj źródła

Added patching from database.

Fixed many bugs connected with correctly writing data to .dat file.
implemented some minor functions. Code needs refactoring. Tested on
different machines, but not 100% stability (has some issues with
patching on Kubera's patched file)
Ivan Arkhipov 7 lat temu
rodzic
commit
832dfc1137

+ 19 - 0
Source/BinaryData.cpp

@@ -21,6 +21,12 @@ namespace LOTRO_DAT {
         memcpy(data_, d.data_, size_);
     }
 
+    BinaryData::BinaryData(char* data, unsigned int size) {
+        size_ = size;
+        data_ = new unsigned char[size_];
+        memcpy(data_, data, size_);
+    }
+
     BinaryData::BinaryData(unsigned int size) {
 		data_ = new unsigned char[size];
         size_ = size;
@@ -241,6 +247,19 @@ namespace LOTRO_DAT {
         return newdata;
     }
 
+    bool BinaryData::operator==(const BinaryData &b) const {
+        if (size() != b.size())
+            return false;
+        for (int i = 0; i < size(); i++)
+            if (data_[i] != b.data_[i])
+                return false;
+        return true;
+    }
+
+    bool BinaryData::operator!=(const BinaryData &b) const {
+        return !(*this == b);
+    }
+
 
     template long long BinaryData::ToNumber<1>(long long const&) const;
     template long long BinaryData::ToNumber<2>(long long const&) const;

+ 4 - 0
Source/BinaryData.h

@@ -19,12 +19,16 @@ namespace LOTRO_DAT
     public:
         BinaryData();
         BinaryData(const BinaryData &d);
+        explicit BinaryData(char* data, unsigned int size);
         explicit BinaryData(unsigned int size);
         ~BinaryData();
 
         unsigned char& operator[] (const unsigned int &pos);
         BinaryData operator +(const BinaryData &b);
 
+        bool operator == (const BinaryData &b) const;
+        bool operator != (const BinaryData &b) const;
+
         template <int T>
         long long ToNumber(const long long &pos) const;
 

BIN
Source/Common/Yaml/libyaml-cppmtd.lib


+ 33 - 22
Source/DatFile.cpp

@@ -39,6 +39,10 @@ namespace LOTRO_DAT {
         else
             throw DatException("Bad DatFile initialization! Not all init states were successfully passed!",
                                INIT_EXCEPTION);
+
+        //patched_ = true;
+        //for (auto i : dictionary_)
+        //    patched_list[i.second->file_id()] = new BinaryData(i.second->MakeHeaderData());
     }
 
     DatFile::~DatFile() {
@@ -48,6 +52,9 @@ namespace LOTRO_DAT {
             std::cout << "Updated header..." << std::endl << std::flush;
             UpdateSubdirectories();
             std::cout << "Updated subdirectories..." << std::endl << std::flush;
+            std::cout << "Changed " << patched_list.size() << " files..." << std::endl << std::flush;
+            for (auto i : patched_list)
+                delete i.second;
         }
 
         if (file_handler_ != nullptr)
@@ -194,7 +201,7 @@ namespace LOTRO_DAT {
         return success;
     }
 
-    /// TODO: Write desctiption
+    /// TODO: Write description and make asserts
     bool DatFile::PatchFile(const char *filename, YAML::Node options, long long dat_id) {
         if (dat_id != dat_id_)
             return false;
@@ -222,7 +229,17 @@ namespace LOTRO_DAT {
         return true;
     }
 
-    /// TODO: Write description
+    /// TODO: Write description and make asserts
+    bool DatFile::PatchFile(const BinaryData &binary_data, const std::u16string &text_data, YAML::Node &options) {
+        auto file_id = options["fid"].as<long long>();
+        Subfile *file = dictionary_[file_id];
+        BinaryData old_data = GetFileData(file);
+        BinaryData data = file->MakeForImport(old_data, binary_data, text_data, options);
+        ApplyFilePatch(file, data);
+        return true;
+    }
+
+    /// TODO: Write description and make asserts
     bool DatFile::PatchDatabase(Database *db) {
         return false;
     }
@@ -287,7 +304,6 @@ namespace LOTRO_DAT {
         return data;
     }
 
-
     /// DatFile constants' getters.
 
     long long DatFile::constant1() const {
@@ -414,11 +430,11 @@ namespace LOTRO_DAT {
         if (dat_state_ != READY)
             throw DatException("Bad DatFile::WriteData() - DatFile isn't in valid state!", WRITE_EXCEPTION);
 
-        _fseeki64(file_handler_, offset, SEEK_SET);
+       _fseeki64(file_handler_, offset, SEEK_SET);
         if (data_offset + size > data.size())
             throw DatException("Bad DatFile::WriteData - trying to write more than BinaryData size", WRITE_EXCEPTION);
 
-        fwrite(data.data() + data_offset, unsigned(size), 1, file_handler_);
+        int x = fwrite(data.data() + data_offset, unsigned(size), 1, file_handler_);		
     }
 
     /// Special functions used by patch process.
@@ -426,30 +442,32 @@ namespace LOTRO_DAT {
 
     void DatFile::ApplyFilePatch(Subfile *file, const BinaryData &data) {
         if (patched_list.count(file->file_id()) != 0) {
+            fprintf(stderr, "Warning: DatFile::ApplyFilePatch - found 2 files in patch with the same file_id. Passing last...\n");
             return;
         }
 
+        patched_list[file->file_id()] = new BinaryData(file->MakeHeaderData());
+        patched_ = true;
+
         auto journal = GetFragmentationJournal();
 
         file->file_size_ = data.size() - 8;
-        file->fragments_count_ = 0;
+        //file->fragments_count_ = 0;
 
         if (data.size() > file->block_size()) {
             file->file_offset_ = journal[0].second;
             file->block_size_ = data.size();
+
             journal[0].second += data.size();
 
-            if (journal[0].first < data.size()) {
-                UpdateFileSize();
-                journal[0].first = 0;
-                std::cout << "File size updated - new size is " << file_size_ << std::endl;
-            } else {
-                journal[0].first -= data.size();
-            }
+            BinaryData nulls(data.size());
+            WriteData(nulls, nulls.size(), file_size_);
+
+            this->file_size_ += data.size();
         }
 
         BinaryData fragments_count(4);
-        fragments_count.FromNumber<4>(file->fragments_count_);
+        fragments_count.FromNumber<4>(0);
 
         BinaryData file_data = fragments_count + data.CutData(4);
 
@@ -458,17 +476,15 @@ namespace LOTRO_DAT {
                                        "Patch wasn't written to .dat file");
 
         WriteData(file_data, file_data.size(), file->file_offset());
-        patched_list[file->file_id()] = new BinaryData(file->MakeHeaderData());
         UpdateFragmentationJournal(journal);
     }
 
     void DatFile::UpdateSubdirectories() {
-        root_directory_->UpdateDirectories(patched_list);
+        root_directory_->UpdateDirectories(patched_list, dictionary_);
     }
 
     std::vector<std::pair<long long, long long> > DatFile::GetFragmentationJournal() {
         BinaryData data(8);
-        std::cout << "journal offset: " << fragmentation_journal_offset_ << std::endl;
         ReadData(data, 8, fragmentation_journal_offset_ + 8);
         std::vector<std::pair<long long, long long> > result;
         result.emplace_back(std::make_pair(data.ToNumber<4>(0), data.ToNumber<4>(4)));
@@ -513,10 +529,5 @@ namespace LOTRO_DAT {
             WriteData(data, 4, fragmentation_journal_offset_ + 8 * (i + 1) + 4);
         }
     }
-
-    void DatFile::UpdateFileSize() {
-        _fseeki64(file_handler_, 0, SEEK_END);
-        file_size_ = _ftelli64(file_handler_);
-    }
 }
 }

+ 2 - 4
Source/DatFile.h

@@ -20,6 +20,7 @@
 #include "Database.h"
 
 // Dat file names definitions
+
 #define CLIENT_LOCAL_ENGLISH 0
 #define CLIENT_GENERAL 1
 #define CLIENT_SOUND 2
@@ -69,7 +70,7 @@ namespace LOTRO_DAT
         int ExtractAllFilesByType(FILE_TYPE type, Database *db);
 
         bool PatchFile(const char *filename, YAML::Node options, long long dat_id);
-
+        bool PatchFile(const BinaryData &binary_data, const std::u16string &text_data, YAML::Node &options);
         bool PatchDatabase(Database *db);
 
         void WriteUnorderedDictionary(std::string path) const;
@@ -104,8 +105,6 @@ namespace LOTRO_DAT
         void UpdateHeader();
         void UpdateFragmentationJournal(const std::vector<std::pair<long long, long long> > &journal);
 
-        void UpdateFileSize();
-
         std::unordered_map<long long, BinaryData *> patched_list;
 
         long long constant1_;
@@ -120,7 +119,6 @@ namespace LOTRO_DAT
 
         int dat_id_;
         bool patched_;
-
     };
 }
 }

+ 48 - 3
Source/Database.cpp

@@ -12,7 +12,6 @@
 #include <cstring>
 
 namespace LOTRO_DAT {
-
     Database::Database() {
         db_ = nullptr;
     }
@@ -22,9 +21,8 @@ namespace LOTRO_DAT {
     }
 
     Database::~Database() {
-        ExecSql("COMMIT TRANSACTION");
-
         if (db_ != nullptr) {
+            ExecSql("COMMIT TRANSACTION");
             sqlite3_finalize(insert_request_);
             sqlite3_close(db_);
         }
@@ -46,11 +44,16 @@ namespace LOTRO_DAT {
         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);
+
 
         ExecSql("BEGIN TRANSACTION");
     }
 
 	void Database::ExecSql(const std::string &sql) {
+        if (db_ == nullptr)
+            throw DatException("Bad Database::ExecSql() - database hasn't been opened!", DATABASE_EXCEPTION);
+
 		char *error;
 		if (sqlite3_exec(db_, sql.c_str(), nullptr, nullptr, &error) != SQLITE_OK) {
 			fprintf(stderr, "SQLite3 error: %s\n", sqlite3_errmsg(db_));
@@ -60,6 +63,9 @@ namespace LOTRO_DAT {
 	}
 
     void Database::PushFile(const BinaryData &binary_data, const std::u16string &text_data, const std::string &options) {
+        if (db_ == nullptr)
+            throw DatException("Bad Database::PushFile() - database hasn't been opened!", DATABASE_EXCEPTION);
+
         sqlite3_bind_blob(insert_request_, 1, binary_data.data(), binary_data.size(), SQLITE_TRANSIENT);
         sqlite3_bind_text16(insert_request_, 2, text_data.c_str(), -1, SQLITE_TRANSIENT);
         sqlite3_bind_text(insert_request_, 3, options.c_str(), -1, SQLITE_TRANSIENT);
@@ -72,4 +78,43 @@ namespace LOTRO_DAT {
 
         sqlite3_reset(insert_request_);
     }
+
+    bool Database::GetNextFile(BinaryData &binary_data, std::u16string &text_data, YAML::Node &options) {
+        if (db_ == nullptr)
+            throw DatException("Bad Database::GetNexFile() - database hasn't been opened!", DATABASE_EXCEPTION);
+
+        int result = sqlite3_step(fetch_one_request_);
+
+        if (result == SQLITE_ROW) {
+            binary_data = BinaryData((char *) sqlite3_column_blob(fetch_one_request_, 0),
+                                     unsigned(sqlite3_column_bytes(fetch_one_request_, 0)));
+
+            text_data = std::u16string((char16_t *)sqlite3_column_text16(fetch_one_request_, 1));
+
+            std::string _options = std::string((char *)sqlite3_column_text(fetch_one_request_, 2),
+                                               unsigned(sqlite3_column_bytes(fetch_one_request_, 2)));
+            options = YAML::Load(_options);
+            return true;
+        }
+
+        if (result == SQLITE_DONE) {
+            return false;
+        }
+
+        fprintf(stderr, "SQLite3 fetch_one request returned %d code. SQLite message is: %s", result, sqlite3_errmsg(db_));
+        throw DatException("Bad Database::GetNextFile() - sqlite3 - error", DATABASE_EXCEPTION);
+    }
+
+    void Database::RemoveDatabase() {
+        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
+    }
+
+    void Database::ClearDatabase() {
+        if (db_ == nullptr)
+            throw DatException("Bad Database::ClearDatabase() - database hasn't been opened!", DATABASE_EXCEPTION);
+        ExecSql(ClearTableCommand_);
+    }
 }

+ 12 - 7
Source/Database.h

@@ -7,6 +7,7 @@
 
 #include "Common/SQLite/sqlite3.h"
 #include <string>
+#include "yaml-cpp/yaml.h"
 
 extern  "C++"
 {
@@ -17,6 +18,7 @@ namespace LOTRO_DAT
     class Database {
     public:
         Database();
+
         explicit Database(const std::string &filename);
 
         ~Database();
@@ -25,27 +27,30 @@ namespace LOTRO_DAT
 
         void PushFile(const BinaryData &binary_data, const std::u16string &text_data, const std::string &options);
 
-        void GetFile();
-        void GetFile(long long file_id);
+        bool GetNextFile(BinaryData &binary_data, std::u16string &text_data, YAML::Node &options);
+
+        void RemoveDatabase();
+
+        void ClearDatabase();
 
     private:
         void ExecSql(const std::string &sql);
 
         sqlite3* db_;
         sqlite3_stmt* insert_request_;
-
-        sqlite3_stmt* get_text_;
-        sqlite3_stmt* get_binary_;
+        sqlite3_stmt* fetch_one_request_;
 
         const std::string CreateTableCommand_ = "CREATE TABLE IF NOT EXISTS `patch_data` ( "
             "`binary_data` BLOB, "
             "`text_data` TEXT, "
             "`options` TEXT NOT NULL);";
 
-        const std::string InsertFileCommand_ = "INSERT INTO patch_data "
-                "(binary_data, text_data, options) "
+        const std::string InsertFileCommand_ = "INSERT INTO `patch_data` "
+                "(`binary_data`, `text_data`, `options`) "
                 "VALUES (?, ?, ?); ";
 
+        const std::string FetchOneCommand = "SELECT * FROM `patch_data`";
+        const std::string ClearTableCommand_ = "DELETE * FROM `patch_data`";
     };
 }
 }

+ 49 - 22
Source/SubDirectory.cpp

@@ -21,6 +21,13 @@ namespace LOTRO_DAT {
         offset_ = 0;
     }
 
+    SubDirectory::~SubDirectory() {
+        for (Subfile* file : subfiles_)
+            delete file;
+        for (SubDirectory *directory : subdirs_)
+            delete directory;
+    }
+
     SubDirectory::SubDirectory(long long offset, DatFile *dat, long long max_subdirs) :
             dat_(dat), offset_(offset), max_subdirs_(max_subdirs) {
         try {
@@ -63,13 +70,8 @@ namespace LOTRO_DAT {
                 break;
 
             try {
-                subdirs_.emplace_back(
-                        SubDirectory(
-                                data.ToNumber<4>(i + 4),
-                                dat_
-                        )
-                );
-
+                SubDirectory *subdir = new SubDirectory(data.ToNumber<4>(i + 4), dat_);
+                subdirs_.push_back(subdir);
             } catch (std::exception &e) {
                 fprintf(stderr, "Caught %s exception.", e.what());
             
@@ -85,8 +87,16 @@ namespace LOTRO_DAT {
         for (unsigned int i = 0; i < 61 * 32; i += 32) {
             if (data.ToNumber<4>(i + 8) < 0x32 || data.ToNumber<4>(i + 12) < 0x32)
                 continue;
+
+            BinaryData mfile_id(4);
+            dat_->ReadData(mfile_id, 4, data.ToNumber<4>(i + 12) + 8);
+
+            if (data.ToNumber<4>(i + 8) != mfile_id.ToNumber<4>(0)) {
+                continue;
+            }
             subfiles_.push_back(
                     MakeSubfile(
+                            offset_ + 63 * 8 + i,
                             data.ToNumber<4>(i), // fragments_count
                             data.ToNumber<4>(i + 4), // unknown1
                             data.ToNumber<4>(i + 8), // file_id
@@ -106,22 +116,39 @@ namespace LOTRO_DAT {
                 dict[i->file_id()] = i;
         }
 
-        for (SubDirectory &i : subdirs_)
-            i.MakeDictionary(dict);
+        for (SubDirectory *i : subdirs_)
+            i->MakeDictionary(dict);
     }
 
-    void SubDirectory::UpdateDirectories(std::unordered_map<long long, BinaryData *> &patched_files) {
+    void SubDirectory::UpdateDirectories(std::unordered_map<long long, BinaryData *> &patched_files, std::unordered_map<long long, Subfile*> &dict) {
         for (unsigned i = 0; i < subfiles_.size(); i++) {
-            if (patched_files.count(subfiles_[i]->file_id()) != 0) {
-                dat_->WriteData(*patched_files[subfiles_[i]->file_id()], 32, offset_ + 63ll * 8ll + i * 32);
+            long long file_id = subfiles_[i]->file_id();
+
+            if (patched_files.count(file_id) != 0) {
+                BinaryData data(32);
+                dat_->ReadData(data, 32, subfiles_[i]->dictionary_offset());
+                if (data != *patched_files[file_id])
+                    continue;
+
+                BinaryData header = dict[subfiles_[i]->file_id()]->MakeHeaderData();
+                BinaryData write_data(4);
+
+                write_data.FromNumber<4>(dict[file_id]->file_offset());
+                dat_->WriteData(write_data, 4, subfiles_[i]->dictionary_offset() + 12);
+
+                write_data.FromNumber<4>(dict[file_id]->file_size());
+                dat_->WriteData(write_data, 4, subfiles_[i]->dictionary_offset() + 16);
+
+                write_data.FromNumber<4>(dict[file_id]->block_size());
+                dat_->WriteData(write_data, 4, subfiles_[i]->dictionary_offset() + 28);
             }
         }
 
-        for (SubDirectory &i : subdirs_)
-            i.UpdateDirectories(patched_files);
+        for (SubDirectory *i : subdirs_)
+            i->UpdateDirectories(patched_files, dict);
     }
 
-    Subfile *SubDirectory::MakeSubfile(long long fragments_count, long long unknown1, long long file_id,
+    Subfile *SubDirectory::MakeSubfile(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) {
 
@@ -129,19 +156,19 @@ namespace LOTRO_DAT {
 
         switch (type) {
             case TEXT:
-                return dynamic_cast<Subfile *>(new TextSubfile(dat_, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
+                return dynamic_cast<Subfile *>(new TextSubfile(dat_, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
             case JPG:
-                return dynamic_cast<Subfile *>(new JpgSubfile(dat_, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
+                return dynamic_cast<Subfile *>(new JpgSubfile(dat_, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
             case DDS:
-                return dynamic_cast<Subfile *>(new DdsSubfile(dat_, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
+                return dynamic_cast<Subfile *>(new DdsSubfile(dat_, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
             case WAV:
-                return dynamic_cast<Subfile *>(new WavSubfile(dat_, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
+                return dynamic_cast<Subfile *>(new WavSubfile(dat_, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
             case OGG:
-                return dynamic_cast<Subfile *>(new OggSubfile(dat_, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
+                return dynamic_cast<Subfile *>(new OggSubfile(dat_, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
             case FONT:
-                return dynamic_cast<Subfile *>(new FontSubfile(dat_, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
+                return dynamic_cast<Subfile *>(new FontSubfile(dat_, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
             case UNKNOWN:
-                return dynamic_cast<Subfile *>(new UnknownSubfile(dat_, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
+                return dynamic_cast<Subfile *>(new UnknownSubfile(dat_, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size));
         }
         throw DatException("Bad SubDirectory::MakeSubfile() - unable to recognize file_type!", SUBFILE_EXCEPTION);
     }

+ 4 - 3
Source/SubDirectory.h

@@ -24,22 +24,23 @@ namespace LOTRO_DAT
     public:
         SubDirectory();
         SubDirectory(long long offset, DatFile *dat, long long max_subdirs = 63);
+        ~SubDirectory();
         void MakeDictionary(std::unordered_map<long long, Subfile*> &dict);
 
-        void UpdateDirectories(std::unordered_map<long long, BinaryData *> &patched_files);
+        void UpdateDirectories(std::unordered_map<long long, BinaryData *> &patched_files, std::unordered_map<long long, Subfile*> &dict);
 
     private:
         void MakeSubDirectories();
         void MakeSubFiles();
 
-        Subfile* MakeSubfile(long long fragments_count, long long unknown1, long long file_id, long long file_offset,
+        Subfile* MakeSubfile(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);
         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<SubDirectory *> subdirs_;
         std::vector<Subfile *> subfiles_;
     };
 }

+ 9 - 3
Source/Subfile.cpp

@@ -17,10 +17,10 @@ namespace LOTRO_DAT {
 
     Subfile::Subfile() = default;
 
-    Subfile::Subfile(DatFile *dat, long long fragments_count, long long unknown1, long long file_id,
+    Subfile::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) :
-            dat_(dat), fragments_count_(fragments_count), unknown1_(unknown1), file_id_(file_id),
+            dat_(dat), dictionary_offset_(dictionary_offset), fragments_count_(fragments_count), unknown1_(unknown1), file_id_(file_id),
             file_offset_(file_offset),
             file_size_(file_size), timestamp_(timestamp), version_(version), block_size_(block_size) {
 
@@ -31,6 +31,12 @@ namespace LOTRO_DAT {
 
     /// Typical getters of private fields, if there's need for getting information about SubFile from outside class.
 
+
+    long long Subfile::dictionary_offset() const {
+        return dictionary_offset_;
+    }
+
+
     long long Subfile::fragments_count() const {
         return fragments_count_;
     }
@@ -107,7 +113,7 @@ namespace LOTRO_DAT {
         throw DatException("Bad Subfile::MakeForImport() - function is not implemented for this type.", IMPORT_EXCEPTION);
     }
 
-    BinaryData Subfile::MakeHeaderData() {
+    BinaryData Subfile::MakeHeaderData() const {
         BinaryData header;
         BinaryData data(4);
         data.FromNumber<4>(fragments_count_);

+ 6 - 3
Source/Subfile.h

@@ -23,8 +23,9 @@ namespace LOTRO_DAT
         friend class DatFile;
     public:
         Subfile();
-        Subfile(DatFile *dat, 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(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);
+		virtual ~Subfile() {};
 
         virtual FILE_TYPE FileType() const;
         virtual std::string Extension() const;
@@ -35,8 +36,9 @@ namespace LOTRO_DAT
         virtual BinaryData MakeForImport(const BinaryData &old_data, const BinaryData &binary_data,
                                 const std::u16string &text_data, const YAML::Node &options);
 
-		BinaryData MakeHeaderData();
+		BinaryData MakeHeaderData() const;
 
+		long long dictionary_offset() const;
         long long fragments_count() const;
         long long unknown1() const;
         long long file_id() const;
@@ -48,6 +50,7 @@ namespace LOTRO_DAT
 
 	protected:
 		DatFile *dat_;
+        long long dictionary_offset_;
 
 		long long fragments_count_;
         long long unknown1_;

+ 6 - 5
Source/Subfiles/DdsSubfile.cpp

@@ -11,11 +11,12 @@
 namespace LOTRO_DAT {
     DdsSubfile::DdsSubfile() {}
 
-    DdsSubfile::DdsSubfile(DatFile *dat, 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(dat, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
+    DdsSubfile::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)
+            : Subfile(dat, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
+
     }
 
     FILE_TYPE DdsSubfile::FileType() const {

+ 5 - 2
Source/Subfiles/DdsSubfile.h

@@ -11,8 +11,11 @@ namespace LOTRO_DAT {
     class DdsSubfile : public Subfile {
     public:
         DdsSubfile();
-        DdsSubfile(DatFile *dat, 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);
+        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);
+        ~DdsSubfile() override {};
 
         FILE_TYPE FileType() const override;
         std::string Extension() const override;

+ 2 - 2
Source/Subfiles/FontSubfile.cpp

@@ -10,11 +10,11 @@
 namespace LOTRO_DAT {
     FontSubfile::FontSubfile() {}
 
-    FontSubfile::FontSubfile(DatFile *dat, long long fragments_count, long long unknown1,
+    FontSubfile::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)
-            : Subfile(dat, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
+            : Subfile(dat, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
     }
 
     FILE_TYPE FontSubfile::FileType() const {

+ 2 - 1
Source/Subfiles/FontSubfile.h

@@ -12,8 +12,9 @@ namespace LOTRO_DAT {
     class FontSubfile : public Subfile {
     public:
         FontSubfile();
-        FontSubfile(DatFile *dat, long long fragments_count, long long unknown1, long long file_id, long long file_offset,
+        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);
+        ~FontSubfile() override {};
 
         FILE_TYPE FileType() const override;
         std::string Extension() const override;

+ 2 - 2
Source/Subfiles/JpgSubfile.cpp

@@ -10,11 +10,11 @@
 namespace LOTRO_DAT {
     JpgSubfile::JpgSubfile() = default;
 
-    JpgSubfile::JpgSubfile(DatFile *dat, long long fragments_count, long long unknown1,
+    JpgSubfile::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)
-            : Subfile(dat, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
+            : Subfile(dat, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
     }
 
     FILE_TYPE JpgSubfile::FileType() const {

+ 2 - 1
Source/Subfiles/JpgSubfile.h

@@ -12,8 +12,9 @@ namespace LOTRO_DAT {
     class JpgSubfile : public Subfile {
     public:
         JpgSubfile();
-        JpgSubfile(DatFile *dat, long long fragments_count, long long unknown1, long long file_id, long long file_offset,
+        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);
+        ~JpgSubfile() override {};
 
         FILE_TYPE FileType() const override;
         std::string Extension() const override;

+ 2 - 2
Source/Subfiles/OggSubfile.cpp

@@ -10,11 +10,11 @@
 namespace LOTRO_DAT {
     OggSubfile::OggSubfile() {}
 
-    OggSubfile::OggSubfile(DatFile *dat, long long fragments_count, long long unknown1,
+    OggSubfile::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)
-            : Subfile(dat, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
+            : Subfile(dat, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
     }
 
     FILE_TYPE OggSubfile::FileType() const {

+ 2 - 1
Source/Subfiles/OggSubfile.h

@@ -11,8 +11,9 @@ namespace LOTRO_DAT {
     class OggSubfile : public Subfile {
     public:
         OggSubfile();
-        OggSubfile(DatFile *dat, long long fragments_count, long long unknown1, long long file_id, long long file_offset,
+        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);
+        ~OggSubfile() override {};
 
         FILE_TYPE FileType() const override;
         std::string Extension() const override;

+ 2 - 2
Source/Subfiles/TextSubfile.cpp

@@ -10,11 +10,11 @@
 namespace LOTRO_DAT {
     TextSubfile::TextSubfile() = default;
 
-    TextSubfile::TextSubfile(DatFile *dat, long long fragments_count, long long unknown1,
+    TextSubfile::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)
-            : Subfile(dat, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
+            : Subfile(dat, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
     }
 
     FILE_TYPE TextSubfile::FileType() const {

+ 2 - 1
Source/Subfiles/TextSubfile.h

@@ -13,9 +13,10 @@ namespace LOTRO_DAT {
     public:
         TextSubfile();
 
-        TextSubfile(DatFile *dat, long long fragments_count, long long unknown1, long long file_id,
+        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);
+        ~TextSubfile() override {};
 
         FILE_TYPE FileType() const override;
         std::string Extension() const override;

+ 2 - 2
Source/Subfiles/UnknownSubfile.cpp

@@ -10,11 +10,11 @@
 namespace LOTRO_DAT {
     UnknownSubfile::UnknownSubfile() {}
 
-    UnknownSubfile::UnknownSubfile(DatFile *dat, long long fragments_count, long long unknown1,
+    UnknownSubfile::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)
-            : Subfile(dat, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
+            : Subfile(dat, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
     }
 
     FILE_TYPE UnknownSubfile::FileType() const {

+ 2 - 1
Source/Subfiles/UnknownSubfile.h

@@ -11,8 +11,9 @@ namespace LOTRO_DAT {
     class UnknownSubfile : public Subfile {
     public:
         UnknownSubfile();
-        UnknownSubfile(DatFile *dat, long long fragments_count, long long unknown1, long long file_id, long long file_offset,
+        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);
+        ~UnknownSubfile() override {};
 
         FILE_TYPE FileType() const override;
         std::string Extension() const override;

+ 2 - 2
Source/Subfiles/WavSubfile.cpp

@@ -10,11 +10,11 @@
 namespace LOTRO_DAT {
     WavSubfile::WavSubfile() = default;
 
-    WavSubfile::WavSubfile(DatFile *dat, long long fragments_count, long long unknown1,
+    WavSubfile::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)
-            : Subfile(dat, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
+            : Subfile(dat, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
     }
 
     FILE_TYPE WavSubfile::FileType() const {

+ 2 - 1
Source/Subfiles/WavSubfile.h

@@ -11,8 +11,9 @@ namespace LOTRO_DAT {
     class WavSubfile : public Subfile {
     public:
         WavSubfile();
-        WavSubfile(DatFile *dat, long long fragments_count, long long unknown1, long long file_id, long long file_offset,
+        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);
+        ~WavSubfile() override {};
 
         FILE_TYPE FileType() const override;
         std::string Extension() const override;

+ 4 - 4
Source/Tests/extract_test.cpp

@@ -23,15 +23,15 @@ const std::string filename = "client_local_English.dat";
 const bool exportImagesToFiles = true;
 const bool exportFontsToFiles = false;
 const bool exportSoundsToFiles = false;
-const bool exportTexturesToFiles = false;
+const bool exportTexturesToFiles = true;
 const bool exportUnknownToFiles = false;
 
 // Change these variables to true if you want export catecory to databases.
-const bool exportTextsToDb = false;
-const bool exportImagesToDb = false;
+const bool exportTextsToDb = true;
+const bool exportImagesToDb = true;
 const bool exportFontsToDb = false;
 const bool exportSoundsToDb = false;
-const bool exportTexturesToDb = false;
+const bool exportTexturesToDb = true;
 const bool exportUnknownToDb = false;
 // There is no need to change anything else
 

+ 25 - 15
Source/Tests/patch_test.cpp

@@ -7,6 +7,7 @@
 #include <algorithm>
 
 #ifdef WIN32
+#include <direct.h>
 #define mkdir(dir, mode) _mkdir(dir)
 #endif
 
@@ -24,23 +25,32 @@ const int file_id = 0;
 int main() {
     const clock_t begin_time = clock();
 
-    BinaryData t;
-    t.ReadFromFile(patch_filename);
-    t.WriteToFile("kek.jpg");
+    freopen("errors.log", "w", stderr);
+
+    Database db;
+    db.InitDatabase("images.db");
+
+    BinaryData binary_data;
+    std::u16string text_data;
+    YAML::Node options;
+
+    DatFile *a;
 
-    //freopen("errors.log", "w", stderr);
     try {
+        a = new DatFile((path + filename).c_str(), 0);
         std::cout << "Starting magic...\n";
-        DatFile a((path + filename).c_str(), 0);
-        std::cout << "Files number: " << a.files_number() << std::endl;
-        a.WriteUnorderedDictionary("");
-
-        YAML::Node options;
-        options["fid"] = "1090551300";
-        options["ext"] = ".jpg";
-        a.PatchFile(patch_filename.c_str(), options, 0);
-        //a.PatchFile(patch_filename.c_str(), options, 0);
-        a.UpdateSubdirectories();
+        std::cout << "Files number: " << a->files_number() << std::endl;
+        a->WriteUnorderedDictionary("");
+
+       while (db.GetNextFile(binary_data, text_data, options)) {
+
+            //binary_data.ReadFromFile("1090552107.jpg");
+            //options["fid"] = "1090552107";
+            //options["ext"] = ".jpg";
+
+            a->PatchFile(binary_data, text_data, options);
+            //delete a;
+        }
     } catch (std::exception &e) {
         fprintf(stderr, "Caught %s exception.", e.what());
         printf("Caught %s exception.", e.what());
@@ -49,7 +59,7 @@ int main() {
         printf("Some critical errors occured. Need to stop execution. See information in errors.log file");
         fprintf(stderr, "Some critical errors occured. Need to stop execution now...");
     }
-
+    delete a;
     fprintf(stdout, "Spent %f seconds on performing magic! Thank you for your patience!\n",
             float(clock() - begin_time) / CLOCKS_PER_SEC);
     system("pause");