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

New functions, preparations for writing patcher

Gi1dor (Ivan Arkhipov) 6 лет назад
Родитель
Сommit
dd6d71e165
13 измененных файлов с 391 добавлено и 224 удалено
  1. 7 0
      BinaryData.cpp
  2. 1 0
      BinaryData.h
  3. 9 4
      CMakeLists.txt
  4. 4 2
      DatFile.cpp
  5. 11 2
      DatFile.h
  6. 2 3
      Database.cpp
  7. 3 11
      Database.h
  8. 1 4
      SubDirectory.cpp
  9. 62 6
      Subfile.cpp
  10. 14 3
      Subfile.h
  11. 234 0
      extract_test.cpp
  12. 43 0
      patch_test.cpp
  13. 0 189
      test.cpp

+ 7 - 0
BinaryData.cpp

@@ -37,6 +37,13 @@ namespace LOTRO_DAT {
         return data_[pos];
     }
 
+    BinaryData BinaryData::operator +(const BinaryData &b) {
+        BinaryData res(size_ + b.size());
+        memcpy(res.data_, data_, size_);
+        memcpy(res.data_ + size_, b.data_, b.size_);
+        return res;
+    }
+
     // Translates T bytes from data into number using UTF-16LE encoding of the .dat file
     template<int T>
     long long BinaryData::ToNumber(const long long &pos) const {

+ 1 - 0
BinaryData.h

@@ -23,6 +23,7 @@ namespace LOTRO_DAT
         ~BinaryData();
 
         unsigned char& operator[] (const unsigned int &pos);
+        BinaryData operator +(const BinaryData &b);
 
         template <int T>
         long long ToNumber(const long long &pos) const;

+ 9 - 4
CMakeLists.txt

@@ -18,9 +18,14 @@ set(SOURCE_FILES
 		CommonFunctions.h
         )
 
-#add_library(LotRO_dat_Patcher SHARED ${SOURCE_FILES})
-#target_link_libraries(LotRO_dat_Patcher ${CMAKE_SOURCE_DIR}/zlib/libzlibstatic.a)
+add_library(LotroDat SHARED ${SOURCE_FILES})
+target_link_libraries(LotroDat ${CMAKE_SOURCE_DIR}/zlib/libzlibstatic.a)
 
-add_executable(LotRO_dat_tester ${SOURCE_FILES} test.cpp)
-target_link_libraries(LotRO_dat_tester ${CMAKE_SOURCE_DIR}/zlib/libzlibstatic.a)
+# FOR EXTRACTOR USE NEXT LINE
+add_executable(LotRO_dat_extract_tester ${SOURCE_FILES} extract_test.cpp)
+target_link_libraries(LotRO_dat_extract_tester ${CMAKE_SOURCE_DIR}/zlib/libzlibstatic.a)
+
+# FOR PATCHER USE NEXT LINE
+add_executable(LotRO_dat_patch_tester ${SOURCE_FILES} patch_test.cpp)
+target_link_libraries(LotRO_dat_patch_tester ${CMAKE_SOURCE_DIR}/zlib/libzlibstatic.a)
 

+ 4 - 2
DatFile.cpp

@@ -18,7 +18,8 @@ namespace LOTRO_DAT {
         dat_state_ = CLOSED;
     }
 
-    DatFile::DatFile(const char *filename) {
+    DatFile::DatFile(const char *filename, int dat_id) {
+        dat_id_ = dat_id;
         dat_state_ = CLOSED;
         OpenDatFile(filename);
         ReadSuperBlock();
@@ -36,7 +37,8 @@ namespace LOTRO_DAT {
                                INIT_EXCEPTION);
     }
 
-    DatFile::DatFile(const std::string &filename) {
+    DatFile::DatFile(const std::string &filename, int dat_id) {
+        dat_id_ = dat_id;
         dat_state_ = CLOSED;
         OpenDatFile(filename.c_str());
         ReadSuperBlock();

+ 11 - 2
DatFile.h

@@ -16,6 +16,13 @@
 #include <unordered_map>
 #include <set>
 
+// Dat file names definitions
+#define CLIENT_LOCAL_ENGLISH 0
+#define CLIENT_GENERAL 1
+#define CLIENT_SOUND 2
+#define CLIENT_SURFACE 3
+#define CLIENT_HIGHRES 4
+
 extern  "C++"
 {
 namespace LOTRO_DAT
@@ -38,8 +45,8 @@ namespace LOTRO_DAT
     {
     public:
         DatFile();
-        explicit DatFile(const char* filename);
-        explicit DatFile(const std::string &filename);
+        explicit DatFile(const char* filename, int dat_id);
+        explicit DatFile(const std::string &filename, int dat_id);
         ~DatFile();
 
         void ReadData(BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);
@@ -71,6 +78,8 @@ namespace LOTRO_DAT
         long long root_directory_offset_;
 
         STATE dat_state_;
+        int dat_id_;
+
     };
 }
 }

+ 2 - 3
Database.cpp

@@ -50,7 +50,6 @@ namespace LOTRO_DAT {
 
         ExecSql(CreateBinaryTableCommand_);
         ExecSql(CreateTextTableCommand_);
-        ExecSql(CreateMetadataTableCommand_);
 
 		sqlite3_prepare_v2(db_, InsertBinaryCommand_.c_str(), InsertBinaryCommand_.length(), &insert_binary_, nullptr);
         sqlite3_prepare_v2(db_, InsertTextCommand_.c_str(), InsertTextCommand_.length(), &insert_text_, nullptr);
@@ -77,8 +76,8 @@ namespace LOTRO_DAT {
 
         sqlite3_bind_int(insert_text_, 1, (int)file_id);
         sqlite3_bind_int(insert_text_, 2, (int)gossip_id);
-        int a = sqlite3_bind_text16(insert_text_, 3, text, -1, SQLITE_TRANSIENT);
-        int b = sqlite3_bind_text(insert_text_, 4, args, -1, SQLITE_TRANSIENT);
+        sqlite3_bind_text16(insert_text_, 3, text, -1, SQLITE_TRANSIENT);
+        sqlite3_bind_text(insert_text_, 4, args, -1, SQLITE_TRANSIENT);
 
         if (sqlite3_step(insert_text_) != SQLITE_DONE) {
             fprintf(stderr, "SQLite3 error: %s\n", sqlite3_errmsg(db_));

+ 3 - 11
Database.h

@@ -29,7 +29,6 @@ namespace LOTRO_DAT
         void PushBinaryFile(long long file_id, const BinaryData& data);
         //BinaryData& GetBinaryFile(long long file_id);
 
-        void AddMetadata();
 
     private:
         void ExecSql(const std::string &sql);
@@ -47,6 +46,7 @@ namespace LOTRO_DAT
         const std::string CreateBinaryTableCommand_ = "CREATE TABLE IF NOT EXISTS `binary_data` ( "
             "`file_id` INTEGER NOT NULL DEFAULT '0', "
             "`data` BLOB, "
+            "`dat_id` INTEGER NOT NULL DEFAULT '-1', "
             "PRIMARY KEY (`file_id`));";
 
         const std::string CreateTextTableCommand_ = "CREATE TABLE IF NOT EXISTS `text_data` ( "
@@ -54,18 +54,10 @@ namespace LOTRO_DAT
             "`gossip_id` INTEGER NOT NULL DEFAULT '0', "
             "`content` TEXT, "
             "`args` TEXT, "
+            "`args_order` TEXT, "
+            "`dat_id` INTEGER NOT NULL DEFAULT '-1', "
             "PRIMARY KEY (`file_id`, `gossip_id`));";
 
-        const std::string CreateMetadataTableCommand_ = "CREATE TABLE IF NOT EXISTS `meta_data` ( "
-            "`name` TEXT NOT NULL, "
-            "`description` TEXT NOT NULL, "
-            "`date` TEXT NOT NULL, "
-            "`author` TEXT NOT NULL, "
-            "`version` TEXT NOT NULL, "
-            "`link` TEXT NOT NULL, "
-            "`content_type` TEXT NOT NULL, "
-            "PRIMARY KEY (`version`));";
-
         const std::string InsertTextCommand_ = "INSERT INTO text_data "
                 "(file_id, gossip_id, content, args) "
                 "VALUES (?, ?, ?, ?); ";

+ 1 - 4
SubDirectory.cpp

@@ -8,7 +8,6 @@
 #include "Subfile.h"
 #include "BinaryData.h"
 
-
 LOTRO_DAT::SubDirectory::SubDirectory() {
     offset_ = 0;
 }
@@ -87,12 +86,10 @@ void LOTRO_DAT::SubDirectory::MakeSubFiles() {
 
 void LOTRO_DAT::SubDirectory::MakeDictionary(std::unordered_map<long long, Subfile *> &dict) {
     for (Subfile &i : subfiles_) {
-            if (i.file_id() == 621002463)
-            fprintf(stderr, "YAY!!!! offset = %lld\n", i.file_offset());
-
         if (dict.count(i.file_id()) == 0 || dict[i.file_id()]->timestamp() < i.timestamp())
 			dict[i.file_id()] = &i;
     }
+
     for (SubDirectory &i : subdirs_)
         i.MakeDictionary(dict);
 }

+ 62 - 6
Subfile.cpp

@@ -11,11 +11,6 @@
 
 #include <algorithm>
 
-#ifdef WIN32
-#include <direct.h>
-#define mkdir(dir, mode) _mkdir(dir)
-#endif
-
 const long long MAXSIZE = 50ll * 1024ll * 1024ll; // Setting maximal file size 50 MB; Files with size more than 50 mb
                                                   // will be recognized as incorrect and passed.
                                                   // This should be done because
@@ -141,6 +136,39 @@ std::string LOTRO_DAT::Subfile::ExtensionToString(LOTRO_DAT::EXTENSION ext) cons
 	}
 }
 
+bool LOTRO_DAT::Subfile::PatchBinaryFile(const LOTRO_DAT::BinaryData &file) {
+    LOTRO_DAT::BinaryData data;
+
+    // Making data for placing in .dat, depending on file extension
+    switch (ext_)
+    {
+        case LOTRO_DAT::TXT:
+            throw LOTRO_DAT::DatException("Bad Subfile::PatchBinaryFile() - trying to patch text file");
+        case LOTRO_DAT::JPG:
+            data = MakeFromJPG(file); break;
+        case LOTRO_DAT::DDS:
+            data = MakeFromDDS(file); break;
+        case LOTRO_DAT::WAV:
+            data = MakeFromWAV(file); break;
+        case LOTRO_DAT::OGG:
+            data = MakeFromOGG(file); break;
+        case LOTRO_DAT::FONT:
+            data = MakeFromFont(file); break;
+        case LOTRO_DAT::UNKNOWN:
+            data = MakeFromUnk(file); break;
+        default:
+            break;
+    }
+
+    if (block_size() >= data.size() + 8) {
+        dat_->WriteData(data, data.size(), file_offset() + 8);
+        return true;
+    }
+    throw DatException("Bad Subfile::PatchBinaryFile() - new data size is bigger, than block_size. This is not implemented yet"
+    , IMPORT_EXCEPTION);
+    return true;
+}
+
 bool LOTRO_DAT::Subfile::ExportFile(const char *filename, Database *db) const {
 	try {
         BinaryData data;
@@ -242,7 +270,6 @@ bool LOTRO_DAT::Subfile::ExportFileAsTXT(const char *filename, Database *db) con
         offset += 1;
 
         for (long long j = 0; j < num_arg_strings; j++) {
-            fprintf(stderr, "Hey!!!\n");
             long long num_args = data.ToNumber<4>(offset);
             offset += 4;
 
@@ -277,6 +304,11 @@ bool LOTRO_DAT::Subfile::ExportFileAsTXT(const char *filename, Database *db) con
 }
 
 LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::GetFileData(long long offset) const {
+    BinaryData mfile_id(4);
+    dat_->ReadData(mfile_id, 4, file_offset() + 8);
+    if (file_id() != mfile_id.ToNumber<4>(0))
+        throw DatException("Bad Subfile::GetFileData() - file_id doesn't match to dictionary", READ_EXCEPTION);
+
     BinaryData data((unsigned)(file_size()));
     if (block_size() >= file_size() + 8) {
         dat_->ReadData(data, file_size(), file_offset() + offset);
@@ -433,3 +465,27 @@ const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::PrepareAsWAV() const {
 const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::PrepareAsUnk() const {
     return GetFileData();
 }
+
+const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::MakeFromJPG(const LOTRO_DAT::BinaryData &file) const {
+    return GetFileData().CutData(0, 24) + file;
+}
+
+const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::MakeFromDDS(const LOTRO_DAT::BinaryData &file) const {
+    throw LOTRO_DAT::DatException("Bad Subfile::MakeFromDDS() - not implemented yet", IMPORT_EXCEPTION);
+}
+
+const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::MakeFromOGG(const LOTRO_DAT::BinaryData &file) const {
+    throw LOTRO_DAT::DatException("Bad Subfile::MakeFromOGG() - not implemented yet", IMPORT_EXCEPTION);
+}
+
+const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::MakeFromFont(const LOTRO_DAT::BinaryData &file) const {
+    throw LOTRO_DAT::DatException("Bad Subfile::MakeFromFont() - not implemented yet", IMPORT_EXCEPTION);
+}
+
+const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::MakeFromWAV(const LOTRO_DAT::BinaryData &file) const {
+    throw LOTRO_DAT::DatException("Bad Subfile::MakeFromWAV() - not implemented yet", IMPORT_EXCEPTION);
+}
+
+const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::MakeFromUnk(const LOTRO_DAT::BinaryData &file) const {
+    throw LOTRO_DAT::DatException("Bad Subfile::MakeFromUnk() - not implemented yet", IMPORT_EXCEPTION);
+}

+ 14 - 3
Subfile.h

@@ -29,9 +29,11 @@ namespace LOTRO_DAT
 
         bool ExportFile(const char* filename, Database *db = nullptr) const;
 		
-		bool ExportFileAsTXT(const char *filename, Database *db = nullptr) const; // Need to have special function,
-                                                                                  // as TXT file consisits of more than 1 file
-		bool PatchFile();
+		bool ExportFileAsTXT(const char *filename, Database *db = nullptr) const;
+
+		bool PatchBinaryFile(const BinaryData &file);
+
+        bool PatchTextFile();
 
         long long fragments_count() const;
 
@@ -71,12 +73,21 @@ namespace LOTRO_DAT
 
 		BinaryData GetFileData(long long offset = 8) const;
 
+        // Export functions block
 		const BinaryData PrepareAsJPG() const;
 		const BinaryData PrepareAsDDS() const;
 		const BinaryData PrepareAsOGG() const;
 		const BinaryData PrepareAsFont() const;
 		const BinaryData PrepareAsWAV() const;
 		const BinaryData PrepareAsUnk() const;
+
+        // Patch functions block
+        const BinaryData MakeFromJPG(const BinaryData &file) const;
+        const BinaryData MakeFromDDS(const BinaryData &file) const;
+        const BinaryData MakeFromOGG(const BinaryData &file) const;
+        const BinaryData MakeFromFont(const BinaryData &file) const;
+        const BinaryData MakeFromWAV(const BinaryData &file) const;
+        const BinaryData MakeFromUnk(const BinaryData &file) const;
     };
 }
 };

+ 234 - 0
extract_test.cpp

@@ -0,0 +1,234 @@
+//
+// Created by Иван_Архипов on 30.10.2017.
+//
+#define _CRT_SECURE_NO_WARNINGS
+#include <iostream>
+#include <ctime>
+#include <algorithm>
+
+#ifdef WIN32
+#include <direct.h>
+#define mkdir(dir, mode) _mkdir(dir)
+#endif
+
+#include "LotroDatPatcher.h"
+using namespace LOTRO_DAT;
+
+
+// Change these 2 variables to your path and name of .dat file
+const std::string path = "E:\\SteamLibrary\\steamapps\\common\\";//Lord Of The Rings Online\\";
+const std::string filename = "client_local_English.dat";
+
+// Change these variables to true if you want export catecory to files.
+const bool exportImagesToFiles = false;
+const bool exportFontsToFiles = true;
+const bool exportSoundsToFiles = false;
+const bool exportTexturesToFiles = false;
+const bool exportUnknownToFiles = true;
+
+// Change these variables to true if you want export catecory to databases.
+const bool exportTextsToDb = true;
+const bool exportImagesToDb = false;
+const bool exportFontsToDb = false;
+const bool exportSoundsToDb = false;
+const bool exportTexturesToDb = false;
+const bool exportUnknownToDb = false;
+// There is no need to change anything else
+
+
+int main() {
+    const clock_t begin_time = clock();
+
+    std::time_t time = std::time(nullptr);
+    mkdir("Extracted data", 744);
+
+    std::time_t result = std::time(nullptr);
+    char *ttime = std::asctime(std::localtime(&result));
+    char *out_time = new char[25];
+    memcpy(out_time, ttime, 24);
+    out_time[24] = '\0';
+
+    std::string output_dir = std::string("Extracted data\\") + filename + " " + std::string(out_time) + "\\";
+    std::replace(output_dir.begin(), output_dir.end(), ':', '-');
+
+    mkdir(output_dir.c_str(), 744);
+    freopen((output_dir + std::string("errors.log")).c_str(), "w", stderr);
+
+    try {
+        std::string txt_dir = "txt\\";
+        std::string jpg_dir = "jpg\\";
+        std::string dds_dir = "dds\\";
+        std::string wav_dir = "wav\\";
+        std::string ogg_dir = "ogg\\";
+        std::string font_dir = "font\\";
+        std::string unk_dir = "unk\\";
+
+        int txt_all = 0, txt_suc = 0;
+        int jpg_all = 0, jpg_suc = 0;
+        int dds_all = 0, dds_suc = 0;
+        int wav_all = 0, wav_suc = 0;
+        int ogg_all = 0, ogg_suc = 0;
+        int font_all = 0, font_suc = 0;
+        int unk_all = 0, unk_suc = 0;
+
+        mkdir(output_dir.c_str(), 744);
+        mkdir((output_dir + txt_dir).c_str(), 744);
+        mkdir((output_dir + jpg_dir).c_str(), 744);
+        mkdir((output_dir + dds_dir).c_str(), 744);
+        mkdir((output_dir + wav_dir).c_str(), 744);
+        mkdir((output_dir + ogg_dir).c_str(), 744);
+        mkdir((output_dir + font_dir).c_str(), 744);
+        mkdir((output_dir + unk_dir).c_str(), 744);
+
+        Database *images = nullptr;
+        Database *sounds = nullptr;
+        Database *texts = nullptr;
+        Database *fonts = nullptr;
+        Database *textures = nullptr;
+        Database *unknown = nullptr;
+
+        if (exportImagesToDb) images = new Database(output_dir + std::string("Images.db"));
+        if (exportSoundsToDb) sounds = new Database(output_dir + std::string("Sounds.db"));
+        if (exportTextsToDb) texts = new Database(output_dir + std::string("Texts.db"));
+        if (exportFontsToDb) fonts = new Database(output_dir + std::string("Fonts.db"));
+        if (exportTexturesToDb) textures = new Database(output_dir + std::string("Textures.db"));
+        if (exportUnknownToDb) unknown = new Database(output_dir + std::string("Unknown.db"));
+
+        std::cout << "Starting search...\n";
+        DatFile a(path + filename, 0);
+        std::cout << "Total files found: " << a.dictionary().size() << std::endl;
+
+        std::cout << "Writing unordered dictionary:\n";
+        FILE *f;
+        fopen_s(&f, (output_dir + "dict.txt").c_str(), "w");
+        fprintf(f, "file_id offset size size2 extension\n");
+        for (auto i : a.dictionary()) {
+            std::string extension = "unk";
+            EXTENSION ext = i.second->ext();
+            if (ext == TXT) extension = "txt";
+            if (ext == JPG) extension = "jpg";
+            if (ext == DDS) extension = "dds";
+            if (ext == WAV) extension = "wav";
+            if (ext == OGG) extension = "ogg";
+            if (ext == FONT) extension = "font";
+            fprintf(f, "%lld %lld %lld %lld %s\n", i.second->file_id(), i.second->file_offset(), i.second->file_size(),
+                    i.second->block_size(), extension.c_str());
+        }
+        fclose(f);
+
+        std::cout << "Beginning unpacking...\n";
+
+        long long all = a.dictionary().size();
+        long long counter = 0;
+
+        for (auto i : a.dictionary()) {
+            EXTENSION ext = i.second->ext();
+            if (ext == TXT) {
+                txt_all++;
+                if (exportTextsToDb)
+                    txt_suc += i.second->ExportFile(
+                            (output_dir + txt_dir + std::to_string(i.second->file_id())).c_str(), texts);
+                //if (exportTextsToFiles) txt_suc += i.second->ExportFile((output_dir + txt_dir + std::to_string(i.second->file_id())).c_str());
+            }
+
+            if (ext == JPG) {
+                jpg_all++;
+                if (exportImagesToDb)
+                    jpg_suc += i.second->ExportFile(
+                            (output_dir + jpg_dir + std::to_string(i.second->file_id())).c_str(), images);
+                if (exportImagesToFiles)
+                    jpg_suc += i.second->ExportFile(
+                            (output_dir + jpg_dir + std::to_string(i.second->file_id())).c_str());
+            }
+
+            if (ext == DDS) {
+                dds_all++;
+                if (exportTexturesToDb)
+                    dds_suc += i.second->ExportFile(
+                            (output_dir + dds_dir + std::to_string(i.second->file_id())).c_str(), textures);
+                if (exportTexturesToFiles)
+                    dds_suc += i.second->ExportFile(
+                            (output_dir + dds_dir + std::to_string(i.second->file_id())).c_str());
+            }
+
+            if (ext == WAV) {
+                wav_all++;
+                if (exportSoundsToDb)
+                    wav_suc += i.second->ExportFile(
+                            (output_dir + wav_dir + std::to_string(i.second->file_id())).c_str(), sounds);
+                if (exportSoundsToFiles)
+                    wav_suc += i.second->ExportFile(
+                            (output_dir + wav_dir + std::to_string(i.second->file_id())).c_str());
+            }
+
+            if (ext == OGG) {
+                ogg_all++;
+                if (exportSoundsToDb)
+                    ogg_suc += i.second->ExportFile(
+                            (output_dir + ogg_dir + std::to_string(i.second->file_id())).c_str(), sounds);
+                if (exportSoundsToFiles)
+                    ogg_suc += i.second->ExportFile(
+                            (output_dir + ogg_dir + std::to_string(i.second->file_id())).c_str());
+            }
+
+            if (ext == FONT) {
+                font_all++;
+                if (exportFontsToDb)
+                    font_suc += i.second->ExportFile(
+                            (output_dir + font_dir + std::to_string(i.second->file_id())).c_str(), fonts);
+                if (exportFontsToFiles)
+                    font_suc += i.second->ExportFile(
+                            (output_dir + font_dir + std::to_string(i.second->file_id())).c_str());
+            }
+
+            if (ext == UNKNOWN) {
+                unk_all++;
+                if (exportUnknownToDb)
+                    unk_suc += i.second->ExportFile(
+                            (output_dir + unk_dir + std::to_string(i.second->file_id())).c_str(), unknown);
+                if (exportUnknownToFiles)
+                    unk_suc += i.second->ExportFile(
+                            (output_dir + unk_dir + std::to_string(i.second->file_id())).c_str());
+            }
+
+            counter++;
+            if ((counter - 1) * 100ll / all != counter * 100ll / all) {
+                fprintf(stdout, "Done: %lld%%\n", counter * 100ll / all);
+            }
+        }
+
+        delete images;
+        delete sounds;
+        delete texts;
+        delete fonts;
+        delete textures;
+        delete unknown;
+
+        std::cout << "Total found:" << std::endl;
+        std::cout << "Txt: " << txt_all << std::endl
+                  << "JPG: " << jpg_all << std::endl
+                  << "DDS: " << dds_all << std::endl
+                  << "WAV: " << wav_all << std::endl
+                  << "OGG: " << ogg_all << std::endl
+                  << "Fonts " << font_all << std::endl
+                  << "Unknown: " << unk_all << std::endl;
+
+        std::cout << "Total exported (counted both to files and databases):" << std::endl;
+        std::cout << "Txt: " << txt_suc << std::endl
+                  << "JPG: " << jpg_suc << std::endl
+                  << "DDS: " << dds_suc << std::endl
+                  << "WAV: " << wav_suc << std::endl
+                  << "OGG: " << ogg_suc << std::endl
+                  << "Fonts " << font_suc << std::endl
+                  << "Unknown: " << unk_suc << std::endl;
+
+        fprintf(stdout, "Spent %f seconds on running unpacker! Thank you for your patience!\n",
+                float(clock() - begin_time) / CLOCKS_PER_SEC);
+        system("pause");
+    } catch (...) {
+        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...");
+    }
+    return 0;
+}
+

+ 43 - 0
patch_test.cpp

@@ -0,0 +1,43 @@
+//
+// Created by Иван_Архипов on 23.11.2017.
+//
+#define _CRT_SECURE_NO_WARNINGS
+#include <iostream>
+#include <ctime>
+#include <algorithm>
+
+#ifdef WIN32
+#include <direct.h>
+#define mkdir(dir, mode) _mkdir(dir)
+#endif
+
+#include "LotroDatPatcher.h"
+using namespace LOTRO_DAT;
+
+// Change these 2 variables to your path and name of .dat file
+const std::string path = "E:\\SteamLibrary\\steamapps\\common\\";//Lord Of The Rings Online\\";
+const std::string filename = "client_local_English.dat";
+
+const std::string patch_filename = "image.jpg";
+const int file_id = 0;
+
+int main() {
+    const clock_t begin_time = clock();
+
+    freopen("patch_errors.log", "w", stderr);
+    try {
+        std::cout << "Starting search...\n";
+        DatFile a(path + filename, 0);
+        std::cout << "Total files found: " << a.dictionary().size() << std::endl;
+
+
+        fprintf(stdout, "Spent %f seconds on running patcher! Thank you for your patience!\n",
+                float(clock() - begin_time) / CLOCKS_PER_SEC);
+        system("pause");
+    } catch (...) {
+        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...");
+    }
+    return 0;
+}
+

+ 0 - 189
test.cpp

@@ -1,189 +0,0 @@
-//
-// Created by Иван_Архипов on 30.10.2017.
-//
-#define _CRT_SECURE_NO_WARNINGS
-#include <iostream>
-#include <windows.h>
-#include <ctime>
-#include <clocale>
-using namespace std;
-#include "LotroDatPatcher.h"
-using namespace LOTRO_DAT;
-
-#include <string>
-#include <locale>
-#include <codecvt>
-
-int main() {
-    const clock_t begin_time = clock();
-
-	// Change these 2 variables to your path and name of .dat file
-    std::string path = "E:\\SteamLibrary\\steamapps\\common\\";//Lord Of The Rings Online\\";
-    std::string filename = "client_local_English.dat";
-
-    bool exportTextsToFiles = false;
-    bool exportImagesToFiles = false;
-    bool exportFontsToFiles = true;
-    bool exportSoundsToFiles = false;
-    bool exportTexturesToFiles = false;
-    bool exportUnknownToFiles = true;
-
-    bool exportTextsToDb = false;
-    bool exportImagesToDb = false;
-    bool exportFontsToDb = true;
-    bool exportSoundsToDb = false;
-    bool exportTexturesToDb = false;
-    bool exportUnknownToDb = false;
-
-	// There is no need to touch something else
-
-	std::string output_dir = std::string("Data (") + filename + std::string(")\\");
-    //freopen((output_dir + std::string("errors.log")).c_str(), "w", stderr);
-
-	std::string txt_dir = "txt\\";
-	std::string jpg_dir = "jpg\\";
-	std::string dds_dir = "dds\\";
-	std::string wav_dir = "wav\\";
-	std::string ogg_dir = "ogg\\";
-	std::string font_dir = "font\\";
-	std::string unk_dir = "unk\\";
-
-	int txt_all = 0, txt_suc = 0;
-	int jpg_all = 0, jpg_suc = 0;
-	int dds_all = 0, dds_suc = 0;
-	int wav_all = 0, wav_suc = 0;
-	int ogg_all = 0, ogg_suc = 0;
-	int font_all = 0, font_suc = 0;
-	int unk_all = 0, unk_suc = 0;
-
-	CreateDirectory(output_dir.c_str(), nullptr);
-	CreateDirectory((output_dir + txt_dir).c_str(), nullptr);
-	CreateDirectory((output_dir + jpg_dir).c_str(), nullptr);
-	CreateDirectory((output_dir + dds_dir).c_str(), nullptr);
-	CreateDirectory((output_dir + wav_dir).c_str(), nullptr);
-	CreateDirectory((output_dir + ogg_dir).c_str(), nullptr);
-	CreateDirectory((output_dir + font_dir).c_str(), nullptr);
-	CreateDirectory((output_dir + unk_dir).c_str(), nullptr);
-
-    Database *images = nullptr;
-    Database *sounds = nullptr;
-    Database *texts = nullptr;
-    Database *fonts = nullptr;
-    Database *textures = nullptr;
-    Database *unknown = nullptr;
-
-    if (exportImagesToDb) images = new Database(output_dir + std::string("Images.db"));
-    if (exportSoundsToDb) sounds = new Database(output_dir + std::string("Sounds.db"));
-    if (exportTextsToDb) texts = new Database(output_dir + std::string("Texts.db"));
-    if (exportFontsToDb) fonts = new Database(output_dir + std::string("Fonts.db"));
-    if (exportTexturesToDb) textures = new Database(output_dir + std::string("Textures.db"));
-    if (exportUnknownToDb) unknown = new Database(output_dir + std::string("Unknown.db"));
-
-	std::cout << "Starting search...\n";
-	DatFile a(path + filename);
-    std::cout << "Total files found: " << a.dictionary().size() << std::endl;
-
-	std::cout << "Writing unordered dictionary:\n";
-	FILE *f;
-	fopen_s(&f, (output_dir + "dict.txt").c_str(), "w");
-	fprintf(f, "file_id offset size size2 extension\n");
-	for (auto i : a.dictionary()) {
-		std::string extension = "unk";
-		EXTENSION ext = i.second->ext();
-		if (ext == TXT) extension = "txt";
-		if (ext == JPG) extension = "jpg";
-		if (ext == DDS) extension = "dds";
-		if (ext == WAV) extension = "wav";
-		if (ext == OGG) extension = "ogg";
-		if (ext == FONT) extension = "font";
-		fprintf(f, "%lld %lld %lld %lld %s\n", i.second->file_id(), i.second->file_offset()
-                , i.second->file_size(), i.second->block_size(), extension.c_str());
-	}
-	fclose(f);
-	
-	std::cout << "Beginning unpacking...\n";
-
-	long long all = a.dictionary().size();
-	long long counter = 0;
-    
-	for (auto i : a.dictionary())
-    {
-		EXTENSION ext = i.second->ext();
-        if (ext == TXT) {
-        	txt_all++;
-            if (exportTextsToDb) txt_suc += i.second->ExportFile((output_dir + txt_dir + std::to_string(i.second->file_id())).c_str(), texts);
-            if (exportTextsToFiles) txt_suc += i.second->ExportFile((output_dir + txt_dir + std::to_string(i.second->file_id())).c_str());
-        }
-
-        if (ext == JPG) {
-            jpg_all++;
-            if (exportImagesToDb) jpg_suc += i.second->ExportFile((output_dir + jpg_dir + std::to_string(i.second->file_id())).c_str(), images);
-            if (exportImagesToFiles) jpg_suc += i.second->ExportFile((output_dir + jpg_dir + std::to_string(i.second->file_id())).c_str());
-        }
-
-        if (ext == DDS) {
-            dds_all++;
-            if (exportTexturesToDb) dds_suc += i.second->ExportFile((output_dir + dds_dir + std::to_string(i.second->file_id())).c_str(), textures);
-            if (exportTexturesToFiles) dds_suc += i.second->ExportFile((output_dir + dds_dir + std::to_string(i.second->file_id())).c_str());
-        }
-
-        if (ext == WAV) {
-            wav_all++;
-            if (exportSoundsToDb) wav_suc += i.second->ExportFile((output_dir + wav_dir + std::to_string(i.second->file_id())).c_str(), sounds);
-            if (exportSoundsToFiles) wav_suc += i.second->ExportFile((output_dir + wav_dir + std::to_string(i.second->file_id())).c_str());
-        }
-
-        if (ext == OGG) {
-            ogg_all++;
-            if (exportSoundsToDb) ogg_suc += i.second->ExportFile((output_dir + ogg_dir + std::to_string(i.second->file_id())).c_str(), sounds);
-            if (exportSoundsToFiles) ogg_suc += i.second->ExportFile((output_dir + ogg_dir + std::to_string(i.second->file_id())).c_str());
-        }
-
-        if (ext == FONT) {
-            font_all++;
-            if (exportFontsToDb) font_suc += i.second->ExportFile((output_dir + font_dir + std::to_string(i.second->file_id())).c_str(), fonts);
-            if (exportFontsToFiles) font_suc += i.second->ExportFile((output_dir + font_dir + std::to_string(i.second->file_id())).c_str());
-        }
-
-        if (ext == UNKNOWN)
-        {
-            unk_all++;
-            if (exportUnknownToDb) unk_suc += i.second->ExportFile((output_dir + unk_dir + std::to_string(i.second->file_id())).c_str(), unknown);
-            if (exportUnknownToFiles) unk_suc += i.second->ExportFile((output_dir + unk_dir + std::to_string(i.second->file_id())).c_str());
-        }
-
-		counter++;
-		if ((counter - 1) * 100ll / all != counter * 100ll / all) {
-			fprintf(stdout, "Done: %lld%%\n", counter * 100ll / all);
-		}
-    }
-
-	delete images;
-    delete sounds;
-    delete texts;
-    delete fonts;
-    delete textures;
-    delete unknown;
-
-	std::cout << "Total found:" << std::endl;
-	std::cout << "Txt: " << txt_all << std::endl
-		<< "JPG: " << jpg_all << std::endl
-		<< "DDS: " << dds_all << std::endl
-		<< "WAV: " << wav_all << std::endl
-		<< "OGG: " << ogg_all << std::endl
-		<< "Fonts " << font_all << std::endl
-		<< "Unknown: " << unk_all << std::endl;
-
-	std::cout << "Total exported (counted both to files and databases):" << std::endl;
-	std::cout << "Txt: " << txt_suc << std::endl
-		<< "JPG: " << jpg_suc << std::endl
-		<< "DDS: " << dds_suc << std::endl
-		<< "WAV: " << wav_suc << std::endl
-		<< "OGG: " << ogg_suc << std::endl
-		<< "Fonts " << font_suc << std::endl
-		<< "Unknown: " << unk_suc << std::endl;
-
-	fprintf(stdout, "Spent %f seconds on running unpacker! Thank you for your patience!\n", float(clock() - begin_time) / CLOCKS_PER_SEC);
-	system("pause");
-	return 0;
-}