FieldTextWriter.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /*
  2. * Copyright (C) 2022 The V-Gears Team
  3. *
  4. * This file is part of V-Gears
  5. *
  6. * V-Gears is free software: you can redistribute it and/or modify it under
  7. * terms of the GNU General Public License as published by the Free Software
  8. * Foundation, version 3.0 (GPLv3) of the License.
  9. *
  10. * V-Gears is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. */
  15. #include <iostream>
  16. #include "FieldTextWriter.h"
  17. #include "Characters.h"
  18. #include "common/VGearsStringUtil.h"
  19. void FieldTextWriter::Begin(std::string file_name){
  20. data_.clear();
  21. logger_.reset(new Logger(file_name));
  22. // Write BOM
  23. /* FIX ME - It needs to write data as UTF8.
  24. std::vector< unsigned char > bomBytes;
  25. bomBytes.push_back(0xFF);
  26. bomBytes.push_back(0xFE);
  27. logger_->Log(bomBytes);
  28. */
  29. tag_open_ = false;
  30. logger_->Log("<texts>\n");
  31. }
  32. u16 FieldTextWriter::GetU16LE(u32 offset){
  33. if (offset+1 >= data_.size()) throw std::out_of_range("");
  34. return *reinterpret_cast<u16*>(&data_[offset]);
  35. }
  36. u8 FieldTextWriter::GetU8(u32 offset){
  37. if (offset >= data_.size()) throw std::out_of_range("");
  38. return data_[offset];
  39. }
  40. void FieldTextWriter::Write(const std::vector<u8>& script_section_buffer, std::string field_name, bool english){
  41. data_ = script_section_buffer;
  42. // Get sector 1 offset (scripts and dialog).
  43. u32 offset_to_sector = 0; // Our buffer IS the start of scripts and dialog.
  44. /*
  45. u16 unknown1; // Always 0x0502.
  46. char nEntities; // Number of entities.
  47. char nModels; // Number of models.
  48. u16 wStringOffset; // Offset to strings code below extracts this offset.
  49. */
  50. u16 offset_to_dialogs = GetU16LE(offset_to_sector + 0x04);
  51. std::vector< unsigned char > dialog; // text line to write out
  52. // HACK: If this function throws then this iteration generates invalid XML
  53. // so close the unclosed XML tag - this function should be made thread safe
  54. // and/or fix the fields that fail to convert correctly.
  55. if (tag_open_){
  56. logger_->Log("[ERROR exception]");
  57. logger_->Log("</dialog>\n");
  58. }
  59. u16 dialogCount = GetU16LE(offset_to_sector + offset_to_dialogs);
  60. // The first string comes right after the table of offsets, so its offset says how many strings
  61. // there are. A field with 256 of them has a count that doesn't fit (it is read as 0), and the
  62. // field would have no texts at all.
  63. if (offset_to_sector + offset_to_dialogs + 0x03 < data_.size()){
  64. const u16 first_offset = GetU16LE(offset_to_sector + offset_to_dialogs + 0x02);
  65. if (first_offset >= 0x02 && (first_offset % 2) == 0){
  66. const u16 count_from_table = (first_offset - 0x02) / 2;
  67. if (count_from_table > dialogCount) dialogCount = count_from_table;
  68. }
  69. }
  70. for (u16 i = 0; i <dialogCount; ++ i){
  71. // gGt offset of string data.
  72. u32 offset = offset_to_sector + offset_to_dialogs + GetU16LE(
  73. offset_to_sector + offset_to_dialogs
  74. + 0x02 + // +2 to skip dialog count
  75. i * 0x02 // *2 because each char is 2 bytes
  76. );
  77. AddText(dialog);
  78. logger_->Log("<dialog name=\"" + field_name + "_" + std::to_string(i) + "\">");
  79. tag_open_ = true;
  80. // Some maps have unclosed color tags.
  81. // Use this counter to close them at the end.
  82. int colour_opened = 0;
  83. for (unsigned char temp = 0x00;; ++ offset){
  84. temp = GetU8(offset);
  85. if (temp == 0xFF) break;
  86. if (temp == 0xE0 && english == true){
  87. dialog.push_back(ENGLISH_CHARS[0x00]);
  88. dialog.push_back(ENGLISH_CHARS[0x00]);
  89. dialog.push_back(ENGLISH_CHARS[0x00]);
  90. dialog.push_back(ENGLISH_CHARS[0x00]);
  91. dialog.push_back(ENGLISH_CHARS[0x00]);
  92. dialog.push_back(ENGLISH_CHARS[0x00]);
  93. dialog.push_back(ENGLISH_CHARS[0x00]);
  94. dialog.push_back(ENGLISH_CHARS[0x00]);
  95. dialog.push_back(ENGLISH_CHARS[0x00]);
  96. dialog.push_back(ENGLISH_CHARS[0x00]);
  97. }
  98. else if (temp == 0xE1 && english == true){
  99. dialog.push_back(ENGLISH_CHARS[0x00]);
  100. dialog.push_back(ENGLISH_CHARS[0x00]);
  101. dialog.push_back(ENGLISH_CHARS[0x00]);
  102. dialog.push_back(ENGLISH_CHARS[0x00]);
  103. }
  104. else if (temp == 0xE2 && english == true){
  105. dialog.push_back(ENGLISH_CHARS[0x0C]);
  106. dialog.push_back(ENGLISH_CHARS[0x00]);
  107. }
  108. else if (temp == 0xE8){
  109. AddText(dialog);
  110. logger_->Log("<next_page />");
  111. }
  112. else if (temp == 0xEA){
  113. AddText(dialog);
  114. logger_->Log("<character id=\"0\" />");
  115. }
  116. else if (temp == 0xEB){
  117. AddText(dialog);
  118. logger_->Log("<character id=\"1\" />");
  119. }
  120. else if (temp == 0xEC){
  121. AddText(dialog);
  122. logger_->Log("<character id=\"2\" />");
  123. }
  124. else if (temp == 0xED){
  125. AddText(dialog);
  126. logger_->Log("<character id=\"3\" />");
  127. }
  128. else if (temp == 0xEE){
  129. AddText(dialog);
  130. logger_->Log("<character id=\"4\" />");
  131. }
  132. else if (temp == 0xEF){
  133. AddText(dialog);
  134. logger_->Log("<character id=\"5\" />");
  135. }
  136. else if (temp == 0xF0){
  137. AddText(dialog);
  138. logger_->Log("<character id=\"6\" />");
  139. }
  140. else if (temp == 0xF1){
  141. AddText(dialog);
  142. logger_->Log("<character id=\"7\" />");
  143. }
  144. else if (temp == 0xF2){
  145. AddText(dialog);
  146. logger_->Log("<character id=\"8\" />");
  147. }
  148. else if (temp == 0xF3){
  149. AddText(dialog);
  150. logger_->Log("<party pos=\"0\" />");
  151. }
  152. else if (temp == 0xF4){
  153. AddText(dialog);
  154. logger_->Log("<party pos=\"1\" />");
  155. }
  156. else if (temp == 0xF5){
  157. AddText(dialog);
  158. logger_->Log("<party pos=\"2\" />");
  159. }
  160. else if (temp == 0xF6){
  161. AddText(dialog);
  162. logger_->Log("<image sprite=\"ButtonCircle\" />");
  163. }
  164. else if (temp == 0xF7){
  165. AddText(dialog);
  166. logger_->Log("<image sprite=\"ButtonTriangle\" />");
  167. }
  168. else if (temp == 0xF8){
  169. AddText(dialog);
  170. logger_->Log("<image sprite=\"ButtonSquare\" />");
  171. }
  172. else if (temp == 0xF9){
  173. AddText(dialog);
  174. logger_->Log("<image sprite=\"ButtonCross\" />");
  175. }
  176. else if ((temp == 0xFA || temp == 0xFB || temp == 0xFC || temp == 0xFD) && english == false){
  177. ++ offset;
  178. unsigned char temp2 = GetU8(offset);
  179. if (temp == 0xFA){
  180. if (JAPANESE_CHARS_FA[temp2] != 0x0000){
  181. dialog.push_back(JAPANESE_CHARS_FA[temp2] >> 8);
  182. dialog.push_back(JAPANESE_CHARS_FA[temp2] & 0xFF);
  183. }
  184. else{
  185. AddText(dialog);
  186. logger_->Log("[MISSING 0xFA " + HexToString(temp2, 2, '0') + "]");
  187. }
  188. }
  189. else if (temp == 0xFB){
  190. if (JAPANESE_CHARS_FB[temp2] != 0x0000){
  191. dialog.push_back(JAPANESE_CHARS_FB[temp2] >> 8);
  192. dialog.push_back(JAPANESE_CHARS_FB[temp2] & 0xFF);
  193. }
  194. else{
  195. AddText(dialog);
  196. logger_->Log("[MISSING 0xFB " + HexToString(temp2, 2, '0') + "]");
  197. }
  198. }
  199. else if (temp == 0xFC){
  200. if (JAPANESE_CHARS_FC[temp2] != 0x0000){
  201. dialog.push_back(JAPANESE_CHARS_FC[temp2] >> 8);
  202. dialog.push_back(JAPANESE_CHARS_FC[temp2] & 0xFF);
  203. }
  204. else{
  205. AddText(dialog);
  206. logger_->Log("[MISSING 0xFC " + HexToString(temp2, 2, '0') + "]");
  207. }
  208. }
  209. else if (temp == 0xFD){
  210. if (JAPANESE_CHARS_FD[temp2] != 0x0000){
  211. dialog.push_back(JAPANESE_CHARS_FD[temp2] >> 8);
  212. dialog.push_back(JAPANESE_CHARS_FD[temp2] & 0xFF);
  213. }
  214. else{
  215. AddText(dialog);
  216. logger_->Log("[MISSING 0xFD " + HexToString(temp2, 2, '0') + "]");
  217. }
  218. }
  219. }
  220. else if (temp == 0xFE){
  221. ++ offset;
  222. unsigned char temp2 = GetU8(offset);
  223. // TODO: Maybe value must be RGB?
  224. // TODO: anfrst map fails to terminate a colour breaking all of
  225. // the XML, so colours turned off for now.
  226. if (temp2 == 0xD2){ // Gray.
  227. AddText(dialog);
  228. logger_->Log("<colour value=\"0.6 0.6 0.6\">");
  229. colour_opened ++;
  230. }
  231. else if (temp2 == 0xD3){ // Blue.
  232. AddText(dialog);
  233. logger_->Log("<colour value=\"0.0 0.0 1.0\">");
  234. colour_opened ++;
  235. }
  236. else if (temp2 == 0xD4){ // Red.
  237. AddText(dialog);
  238. logger_->Log("<colour value=\"1.0 0.0 0.0\">");
  239. colour_opened ++;
  240. }
  241. else if (temp2 == 0xD5){ // Purple
  242. AddText(dialog);
  243. logger_->Log("<colour value=\"1.0 0.2 0.8\" >");
  244. colour_opened ++;
  245. }
  246. else if (temp2 == 0xD6){ // Green.
  247. AddText(dialog);
  248. logger_->Log("<colour value=\"0.0 1.0 0.0\" >");
  249. colour_opened ++;
  250. }
  251. else if (temp2 == 0xD7){ // Cyan.
  252. AddText(dialog);
  253. logger_->Log("<colour value=\"0.0 1.0 0.8\" >");
  254. colour_opened ++;
  255. }
  256. else if (temp2 == 0xD8){ // Yellow
  257. AddText(dialog);
  258. logger_->Log("<colour value=\"1.0 1.0 0.4\">");
  259. colour_opened ++;
  260. }
  261. else if (temp2 == 0xD9){ // White, or back to normal.
  262. AddText(dialog);
  263. logger_->Log("</colour>");
  264. colour_opened --;
  265. }
  266. else if (temp2 == 0xDA){ // Flash (blink), global for a window.
  267. // TODO: Implement
  268. }
  269. else if (temp2 == 0xDB){ // Rainbow, global for a window.
  270. // TODO: Implement
  271. }
  272. else if (temp2 == 0xDC){
  273. AddText(dialog);
  274. logger_->Log("<pause_ok />");
  275. }
  276. else if (temp2 == 0xDD){
  277. u16 wait = GetU16LE(offset + 1);
  278. ++ offset;
  279. ++ offset;
  280. AddText(dialog);
  281. logger_->Log("<pause time=\"" + IntToString(wait) + "\" />");
  282. }
  283. else{
  284. if (JAPANESE_CHARS_FE[temp2] != 0x0000 && english == false){
  285. dialog.push_back(JAPANESE_CHARS_FE[temp2] >> 8);
  286. dialog.push_back(JAPANESE_CHARS_FE[temp2] & 0xFF);
  287. }
  288. else{
  289. AddText(dialog);
  290. logger_->Log("[MISSING 0xFE " + HexToString(temp2, 2, '0') + "]");
  291. }
  292. }
  293. }
  294. else{
  295. if (japanese_chars[temp] != 0x0000 && english == false){
  296. // dialog.push_back(japanese_chars[temp]);
  297. }
  298. else if (ENGLISH_CHARS[temp] != 0x0000 && english == true){
  299. dialog.push_back(ENGLISH_CHARS[temp]);
  300. }
  301. else{
  302. AddText(dialog);
  303. if (temp == 0xa9) logger_->Log("..."); // TODO Verify.
  304. else logger_->Log("[MISSING CHAR " + HexToString(temp, 2, '0') + "]");
  305. }
  306. }
  307. }
  308. // Close unclosed color tags;
  309. for (; colour_opened > 0; colour_opened --) logger_->Log("</colour>");
  310. AddText(dialog);
  311. logger_->Log("</dialog>\n\n");
  312. tag_open_ = false;
  313. }
  314. }
  315. void FieldTextWriter::End(){
  316. if (logger_) logger_->Log("</texts>\n");
  317. logger_.reset();
  318. }
  319. void FieldTextWriter::AddText(std::vector< unsigned char >& text){
  320. if (text.empty()) return;
  321. std::string str(reinterpret_cast<char*>(text.data()), text.size());
  322. str = Ogre::StringUtil::replaceAll(str, "&", "&amp;");
  323. str = Ogre::StringUtil::replaceAll(str, "\"", "&quot;");
  324. str = Ogre::StringUtil::replaceAll(str, "'", "&apos;");
  325. str = Ogre::StringUtil::replaceAll(str, "<", "&lt;");
  326. str = Ogre::StringUtil::replaceAll(str, ">", "&gt;");
  327. logger_->Log(str);
  328. text.clear();
  329. }