浏览代码

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 年之前
父节点
当前提交
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;
 

二进制
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");