Quellcode durchsuchen

ver. 1.0.7 (added locales original and ru and switching between them)

Ivan Arkhipov vor 6 Jahren
Ursprung
Commit
594f18e5a6
12 geänderte Dateien mit 454 neuen und 106 gelöschten Zeilen
  1. 1 1
      CMakeLists.txt
  2. 2 0
      src/Common/DatException.h
  3. 187 58
      src/DatFile.cpp
  4. 82 24
      src/DatFile.h
  5. 13 1
      src/Database.cpp
  6. 5 0
      src/Database.h
  7. 144 16
      src/Examples/patcher_example.cpp
  8. 1 0
      src/LotroDatPatcher.h
  9. 1 4
      src/SubDirectory.cpp
  10. 2 1
      src/SubDirectory.h
  11. 13 0
      src/Subfile.cpp
  12. 3 1
      src/Subfile.h

+ 1 - 1
CMakeLists.txt

@@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD 11)
 set(PROJECT_BINARY_DIR bin)
 set(PROJECT_VERSION 0.1.0)
 
-SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS} -O2" )
+SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" )
 SET(CMAKE_EXE_LINKER_FLAGS  "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
 
 if (MSVS)

+ 2 - 0
src/Common/DatException.h

@@ -24,6 +24,8 @@ namespace LOTRO_DAT
         EXPORT_EXCEPTION,
         DATA_EXCEPTION,
         DATABASE_EXCEPTION,
+        LOCALE_EXCEPTION,
+        NOFILE_EXCEPTION,
         UNKNOWN_EXCEPTION
     };
 

+ 187 - 58
src/DatFile.cpp

