Browse Source

Modified Locale correctness check system, small fixes in FS, IO and Backup modules

Ivan Arkhipov 6 years ago
parent
commit
a61a992781

+ 2 - 0
include/DatSubsystems/DatFileSystem.h

@@ -84,6 +84,8 @@ namespace LOTRO_DAT {
         std::set<SubDirectory, SubDirectory::SubDirectoryOffsetComparator> subdir_init_queue_;
         std::unordered_map<long long, SubFile> subfile_init_map_;
         std::set<SubFile, SubFile::SubFileOffsetComparator> subfile_init_queue_;
+    public:
+        long long patched_file_end;
     };
 }
 };

+ 2 - 1
include/DatSubsystems/DatIO.h

@@ -50,6 +50,7 @@ namespace LOTRO_DAT {
 
         DatOperationResult<> DeInit();
 
+        unsigned int getHeaderHash();
     private:
 
         //------------------------------------------------//
@@ -80,6 +81,7 @@ namespace LOTRO_DAT {
         std::string filename_;
 
     public:
+        /// Header values
         long long constant1;
         long long constant2;
         long long file_size;
@@ -93,7 +95,6 @@ namespace LOTRO_DAT {
 
     private:
         long long actual_dat_size_;
-        long long elapsed_eof_buffer_;
         const unsigned MAX_EOF_BUFFER = 15 * 1024 * 1024; /// Maximal size of buffer, which is written to the end of dat file to improve write speed of small fragments
     };
 }

+ 3 - 0
include/DatSubsystems/DatLocaleManager.h

@@ -51,6 +51,8 @@ namespace LOTRO_DAT {
 
         DatOperationResult<> DeInit();
 
+        DatOperationResult<> CommitLocales();
+
         LOCALE GetCurrentLocale();
 
         bool CheckLocaleCorrect();
@@ -66,6 +68,7 @@ namespace LOTRO_DAT {
         void UpdateCategory(long long file_id, long long category);
 
         const std::set<long long>& GetInactiveCategories();
+
     private:
         std::map<long long, SubFile> &GetLocaleDictReference(LOCALE locale);
 

+ 6 - 6
src/DatSubsystems/DatBackupManager.cpp

@@ -122,21 +122,21 @@ namespace LOTRO_DAT {
 
     DatOperationResult<> DatBackupManager::CopyDatFile(DatFile &source, FILE *target) {
         long long parts_count =
-                source.GetIO().file_size / COPY_BLOCK_SIZE + (source.GetIO().file_size % COPY_BLOCK_SIZE != 0);
+                source.GetIO().GetActualDatSize().value / COPY_BLOCK_SIZE + (source.GetIO().GetActualDatSize().value % COPY_BLOCK_SIZE != 0);
         long long newfile_size = 0;
-        long long elapsed_size = source.GetIO().file_size - newfile_size;
+        unsigned elapsed_size = unsigned(source.GetIO().GetActualDatSize().value - newfile_size);
 
         BinaryData data(COPY_BLOCK_SIZE);
         for (unsigned i = 0; i < parts_count; i++) {
             dat->GetStatusModule().SetPercentage(i * 100 / (unsigned)parts_count);
 
-            auto operation = source.GetIO().ReadData(data, std::min((long long)(COPY_BLOCK_SIZE), elapsed_size), newfile_size);
+            auto operation = source.GetIO().ReadData(data, std::min(COPY_BLOCK_SIZE, elapsed_size), newfile_size);
             if (operation.result == ERROR)
                 return DatOperationResult<>(ERROR, "Copy failed. Read data error");
-            fwrite(data.data(), data.size(), 1, target);
+            fwrite(data.data(), std::min(COPY_BLOCK_SIZE, elapsed_size), 1, target);
 
-            newfile_size += std::min((long long)(COPY_BLOCK_SIZE), elapsed_size);
-            elapsed_size -= std::min((long long)(COPY_BLOCK_SIZE), elapsed_size);
+            newfile_size += std::min(COPY_BLOCK_SIZE, elapsed_size);
+            elapsed_size -= std::min(COPY_BLOCK_SIZE, elapsed_size);
         }
 
         return DatOperationResult<>(SUCCESS);

+ 2 - 0
src/DatSubsystems/DatFileSystem.cpp

@@ -198,6 +198,7 @@ namespace LOTRO_DAT {
             return DatOperationResult<>(ERROR, "DATFSINIT: no connection with Dat (dat is nullptr)");
 
         DeInit();
+        patched_file_end = dat->GetIO().file_size;
         SubDirectory root_directory(0, (unsigned) dat->GetIO().root_directory_offset);
         subdir_init_queue_.insert(root_directory);
         return DatOperationResult<>(SUCCESS);
@@ -256,6 +257,7 @@ namespace LOTRO_DAT {
         fprintf(file, "Files in dictionary number: %d\n", dictionary_.size());
         fprintf(file, "Files visited: %d\n", visited_subfiles_ids_.size());
         fprintf(file, "Folders visited: %d\n", visited_subdirectories_offsets_.size());
+        fprintf(file, "File patched size: %lld\n", patched_file_end);
 
         std::map<FILE_TYPE, size_t> filetypes_count{{TEXT, 0},
                                                     {JPG, 0},

+ 33 - 16
src/DatSubsystems/DatIO.cpp

@@ -14,6 +14,19 @@
 #define ftell _ftelli64
 #endif
 
+unsigned int RSHash(const std::string& str) {
+    unsigned int b    = 378551;
+    unsigned int a    = 63689;
+    unsigned int hash = 0;
+
+    for (char i : str) {
+        hash = hash * a + i;
+        a    = a * b;
+    }
+
+    return hash;
+}
+
 extern "C++"
 {
 namespace LOTRO_DAT {
@@ -26,7 +39,7 @@ namespace LOTRO_DAT {
      * \param[in] datFile Указатель на объект управляющего класса DatFile
      */
 
-    DatIO::DatIO(DatFile *datFile) : dat(datFile), file_handler_(nullptr), filename_("none"), actual_dat_size_(0), elapsed_eof_buffer_(0) {
+    DatIO::DatIO(DatFile *datFile) : dat(datFile), file_handler_(nullptr), filename_("none"), actual_dat_size_(0) {
     }
 
 
@@ -139,16 +152,13 @@ namespace LOTRO_DAT {
         free_dat_size = data.ToNumber<4>(0x19C);
 
         if (constant1 != 0x4C5000)
-            return DatOperationResult<>(ERROR, std::string("Variable at position 0x100 is not equal to .dat file constant!"));
+            return DatOperationResult<>(ERROR, "Variable at position 0x100 is not equal to .dat file constant!");
 
         if (constant2 != 0x5442)
-            return DatOperationResult<>(ERROR, std::string("Variable at position 0x140 is not equal to .dat file constant!"));
-
-        if (file_size != actual_dat_size_)
-            LOG(INFO) << "Variable at 0x148 position is not equal to .dat file size!";
+            return DatOperationResult<>(ERROR, "Variable at position 0x140 is not equal to .dat file constant!");
 
         LOG(INFO) << "DatIO: Superblock read successfully";
-        return DatOperationResult<>(SUCCESS, std::string("Superblock read successfully."));
+        return DatOperationResult<>(SUCCESS, "Superblock read successfully.");
     }
 
 
@@ -202,17 +212,10 @@ namespace LOTRO_DAT {
      */
 
     void DatIO::UpdateBufferIfNeeded(long long size_to_write) {
-        if (elapsed_eof_buffer_ >= size_to_write) {
-            elapsed_eof_buffer_ -= size_to_write;
-            return;
-        }
-
         BinaryData empty_buffer(std::max(MAX_EOF_BUFFER, unsigned(size_to_write)));
-
         fseek(file_handler_, 0, SEEK_END);
         fwrite(empty_buffer.data(), empty_buffer.size(), 1, file_handler_);
         actual_dat_size_ += empty_buffer.size();
-        elapsed_eof_buffer_ += empty_buffer.size() - size_to_write;
     }
 
 
@@ -278,8 +281,6 @@ namespace LOTRO_DAT {
         fragmentation_journal_end = 0;
         root_directory_offset = 0;
         fragmentation_journal_offset = 0;
-        elapsed_eof_buffer_ = 0;
-
         return DatOperationResult<>(SUCCESS, "File deinitialisation successfull");
     }
 
@@ -379,5 +380,21 @@ namespace LOTRO_DAT {
         fprintf(file, "DatLibrary: EOF buffer size constant = %d\n", MAX_EOF_BUFFER);
     }
 
+    unsigned int DatIO::getHeaderHash() {
+        std::string values =
+                std::to_string(constant1) +
+                std::to_string(constant2) +
+                std::to_string(file_size) +
+                std::to_string(version1) +
+                std::to_string(version2) +
+                std::to_string(fragmentation_journal_offset) +
+                std::to_string(fragmentation_journal_end) +
+                std::to_string(fragmentation_journal_size) +
+                std::to_string(root_directory_offset) +
+                std::to_string(free_dat_size);
+
+        return RSHash(values);
+    }
+
 }
 }

+ 163 - 148
src/DatSubsystems/DatLocaleManager.cpp

@@ -53,8 +53,13 @@ namespace LOTRO_DAT {
         long long locale_offset = locale_offset_data.ToNumber<4>(0);
 
         if (locale_offset == 0 || locale_offset + 8 >= dat->GetIO().GetActualDatSize().value) {
-            LOG(INFO) << "Dictionary offset is empty or incorrect. Passing.";
-            return DatOperationResult<>();
+            if (CheckLocaleCorrect()) {
+                LOG(INFO) << "Dictionary offset is empty or incorrect. Passing.";
+                return DatOperationResult<>();
+            } else {
+                return DatOperationResult<>(ERROR,
+                                            "Locale dict is incorrect, through patched mark is standing. Dat file may be corrupted");
+            }
         }
 
         BinaryData locale_info(12);
@@ -68,10 +73,11 @@ namespace LOTRO_DAT {
             if (CheckLocaleCorrect())
                 return DatOperationResult<>(SUCCESS);
             else
-                return DatOperationResult<>(ERROR, "Version of locales' dictionary is incorrect, through patched mark is standing. Dat file may be corrupted");
+                return DatOperationResult<>(ERROR,
+                                            "Version of locales' dictionary is incorrect, through patched mark is standing. Dat file may be corrupted");
         }
 
-        BinaryData dicts_data = BinaryData((unsigned)dict_size);
+        BinaryData dicts_data = BinaryData((unsigned) dict_size);
         dat->GetIO().ReadData(dicts_data, dict_size - 12, locale_offset + 12);
 
         if (dicts_data.size() < 15) {
@@ -83,7 +89,8 @@ namespace LOTRO_DAT {
         std::string hi = std::string((char *) (hi_data.data()));
 
         if (hi != "Hi from Gi1dor!")
-            return DatOperationResult<>(ERROR, "INITLOCALE: Data in locales' dictionary is incorrect (couldn't receive 'Hello').");
+            return DatOperationResult<>(ERROR,
+                                        "INITLOCALE: Data in locales' dictionary is incorrect (couldn't receive 'Hello').");
 
         int offset = 15;
         BinaryData current_locale_data = dicts_data.CutData(offset, offset + 4) + BinaryData("\0", 1);
@@ -91,7 +98,8 @@ namespace LOTRO_DAT {
         offset += 4;
 
         if (locale != "PATC" && locale != "ORIG")
-            return DatOperationResult<>(ERROR, "INITLOCALE: Data in locales' dictionary is incorrect (current locale mark is invalid).");
+            return DatOperationResult<>(ERROR,
+                                        "INITLOCALE: Data in locales' dictionary is incorrect (current locale mark is invalid).");
 
         current_locale_ = (locale == "PATC" ? PATCHED : ORIGINAL);
 
@@ -118,8 +126,9 @@ namespace LOTRO_DAT {
         LOG(INFO) << "Finished initialising locales";
 
         if (CheckLocaleCorrect()) {
-            dat->GetIO().file_size = locale_info.ToNumber<4>(8);
-            LOG(INFO) << "Locales initialisation success. Dictionary size is " << dict_size << ". Version is " << dict_version << ". Localed .dat size = " << dat->GetIO().file_size;
+            dat->GetFileSystem().patched_file_end = locale_info.ToNumber<4>(8);
+            LOG(INFO) << "Locales initialisation success. Dictionary size is " << dict_size << ". Version is "
+                      << dict_version << ". Localed .dat size = " << dat->GetFileSystem().patched_file_end;
             return DatOperationResult<>(SUCCESS);
         } else {
             orig_dict_.clear();
@@ -131,7 +140,6 @@ namespace LOTRO_DAT {
         }
     }
 
-
     /*!
      * \Author Gi1dor
      * \date 06.07.2018
@@ -145,8 +153,8 @@ namespace LOTRO_DAT {
             dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
 
         dat->GetStatusModule().SetDebugMessage("Changing locale to " +
-                                                       (locale == PATCHED ? std::string(" patched version")
-                                                                          : std::string(" original version")));
+                                               (locale == PATCHED ? std::string(" patched version")
+                                                                  : std::string(" original version")));
 
         LOG(INFO) << "Setting locale to " << (locale == PATCHED ? " PATCHED" : " ORIGINAL");
         if (!dat)
@@ -161,7 +169,7 @@ namespace LOTRO_DAT {
             return DatOperationResult<>(SUCCESS);
         }
 
-        std::map<long long, SubFile>& dict = GetLocaleDictReference(locale);
+        std::map<long long, SubFile> &dict = GetLocaleDictReference(locale);
         for (const auto &file : dict) {
             long long file_id = file.first;
 
@@ -203,31 +211,6 @@ namespace LOTRO_DAT {
      * \Author Gi1dor
      * \date 06.07.2018
      * Деинициализация модуля. Должна происходить перед деинициализацией модулей ввода-вывода и файловой системы.
-     * Записывает словарь. Если ранее словарь не существовал - новый будет записан в конец файла.
-     * Для словаря выделяется блок не менее 20мб
-     *
-     * \warning Не должна вызываться вручную! Автоматически вызывается в функции Deinitialise класса DatFile
-     *
-     * Структура словарей локализации:
-     * ======== LOCALE DICT STRUCTURE =========
-     * 4                                bytes for dict size (in bytes)
-     * 4                                bytes for locale version
-     * 4                                bytes for .dat file size (with patches)
-     * 15                               bytes for "Hi from Gi1dor"
-     * 4                                bytes for LOCALE
-     * 4                                bytes for orig_dict.size()
-     * (32 + 4) * orig_dict.size()      bytes for orig_dict data
-     * 4                                bytes for patch_dict.size()
-     * (32 + 4) * patch_dict.size()     bytes for patch_dict data
-     * 4                                bytes for inactive_categories dict
-     * 4 * inactive_categories.size()   bytes for inactive_categories data
-     * ========================================
-     * Помимо этого:
-     * 0x128-0x12C - 0, если выбрана локаль ORIGINAL и обновление клиентом игры не испортит .dat file
-     *               значение переменной-размера .dat файла (оффсет 0x148), в противном случае.
-     *               Отличие значения в 0x128 от 0 и значения в 0x148 => файл ресурсов мог быть повреждён
-     *
-     * 0x12C-0x130 - Офсет начала словаря локализации
      */
 
     DatOperationResult<> DatLocaleManager::DeInit() {
@@ -235,103 +218,7 @@ namespace LOTRO_DAT {
         if (!dat)
             return DatOperationResult<>(ERROR, "LOCALEDEINIT: no connection with Dat (dat is nullptr)");
 
-        if (patch_dict_.empty()) {
-            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
-            return DatOperationResult<>(SUCCESS);
-        }
-
-        BinaryData binary_data = BinaryData(4 + 4 + 4 + 14 + 15 + 4
-                                            + 4 + (32 + 4) * orig_dict_.size()
-                                            + 4 + (32 + 4) * patch_dict_.size()
-                                            + 4 + 4 * inactive_categories.size());
-
-        // First 12 bytes will be filled just before writing data to file
-        size_t current_size = 12;
-        
-        binary_data.Append(BinaryData("Hi from Gi1dor!", 15), current_size);
-        current_size += 15;
-
-        binary_data.Append(BinaryData((current_locale_ == ORIGINAL ? "ORIG" : "PATC"), 4), current_size);
-        current_size += 4;
-
-        binary_data.Append(BinaryData::FromNumber<4>(orig_dict_.size()), current_size);
-        current_size += 4;
-
-        for (const auto &file : orig_dict_) {
-            binary_data.Append(file.second.MakeHeaderData(), current_size);
-            current_size += 32;
-            binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size);
-            current_size += 4;
-        }
-
-        binary_data.Append(BinaryData::FromNumber<4>(patch_dict_.size()), current_size);
-        current_size += 4;
-
-        for (const auto &file : patch_dict_) {
-            binary_data.Append(file.second.MakeHeaderData(), current_size);
-            current_size += 32;
-            binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size);
-            current_size += 4;
-        }
-
-        binary_data.Append(BinaryData::FromNumber<4>(inactive_categories.size()), current_size);
-        current_size += 4;
-        for (auto patch_id : inactive_categories) {
-            binary_data.Append(BinaryData::FromNumber<4>(patch_id), current_size);
-            current_size += 4;
-        }
-
-
-        BinaryData dicts_data(4);
-        dat->GetIO().ReadData(dicts_data, 4, 300);
-        long long dict_offset = dicts_data.ToNumber<4>(0);
-        dat->GetIO().ReadData(dicts_data, 4, dict_offset);
-        long long dict_size = dicts_data.ToNumber<4>(0);
-
-        if (binary_data.size() > dict_size || dict_offset == 0) {
-            long long new_dict_offset = dat->GetIO().file_size + 12;
-
-            // Updating first 12 bytes
-            binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)), 0);
-            binary_data.Append(BinaryData::FromNumber<4>(101), 4);
-            binary_data.Append(BinaryData::FromNumber<4>(dat->GetIO().file_size + binary_data.size() + 20 * 1024 * 1024), 8);
-
-            auto operation = dat->GetIO().WriteData(binary_data, binary_data.size(), new_dict_offset);
-            if (operation.result != SUCCESS)
-                return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales");
-
-            dat->GetIO().WriteData(BinaryData::FromNumber<4>(new_dict_offset), 4, 300);
-
-            if (current_locale_ == PATCHED) {
-                BinaryData file_size_flag(4);
-                dat->GetIO().ReadData(file_size_flag, 4, 0x148);
-                dat->GetIO().WriteData(file_size_flag, 4, 296);
-            } else {
-                dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
-            }
-
-            dat->GetIO().file_size += binary_data.size();
-
-            // Adding space for 20 megabytes locales file in total.
-            BinaryData nulls(unsigned(20 * 1024 * 1024));
-            dat->GetIO().WriteData(nulls, nulls.size(), dat->GetIO().file_size);
-            dat->GetIO().file_size += nulls.size();
-        } else {
-            binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)), 0);
-            binary_data.Append(BinaryData::FromNumber<4>(101), 4);
-            binary_data.Append(BinaryData::FromNumber<4>(dat->GetIO().file_size), 8);
-
-            dat->GetIO().WriteData(BinaryData::FromNumber<4>(current_locale_), 4, 296, 0);
-            auto operation = dat->GetIO().WriteData(binary_data, binary_data.size(), dict_offset);
-
-            if (operation.result != SUCCESS)
-                return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales. ERRMSG: " + operation.msg);
-        }
-
-        LOG(INFO) << "Locales committed successfully";
-
-
-        return DatOperationResult<>(SUCCESS);
+        return CommitLocales();
     }
 
 
@@ -344,7 +231,7 @@ namespace LOTRO_DAT {
      */
 
     void DatLocaleManager::UpdateLocaleFile(DatLocaleManager::LOCALE locale, const SubFile &file) {
-        std::map<long long, SubFile>& dict = GetLocaleDictReference(locale);
+        std::map<long long, SubFile> &dict = GetLocaleDictReference(locale);
         dict[file.file_id()] = file;
     }
 
@@ -357,9 +244,11 @@ namespace LOTRO_DAT {
      */
 
     DatOperationResult<SubFile> DatLocaleManager::GetLocaleFile(long long file_id, DatLocaleManager::LOCALE locale) {
-        std::map<long long, SubFile>& dict = GetLocaleDictReference(locale);
+        std::map<long long, SubFile> &dict = GetLocaleDictReference(locale);
         if (dict.count(file_id) == 0)
-            return DatOperationResult<SubFile>(SubFile(), ERROR, "GETLOCFILE: cannot get file with id = " + std::to_string(file_id) + " from dict " + std::to_string(locale));
+            return DatOperationResult<SubFile>(SubFile(), ERROR,
+                                               "GETLOCFILE: cannot get file with id = " + std::to_string(file_id) +
+                                               " from dict " + std::to_string(locale));
         return DatOperationResult<SubFile>(dict[file_id], SUCCESS);
     }
 
@@ -406,8 +295,6 @@ namespace LOTRO_DAT {
             long long dict_version = locale_info.ToNumber<4>(4);
 
             fprintf(file, "Locales' dictionary size = %lld, version = %lld\n", dict_size, dict_version);
-
-            dat->GetIO().file_size = locale_info.ToNumber<4>(8);
         }
 
         fprintf(file, "Current locale id = %d\n", current_locale_);
@@ -435,15 +322,15 @@ namespace LOTRO_DAT {
         long long locale_offset = locale_offset_data.ToNumber<4>(0);
 
         if (locale_offset == 0 || locale_offset + 8 >= dat->GetIO().GetActualDatSize().value)
-            return locale_status == ORIGINAL;
+            return locale_status == 0;
 
         BinaryData dicts_data = BinaryData(4);
         auto operation = dat->GetIO().ReadData(dicts_data, 4, locale_offset + 12 + 15);
         if (operation.result == ERROR)
-            return locale_status == ORIGINAL;
+            return locale_status == 0;
 
         BinaryData locale_data = dicts_data + BinaryData("\0", 1);
-        std::string locale((char *)(locale_data.data()));
+        std::string locale((char *) (locale_data.data()));
 
         LOCALE dat_locale = (locale == "PATC" ? PATCHED : ORIGINAL);
 
@@ -452,12 +339,8 @@ namespace LOTRO_DAT {
             return false;
         }
 
-        BinaryData original_filesize_variable(4);
-        dat->GetIO().ReadData(original_filesize_variable, 4, 0x148);
-        long long original_filesize = original_filesize_variable.ToNumber<4>(0);
-
-        if (locale_status != original_filesize) {
-            LOG(ERROR) << "CHCKLOCALECORRECT: locale & file size values doesn't match!";
+        if (locale_status != 0 &&  locale_status != dat->GetIO().getHeaderHash()){
+            LOG(ERROR) << "CHCKLOCALECORRECT: Locale hash doesn't match!";
             return false;
         }
 
@@ -499,4 +382,136 @@ namespace LOTRO_DAT {
     const std::set<long long> &DatLocaleManager::GetInactiveCategories() {
         return inactive_categories;
     }
+
+    /*!
+     * \Author Gi1dor
+     * \date 21.10.2018
+     * Записывает словарь. Если ранее словарь не существовал - новый будет записан в конец файла.
+     * Для словаря выделяется блок не менее 20мб
+     *
+     * \warning Не должна вызываться вручную! Автоматически вызывается в функции Deinitialise класса DatFile
+     *
+     * Структура словарей локализации:
+     * ======== LOCALE DICT STRUCTURE =========
+     * 4                                bytes for dict size (in bytes)
+     * 4                                bytes for locale version
+     * 4                                bytes for .dat file size (with patches)
+     * 15                               bytes for "Hi from Gi1dor"
+     * 4                                bytes for LOCALE
+     * 4                                bytes for orig_dict.size()
+     * (32 + 4) * orig_dict.size()      bytes for orig_dict data
+     * 4                                bytes for patch_dict.size()
+     * (32 + 4) * patch_dict.size()     bytes for patch_dict data
+     * 4                                bytes for inactive_categories dict
+     * 4 * inactive_categories.size()   bytes for inactive_categories data
+     * ========================================
+     * Помимо этого:
+     * 0x128-0x12C - 0, если выбрана локаль ORIGINAL и обновление клиентом игры не испортит .dat file
+     *               значение переменной-размера .dat файла (оффсет 0x148), в противном случае.
+     *               Отличие значения в 0x128 от 0 и значения в 0x148 => файл ресурсов мог быть повреждён
+     *
+     * 0x12C-0x130 - Офсет начала словаря локализации
+     */
+
+    DatOperationResult<> DatLocaleManager::CommitLocales() {
+        if (patch_dict_.empty()) {
+            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
+            return DatOperationResult<>(SUCCESS);
+        }
+
+        BinaryData binary_data = BinaryData(4 + 4 + 4 + 14 + 15 + 4
+                                            + 4 + (32 + 4) * orig_dict_.size()
+                                            + 4 + (32 + 4) * patch_dict_.size()
+                                            + 4 + 4 * inactive_categories.size());
+
+        // First 12 bytes will be filled just before writing data to file
+        size_t current_size = 12;
+
+        binary_data.Append(BinaryData("Hi from Gi1dor!", 15), current_size);
+        current_size += 15;
+
+        binary_data.Append(BinaryData((current_locale_ == ORIGINAL ? "ORIG" : "PATC"), 4), current_size);
+        current_size += 4;
+
+        binary_data.Append(BinaryData::FromNumber<4>(orig_dict_.size()), current_size);
+        current_size += 4;
+
+        for (const auto &file : orig_dict_) {
+            binary_data.Append(file.second.MakeHeaderData(), current_size);
+            current_size += 32;
+            binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size);
+            current_size += 4;
+        }
+
+        binary_data.Append(BinaryData::FromNumber<4>(patch_dict_.size()), current_size);
+        current_size += 4;
+
+        for (const auto &file : patch_dict_) {
+            binary_data.Append(file.second.MakeHeaderData(), current_size);
+            current_size += 32;
+            binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size);
+            current_size += 4;
+        }
+
+        binary_data.Append(BinaryData::FromNumber<4>(inactive_categories.size()), current_size);
+        current_size += 4;
+        for (auto patch_id : inactive_categories) {
+            binary_data.Append(BinaryData::FromNumber<4>(patch_id), current_size);
+            current_size += 4;
+        }
+
+
+        BinaryData dicts_data(4);
+        dat->GetIO().ReadData(dicts_data, 4, 300);
+        long long dict_offset = dicts_data.ToNumber<4>(0);
+        dat->GetIO().ReadData(dicts_data, 4, dict_offset);
+        long long dict_size = dicts_data.ToNumber<4>(0);
+
+        if (binary_data.size() > dict_size || dict_offset == 0) {
+            long long new_dict_offset = dat->GetFileSystem().patched_file_end + 12;
+
+            // Updating first 12 bytes
+            binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)), 0);
+            binary_data.Append(BinaryData::FromNumber<4>(101), 4);
+            binary_data.Append(
+                    BinaryData::FromNumber<4>(dat->GetFileSystem().patched_file_end + binary_data.size() + 20 * 1024 * 1024), 8);
+
+            auto operation = dat->GetIO().WriteData(binary_data, binary_data.size(), new_dict_offset);
+            if (operation.result != SUCCESS)
+                return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales");
+
+            dat->GetIO().WriteData(BinaryData::FromNumber<4>(new_dict_offset), 4, 300);
+
+            if (current_locale_ == PATCHED) {
+                dat->GetIO().WriteData(BinaryData::FromNumber<4>(dat->GetIO().getHeaderHash()), 4, 296);
+            } else {
+                dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
+            }
+            dat->GetFileSystem().patched_file_end += binary_data.size();
+
+            LOG(INFO) << "Writing 20 mbytes to " << dat->GetFileSystem().patched_file_end;
+
+            BinaryData nulls(unsigned(20 * 1024 * 1024));
+            dat->GetIO().WriteData(nulls, nulls.size(), dat->GetFileSystem().patched_file_end);
+            dat->GetFileSystem().patched_file_end += nulls.size();
+        } else {
+            binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)), 0);
+            binary_data.Append(BinaryData::FromNumber<4>(101), 4);
+            binary_data.Append(BinaryData::FromNumber<4>(dat->GetFileSystem().patched_file_end), 8);
+
+            if (current_locale_ == PATCHED) {
+                dat->GetIO().WriteData(BinaryData::FromNumber<4>(dat->GetIO().getHeaderHash()), 4, 296);
+            } else {
+                dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
+            }
+
+            auto operation = dat->GetIO().WriteData(binary_data, binary_data.size(), dict_offset);
+
+            if (operation.result != SUCCESS)
+                return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales. ERRMSG: " + operation.msg);
+        }
+
+        LOG(INFO) << "Locales committed successfully";
+        return DatOperationResult<>(SUCCESS);
+    }
 }

+ 2 - 2
src/DatSubsystems/DatPatcher.cpp

@@ -149,9 +149,9 @@ namespace LOTRO_DAT {
         if (dat->GetLocaleManager().GetLocaleFile(file_id, DatLocaleManager::PATCHED).result == ERROR
             || data.size() > file->block_size()) {
 
-            new_file.file_offset_ = dat->GetIO().file_size;
+            new_file.file_offset_ = dat->GetFileSystem().patched_file_end;
             new_file.block_size_ = std::max(data.size(), 256u);
-            dat->GetIO().file_size += new_file.block_size_ + 8;
+            dat->GetFileSystem().patched_file_end += new_file.block_size_ + 8;
         }
         new_file.file_size_ = data.size() - 8;
 

+ 1 - 1
src/Examples/extractor_example.cpp

@@ -32,7 +32,7 @@ bool exportUnknownToDb = false;
 // There is no need to change anything else
 
 int main() {
-    std::cout << "Gi1dor's LotRO .dat extractor ver. 7.0.0" << std::endl;
+    std::cout << "Gi1dor's LotRO .dat extractor ver. 7.1.0" << std::endl;
 
     std::cout << "Hello! I'm a basic shell version of .dat file extractor. I can open .dat file directly, "
             "if you write path to it (with name of file) in file \"dat_file_path.txt\"\n";

+ 1 - 1
src/Examples/info_gatherer.cpp

@@ -11,7 +11,7 @@ using namespace LOTRO_DAT;
 #include <ctime>
 
 int main() {
-    std::cout << "Gi1dor's LotRO .dat patcher ver. 6.0.0" << std::endl;
+    std::cout << "Gi1dor's LotRO .dat patcher ver. 7.1.0" << std::endl;
     freopen("patcher_errors.log", "w", stderr);
 
     setbuf(stdout, nullptr);

+ 31 - 31
src/Examples/patcher_example.cpp

@@ -15,7 +15,7 @@ using namespace LOTRO_DAT;
 using namespace std;
 
 int main() {
-    std::cout << "Gi1dor's LotRO .dat patcher ver. 6.0.0" << std::endl;
+    std::cout << "Gi1dor's LotRO .dat patcher ver. 7.1.0" << std::endl;
     freopen("patcher_errors.log", "w", stderr);
 
     setbuf(stdout, nullptr);
@@ -61,7 +61,7 @@ int main() {
                 "print disabled categories (enter 6), create backup (enter 7), remove backup (enter 8), "
                 "restore .dat file from backup (enter 9), check if backup exists (enter 10) or exit (enter -1)\n";
 
-        std::cout << "\n=================\nOptions 4,5,6,7,8,9,10 are currently unavailable\n";
+        std::cout << "\n=================\nOptions 4,5 are currently unavailable\n";
         int cmd = 0;
         std::cout << "Enter number of command (1-10): ";
         std::cin >> cmd;
@@ -141,7 +141,7 @@ int main() {
         if (cmd == 3) {
             std::cout << "Current locale is " << (file.GetLocaleManager().GetCurrentLocale() == DatLocaleManager::PATCHED ? "RU" : "Original") << endl;
         }
-//
+
 //        if (cmd == 4) {
 //            int category_id = 0;
 //            std::cout << "Enter category id: ";
@@ -149,40 +149,40 @@ int main() {
 //            file.EnableCategory(category_id);
 //            std::cout << "Category successfully enabled!" << std::endl;
 //        }
-//
+
 //        if (cmd == 5) {
 //            int category_id = 0;
 //            std::cout << "Enter category id: ";
 //            std::cin >> category_id;
-//            file.DisableCategory(category_id);
+//            file.GetLocaleManager().Ca DisableCategory(category_id);
 //            std::cout << "Category successfully disabled!" << std::endl;
 //        }
-//
-//        if (cmd == 6) {
-//            std::cout << "Disabled categories: ";
-//            for (auto i : file.GetInactiveCategoriesList())
-//                std::cout << i << " ";
-//            std::cout << endl;
-//        }
-//
-//        if (cmd == 7) {
-//            std::cout << "Creating backup..." << std::endl;
-//            std::cout << "Create backup function returned " << file.CreateBackup("cli_local_En.backup") << std::endl;
-//        }
-//
-//        if (cmd == 8) {
-//            std::cout << "Removing backup..." << std::endl;
-//            std::cout << "Remove backup function returned " << file.RemoveBackup("cli_local_En.backup") << std::endl;
-//        }
-//
-//        if (cmd == 9) {
-//            std::cout << "Restoring file from backup..." << std::endl;
-//            std::cout << "Restore file function returned " << file.RestoreFromBackup("cli_local_En.backup") << std::endl;
-//        }
-//
-//        if (cmd == 10) {
-//            std::cout << "Backup file " << (file.CheckIfBackupAvailable("clie_local_En.backup") ? "exists!" : "doesn't exist.") << std::endl;
-//        }
+
+        if (cmd == 6) {
+            std::cout << "Disabled categories: ";
+            for (auto i : file.GetLocaleManager().GetInactiveCategories())
+                std::cout << i << " ";
+            std::cout << endl;
+        }
+
+        if (cmd == 7) {
+            std::cout << "Creating backup..." << std::endl;
+            std::cout << "Create backup function returned " << file.GetBackupManager().CreateBackup("cli_local_En.backup").result << std::endl;
+        }
+
+        if (cmd == 8) {
+            std::cout << "Removing backup..." << std::endl;
+            std::cout << "Remove backup function returned " << file.GetBackupManager().RemoveBackup("cli_local_En.backup").result << std::endl;
+        }
+
+        if (cmd == 9) {
+            std::cout << "Restoring file from backup..." << std::endl;
+            std::cout << "Restore file function returned " << file.GetBackupManager().RestoreFromBackup("cli_local_En.backup").result << std::endl;
+        }
+
+        if (cmd == 10) {
+            std::cout << "Backup file " << (file.GetBackupManager().CheckIfBackupAvailable("clie_local_En.backup") ? "exists!" : "doesn't exist.") << std::endl;
+        }
     }
     //system("pause");
     return 0;