瀏覽代碼

Completed core subsystems: IO, LocaleManager, FileSystem

Ivan Arkhipov 6 年之前
父節點
當前提交
164f99b8ee

+ 2 - 0
include/DatSubsystems/DatFileSystem.h

@@ -35,6 +35,8 @@ namespace LOTRO_DAT {
 
         DatOperationResult<bool> CheckCorrectSubfile(const SubFile &file);
 
+        bool FileExists(long long file_id);
+
         DatOperationResult<> UpdateFile(const SubFile& file);
 
         DatOperationResult<> DeInit();

+ 41 - 0
include/DatSubsystems/DatLocaleManager.h

@@ -5,12 +5,23 @@
 #ifndef LOTRO_DAT_LIBRARY_DATLOCALEMANAGER_H
 #define LOTRO_DAT_LIBRARY_DATLOCALEMANAGER_H
 
+#include <memory>
+#include <map>
+#include <DatOperationResult.h>
+#include <set>
+
 extern "C++" {
 namespace LOTRO_DAT {
     class DatFile;
+    class SubFile;
 
     class DatLocaleManager {
     public:
+        enum LOCALE {
+            PATCHED,
+            ORIGINAL
+        };
+
         DatLocaleManager() = delete;
         DatLocaleManager(const DatLocaleManager &other) = delete;
         DatLocaleManager& operator=(const DatLocaleManager &other) = delete;
@@ -18,11 +29,41 @@ namespace LOTRO_DAT {
 
         explicit DatLocaleManager(DatFile *datFilePtr);
 
+        DatOperationResult<> Init();
+        DatOperationResult<> SetLocale(LOCALE locale);
+        DatOperationResult<> DeInit();
+
+        LOCALE GetCurrentLocale();
+        void UpdateLocaleFile(LOCALE locale, const SubFile& file);
+        DatOperationResult<SubFile> GetLocaleFile(long long file_id, LOCALE locale);
+
+    private:
+        std::map<long long, SubFile>& GetLocaleDictReference(LOCALE locale);
+
     private:
         DatFile *dat;
+        std::map<long long, SubFile> orig_dict_;
+        std::map<long long, SubFile> patch_dict_;
+        std::set<long long> inactive_categories;
 
+        LOCALE current_locale_;
     };
 }
 };
 
+/*
+ * ======== LOCALE DICT STRUCTURE =========
+ * 4                                bytes for block 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
+ */
+
 #endif //LOTRO_DAT_LIBRARY_DATLOCALEMANAGER_H

+ 4 - 0
src/DatSubsystems/DatFileSystem.cpp

@@ -204,4 +204,8 @@ namespace LOTRO_DAT{
         }
         return DatOperationResult<>(SUCCESS);
     }
+
+    bool DatFileSystem::FileExists(long long file_id) {
+        return dictionary_.count(file_id) != 0;
+    }
 }

+ 227 - 1
src/DatSubsystems/DatLocaleManager.cpp

@@ -2,4 +2,230 @@
 // Created by kikab on 04.06.2018.
 //
 
