|
@@ -3,16 +3,19 @@
|
|
|
|
|
|
|
|
|
#include "DatFile.h"
|
|
|
-
|
|
|
#include "BinaryData.h"
|
|
|
+
|
|
|
#include "DatException.h"
|
|
|
#include "SubDirectory.h"
|
|
|
#include "Subfile.h"
|
|
|
#include "SubfileData.h"
|
|
|
|
|
|
-#include "EasyLogging++/easylogging++.h"
|
|
|
+#include <EasyLogging++/easylogging++.h>
|
|
|
+#include <unistd.h>
|
|
|
+
|
|
|
#define ELPP_FEATURE_CRASH_LOG
|
|
|
INITIALIZE_EASYLOGGINGPP
|
|
|
+
|
|
|
#include <locale>
|
|
|
|
|
|
#ifdef WIN32
|
|
@@ -31,7 +34,8 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
el::Configurations defaultConf;
|
|
|
defaultConf.setToDefault();
|
|
|
- defaultConf.setGlobally(el::ConfigurationType::Format, "%datetime %level %fbase (line %line) : %msg (function: %func)");
|
|
|
+ defaultConf.setGlobally(el::ConfigurationType::Format,
|
|
|
+ "%datetime %level %fbase (line %line) : %msg (function: %func)");
|
|
|
defaultConf.setGlobally(el::ConfigurationType::ToFile, "true");
|
|
|
defaultConf.setGlobally(el::ConfigurationType::Filename, "dat_library.log");
|
|
|
defaultConf.setGlobally(el::ConfigurationType::ToStandardOutput, "false");
|
|
@@ -70,6 +74,7 @@ namespace LOTRO_DAT {
|
|
|
filename_ = "none";
|
|
|
|
|
|
DAT_RESULT result;
|
|
|
+ DAT_RESULT return_value = SUCCESS;
|
|
|
|
|
|
result = OpenDatFile(filename.c_str());
|
|
|
if (result != SUCCESS) {
|
|
@@ -77,39 +82,49 @@ namespace LOTRO_DAT {
|
|
|
CloseDatFile();
|
|
|
return result;
|
|
|
}
|
|
|
+ return_value = std::max(return_value, result);
|
|
|
|
|
|
result = ReadSuperBlock();
|
|
|
- if (result != SUCCESS) {
|
|
|
+ if (result <= 0) {
|
|
|
LOG(ERROR) << "Unable to read super block. Aborting.";
|
|
|
CloseDatFile();
|
|
|
return result;
|
|
|
}
|
|
|
+ return_value = std::max(return_value, result);
|
|
|
|
|
|
result = MakeDirectories();
|
|
|
- if (result != SUCCESS) {
|
|
|
+ if (result <= 0) {
|
|
|
LOG(ERROR) << "Unable to make directories. Aborting.";
|
|
|
CloseDatFile();
|
|
|
return result;
|
|
|
}
|
|
|
+ return_value = std::max(return_value, result);
|
|
|
|
|
|
result = MakeDictionary();
|
|
|
- if (result != SUCCESS) {
|
|
|
+ if (result <= 0) {
|
|
|
LOG(ERROR) << "Unable to make dictionary. Aborting.";
|
|
|
CloseDatFile();
|
|
|
return result;
|
|
|
}
|
|
|
+ return_value = std::max(return_value, result);
|
|
|
|
|
|
result = InitLocales();
|
|
|
- if (result != SUCCESS) {
|
|
|
+ if (result <= 0) {
|
|
|
LOG(ERROR) << "Unable to initialize locales. Aborting.";
|
|
|
CloseDatFile();
|
|
|
return result;
|
|
|
}
|
|
|
+ return_value = std::max(return_value, result);
|
|
|
|
|
|
+ if (return_value >= 2) {
|
|
|
+ LOG(WARNING) << "Dat file is corrupted. Trying to delete corrupted dictionary rows";
|
|
|
+ if (RepairDatFile() != SUCCESS)
|
|
|
+ return CRITICAL_DAT_ERROR;
|
|
|
+ }
|
|
|
LOG(INFO) << "File " << filename << " opened successfully!";
|
|
|
filename_ = filename;
|
|
|
dat_state_ = READY;
|
|
|
- return SUCCESS;
|
|
|
+ return return_value;
|
|
|
}
|
|
|
|
|
|
DAT_STATE DatFile::DatFileState() const {
|
|
@@ -222,8 +237,9 @@ namespace LOTRO_DAT {
|
|
|
int success = 0;
|
|
|
for (auto i : dictionary_) {
|
|
|
FILE_TYPE file_type = i.second->FileType();
|
|
|
- if (file_type == type) {
|
|
|
- success += (ExtractFile(i.second->file_id(), (path + std::to_string(i.second->file_id()))) == SUCCESS ? 1 : 0);
|
|
|
+ if (file_type == type) {
|
|
|
+ success += (ExtractFile(i.second->file_id(), (path + std::to_string(i.second->file_id()))) == SUCCESS
|
|
|
+ ? 1 : 0);
|
|
|
}
|
|
|
}
|
|
|
LOG(INFO) << "Successfully extracted " << success << " files";
|
|
@@ -246,7 +262,7 @@ namespace LOTRO_DAT {
|
|
|
int success = 0;
|
|
|
for (auto i : dictionary_) {
|
|
|
FILE_TYPE file_type = i.second->FileType();
|
|
|
- if (file_type == type) {
|
|
|
+ if (file_type == type) {
|
|
|
success += (ExtractFile(i.second->file_id(), db) == SUCCESS ? 1 : 0);
|
|
|
}
|
|
|
}
|
|
@@ -271,11 +287,16 @@ namespace LOTRO_DAT {
|
|
|
auto file_id = options["fid"].as<long long>();
|
|
|
|
|
|
if (dictionary_[file_id] == nullptr) {
|
|
|
- LOG(ERROR) << "Cannot patch file - there is no file in dictionary with file_id = " << file_id;
|
|
|
+ LOG(ERROR) << "Cannot patch file - there is no file in dictionary with file_id = " << file_id;
|
|
|
return NO_FILE_ERROR;
|
|
|
}
|
|
|
|
|
|
BinaryData old_data = GetFileData(dictionary_[file_id]);
|
|
|
+ if (old_data.Empty()) {
|
|
|
+ LOG(ERROR) << "GetFileData returned empty data. Aborting.";
|
|
|
+ return DAT_PATCH_FILE_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
data = dictionary_[file_id]->MakeForImport(old_data, SubfileData(data, u"", options));
|
|
|
|
|
|
try {
|
|
@@ -283,16 +304,18 @@ namespace LOTRO_DAT {
|
|
|
if (result != SUCCESS)
|
|
|
return result;
|
|
|
} catch (std::exception &e) {
|
|
|
- LOG(ERROR) << "Caught " << e.what() <<" exception.";
|
|
|
+ LOG(ERROR) << "Caught " << e.what() << " exception.";
|
|
|
return FAILED;
|
|
|
}
|
|
|
- LOG(DEBUG) << "Successfully patched file with filename = " << filename << " and id = " << options["fid"].as<long long>();
|
|
|
+ LOG(DEBUG) << "Successfully patched file with filename = " << filename << " and id = "
|
|
|
+ << options["fid"].as<long long>();
|
|
|
return SUCCESS;
|
|
|
}
|
|
|
|
|
|
|
|
|
DAT_RESULT DatFile::PatchFile(const SubfileData &data, bool rewrite_original) {
|
|
|
- LOG(DEBUG) << "Patching file with id = " << data.options["fid"].as<long long>() << (rewrite_original ? " REWRITING ORIGINAL FILE." : ".");
|
|
|
+ LOG(DEBUG) << "Patching file with id = " << data.options["fid"].as<long long>()
|
|
|
+ << (rewrite_original ? " REWRITING ORIGINAL FILE." : ".");
|
|
|
if (dat_state_ < READY) {
|
|
|
LOG(ERROR) << "Dat state isn't READY. Cannot patch.";
|
|
|
return INCORRECT_STATE_ERROR;
|
|
@@ -324,6 +347,11 @@ namespace LOTRO_DAT {
|
|
|
}
|
|
|
|
|
|
BinaryData old_data = GetFileData(file);
|
|
|
+ if (old_data.Empty()) {
|
|
|
+ LOG(ERROR) << "GetFileData returned empty data. Aborting.";
|
|
|
+ return DAT_PATCH_FILE_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
BinaryData patch_data = file->MakeForImport(old_data, data);
|
|
|
try {
|
|
|
DAT_RESULT result = ApplyFilePatch(file, patch_data, rewrite_original);
|
|
@@ -333,7 +361,8 @@ namespace LOTRO_DAT {
|
|
|
LOG(ERROR) << "Caught " << e.what() << " exception";
|
|
|
return FAILED;
|
|
|
}
|
|
|
- LOG(DEBUG) << "Patched successfully file " << data.options["fid"].as<long long>() << (rewrite_original ? " REWRITING ORIGINAL FILE." : ".");
|
|
|
+ LOG(DEBUG) << "Patched successfully file " << data.options["fid"].as<long long>()
|
|
|
+ << (rewrite_original ? " REWRITING ORIGINAL FILE." : ".");
|
|
|
return SUCCESS;
|
|
|
}
|
|
|
|
|
@@ -352,7 +381,7 @@ namespace LOTRO_DAT {
|
|
|
DAT_RESULT result = PatchFile(data);
|
|
|
if (result != SUCCESS)
|
|
|
LOG(ERROR) << "Cannot patch file" << data.options["fid"].as<long long>() << " continuing";
|
|
|
- data = db->GetNextFile();
|
|
|
+ data = db->GetNextFile();
|
|
|
}
|
|
|
DAT_RESULT result = CommitChanges();
|
|
|
if (result != SUCCESS)
|
|
@@ -400,11 +429,19 @@ namespace LOTRO_DAT {
|
|
|
try {
|
|
|
BinaryData mfile_id(20);
|
|
|
ReadData(mfile_id, 20, file->file_offset() + 8);
|
|
|
+ if (mfile_id.Empty()) {
|
|
|
+ LOG(ERROR) << "Error while reading file " << file->file_id() << " header (offset = "
|
|
|
+ << file->file_offset() << "); Aborting.";
|
|
|
+ return BinaryData(0);
|
|
|
+ }
|
|
|
+ if (!mfile_id.CheckCompression() && file->file_id() != mfile_id.ToNumber<4>(0)) {
|
|
|
+ LOG(ERROR) << "Bad DatFile::GetFileData() - file_id in Subfile ("
|
|
|
+ << file->file_id()
|
|
|
+ << ") doesn't match to file_id (" << mfile_id.ToNumber<4>(0) << ")in DatFile.";
|
|
|
+ return BinaryData(0);
|
|
|
+ }
|
|
|
|
|
|
- if (!mfile_id.CheckCompression() && file->file_id() != mfile_id.ToNumber<4>(0))
|
|
|
- throw DatException("Bad DatFile::GetFileData() - file_id in Subfile doesn't match to file_id in DatFile.", READ_EXCEPTION);
|
|
|
-
|
|
|
- BinaryData data((unsigned)(file->file_size() + (8 - offset)));
|
|
|
+ BinaryData data((unsigned) (file->file_size() + (8 - offset)));
|
|
|
if (file->block_size() >= file->file_size() + 8) {
|
|
|
ReadData(data, file->file_size() + (8 - offset), file->file_offset() + offset);
|
|
|
return data;
|
|
@@ -417,22 +454,24 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
long long current_block_size = file->block_size() - offset - 8 * fragments_number;
|
|
|
|
|
|
- ReadData(data, current_block_size , file->file_offset() + offset);
|
|
|
+ ReadData(data, current_block_size, file->file_offset() + offset);
|
|
|
|
|
|
BinaryData FragmentsDictionary(8 * unsigned(fragments_number));
|
|
|
- ReadData(FragmentsDictionary, 8 * unsigned(fragments_number), file->file_offset() + file->block_size() - 8 * fragments_number);
|
|
|
+ ReadData(FragmentsDictionary, 8 * unsigned(fragments_number),
|
|
|
+ file->file_offset() + file->block_size() - 8 * fragments_number);
|
|
|
|
|
|
|
|
|
for (long long i = 0; i < fragments_number; i++) {
|
|
|
long long fragment_size = FragmentsDictionary.ToNumber<4>(8 * i);
|
|
|
long long fragment_offset = FragmentsDictionary.ToNumber<4>(8 * i + 4);
|
|
|
- ReadData(data, std::min(fragment_size, file->file_size() - current_block_size), fragment_offset, current_block_size );
|
|
|
+ ReadData(data, std::min(fragment_size, file->file_size() - current_block_size), fragment_offset,
|
|
|
+ current_block_size);
|
|
|
current_block_size += fragment_size;
|
|
|
}
|
|
|
LOG(DEBUG) << "Successfully got file " << file->file_id() << " data";
|
|
|
return data;
|
|
|
} catch (std::exception &e) {
|
|
|
- LOG(ERROR) << "Caught " << e.what() << "exception";
|
|
|
+ LOG(ERROR) << "Caught " << e.what() << " exception";
|
|
|
}
|
|
|
return BinaryData(0);
|
|
|
}
|
|
@@ -491,7 +530,9 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
if (file_size_ != size1) {
|
|
|
LOG(ERROR) << "variable at 0x148 position is not equal to .dat file size!";
|
|
|
- return INCORRECT_SUPERBLOCK_ERROR;
|
|
|
+ file_size_ = size1;
|
|
|
+ dat_state_ = SUCCESS_SUPERBLOCK;
|
|
|
+ return CORRUPTED_FILE_WARNING;
|
|
|
}
|
|
|
|
|
|
dat_state_ = SUCCESS_SUPERBLOCK;
|
|
@@ -533,17 +574,22 @@ namespace LOTRO_DAT {
|
|
|
DAT_RESULT DatFile::ReadData(BinaryData &data, long long size, long long offset, long long data_offset) {
|
|
|
if (dat_state_ == CLOSED) {
|
|
|
LOG(ERROR) << "Dat state is CLOSED. Cannot read data.";
|
|
|
- throw DatException("", READ_EXCEPTION);
|
|
|
+ data = BinaryData(0);
|
|
|
+ return INIT_ERROR;
|
|
|
}
|
|
|
|
|
|
if (data_offset + size > data.size()) {
|
|
|
- LOG(ERROR) << "Trying to read more than BinaryData size: Reading " << size << " bytes from " << offset << " position.";
|
|
|
- throw DatException("", READ_EXCEPTION);
|
|
|
+ LOG(ERROR) << "Trying to read more than BinaryData size: Reading " << size << " bytes from " << offset
|
|
|
+ << " position.";
|
|
|
+ data = BinaryData(0);
|
|
|
+ return DAT_READ_ERROR;
|
|
|
}
|
|
|
|
|
|
if (offset + size > file_size_) {
|
|
|
- LOG(ERROR) << "Trying to read more than DatFile size elapsed: Reading " << size << " bytes from " << offset << " position.";
|
|
|
- throw DatException("", READ_EXCEPTION);
|
|
|
+ LOG(ERROR) << "Trying to read more than DatFile size elapsed: Reading " << size << " bytes from " << offset
|
|
|
+ << " position.";
|
|
|
+ data = BinaryData(0);
|
|
|
+ return DAT_READ_ERROR;
|
|
|
}
|
|
|
|
|
|
fseek(file_handler_, offset, SEEK_SET);
|
|
@@ -557,9 +603,11 @@ namespace LOTRO_DAT {
|
|
|
return INCORRECT_STATE_ERROR;
|
|
|
}
|
|
|
|
|
|
- fseek(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);
|
|
|
+ fseek(file_handler_, offset, SEEK_SET);
|
|
|
+ if (data_offset + size > data.size()) {
|
|
|
+ LOG(ERROR) << "Trying to write more than BinaryData size";
|
|
|
+ return DAT_WRITE_ERROR;
|
|
|
+ }
|
|
|
|
|
|
fwrite(data.data() + data_offset, unsigned(size), 1, file_handler_);
|
|
|
return SUCCESS;
|
|
@@ -736,15 +784,18 @@ namespace LOTRO_DAT {
|
|
|
pending_patch_.clear();
|
|
|
|
|
|
current_locale_ = ORIGINAL;
|
|
|
- filename_ = "none";
|
|
|
|
|
|
- if (file_handler_ != nullptr)
|
|
|
+ if (file_handler_ != nullptr) {
|
|
|
fclose(file_handler_);
|
|
|
+ }
|
|
|
|
|
|
delete root_directory_;
|
|
|
|
|
|
- patched_list.clear();
|
|
|
dictionary_.clear();
|
|
|
+ patched_list.clear();
|
|
|
+
|
|
|
+ truncate64(filename_.c_str(), file_size_);
|
|
|
+ filename_ = "none";
|
|
|
|
|
|
dat_state_ = CLOSED;
|
|
|
LOG(INFO) << "File closed successfully.";
|
|
@@ -761,13 +812,13 @@ namespace LOTRO_DAT {
|
|
|
dicts_data = GetFileData(dictionary_[2013266257]);
|
|
|
|
|
|
if (dicts_data.size() < 29) {
|
|
|
- LOG(WARNING) << "locales file is empty.. Initialising locale dicts as empty";
|
|
|
- LOG(INFO) << "Could't find locales file... Continuing without them";
|
|
|
+ LOG(WARNING) << "Locales file is empty.. Initialising locale dicts as empty";
|
|
|
+ LOG(INFO) << "Could't find locales file or it's corrupted/empty... Continuing without locales";
|
|
|
return SUCCESS;
|
|
|
}
|
|
|
|
|
|
BinaryData hi_data = dicts_data.CutData(14, 29) + BinaryData("\0", 1);
|
|
|
- std::string hi = std::string((char*)(hi_data.data()));
|
|
|
+ std::string hi = std::string((char *) (hi_data.data()));
|
|
|
LOG(DEBUG) << "hi info is " << hi;
|
|
|
|
|
|
if (hi != "Hi from Gi1dor!") {
|
|
@@ -778,7 +829,7 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
int offset = 29;
|
|
|
BinaryData current_locale_data = dicts_data.CutData(offset, offset + 4) + BinaryData("\0", 1);
|
|
|
- std::string locale((char*)(current_locale_data.data()));
|
|
|
+ std::string locale((char *) (current_locale_data.data()));
|
|
|
offset += 4;
|
|
|
LOG(DEBUG) << "current locale:" << locale;
|
|
|
|
|
@@ -855,6 +906,38 @@ namespace LOTRO_DAT {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ bool DatFile::CorrectSubfile(Subfile *file) {
|
|
|
+ BinaryData mfile_id(20);
|
|
|
+ ReadData(mfile_id, 20, file->file_offset() + 8);
|
|
|
+ if (mfile_id.Empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return mfile_id.CheckCompression() || file->file_id() == mfile_id.ToNumber<4>(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ DAT_RESULT DatFile::RepairDatFile() {
|
|
|
+ for (auto file : dictionary_) {
|
|
|
+ auto subfile = file.second;
|
|
|
+ auto file_id = file.first;
|
|
|
+
|
|
|
+ if (CorrectSubfile(subfile))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (orig_dict_.count(file_id) == 0 || subfile->file_offset() == orig_dict_[file_id]->file_offset())
|
|
|
+ return CRITICAL_DAT_ERROR;
|
|
|
+
|
|
|
+ dictionary_[file_id]->file_offset_ = orig_dict_[file_id]->file_offset_;
|
|
|
+ dictionary_[file_id]->file_size_ = orig_dict_[file_id]->file_size_;
|
|
|
+ dictionary_[file_id]->block_size_ = orig_dict_[file_id]->block_size_;
|
|
|
+ dictionary_[file_id]->timestamp_ = orig_dict_[file_id]->timestamp_;
|
|
|
+ dictionary_[file_id]->version_ = orig_dict_[file_id]->version_;
|
|
|
+ patch_dict_.erase(file_id);
|
|
|
+ orig_dict_.erase(file_id);
|
|
|
+ }
|
|
|
+ return SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
DAT_RESULT DatFile::SetLocale(LOCALE locale) {
|
|
|
LOG(INFO) << "Setting locale to " << (locale == PATCHED ? " PATCHED" : " ORIGINAL");
|
|
|
if (dat_state_ < READY) {
|
|
@@ -876,7 +959,8 @@ namespace LOTRO_DAT {
|
|
|
continue;
|
|
|
}
|
|
|
if (dictionary_[file.first]->MakeHeaderData().CutData(8, 16) ==
|
|
|
- file.second->MakeHeaderData().CutData(8, 16) || inactive_categories.count(orig_dict_[file.first]->category) != 0)
|
|
|
+ file.second->MakeHeaderData().CutData(8, 16) ||
|
|
|
+ inactive_categories.count(orig_dict_[file.first]->category) != 0)
|
|
|
continue;
|
|
|
|
|
|
long long file_id = file.first;
|
|
@@ -909,13 +993,13 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
for (auto i : dictionary_) {
|
|
|
long long file_id = i.first;
|
|
|
- Subfile* subfile = i.second;
|
|
|
+ Subfile *subfile = i.second;
|
|
|
if (inactive_categories.count(subfile->category) > 0)
|
|
|
continue;
|
|
|
if (patch_dict_.count(file_id) > 0
|
|
|
&& (subfile->file_size() != patch_dict_[file_id]->file_size()
|
|
|
- || subfile->file_offset() != patch_dict_[file_id]->file_offset()
|
|
|
- || subfile->block_size() != patch_dict_[file_id]->block_size())) {
|
|
|
+ || subfile->file_offset() != patch_dict_[file_id]->file_offset()
|
|
|
+ || subfile->block_size() != patch_dict_[file_id]->block_size())) {
|
|
|
orig_dict_.erase(file_id);
|
|
|
patch_dict_.erase(file_id);
|
|
|
pending_patch_.insert(file_id);
|
|
@@ -1075,23 +1159,24 @@ namespace LOTRO_DAT {
|
|
|
return SUCCESS;
|
|
|
}
|
|
|
|
|
|
- const std::set<long long>& DatFile::GetInactiveCategoriesList() {
|
|
|
+ const std::set<long long> &DatFile::GetInactiveCategoriesList() {
|
|
|
return inactive_categories;
|
|
|
}
|
|
|
|
|
|
bool DatFile::CheckIfNotPatched() {
|
|
|
- LOG(INFO) << "DatFile " << (patch_dict_.empty() ? "HASN'T " : "HAS already") << " been patched by LEGACY launcher!";
|
|
|
+ LOG(INFO) << "DatFile " << (patch_dict_.empty() ? "HASN'T " : "HAS already")
|
|
|
+ << " been patched by LEGACY launcher!";
|
|
|
return patch_dict_.empty();
|
|
|
}
|
|
|
|
|
|
bool DatFile::CheckIfPatchedByOldLauncher() {
|
|
|
- LOG(INFO) << "DatFile " << (dictionary_.count(620750000) == 0 ? "HASN'T " : "HAS already") << " been patched by OLD LAUNCHER!";
|
|
|
+ LOG(INFO) << "DatFile " << (dictionary_.count(620750000) == 0 ? "HASN'T " : "HAS already")
|
|
|
+ << " been patched by OLD LAUNCHER!";
|
|
|
return dictionary_.count(620750000) > 0;
|
|
|
}
|
|
|
|
|
|
const std::string &DatFile::filename() const {
|
|
|
return filename_;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
}
|