DatFile.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. //
  2. // Created by Иван_Архипов on 31.10.2017.
  3. //
  4. #include "DatFile.h"
  5. #include "BinaryData.h"
  6. #include "Common/DatException.h"
  7. #include "SubDirectory.h"
  8. #include "Subfile.h"
  9. #include "yaml-cpp/yaml.h"
  10. #include <locale>
  11. extern "C++"
  12. {
  13. namespace LOTRO_DAT {
  14. DatFile::DatFile() {
  15. dat_state_ = CLOSED;
  16. }
  17. DatFile::DatFile(const char *filename, int dat_id) {
  18. dat_id_ = dat_id;
  19. dat_state_ = CLOSED;
  20. OpenDatFile(filename);
  21. ReadSuperBlock();
  22. MakeDirectories();
  23. try {
  24. MakeDictionary();
  25. } catch (...) {
  26. fprintf(stderr, "Unable to make dictionary!! Unable to init DatFile!!!");
  27. return;
  28. }
  29. if (dat_state_ == SUCCESS_DICTIONARY)
  30. dat_state_ = READY;
  31. else
  32. throw DatException("Bad DatFile initialization! Not all init states were successfully passed!",
  33. INIT_EXCEPTION);
  34. }
  35. DatFile::~DatFile() {
  36. if (file_handler_ != nullptr)
  37. fclose(file_handler_);
  38. delete file_handler_;
  39. delete root_directory_;
  40. }
  41. /// Extracts file with file_id.
  42. /// If path is undefined then it will be recognised as current working directory
  43. /// Output file path consists of "path + file_id + file_extension";
  44. /// NOTICE: The directory, mentioned in "std::string path" variable SHOULD BE ALREADY CREATED;
  45. /// Otherwise DatException() will be thrown.
  46. /// Returns true, if file was successfully extracted;
  47. /// Throws DatException() if undefined behaviour happened
  48. bool DatFile::ExtractFile(long long file_id, const std::string path) {
  49. if (dat_state_ != READY) {
  50. throw DatException("Bad DatFile::ExtractFile() - invalid DatFile state!", EXPORT_EXCEPTION);
  51. }
  52. BinaryData file_data = GetFileData(dictionary_[file_id], 8);
  53. long long export_size = 0;
  54. std::vector<BinaryData> binary_data;
  55. std::vector<std::u16string> text_data;
  56. std::vector<YAML::Node> options;
  57. dictionary_[file_id]->PrepareForExport(file_data, export_size, binary_data, text_data, options);
  58. for (int i = 0; i < export_size; ++i) {
  59. binary_data[i].WriteToFile(path + "_" + std::to_string(i) + options[i]["extension"].as<std::string>());
  60. }
  61. }
  62. /// Extracts file with file_id to database "db".
  63. /// DATABASE SHOULD BE ALREADY CREATED; Otherwise DatException will be called.
  64. /// NOTICE: The directory, mentioned in "std::string path" variable SHOULD BE ALREADY CREATED;
  65. /// Otherwise DatException() will be thrown.
  66. /// Returns true, if file was successfully extracted;
  67. /// Throws DatException() if undefined behaviour happened
  68. bool DatFile::ExtractFile(long long file_id, Database *db) {
  69. if (dat_state_ != READY) {
  70. throw DatException("Bad DatFile::ExtractFile() - invalid DatFile state!", EXPORT_EXCEPTION);
  71. }
  72. BinaryData file_data = GetFileData(dictionary_[file_id], 8);
  73. long long export_size = 0;
  74. std::vector<BinaryData> binary_data;
  75. std::vector<std::u16string> text_data;
  76. std::vector<YAML::Node> options;
  77. dictionary_[file_id]->PrepareForExport(file_data, export_size, binary_data, text_data, options);
  78. // TODO: Complete this function
  79. }
  80. /// Extracts all files with specific type to "path + type + file_id + extension" files;
  81. /// If path is undefined then it will be recognised as current working directory
  82. /// NOTICE: The directory, mentioned in "std::string path" variable SHOULD BE ALREADY CREATED;
  83. /// Otherwise DatException() will be thrown.
  84. /// Returns number of successfully extracted files
  85. /// Throws DatException() if undefined behaviour happened
  86. int DatFile::ExtractAllFilesByType(FILE_TYPE type, std::string path) {
  87. if (dat_state_ != READY) {
  88. throw DatException("Bad DatFile::ExtractAllFilesByType() - invalid DatFile state!", EXPORT_EXCEPTION);
  89. }
  90. int success = 0;
  91. for (auto i : dictionary_) {
  92. FILE_TYPE ext = i.second->FileType();
  93. if (ext == type) {
  94. success += ExtractFile(i.second->file_id(), (path + std::to_string(i.second->file_id())));
  95. }
  96. }
  97. return success;
  98. }
  99. /// Extracts all files with specific type to database "db";
  100. /// DATABASE SHOULD BE ALREADY CREATED; Otherwise DatException will be called.
  101. /// Returns number of successfully extracted files
  102. /// Throws DatException() if undefined behaviour happened
  103. int DatFile::ExtractAllFilesByType(FILE_TYPE type, Database *db) {
  104. if (dat_state_ != READY) {
  105. throw DatException("Bad DatFile::ExtractAllFilesByType() - invalid DatFile state!", EXPORT_EXCEPTION);
  106. }
  107. int success = 0;
  108. for (auto i : dictionary_) {
  109. FILE_TYPE ext = i.second->FileType();
  110. if (ext == type) {
  111. success += ExtractFile(i.second->file_id(), db);
  112. }
  113. }
  114. return success;
  115. }
  116. void DatFile::OpenDatFile(const char *dat_name) {
  117. if (dat_state_ != CLOSED)
  118. throw DatException("Bad initialisation of DatFile - current DatFile isn't in correct state!",
  119. INIT_EXCEPTION);
  120. fopen_s(&file_handler_, dat_name, "r+b");
  121. if (file_handler_ == nullptr) {
  122. std::string err = "Bad DatFile::OpenDatFile. Unable to open file ";
  123. err += dat_name;
  124. throw DatException(err.c_str(), INIT_EXCEPTION);
  125. }
  126. fseek(file_handler_, 0, SEEK_END);
  127. file_size_ = ftell(file_handler_);
  128. fseek(file_handler_, 0, SEEK_SET);
  129. dat_state_ = SUCCESS_OPENED;
  130. }
  131. void DatFile::ReadSuperBlock() {
  132. if (dat_state_ != SUCCESS_OPENED)
  133. throw DatException("Bad DatFile::ReadSuperBlock() - DatFile isn't in valid state!", INIT_EXCEPTION);
  134. BinaryData data(1024);
  135. ReadData(data, 1024);
  136. constant1_ = data.ToNumber<4>(0x100);
  137. constant2_ = data.ToNumber<4>(0x140);
  138. version1_ = data.ToNumber<4>(0x14C);
  139. version2_ = data.ToNumber<4>(0x150);
  140. root_directory_offset_ = data.ToNumber<4>(0x160);
  141. auto size1 = data.ToNumber<4>(0x148);
  142. if (constant1_ != 0x4C5000)
  143. throw DatException(
  144. "Bad DatFile::ReadSuperBlock - variable at position 0x100 is not equal to .dat file constant!",
  145. INIT_EXCEPTION);
  146. if (constant2_ != 0x5442)
  147. throw DatException(
  148. "Bad DatFile::ReadSuperBlock - variable at position 0x140 is not equal to .dat file constant!",
  149. INIT_EXCEPTION);
  150. if (file_size_ != size1)
  151. throw DatException(
  152. "Bad DatFile::ReadSuperBlock - variable at 0x148 position is not equal to .dat file size!",
  153. INIT_EXCEPTION);
  154. dat_state_ = SUCCESS_SUPERBLOCK;
  155. }
  156. void DatFile::MakeDirectories() {
  157. if (dat_state_ != SUCCESS_SUPERBLOCK)
  158. throw DatException("Bad DatFile::MakeDirectories() - DatFile isn't in valid state!", INIT_EXCEPTION);
  159. root_directory_ = new SubDirectory((unsigned) root_directory_offset_, this);
  160. dat_state_ = SUCCESS_DIRECTORIES;
  161. }
  162. void DatFile::MakeDictionary() {
  163. if (dat_state_ != SUCCESS_DIRECTORIES)
  164. throw DatException("Bad DatFile::MakeDictionary() - DatFile isn't in valid state!", INIT_EXCEPTION);
  165. try {
  166. root_directory_->MakeDictionary(dictionary_);
  167. } catch (...) {
  168. fprintf(stderr, "Bad DatFile::MakeDictionary() - File is corrupted?\n");
  169. return;
  170. }
  171. dat_state_ = SUCCESS_DICTIONARY;
  172. }
  173. void DatFile::ReadData(BinaryData &data, long long size, long long offset, long long data_offset) {
  174. if (dat_state_ == CLOSED)
  175. throw DatException("Bad DatFile::ReadData() - DatFile isn't in valid state!", READ_EXCEPTION);
  176. if (data_offset + size > data.size()) {
  177. std::string err = "Bad DatFile::ReadData - trying to read more than BinaryData size\n";
  178. err += std::string("Reading ") + std::to_string(size) + std::string(" bytes from ")
  179. + std::to_string(offset) + std::string(" position in dat file.");
  180. throw DatException(err.c_str(), READ_EXCEPTION);
  181. }
  182. if (offset + size > file_size()) {
  183. std::string err = "Bad DatFile::ReadData - trying to read more than DatFile size elapsed\n";
  184. err += std::string("Reading ") + std::to_string(size) + std::string(" bytes from ")
  185. + std::to_string(offset) + std::string(" position in dat file.");
  186. throw DatException(err.c_str(), READ_EXCEPTION);
  187. }
  188. _fseeki64(file_handler_, offset, SEEK_SET);
  189. fread(data.data() + data_offset, size, 1, file_handler_);
  190. data.CheckCompression();
  191. }
  192. void DatFile::WriteData(const BinaryData &data, long long size, long long offset, long long data_offset) {
  193. if (dat_state_ != READY)
  194. throw DatException("Bad DatFile::WriteData() - DatFile isn't in valid state!", WRITE_EXCEPTION);
  195. _fseeki64(file_handler_, offset, SEEK_SET);
  196. if (data_offset + size > data.size())
  197. throw DatException("Bad DatFile::WriteData - trying to write more than BinaryData size", WRITE_EXCEPTION);
  198. fwrite(data.data() + data_offset, size, 1, file_handler_);
  199. }
  200. long long DatFile::constant1() const {
  201. return constant1_;
  202. }
  203. long long DatFile::constant2() const {
  204. return constant2_;
  205. }
  206. long long DatFile::file_size() const {
  207. return file_size_;
  208. }
  209. long long DatFile::version1() const {
  210. return version1_;
  211. }
  212. long long DatFile::version2() const {
  213. return version2_;
  214. }
  215. void DatFile::WriteUnorderedDictionary(std::string path) const {
  216. FILE *f;
  217. fopen_s(&f, (path + "dict.txt").c_str(), "w");
  218. fprintf(f, "file_id offset size size2 extension\n");
  219. for (auto i : dictionary_) {
  220. fprintf(f, "%lld %lld %lld %lld %s\n", i.second->file_id(), i.second->file_offset(), i.second->file_size(),
  221. i.second->block_size(), i.second->Extension());
  222. }
  223. fclose(f);
  224. }
  225. long long DatFile::files_number() const {
  226. return dictionary_.size();
  227. }
  228. BinaryData DatFile::GetFileData(const Subfile *file, long long int offset = 0) {
  229. BinaryData mfile_id(4);
  230. ReadData(mfile_id, 4, file->file_offset() + 8);
  231. if (file->file_id() != mfile_id.ToNumber<4>(0))
  232. throw DatException("Bad DatFile::GetFileData() - file_id in Subfile doesn't match to file_id in DatFile.", READ_EXCEPTION);
  233. BinaryData data((unsigned)(file->file_size()));
  234. if (file->block_size() >= file->file_size() + 8) {
  235. ReadData(data, file->file_size(), file->file_offset() + offset);
  236. return data;
  237. }
  238. BinaryData fragments_count(4);
  239. ReadData(fragments_count, 4, file->file_offset());
  240. long long fragments_number = fragments_count.ToNumber<4>(0);
  241. long long current_block_size = file->block_size() - offset - 8 * fragments_number;
  242. ReadData(data, current_block_size , file->file_offset() + offset);
  243. BinaryData FragmentsDictionary(8 * unsigned(fragments_number));
  244. ReadData(FragmentsDictionary, 8 * unsigned(fragments_number), file->file_offset() + file->block_size() - 8 * fragments_number);
  245. for (long long i = 0; i < fragments_number; i++) {
  246. long long fragment_size = FragmentsDictionary.ToNumber<4>(8 * i);
  247. long long fragment_offset = FragmentsDictionary.ToNumber<4>(8 * i + 4);
  248. ReadData(data, std::min(fragment_size, file->file_size() - current_block_size), fragment_offset, current_block_size );
  249. current_block_size += fragment_size;
  250. }
  251. return data;
  252. }
  253. }
  254. }