|
@@ -0,0 +1,323 @@
|
|
|
|
+//
|
|
|
|
+// Created by Иван_Архипов on 31.10.2017.
|
|
|
|
+//
|
|
|
|
+
|
|
|
|
+#include "DatFile.h"
|
|
|
|
+
|
|
|
|
+#include "../BinaryData/BinaryData.h"
|
|
|
|
+#include "../Common/DatException.h"
|
|
|
|
+#include "../SubDirectory/SubDirectory.h"
|
|
|
|
+#include "../Subfiles/Subfile.h"
|
|
|
|
+
|
|
|
|
+#include <locale>
|
|
|
|
+
|
|
|
|
+extern "C++"
|
|
|
|
+{
|
|
|
|
+namespace LOTRO_DAT {
|
|
|
|
+ DatFile::DatFile() {
|
|
|
|
+ dat_state_ = CLOSED;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ DatFile::DatFile(const char *filename, int dat_id) {
|
|
|
|
+ dat_id_ = dat_id;
|
|
|
|
+ dat_state_ = CLOSED;
|
|
|
|
+ OpenDatFile(filename);
|
|
|
|
+ ReadSuperBlock();
|
|
|
|
+ MakeDirectories();
|
|
|
|
+ try {
|
|
|
|
+ MakeDictionary();
|
|
|
|
+ } catch (...) {
|
|
|
|
+ fprintf(stderr, "Unable to make dictionary!! Unable to init DatFile!!!");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ 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, int dat_id) {
|
|
|
|
+ dat_id_ = dat_id;
|
|
|
|
+ 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_;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Extracts file with file_id.
|
|
|
|
+ /// If path is undefined then it will be recognised as current working directory
|
|
|
|
+ /// Output file path consists of "path + file_id + file_extension";
|
|
|
|
+ /// NOTICE: The directory, mentioned in "std::string path" variable SHOULD BE ALREADY CREATED;
|
|
|
|
+ /// Otherwise DatException() will be thrown.
|
|
|
|
+ /// Returns true, if file was successfully extracted;
|
|
|
|
+ /// Throws DatException() if undefined behaviour happened
|
|
|
|
+
|
|
|
|
+ bool DatFile::ExtractFile(long long file_id, const std::string path) {
|
|
|
|
+ if (dat_state_ != READY) {
|
|
|
|
+ throw DatException("Bad DatFile::ExtractFile() - invalid DatFile state!", EXPORT_EXCEPTION);
|
|
|
|
+ }
|
|
|
|
+ return dictionary_[file_id]->ExportFile(path.c_str());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Extracts file with file_id to database "db".
|
|
|
|
+ /// DATABASE SHOULD BE ALREADY CREATED; Otherwise DatException will be called.
|
|
|
|
+ /// NOTICE: The directory, mentioned in "std::string path" variable SHOULD BE ALREADY CREATED;
|
|
|
|
+ /// Otherwise DatException() will be thrown.
|
|
|
|
+ /// Returns true, if file was successfully extracted;
|
|
|
|
+ /// Throws DatException() if undefined behaviour happened
|
|
|
|
+
|
|
|
|
+ bool DatFile::ExtractFile(long long file_id, Database *db) {
|
|
|
|
+ if (dat_state_ != READY) {
|
|
|
|
+ throw DatException("Bad DatFile::ExtractFile() - invalid DatFile state!", EXPORT_EXCEPTION);
|
|
|
|
+ }
|
|
|
|
+ return dictionary_[file_id]->ExportFile(db);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Extracts all files with specific type to "path + type + file_id + extension" files;
|
|
|
|
+ /// If path is undefined then it will be recognised as current working directory
|
|
|
|
+ /// NOTICE: The directory, mentioned in "std::string path" variable SHOULD BE ALREADY CREATED;
|
|
|
|
+ /// Otherwise DatException() will be thrown.
|
|
|
|
+ /// Returns number of successfully extracted files
|
|
|
|
+ /// Throws DatException() if undefined behaviour happened
|
|
|
|
+
|
|
|
|
+ int DatFile::ExtractAllFilesByType(FILE_TYPE type, std::string path) {
|
|
|
|
+ if (dat_state_ != READY) {
|
|
|
|
+ throw DatException("Bad DatFile::ExtractAllFilesByType() - invalid DatFile state!", EXPORT_EXCEPTION);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int success = 0;
|
|
|
|
+ for (auto i : dictionary_) {
|
|
|
|
+ FILE_TYPE ext = i.second->ext();
|
|
|
|
+ if (ext == type) {
|
|
|
|
+ success += i.second->ExportFile((path + std::to_string(i.second->file_id())).c_str());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return success;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Extracts all files with specific type to database "db";
|
|
|
|
+ /// DATABASE SHOULD BE ALREADY CREATED; Otherwise DatException will be called.
|
|
|
|
+ /// Returns number of successfully extracted files
|
|
|
|
+ /// Throws DatException() if undefined behaviour happened
|
|
|
|
+
|
|
|
|
+ int DatFile::ExtractAllFilesByType(FILE_TYPE type, Database *db) {
|
|
|
|
+ if (dat_state_ != READY) {
|
|
|
|
+ throw DatException("Bad DatFile::ExtractAllFilesByType() - invalid DatFile state!", EXPORT_EXCEPTION);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int success = 0;
|
|
|
|
+ for (auto i : dictionary_) {
|
|
|
|
+ FILE_TYPE ext = i.second->ext();
|
|
|
|
+ if (ext == type) {
|
|
|
|
+ success += i.second->ExportFile(db);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return success;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Patches .dat with text file with specific file_id, gossip_id and values, equal to values in text database.
|
|
|
|
+ /// std::string text contains of text data, surrounded by '[' and ']', where <--DO_NOT_TOUCH!--> represents
|
|
|
|
+ /// position of variables in order, described in args_order.
|
|
|
|
+ /// std::string args_order contains of numbers, divided by '-'. Ex. "1-2-3-4". There should be not less numbers, than
|
|
|
|
+ /// <--DO_NOT_TOUCH!--> pieces in text variable/
|
|
|
|
+ /// std::string args contains of numbers, divided by ' '. These numbers are references to variables. There should
|
|
|
|
+ /// be not less references, than the biggest number in args_order
|
|
|
|
+ /// Returns true if text was succesfully patched.
|
|
|
|
+ /// Throws DatException() if undefined behaviour happened;
|
|
|
|
+
|
|
|
|
+ bool DatFile::PatchTextFile(long long file_id, long long gossip_id, std::string text, std::string args_order,
|
|
|
|
+ std::string args) {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Patches .dat with text file with specific file_id and gossip_id.
|
|
|
|
+ /// All text file data is got from database.
|
|
|
|
+ /// Returns true if text file was successfully patched.
|
|
|
|
+ /// Throws DatException() if undefined behaviour happened;
|
|
|
|
+
|
|
|
|
+ bool DatFile::PatchTextFile(long long file_id, long long gossip_id, Database *db) {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Patches .dat with binary file with specific file_id.
|
|
|
|
+ /// All file data is got from file in "file_path".
|
|
|
|
+ /// Returns true if file was successfully patched.
|
|
|
|
+ /// Throws DatException() if undefined behaviour happened;
|
|
|
|
+
|
|
|
|
+ bool DatFile::PatchBinaryFile(long long file_id, std::string file_path) {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Patches .dat with binary file with specific file_id.
|
|
|
|
+ /// All file data is got from Database "db".
|
|
|
|
+ /// Returns true if file was successfully patched.
|
|
|
|
+ /// Throws DatException() if undefined behaviour happened;
|
|
|
|
+
|
|
|
|
+ bool DatFile::PatchBinaryFile(long long file_id, Database *db) {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+ try {
|
|
|
|
+ root_directory_->MakeDictionary(dictionary_);
|
|
|
|
+ } catch (...) {
|
|
|
|
+ fprintf(stderr, "Bad DatFile::MakeDictionary() - File is corrupted?\n");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ 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_);
|
|
|
|
+ data.CheckCompression();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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_;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void DatFile::WriteUnorderedDictionary(std::string path) const {
|
|
|
|
+ FILE *f;
|
|
|
|
+ fopen_s(&f, (path + "dict.txt").c_str(), "w");
|
|
|
|
+ fprintf(f, "file_id offset size size2 extension\n");
|
|
|
|
+ for (auto i : dictionary_) {
|
|
|
|
+ std::string extension = "unk";
|
|
|
|
+ FILE_TYPE ext = i.second->ext();
|
|
|
|
+ if (ext == TEXT) 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 %lld %lld %s\n", i.second->file_id(), i.second->file_offset(), i.second->file_size(),
|
|
|
|
+ i.second->block_size(), extension.c_str());
|
|
|
|
+ }
|
|
|
|
+ fclose(f);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ long long DatFile::files_number() const {
|
|
|
|
+ return dictionary_.size();
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+}
|