Browse Source

Initial commit

Gi1dor 6 years ago
commit
c9c6685104
14 changed files with 1175 additions and 0 deletions
  1. 23 0
      .gitignore
  2. 140 0
      BinaryData.cpp
  3. 46 0
      BinaryData.h
  4. 10 0
      CMakeLists.txt
  5. 67 0
      DatException.h
  6. 187 0
      DatFile.cpp
  7. 77 0
      DatFile.h
  8. 11 0
      LotroDatPatcher.h
  9. 0 0
      README
  10. 93 0
      SubDirectory.cpp
  11. 40 0
      SubDirectory.h
  12. 266 0
      Subfile.cpp
  13. 76 0
      Subfile.h
  14. 139 0
      test.cpp

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+# cmake build directory
+cmake-build-debug 
+# ide project directory
+.idea
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+
+# Build and stuff dirs
+build*
+debug*
+stuff*
+debug_build
+release_build
+/bin
+/lib
+/install

+ 140 - 0
BinaryData.cpp

@@ -0,0 +1,140 @@
+//
+// Created by Иван_Архипов on 31.10.2017.
+//
+
+#include "BinaryData.h"
+#include "DatException.h"
+
+extern  "C++"
+{
+namespace LOTRO_DAT {
+
+    BinaryData::BinaryData() {
+        data_ = nullptr;
+        size_ = 0;
+    }
+
+    BinaryData::BinaryData(const BinaryData &d) {
+        size_ = d.size_;
+        data_ = new unsigned char[size_];
+        memcpy(data_, d.data_, size_);
+    }
+
+    BinaryData::BinaryData(unsigned int size) {
+		data_ = new unsigned char[size];
+        size_ = size;
+    }
+
+    BinaryData::~BinaryData() {
+        if (size_ != 0 && data_ != nullptr)
+            delete[] data_;
+    }
+
+    unsigned char&  BinaryData::operator[](const unsigned int &pos) {
+        if (pos >= size_)
+            throw DatException("Bad BinaryData::operator[]. Position is out of range.");
+        return data_[pos];
+    }
+
+    // Translates data into specified number type due to UTF-16LE encoding of the .dat file
+    template<int T>
+    long long BinaryData::ToNumber(const long long &pos) const {
+        try {
+            long long ans = 0;
+
+            if (pos + 3 >= size_)
+                throw DatException("Bad BinaryData::ToNumber(). Reached end of BinaryData!", DATA_EXCEPTION);
+
+            for (int i = T - 1; i >= 0; i--)
+                ans = ((ans << 8ll) | (unsigned char)(data_[pos + i]));
+
+            return ans;
+        } catch (...) {
+            throw DatException("Bad BinaryData::ToNumber(). Error while using template function", DATA_EXCEPTION);
+        }
+    }
+
+    // Makes data from specified number type
+    template<int T>
+    void BinaryData::FromNumber(const long long &number) {
+        if (T < 0)
+            throw DatException("Bad BinaryData::FromNumber() - trying to make data from amount of bytes < 0");
+        try {
+            if (size_ != 0 && data_ != nullptr)
+                delete[] data_;
+
+            size_ = size_t(T);
+            data_ = new unsigned char[size_];
+
+            for (size_t i = 0; i < size_; i++)
+            {
+                data_[i] = (unsigned char)((number >> (8 * i)) & 0xFF);
+            }
+
+        } catch (...) {
+            throw DatException("Bad BinaryData::ToNumber(). Error in using template function", DATA_EXCEPTION);
+        }
+    }
+
+    BinaryData &BinaryData::operator=(const BinaryData &data) {
+        if (&data == this)
+            return *this;
+
+        if (size_ != 0 && data_ != nullptr)
+            delete[] data_;
+
+        size_ = data.size_;
+        data_ = nullptr;
+
+        if (size_ != 0) {
+            data_ = new unsigned char[size_];
+            memcpy(data_, data.data_, size_);
+        }
+        return *this;
+    }
+
+    size_t BinaryData::size() const {
+        return size_;
+    }
+
+    unsigned char *BinaryData::data() const {
+        return data_;
+    }
+
+	bool BinaryData::WriteToFile(const char *filename) const {
+		FILE *f;
+		fopen_s(&f, filename, "wb");
+		if (f == nullptr) {
+			throw DatException("Bad BinaryData::WriteToFile() - unable to open output file", EXPORT_EXCEPTION);
+			return false;
+		}
+
+		fwrite(data(), size(), 1, f);
+		fclose(f);
+		return true;
+	}
+
+	bool BinaryData::WriteToFile(const std::string &filename) const {
+		FILE *f;
+		fopen_s(&f, filename.c_str(), "wb");
+		if (f == nullptr) {
+			throw DatException("Bad BinaryData::WriteToFile() - unable to open output file", EXPORT_EXCEPTION);
+			return false;
+		}
+
+		fwrite(data(), size(), 1, f);
+		fclose(f);
+		return true;
+	}
+
+    template long long BinaryData::ToNumber<1>(long long const&) const;
+    template long long BinaryData::ToNumber<2>(long long const&) const;
+    template long long BinaryData::ToNumber<4>(long long const&) const;
+    template long long BinaryData::ToNumber<8>(long long const&) const;
+
+    template void BinaryData::FromNumber<1>(const long long &);
+    template void BinaryData::FromNumber<2>(const long long &);
+    template void BinaryData::FromNumber<4>(const long long &);
+    template void BinaryData::FromNumber<8>(const long long &);
+}
+}

