DatFileSystem.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. //
  2. // Created by kikab on 04.06.2018.
  3. //
  4. #include <DatSubsystems/DatFileSystem.h>
  5. #include <EasyLogging++/easylogging++.h>
  6. #include <BinaryData.h>
  7. #include <DatFile.h>
  8. #include <SubFile.h>
  9. #include <SubDirectory.h>
  10. namespace LOTRO_DAT{
  11. DatFileSystem::DatFileSystem(DatFile *datFilePtr) : dat(datFilePtr) {
  12. LOG(INFO) << "Initialization of empty DatFileSystem";
  13. }
  14. DatOperationResult<BinaryData> DatFileSystem::GetFileData(long long file_id, long long int offset) {
  15. if (!dat)
  16. return DatOperationResult<BinaryData>(BinaryData(), ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
  17. if (dictionary_.count(file_id))
  18. return DatOperationResult<BinaryData>(BinaryData(), ERROR, "DatFileSystem error: no file found with id = " + std::to_string(file_id));
  19. auto file = dictionary_[file_id];
  20. LOG(DEBUG) << "Getting file " << file->file_id() << " data";
  21. BinaryData mfile_id(20);
  22. auto operation = dat->getIO().ReadData(mfile_id, 20, file->file_offset() + 8);
  23. if (operation.result == ERROR) {
  24. LOG(ERROR) << "Error while reading file " << file->file_id() << " header (offset = "
  25. << file->file_offset() << "); Message: " << operation.msg << "Aborting.";
  26. return DatOperationResult<BinaryData>(BinaryData(), ERROR, std::string("DatFileSystem error: Read ") + std::to_string(file_id) + " error." + operation.msg);
  27. }
  28. if (!mfile_id.CheckCompression() && file->file_id() != mfile_id.ToNumber<4>(0)) {
  29. LOG(ERROR) << "Bad DatFile::GetFileData() - file_id in SubFile ("
  30. << file->file_id()
  31. << ") doesn't match to file_id (" << mfile_id.ToNumber<4>(0) << ")in DatFile.";
  32. return DatOperationResult<BinaryData>(BinaryData(), ERROR, std::string("DatFileSystem error: Read ") + std::to_string(file_id) + " error: SubFile value differs from value in dict");
  33. }
  34. BinaryData data((unsigned)(file->file_size() + (8 - offset)));
  35. if (file->block_size() >= file->file_size() + 8) {
  36. dat->getIO().ReadData(data, file->file_size() + (8 - offset), file->file_offset() + offset);
  37. return DatOperationResult<BinaryData>(data, SUCCESS);
  38. }
  39. BinaryData fragments_count(4);
  40. dat->getIO().ReadData(fragments_count, 4, file->file_offset());
  41. long long fragments_number = fragments_count.ToNumber<4>(0);
  42. long long current_block_size = file->block_size() - offset - 8 * fragments_number;
  43. dat->getIO().ReadData(data, current_block_size, file->file_offset() + offset);
  44. BinaryData FragmentsDictionary(8 * unsigned(fragments_number));
  45. dat->getIO().ReadData(FragmentsDictionary, 8 * unsigned(fragments_number),
  46. file->file_offset() + file->block_size() - 8 * fragments_number);
  47. for (long long i = 0; i < fragments_number; i++) {
  48. long long fragment_size = FragmentsDictionary.ToNumber<4>(8 * i);
  49. long long fragment_offset = FragmentsDictionary.ToNumber<4>(8 * i + 4);
  50. dat->getIO().ReadData(data, std::min(fragment_size, file->file_size() - current_block_size), fragment_offset,
  51. current_block_size);
  52. current_block_size += fragment_size;
  53. }
  54. LOG(DEBUG) << "Successfully got file " << file->file_id() << " data";
  55. return DatOperationResult<BinaryData>(data, SUCCESS);
  56. }
  57. DatOperationResult<SubFile> DatFileSystem::GetFile(long long file_id) {
  58. if (!dat)
  59. return DatOperationResult<SubFile>(SubFile(), ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
  60. if (dictionary_.count(file_id) == 0)
  61. return DatOperationResult<SubFile>(SubFile(), ERROR, "No file with id = " + std::to_string(file_id) + " found in dictionary");
  62. return DatOperationResult<SubFile>(*dictionary_[file_id].get(), SUCCESS);
  63. }
  64. DatOperationResult<> DatFileSystem::UpdateFile(const SubFile &file) {
  65. if (!dat)
  66. return DatOperationResult<>(ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
  67. if (dictionary_.count(file.file_id()) == 0)
  68. return DatOperationResult<>(ERROR, "No file with id = " + std::to_string(file.file_id()) + " found in dictionary. Cannot update file");
  69. *dictionary_[file.file_id()] = file;
  70. return DatOperationResult<>(SUCCESS);
  71. }
  72. DatOperationResult<> DatFileSystem::MakeDirectories() {
  73. LOG(DEBUG) << "Started making directories";
  74. if (!dat)
  75. return DatOperationResult<>(ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
  76. root_directory_ = std::make_shared<SubDirectory>((unsigned)dat->getIO().root_directory_offset, *dat);
  77. SubDirectory::subdir_init_queue_.insert(root_directory_);
  78. while (!SubDirectory::subdir_init_queue_.empty()) {
  79. std::shared_ptr<SubDirectory> dir = *SubDirectory::subdir_init_queue_.begin();
  80. SubDirectory::subdir_init_queue_.erase(SubDirectory::subdir_init_queue_.begin());
  81. if (dir->MakeSubDirectories())
  82. SubDirectory::subfile_init_queue_.insert(dir);
  83. else
  84. dir->clear();
  85. }
  86. while (!SubDirectory::subfile_init_queue_.empty()) {
  87. std::shared_ptr<SubDirectory> dir = *SubDirectory::subfile_init_queue_.begin();
  88. SubDirectory::subfile_init_queue_.erase(SubDirectory::subfile_init_queue_.begin());
  89. if (!dir->MakeSubFiles())
  90. dir->clear();
  91. }
  92. LOG(DEBUG) << "Directories made successfully";
  93. return DatOperationResult<>(SUCCESS);
  94. }
  95. DatOperationResult<> DatFileSystem::MakeDictionary() {
  96. LOG(DEBUG) << "Started making dictionary";
  97. if (!dat)
  98. return DatOperationResult<>(ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
  99. if (root_directory_ == nullptr) {
  100. return DatOperationResult<>(ERROR, "DatFileSystem error: making dictionary from nullptr root directory");
  101. }
  102. root_directory_->MakeDictionary(dictionary_);
  103. LOG(DEBUG) << "Dictionary made successfull";
  104. return DatOperationResult<>(SUCCESS);
  105. }
  106. DatOperationResult<> DatFileSystem::CommitDirectories() {
  107. if (!dat)
  108. return DatOperationResult<>(ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
  109. for (auto file_id : subfile_pending_update) {
  110. if (dictionary_[file_id] == nullptr)
  111. continue;
  112. auto operation = CheckCorrectSubfile(*dictionary_[file_id].get());
  113. if (operation.result == ERROR) {
  114. LOG(ERROR) << "Check subfile correctness failed. Error message: " << operation.msg;
  115. continue;
  116. }
  117. if (!operation.value) {
  118. LOG(DEBUG) << "Incorrect SubFile " << file_id << " data: doesn't match";
  119. continue;
  120. }
  121. auto operation1 = dat->getIO().WriteData(dictionary_[file_id]->MakeHeaderData(), 32, dictionary_[file_id]->dictionary_offset());
  122. if (operation1.result == ERROR) {
  123. LOG(ERROR) << "Unable to write data to dictionary for file " << file_id << ". Returned message: " << operation.msg;
  124. }
  125. }
  126. subfile_pending_update.clear();
  127. return DatOperationResult<>(SUCCESS);
  128. }
  129. DatOperationResult<bool> DatFileSystem::CheckCorrectSubfile(const SubFile &file) {
  130. if (!dat)
  131. return DatOperationResult<bool>(false, ERROR, "DatFileSystem error: no connection with Dat (dat is nullptr)");
  132. BinaryData mfile_id(20);
  133. dat->getIO().ReadData(mfile_id, 20, file.file_offset() + 8);
  134. if (mfile_id.Empty())
  135. return DatOperationResult<bool>(false, SUCCESS);
  136. return DatOperationResult<bool>((mfile_id.CheckCompression() || file.file_id() == mfile_id.ToNumber<4>(0)) && file.file_size() < 50ll * 1024ll * 1024ll, SUCCESS);
  137. }
  138. DatOperationResult<> DatFileSystem::Init() {
  139. auto operation = MakeDirectories();
  140. if (operation.result == ERROR) {
  141. LOG(ERROR) << "Unable to make direcories. Error msg: " << operation.msg;
  142. return DatOperationResult<>(ERROR, "Unable to init Dat file system due to fail while making directories");
  143. }
  144. operation = MakeDictionary();
  145. if (operation.result == ERROR) {
  146. LOG(ERROR) << "Unable to make dictionary. Error msg: " << operation.msg;
  147. return DatOperationResult<>(ERROR, "Unable to init Dat file system due to fail while making dictionary");
  148. }
  149. return DatOperationResult<>(SUCCESS);
  150. }
  151. DatOperationResult<> DatFileSystem::DeInit() {
  152. auto operation = CommitDirectories();
  153. if (operation.result == ERROR) {
  154. LOG(ERROR) << "Unable to commit direcories. Error msg: " << operation.msg;
  155. return DatOperationResult<>(ERROR, "Unable to deinit Dat file system due to fail while making directories");
  156. }
  157. return DatOperationResult<>(SUCCESS);
  158. }
  159. }