@@ -16,12 +16,10 @@
 extern "C++"
 {
 namespace LOTRO_DAT {
-    DatFile::DatFile() : dat_state_(CLOSED), patched_(false) {}
+    DatFile::DatFile() : dat_state_(CLOSED) {}
 
-    DatFile::DatFile(const char *filename, int dat_id)
-            : dat_id_(dat_id)
-            , dat_state_(CLOSED)
-            , patched_(false) {
+    DatFile::DatFile(const char *filename, int dat_id) : dat_id_(dat_id) , dat_state_(CLOSED) {
+        filename_ = std::string(filename);
         OpenDatFile(filename);
         ReadSuperBlock();
         MakeDirectories();
@@ -33,28 +31,32 @@ namespace LOTRO_DAT {
             fprintf(stderr, "Unable to make dictionary!! Unable to init DatFile!!!");
             return;
         }
+
+        InitLocale(PATCHED, (std::string(filename) + std::string("patched.dbgm")).c_str());
+        InitLocale(ORIGINAL,(std::string(filename) + std::string("original.dbgm")).c_str());
+
+        FILE *locale = fopen((std::string(filename) + ".dbgm").c_str(), "r");
+        if (locale == nullptr)
+            current_locale_ = ORIGINAL;
+        else {
+            auto loc = new char[10];
+            fscanf(locale, "%s", loc);
+            if (std::string(loc) == "RU")
+                current_locale_ = PATCHED;
+            if (std::string(loc) == "EN")
+                current_locale_ = ORIGINAL;
+        }
+
         if (dat_state_ == SUCCESS_DICTIONARY)
             dat_state_ = READY;
         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() {
-        if (patched_) {
-            std::cout << "There are some updated files. Rewriting dictionary..." << std::endl << std::flush;
-            UpdateHeader();
-            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;
-        }
+        CommitChanges();
+        CommitLocales();
 
         if (file_handler_ != nullptr)
 		    fclose(file_handler_);
@@ -71,7 +73,7 @@ namespace LOTRO_DAT {
     /// Throws DatException() if undefined behaviour happened
 
     bool DatFile::ExtractFile(long long file_id, const std::string &path) {
-        if (dat_state_ != READY) {
+        if (dat_state_ < READY) {
             throw DatException("Bad DatFile::ExtractFile() - invalid DatFile state!", EXPORT_EXCEPTION);
         }
         BinaryData file_data;
@@ -105,7 +107,7 @@ namespace LOTRO_DAT {
     /// Throws DatException() if undefined behaviour happened
 
     bool DatFile::ExtractFile(long long file_id, Database *db) {
-        if (dat_state_ != READY) {
+        if (dat_state_ < READY) {
             throw DatException("Bad DatFile::ExtractFile() - invalid DatFile state!", EXPORT_EXCEPTION);
         }
 
@@ -157,7 +159,7 @@ namespace LOTRO_DAT {
     /// Throws DatException() if undefined behaviour happened
 
     int DatFile::ExtractAllFilesByType(FILE_TYPE type, std::string path) {
-        if (dat_state_ != READY) {
+        if (dat_state_ <  READY) {
             throw DatException("Bad DatFile::ExtractAllFilesByType() - invalid DatFile state!", EXPORT_EXCEPTION);
         }
 
@@ -177,7 +179,7 @@ namespace LOTRO_DAT {
     /// Throws DatException() if undefined behaviour happened
 
     int DatFile::ExtractAllFilesByType(FILE_TYPE type, Database *db) {
-        if (dat_state_ != READY) {
+        if (dat_state_ <  READY) {
             throw DatException("Bad DatFile::ExtractAllFilesByType() - invalid DatFile state!", EXPORT_EXCEPTION);
         }
 
@@ -191,8 +193,12 @@ namespace LOTRO_DAT {
         return success;
     }
 
-    /// TODO: Write description and make asserts
+    // TODO: Write description and make asserts
     bool DatFile::PatchFile(const char *filename, YAML::Node options) {
+        if (dat_state_ <  READY) {
+            throw DatException("Bad DatFile::PatchFile() - invalid DatFile state!", EXPORT_EXCEPTION);
+        }
+
         if (options["did"].IsDefined() && options["did"].as<int>() != dat_id_)
             return false;
 
@@ -200,6 +206,12 @@ namespace LOTRO_DAT {
         data.ReadFromFile(filename);
 
         auto file_id = options["fid"].as<long long>();
+
+        if (dictionary_[file_id] == nullptr) {
+            fprintf(stderr, "ERROR DatFile::PatchFile() - Cannot patch file - there is no file in dictionary with file_id = %lld.\n", file_id);
+            return false;
+        }
+
         BinaryData old_data = GetFileData(dictionary_[file_id]);
         data = dictionary_[file_id]->MakeForImport(old_data, SubfileData(data, u"", options));
 
@@ -221,6 +233,10 @@ namespace LOTRO_DAT {
 
     // TODO: Write description and make asserts
     bool DatFile::PatchFile(const SubfileData &data) {
+        if (dat_state_ <  READY) {
+            throw DatException("Bad DatFile::PatchFile() - invalid DatFile state!", EXPORT_EXCEPTION);
+        }
+
         auto file_id = data.options["fid"].as<long long>();
         Subfile *file = dictionary_[file_id];
 
@@ -231,12 +247,16 @@ namespace LOTRO_DAT {
 
         BinaryData old_data = GetFileData(file);
         BinaryData patch_data = file->MakeForImport(old_data, data);
-        ApplyFilePatch(file, patch_data);
+        ApplyFilePatch(dictionary_[file_id], patch_data);
         return true;
     }
 
-    // TODO: Write description and make asserts
+    // TODO: Write description
     bool DatFile::PatchAllDatabase(Database *db) {
+        if (dat_state_ <  READY) {
+            throw DatException("Bad DatFile::PatchAllDatabase() - invalid DatFile state!", EXPORT_EXCEPTION);
+        }
+
         SubfileData data;
         try {
             data = db->GetNextFile();
@@ -261,6 +281,8 @@ namespace LOTRO_DAT {
                 return false;
             }
         }
+        CommitChanges();
+        CommitLocales();
         return true;
     }
 
@@ -324,28 +346,6 @@ namespace LOTRO_DAT {
         return data;
     }
 
-    /// DatFile constants' getters.
-
-    long long DatFile::constant1() const {
-        return constant1_;
-    }
-
-    long long DatFile::constant2() const {
-        return constant2_;
-    }
-
-    long long DatFile::file_size() const {
-        return file_size_;
-    }
-
-    long long DatFile::version1() const {
-        return version1_;
-    }
-
-    long long DatFile::version2() const {
-        return version2_;
-    }
-
     /// DatFile special functions for opening and reading/writing raw data.
     /// Shouldn't be used by any external classes except Subfile and Subdirectory.
 
@@ -359,7 +359,7 @@ namespace LOTRO_DAT {
         if (file_handler_ == nullptr) {
             std::string err = "Bad DatFile::OpenDatFile. Unable to open file ";
             err += dat_name;
-            throw DatException(err.c_str(), INIT_EXCEPTION);
+            throw DatException(err.c_str(), NOFILE_EXCEPTION);
         }
 
         fseek(file_handler_, 0, SEEK_END);
@@ -434,7 +434,7 @@ namespace LOTRO_DAT {
             throw DatException(err.c_str(), READ_EXCEPTION);
         }
 
-        if (offset + size > file_size()) {
+        if (offset + size > file_size_) {
             std::string err = "Bad DatFile::ReadData - trying to read more than DatFile size elapsed\n";
             err += std::string("Reading ") + std::to_string(size) + std::string(" bytes from ")
                    + std::to_string(offset) + std::string(" position in dat file.");
@@ -447,14 +447,14 @@ namespace LOTRO_DAT {
     }
 
     void DatFile::WriteData(const BinaryData &data, long long size, long long offset, long long data_offset) {
-        if (dat_state_ != READY)
+        if (dat_state_ < READY)
             throw DatException("Bad DatFile::WriteData() - DatFile isn't in valid state!", WRITE_EXCEPTION);
 
        _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);
 
-        int x = fwrite(data.data() + data_offset, unsigned(size), 1, file_handler_);		
+        fwrite(data.data() + data_offset, unsigned(size), 1, file_handler_);
     }
 
     /// Special functions used by patch process.
@@ -465,14 +465,17 @@ namespace LOTRO_DAT {
             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;
+        if (current_locale() != PATCHED) {
+            std::cout << "Changing locale to RU in order to patch file";
+            SetLocale(PATCHED);
+        }
+        dat_state_ = UPDATED;
 
         auto journal = GetFragmentationJournal();
 
         file->file_size_ = data.size() - 8;
 
-        //if (data.size() > file->block_size()) {
+        if (patch_dict_.count(file->file_id()) == 0 || data.size() > file->block_size()) {
             file->file_offset_ = journal[0].second;
             file->block_size_ = std::max(data.size(), 256u);
 
@@ -482,7 +485,7 @@ namespace LOTRO_DAT {
             WriteData(nulls, nulls.size(), file_size_);
 
             this->file_size_ += data.size();
-        //}
+        }
 
         BinaryData fragments_count(4);
         fragments_count.FromNumber<4>(0);
@@ -495,8 +498,12 @@ namespace LOTRO_DAT {
 
         WriteData(file_data, file_data.size(), file->file_offset());
 
-        nulls = BinaryData(unsigned(file->block_size_ - file_data.size()));
-        WriteData(nulls, nulls.size(), file->file_offset() + file_data.size());
+        auto file_id = file->file_id();
+        patched_list.insert(file_id);
+
+        delete patch_dict_[file_id]; // Удалили старое значение в русском словаре
+        patch_dict_[file_id] = new Subfile(this, file->MakeHeaderData()); // Создали новое значение
+
         UpdateFragmentationJournal(journal);
     }
 
@@ -550,5 +557,127 @@ namespace LOTRO_DAT {
             WriteData(data, 4, fragmentation_journal_offset_ + 8 * (i + 1) + 4);
         }
     }
+
+    void DatFile::CommitChanges() {
+        if (dat_state_ != UPDATED)
+            return;
+        std::cout << "There are some updated files. Rewriting dictionary..." << std::endl << std::flush;
+        UpdateHeader();
+        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;
+        patched_list.clear();
+        dat_state_ = READY;
+    }
+
+    // LOCALE MANAGING SECTION
+
+    void DatFile::InitLocale(LOCALE locale, const char* filename) {
+        auto dict = GetLocaleDictReference(locale);
+        dict->clear();
+
+        FILE *dict_file = fopen(filename, "rb");
+
+        if (dict_file == nullptr) {
+            if (locale == ORIGINAL) {
+                for (auto file : dictionary_) {
+                    (*dict)[file.first] = new Subfile(this, file.second->MakeHeaderData());
+                }
+            }
+            return;
+        }
+
+        size_t size;
+        fread(&size, sizeof(size_t), 1, dict_file);
+
+        std::cout << "There are " << size << " files in " << std::string(filename) << " dictionary...\n";
+
+        for (size_t i = 0; i < size; i++) {
+            BinaryData header(32);
+            fread(header.data(), unsigned(header.size()), 1, dict_file);
+            auto file = new Subfile(this, header);
+            (*dict)[file->file_id()] = file;
+        }
+
+        fclose(dict_file);
+    }
+
+    std::unordered_map<long long, Subfile *> *DatFile::GetLocaleDictReference(LOCALE locale) {
+        switch (locale) {
+            case PATCHED:
+                return &patch_dict_;
+            case ORIGINAL:
+                return &orig_dict_;
+            default:
+                throw DatException("Bad DatFile::GetLocaleDictReference() - unknown locale!!!", LOCALE_EXCEPTION);
+        }
+    }
+
+    void DatFile::SetLocale(LOCALE locale) {
+        if (current_locale_ == locale) {
+            return;
+        }
+        dat_state_ = UPDATED;
+        auto dict = GetLocaleDictReference(locale);
+        for (auto file : *dict) {
+            if (dictionary_[file.first] == nullptr) {
+                fprintf(stderr, "WARNING: In locale dictionary there is file with file_id = %lld, which is not in .dat "
+                        "file! Passing it and removing from locale dictionary\n", file.first);
+                dict->erase(file.first);
+                continue;
+            }
+            if (dictionary_[file.first]->MakeHeaderData().CutData(8, 16) == file.second->MakeHeaderData().CutData(8, 16))
+                continue;
+
+            dictionary_[file.first]->file_offset_ = file.second->file_offset_;
+            dictionary_[file.first]->file_size_ = file.second->file_size_;
+            dictionary_[file.first]->block_size_= file.second->block_size_;
+            dictionary_[file.first]->timestamp_ = file.second->timestamp_;
+            dictionary_[file.first]->version_ = file.second->version_;
+
+            dictionary_[file.first] = file.second;
+            patched_list.insert(file.first);
+            dat_state_ = UPDATED;
+        }
+        current_locale_ = locale;
+        CommitChanges();
+        CommitLocales();
+    }
+
+    void DatFile::SaveLocale(LOCALE locale, const char *filename) {
+        auto dict = GetLocaleDictReference(locale);
+        FILE *dict_file = fopen(filename, "wb");
+
+        size_t count = size_t(dict->size());
+        fwrite(&count, sizeof(size_t), 1, dict_file);
+
+        for (auto file : *dict) {
+            BinaryData header = file.second->MakeHeaderData();
+            fwrite(header.data(), unsigned(header.size()), 1, dict_file);
+        }
+        fclose(dict_file);
+    }
+
+    LOCALE DatFile::current_locale() {
+        return current_locale_;
+    }
+
+    void DatFile::CommitLocales() {
+        std::cout << "Commiting locales..." << std::endl;
+        std::cout << "Saving patched locale..." << std::endl;
+        SaveLocale(PATCHED, (std::string(filename_) + std::string("patched.dbgm")).c_str());
+        std::cout << "Saving original locale..." << std::endl;
+        SaveLocale(ORIGINAL,(std::string(filename_) + std::string("original.dbgm")).c_str());
+
+        std::cout << "Writing current locale" << std::endl;
+        FILE *locale = fopen((std::string(filename_) + ".dbgm").c_str(), "w");
+        if (current_locale_ == ORIGINAL)
+            fprintf(locale, "EN");
+        else
+            fprintf(locale, "RU");
+        fclose(locale);
+        std::cout << "Done!" << std::endl;
+    }
 }
 }

+ 82 - 24
src/DatFile.h

@@ -17,6 +17,7 @@
 #include <set>
 #include <vector>
 #include <yaml-cpp/node/node.h>
+#include <unordered_set>
 #include "Database.h"
 
 // Dat file names definitions
@@ -27,17 +28,20 @@
 #define CLIENT_SURFACE 3
 #define CLIENT_HIGHRES 4
 
-extern  "C++"
-{
-namespace LOTRO_DAT
+extern "C++"
 {
+namespace LOTRO_DAT {
     class BinaryData;
+
     class DatException;
+
     class SubDirectory;
+
     class Subfile;
+
     class SubfileData;
 
-    enum FILE_TYPE : int{
+    enum FILE_TYPE : int {
         TEXT,
         JPG,
         DDS,
@@ -48,64 +52,119 @@ namespace LOTRO_DAT
     };
 
     enum DAT_STATE {
-        CLOSED,
-        SUCCESS_OPENED,
-        SUCCESS_SUPERBLOCK,
-        SUCCESS_DIRECTORIES,
-        SUCCESS_DICTIONARY,
-        READY
+        CLOSED = 1,
+        SUCCESS_OPENED = 2,
+        SUCCESS_SUPERBLOCK = 3,
+        SUCCESS_DIRECTORIES = 4,
+        SUCCESS_DICTIONARY = 5,
+        READY = 6,
+        UPDATED = 7
+    };
+
+    enum LOCALE : unsigned {
+        ORIGINAL = 0,
+        PATCHED = 1
     };
 
     class DatFile {
         friend class SubDirectory;
+
     public:
         DatFile();
-        explicit DatFile(const char* filename, int dat_id);
-        ~DatFile();
+
+        explicit DatFile(const char *filename, int dat_id);
+
+        virtual ~DatFile();
 
         bool ExtractFile(long long file_id, const std::string &path = "");
+
         bool ExtractFile(long long file_id, Database *db);
 
         int ExtractAllFilesByType(FILE_TYPE type, std::string path = "");
+
         int ExtractAllFilesByType(FILE_TYPE type, Database *db);
 
         bool PatchFile(const char *filename, YAML::Node options);
+
         bool PatchFile(const SubfileData &data);
+
         bool PatchAllDatabase(Database *db);
 
         void WriteUnorderedDictionary(std::string path) const;
 
-        long long constant1() const;
-        long long constant2() const;
-        long long file_size() const;
-        long long version1() const;
-        long long version2() const;
-
         long long files_number() const;
 
-        BinaryData GetFileData(const Subfile* file, long long offset = 0);
+        BinaryData GetFileData(const Subfile *file, long long offset = 0);
 
-        void UpdateSubdirectories();
+        void CommitChanges();
 
     private:
-        void OpenDatFile(const char* dat_name);
+        // INIT SECTION
+        void OpenDatFile(const char *dat_name);
+
         void ReadSuperBlock();
+
         void MakeDirectories();
+
         void MakeDictionary();
 
+        bool CheckIfUpdated();
+
+        void RepairPatches(Database *db);
+
+        // READ-WRITE SECTION
+
         void ReadData(BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);
+
         void WriteData(const BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);
 
+        // PATCH SECTION
+
         void ApplyFilePatch(Subfile *file, const BinaryData &data);
+
         std::vector<std::pair<long long, long long> > GetFragmentationJournal();
+
         void UpdateHeader();
+
+        void UpdateSubdirectories();
+
         void UpdateFragmentationJournal(const std::vector<std::pair<long long, long long> > &journal);
 
+        // LOCALE MANAGING SECTION
+
+    public:
+        void InitLocale(LOCALE locale, const char* dict_name);
+
+        void SaveLocale(LOCALE locale, const char* filename);
+
+        void SetLocale(LOCALE locale);
+
+        void CommitLocales();
+
+        LOCALE current_locale();
+
+    private:
+        std::unordered_map<long long, Subfile*>* GetLocaleDictReference(LOCALE locale);
+
+    private:
+        const size_t locale_number = 2;
+        LOCALE current_locale_;
+        FILE* db_blocker_;
+
+        std::string filename_;
+
+        std::unordered_map<long long, Subfile*> orig_dict_;
+        std::unordered_map<long long, Subfile*> patch_dict_;
+        std::unordered_map<long long, Subfile*> ru_pending_patch;
+
     private:
         FILE *file_handler_;
+
         SubDirectory *root_directory_;
-        std::unordered_map<long long, Subfile*> dictionary_;
-        std::unordered_map<long long, BinaryData *> patched_list;
+
+        std::unordered_set<long long> patched_list;
+
+        std::unordered_map<long long, Subfile *> dictionary_;
 
         long long constant1_;
         long long constant2_;
@@ -118,7 +177,6 @@ namespace LOTRO_DAT
         DAT_STATE dat_state_;
 
         int dat_id_;
-        bool patched_;
     };
 }
 }

+ 13 - 1
src/Database.cpp

@@ -47,7 +47,7 @@ namespace LOTRO_DAT {
 
 		sqlite3_prepare_v2(db_, InsertFileCommand_.c_str(), InsertFileCommand_.length(), &insert_request_, nullptr);
 		sqlite3_prepare_v2(db_, FetchOneCommand.c_str(), FetchOneCommand.length(), &fetch_one_request_, nullptr);
-
+		sqlite3_prepare_v2(db_, GetRowsNumberCommand_.c_str(), GetRowsNumberCommand_.length(), &get_rows_number_request_, nullptr);
 
         ExecSql("BEGIN TRANSACTION");
     }
@@ -123,4 +123,16 @@ namespace LOTRO_DAT {
             throw DatException("Bad Database::ClearDatabase() - database hasn't been opened!", DATABASE_EXCEPTION);
         ExecSql(ClearTableCommand_);
     }
+
+    size_t Database::CountRows() {
+        int result = sqlite3_step(get_rows_number_request_);
+
+        if (result == SQLITE_ERROR) {
+            fprintf(stderr, "ERROR: Bad Database::CountRows() - Error when counting rows  %s\n", sqlite3_errmsg(db_));
+            return 0;
+        }
+        long long res = sqlite3_column_int64(get_rows_number_request_, 0);
+        sqlite3_reset(get_rows_number_request_);
+        return size_t(res);
+    }
 }

+ 5 - 0
src/Database.h

@@ -34,12 +34,16 @@ namespace LOTRO_DAT
 
         void ClearDatabase();
 
+        size_t CountRows();
+
     private:
         void ExecSql(const std::string &sql);
 
         sqlite3* db_;
         sqlite3_stmt* insert_request_;
         sqlite3_stmt* fetch_one_request_;
+        sqlite3_stmt* get_rows_number_request_;
+
 
         const std::string CreateTableCommand_ = "CREATE TABLE IF NOT EXISTS `patch_data` ( "
             "`binary_data` BLOB, "
@@ -52,6 +56,7 @@ namespace LOTRO_DAT
 
         const std::string FetchOneCommand = "SELECT * FROM `patch_data`";
         const std::string ClearTableCommand_ = "DELETE * FROM `patch_data`";
+        const std::string GetRowsNumberCommand_ = "SELECT Count(*) as count FROM `patch_data`";
     };
 }
 }

+ 144 - 16
src/Examples/patcher_example.cpp

@@ -12,32 +12,162 @@
 #endif
 
 #include "../LotroDatPatcher.h"
-#include "../Subfile.h"
 using namespace LOTRO_DAT;
+using namespace std;
 
-// 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 rulocale_filename = "rulocale.dbgm";
+const std::string enlocale_filename = "enlocale.dbgm";
 
 int main() {
-    const clock_t begin_time = clock();
-    std::cout << "Gi1dor's LotRO .dat patcher ver. 1.0.3" << std::endl;
+    std::cout << "Gi1dor's LotRO .dat patcher ver. 1.0.6" << std::endl;
     freopen("errors.log", "w", stderr);
 
-    setbuf(stdout, NULL);
+   // setbuf(stdout, NULL);
     setbuf(stderr, NULL);
 
-    setvbuf (stdout, NULL, _IONBF, BUFSIZ);
+   // setvbuf (stdout, NULL, _IONBF, BUFSIZ);
     setvbuf (stderr, NULL, _IONBF, BUFSIZ);
 
-    Database db;
-    db.InitDatabase("patch.db");
-
     try {
-        DatFile *a = new DatFile((path + filename).c_str(), 0);
-        std::cout << "Starting magic...\n";
+        std::cout << "Hello! I'm a basic shell version of .dat file patcher.\n";
+        DatFile *a = nullptr;
+        ifstream in("dat_file_path.txt");
+        if (!in.fail()) {
+            std::string filename;
+            getline(in, filename);
+            try {
+                std::cout << "Using .dat file from dat_file_path.txt...\n";
+                std::cout << "Opening file " << filename << std::endl;
+
+                a = new DatFile(filename.c_str(), 0);
+            } catch (DatException &e) {
+                std::cout << "Dat file path from dat_file_path.txt - " << filename << " may be incorrect (cannot open DatFile there)\n";
+            }
+        }
+
+        while (a == nullptr) {
+            std::cout << "Please, tell, where the .dat file is\n";
+            std::cout << "Enter path to file (including filename): ";
+            std::string filename;
+            std::getline(std::cin, filename);
+
+            std::cout << "Opening file " << filename << std::endl;
+
+            try {
+                a = new DatFile(filename.c_str(), 0);
+            } catch (DatException &e) {
+                if (e.type() == NOFILE_EXCEPTION)
+                    std::cout << "Cannot find file... Could you enter .dat filename once more?" << std::endl;
+                else
+                    std::cout << "Some error caused while opening the file... "
+                            "Could you enter .dat filename once more?" << std::endl;
+                delete a;
+            }
+        }
+
+        std::cout << "Great! File initialised successfully!\n";
         std::cout << "Files number: " << a->files_number() << std::endl;
-        a->PatchAllDatabase(&db);
+
+        while (true) {
+            std::cout << "Please, choose, what should I do. I can patch data from database to .dat file (enter 1), "
+                    "change locale (enter 2), print current locale (enter 3) or exit (enter -1)\n";
+            int cmd = 0;
+            std::cout << "Enter number of command (1-4): ";
+            std::cin >> cmd;
+
+            std::string tmp;
+            std::getline(std::cin, tmp);
+
+            if (cmd == -1) {
+                std::cout << "Exiting. Thanks for using me!\n";
+                break;
+            }
+            if (cmd == 1) {
+                std::cout << "You've chosen to patch database! Write name of database file (it should be in the same "
+                        "directory), of enter 0 to return to main dialogue.\n";
+                while (true) {
+                    std::cout << "Enter name of file or 0: ";
+                    std::string dbname;
+                    std::getline(std::cin, dbname);
+
+                    if (dbname == std::to_string(0)) {
+                        std::cout << "Okay, returning back...\n\n";
+                        break;
+                    }
+
+                    Database db;
+                    try {
+                        std::cout << "Opening database... " << dbname << std::endl;
+                        db.InitDatabase(dbname);
+                        if (db.CountRows() == 0) {
+                            std::cout << "There are no files in database or database doesn't exist. "
+                                    "Please, try again!\n";
+                            continue;
+                        }
+                        std::cout << "There are " << db.CountRows() << " files in database." << std::endl;
+                    } catch (DatException &e) {
+                        std::cout << "Unfortunately, I cannot open this database. Could you try again please?\n";
+                        continue;
+                    }
+
+                    std::cout << "Successfully opened database! Beginning patching...\n";
+                    const clock_t begin_time = clock();
+                    size_t all = db.CountRows();
+                    size_t now = 0;
+
+                    SubfileData data;
+
+                    try {
+                        data = db.GetNextFile();
+                    } catch (std::exception &e) {
+                        fprintf(stderr, "Caught %s exception.\n", e.what());
+                        fprintf(stderr, "DatFile::PatchAllDatabase() error! Caught exception while fetching "
+                                "file from database! Stopping...\n");
+                        return false;
+                    }
+                    while (data != SubfileData()) {
+                        try {
+                            a->PatchFile(data);
+                        } catch (std::exception &e) {
+                            fprintf(stderr, "Caught %s exception.\n", e.what());
+                            fprintf(stderr, "DatFile::PatchAllDatabase() error! Caught exception while "
+                                    "patching file! Passing...\n");
+                        }
+
+                        try {
+                            data = db.GetNextFile();
+                        } catch (std::exception &e) {
+                            fprintf(stderr, "Caught %s exception.\n", e.what());
+                            fprintf(stderr,"DatFile::PatchAllDatabase() error! Caught exception while fetching "
+                                            "file from database! Stopping...\n");
+                            break;
+                        }
+                        ++now;
+                        if (now * 100 / all > (now - 1) * 100 / all)
+                            std::cout << now * 100 / all << "%\n";
+                    }
+
+                    a->CommitChanges();
+                    a->CommitLocales();
+
+                    fprintf(stdout, "Spent %f seconds on patching! Thank you for your patience!\n",
+                            float(clock() - begin_time) / CLOCKS_PER_SEC);
+
+                    std::cout << "Great! File was patched successfully!\n\n";
+                    break;
+                }
+            }
+            if (cmd == 2) {
+                std::cout << "Old locale is " << (a->current_locale() == PATCHED ? "RU" : "Original") << endl;
+                std::cout << "Changing locale..." << std::endl;
+                a->SetLocale(a->current_locale() == PATCHED ? ORIGINAL : PATCHED);
+                std::cout << "New locale is " << (a->current_locale() == PATCHED ? "RU" : "Original") << endl << endl;
+            }
+
+            if (cmd == 3) {
+                std::cout << "Current locale is " << (a->current_locale() == PATCHED ? "RU" : "Original") << endl << endl;
+            }
+        }
         delete a;
     } catch (std::exception &e) {
         fprintf(stderr, "Caught %s exception.", e.what());
@@ -48,8 +178,6 @@ int main() {
         fprintf(stderr, "Some critical errors occured. Need to stop execution now...\n");
     }
 
-    fprintf(stdout, "Spent %f seconds on performing magic! Thank you for your patience!\n",
-            float(clock() - begin_time) / CLOCKS_PER_SEC);
     system("pause");
     return 0;
 }

+ 1 - 0
src/LotroDatPatcher.h

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

+ 1 - 4
src/SubDirectory.cpp

@@ -120,17 +120,14 @@ namespace LOTRO_DAT {
             i->MakeDictionary(dict);
     }
 
-    void SubDirectory::UpdateDirectories(std::unordered_map<long long, BinaryData *> &patched_files, std::unordered_map<long long, Subfile*> &dict) {
+    void SubDirectory::UpdateDirectories(std::unordered_set<long long> &patched_files, std::unordered_map<long long, Subfile*> &dict) {
         for (unsigned i = 0; i < subfiles_.size(); i++) {
             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.CutData(8, 16) != patched_files[file_id]->CutData(8, 16))
-                    continue;
 
-                BinaryData header = dict[subfiles_[i]->file_id()]->MakeHeaderData();
                 BinaryData write_data(4);
 
                 write_data.FromNumber<4>(dict[file_id]->file_offset());

+ 2 - 1
src/SubDirectory.h

@@ -8,6 +8,7 @@
 #include <vector>
 #include <map>
 #include <unordered_map>
+#include <unordered_set>
 
 extern "C++"
 {
@@ -27,7 +28,7 @@ namespace LOTRO_DAT
         ~SubDirectory();
         void MakeDictionary(std::unordered_map<long long, Subfile*> &dict);
 
-        void UpdateDirectories(std::unordered_map<long long, BinaryData *> &patched_files, std::unordered_map<long long, Subfile*> &dict);
+        void UpdateDirectories(std::unordered_set<long long> &patched_files, std::unordered_map<long long, Subfile*> &dict);
 
     private:
         void MakeSubDirectories();

+ 13 - 0
src/Subfile.cpp

@@ -18,6 +18,18 @@ namespace LOTRO_DAT {
 
     Subfile::Subfile() = default;
 
+    Subfile::Subfile(DatFile *dat, const BinaryData &header) {
+        dat_ = dat;
+        fragments_count_ = header.ToNumber<4>(0); // fragments_count
+        unknown1_ = header.ToNumber<4>(4); // unknown1
+        file_id_ = header.ToNumber<4>(8); // file_id
+        file_offset_ = header.ToNumber<4>(12); // file_offset
+        file_size_ = header.ToNumber<4>(16); // block_size
+        timestamp_ = header.ToNumber<4>(20); // timestamp
+        version_ = header.ToNumber<4>(24); // version
+        block_size_ = header.ToNumber<4>(28); // block_size
+    }
+
     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) :
@@ -114,6 +126,7 @@ namespace LOTRO_DAT {
 
     BinaryData Subfile::MakeHeaderData() const {
         BinaryData header;
+
         BinaryData data(4);
         data.FromNumber<4>(fragments_count_);
         header = header + data;

+ 3 - 1
src/Subfile.h

@@ -23,9 +23,11 @@ namespace LOTRO_DAT
     class Subfile
     {
         friend class DatFile;
+		friend class SubDirectory;
     public:
         Subfile();
-        Subfile(DatFile *dat, long long dictionary_offset, long long fragments_count, long long unknown1,
+		Subfile(DatFile *dat, const BinaryData &header);
+		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);