-#include "DatSubsystems/DatLocaleManager.h"
+#include <DatSubsystems/DatLocaleManager.h>
+#include <EasyLogging++/easylogging++.h>
+#include <DatFile.h>
+#include <SubFile.h>
+
+namespace LOTRO_DAT {
+    DatLocaleManager::DatLocaleManager(DatFile *datFilePtr) : dat(datFilePtr), current_locale_(ORIGINAL) {
+    }
+
+    DatOperationResult<> DatLocaleManager::Init() {
+        if (!dat)
+            return DatOperationResult<>(ERROR, "LOCALEINIT: no connection with Dat (dat is nullptr)");
+
+        orig_dict_.clear();
+        patch_dict_.clear();
+
+        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()) {
+            LOG(INFO) << "Dictionary offset is empty or incorrect. Passing.";
+            return DatOperationResult<>();
+        }
+
+        BinaryData locale_info(12);
+        dat->getIO().ReadData(locale_info, 12, locale_offset);
+
+        long long dict_size = locale_info.ToNumber<4>(0);
+        long long dict_version = locale_info.ToNumber<4>(4);
+        dat->getIO().file_size = locale_info.ToNumber<4>(8);
+
+        LOG(INFO) << "Dictionary size is " << dict_size << ". Version is " << dict_version << ". Localed .dat size = " << dat->getIO().file_size;
+
+        if (dict_version != 101) {
+            LOG(WARNING) << "DICTIONARY IS OLD! Version = " << dict_version;
+            dat->getIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
+            return DatOperationResult<>(ERROR, "Version of locales' dictionary is old (ver = " + std::to_string(dict_version) + "), cannot work with this type of .dat file.");
+        }
+
+        BinaryData dicts_data = BinaryData((unsigned)dict_size);
+        dat->getIO().ReadData(dicts_data, dict_size, locale_offset + 12);
+
+        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 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').");
+
+        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).");
+
+        current_locale_ = (locale == "PATC" ? PATCHED : ORIGINAL);
+
+        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);
+            orig_dict_[file.file_id()] = file;
+            offset += 36;
+        }
+
+        size_t patch_dict_size = size_t(dicts_data.CutData(offset, offset + 4).ToNumber<4>(0));
+        offset += 4;
+        for (size_t i = 0; i < patch_dict_size; i++) {
+            auto file = SubFile(*dat, dicts_data.CutData(offset, offset + 32));
+            file.category = dicts_data.ToNumber<4>(offset);
+            orig_dict_[file.file_id()] = file;
+            offset += 36;
+        }
+
+        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) << "Finished initialising locales";
+        return DatOperationResult<>(SUCCESS);
+    }
+
+    DatOperationResult<> DatLocaleManager::SetLocale(DatLocaleManager::LOCALE locale) {
+        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.";
+            return DatOperationResult<>(SUCCESS);
+        }
+
+        auto dict = GetLocaleDictReference(locale);
+        for (const auto &file : dict) {
+            long long file_id = file.first;
+
+            if (!dat->getFileSystem().FileExists(file_id)) {
+                LOG(WARNING) << "In locale dictionary there is file with file_id = " << file_id
+                             << "which is not in .dat file! Passing it and removing from locale dictionary";
+                dict.erase(file_id);
+                continue;
+            }
+
+            auto dict_file_result = dat->getFileSystem().GetFile(file_id);
+            if (dict_file_result.result != SUCCESS) {
+                LOG(WARNING) << "Unable to get file with id = " << file_id << "from datFileSystem! Error msg:" << dict_file_result.msg;
+                continue;
+            }
+            SubFile dict_file = dict_file_result.value;
+
+            if (dict_file.MakeHeaderData().CutData(8, 16) == file.second.MakeHeaderData().CutData(8, 16))
+                continue;
+
+            dat->getFileSystem().UpdateFile(file.second);
+        }
+        current_locale_ = locale;
+        return DatOperationResult<>(SUCCESS);
+    }
+
+    DatLocaleManager::LOCALE DatLocaleManager::GetCurrentLocale() {
+        return current_locale_;
+    }
+
+    DatOperationResult<> DatLocaleManager::DeInit() {
+        LOG(INFO) << "Committing locales...";
+        if (!dat)
+            return DatOperationResult<>(ERROR, "LOCALEDEINIT: no connection with Dat (dat is nullptr)");
+
+        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());
+
+        size_t current_size = 0;
+        binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)));
+        binary_data.Append(BinaryData::FromNumber<4>(101));
+        binary_data.Append(BinaryData::FromNumber<4>(dat->getIO().file_size + binary_data.size() + 12 + 20 * 1024 * 1024));
+        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) {
+            auto operation = dat->getIO().WriteData(binary_data, binary_data.size(), dat->getIO().file_size + 12);
+            if (operation.result != SUCCESS)
+                return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales. ERRMSG: " + operation.msg);
+            dat->getIO().WriteData(BinaryData::FromNumber<4>(dat->getIO().file_size), 4, 300);
+
+            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 {
+            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 commited successfully";
+        return DatOperationResult<>(SUCCESS);
+    }
+
+
+    void DatLocaleManager::UpdateLocaleFile(DatLocaleManager::LOCALE locale, const SubFile &file) {
+        auto dict = GetLocaleDictReference(locale);
+        dict[file.file_id()] = file;
+    }
+
+    DatOperationResult<SubFile> DatLocaleManager::GetLocaleFile(long long file_id, DatLocaleManager::LOCALE locale) {
+        auto 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>(dict[file_id], SUCCESS);
+    }
+
+
+    std::map<long long, SubFile> &DatLocaleManager::GetLocaleDictReference(DatLocaleManager::LOCALE locale) {
+        return current_locale_ == PATCHED ? patch_dict_ : orig_dict_;
+    }
+}