SubFile.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. //
  2. // Created by Иван_Архипов on 01.11.2017.
  3. //
  4. #include "SubFile.h"
  5. #include "BinaryData.h"
  6. #include "DatFile.h"
  7. #include "SubfileData.h"
  8. #include "EasyLogging++/easylogging++.h"
  9. #include "DatOperationResult.h"
  10. #include "Subfiles/TextSubFile.h"
  11. #include "Subfiles/DdsSubFile.h"
  12. #include "Subfiles/FontSubFile.h"
  13. #include "Subfiles/JpgSubFile.h"
  14. #include "Subfiles/OggSubFile.h"
  15. #include "Subfiles/WavSubFile.h"
  16. #include "Subfiles/UnknownSubFile.h"
  17. #include <algorithm>
  18. const long long MAXSIZE = 50ll * 1024ll * 1024ll; // Setting maximal file size 50 MB; Files with size more than 50 mb
  19. // will be recognized as incorrect and passed.
  20. // This should be done because of not completely correct implementation
  21. // of Subfiles and Subdirectories search in DatFile.
  22. namespace LOTRO_DAT {
  23. SubFile::SubFile() : category(0), dat_(nullptr), unknown1_(0), file_id_(0), file_offset_(0), file_size_(0),
  24. timestamp_(0), version_(0), block_size_(0), unknown2_(0) {
  25. }
  26. SubFile::SubFile(DatFile &dat, const BinaryData &header) : dat_(&dat) {
  27. category = 0;
  28. unknown1_ = header.ToNumber<4>(0); // unknown1
  29. file_id_ = header.ToNumber<4>(4); // file_id
  30. file_offset_ = header.ToNumber<4>(8); // file_offset
  31. file_size_ = header.ToNumber<4>(12); // block_size
  32. timestamp_ = header.ToNumber<4>(16); // timestamp
  33. version_ = header.ToNumber<4>(20); // version
  34. block_size_ = header.ToNumber<4>(24); // block_size
  35. unknown2_ = header.ToNumber<4>(28); // unknown2
  36. if (file_size_ > MAXSIZE) {
  37. LOG(ERROR) << "Bad SubFile::SubFile() - File size of file " << file_id_ << " with offset " << file_offset_
  38. << " is too much... Maybe it's incorrect..?";
  39. file_id_ = -1;
  40. return;
  41. }
  42. }
  43. SubFile::SubFile(DatFile &dat, long long dictionary_offset, long long unknown1, long long file_id,
  44. long long file_offset, long long file_size, long long timestamp, long long version,
  45. long long block_size, long long unknown2) :
  46. category(0), dat_(&dat), dictionary_offset_(dictionary_offset), unknown1_(unknown1), file_id_(file_id),
  47. file_offset_(file_offset), file_size_(file_size), timestamp_(timestamp), version_(version),
  48. block_size_(block_size), unknown2_(unknown2) {
  49. if (file_size_ > MAXSIZE) {
  50. LOG(ERROR) << "Bad SubFile::SubFile() - File size of file " << file_id << " with offset " << file_offset
  51. << " is too much... Maybe it's incorrect..?";
  52. file_id_ = -1;
  53. return;
  54. }
  55. }
  56. /// Typical getters of private fields, if there's need for getting information about SubFile from outside class.
  57. long long SubFile::dictionary_offset() const {
  58. return dictionary_offset_;
  59. }
  60. long long SubFile::unknown1() const {
  61. return unknown1_;
  62. }
  63. long long SubFile::file_id() const {
  64. return file_id_;
  65. }
  66. long long SubFile::file_offset() const {
  67. return file_offset_;
  68. }
  69. long long SubFile::file_size() const {
  70. return file_size_;
  71. }
  72. long long SubFile::timestamp() const {
  73. return timestamp_;
  74. }
  75. long long SubFile::version() const {
  76. return version_;
  77. }
  78. long long SubFile::block_size() const {
  79. return block_size_;
  80. }
  81. long long SubFile::unknown2() const {
  82. return unknown2_;
  83. }
  84. /// bool Subfile::FileType(...);
  85. /// Virtual function, can (and should) be redefined in child class, otherwise an exception will be thrown while exporting/importing file.
  86. /// Returns enum FILE_TYPE value, which is declared in DatFile.h
  87. FILE_TYPE SubFile::FileType() const {
  88. LOG(ERROR) << "INCORRECT IMPLEMENTATION!";
  89. return UNKNOWN;
  90. }
  91. /// std::string Subfile::Extension()
  92. /// Virtual function, can (and should) be redefined in child class, otherwise an exception will be thrown while exporting/importing file.
  93. /// Returns std::string with extension, beggined with '.', ex. ".jpg", ".txt" and so on;
  94. std::string SubFile::Extension() const {
  95. LOG(ERROR) << "INCORRECT IMPLEMENTATION!";
  96. return ".subfile";
  97. }
  98. /// bool Subfile::PrepareForExport(...);
  99. /// Virtual function, can be redefined in child class, otherwise an exception will be thrown while exporting file.
  100. /// Takes constant BinaryData& file_data, which contains all file data in .dat file, except first 8 bytes before file_id.
  101. /// Takes references to export_size - amount of exported files/strings, and content of exported data such as:
  102. /// 1) field binary_data - exported as RAW data
  103. /// 2) field text_data - UTF-16 text, exporting in UTF-8
  104. /// 3) field options - YAML field, which consists of some parameters of file such as file_id, extension and so on.
  105. /// Returns true if preparation was success. Otherwise returns false;
  106. SubfileData SubFile::PrepareForExport(const BinaryData &) {
  107. LOG(ERROR) << "INCORRECT IMPLEMENTATION!";
  108. return SubfileData();
  109. }
  110. /// BinaryData Subfile::PrepareForImport(...);
  111. /// Virtual function, can be redefined in child class, otherwise an exception will be thrown while importing file.
  112. /// Takes constant BinaryData& file_data, which contains all file data in .dat file, including first 8 bytes befire file_id.
  113. /// 1) const field binary_data - exported as RAW data
  114. /// 2) const field text_data - UTF-16 text, exporting in UTF-8
  115. /// 3) const field options - YAML field, which consists of some parameters of file such as file_id, extension and so on.
  116. /// Returns BinaryData - bytes array, prepared for writing in .dat file
  117. BinaryData SubFile::MakeForImport(const BinaryData &, const SubfileData &) {
  118. LOG(ERROR) << "INCORRECT IMPLEMENTATION!";
  119. return BinaryData(0);
  120. }
  121. BinaryData SubFile::MakeHeaderData() const {
  122. BinaryData header = BinaryData::FromNumber<4>(unknown1_)
  123. + BinaryData::FromNumber<4>(file_id_)
  124. + BinaryData::FromNumber<4>(file_offset_)
  125. + BinaryData::FromNumber<4>(file_size_)
  126. + BinaryData::FromNumber<4>(timestamp_)
  127. + BinaryData::FromNumber<4>(version_)
  128. + BinaryData::FromNumber<4>(block_size_)
  129. + BinaryData::FromNumber<4>(unknown2_);
  130. return header;
  131. }
  132. bool SubFile::operator==(const SubFile &b) const {
  133. return unknown1_ == b.unknown1_
  134. && file_id_ == b.file_id_
  135. && file_offset_ == b.file_offset_
  136. && file_size_ == b.file_size_
  137. && timestamp_ == b.timestamp_
  138. && version_ == b.version_
  139. && block_size_ == b.block_size_
  140. && unknown2_ == b.unknown2_;
  141. }
  142. bool SubFile::operator!=(const SubFile &b) const {
  143. return !(*this == b);
  144. }
  145. std::shared_ptr<SubFile> SubFile::MakeSubfile(DatFile &dat, SubFile preinit_file) {
  146. FILE_TYPE type = SubFile::GetSubfileType(dat, preinit_file);
  147. switch (type) {
  148. case TEXT:
  149. return std::dynamic_pointer_cast<SubFile>(std::make_shared<TextSubFile>(preinit_file));
  150. case JPG:
  151. return std::dynamic_pointer_cast<SubFile>(std::make_shared<JpgSubFile>(preinit_file));
  152. case DDS:
  153. return std::dynamic_pointer_cast<SubFile>(std::make_shared<DdsSubFile>(preinit_file));
  154. case WAV:
  155. return std::dynamic_pointer_cast<SubFile>(std::make_shared<WavSubFile>(preinit_file));
  156. case OGG:
  157. return std::dynamic_pointer_cast<SubFile>(std::make_shared<OggSubFile>(preinit_file));
  158. case FONT:
  159. return std::dynamic_pointer_cast<SubFile>(std::make_shared<FontSubFile>(preinit_file));
  160. case UNKNOWN:
  161. return std::dynamic_pointer_cast<SubFile>(std::make_shared<UnknownSubFile>(preinit_file));
  162. case NO_TYPE:
  163. return std::shared_ptr<SubFile>();
  164. }
  165. LOG(ERROR) << "Incorrect file type..";
  166. return std::dynamic_pointer_cast<SubFile>(std::make_shared<UnknownSubFile>(preinit_file));
  167. }
  168. FILE_TYPE SubFile::GetSubfileType(DatFile &dat, SubFile preinit_file) {
  169. // Text check based on file_id
  170. if (((unsigned long long) preinit_file.file_id_ >> 24ull) == 0x25ull)
  171. return TEXT;
  172. // Font check based on file_id
  173. if (((unsigned long long) preinit_file.file_id_ >> 24ull) == 0x42ull)
  174. return FONT;
  175. BinaryData header(64);
  176. dat.GetIO().ReadData(header, 64, (unsigned) preinit_file.file_offset_ + 8);
  177. if (header.Empty()) {
  178. LOG(ERROR) << "Unable to read file header. file_id = " << preinit_file.file_id_ << ", offset = "
  179. << preinit_file.file_offset_;
  180. return UNKNOWN;
  181. }
  182. // jpeg / dds check
  183. if (((unsigned long long) preinit_file.file_id_ >> 24ull) == 0x41ull) {
  184. long long soi = header.ToNumber<2>(24);
  185. long long marker = header.ToNumber<2>(26);
  186. //auto markerSize = header.ToNumber<short>(28);
  187. //auto four = header.ToNumber<int>(30);
  188. if ((soi == 0xD8FFll && marker == 0xE0FFll) || marker == 0xE1FFll)
  189. return JPG;
  190. return DDS;
  191. }
  192. // Ogg and Wav check
  193. if (header[8] == 0x4F && header[9] == 0x67 && header[10] == 0x67 && header[11] == 0x53)
  194. return OGG;
  195. if (header[8] == 0x52 && header[9] == 0x49 && header[10] == 0x46 && header[11] == 0x46)
  196. return WAV;
  197. return UNKNOWN;
  198. }
  199. };