+ 46 - 0
BinaryData.h

@@ -0,0 +1,46 @@
+//
+// Created by Иван_Архипов on 31.10.2017.
+//
+
+#ifndef LOTRO_DAT_PATCHER_BINARYDATA_H
+#define LOTRO_DAT_PATCHER_BINARYDATA_H
+
+#include <cstdio>
+#include <string>
+
+extern  "C++"
+{
+namespace LOTRO_DAT
+{
+    class BinaryData
+    {
+    public:
+        BinaryData();
+        BinaryData(const BinaryData &d);
+        explicit BinaryData(unsigned int size);
+        ~BinaryData();
+
+        unsigned char& operator[] (const unsigned int &pos);
+
+        template <int T>
+        long long ToNumber(const long long &pos) const;
+
+        template <int T>
+        void FromNumber(const long long &number);
+
+        size_t size() const;
+        unsigned char* data() const;
+
+        BinaryData& operator=(const BinaryData& data);
+
+		bool WriteToFile(const char *filename) const;
+		bool WriteToFile(const std::string &filename) const;
+
+    private:
+        unsigned char *data_;
+        size_t size_;
+    };
+}
+}
+
+#endif //LOTRO_DAT_PATCHER_BINARYDATA_H

+ 10 - 0
CMakeLists.txt

@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.8)
+project(LotRO_dat_Patcher)
+
+set(CMAKE_CXX_STANDARD 11)
+
+set(SOURCE_FILES DatFile.cpp DatFile.h BinaryData.cpp BinaryData.h DatException.h LotroDatPatcher.h Subfile.cpp Subfile.h SubDirectory.cpp SubDirectory.h)
+#set(SOURCE_FILES DatFile.cpp DatFile.h BinaryData.cpp BinaryData.h DatException.h LotroDatPatcher.h)
+
+#add_library(LotRO_dat_Patcher SHARED ${SOURCE_FILES})
+add_executable(LotRO_dat_tester ${SOURCE_FILES} test.cpp)

+ 67 - 0
DatException.h

@@ -0,0 +1,67 @@
+//
+// Created by Иван_Архипов on 31.10.2017.
+//
+#ifndef LOTRO_DAT_PATCHER_DATEXCEPTION_H
+#define LOTRO_DAT_PATCHER_DATEXCEPTION_H
+
+#define DAT_DEBUG
+
+#include <string>
+#include <iostream>
+#include <cstring>
+
+extern  "C++"
+{
+namespace LOTRO_DAT
+{
+    enum DAT_EXCEPTION_TYPE {
+        INIT_EXCEPTION,
+        READ_EXCEPTION,
+        WRITE_EXCEPTION,
+        SUBDIR_EXCEPTION,
+        SUBFILE_EXCEPTION,
+        IMPORT_EXCEPTION,
+        EXPORT_EXCEPTION,
+        DATA_EXCEPTION,
+        UNKNOWN_EXCEPTION
+    };
+
+    class DatException : std::exception {
+    public:
+        DatException()
+                : msg_(const_cast<char *>("Unknown DatException has been raised!"))
+                , type_(UNKNOWN_EXCEPTION)
+        {}
+
+        explicit DatException(const char *msg, DAT_EXCEPTION_TYPE type = UNKNOWN_EXCEPTION) noexcept
+                : std::exception()
+                , type_(type)
+        {
+            msg_ = new char[strlen(msg) + 1];
+            memcpy(msg_, msg, strlen(msg) + 1);
+            #ifdef DAT_DEBUG
+                fprintf(stderr, "%s\n", msg_);
+            #endif
+        }
+
+        ~DatException() override
+        {
+            delete msg_;
+        }
+
+        const char* what() const noexcept override
+        {
+            return "DatException";
+        }
+
+        const DAT_EXCEPTION_TYPE type() const
+        {
+            return type_;
+        }
+    private:
+        char *msg_;
+        const DAT_EXCEPTION_TYPE type_;
+    };
+}
+}
+#endif //LOTRO_DAT_PATCHER_DATEXCEPTION_H

+ 187 - 0
DatFile.cpp

