Browse Source

Merge branch 'dev' of LotRO_Legacy/LotroDat into master

Ivan Arkhipov 4 years ago
parent
commit
62eb05b61a

+ 5 - 1
CHANGELOG

@@ -75,4 +75,8 @@ Version 7.2.1
     * Bug fixes (Text files used incorrect fragments during import, if there was no fragment in patch with specified id)
 ----------------------------------------------------------------------
 Version 7.2.2
-    * Small fixes, added one more protection layer: locales dictionary now contains hash of header at the moment of last patch applying. So that system can easily detect if file was modified and there's a need to update patches.
+    * Small fixes, added one more protection layer: locales dictionary now contains hash of header at the moment of last patch applying. So that system can easily detect if file was modified and there's a need to update patches.
+----------------------------------------------------------------------
+Version 8.0.0
+    * Made locales initialization and patching process more stable
+    * Minor code improvements in binary examples (patcher, extractor)   

+ 2 - 2
CMakeLists.txt

@@ -3,8 +3,8 @@ project(LotroDat)
 
 set(CMAKE_CXX_STANDARD 14)
 set(PROJECT_BINARY_DIR bin)
-set(PROJECT_VERSION 7.2.2)
-
+set(PROJECT_VERSION 8.0.0)
+configure_file(${CMAKE_SOURCE_DIR}/LotroDat.h.tmpl ${CMAKE_SOURCE_DIR}/include/LotroDat.h)
 SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS} -Wall -Wextra -O2")
 SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
 

+ 17 - 0
LotroDat.h.tmpl

@@ -0,0 +1,17 @@
+#ifndef LOTRO_DAT_LIB
+#define LOTRO_DAT_LIB
+
+#define LOTRO_DAT_VERSION  "@PROJECT_VERSION@"
+#define LOTRO_DAT_VERSION_MAJOR "@PROJECT_VERSION_MAJOR@"
+#define LOTRO_DAT_VERSION_MINOR "@PROJECT_VERSION_MINOR@"
+#define LOTRO_DAT_VERSION_PATCH "@PROJECT_VERSION_PATCH@"
+
+#include "DatFile.h"
+#include "Database.h"
+#include "SubfileData.h"
+#include "DatOperationResult.h"
+
+#include <yaml-cpp/yaml.h>
+#include <ZLib/zlib.h>
+
+#endif /* LOTRO_DAT_LIB */

+ 1 - 1
include/DatSubsystems/DatFileSystem.h

@@ -84,7 +84,7 @@ namespace LOTRO_DAT {
         std::unordered_map<long long, SubFile> subfile_init_map_;
         std::set<SubFile, SubFile::SubFileOffsetComparator> subfile_init_queue_;
     public:
-        long long patched_file_end;
+        unsigned patched_file_end;
     };
 }
 };

+ 2 - 0
include/DatSubsystems/DatLocaleManager.h

@@ -84,6 +84,8 @@ namespace LOTRO_DAT {
         std::map<long long, SubFile> patch_dict_;
         std::set<long long> inactive_categories;
         LOCALE current_locale_;
+        uint64_t dict_offset_ = 0;
+        uint64_t dict_size_ = 0;
     };
 }
 };

+ 9 - 5
include/LotroDat.h

@@ -1,8 +1,10 @@
-//
-// Created by Иван_Архипов on 01.11.2017.
-//
+#ifndef LOTRO_DAT_LIB
+#define LOTRO_DAT_LIB
 
-#define LOTRO_DAT_VERSION "7.3.0"
+#define LOTRO_DAT_VERSION  "8.0.0"
+#define LOTRO_DAT_VERSION_MAJOR ""
+#define LOTRO_DAT_VERSION_MINOR ""
+#define LOTRO_DAT_VERSION_PATCH ""
 
 #include "DatFile.h"
 #include "Database.h"
@@ -10,4 +12,6 @@
 #include "DatOperationResult.h"
 
 #include <yaml-cpp/yaml.h>
-#include <ZLib/zlib.h>
+#include <ZLib/zlib.h>
+
+#endif /* LOTRO_DAT_LIB */

+ 5 - 5
src/DatFile.cpp

