|
@@ -16,12 +16,10 @@
|
|
|
extern "C++"
|
|
|
{
|
|
|
namespace LOTRO_DAT {
|
|
|
- DatFile::DatFile() : dat_state_(CLOSED), patched_(false) {}
|
|
|
+ DatFile::DatFile() : dat_state_(CLOSED) {}
|
|
|
|
|
|
- DatFile::DatFile(const char *filename, int dat_id)
|
|
|
- : dat_id_(dat_id)
|
|
|
- , dat_state_(CLOSED)
|
|
|
- , patched_(false) {
|
|
|
+ DatFile::DatFile(const char *filename, int dat_id) : dat_id_(dat_id) , dat_state_(CLOSED) {
|
|
|
+ filename_ = std::string(filename);
|
|
|
OpenDatFile(filename);
|
|
|
ReadSuperBlock();
|
|
|
MakeDirectories();
|
|
@@ -33,28 +31,32 @@ namespace LOTRO_DAT {
|
|
|
fprintf(stderr, "Unable to make dictionary!! Unable to init DatFile!!!");
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
+ InitLocale(PATCHED, (std::string(filename) + std::string("patched.dbgm")).c_str());
|
|
|
+ InitLocale(ORIGINAL,(std::string(filename) + std::string("original.dbgm")).c_str());
|
|
|
+
|
|
|
+ FILE *locale = fopen((std::string(filename) + ".dbgm").c_str(), "r");
|
|
|
+ if (locale == nullptr)
|
|
|
+ current_locale_ = ORIGINAL;
|
|
|
+ else {
|
|
|
+ auto loc = new char[10];
|
|
|
+ fscanf(locale, "%s", loc);
|
|
|
+ if (std::string(loc) == "RU")
|
|
|
+ current_locale_ = PATCHED;
|
|
|
+ if (std::string(loc) == "EN")
|
|
|
+ current_locale_ = ORIGINAL;
|
|
|
+ }
|
|
|
+
|
|
|
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 (patched_) {
|
|
|
- std::cout << "There are some updated files. Rewriting dictionary..." << std::endl << std::flush;
|
|
|
- UpdateHeader();
|
|
|
- std::cout << "Updated header..." << std::endl << std::flush;
|
|
|
- UpdateSubdirectories();
|
|
|
- std::cout << "Updated subdirectories..." << std::endl << std::flush;
|
|
|
- std::cout << "Changed " << patched_list.size() << " files..." << std::endl << std::flush;
|
|
|
- for (auto i : patched_list)
|
|
|
- delete i.second;
|
|
|
- }
|
|
|
+ CommitChanges();
|
|
|
+ CommitLocales();
|
|
|
|
|
|
if (file_handler_ != nullptr)
|
|
|
fclose(file_handler_);
|
|
@@ -71,7 +73,7 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
|
|
|
bool DatFile::ExtractFile(long long file_id, const std::string &path) {
|
|
|
- if (dat_state_ != READY) {
|
|
|
+ if (dat_state_ < READY) {
|
|
|
throw DatException("Bad DatFile::ExtractFile() - invalid DatFile state!", EXPORT_EXCEPTION);
|
|
|
}
|
|
|
BinaryData file_data;
|
|
@@ -105,7 +107,7 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
|
|
|
bool DatFile::ExtractFile(long long file_id, Database *db) {
|
|
|
- if (dat_state_ != READY) {
|
|
|
+ if (dat_state_ < READY) {
|
|
|
throw DatException("Bad DatFile::ExtractFile() - invalid DatFile state!", EXPORT_EXCEPTION);
|
|
|
}
|
|
|
|
|
@@ -157,7 +159,7 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
|
|
|
int DatFile::ExtractAllFilesByType(FILE_TYPE type, std::string path) {
|
|
|
- if (dat_state_ != READY) {
|
|
|
+ if (dat_state_ < READY) {
|
|
|
throw DatException("Bad DatFile::ExtractAllFilesByType() - invalid DatFile state!", EXPORT_EXCEPTION);
|
|
|
}
|
|
|
|
|
@@ -177,7 +179,7 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
|
|
|
int DatFile::ExtractAllFilesByType(FILE_TYPE type, Database *db) {
|
|
|
- if (dat_state_ != READY) {
|
|
|
+ if (dat_state_ < READY) {
|
|
|
throw DatException("Bad DatFile::ExtractAllFilesByType() - invalid DatFile state!", EXPORT_EXCEPTION);
|
|
|
}
|
|
|
|
|
@@ -191,8 +193,12 @@ namespace LOTRO_DAT {
|
|
|
return success;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+
|
|
|
bool DatFile::PatchFile(const char *filename, YAML::Node options) {
|
|
|
+ if (dat_state_ < READY) {
|
|
|
+ throw DatException("Bad DatFile::PatchFile() - invalid DatFile state!", EXPORT_EXCEPTION);
|
|
|
+ }
|
|
|
+
|
|
|
if (options["did"].IsDefined() && options["did"].as<int>() != dat_id_)
|
|
|
return false;
|
|
|
|
|
@@ -200,6 +206,12 @@ namespace LOTRO_DAT {
|
|
|
data.ReadFromFile(filename);
|
|
|
|
|
|
auto file_id = options["fid"].as<long long>();
|
|
|
+
|
|
|
+ if (dictionary_[file_id] == nullptr) {
|
|
|
+ fprintf(stderr, "ERROR DatFile::PatchFile() - Cannot patch file - there is no file in dictionary with file_id = %lld.\n", file_id);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
BinaryData old_data = GetFileData(dictionary_[file_id]);
|
|
|
data = dictionary_[file_id]->MakeForImport(old_data, SubfileData(data, u"", options));
|
|
|
|
|
@@ -221,6 +233,10 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
|
|
|
bool DatFile::PatchFile(const SubfileData &data) {
|
|
|
+ if (dat_state_ < READY) {
|
|
|
+ throw DatException("Bad DatFile::PatchFile() - invalid DatFile state!", EXPORT_EXCEPTION);
|
|
|
+ }
|
|
|
+
|
|
|
auto file_id = data.options["fid"].as<long long>();
|
|
|
Subfile *file = dictionary_[file_id];
|
|
|
|
|
@@ -231,12 +247,16 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
BinaryData old_data = GetFileData(file);
|
|
|
BinaryData patch_data = file->MakeForImport(old_data, data);
|
|
|
- ApplyFilePatch(file, patch_data);
|
|
|
+ ApplyFilePatch(dictionary_[file_id], patch_data);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+
|
|
|
bool DatFile::PatchAllDatabase(Database *db) {
|
|
|
+ if (dat_state_ < READY) {
|
|
|
+ throw DatException("Bad DatFile::PatchAllDatabase() - invalid DatFile state!", EXPORT_EXCEPTION);
|
|
|
+ }
|
|
|
+
|
|
|
SubfileData data;
|
|
|
try {
|
|
|
data = db->GetNextFile();
|
|
@@ -261,6 +281,8 @@ namespace LOTRO_DAT {
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
+ CommitChanges();
|
|
|
+ CommitLocales();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
@@ -324,28 +346,6 @@ namespace LOTRO_DAT {
|
|
|
return data;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
- 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_;
|
|
|
- }
|
|
|
-
|
|
|
|
|
|
|
|
|
|
|
@@ -359,7 +359,7 @@ namespace LOTRO_DAT {
|
|
|
if (file_handler_ == nullptr) {
|
|
|
std::string err = "Bad DatFile::OpenDatFile. Unable to open file ";
|
|
|
err += dat_name;
|
|
|
- throw DatException(err.c_str(), INIT_EXCEPTION);
|
|
|
+ throw DatException(err.c_str(), NOFILE_EXCEPTION);
|
|
|
}
|
|
|
|
|
|
fseek(file_handler_, 0, SEEK_END);
|
|
@@ -434,7 +434,7 @@ namespace LOTRO_DAT {
|
|
|
throw DatException(err.c_str(), READ_EXCEPTION);
|
|
|
}
|
|
|
|
|
|
- if (offset + size > file_size()) {
|
|
|
+ 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.");
|
|
@@ -447,14 +447,14 @@ namespace LOTRO_DAT {
|
|
|
}
|
|
|
|
|
|
void DatFile::WriteData(const BinaryData &data, long long size, long long offset, long long data_offset) {
|
|
|
- if (dat_state_ != READY)
|
|
|
+ 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);
|
|
|
|
|
|
- int x = fwrite(data.data() + data_offset, unsigned(size), 1, file_handler_);
|
|
|
+ fwrite(data.data() + data_offset, unsigned(size), 1, file_handler_);
|
|
|
}
|
|
|
|
|
|
|
|
@@ -465,14 +465,17 @@ namespace LOTRO_DAT {
|
|
|
fprintf(stderr, "Warning: DatFile::ApplyFilePatch - found 2 files in patch with the same file_id. Passing last...\n");
|
|
|
return;
|
|
|
}
|
|
|
- patched_list[file->file_id()] = new BinaryData(file->MakeHeaderData());
|
|
|
- patched_ = true;
|
|
|
+ if (current_locale() != PATCHED) {
|
|
|
+ std::cout << "Changing locale to RU in order to patch file";
|
|
|
+ SetLocale(PATCHED);
|
|
|
+ }
|
|
|
+ dat_state_ = UPDATED;
|
|
|
|
|
|
auto journal = GetFragmentationJournal();
|
|
|
|
|
|
file->file_size_ = data.size() - 8;
|
|
|
|
|
|
-
|
|
|
+ if (patch_dict_.count(file->file_id()) == 0 || data.size() > file->block_size()) {
|
|
|
file->file_offset_ = journal[0].second;
|
|
|
file->block_size_ = std::max(data.size(), 256u);
|
|
|
|
|
@@ -482,7 +485,7 @@ namespace LOTRO_DAT {
|
|
|
WriteData(nulls, nulls.size(), file_size_);
|
|
|
|
|
|
this->file_size_ += data.size();
|
|
|
-
|
|
|
+ }
|
|
|
|
|
|
BinaryData fragments_count(4);
|
|
|
fragments_count.FromNumber<4>(0);
|
|
@@ -495,8 +498,12 @@ namespace LOTRO_DAT {
|
|
|
|
|
|
WriteData(file_data, file_data.size(), file->file_offset());
|
|
|
|
|
|
- nulls = BinaryData(unsigned(file->block_size_ - file_data.size()));
|
|
|
- WriteData(nulls, nulls.size(), file->file_offset() + file_data.size());
|
|
|
+ auto file_id = file->file_id();
|
|
|
+ patched_list.insert(file_id);
|
|
|
+
|
|
|
+ delete patch_dict_[file_id];
|
|
|
+ patch_dict_[file_id] = new Subfile(this, file->MakeHeaderData());
|
|
|
+
|
|
|
UpdateFragmentationJournal(journal);
|
|
|
}
|
|
|
|
|
@@ -550,5 +557,127 @@ namespace LOTRO_DAT {
|
|
|
WriteData(data, 4, fragmentation_journal_offset_ + 8 * (i + 1) + 4);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ void DatFile::CommitChanges() {
|
|
|
+ if (dat_state_ != UPDATED)
|
|
|
+ return;
|
|
|
+ std::cout << "There are some updated files. Rewriting dictionary..." << std::endl << std::flush;
|
|
|
+ UpdateHeader();
|
|
|
+ std::cout << "Updated header..." << std::endl << std::flush;
|
|
|
+ UpdateSubdirectories();
|
|
|
+ std::cout << "Updated subdirectories..." << std::endl << std::flush;
|
|
|
+ std::cout << "Changed " << patched_list.size() << " files..." << std::endl << std::flush;
|
|
|
+ patched_list.clear();
|
|
|
+ dat_state_ = READY;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ void DatFile::InitLocale(LOCALE locale, const char* filename) {
|
|
|
+ auto dict = GetLocaleDictReference(locale);
|
|
|
+ dict->clear();
|
|
|
+
|
|
|
+ FILE *dict_file = fopen(filename, "rb");
|
|
|
+
|
|
|
+ if (dict_file == nullptr) {
|
|
|
+ if (locale == ORIGINAL) {
|
|
|
+ for (auto file : dictionary_) {
|
|
|
+ (*dict)[file.first] = new Subfile(this, file.second->MakeHeaderData());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t size;
|
|
|
+ fread(&size, sizeof(size_t), 1, dict_file);
|
|
|
+
|
|
|
+ std::cout << "There are " << size << " files in " << std::string(filename) << " dictionary...\n";
|
|
|
+
|
|
|
+ for (size_t i = 0; i < size; i++) {
|
|
|
+ BinaryData header(32);
|
|
|
+ fread(header.data(), unsigned(header.size()), 1, dict_file);
|
|
|
+ auto file = new Subfile(this, header);
|
|
|
+ (*dict)[file->file_id()] = file;
|
|
|
+ }
|
|
|
+
|
|
|
+ fclose(dict_file);
|
|
|
+ }
|
|
|
+
|
|
|
+ std::unordered_map<long long, Subfile *> *DatFile::GetLocaleDictReference(LOCALE locale) {
|
|
|
+ switch (locale) {
|
|
|
+ case PATCHED:
|
|
|
+ return &patch_dict_;
|
|
|
+ case ORIGINAL:
|
|
|
+ return &orig_dict_;
|
|
|
+ default:
|
|
|
+ throw DatException("Bad DatFile::GetLocaleDictReference() - unknown locale!!!", LOCALE_EXCEPTION);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void DatFile::SetLocale(LOCALE locale) {
|
|
|
+ if (current_locale_ == locale) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ dat_state_ = UPDATED;
|
|
|
+ auto dict = GetLocaleDictReference(locale);
|
|
|
+ for (auto file : *dict) {
|
|
|
+ if (dictionary_[file.first] == nullptr) {
|
|
|
+ fprintf(stderr, "WARNING: In locale dictionary there is file with file_id = %lld, which is not in .dat "
|
|
|
+ "file! Passing it and removing from locale dictionary\n", file.first);
|
|
|
+ dict->erase(file.first);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (dictionary_[file.first]->MakeHeaderData().CutData(8, 16) == file.second->MakeHeaderData().CutData(8, 16))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ dictionary_[file.first]->file_offset_ = file.second->file_offset_;
|
|
|
+ dictionary_[file.first]->file_size_ = file.second->file_size_;
|
|
|
+ dictionary_[file.first]->block_size_= file.second->block_size_;
|
|
|
+ dictionary_[file.first]->timestamp_ = file.second->timestamp_;
|
|
|
+ dictionary_[file.first]->version_ = file.second->version_;
|
|
|
+
|
|
|
+ dictionary_[file.first] = file.second;
|
|
|
+ patched_list.insert(file.first);
|
|
|
+ dat_state_ = UPDATED;
|
|
|
+ }
|
|
|
+ current_locale_ = locale;
|
|
|
+ CommitChanges();
|
|
|
+ CommitLocales();
|
|
|
+ }
|
|
|
+
|
|
|
+ void DatFile::SaveLocale(LOCALE locale, const char *filename) {
|
|
|
+ auto dict = GetLocaleDictReference(locale);
|
|
|
+ FILE *dict_file = fopen(filename, "wb");
|
|
|
+
|
|
|
+ size_t count = size_t(dict->size());
|
|
|
+ fwrite(&count, sizeof(size_t), 1, dict_file);
|
|
|
+
|
|
|
+ for (auto file : *dict) {
|
|
|
+ BinaryData header = file.second->MakeHeaderData();
|
|
|
+ fwrite(header.data(), unsigned(header.size()), 1, dict_file);
|
|
|
+ }
|
|
|
+ fclose(dict_file);
|
|
|
+ }
|
|
|
+
|
|
|
+ LOCALE DatFile::current_locale() {
|
|
|
+ return current_locale_;
|
|
|
+ }
|
|
|
+
|
|
|
+ void DatFile::CommitLocales() {
|
|
|
+ std::cout << "Commiting locales..." << std::endl;
|
|
|
+ std::cout << "Saving patched locale..." << std::endl;
|
|
|
+ SaveLocale(PATCHED, (std::string(filename_) + std::string("patched.dbgm")).c_str());
|
|
|
+ std::cout << "Saving original locale..." << std::endl;
|
|
|
+ SaveLocale(ORIGINAL,(std::string(filename_) + std::string("original.dbgm")).c_str());
|
|
|
+
|
|
|
+ std::cout << "Writing current locale" << std::endl;
|
|
|
+ FILE *locale = fopen((std::string(filename_) + ".dbgm").c_str(), "w");
|
|
|
+ if (current_locale_ == ORIGINAL)
|
|
|
+ fprintf(locale, "EN");
|
|
|
+ else
|
|
|
+ fprintf(locale, "RU");
|
|
|
+ fclose(locale);
|
|
|
+ std::cout << "Done!" << std::endl;
|
|
|
+ }
|
|
|
}
|
|
|
}
|