@@ -0,0 +1,187 @@
+//
+// Created by Иван_Архипов on 31.10.2017.
+//
+
+#include "DatFile.h"
+
+#include "BinaryData.h"
+#include "DatException.h"
+#include "SubDirectory.h"
+#include "Subfile.h"
+
+#include <locale>
+
+extern "C++"
+{
+namespace LOTRO_DAT
+{
+    DatFile::DatFile(const char* filename)
+    {
+        dat_state_ = CLOSED;
+        OpenDatFile(filename);
+        ReadSuperBlock();
+        MakeDirectories();
+        MakeDictionary();
+        if (dat_state_ == SUCCESS_DICTIONARY)
+            dat_state_ = READY;
+        else
+            throw DatException("Bad DatFile initialization! Not all init states were successfully passed!", INIT_EXCEPTION);
+    }
+
+    DatFile::DatFile(const std::string &filename)
+    {
+        dat_state_ = CLOSED;
+        OpenDatFile(filename.c_str());
+        ReadSuperBlock();
+        MakeDirectories();
+        MakeDictionary();
+        if (dat_state_ == SUCCESS_DICTIONARY)
+            dat_state_ = READY;
+        else
+            throw DatException("Bad DatFile initialization! Not all init states were successfully passed!", INIT_EXCEPTION);
+    }
+
+    DatFile::~DatFile()
+    {
+        if (file_handler_ != nullptr)
+            fclose(file_handler_);
+        delete file_handler_;
+        delete root_directory_;
+    }
+
+    void DatFile::OpenDatFile(const char* dat_name)
+    {
+        if (dat_state_ != CLOSED)
+            throw DatException("Bad initialisation of DatFile - current DatFile isn't in correct state!", INIT_EXCEPTION);
+
+        fopen_s(&file_handler_, dat_name, "r+b");
+
+        if(file_handler_ == nullptr) {
+            std::string err = "Bad DatFile::OpenDatFile. Unable to open file ";
+            err += dat_name;
+            throw DatException(err.c_str(), INIT_EXCEPTION);
+        }
+
+        _fseeki64(file_handler_, 0, SEEK_END);
+        file_size_ = _ftelli64(file_handler_);
+        _fseeki64(file_handler_, 0, SEEK_SET);
+
+        dat_state_ = SUCCESS_OPENED;
+    }
+
+    void DatFile::ReadSuperBlock()
+    {
+        if (dat_state_ != SUCCESS_OPENED)
+            throw DatException("Bad DatFile::ReadSuperBlock() - DatFile isn't in valid state!", INIT_EXCEPTION);
+
+        BinaryData data(1024);
+        ReadData(data, 1024);
+
+        constant1_ = data.ToNumber<4>(0x100);
+        constant2_ = data.ToNumber<4>(0x140);
+        version1_ = data.ToNumber<4>(0x14C);
+        version2_ = data.ToNumber<4>(0x150);
+        root_directory_offset_ = data.ToNumber<4>(0x160);
+
+        auto size1 = data.ToNumber<4>(0x148);
+
+        if (constant1_ != 0x4C5000)
+            throw DatException("Bad DatFile::ReadSuperBlock - variable at position 0x100 is not equal to .dat file constant!"
+                    , INIT_EXCEPTION);
+
+        if (constant2_ != 0x5442)
+            throw DatException("Bad DatFile::ReadSuperBlock - variable at position 0x140 is not equal to .dat file constant!"
+                    , INIT_EXCEPTION);
+
+        if (file_size_ != size1)
+            throw DatException("Bad DatFile::ReadSuperBlock - variable at 0x148 position is not equal to .dat file size!"
+                    , INIT_EXCEPTION);
+
+        dat_state_ = SUCCESS_SUPERBLOCK;
+    }
+
+    void DatFile::MakeDirectories()
+    {
+        if (dat_state_ != SUCCESS_SUPERBLOCK)
+            throw DatException("Bad DatFile::MakeDirectories() - DatFile isn't in valid state!", INIT_EXCEPTION);
+        root_directory_ = new SubDirectory((unsigned)root_directory_offset_, this);
+        dat_state_ = SUCCESS_DIRECTORIES;
+    }
+
+    void DatFile::MakeDictionary()
+    {
+        if (dat_state_ != SUCCESS_DIRECTORIES)
+            throw DatException("Bad DatFile::MakeDictionary() - DatFile isn't in valid state!", INIT_EXCEPTION);
+        root_directory_->MakeDictionary(dictionary_);
+        dat_state_ = SUCCESS_DICTIONARY;
+    }
+
+    void DatFile::ReadData(BinaryData &data, long long size, long long offset, long long data_offset)
+    {
+        if (dat_state_ == CLOSED)
+            throw DatException("Bad DatFile::ReadData() - DatFile isn't in valid state!", READ_EXCEPTION);
+
+        if (data_offset + size > data.size())
+        {
+            std::string err = "Bad DatFile::ReadData - trying to read more than BinaryData size\n";
+            err += std::string("Reading ") + std::to_string(size) + std::string(" bytes from ")
+                   + std::to_string(offset) + std::string(" position in dat file.");
+            throw DatException(err.c_str(), READ_EXCEPTION);
+        }
+
+        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.");
+            throw DatException(err.c_str(), READ_EXCEPTION);
+        }
+
+        _fseeki64(file_handler_, offset, SEEK_SET);
+        fread(data.data() + data_offset, size, 1, file_handler_);
+    }
+
+    void DatFile::WriteData(const BinaryData &data, long long size, long long offset, long long data_offset)
+    {
+        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);
+
+        fwrite(data.data() + data_offset, size, 1, file_handler_);
+    }
+
+    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_;
+    }
+
+    const std::unordered_map<long long, Subfile *> &DatFile::dictionary()
+    {
+        return dictionary_;
+    }
+
+}
+}