@@ -3,6 +3,7 @@
 //
 #define NOMINMAX
 
+#include <LotroDat.h>
 #include <DatFile.h>
 #include <DatOperationResult.h>
 #include <EasyLogging++/easylogging++.h>
@@ -12,8 +13,6 @@
 #define ftell _ftelli64
 #endif
 
-#define VERSION "7.1.0"
-
 INITIALIZE_EASYLOGGINGPP
 
 extern "C++"
@@ -127,6 +126,7 @@ namespace LOTRO_DAT {
         initialized_ = true;
 
         status_->SetDefaultStatus();
+        LOG(INFO) << "DATINIT: All initialization processes finished successfully";
         return DatOperationResult<>();
     }
 
@@ -139,11 +139,11 @@ namespace LOTRO_DAT {
 
         fprintf(out, "########################################################################################\n"
                      "# LOTRO Dat library version: %8s                                                  #\n"
-                     "# Author: Gi1dor (e1.gildor@gmail.com)                                                 #\n"
+                     "# Author: Endevir (aka Gi1dor) (me@endevir.ru)                                         #\n"
                      "# Is part of LOTRO Legacy project (http://translate.lotros.ru/)                        #\n"
-                     "# Last version is available on http://git.gi1dor.ru/LotRO_Legacy/Universal_dat_library #\n"
+                     "# Last version is available on http://git.endevir.ru/LotRO_Legacy/LotroDat             #\n"
                      "########################################################################################\n\n\n",
-                VERSION);
+                LOTRO_DAT_VERSION);
 
         io_->PrintInformaion(out);
         fileSystem_->PrintInformaion(out);

+ 1 - 1
src/DatSubsystems/DatFileSystem.cpp

