Subfile.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. //
  2. // Created by Иван_Архипов on 01.11.2017.
  3. //
  4. #include "Subfile.h"
  5. #include "../BinaryData/BinaryData.h"
  6. #include "../DatFile/DatFile.h"
  7. #include "../Common/DatException.h"
  8. #include "../Database/Database.h"
  9. #include "../Common/CommonFunctions.h"
  10. #include <algorithm>
  11. const long long MAXSIZE = 50ll * 1024ll * 1024ll; // Setting maximal file size 50 MB; Files with size more than 50 mb
  12. // will be recognized as incorrect and passed.
  13. // This should be done because
  14. LOTRO_DAT::Subfile::Subfile() {
  15. ext_ = UNKNOWN;
  16. compressed_ = false;
  17. }
  18. LOTRO_DAT::Subfile::Subfile(DatFile *dat, long long fragments_count, long long unknown1, long long file_id, long long file_offset,
  19. long long file_size, long long timestamp, long long version, long long block_size) :
  20. dat_(dat), fragments_count_(fragments_count), unknown1_(unknown1), file_id_(file_id), file_offset_(file_offset),
  21. file_size_(file_size), timestamp_(timestamp), version_(version), block_size_(block_size) {
  22. if (file_size_ > MAXSIZE)
  23. throw DatException("Bad Subfile::Subfile() - File size is too much... Maybe it's incorrect..?", SUBFILE_EXCEPTION);
  24. ext_ = GetExtension();
  25. BinaryData header(20);
  26. dat_->ReadData(header, 10, file_offset_ + 8);
  27. compressed_ = header.CheckCompression();
  28. }
  29. long long LOTRO_DAT::Subfile::fragments_count() const {
  30. return fragments_count_;
  31. }
  32. long long LOTRO_DAT::Subfile::unknown1() const {
  33. return unknown1_;
  34. }
  35. long long LOTRO_DAT::Subfile::file_id() const {
  36. return file_id_;
  37. }
  38. long long LOTRO_DAT::Subfile::file_offset() const {
  39. return file_offset_;
  40. }
  41. long long LOTRO_DAT::Subfile::file_size() const {
  42. return file_size_;
  43. }
  44. long long LOTRO_DAT::Subfile::timestamp() const {
  45. return timestamp_;
  46. }
  47. long long LOTRO_DAT::Subfile::version() const {
  48. return version_;
  49. }
  50. long long LOTRO_DAT::Subfile::block_size() const {
  51. return block_size_;
  52. }
  53. LOTRO_DAT::FILE_TYPE LOTRO_DAT::Subfile::GetExtension() const {
  54. // Text check based on file_id
  55. if ((file_id_ >> 24ll) == 0x25ll)
  56. return TEXT;
  57. // Font check based on file_id
  58. if ((file_id_ >> 24ll) == 0x42ll)
  59. return FONT;
  60. BinaryData header(64);
  61. try {
  62. dat_->ReadData(header, 64, (unsigned) file_offset_ + 8);
  63. } catch (DatException &e) {
  64. if (e.type() == READ_EXCEPTION) {
  65. std::string err =
  66. "Bad Subfile::getExtension() - unable to read header of file with id = " + std::to_string(file_id()) +
  67. " and offset = " + std::to_string(file_offset());
  68. throw DatException(err.c_str(), SUBFILE_EXCEPTION);
  69. } else
  70. throw e;
  71. }
  72. // jpeg / dds check
  73. if ((file_id_ >> 24ll) == 0x41ll) {
  74. long long soi = header.ToNumber<2>(24);
  75. long long marker = header.ToNumber<2>(26);
  76. //auto markerSize = header.ToNumber<short>(28);
  77. //auto four = header.ToNumber<int>(30);
  78. if (soi == 0xD8FFll && marker == 0xE0FFll || marker == 0xE1FFll)
  79. return JPG;
  80. return DDS;
  81. }
  82. // Ogg and Wav check
  83. if (header[8] == 0x4F && header[9] == 0x67 && header[10] == 0x67 && header[11] == 0x53)
  84. return OGG;
  85. if (header[8] == 0x52 && header[9] == 0x49 && header[10] == 0x46 && header[11] == 0x46)
  86. return WAV;
  87. return UNKNOWN;
  88. }
  89. LOTRO_DAT::FILE_TYPE LOTRO_DAT::Subfile::ext() const {
  90. return ext_;
  91. }
  92. std::string LOTRO_DAT::Subfile::ExtensionToString(LOTRO_DAT::FILE_TYPE ext) const {
  93. switch (ext)
  94. {
  95. case LOTRO_DAT::TEXT:
  96. return ".txt";
  97. case LOTRO_DAT::JPG:
  98. return ".jpg";
  99. case LOTRO_DAT::DDS:
  100. return ".dds";
  101. case LOTRO_DAT::WAV:
  102. return ".wav";
  103. case LOTRO_DAT::OGG:
  104. return ".ogg";
  105. case LOTRO_DAT::FONT:
  106. return ".fontbin";
  107. case LOTRO_DAT::UNKNOWN:
  108. return ".unk";
  109. default:
  110. return "";
  111. }
  112. }
  113. bool LOTRO_DAT::Subfile::PatchBinaryFile(const LOTRO_DAT::BinaryData &file) {
  114. LOTRO_DAT::BinaryData data;
  115. // Making data for placing in .dat, depending on file extension
  116. switch (ext_)
  117. {
  118. case LOTRO_DAT::TEXT:
  119. throw LOTRO_DAT::DatException("Bad Subfile::PatchBinaryFile() - trying to patch text file");
  120. case LOTRO_DAT::JPG:
  121. data = MakeFromJPG(file); break;
  122. case LOTRO_DAT::DDS:
  123. data = MakeFromDDS(file); break;
  124. case LOTRO_DAT::WAV:
  125. data = MakeFromWAV(file); break;
  126. case LOTRO_DAT::OGG:
  127. data = MakeFromOGG(file); break;
  128. case LOTRO_DAT::FONT:
  129. data = MakeFromFont(file); break;
  130. case LOTRO_DAT::UNKNOWN:
  131. data = MakeFromUnk(file); break;
  132. default:
  133. break;
  134. }
  135. if (block_size() >= data.size() + 8) {
  136. dat_->WriteData(data, data.size(), file_offset() + 8);
  137. return true;
  138. }
  139. throw DatException("Bad Subfile::PatchBinaryFile() - new data size is bigger, than block_size. This is not implemented yet"
  140. , IMPORT_EXCEPTION);
  141. return true;
  142. }
  143. bool LOTRO_DAT::Subfile::ExportFile(const char *filename) const {
  144. try {
  145. BinaryData data;
  146. switch (ext_)
  147. {
  148. case LOTRO_DAT::TEXT:
  149. return false;
  150. case LOTRO_DAT::JPG:
  151. data = PrepareAsJPG(); break;
  152. case LOTRO_DAT::DDS:
  153. data = PrepareAsDDS(); break;
  154. case LOTRO_DAT::WAV:
  155. data = PrepareAsWAV(); break;
  156. case LOTRO_DAT::OGG:
  157. data = PrepareAsOGG(); break;
  158. case LOTRO_DAT::FONT:
  159. data = PrepareAsFont(); break;
  160. case LOTRO_DAT::UNKNOWN:
  161. data = PrepareAsUnk(); break;
  162. default:
  163. break;
  164. }
  165. std::string s = std::string(filename) + std::string(ExtensionToString(ext_));
  166. return data.WriteToFile(s);
  167. }
  168. catch (DatException &e) {
  169. fprintf(stderr, "Caught exception while unpacking the file with id %lld and offset %lld. Continuing without this file...\n", file_id(), file_offset());
  170. fprintf(stderr, "%s\n", e.what());
  171. return false;
  172. }
  173. }
  174. bool LOTRO_DAT::Subfile::ExportFile(Database *db) const {
  175. try {
  176. BinaryData data;
  177. switch (ext_)
  178. {
  179. case LOTRO_DAT::TEXT:
  180. return ExportFileAsTXT(db);
  181. case LOTRO_DAT::JPG:
  182. data = PrepareAsJPG(); break;
  183. case LOTRO_DAT::DDS:
  184. data = PrepareAsDDS(); break;
  185. case LOTRO_DAT::WAV:
  186. data = PrepareAsWAV(); break;
  187. case LOTRO_DAT::OGG:
  188. data = PrepareAsOGG(); break;
  189. case LOTRO_DAT::FONT:
  190. data = PrepareAsFont(); break;
  191. case LOTRO_DAT::UNKNOWN:
  192. data = PrepareAsUnk(); break;
  193. default:
  194. break;
  195. }
  196. db->PushBinaryFile(file_id_, data);
  197. return true;
  198. }
  199. catch (DatException &e) {
  200. fprintf(stderr, "Caught exception while unpacking the file with id %lld and offset %lld. Continuing without this file...\n", file_id(), file_offset());
  201. fprintf(stderr, "%s\n", e.what());
  202. return false;
  203. }
  204. }
  205. bool LOTRO_DAT::Subfile::ExportFileAsTXT(Database *db) const {
  206. if (db == nullptr) {
  207. return false;
  208. }
  209. if (file_size() == 10) // File is empty, nothing to do;
  210. return false;
  211. BinaryData data = GetFileData();
  212. long long offset = 9; // first 4 bytes - file_id, then 4 bytes - unknown, then 1 byte - unknown
  213. long long text_fragment_num = data.ToNumber<1>(offset);
  214. if ((text_fragment_num & 0x80) != 0) {
  215. text_fragment_num = (((text_fragment_num ^ 0x80) << 8) | data.ToNumber<1>(offset + 1));
  216. offset += 1;
  217. }
  218. offset += 1;
  219. for (long long i = 0; i < text_fragment_num; i++) {
  220. long long fragment_id = data.ToNumber<8>(offset);
  221. offset += 8;
  222. long long num_pieces = data.ToNumber<4>(offset);
  223. offset += 4;
  224. std::vector<std::u16string> text_pieces;
  225. std::vector<long long> arg_references;
  226. std::vector<std::vector<BinaryData> > arg_strings;
  227. for (long long j = 0; j < num_pieces; j++) {
  228. long long piece_size = data.ToNumber<1>(offset);
  229. if ((piece_size & 128) != 0) {
  230. piece_size = (((piece_size ^ 128) << 8) | data.ToNumber<1>(offset + 1));
  231. offset += 1;
  232. }
  233. offset += 1;
  234. BinaryData piece_data = data.CutData(offset, offset + piece_size * 2);
  235. std::u16string piece;
  236. for (long long k = 0; k < piece_size; k++) {
  237. char16_t c = char16_t(((short(piece_data[2 * k + 1])) << 8) | (short(piece_data[2 * k])));
  238. piece += c;
  239. }
  240. text_pieces.push_back(piece);
  241. offset += piece_size * 2;
  242. }
  243. long long num_references = data.ToNumber<4>(offset);
  244. offset += 4;
  245. for (long long j = 0; j < num_references; j++) {
  246. arg_references.emplace_back(data.ToNumber<4>(offset));
  247. offset += 4;
  248. }
  249. long long num_arg_strings = data.ToNumber<1>(offset);
  250. offset += 1;
  251. for (long long j = 0; j < num_arg_strings; j++) {
  252. long long num_args = data.ToNumber<4>(offset);
  253. offset += 4;
  254. arg_strings.emplace_back();
  255. for (long long k = 0; k < num_args; k++) {
  256. long long string_size = data.ToNumber<1>(offset);
  257. if ((string_size & 0x80) != 0) {
  258. string_size = (((string_size ^ 0x80) << 8) | data.ToNumber<1>(offset + 1));
  259. offset += 1;
  260. }
  261. offset += 1;
  262. arg_strings[j].emplace_back(data.CutData(offset, offset + string_size * 2));
  263. offset += string_size * 2;
  264. }
  265. }
  266. std::u16string text = u"[";
  267. for (int i = 0; i + 1 < text_pieces.size(); i++)
  268. text += text_pieces[i] + u"<--DO_NOT_TOUCH!-->";
  269. text += text_pieces[text_pieces.size() - 1] + u"]";
  270. std::string arguments;
  271. for (int i = 0; i + 1 < arg_references.size(); i++)
  272. arguments += std::to_string(arg_references[i]) + "-";
  273. if (arg_references.size() >= 1)
  274. arguments += std::to_string(arg_references[arg_references.size() - 1]);
  275. db->PushTextFile(file_id(), fragment_id, text.c_str(), arguments.c_str());
  276. }
  277. return true;
  278. }
  279. LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::GetFileData(long long offset) const {
  280. BinaryData mfile_id(4);
  281. dat_->ReadData(mfile_id, 4, file_offset() + 8);
  282. if (file_id() != mfile_id.ToNumber<4>(0))
  283. throw DatException("Bad Subfile::GetFileData() - file_id doesn't match to dictionary", READ_EXCEPTION);
  284. BinaryData data((unsigned)(file_size()));
  285. if (block_size() >= file_size() + 8) {
  286. dat_->ReadData(data, file_size(), file_offset() + offset);
  287. return data;
  288. }
  289. BinaryData fragments_count(4);
  290. dat_->ReadData(fragments_count, 4, file_offset());
  291. long long fragments_number = fragments_count.ToNumber<4>(0);
  292. long long current_block_size = block_size() - offset - 8 * fragments_number;
  293. dat_->ReadData(data, current_block_size , file_offset() + offset);
  294. BinaryData FragmentsDictionary(8 * unsigned(fragments_number));
  295. dat_->ReadData(FragmentsDictionary, 8 * unsigned(fragments_number), file_offset() + block_size() - 8 * fragments_number);
  296. for (long long i = 0; i < fragments_number; i++) {
  297. long long fragment_size = FragmentsDictionary.ToNumber<4>(8 * i);
  298. long long fragment_offset = FragmentsDictionary.ToNumber<4>(8 * i + 4);
  299. dat_->ReadData(data, std::min(fragment_size, file_size() - current_block_size), fragment_offset, current_block_size );
  300. current_block_size += fragment_size;
  301. }
  302. return data;
  303. }
  304. const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::PrepareAsJPG() const {
  305. return GetFileData().CutData(24);
  306. }
  307. const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::PrepareAsDDS() const {
  308. BinaryData data = GetFileData();
  309. if (compressed_)
  310. data.DecompressData(4);
  311. BinaryData ddsData(data.size() - 24 + 128);
  312. for (int i = 0; i < 128; i++)
  313. ddsData[i] = 0;
  314. memcpy(ddsData.data() + 128, data.data() + 24, data.size() - 24);
  315. ddsData[0] = 0x44; // D
  316. ddsData[1] = 0x44; // D
  317. ddsData[2] = 0x53; // S
  318. ddsData[3] = 0x20;
  319. ddsData[4] = 0x7C;
  320. ddsData[8] = 7;
  321. ddsData[9] = 0x10;
  322. // width, height
  323. ddsData[12] = data[12];
  324. ddsData[13] = data[13];
  325. ddsData[14] = data[14];
  326. ddsData[15] = data[15];
  327. ddsData[16] = data[8];
  328. ddsData[17] = data[9];
  329. ddsData[18] = data[10];
  330. ddsData[19] = data[11];
  331. long long compression = data.ToNumber<4>(0x10);
  332. switch (compression) {
  333. case 20: // 14 00 00 00 - 888 (R8G8B8)
  334. ddsData[0x4C] = 0x20; // ?
  335. ddsData[0x50] = 0x40; // compressed or not
  336. ddsData[0x58] = 0x18; // bytes per pixel
  337. ddsData[0x5E] = 0xFF;
  338. ddsData[0x61] = 0xFF;
  339. ddsData[0x64] = 0xFF;
  340. break;
  341. case 21: // 15 00 00 00 - 8888 (R8G8B8A8)
  342. ddsData[0x4C] = 0x20; // ?
  343. ddsData[0x50] = 0x40; // compressed or not
  344. ddsData[0x58] = 0x20; // bytes per pixel
  345. ddsData[0x5E] = 0xFF;
  346. ddsData[0x61] = 0xFF;
  347. ddsData[0x64] = 0xFF;
  348. ddsData[0x6B] = 0xFF;
  349. break;
  350. case 28: // 1C 00 00 00 - 332 (?)
  351. ddsData[0x4C] = 0x20; // ?
  352. ddsData[0x50] = 0x40; // compressed or not
  353. ddsData[0x58] = 0x08; // bytes per pixel
  354. ddsData[0x5E] = 0xFF;
  355. ddsData[0x61] = 0xFF;
  356. ddsData[0x64] = 0xFF;
  357. break;
  358. case 827611204: // 44 58 54 31 - DXT1
  359. ddsData[76] = 32;
  360. ddsData[80] = 4;
  361. ddsData[84] = 68;
  362. ddsData[85] = 88;
  363. ddsData[86] = 84;
  364. ddsData[87] = 49;
  365. break;
  366. case 861165636: // 44 58 54 33 - DXT3
  367. ddsData[22] = 1;
  368. ddsData[76] = 32;
  369. ddsData[80] = 4;
  370. ddsData[84] = 68;
  371. ddsData[85] = 88;
  372. ddsData[86] = 84;
  373. ddsData[87] = 51;
  374. ddsData[108] = 8;
  375. ddsData[109] = 16;
  376. ddsData[110] = 64;
  377. break;
  378. case 894720068: // 44 58 54 35 - DXT5
  379. ddsData[10] = 8;
  380. ddsData[22] = 1;
  381. ddsData[28] = 1;
  382. ddsData[76] = 32;
  383. ddsData[80] = 4;
  384. ddsData[84] = 68;
  385. ddsData[85] = 88;
  386. ddsData[86] = 84;
  387. ddsData[87] = 53;
  388. ddsData[88] = 32;
  389. ddsData[94] = 255;
  390. ddsData[97] = 255;
  391. ddsData[100] = 255;
  392. ddsData[107] = 255;
  393. ddsData[109] = 16;
  394. break;
  395. }
  396. return ddsData;
  397. }
  398. const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::PrepareAsOGG() const {
  399. return GetFileData().CutData(8);
  400. }
  401. const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::PrepareAsFont() const {
  402. return GetFileData();
  403. }
  404. const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::PrepareAsWAV() const {
  405. return GetFileData().CutData(8);
  406. }
  407. const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::PrepareAsUnk() const {
  408. return GetFileData();
  409. }
  410. const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::MakeFromJPG(const LOTRO_DAT::BinaryData &file) const {
  411. return GetFileData().CutData(0, 24) + file;
  412. }
  413. const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::MakeFromDDS(const LOTRO_DAT::BinaryData &file) const {
  414. throw LOTRO_DAT::DatException("Bad Subfile::MakeFromDDS() - not implemented yet", IMPORT_EXCEPTION);
  415. }
  416. const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::MakeFromOGG(const LOTRO_DAT::BinaryData &file) const {
  417. throw LOTRO_DAT::DatException("Bad Subfile::MakeFromOGG() - not implemented yet", IMPORT_EXCEPTION);
  418. }
  419. const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::MakeFromFont(const LOTRO_DAT::BinaryData &file) const {
  420. throw LOTRO_DAT::DatException("Bad Subfile::MakeFromFont() - not implemented yet", IMPORT_EXCEPTION);
  421. }
  422. const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::MakeFromWAV(const LOTRO_DAT::BinaryData &file) const {
  423. throw LOTRO_DAT::DatException("Bad Subfile::MakeFromWAV() - not implemented yet", IMPORT_EXCEPTION);
  424. }
  425. const LOTRO_DAT::BinaryData LOTRO_DAT::Subfile::MakeFromUnk(const LOTRO_DAT::BinaryData &file) const {
  426. throw LOTRO_DAT::DatException("Bad Subfile::MakeFromUnk() - not implemented yet", IMPORT_EXCEPTION);
  427. }