+ 77 - 0
DatFile.h

@@ -0,0 +1,77 @@
+//
+// Created by Иван_Архипов on 31.10.2017.
+//
+
+#ifndef LOTRO_DAT_PATCHER_DATFILE_H
+#define LOTRO_DAT_PATCHER_DATFILE_H
+
+#ifdef LOTRO_DAT_EXPORTS
+#define LOTRO_DAT_API __declspec(dllexport)
+#else
+#define LOTRO_DAT_API __declspec(dllimport)
+#endif
+
+#include <fstream>
+#include <map>
+#include <unordered_map>
+#include <set>
+
+extern  "C++"
+{
+namespace LOTRO_DAT
+{
+    class BinaryData;
+    class DatException;
+    class SubDirectory;
+    class Subfile;
+
+    enum STATE {
+        CLOSED,
+        SUCCESS_OPENED,
+        SUCCESS_SUPERBLOCK,
+        SUCCESS_DIRECTORIES,
+        SUCCESS_DICTIONARY,
+        READY
+    };
+
+    class DatFile
+    {
+    public:
+        explicit DatFile(const char* filename);
+        explicit DatFile(const std::string &filename);
+        ~DatFile();
+
+        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);
+
+        long long constant1() const;
+        long long constant2() const;
+        long long file_size() const;
+        long long version1() const;
+        long long version2() const;
+
+        const std::unordered_map<long long, Subfile*> &dictionary();
+
+    private:
+        FILE *file_handler_;
+        SubDirectory *root_directory_;
+        std::unordered_map<long long, Subfile*> dictionary_;
+
+        void OpenDatFile(const char* dat_name);
+        void ReadSuperBlock();
+        void MakeDirectories();
+        void MakeDictionary();
+
+        long long constant1_;
+        long long constant2_;
+        long long file_size_;
+        long long version1_;
+        long long version2_;
+        long long root_directory_offset_;
+
+        STATE dat_state_;
+    };
+}
+}
+
+#endif //LOTRO_DAT_PATCHER_DATFILE_H

+ 11 - 0
LotroDatPatcher.h

@@ -0,0 +1,11 @@
+//
+// Created by Иван_Архипов on 01.11.2017.
+//
+
+#include "DatFile.h"
+#include "BinaryData.h"
+
+#include "Subfile.h"
+
+#include "DatException.h"
+#include "SubDirectory.h"

+ 0 - 0
README


+ 93 - 0
SubDirectory.cpp

@@ -0,0 +1,93 @@
+//
+// Created by Иван_Архипов on 07.11.2017.
+//
+#include "SubDirectory.h"
+
+#include "DatFile.h"
+#include "DatException.h"
+#include "Subfile.h"
+#include "BinaryData.h"
+
+
+LOTRO_DAT::SubDirectory::SubDirectory() {
+    offset_ = 0;
+}
+
+LOTRO_DAT::SubDirectory::SubDirectory(long long offset, DatFile *dat) :
+        offset_(offset), dat_(dat) {
+    try {
+        MakeSubDirectories();
+    } catch (...) {
+		fprintf(stderr, "Unable to initialize directory at offset %lld. Initializing as empty directory...\n", offset);
+		subdirs_.clear();
+		subfiles_.clear();
+		return;
+	}
+
+    try {
+        MakeSubFiles();
+	} catch (...) {
+		fprintf(stderr, "Unable to initialize directory at offset %lld. Initializing as empty directory...\n", offset);
+		subdirs_.clear();
+		subfiles_.clear();
+		return;
+	}
+}
+
+void LOTRO_DAT::SubDirectory::MakeSubDirectories() {
+    BinaryData data(1024);
+    dat_->ReadData(data, 63 * 8, offset_);
+    if (data.ToNumber<4>(0) != 0 || data.ToNumber<4>(4) != 0) {
+        std::string err =
+                std::string("Bad SubDirectory::MakeSubDirectories - first 8 bytes are not equal to 0 at offset ")
+                + std::to_string(offset_);
+        throw DatException(err.c_str(), SUBDIR_EXCEPTION);
+    }
+
+    for (unsigned int i = 8; i < 63 * 8; i += 8) {
+        if (data.ToNumber<4>(i) == 0)
+            break;
+
+        try {
+            subdirs_.emplace_back(
+                    SubDirectory(
+                            data.ToNumber<4>(i + 4),
+                            dat_
+                    )
+            );
+        } catch (DatException &e) {
+            fprintf(stderr, "Making SubDirectory at offset %lld failed, continuing\n",  data.ToNumber<4>(i + 4));
+            // TODO - do something here....
+        }
+    }
+}
+
+void LOTRO_DAT::SubDirectory::MakeSubFiles() {
+    BinaryData data(2048);
+    dat_->ReadData(data, 61 * 32, offset_ + (63 * 8));
+
+    for (unsigned int i = 0; i < 61 * 32; i += 32) {
+        if (data.ToNumber<4>(i + 8) < 0x32 || data.ToNumber<4>(i + 12) < 0x32)
+            continue;
+        subfiles_.emplace_back(
+                Subfile(
+                        dat_,
+                        data.ToNumber<4>(i), // unknown0
+                        data.ToNumber<4>(i + 4), // unknown1
+                        data.ToNumber<4>(i + 8), // file_id
+                        data.ToNumber<4>(i + 12), // file_offset
+                        data.ToNumber<4>(i + 16), // size1
+                        data.ToNumber<4>(i + 20), // timestamp
+                        data.ToNumber<4>(i + 24), // version
+                        data.ToNumber<4>(i + 28) // size2
+                )
+        );
+    }
+}
+
+void LOTRO_DAT::SubDirectory::MakeDictionary(std::unordered_map<long long, Subfile *> &dict) {
+    for (Subfile &i : subfiles_)
+        dict[i.file_id()] = &i;
+    for (SubDirectory &i : subdirs_)
+        i.MakeDictionary(dict);
+}

