datfile.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. #include "datfile.h"
  2. #include "subfiledata.h"
  3. #include "subfiles/ddssubfile.h"
  4. #include <iostream>
  5. #include <yaml-cpp/yaml.h>
  6. #include <EasyLogging++/easylogging++.h>
  7. INITIALIZE_EASYLOGGINGPP
  8. namespace LOTRO_DAT {
  9. DatExportApi DatFile::api_;
  10. DatFile::DatFile(int file_handle) {
  11. initialized_ = false;
  12. file_handle_ = file_handle;
  13. el::Configurations defaultConf;
  14. el::Loggers::addFlag(el::LoggingFlag::LogDetailedCrashReason);
  15. el::Loggers::addFlag(el::LoggingFlag::ImmediateFlush);
  16. el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck);
  17. defaultConf.setToDefault();
  18. defaultConf.setGlobally(el::ConfigurationType::Format, "%datetime %level : %msg (function: %func)");
  19. defaultConf.setGlobally(el::ConfigurationType::ToFile, "true");
  20. defaultConf.setGlobally(el::ConfigurationType::Filename, "dat_library.log");
  21. defaultConf.setGlobally(el::ConfigurationType::ToStandardOutput, "false");
  22. defaultConf.setGlobally(el::ConfigurationType::MaxLogFileSize, "15728640"); // 15MB
  23. #ifndef NDEBUG
  24. defaultConf.set(el::Level::Debug, el::ConfigurationType::Enabled, "true");
  25. defaultConf.set(el::Level::Debug, el::ConfigurationType::Filename, "dat_library_debug.log");
  26. #elif NDEBUG
  27. defaultConf.set(el::Level::Debug, el::ConfigurationType::Enabled, "false");
  28. #endif
  29. el::Loggers::reconfigureAllLoggers(defaultConf);
  30. export_data_buf_ = BinaryData(64 * 1024 * 1024); // 64 MB - max file size;
  31. }
  32. DatFile::~DatFile() {
  33. Deinit();
  34. }
  35. bool DatFile::Init(const std::string& filename) {
  36. if (initialized_) {
  37. Deinit();
  38. }
  39. if (api_.OpenDatFile(file_handle_, filename.c_str(), 130) == file_handle_) {
  40. initialized_ = true;
  41. filename_ = filename;
  42. LoadAllFilesInfo();
  43. return true;
  44. }
  45. return false;
  46. }
  47. void DatFile::LoadAllFilesInfo() {
  48. int subfiles_num = api_.GetNumSubfiles(file_handle_);
  49. for (int i = 0; i < subfiles_num; ++i) {
  50. SubfileInfo file_info;
  51. api_.GetSubfileSizes(file_handle_, &file_info.file_id, &file_info.size, &file_info.iteration, i, 1);
  52. files_info_[file_info.file_id] = file_info;
  53. }
  54. }
  55. void DatFile::Deinit() {
  56. if (initialized_) {
  57. api_.CloseDatFile(file_handle_);
  58. files_info_.clear();
  59. initialized_ = false;
  60. }
  61. }
  62. bool DatFile::checkIfPatchedByLegacyV1(const std::string& filename) {
  63. FILE* file_handler = fopen(filename.c_str(), "r+b");
  64. if (file_handler == nullptr) {
  65. return false;
  66. }
  67. BinaryData data(1024);
  68. fread(data.data(), 1024, 1, file_handler);
  69. fclose(file_handler);
  70. return (data.ToNumber<4>(0x128) != 0 || data.ToNumber<4>(0x12C) != 0);
  71. }
  72. int DatFile::GetDatFileMaxIteration() const {
  73. int subfiles_num = api_.GetNumSubfiles(file_handle_);
  74. int max_iteration = -1;
  75. for (int i = 0; i < subfiles_num; ++i) {
  76. SubfileInfo file_info;
  77. api_.GetSubfileSizes(file_handle_, &file_info.file_id, &file_info.size, &file_info.iteration, i, 1);
  78. max_iteration = std::max(max_iteration, file_info.iteration);
  79. }
  80. return max_iteration;
  81. }
  82. bool DatFile::Initialized() const{
  83. return initialized_;
  84. }
  85. const std::string& DatFile::GetFilename() const {
  86. return filename_;
  87. }
  88. SubfileInfo DatFile::getSubfileInfo(int file_id) const {
  89. if (files_info_.count(file_id) == 0) {
  90. return SubfileInfo();
  91. } else {
  92. return files_info_.at(file_id);
  93. }
  94. }
  95. size_t DatFile::GetFilesNumInDatFile() {
  96. return api_.GetNumSubfiles(file_handle_);
  97. }
  98. size_t DatFile::PatchAllFilesFromDatabase(Database& db) {
  99. size_t patched_files_num = 0;
  100. SubfileData file;
  101. int i = 0;
  102. const int total_files = db.CountRows();
  103. std::cout << "Patching all files from database..." << std::endl;
  104. while (!(file = db.GetNextFile()).Empty()) {
  105. if (i * 100 / total_files != (i - 1) * 100 / total_files) {
  106. std::cout << "Completed " << i * 100 / total_files << "%" << std::endl;
  107. }
  108. ++i;
  109. if (!file.options["fid"]) {
  110. LOG(ERROR) << "Incorrect db entry - no file_id specified";
  111. continue;
  112. }
  113. PatchFile(file);
  114. ++patched_files_num;
  115. }
  116. return patched_files_num;
  117. }
  118. void DatFile::PatchFile(SubfileData file_data, int version, int iteration, bool create) {
  119. if (!file_data.options["fid"]) {
  120. LOG(ERROR) << "Trying to patch file, but file id is not specified, skipping!";
  121. return;
  122. }
  123. int file_id = file_data.options["fid"].as<int>();
  124. if (files_info_.count(file_id) == 0 && !create) {
  125. LOG(ERROR) << "Trying to patch file, not existing in files_info. File id = " << file_id;
  126. return;
  127. }
  128. if (create) {
  129. BinaryData file = file_data.binary_data;
  130. api_.PutSubfileData(file_handle_, file_id, file.data(), 0, file.size(), version, iteration);
  131. return;
  132. }
  133. const SubfileInfo& file_info = files_info_[file_id];
  134. int existing_file_version = 0; // will be evaluated with api_.GetSubfileData
  135. int size = api_.GetSubfileData(file_handle_, file_id, export_data_buf_.data(), existing_file_version);
  136. if (size <= 0) {
  137. LOG(ERROR) << "Trying to patch file, not existing in .dat file. File id = " << file_id;
  138. return;
  139. }
  140. BinaryData old_data = export_data_buf_.CutData(0, size);
  141. BinaryData file = BuildForImport(old_data, file_data);
  142. if (version == -1) {
  143. version = existing_file_version;
  144. }
  145. if (iteration == -1) {
  146. iteration = file_info.iteration;
  147. }
  148. api_.PutSubfileData(file_handle_, file_id, file.data(), 0, file.size(), version, iteration);
  149. }
  150. void DatFile::PatchFile(int file_id, FILE_TYPE type, std::string path_to_file, int version, int iteration) {
  151. BinaryData new_data(64 * 1024 * 1024);
  152. std::ifstream in(path_to_file, std::ifstream::binary);
  153. in.read((char*)new_data.data(), new_data.size());
  154. std::streamsize data_size = in.gcount();
  155. in.close();
  156. SubfileData imported_subfile;
  157. imported_subfile.binary_data = new_data.CutData(0, data_size);
  158. imported_subfile.options["ext"] = StringFromFileType(type);
  159. imported_subfile.options["fid"] = file_id;
  160. PatchFile(imported_subfile, version, iteration);
  161. }
  162. FILE_TYPE DatFile::GetExistingFileType(int file_id) {
  163. int version = 0;
  164. api_.GetSubfileData(file_handle_, file_id, export_data_buf_.data(), version);
  165. return FileTypeFromFileContents(file_id, export_data_buf_);
  166. }
  167. void DatFile::PerformOperationOnAllSubfiles(const SubfileOperation& operation) {
  168. if (files_info_.empty()) {
  169. LoadAllFilesInfo();
  170. }
  171. std::cout << "Performing operation on all files...";
  172. int i = 0;
  173. for (const std::pair<int, SubfileInfo>& info : files_info_) {
  174. if (i * 100 / files_info_.size() != (i - 1) * 100 / files_info_.size()) {
  175. std::cout << "Completed " << i * 100 / files_info_.size() << "%" << std::endl;
  176. }
  177. operation(info.second);
  178. ++i;
  179. }
  180. }
  181. int DatFile::ExportFilesByType(FILE_TYPE type, Database& db) {
  182. int num_files = 0;
  183. SubfileOperation operation = [this, type, &db, &num_files](const SubfileInfo& info) {
  184. FILE_TYPE file_type = GetExistingFileType(info.file_id);
  185. if (file_type == type) {
  186. ExportFileById(info.file_id, db);
  187. ++num_files;
  188. }
  189. };
  190. PerformOperationOnAllSubfiles(operation);
  191. return num_files;
  192. }
  193. int DatFile::ExportFilesByType(FILE_TYPE type, std::string path_to_directory) {
  194. int num_files = 0;
  195. SubfileOperation operation = [this, type, path_to_directory, &num_files](const SubfileInfo& info) {
  196. FILE_TYPE file_type = GetExistingFileType(info.file_id);
  197. if (file_type == type) {
  198. ExportFileById(info.file_id, path_to_directory + "/" + std::to_string(info.file_id));
  199. ++num_files;
  200. }
  201. };
  202. PerformOperationOnAllSubfiles(operation);
  203. return num_files;
  204. }
  205. void DatFile::ExportFileById(int file_id, std::string target_file_path) {
  206. int version = 0;
  207. int size = api_.GetSubfileData(file_handle_, file_id, export_data_buf_.data(), version);
  208. auto data = export_data_buf_.CutData(0, size);
  209. FILE_TYPE file_type = FileTypeFromFileContents(file_id, data);
  210. SubfileData file = BuildForExport(file_id, data);
  211. std::ofstream out(target_file_path + StringFromFileType(file_type), std::ofstream::binary);
  212. out.write((char*)file.binary_data.data(), file.binary_data.size());
  213. out.close();
  214. }
  215. void DatFile::ExportFileById(int file_id, Database& db) {
  216. int version = 0;
  217. int size = api_.GetSubfileData(file_handle_, file_id, export_data_buf_.data(), version);
  218. auto data = export_data_buf_.CutData(0, size);
  219. SubfileData file = BuildForExport(file_id, data);
  220. db.PushFile(file);
  221. }
  222. int DatFile::GetFileVersion(int file_id) {
  223. return api_.GetSubfileVersion(file_handle_, file_id);
  224. }
  225. SubfileData DatFile::GetFile(int file_id) {
  226. int version = 0;
  227. int size = api_.GetSubfileData(file_handle_, file_id, export_data_buf_.data(), version);
  228. auto data = export_data_buf_.CutData(0, size);
  229. return BuildForExport(file_id, data);
  230. }
  231. }; // namespace LOTRO_DAT