// // Created by kikab on 04.06.2018. // #include "BinaryData.h" #include "DatFile.h" #include "DatSubsystems/DatIO.h" #include "EasyLogging++/easylogging++.h" #include "SubDirectory.h" #include "SubFile.h" #include #include #include #include #include #ifdef WIN32 #define fseek _fseeki64 #define ftell _ftelli64 #endif extern "C++" { namespace LOTRO_DAT { DatIO::DatIO(DatFile *datFile) : dat(datFile), file_handler_(nullptr), filename_(), actual_dat_size_(0) { } //------------------------------------------------// // INIT SECTION //------------------------------------------------// DatOperationResult<> DatIO::Init(const std::string &filename) { LOG(INFO) << "Initializing IO: " << filename; filename_ = filename; auto result = OpenDatFile(); if (result.result == ERROR) { LOG(ERROR) << "Error in OpenDatFile: " << result.msg; DeInit(); return result; } result = ReadSuperBlock(); if (result.result == ERROR) { LOG(ERROR) << "Error in ReadSuperBlock: " << result.msg; DeInit(); return result; } LOG(INFO) << "Successfull initializing IO: " << filename; return DatOperationResult<>(SUCCESS, "DatIO initialized successfully"); } DatOperationResult<> DatIO::OpenDatFile() { LOG(DEBUG) << "DatIO: Started opening DatFile"; file_handler_ = fopen(filename_.c_str(), "r+b"); if (file_handler_ == nullptr) { LOG(ERROR) << "DatIO: Unable to open file " << filename_ << ". Presumably - no file found..."; return DatOperationResult<>(ERROR, std::string("Unable to locate and open file " + filename_)); } fseek(file_handler_, 0, SEEK_END); actual_dat_size_ = ftell(file_handler_); fseek(file_handler_, 0, SEEK_SET); LOG(INFO) << "DatIO: file opened"; return DatOperationResult<>(SUCCESS, std::string("Successfully opened file " + filename_)); } DatOperationResult<> DatIO::ReadSuperBlock() { LOG(INFO) << "DatIO: Started reading superblock"; BinaryData data(1024); ReadData(data, 1024); constant1 = data.ToNumber<4>(0x100); constant2 = data.ToNumber<4>(0x140); version1 = data.ToNumber<4>(0x14C); file_size = data.ToNumber<4>(0x148); version2 = data.ToNumber<4>(0x150); fragmentation_journal_offset = data.ToNumber<4>(0x154); fragmentation_journal_end = data.ToNumber<4>(0x158); fragmentation_journal_size = data.ToNumber<4>(0x15C); root_directory_offset = data.ToNumber<4>(0x160); free_dat_size = data.ToNumber<4>(0x19C); if (constant1 != 0x4C5000) { LOG(ERROR) << "Variable at position 0x100 is not equal to .dat file constant!"; return DatOperationResult<>(ERROR, std::string("Variable at position 0x100 is not equal to .dat file constant!")); } if (constant2 != 0x5442) { LOG(ERROR) << "Variable at position 0x140 is not equal to .dat file constant!"; return DatOperationResult<>(ERROR, std::string("Variable at position 0x140 is not equal to .dat file constant!")); } if (file_size != actual_dat_size_) { LOG(INFO) << "Variable at 0x148 position is not equal to .dat file size!"; //return CORRUPTED_FILE_WARNING; } LOG(INFO) << "DatIO: Superblock read successfully"; return DatOperationResult<>(SUCCESS, std::string("Superblock read successfully.")); } //------------------------------------------------// // PUBLIC READ/WRITE SECTION //------------------------------------------------// DatOperationResult<> DatIO::ReadData(BinaryData &data, long long size, long long offset, long long data_offset) const { if (file_handler_ == nullptr) { LOG(ERROR) << "IOError: file handler is null pointer on reading data."; data = BinaryData(0); return DatOperationResult<>(ERROR, "IOError: file handler is null pointer on reading data."); } if (data_offset + size > data.size()) { LOG(ERROR) << "Trying to read more than BinaryData size: Reading " << size << " bytes from " << offset << " position."; data = BinaryData(0); return DatOperationResult<>(ERROR, "IOError: Parameters: offset, size are out of range."); } if (offset + size > actual_dat_size_) { LOG(ERROR) << "Trying to read more than DatFile size elapsed: Reading " << size << " bytes from " << offset << " position."; data = BinaryData(0); return DatOperationResult<>(ERROR, "IOError: Reading from end of file."); } if (offset != ftell(file_handler_)) fseek(file_handler_, offset, SEEK_SET); fread(data.data() + data_offset, unsigned(size), 1, file_handler_); return DatOperationResult<>(SUCCESS, "Read data successful."); } DatOperationResult<> DatIO::WriteData(const BinaryData &data, long long size, long long offset, long long data_offset) { if (file_handler_ == nullptr) { LOG(ERROR) << "IOError: file handler is null pointer on writing data"; return DatOperationResult<>(ERROR, "IOError: file handler is null pointer on writing data."); } if (offset != ftell(file_handler_)) fseek(file_handler_, offset, SEEK_SET); if (data_offset + size > data.size()) { LOG(ERROR) << "Trying to write more than BinaryData size"; return DatOperationResult<>(ERROR, "IOError: writing more than BinaryData size."); } fwrite(data.data() + data_offset, unsigned(size), 1, file_handler_); if (offset + size > actual_dat_size_) { actual_dat_size_ = offset + size; } return DatOperationResult<>(SUCCESS, "Data writing successful."); } //------------------------------------------------// // DEINIT SECTION //------------------------------------------------// DatOperationResult<> DatIO::DeInit() { if (file_handler_ != nullptr) { fclose(file_handler_); } filename_ = "none"; file_handler_ = nullptr; constant1 = 0; constant2 = 0; file_size = 0; version1 = 0; version2 = 0; fragmentation_journal_size = 0; fragmentation_journal_end = 0; root_directory_offset = 0; fragmentation_journal_offset = 0; return DatOperationResult<>(SUCCESS, "File deinitialisation successfull"); } DatOperationResult<> DatIO::ModifyFragmentationJournal() { if (fragmentation_journal_size == 0) return DatOperationResult<>(SUCCESS, "DatIO: Fragmentation journal is empty. Nothing to do."); LOG(DEBUG) << "Modifying fragmentation journal"; BinaryData data(4); ReadData(data, 4, fragmentation_journal_offset + 8 * fragmentation_journal_size); LOG(INFO) << "FREE_SIZE BLOCK = " << data.ToNumber<4>(0); long long free_size = data.ToNumber<4>(0); long long free_offset = file_size; BinaryData nulldata = BinaryData(unsigned(free_size)); WriteData(nulldata, nulldata.size(), file_size); file_size += nulldata.size(); WriteData(BinaryData::FromNumber<4>(free_size), 4, fragmentation_journal_offset + 8 * fragmentation_journal_size); WriteData(BinaryData::FromNumber<4>(free_offset), 4, fragmentation_journal_offset + 8 * fragmentation_journal_size + 4); //nulldata = BinaryData(8); //WriteData(nulldata, nulldata.size(), fragmentation_journal_offset + 16); LOG(DEBUG) << "Finished modifying fragmentation journal"; return DatOperationResult<>(SUCCESS, "Fragmentation journal patched successfully!"); } DatOperationResult<> DatIO::UpdateHeader() { LOG(DEBUG) << "Updating header"; WriteData(BinaryData::FromNumber<4>(constant1), 4, 0x100); WriteData(BinaryData::FromNumber<4>(constant2), 4, 0x140); //WriteData(BinaryData::FromNumber<4>( 0 ), 4, 0x144); WriteData(BinaryData::FromNumber<4>(file_size), 4, 0x148); WriteData(BinaryData::FromNumber<4>(version1), 4, 0x14C); WriteData(BinaryData::FromNumber<4>(version2), 4, 0x150); WriteData(BinaryData::FromNumber<4>(fragmentation_journal_offset), 4, 0x154); WriteData(BinaryData::FromNumber<4>(fragmentation_journal_end), 4, 0x158); WriteData(BinaryData::FromNumber<4>(fragmentation_journal_size), 4, 0x15C); WriteData(BinaryData::FromNumber<4>(root_directory_offset), 4, 0x160); WriteData(BinaryData::FromNumber<4>(free_dat_size), 4, 0x19C); LOG(DEBUG) << "Finished updating header"; return DatOperationResult<>(SUCCESS, "File header patched successfully");; } DatOperationResult DatIO::GetActualDatSize() { return DatOperationResult(actual_dat_size_, SUCCESS); } DatOperationResult DatIO::GetFilename() { return DatOperationResult(filename_, SUCCESS); } } }