+ 40 - 0
SubDirectory.h

@@ -0,0 +1,40 @@
+//
+// Created by Иван_Архипов on 07.11.2017.
+//
+
+#ifndef LOTRO_DAT_PATCHER_SUBDIRECTORY_H
+#define LOTRO_DAT_PATCHER_SUBDIRECTORY_H
+
+#include <vector>
+#include <map>
+#include <unordered_map>
+
+extern "C++"
+{
+namespace LOTRO_DAT
+{
+    class DatFile;
+    class DatException;
+    class BinaryData;
+    class Subfile;
+
+    class SubDirectory
+    {
+    public:
+        SubDirectory();
+        SubDirectory(long long offset, DatFile *dat);
+        void MakeDictionary(std::unordered_map<long long, Subfile*> &dict);
+
+    private:
+        void MakeSubDirectories();
+        void MakeSubFiles();
+
+        DatFile *dat_;
+        long long  offset_;
+        std::vector<SubDirectory> subdirs_;
+        std::vector<Subfile> subfiles_;
+    };
+}
+};
+
+#endif //LOTRO_DAT_PATCHER_SUBDIRECTORY_H

+ 266 - 0
Subfile.cpp

@@ -0,0 +1,266 @@
+//
+// Created by Иван_Архипов on 01.11.2017.
+//
+
+#include "Subfile.h"
+#include "BinaryData.h"
+#include "DatFile.h"
+#include "DatException.h"
+#include <iostream>
+
+const long long MAXSIZE = 10000000ll;
+
+LOTRO_DAT::Subfile::Subfile() {
+	ext_ = UNKNOWN;
+}
+
+LOTRO_DAT::Subfile::Subfile(DatFile *dat, long long unknown0, long long unknown1, long long file_id, long long file_offset, long long size1,
+                            long long timestamp, long long version, long long size2) :
+        dat_(dat), unknown0_(unknown0), unknown1_(unknown1), file_id_(file_id), file_offset_(file_offset + 8),
+        size1_(size1), timestamp_(timestamp), version_(version), size2_(size2) {
+	if (size1_ > MAXSIZE)
+		throw DatException("Bad Subfile::Subfile() - File size is too much... Maybe it's incorrect..?", SUBFILE_EXCEPTION);
+	ext_ = GetExtension();
+}
+
+long long LOTRO_DAT::Subfile::unknown0() const {
+    return unknown0_;
+}
+
+long long LOTRO_DAT::Subfile::unknown1() const {
+    return unknown1_;
+}
+
+long long LOTRO_DAT::Subfile::file_id() const {
+    return file_id_;
+}
+
+long long LOTRO_DAT::Subfile::file_offset() const {
+    return file_offset_;
+}
+
+long long LOTRO_DAT::Subfile::size1() const {
+    return size1_;
+}
+
+long long LOTRO_DAT::Subfile::timestamp() const {
+    return timestamp_;
+}
+
+long long LOTRO_DAT::Subfile::version() const {
+    return version_;
+}
+
+long long LOTRO_DAT::Subfile::size2() const {
+    return size2_;
+}
+
+LOTRO_DAT::EXTENSION LOTRO_DAT::Subfile::GetExtension() const {
+    // Text check based on file_id
+    if ((file_id_ >> 24ll) == 0x25ll)
+        return TXT;
+
+    // Font check based on file_id
+    if ((file_id_ >> 24ll) == 0x42ll)
+        return FONT;
+
+    BinaryData header(64);
+    try {
+        dat_->ReadData(header, 64, (unsigned) file_offset_);
+    } catch (DatException &e) {
+        if (e.type() == READ_EXCEPTION) {
+            std::string err =
+                    "Bad Subfile::getExtension() - unable to read header of file with id = " + std::to_string(file_id()) +
+                    " and offset = " + std::to_string(file_offset());
+            throw DatException(err.c_str(), SUBFILE_EXCEPTION);
+        } else
+            throw e;
+    }
+
+    // jpeg / dds check
+    if ((file_id_ >> 24ll) == 0x41ll) {
+        long long soi = header.ToNumber<2>(24);
+        long long marker = header.ToNumber<2>(26);
+
+        //auto markerSize = header.ToNumber<short>(28);
+        //auto four = header.ToNumber<int>(30);
+
+		if (soi == 0xD8FFll && marker == 0xE0FFll || marker == 0xE1FFll)
+            return JPG;
+        return DDS;
+    }
+
+    if (header[8] == 0x4F && header[9] == 0x67 && header[10] == 0x67 && header[11] == 0x53)
+        return OGG;
+    if (header[8] == 0x52 && header[9] == 0x49 && header[10] == 0x46 && header[11] == 0x46)
+        return WAV;
+
+    return UNKNOWN;
+}
+
+LOTRO_DAT::EXTENSION LOTRO_DAT::Subfile::ext() const {
+	return ext_;
+}
+
+bool LOTRO_DAT::Subfile::ExportFile(const char *filename) const {
+	try {
+		if (ext() == TXT)
+			return ExportAsTXT(filename);
+		if (ext() == JPG)
+			return ExportAsJPG(filename);
+		if (ext() == DDS)
+			return ExportAsDDS(filename);
+		if (ext() == OGG)
+			return ExportAsOGG(filename);
+		if (ext() == WAV)
+			return ExportAsWAV(filename);
+		if (ext() == FONT)
+			return ExportAsFont(filename);
+		if (ext() == UNKNOWN)
+			return ExportAsUnk(filename);
+	}
+	catch (...) {
+		fprintf(stderr, "Caught exception while unpacking the file with id %lld and offset %lld. Continuing...\n", file_id(), file_offset());
+		return false;
+	}
+    throw DatException("Bad Subfile::ExportFile() - got incorrect extension", EXPORT_EXCEPTION);
+}
+
+bool LOTRO_DAT::Subfile::ExportAsTXT(const char *filename) const {
+    throw DatException("ExportAsTXT function is not implemented yet", EXPORT_EXCEPTION);
+}
+
+bool LOTRO_DAT::Subfile::ExportAsJPG(const char *filename) const {
+    BinaryData data((unsigned) size1_ - 24);
+    dat_->ReadData(data, (unsigned) size1_ - 24, (unsigned) file_offset_ + 24);
+	return data.WriteToFile(std::string(filename) + ".jpg");
+}
+
+bool LOTRO_DAT::Subfile::ExportAsDDS(const char *filename) const {
+    BinaryData data((unsigned) size1_);
+    dat_->ReadData(data, (unsigned) size1_, (unsigned) file_offset_);
+
+    BinaryData ddsData((unsigned) size1_ - 24 + 128);
+    for (int i = 0; i < 128; i++)
+        ddsData[i] = 0;
+
+    memcpy(ddsData.data() + 128, data.data() + 24, (size_t) size1_ - 24);
+
+    ddsData[0] = 0x44; // D
+    ddsData[1] = 0x44; // D
+    ddsData[2] = 0x53; // S
+    ddsData[3] = 0x20;
+    ddsData[4] = 0x7C;
+
+    ddsData[8] = 7;
+    ddsData[9] = 0x10;
+
+    // width, height
+    ddsData[12] = data[12];
+    ddsData[13] = data[13];
+    ddsData[14] = data[14];
+    ddsData[15] = data[15];
+
+    ddsData[16] = data[8];
+    ddsData[17] = data[9];
+    ddsData[18] = data[10];
+    ddsData[19] = data[11];
+
+    long long compression = data.ToNumber<4>(0x10);
+
+    switch (compression) {
+        case 20:        // 14 00 00 00 - 888 (R8G8B8)
+            ddsData[0x4C] = 0x20;  // ?
+            ddsData[0x50] = 0x40;  // compressed or not
+
+            ddsData[0x58] = 0x18;  // bytes per pixel
+            ddsData[0x5E] = 0xFF;
+            ddsData[0x61] = 0xFF;
+            ddsData[0x64] = 0xFF;
+            break;
+        case 21:        // 15 00 00 00 - 8888 (R8G8B8A8)
+            ddsData[0x4C] = 0x20;  // ?
+            ddsData[0x50] = 0x40;  // compressed or not
+
+            ddsData[0x58] = 0x20;  // bytes per pixel
+            ddsData[0x5E] = 0xFF;
+            ddsData[0x61] = 0xFF;
+            ddsData[0x64] = 0xFF;
+            ddsData[0x6B] = 0xFF;
+            break;
+        case 28:        // 1C 00 00 00 - 332 (?)
+            ddsData[0x4C] = 0x20;  // ?
+            ddsData[0x50] = 0x40;  // compressed or not
+
+            ddsData[0x58] = 0x08;  // bytes per pixel
+            ddsData[0x5E] = 0xFF;
+            ddsData[0x61] = 0xFF;
+            ddsData[0x64] = 0xFF;
+            break;
+        case 827611204: // 44 58 54 31 - DXT1
+            ddsData[76] = 32;
+            ddsData[80] = 4;
+
+            ddsData[84] = 68;
+            ddsData[85] = 88;
+            ddsData[86] = 84;
+            ddsData[87] = 49;
+            break;
+        case 861165636: // 44 58 54 33 - DXT3
+            ddsData[22] = 1;
+            ddsData[76] = 32;
+            ddsData[80] = 4;
+
+            ddsData[84] = 68;
+            ddsData[85] = 88;
+            ddsData[86] = 84;
+            ddsData[87] = 51;
+
+            ddsData[108] = 8;
+            ddsData[109] = 16;
+            ddsData[110] = 64;
+            break;
+        case 894720068: // 44 58 54 35 - DXT5
+            ddsData[10] = 8;
+            ddsData[22] = 1;
+            ddsData[28] = 1;
+            ddsData[76] = 32;
+            ddsData[80] = 4;
+
+            ddsData[84] = 68;
+            ddsData[85] = 88;
+            ddsData[86] = 84;
+            ddsData[87] = 53;
+
+            ddsData[88] = 32;
+            ddsData[94] = 255;
+            ddsData[97] = 255;
+            ddsData[100] = 255;
+            ddsData[107] = 255;
+            ddsData[109] = 16;
+            break;
+    }
+	return ddsData.WriteToFile(std::string(filename) + ".dds");
+}
+
+bool LOTRO_DAT::Subfile::ExportAsOGG(const char *filename) const {
+    BinaryData data((unsigned) size1_ - 8);
+    dat_->ReadData(data, (unsigned) size1_ - 8, (unsigned) file_offset_ + 8);
+	return data.WriteToFile(std::string(filename) + ".ogg");
+}
+
+bool LOTRO_DAT::Subfile::ExportAsFont(const char *filename) const {
+    throw DatException("Bad Subfile::ExportAsFont() - this function hasn't been implemented yet", EXPORT_EXCEPTION);
+}
+
+bool LOTRO_DAT::Subfile::ExportAsWAV(const char *filename) const {
+    //throw DatException("Bad Subfile::ExportAsWAV() - this function hasn't been implemented yet", EXPORT_EXCEPTION);
+    BinaryData data((unsigned) size1_ - 8);
+    dat_->ReadData(data, (unsigned) size1_ - 8, (unsigned) file_offset_ + 8);
+	
+	return data.WriteToFile(std::string(filename) + ".wav");
+}
+
+bool LOTRO_DAT::Subfile::ExportAsUnk(const char *filename) const {
+    throw DatException("Bad Subfile::ExportAsUnk() - this function hasn't been implemented yet", EXPORT_EXCEPTION);
+}

