datfile.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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::Initialized() const{
  63. return initialized_;
  64. }
  65. const std::string& DatFile::GetFilename() const {
  66. return filename_;
  67. }
  68. SubfileInfo DatFile::getSubfileInfo(int file_id) const {
  69. if (files_info_.count(file_id) == 0) {
  70. return SubfileInfo();
  71. } else {
  72. return files_info_.at(file_id);
  73. }
  74. }
  75. size_t DatFile::GetFilesNumInDatFile() {
  76. return api_.GetNumSubfiles(file_handle_);
  77. }
  78. size_t DatFile::PatchAllFilesFromDatabase(Database& db) {
  79. size_t patched_files_num = 0;
  80. SubfileData file;
  81. int i = 0;
  82. const int total_files = db.CountRows();
  83. std::cout << "Patching all files from database..." << std::endl;
  84. while (!(file = db.GetNextFile()).Empty()) {
  85. if (i * 100 / total_files != (i - 1) * 100 / total_files) {
  86. std::cout << "Completed " << i * 100 / total_files << "%" << std::endl;
  87. }
  88. ++i;
  89. if (!file.options["fid"]) {
  90. LOG(ERROR) << "Incorrect db entry - no file_id specified";
  91. continue;
  92. }
  93. PatchFile(file);
  94. ++patched_files_num;
  95. }
  96. return patched_files_num;
  97. }
  98. void DatFile::PatchFile(SubfileData file_data, int version, int iteration) {
  99. if (!file_data.options["fid"]) {
  100. LOG(ERROR) << "Trying to patch file, but file id is not specified, skipping!";
  101. return;
  102. }
  103. int file_id = file_data.options["fid"].as<int>();
  104. if (files_info_.count(file_id) == 0) {
  105. LOG(ERROR) << "Trying to patch file, not existing in files_info. File id = " << file_id;
  106. return;
  107. }
  108. const SubfileInfo& file_info = files_info_[file_id];
  109. int existing_file_version = 0; // will be evaluated with api_.GetSubfileData
  110. int size = api_.GetSubfileData(file_handle_, file_id, export_data_buf_.data(), existing_file_version);
  111. if (size <= 0) {
  112. LOG(ERROR) << "Trying to patch file, not existing in .dat file. File id = " << file_id;
  113. return;
  114. }
  115. BinaryData old_data = export_data_buf_.CutData(0, size);
  116. BinaryData file = BuildForImport(old_data, file_data);
  117. if (version == -1) {
  118. version = existing_file_version;
  119. }
  120. if (iteration == -1) {
  121. iteration = file_info.iteration;
  122. }
  123. api_.PutSubfileData(file_handle_, file_id, file.data(), 0, file.size(), version, iteration);
  124. }
  125. void DatFile::PatchFile(int file_id, FILE_TYPE type, std::string path_to_file, int version, int iteration) {
  126. BinaryData new_data(64 * 1024 * 1024);
  127. std::ifstream in(path_to_file, std::ifstream::binary);
  128. in.read((char*)new_data.data(), new_data.size());
  129. std::streamsize data_size = in.gcount();
  130. in.close();
  131. SubfileData imported_subfile;
  132. imported_subfile.binary_data = new_data.CutData(0, data_size);
  133. imported_subfile.options["ext"] = StringFromFileType(type);
  134. imported_subfile.options["fid"] = file_id;
  135. PatchFile(imported_subfile, version, iteration);
  136. }
  137. FILE_TYPE DatFile::GetExistingFileType(int file_id) {
  138. int version = 0;
  139. api_.GetSubfileData(file_handle_, file_id, export_data_buf_.data(), version);
  140. return FileTypeFromFileContents(file_id, export_data_buf_);
  141. }
  142. void DatFile::PerformOperationOnAllSubfiles(const SubfileOperation& operation) {
  143. if (files_info_.empty()) {
  144. LoadAllFilesInfo();
  145. }
  146. std::cout << "Performing operation on all files...";
  147. int i = 0;
  148. for (const std::pair<int, SubfileInfo>& info : files_info_) {
  149. if (i * 100 / files_info_.size() != (i - 1) * 100 / files_info_.size()) {
  150. std::cout << "Completed " << i * 100 / files_info_.size() << "%" << std::endl;
  151. }
  152. operation(info.second);
  153. ++i;
  154. }
  155. }
  156. int DatFile::ExportFilesByType(FILE_TYPE type, Database& db) {
  157. int num_files = 0;
  158. SubfileOperation operation = [this, type, &db, &num_files](const SubfileInfo& info) {
  159. FILE_TYPE file_type = GetExistingFileType(info.file_id);
  160. if (file_type == type) {
  161. ExportFileById(info.file_id, db);
  162. ++num_files;
  163. }
  164. };
  165. PerformOperationOnAllSubfiles(operation);
  166. return num_files;
  167. }
  168. int DatFile::ExportFilesByType(FILE_TYPE type, std::string path_to_directory) {
  169. int num_files = 0;
  170. SubfileOperation operation = [this, type, path_to_directory, &num_files](const SubfileInfo& info) {
  171. FILE_TYPE file_type = GetExistingFileType(info.file_id);
  172. if (file_type == type) {
  173. ExportFileById(info.file_id, path_to_directory + "/" + std::to_string(info.file_id));
  174. ++num_files;
  175. }
  176. };
  177. PerformOperationOnAllSubfiles(operation);
  178. return num_files;
  179. }
  180. void DatFile::ExportFileById(int file_id, std::string target_file_path) {
  181. int version = 0;
  182. int size = api_.GetSubfileData(file_handle_, file_id, export_data_buf_.data(), version);
  183. auto data = export_data_buf_.CutData(0, size);
  184. FILE_TYPE file_type = FileTypeFromFileContents(file_id, data);
  185. SubfileData file = BuildForExport(file_id, data);
  186. std::ofstream out(target_file_path + StringFromFileType(file_type), std::ofstream::binary);
  187. out.write((char*)file.binary_data.data(), file.binary_data.size());
  188. out.close();
  189. }
  190. void DatFile::ExportFileById(int file_id, Database& db) {
  191. int version = 0;
  192. int size = api_.GetSubfileData(file_handle_, file_id, export_data_buf_.data(), version);
  193. auto data = export_data_buf_.CutData(0, size);
  194. SubfileData file = BuildForExport(file_id, data);
  195. db.PushFile(file);
  196. }
  197. int DatFile::GetFileVersion(int file_id) {
  198. return api_.GetSubfileVersion(file_handle_, file_id);
  199. }
  200. SubfileData DatFile::GetFile(int file_id) {
  201. int version = 0;
  202. int size = api_.GetSubfileData(file_handle_, file_id, export_data_buf_.data(), version);
  203. auto data = export_data_buf_.CutData(0, size);
  204. return BuildForExport(file_id, data);
  205. }
  206. }; // namespace LOTRO_DAT