@@ -257,7 +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);
+        fprintf(file, "File patched size: %u\n", patched_file_end);
 
         std::map<FILE_TYPE, size_t> filetypes_count{{TEXT, 0},
                                                     {JPG, 0},

+ 136 - 142
src/DatSubsystems/DatLocaleManager.cpp

@@ -35,91 +35,82 @@ namespace LOTRO_DAT {
      * 4 * inactive_categories.size()   bytes for inactive_categories data
      * ========================================
      * Помимо этого:
-     * 0x128-0x12C - Флаг, установлена ли альтернативная версия (flag)
+     * 0x128-0x12C - 0, если .dat файл не был изменен библиотекой, обновление клиентом игры не испортит .dat file
+     *               1, если активна локаль ORIGINAL, обновление клиентом игры не испортит .dat file
+     *               2, если активна локаль PATCHED, обновление клиентом игры может испортить .dat file
+     *
      * 0x12C-0x130 - Офсет начала словаря локализации (offset)
      *
-     * \warning Если flag != 0 и offset == 0 - то dat файл считается повреждённым. Проверка осуществляется функцией CheckLocaleCorrectж
+     * \warning Если flag != 0 и offset == 0 - то dat файл считается повреждённым. Проверка осуществляется функцией CheckLocaleCorrect
      */
 
     DatOperationResult<> DatLocaleManager::Init() {
         if (!dat)
             return DatOperationResult<>(ERROR, "LOCALEINIT: no connection with Dat (dat is nullptr)");
 
-        orig_dict_.clear();
-        patch_dict_.clear();
-        inactive_categories.clear();
-        current_locale_ = ORIGINAL;
+        ClearData();
 
         LOG(INFO) << "Initialising locales...";
-        BinaryData locale_offset_data(4);
-        dat->GetIO().ReadData(locale_offset_data, 4, 300);
-        long long locale_offset = locale_offset_data.ToNumber<4>(0);
-
-        if (locale_offset == 0 || locale_offset + 8 >= dat->GetIO().GetActualDatSize().value) {
-            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");
-            }
+        
+        if (!CheckLocaleCorrect()) {
+            return DatOperationResult<>(ERROR, "LOCALEINIT: Check locale correct failed!");
         }
 
+        BinaryData locale_dict_offset_data(4);
+        dat->GetIO().ReadData(locale_dict_offset_data, 4, 0x12C);
+        dict_offset_ = locale_dict_offset_data.ToNumber<4>(0);
+
         BinaryData locale_info(16);
-        dat->GetIO().ReadData(locale_info, 16, locale_offset);
+        auto operation = dat->GetIO().ReadData(locale_info, 16, dict_offset_);
+        if (!operation.result) {
+            ClearData();
+            LOG(INFO) << "LOCALEINIT: Locale offset is incorrect, skipping initialization.";
+            return DatOperationResult<>(SUCCESS);
+        }
 
-        long long dict_size = locale_info.ToNumber<4>(0);
-        long long dict_version = locale_info.ToNumber<4>(4);
-        long long patched_file_end = locale_info.ToNumber<4>(8);
-        long long dat_file_header_hash = locale_info.ToNumber<4>(12);
+        dict_size_ = locale_info.ToNumber<4>(0);
+        unsigned dict_version = locale_info.ToNumber<4>(4);
+        unsigned patched_file_end = locale_info.ToNumber<4>(8);
+        unsigned dat_file_header_hash = locale_info.ToNumber<4>(12);
 
         if (dict_version != DAT_LOCALE_DICT_VERSION) {
-            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
-            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");
+            ClearData();
+            LOG(WARNING) << "LOCALEINIT: Locale dictionary version is outdated, skipping initialization.";
+            return DatOperationResult<>(SUCCESS);
         }
 
         if (dat_file_header_hash != dat->GetIO().GetHeaderHash()) {
-            LOG(INFO) << "Header hash, written after last patch apply doesn't match current hash. Seems like dat-file was modified somewhere else, so removing all patch data";
-            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
-            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");
+            ClearData();
+            LOG(WARNING) << "LOCALEINIT: Locale header hash does not match real dat file header hash, skipping initialization.";
+            return DatOperationResult<>(SUCCESS);
         }
 
-        BinaryData dicts_data = BinaryData((unsigned) dict_size);
-        dat->GetIO().ReadData(dicts_data, dict_size - 16, locale_offset + 16);
-
-        if (dicts_data.size() < 15) {
-            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
-            return DatOperationResult<>(ERROR, "INITLOCALE: Data in locales' dictionary is incorrect.");
-        }
+        BinaryData dicts_data = BinaryData((unsigned)dict_size_);
+        dat->GetIO().ReadData(dicts_data, dict_size_ - 16, dict_offset_ + 16);
 
         BinaryData hi_data = dicts_data.CutData(0, 15) + BinaryData("\0", 1);
         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').");
+        if (hi != "Hi from Gi1dor!") {
+            ClearData();
+            LOG(WARNING) << "LOCALEINIT: couldn't receive Hello, skipping initialization.";
+            return DatOperationResult<>(SUCCESS);
+        }
 
         int offset = 15;
         BinaryData current_locale_data = dicts_data.CutData(offset, offset + 4) + BinaryData("\0", 1);
         std::string locale((char *) (current_locale_data.data()));
-        offset += 4;
-
-        if (locale != "PATC" && locale != "ORIG")
-            return DatOperationResult<>(ERROR,
-                                        "INITLOCALE: Data in locales' dictionary is incorrect (current locale mark is invalid).");
+        if (locale != "PATC" && locale != "ORIG") {
+            ClearData();
+            LOG(WARNING) << "LOCALEINIT: locale status in dict seems incorrect, skipping initialization.";
+            return DatOperationResult<>(SUCCESS);
+        }
 
         current_locale_ = (locale == "PATC" ? PATCHED : ORIGINAL);
-
+        offset += 4;
+        
         size_t orig_dict_size = size_t(dicts_data.CutData(offset, offset + 4).ToNumber<4>(0));
         offset += 4;
+
         for (size_t i = 0; i < orig_dict_size; i++) {
             auto file = SubFile(*dat, dicts_data.CutData(offset, offset + 32));
             file.category = dicts_data.ToNumber<4>(offset + 32);
@@ -147,21 +138,12 @@ namespace LOTRO_DAT {
         LOG(INFO) << "There are " << patch_dict_.size() << " files in patch locale dictionary";
         LOG(INFO) << "There are " << orig_dict_.size() << " files in original locale dictionary";
         LOG(INFO) << "There are " << inactive_categories.size() << " categories inactive: ";
-        LOG(INFO) << "Finished initialising locales";
+        LOG(INFO) << "Successfully finished initialising locales!";
 
-        if (CheckLocaleCorrect()) {
-            dat->GetFileSystem().patched_file_end = patched_file_end;
-            LOG(INFO) << "Locales initialisation success. Dictionary size is " << dict_size << ". Version is "
-                      << dict_version << ". Localed .dat size = " << patched_file_end;
-            return DatOperationResult<>(SUCCESS);
-        } else {
-            orig_dict_.clear();
-            patch_dict_.clear();
-            inactive_categories.clear();
-            current_locale_ = ORIGINAL;
-            return DatOperationResult<>(ERROR,
-                                        "Locale dict is incorrect, through patched mark is standing. Dat file may be corrupted");
-        }
+        dat->GetFileSystem().patched_file_end = patched_file_end;
+        LOG(INFO) << "Locales initialisation success. Dictionary size is " << dict_size_ << ". Version is "
+                  << dict_version << ". Localed .dat size = " << patched_file_end;
+        return DatOperationResult<>(SUCCESS);
     }
 
     /*!
@@ -173,16 +155,15 @@ namespace LOTRO_DAT {
      */
 
     DatOperationResult<> DatLocaleManager::SetLocale(DatLocaleManager::LOCALE locale) {
-        if (dat->GetStatusModule().GetStatus() == DatStatus::E_FREE)
+        if (dat->GetStatusModule().GetStatus() == DatStatus::E_FREE) {
             dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
+        }
 
         dat->GetStatusModule().SetDebugMessage("Changing locale to " +
                                                (locale == PATCHED ? std::string(" patched version")
                                                                   : std::string(" original version")));
 
         LOG(INFO) << "Setting locale to " << (locale == PATCHED ? " PATCHED" : " ORIGINAL");
-        if (!dat)
-            return DatOperationResult<>(ERROR, "SETLOCALE: no connection with Dat (dat is nullptr)");
 
         if (current_locale_ == locale) {
             LOG(INFO) << "Locale is already " << locale << ", nothing to do.";
@@ -250,9 +231,6 @@ namespace LOTRO_DAT {
 
     DatOperationResult<> DatLocaleManager::DeInit() {
         LOG(INFO) << "Committing locales...";
-        if (!dat)
-            return DatOperationResult<>(ERROR, "LOCALEDEINIT: no connection with Dat (dat is nullptr)");
-
         auto result = CommitLocales();
         ClearData();
         return result;
@@ -343,6 +321,8 @@ namespace LOTRO_DAT {
     /*!
      * \Author Gi1dor
      * \date 06.07.2018
+     * TODO: update descr
+     * 
      * Осуществляет проверку корректности dat файла с позиции локалей
      * Файл считается некорректным, если активной является альтернативная локаль, причём нет возможности вернуть оригинальную (блок локалей неверный/повреждён)
      * Байты 0x128-0x12C равны 0, если на момент пользования патчером активна оригинальная локаль, и DatIO::file_size, если нет
@@ -350,37 +330,80 @@ namespace LOTRO_DAT {
      */
 
     bool DatLocaleManager::CheckLocaleCorrect() {
-        BinaryData locale_info(4);
-        dat->GetIO().ReadData(locale_info, 4, 296);
-        long long locale_status = locale_info.ToNumber<4>(0);
+        BinaryData patch_mark_data(4);
+        dat->GetIO().ReadData(patch_mark_data, 4, 0x128);
+        uint32_t patch_mark = patch_mark_data.ToNumber<4>(0);
 
         BinaryData locale_offset_data(4);
         dat->GetIO().ReadData(locale_offset_data, 4, 300);
         long long locale_offset = locale_offset_data.ToNumber<4>(0);
 
-        if (locale_offset == 0 || locale_offset + 8 >= dat->GetIO().GetActualDatSize().value)
-            return locale_status == 0;
+        LOG(INFO) << "CHCKLOCALECORRECT: patch mark = " << patch_mark << ", locale_offset =  " << locale_offset;
+
+        if (patch_mark == 0 && locale_offset == 0) {
+            LOG(INFO) << "CHCKLOCALECORRECT: No file patch info found, check successfull!";
+            return true;
+        }
 
-        BinaryData dicts_data = BinaryData(4);
-        auto operation = dat->GetIO().ReadData(dicts_data, 4, locale_offset + 16 + 15);
-        if (operation.result == ERROR)
-            return locale_status == 0;
+        if (patch_mark == 0 && locale_offset != 0) {
+            LOG(ERROR) << "CHCKLOCALECORRECT: Locale dict exists, but patch mark is incorrect!";
+            return false;
+        }
 
-        BinaryData locale_data = dicts_data + BinaryData("\0", 1);
-        std::string locale((char *) (locale_data.data()));
+        if (patch_mark != 0 && locale_offset == 0) {
+            LOG(ERROR) << "CHCKLOCALECORRECT: Locale offset is null, but patch mark exists!";
+            return false;
+        }
 
-        LOCALE dat_locale = (locale == "PATC" ? PATCHED : ORIGINAL);
+        if (patch_mark == 1) {
+            LOG(INFO) << "CHCKLOCALECORRECT: Header is fine, patch mark is original, so skipping dat locale dict correctness check";
+            return true;
+        }
 
-        if ((locale_status == 0 && dat_locale == PATCHED) || (locale_status != 0 && dat_locale == ORIGINAL)) {
-            LOG(ERROR) << "CHCKLOCALECORRECT: Locale status and current_locale doesn't match!";
+        if (patch_mark != 2) {
+            LOG(INFO) << "CHCKLOCALECORRECT: Patch mark is invalid! Found " << patch_mark << ", expected: 0, 1 or 2!";
+            return false;
+        }
+
+        // for this moment patch_mark is 2, file should be patched and current locale should be PATCHED
+
+        BinaryData locale_dict_header(16 + 15 + 4);
+        auto operation = dat->GetIO().ReadData(locale_dict_header, 16 + 15 + 4, locale_offset);
+
+        if (!operation.result) {
+            LOG(ERROR) << "CHCKLOCALECORRECT: incorrect locale offset (cannot read data at offset" << locale_offset << ")";
+            return false;
+        }
+
+        long long dict_version = locale_dict_header.ToNumber<4>(4);
+        long long dat_header_hashsum = locale_dict_header.ToNumber<4>(12);
+        BinaryData hi_data = locale_dict_header.CutData(16, 16 + 15) + BinaryData("\0", 1);
+        std::string hi_string = std::string((char *) (hi_data.data()));
+
+        BinaryData current_locale_data = locale_dict_header.CutData(16 + 15, 16 + 15 + 4) + BinaryData("\0", 1);
+        std::string locale_string = std::string((char *) (current_locale_data.data()));
+
+        if (dict_version != DAT_LOCALE_DICT_VERSION) {
+            LOG(ERROR) << "CHCKLOCALECORRECT: Locale dict version is incorrect, through patch mark shows, that file was patched. Found version:" << dict_version << " expected: " << DAT_LOCALE_DICT_VERSION;
             return false;
         }
 
-        if (locale_status != 0 && locale_status != dat->GetIO().GetHeaderHash()){
-            LOG(ERROR) << "CHCKLOCALECORRECT: Locale hash doesn't match!";
+        if (locale_string != "PATC") {
+            LOG(ERROR) << "CHCKLOCALECORRECT: Data in locales' dictionary is incorrect (dictionary locale mark is invalid). Found: " << locale_string << " expected: PATC";
             return false;
         }
 
+        if (hi_string != "Hi from Gi1dor!") {
+            LOG(ERROR) << "CHCKLOCALECORRECT: Data in locales' dictionary is incorrect (couldn't receive 'Hello').";
+            return false;
+        }
+
+        if (dat_header_hashsum != dat->GetIO().GetHeaderHash()) {
+            LOG(ERROR) << "CHCKLOCALECORRECT: Dat header checksum from header does not match dat header checksum from locale dictionary!";
+            return false;
+        }
+
+        LOG(INFO) << "CHCKLOCALECORRECT: All checks passed successfully, no errors found!";
         return true;
     }
 
@@ -444,9 +467,9 @@ namespace LOTRO_DAT {
      * 4 * inactive_categories.size()   bytes for inactive_categories data
      * ========================================
      * Помимо этого:
-     * 0x128-0x12C - 0, если выбрана локаль ORIGINAL и обновление клиентом игры не испортит .dat file
-     *               хэш хедера файла в противном случае.
-     *               Отличие значения в 0x128 от 0 и значения в 0x148 => файл ресурсов мог быть повреждён
+     * 0x128-0x12C - 0, если .dat файл не был изменен библиотекой, обновление клиентом игры не испортит .dat file
+     *               1, если активна локаль ORIGINAL, обновление клиентом игры не испортит .dat file
+     *               2, если активна локаль PATCHED, обновление клиентом игры может испортить .dat file
      *
      * 0x12C-0x130 - Офсет начала словаря локализации
      */
@@ -456,8 +479,6 @@ namespace LOTRO_DAT {
             return DatOperationResult<>(SUCCESS);
 
         if (patch_dict_.empty()) {
-            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
-            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
             return DatOperationResult<>(SUCCESS);
         }
 
@@ -510,58 +531,29 @@ namespace LOTRO_DAT {
             current_size += 4;
         }
 
+        // Updating dict offset, if current does not match prerequisites
+        if (binary_data.size() > dict_size_ || dict_offset_ == 0) {
+            dict_offset_ = dat->GetFileSystem().patched_file_end;
+            dict_size_ = std::max(binary_data.size() + 4, 20u * 1024u * 1024u);
 
-        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 + 16;
-
-            // Updating first 16 bytes
-            binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)), 0);
-            binary_data.Append(BinaryData::FromNumber<4>(DAT_LOCALE_DICT_VERSION), 4);
-            binary_data.Append(
-                    BinaryData::FromNumber<4>(dat->GetFileSystem().patched_file_end + binary_data.size() + 20 * 1024 * 1024), 8);
-            binary_data.Append(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 12);
-            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));
+            BinaryData nulls(dict_size_);
             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>(DAT_LOCALE_DICT_VERSION), 4);
-            binary_data.Append(BinaryData::FromNumber<4>(dat->GetFileSystem().patched_file_end), 8);
-            binary_data.Append(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 12);
-
-            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->GetIO().WriteData(BinaryData::FromNumber<4>(dict_offset_), 4, 0x12C);
+        }
 
-            auto operation = dat->GetIO().WriteData(binary_data, binary_data.size(), dict_offset);
+        // Updating first 16 bytes
+        binary_data.Append(BinaryData::FromNumber<4>(dict_size_), 0);
+        binary_data.Append(BinaryData::FromNumber<4>(DAT_LOCALE_DICT_VERSION), 4);
+        binary_data.Append(BinaryData::FromNumber<4>(dat->GetFileSystem().patched_file_end), 8);
+        binary_data.Append(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 12);
+        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);
-            }
+        if (current_locale_ == ORIGINAL) {
+            dat->GetIO().WriteData(BinaryData::FromNumber<4>(1), 4, 296);
+        } else {
+            dat->GetIO().WriteData(BinaryData::FromNumber<4>(2), 4, 296);
         }
 
         dat->GetStatusModule().SetDefaultStatus();
@@ -570,10 +562,12 @@ namespace LOTRO_DAT {
     }
 
     void DatLocaleManager::ClearData() {
+        dict_size_ = 0;
+        dict_offset_ = 0;
         orig_dict_.clear();
         patch_dict_.clear();
         inactive_categories.clear();
-        current_locale_ = LOCALE(-1);
+        current_locale_ = ORIGINAL;
     }
 
     DatOperationResult<> DatLocaleManager::EnableCategory(long long category) {

+ 41 - 16
src/Examples/extractor_example.cpp

@@ -12,7 +12,6 @@
 
 #include "LotroDat.h"
 using namespace LOTRO_DAT;
-using namespace std;
 
 // Change these variables to true if you want export category to files.
 bool exportTextsToFiles = false;
@@ -80,23 +79,35 @@ void DatStatusChangedHandler(DatStatus::ProgressInfo info) {
 
 int main() {
     std::cout.precision(1);
-    std::cout << fixed;
+    std::cout << std::fixed;
     std::cout << "Gi1dor's LotRO .dat extractor ver. " << LOTRO_DAT_VERSION << 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";
     DatFile file;
 
-    ifstream in("dat_file_path.txt");
+    std::ifstream in("dat_file_path.txt");
     if (!in.fail()) {
         std::string filename;
-        getline(in, filename);
-        std::cout << "Using .dat file from dat_file_path.txt...\n";
-        std::cout << "Opening file " << filename << std::endl;
+        std::getline(in, filename);
+
+        std::string file_id_str;
+        std::getline(in, file_id_str);
+        int file_id = 0;
+        try {
+            file_id = stoi(file_id_str);
+        } catch (std::invalid_argument) {
+            file_id = -1;
+            std::cout << "Invalid file_id on second line of file dat_file_path.txt!\n\n";
+        }
 
-        auto operation = file.Initialise(filename, 0);
-        if (operation.result == false) {
-            std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\n";
+        if (file_id != -1) {
+            std::cout << "Using .dat file from dat_file_path.txt...\n";
+            std::cout << "Opening file " << filename << " with id = " << file_id << std::endl;
+            auto operation = file.Initialise(filename, file_id);
+            if (operation.result == false) {
+                std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\n";
+            }
         }
     }
 
@@ -106,9 +117,20 @@ int main() {
         std::string filename;
         std::getline(std::cin, filename);
 
+        std::cout << "Enter file id: ";
+        std::string file_id_str;
+        std::getline(std::cin, file_id_str);
+        int file_id = 0;
+        try {
+            file_id = stoi(file_id_str);
+        } catch (std::invalid_argument) {
+            std::cout << "Invalid command entered!\n\n";
+            continue;
+        }
+
         std::cout << "Opening file " << filename << std::endl;
 
-        auto operation = file.Initialise(filename, 0);
+        auto operation = file.Initialise(filename, file_id);
         if (operation.result == false) {
             std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\nTo try again enter path to .dat file\n";
         }
@@ -119,17 +141,20 @@ int main() {
     std::cout << "Gathering file information...\n";
     file.GatherInformation("dat_info.log");
 
-    int cmd = 0;
     while (true) {
         std::cout << "Please, choose, what should I do. I can extract .dat file to files (press 1), "
                     "open another .dat file (press 2), or configure, what should I extract. Choose, what to do or exit (press -1)\n";
 
-        if (cmd != 3) {
-            std::cout << "Enter number of command (1-3): ";
-            std::cin >> cmd;
+        std::cout << "Enter number of command (1-3): ";
+        std::string command;
+        std::getline(std::cin, command);
 
-            std::string tmp;
-            std::getline(std::cin, tmp);
+        int cmd = -10;
+        try {
+            cmd = stoi(command);
+        } catch (std::invalid_argument) {
+            std::cout << "Invalid command entered!\n\n";
+            continue;
         }
 
         if (cmd == -1) {

+ 49 - 21
src/Examples/patcher_example.cpp

@@ -12,7 +12,6 @@
 
 #include "LotroDat.h"
 using namespace LOTRO_DAT;
-using namespace std;
 
 void DatStatusChangedHandler(DatStatus::ProgressInfo info) {
     if (info.status == DatStatus::DAT_STATUS::E_FREE) {
@@ -63,7 +62,7 @@ void DatStatusChangedHandler(DatStatus::ProgressInfo info) {
 
 int main() {
     std::cout.precision(1);
-    std::cout << fixed;
+    std::cout << std::fixed;
     std::cout << "Gi1dor's LotRO .dat patcher ver. " << LOTRO_DAT_VERSION << std::endl;
     freopen("patcher_errors.log", "w", stderr);
 
@@ -77,16 +76,29 @@ int main() {
             "if you write path to it (with name of file) in file \"dat_file_path.txt\"\n";
     DatFile file;
 
-    ifstream in("dat_file_path.txt");
+    std::ifstream in("dat_file_path.txt");
     if (!in.fail()) {
         std::string filename;
-        getline(in, filename);
+        std::getline(in, filename);
+
+        std::string file_id_str;
+        std::getline(in, file_id_str);
+        int file_id = 0;
+        try {
+            file_id = stoi(file_id_str);
+        } catch (std::invalid_argument) {
+            file_id = -1;
+            std::cout << "Invalid file_id on second line of file dat_file_path.txt!\n\n";
+        }
 
-        std::cout << "Using .dat file from dat_file_path.txt...\n";
-        std::cout << "Opening file " << filename << std::endl;
-        auto operation = file.Initialise(filename, 0);
-        if (operation.result == ERROR)
-            std::cout << "Cannot initialise dat file " << filename << " \n";
+        if (file_id != -1) {
+            std::cout << "Using .dat file from dat_file_path.txt...\n";
+            std::cout << "Opening file " << filename << " with id = " << file_id << std::endl;
+            auto operation = file.Initialise(filename, file_id);
+            if (operation.result == false) {
+                std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\n";
+            }
+        }
     }
 
     while (!file.Initialized()) {
@@ -95,11 +107,23 @@ int main() {
         std::string filename;
         std::getline(std::cin, filename);
 
+        std::cout << "Enter file id: ";
+        std::string file_id_str;
+        std::getline(std::cin, file_id_str);
+        int file_id = 0;
+        try {
+            file_id = stoi(file_id_str);
+        } catch (std::invalid_argument) {
+            std::cout << "Invalid command entered!\n\n";
+            continue;
+        }
+
         std::cout << "Opening file " << filename << std::endl;
 
-        auto operation = file.Initialise(filename, 0);
-        if (operation.result == ERROR)
-            std::cout << "Cannot initialise dat file " << filename << ", please, try again\n";
+        auto operation = file.Initialise(filename, file_id);
+        if (operation.result == false) {
+            std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\nTo try again enter path to .dat file\n";
+        }
     }
 
     std::cout << "Great! File initialised successfully!\n";
@@ -111,12 +135,16 @@ 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";
 
-        int cmd = 0;
         std::cout << "Enter number of command (1-10): ";
-        std::cin >> cmd;
-
-        std::string tmp;
-        std::getline(std::cin, tmp);
+        std::string command;
+        std::getline(std::cin, command);
+        int cmd = -10;
+        try {
+            cmd = stoi(command);
+        } catch (std::invalid_argument) {
+            std::cout << "Invalid command entered!\n\n";
+            continue;
+        }
 
         if (cmd == -1) {
             std::cout << "Exiting. Thanks for using me!\n";
@@ -170,14 +198,14 @@ int main() {
             }
         }
         if (cmd == 2) {
-            std::cout << "Old locale is " << (file.GetLocaleManager().GetCurrentLocale() == DatLocaleManager::PATCHED ? "RU" : "Original") << endl;
+            std::cout << "Old locale is " << (file.GetLocaleManager().GetCurrentLocale() == DatLocaleManager::PATCHED ? "RU" : "Original") << std::endl;
             std::cout << "Changing locale..." << std::endl;
             file.GetLocaleManager().SetLocale(file.GetLocaleManager().GetCurrentLocale() == DatLocaleManager::PATCHED  ? DatLocaleManager::ORIGINAL : DatLocaleManager::PATCHED);
-            std::cout << "New locale is " << (file.GetLocaleManager().GetCurrentLocale() == DatLocaleManager::PATCHED ? "RU" : "Original") << endl;
+            std::cout << "New locale is " << (file.GetLocaleManager().GetCurrentLocale() == DatLocaleManager::PATCHED ? "RU" : "Original") << std::endl;
         }
 
         if (cmd == 3) {
-            std::cout << "Current locale is " << (file.GetLocaleManager().GetCurrentLocale() == DatLocaleManager::PATCHED ? "RU" : "Original") << endl;
+            std::cout << "Current locale is " << (file.GetLocaleManager().GetCurrentLocale() == DatLocaleManager::PATCHED ? "RU" : "Original") << std::endl;
         }
 
         if (cmd == 4) {
@@ -200,7 +228,7 @@ int main() {
             std::cout << "Disabled categories: ";
             for (auto i : file.GetLocaleManager().GetInactiveCategories())
                 std::cout << i << " ";
-            std::cout << endl;
+            std::cout << std::endl;
         }
 
         if (cmd == 7) {