+ 76 - 0
Subfile.h

@@ -0,0 +1,76 @@
+//
+// Created by Иван_Архипов on 01.11.2017.
+//
+
+#ifndef LOTRO_DAT_PATCHER_SUBFILE_H
+#define LOTRO_DAT_PATCHER_SUBFILE_H
+
+extern "C++"
+{
+namespace LOTRO_DAT
+{
+    class DatFile;
+
+    enum EXTENSION
+    {
+        TXT, JPG, DDS, WAV, OGG, FONT, UNKNOWN
+    };
+
+    class Subfile
+    {
+    public:
+        Subfile();
+
+        Subfile(DatFile *dat, long long unknown0, long long unknown1, long long file_id, long long file_offset, long long size1,
+                long long timestamp, long long version, long long size2);
+
+        bool ExportFile(const char* filename) const;
+
+        bool PatchFile();
+
+        long long unknown0() const;
+
+        long long unknown1() const;
+
+        long long file_id() const;
+
+        long long file_offset() const;
+
+        long long size1() const;
+
+        long long timestamp() const;
+
+        long long version() const;
+
+        long long size2() const;
+
+		EXTENSION ext() const;
+    
+	private:
+        long long unknown0_;
+        long long unknown1_;
+        long long file_id_;
+        long long file_offset_;
+        long long size1_;
+        long long timestamp_;
+        long long version_;
+        long long size2_;
+
+        DatFile *dat_;
+		EXTENSION ext_;
+
+
+		EXTENSION GetExtension() const;
+        bool ExportAsTXT(const char* filename) const;
+        bool ExportAsJPG(const char* filename) const;
+        bool ExportAsDDS(const char* filename) const;
+        bool ExportAsOGG(const char* filename) const;
+        bool ExportAsFont(const char* filename) const;
+        bool ExportAsWAV(const char* filename) const;
+        bool ExportAsUnk(const char* filename) const;
+    };
+}
+};
+
+
+#endif //LOTRO_DAT_PATCHER_SUBFILE_H

