TextSubfile.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. //
  2. // Created by Иван_Архипов on 24.11.2017.
  3. //
  4. #include "Subfiles/TextSubfile.h"
  5. #include "BinaryData.h"
  6. #include "DatFile.h"
  7. #include "DatException.h"
  8. #include "SubfileData.h"
  9. #include <algorithm>
  10. #include <codecvt>
  11. #include <locale>
  12. std::u16string to_utf16(long long x) {
  13. std::u16string res;
  14. while (x > 0) {
  15. res += char16_t(u'0' + x % 10);
  16. x /= 10ll;
  17. }
  18. std::reverse(res.begin(), res.end());
  19. return res;
  20. }
  21. long long from_utf16(const std::u16string &num) {
  22. long long res = 0;
  23. for (auto c : num) {
  24. res = res * 10ll + (c - u'0');
  25. }
  26. return res;
  27. }
  28. std::string argumentsFromUtf16(const std::u16string &args) {
  29. std::string res;
  30. size_t pointer = 0;
  31. while (pointer < args.length()) {
  32. size_t pointer1 = args.find(u'-', pointer);
  33. if (pointer1 == std::u16string::npos)
  34. pointer1 = args.length();
  35. if (!res.empty())
  36. res += "-";
  37. res += std::to_string(from_utf16(args.substr(pointer, pointer1 - pointer)));
  38. pointer = pointer1 + 1;
  39. }
  40. return res;
  41. }
  42. namespace LOTRO_DAT {
  43. TextSubfile::TextSubfile() = default;
  44. TextSubfile::TextSubfile(DatFile *dat, long long dictionary_offset, long long fragments_count, long long unknown1,
  45. long long file_id, long long file_offset, long long file_size,
  46. long long timestamp,
  47. long long version, long long block_size)
  48. : Subfile(dat, dictionary_offset, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
  49. }
  50. FILE_TYPE TextSubfile::FileType() const {
  51. return TEXT;
  52. }
  53. std::string TextSubfile::Extension() const {
  54. return std::string(".txt");
  55. }
  56. SubfileData TextSubfile::PrepareForExport(const BinaryData &file_data) {
  57. SubfileData result;
  58. long long offset = 9; // first 4 bytes - file_id, then 4 bytes - unknown, then 1 byte - unknown
  59. long long text_fragment_num = file_data.ToNumber<1>(offset);
  60. if ((text_fragment_num & 0x80) != 0) {
  61. text_fragment_num = (((text_fragment_num ^ 0x80) << 8) | file_data.ToNumber<1>(offset + 1));
  62. offset += 1;
  63. }
  64. offset += 1;
  65. for (long long i = 0; i < text_fragment_num; i++) {
  66. long long fragment_id = file_data.ToNumber<8>(offset);
  67. offset += 8;
  68. std::vector<std::u16string> text_pieces = MakePieces(file_data, offset);
  69. std::vector<long long> arg_references = MakeArgumentReferences(file_data, offset);
  70. std::vector<std::vector<BinaryData>> arg_strings = MakeArgumentStrings(file_data, offset);
  71. std::u16string text = u"[";
  72. for (size_t j = 0; j + 1 < text_pieces.size(); j++)
  73. text += text_pieces[j] + u"<--DO_NOT_TOUCH!-->";
  74. text += text_pieces[text_pieces.size() - 1] + u"]";
  75. std::u16string arguments;
  76. for (size_t j = 0; j + 1 < arg_references.size(); j++)
  77. arguments += to_utf16(arg_references[j]) + u"-";
  78. if (!arg_references.empty())
  79. arguments += to_utf16(arg_references[arg_references.size() - 1]);
  80. if (result.text_data.length() > 0)
  81. result.text_data += u"|||";
  82. result.text_data += to_utf16(fragment_id) + u":::";
  83. result.text_data += arguments + u":::";
  84. result.text_data += text;
  85. }
  86. result.options["fid"] = file_id();
  87. result.options["ext"] = Extension();
  88. return result;
  89. }
  90. BinaryData TextSubfile::MakeForImport(const BinaryData &old_data, const SubfileData &data) {
  91. std::unordered_map<long long, SubfileData> patch_fragments = ParsePatchFragments(data);
  92. BinaryData new_data;
  93. if (file_size() <= 10 + 8) // File is empty, nothing to do;
  94. return old_data;
  95. long long offset = 9 + 8; // first 8 bytes - file_info. After them:
  96. // first 4 bytes - file_id, then 4 bytes - unknown, then 1 byte - unknown
  97. long long text_fragment_num = old_data.ToNumber<1>(offset);
  98. if ((text_fragment_num & 0x80) != 0) {
  99. text_fragment_num = (((text_fragment_num ^ 0x80) << 8) | old_data.ToNumber<1>(offset + 1));
  100. offset += 1;
  101. }
  102. offset += 1;
  103. new_data = new_data + old_data.CutData(0, offset); // Adding file info
  104. for (long long i = 0; i < text_fragment_num; i++) {
  105. long long fragment_id = old_data.ToNumber<8>(offset);
  106. offset += 8;
  107. new_data = new_data + old_data.CutData(offset - 8, offset);
  108. if (patch_fragments.count(fragment_id) == 0) {
  109. try {
  110. // Retrieving old pieces
  111. new_data = new_data + GetPieceData(old_data, offset);
  112. } catch (std::exception &e) {
  113. fprintf(stderr, "Caught %s exception.\n", e.what());
  114. fprintf(stderr, "ERROR TextSubfile::MakeForImport() - unable to get piece data for file_id %lld and fragment_id %lld", file_id(), fragment_id);
  115. throw DatException("Bad TextSubfile::MakeForImport()", IMPORT_EXCEPTION);
  116. }
  117. try {
  118. // Retrieving old references
  119. new_data = new_data + GetArgumentReferenceData(old_data, offset);
  120. } catch (std::exception &e) {
  121. fprintf(stderr, "Caught %s exception.\n", e.what());
  122. fprintf(stderr, "ERROR TextSubfile::MakeForImport() - unable to get argument reference data for file_id %lld and fragment_id %lld", file_id(), fragment_id);
  123. throw DatException("Bad TextSubfile::MakeForImport()", IMPORT_EXCEPTION);
  124. }
  125. try {
  126. // Retrieving old ref_strings
  127. new_data = new_data + GetArgumentStringsData(old_data, offset);
  128. } catch (std::exception &e) {
  129. fprintf(stderr, "Caught %s exception.\n", e.what());
  130. fprintf(stderr, "ERROR TextSubfile::MakeForImport() - unable to get argument string for file_id %lld and fragment_id %lld", file_id(), fragment_id);
  131. throw DatException("Bad TextSubfile::MakeForImport()", IMPORT_EXCEPTION);
  132. }
  133. } else {
  134. try {
  135. // Making and adding new pieces
  136. new_data = new_data + BuildPieces(old_data, patch_fragments[fragment_id], offset);
  137. } catch (std::exception &e) {
  138. fprintf(stderr, "Caught %s exception.\n", e.what());
  139. fprintf(stderr, "ERROR TextSubfile::MakeForImport() - unable to build piece data for file_id %lld and fragment_id %lld", file_id(), fragment_id);
  140. throw DatException("Bad TextSubfile::MakeForImport()", IMPORT_EXCEPTION);
  141. }
  142. try {
  143. // Making and adding new references
  144. new_data = new_data + BuildArgumentReferences(old_data, patch_fragments[fragment_id], offset);
  145. } catch (std::exception &e) {
  146. fprintf(stderr, "Caught %s exception.\n", e.what());
  147. fprintf(stderr, "ERROR TextSubfile::MakeForImport() - unable to build argument references data for file_id %lld and fragment_id %lld", file_id(), fragment_id);
  148. throw DatException("Bad TextSubfile::MakeForImport()", IMPORT_EXCEPTION);
  149. }
  150. try {
  151. // Making and adding new strings
  152. new_data = new_data + BuildArgumentStrings(old_data, patch_fragments[fragment_id], offset);
  153. } catch (std::exception &e) {
  154. fprintf(stderr, "Caught %s exception.\n", e.what());
  155. fprintf(stderr, "ERROR TextSubfile::MakeForImport() - unable to build argument strings data for file_id %lld and fragment_id %lld", file_id(), fragment_id);
  156. throw DatException("Bad TextSubfile::MakeForImport()", IMPORT_EXCEPTION);
  157. }
  158. }
  159. }
  160. new_data = new_data + old_data.CutData(offset); // Adding elapsed file data
  161. return new_data;
  162. }
  163. std::unordered_map<long long, SubfileData> TextSubfile::ParsePatchFragments(const SubfileData &data) {
  164. std::unordered_map<long long, SubfileData> res;
  165. std::u16string text = data.text_data;
  166. size_t pointer = 0;
  167. while (pointer < text.length()) {
  168. // Parsing fragment_id
  169. size_t pointer1 = text.find(u":::", pointer);
  170. if (pointer1 == std::u16string::npos)
  171. throw DatException("Bad TextSubfile::ParsePatchFragments() - Unable to parse fragment id! Cannot find '...' divider");
  172. long long fragment_id = from_utf16(text.substr(pointer, pointer1 - pointer));
  173. pointer = pointer1 + 3;
  174. res[fragment_id] = SubfileData();
  175. res[fragment_id].options["gid"] = fragment_id;
  176. // Parsing arguments
  177. pointer1 = text.find(u":::", pointer);
  178. if (pointer1 == std::u16string::npos)
  179. throw DatException("Bad TextSubfile::ParsePatchFragments() - Unable to parse arguments! Cannot find '...' divider");
  180. std::u16string arguments = text.substr(pointer, pointer1 - pointer);
  181. pointer = pointer1 + 3;
  182. if (arguments.length() > 0) {
  183. res[fragment_id].options["args"] = argumentsFromUtf16(arguments);
  184. }
  185. // Parsing text
  186. pointer1 = text.find(u"|||", pointer);
  187. if (pointer1 == std::u16string::npos)
  188. pointer1 = text.length();
  189. std::u16string text_data = text.substr(pointer, pointer1 - pointer);
  190. pointer = pointer1 + 3;
  191. res[fragment_id].text_data = text_data;
  192. }
  193. return res;
  194. }
  195. // Make pieces/arguments/argument strings functions
  196. std::vector<std::u16string> TextSubfile::MakePieces(const BinaryData &data, long long &offset) {
  197. long long num_pieces = data.ToNumber<4>(offset);
  198. offset += 4;
  199. std::vector<std::u16string> text_pieces;
  200. for (long long j = 0; j < num_pieces; j++) {
  201. long long piece_size = data.ToNumber<1>(offset);
  202. if ((piece_size & 128) != 0) {
  203. piece_size = (((piece_size ^ 128) << 8) | data.ToNumber<1>(offset + 1));
  204. offset += 1;
  205. }
  206. offset += 1;
  207. BinaryData piece_data = data.CutData(offset, offset + piece_size * 2);
  208. std::u16string piece;
  209. for (long long k = 0; k < piece_size; k++) {
  210. char16_t c = char16_t(
  211. ((short(piece_data[2 * unsigned(k) + 1])) << 8) | (short(piece_data[2 * unsigned(k)])));
  212. piece += c;
  213. }
  214. text_pieces.push_back(piece);
  215. offset += piece_size * 2;
  216. }
  217. return text_pieces;
  218. }
  219. std::vector<long long> TextSubfile::MakeArgumentReferences(const BinaryData &data, long long &offset) {
  220. std::vector<long long> arg_references;
  221. long long num_references = data.ToNumber<4>(offset);
  222. offset += 4;
  223. for (long long j = 0; j < num_references; j++) {
  224. arg_references.emplace_back(data.ToNumber<4>(offset));
  225. offset += 4;
  226. }
  227. return arg_references;
  228. }
  229. std::vector<std::vector<BinaryData>> TextSubfile::MakeArgumentStrings(const BinaryData &data, long long &offset) {
  230. std::vector<std::vector<BinaryData> > arg_strings;
  231. long long num_arg_strings = data.ToNumber<1>(offset);
  232. offset += 1;
  233. for (long long j = 0; j < num_arg_strings; j++) {
  234. long long num_args = data.ToNumber<4>(offset);
  235. offset += 4;
  236. arg_strings.emplace_back();
  237. for (long long k = 0; k < num_args; k++) {
  238. long long string_size = data.ToNumber<1>(offset);
  239. if ((string_size & 0x80) != 0) {
  240. string_size = (((string_size ^ 0x80) << 8) | data.ToNumber<1>(offset + 1));
  241. offset += 1;
  242. }
  243. offset += 1;
  244. arg_strings[unsigned(j)].emplace_back(data.CutData(offset, offset + string_size * 2));
  245. offset += string_size * 2;
  246. }
  247. }
  248. return arg_strings;
  249. }
  250. // Build pieces/arguments/argument strings functions from fragment SubfileData
  251. BinaryData TextSubfile::BuildPieces(const BinaryData &data, const SubfileData &new_data, long long &offset) {
  252. try {
  253. // Moving &offset pointer in &data
  254. GetPieceData(data, offset);
  255. } catch (std::exception &e) {
  256. fprintf(stderr, "Caught %s exception.\n", e.what());
  257. fprintf(stderr, "ERROR TextSubfile::BuildPieces() - unable to get piece data for file_id %lld", file_id());
  258. throw DatException("Bad TextSubfile::BuildPieces()", IMPORT_EXCEPTION);
  259. }
  260. // Deleting '[' and ']' brackets
  261. std::u16string text_data = new_data.text_data.substr(1, new_data.text_data.size() - 2);
  262. std::vector<std::u16string> pieces;
  263. const std::u16string DNT = u"<--DO_NOT_TOUCH!-->";
  264. size_t prev = 0;
  265. size_t next = text_data.find(DNT, prev);
  266. while (next != std::string::npos) {
  267. std::u16string piece = text_data.substr(prev, next - prev);
  268. pieces.push_back(piece);
  269. prev = next + DNT.length();
  270. next = text_data.find(DNT, prev);
  271. }
  272. std::u16string piece = text_data.substr(prev);
  273. pieces.push_back(piece);
  274. // Building BinaryData from pieces
  275. BinaryData result;
  276. BinaryData temp_data = BinaryData::FromNumber<4>(pieces.size());
  277. result = result + temp_data;
  278. for (long long i = 0; i < pieces.size(); i++) {
  279. long long piece_size = pieces[i].length();
  280. if (piece_size < 128) {
  281. temp_data = BinaryData::FromNumber<1>(piece_size);
  282. } else {
  283. temp_data = BinaryData::FromNumberRAW<2>((piece_size | 32768));
  284. }
  285. result = result + temp_data;
  286. for (long long j = 0; j < piece_size; j++) {
  287. temp_data = BinaryData::FromNumber<2>(short(pieces[i][j]));
  288. result = result + temp_data;
  289. }
  290. }
  291. return result;
  292. }
  293. BinaryData TextSubfile::BuildArgumentReferences(const BinaryData &data, const SubfileData &new_data,
  294. long long &offset) {
  295. // Moving &offset pointer in &data
  296. GetArgumentReferenceData(data, offset);
  297. // If there are no args - making 4 null-bytes and return;
  298. if (!new_data.options["args"]) {
  299. BinaryData result = BinaryData::FromNumber<4>(0);
  300. return result;
  301. }
  302. // Parsing arguments from list in options["args"]
  303. std::string args_list = new_data.options["args"].as<std::string>();
  304. std::vector<long long> arguments;
  305. size_t prev = 0;
  306. size_t next = args_list.find('-', prev);
  307. while (next != std::string::npos) {
  308. std::string argument = args_list.substr(prev, next - prev);
  309. arguments.push_back(std::stoll(argument));
  310. prev = next + 1;
  311. next = args_list.find('-', prev);
  312. }
  313. std::string argument = args_list.substr(prev);
  314. arguments.push_back(std::stoll(argument));
  315. BinaryData result;
  316. BinaryData temp_data = BinaryData::FromNumber<4>(arguments.size());
  317. result = result + temp_data;
  318. for (auto arg_reference : arguments) {
  319. temp_data = BinaryData::FromNumber<4>(arg_reference);
  320. result = result + temp_data;
  321. }
  322. return result;
  323. }
  324. BinaryData TextSubfile::BuildArgumentStrings(const BinaryData &data, const SubfileData &new_data,
  325. long long &offset) {
  326. return GetArgumentStringsData(data, offset);
  327. }
  328. // Get BinaryData contents of pieces/arguments/argument strings
  329. BinaryData TextSubfile::GetPieceData(const BinaryData &data, long long &offset) const {
  330. long long old_offset = offset;
  331. long long num_pieces = data.ToNumber<4>(offset);
  332. offset += 4;
  333. for (long long j = 0; j < num_pieces; j++) {
  334. long long piece_size = data.ToNumber<1>(offset);
  335. if ((piece_size & 128) != 0) {
  336. piece_size = (((piece_size ^ 128) << 8) | data.ToNumber<1>(offset + 1));
  337. offset += 1;
  338. }
  339. offset += 1;
  340. offset += piece_size * 2;
  341. }
  342. return data.CutData(old_offset, offset);
  343. }
  344. BinaryData TextSubfile::GetArgumentReferenceData(const BinaryData &data, long long &offset) const {
  345. long long old_offset = offset;
  346. long long num_references = data.ToNumber<4>(offset);
  347. offset += 4;
  348. offset += 4 * num_references;
  349. return data.CutData(old_offset, offset);
  350. }
  351. BinaryData TextSubfile::GetArgumentStringsData(const BinaryData &data, long long &offset) const {
  352. long long old_offset = offset;
  353. long long num_arg_strings = data.ToNumber<1>(offset);
  354. offset += 1;
  355. for (long long j = 0; j < num_arg_strings; j++) {
  356. long long num_args = data.ToNumber<4>(offset);
  357. offset += 4;
  358. for (long long k = 0; k < num_args; k++) {
  359. long long string_size = data.ToNumber<1>(offset);
  360. if ((string_size & 0x80) != 0) {
  361. string_size = (((string_size ^ 0x80) << 8) | data.ToNumber<1>(offset + 1));
  362. offset += 1;
  363. }
  364. offset += 1;
  365. offset += string_size * 2;
  366. }
  367. }
  368. return data.CutData(old_offset, offset);
  369. }
  370. };