+ 139 - 0
test.cpp

@@ -0,0 +1,139 @@
+//
+// Created by Иван_Архипов on 30.10.2017.
+//
+#define _CRT_SECURE_NO_WARNINGS
+#include <iostream>
+#include <windows.h>
+
+#include "LotroDatPatcher.h"
+using namespace LOTRO_DAT;
+
+
+int main() {
+    std::ios_base::sync_with_stdio(0);
+    std::cin.tie(0);
+
+	std::string path = "E:\\SteamLibrary\\steamapps\\common\\Lord of the Rings Online\\";
+	std::string filename = "client_local_English.dat";
+	std::string output_dir = std::string("Data (") + filename + std::string(")\\");
+
+	std::string txt_dir = "txt\\";
+	std::string jpg_dir = "jpg\\";
+	std::string dds_dir = "dds\\";
+	std::string wav_dir = "wav\\";
+	std::string ogg_dir = "ogg\\";
+	std::string font_dir = "font\\";
+	std::string unk_dir = "unk\\";
+
+	int txt_all = 0, txt_suc = 0;
+	int jpg_all = 0, jpg_suc = 0;
+	int dds_all = 0, dds_suc = 0;
+	int wav_all = 0, wav_suc = 0;
+	int ogg_all = 0, ogg_suc = 0;
+	int font_all = 0, font_suc = 0;
+	int unk_all = 0, unk_suc = 0;
+
+	CreateDirectory(output_dir.c_str(), nullptr);
+	CreateDirectory((output_dir + txt_dir).c_str(), nullptr);
+	CreateDirectory((output_dir + jpg_dir).c_str(), nullptr);
+	CreateDirectory((output_dir + dds_dir).c_str(), nullptr);
+	CreateDirectory((output_dir + wav_dir).c_str(), nullptr);
+	CreateDirectory((output_dir + ogg_dir).c_str(), nullptr);
+	CreateDirectory((output_dir + font_dir).c_str(), nullptr);
+	CreateDirectory((output_dir + unk_dir).c_str(), nullptr);
+	
+
+	std::cerr << "Starting search...\n";
+	DatFile a(path + filename);
+    std::cerr << "Total files found: " << a.dictionary().size() << std::endl;
+
+	std::cerr << "Writing unordered dictionary:\n";
+	FILE *f;
+	fopen_s(&f, (output_dir + "dict.txt").c_str(), "w");
+	fprintf(f, "file_id | offset | extension\n");
+	for (auto i : a.dictionary()) {
+		std::string extension = "unk";
+		EXTENSION ext = i.second->ext();
+		if (ext == TXT) extension = "txt";
+		if (ext == JPG) extension = "jpg";
+		if (ext == DDS) extension = "dds";
+		if (ext == WAV) extension = "wav";
+		if (ext == OGG) extension = "ogg";
+		if (ext == FONT) extension = "font";
+		fprintf(f, "%lld %lld %s\n", i.second->file_id(), i.second->file_offset(), extension.c_str());
+	}
+	fclose(f);
+
+	std::cerr << "Beginning unpacking...\n";
+
+	long long all = a.dictionary().size();
+	long long counter = 0;
+    
+	for (auto i : a.dictionary())
+    {
+		EXTENSION ext = i.second->ext();
+        if (ext == TXT) {
+        	txt_all++;
+            //txt_suc += i.second->ExportFile((output_dir + txt_dir + std::to_string(i.second->file_id())).c_str());
+        }
+
+        if (ext == JPG) {
+            jpg_all++;
+            jpg_suc += i.second->ExportFile((output_dir + jpg_dir + std::to_string(i.second->file_id())).c_str());
+        }
+
+        if (ext == DDS) {
+            dds_all++;
+            dds_suc += i.second->ExportFile((output_dir + dds_dir + std::to_string(i.second->file_id())).c_str());
+        }
+
+        if (ext == WAV) {
+            wav_all++;
+            wav_suc += i.second->ExportFile((output_dir + wav_dir + std::to_string(i.second->file_id())).c_str());
+        }
+
+        if (ext == OGG) {
+            ogg_all++;
+            ogg_suc += i.second->ExportFile((output_dir + ogg_dir + std::to_string(i.second->file_id())).c_str());
+        }
+
+        if (ext == FONT) {
+            font_all++;
+            //font_suc += i.second->ExportFile((output_dir + font_dir + std::to_string(i.second->file_id())).c_str());
+        }
+
+        if (ext == UNKNOWN)
+        {
+            //cout << " - unk\n";
+            unk_all++;
+            //unk_suc += i.second->ExportFile((output_dir + unk_dir + std::to_string(i.second->file_id())).c_str());
+        }
+
+		counter++;
+		if ((counter - 1) * 100ll / all != counter * 100ll / all) {
+			fprintf(stderr, "Done: %lld%%\n", counter * 100ll / all);
+		}
+    }
+
+	std::cerr << "Total found:" << std::endl;
+	std::cerr << "Txt: " << txt_all << std::endl
+		<< "JPG: " << jpg_all << std::endl
+		<< "DDS: " << dds_all << std::endl
+		<< "WAV: " << wav_all << std::endl
+		<< "OGG: " << ogg_all << std::endl
+		<< "Fonts " << font_all << std::endl
+		<< "Unknown: " << unk_all << std::endl;
+
+	std::cerr << "Total exported:" << std::endl;
+	std::cerr << "Txt: " << txt_suc << std::endl
+		<< "JPG: " << jpg_suc << std::endl
+		<< "DDS: " << dds_suc << std::endl
+		<< "WAV: " << wav_suc << std::endl
+		<< "OGG: " << ogg_suc << std::endl
+		<< "Fonts " << font_suc << std::endl
+		<< "Unknown: " << unk_suc << std::endl;
+
+	std::cerr << "Success. Press enter to close";
+	system("pause");
+	return 0;
+}