FieldTextWriter.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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. const u16 dialogCount = GetU16LE(offset_to_sector + offset_to_dialogs);
  60. for (u16 i = 0; i <dialogCount; ++ i){
  61. // gGt offset of string data.
  62. u32 offset = offset_to_sector + offset_to_dialogs + GetU16LE(
  63. offset_to_sector + offset_to_dialogs
  64. + 0x02 + // +2 to skip dialog count
  65. i * 0x02 // *2 because each char is 2 bytes
  66. );
  67. AddText(dialog);
  68. logger_->Log("<dialog name=\"" + field_name + "_" + std::to_string(i) + "\">");
  69. tag_open_ = true;
  70. // Some maps have unclosed color tags.
  71. // Use this counter to close them at the end.
  72. int colour_opened = 0;
  73. for (unsigned char temp = 0x00;; ++ offset){
  74. temp = GetU8(offset);
  75. if (temp == 0xFF) break;
  76. if (temp == 0xE0 && english == true){
  77. dialog.push_back(ENGLISH_CHARS[0x00]);
  78. dialog.push_back(ENGLISH_CHARS[0x00]);
  79. dialog.push_back(ENGLISH_CHARS[0x00]);
  80. dialog.push_back(ENGLISH_CHARS[0x00]);
  81. dialog.push_back(ENGLISH_CHARS[0x00]);
  82. dialog.push_back(ENGLISH_CHARS[0x00]);
  83. dialog.push_back(ENGLISH_CHARS[0x00]);
  84. dialog.push_back(ENGLISH_CHARS[0x00]);
  85. dialog.push_back(ENGLISH_CHARS[0x00]);
  86. dialog.push_back(ENGLISH_CHARS[0x00]);
  87. }
  88. else if (temp == 0xE1 && english == true){
  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. }
  94. else if (temp == 0xE2 && english == true){
  95. dialog.push_back(ENGLISH_CHARS[0x0C]);
  96. dialog.push_back(ENGLISH_CHARS[0x00]);
  97. }
  98. else if (temp == 0xE8){
  99. AddText(dialog);
  100. logger_->Log("<next_page />");
  101. }
  102. else if (temp == 0xEA){
  103. AddText(dialog);
  104. logger_->Log("<character id=\"0\" />");
  105. }
  106. else if (temp == 0xEB){
  107. AddText(dialog);
  108. logger_->Log("<character id=\"1\" />");
  109. }
  110. else if (temp == 0xEC){
  111. AddText(dialog);
  112. logger_->Log("<character id=\"2\" />");
  113. }
  114. else if (temp == 0xED){
  115. AddText(dialog);
  116. logger_->Log("<character id=\"3\" />");
  117. }
  118. else if (temp == 0xEE){
  119. AddText(dialog);
  120. logger_->Log("<character id=\"4\" />");
  121. }
  122. else if (temp == 0xEF){
  123. AddText(dialog);
  124. logger_->Log("<character id=\"5\" />");
  125. }
  126. else if (temp == 0xF0){
  127. AddText(dialog);
  128. logger_->Log("<character id=\"6\" />");
  129. }
  130. else if (temp == 0xF1){
  131. AddText(dialog);
  132. logger_->Log("<character id=\"7\" />");
  133. }
  134. else if (temp == 0xF2){
  135. AddText(dialog);
  136. logger_->Log("<character id=\"8\" />");
  137. }
  138. else if (temp == 0xF3){
  139. AddText(dialog);
  140. logger_->Log("<party pos=\"0\" />");
  141. }
  142. else if (temp == 0xF4){
  143. AddText(dialog);
  144. logger_->Log("<party pos=\"1\" />");
  145. }
  146. else if (temp == 0xF5){
  147. AddText(dialog);
  148. logger_->Log("<party pos=\"2\" />");
  149. }
  150. else if (temp == 0xF6){
  151. AddText(dialog);
  152. logger_->Log("<image sprite=\"ButtonCircle\" />");
  153. }
  154. else if (temp == 0xF7){
  155. AddText(dialog);
  156. logger_->Log("<image sprite=\"ButtonTriangle\" />");
  157. }
  158. else if (temp == 0xF8){
  159. AddText(dialog);
  160. logger_->Log("<image sprite=\"ButtonSquare\" />");
  161. }
  162. else if (temp == 0xF9){
  163. AddText(dialog);
  164. logger_->Log("<image sprite=\"ButtonCross\" />");
  165. }
  166. else if ((temp == 0xFA || temp == 0xFB || temp == 0xFC || temp == 0xFD) && english == false){
  167. ++ offset;
  168. unsigned char temp2 = GetU8(offset);
  169. if (temp == 0xFA){
  170. if (JAPANESE_CHARS_FA[temp2] != 0x0000){
  171. dialog.push_back(JAPANESE_CHARS_FA[temp2] >> 8);
  172. dialog.push_back(JAPANESE_CHARS_FA[temp2] & 0xFF);
  173. }
  174. else{
  175. AddText(dialog);
  176. logger_->Log("[MISSING 0xFA " + HexToString(temp2, 2, '0') + "]");
  177. }
  178. }
  179. else if (temp == 0xFB){
  180. if (JAPANESE_CHARS_FB[temp2] != 0x0000){
  181. dialog.push_back(JAPANESE_CHARS_FB[temp2] >> 8);
  182. dialog.push_back(JAPANESE_CHARS_FB[temp2] & 0xFF);
  183. }
  184. else{
  185. AddText(dialog);
  186. logger_->Log("[MISSING 0xFB " + HexToString(temp2, 2, '0') + "]");
  187. }
  188. }
  189. else if (temp == 0xFC){
  190. if (JAPANESE_CHARS_FC[temp2] != 0x0000){
  191. dialog.push_back(JAPANESE_CHARS_FC[temp2] >> 8);
  192. dialog.push_back(JAPANESE_CHARS_FC[temp2] & 0xFF);
  193. }
  194. else{
  195. AddText(dialog);
  196. logger_->Log("[MISSING 0xFC " + HexToString(temp2, 2, '0') + "]");
  197. }
  198. }
  199. else if (temp == 0xFD){
  200. if (JAPANESE_CHARS_FD[temp2] != 0x0000){
  201. dialog.push_back(JAPANESE_CHARS_FD[temp2] >> 8);
  202. dialog.push_back(JAPANESE_CHARS_FD[temp2] & 0xFF);
  203. }
  204. else{
  205. AddText(dialog);
  206. logger_->Log("[MISSING 0xFD " + HexToString(temp2, 2, '0') + "]");
  207. }
  208. }
  209. }
  210. else if (temp == 0xFE){
  211. ++ offset;
  212. unsigned char temp2 = GetU8(offset);
  213. // TODO: Maybe value must be RGB?
  214. // TODO: anfrst map fails to terminate a colour breaking all of
  215. // the XML, so colours turned off for now.
  216. if (temp2 == 0xD2){ // Gray.
  217. AddText(dialog);
  218. logger_->Log("<colour value=\"0.6 0.6 0.6\">");
  219. colour_opened ++;
  220. }
  221. else if (temp2 == 0xD3){ // Blue.
  222. AddText(dialog);
  223. logger_->Log("<colour value=\"0.0 0.0 1.0\">");
  224. colour_opened ++;
  225. }
  226. else if (temp2 == 0xD4){ // Red.
  227. AddText(dialog);
  228. logger_->Log("<colour value=\"1.0 0.0 0.0\">");
  229. colour_opened ++;
  230. }
  231. else if (temp2 == 0xD5){ // Purple
  232. AddText(dialog);
  233. logger_->Log("<colour value=\"1.0 0.2 0.8\" >");
  234. colour_opened ++;
  235. }
  236. else if (temp2 == 0xD6){ // Green.
  237. AddText(dialog);
  238. logger_->Log("<colour value=\"0.0 1.0 0.0\" >");
  239. colour_opened ++;
  240. }
  241. else if (temp2 == 0xD7){ // Cyan.
  242. AddText(dialog);
  243. logger_->Log("<colour value=\"0.0 1.0 0.8\" >");
  244. colour_opened ++;
  245. }
  246. else if (temp2 == 0xD8){ // Yellow
  247. AddText(dialog);
  248. logger_->Log("<colour value=\"1.0 1.0 0.4\">");
  249. colour_opened ++;
  250. }
  251. else if (temp2 == 0xD9){ // White, or back to normal.
  252. AddText(dialog);
  253. logger_->Log("</colour>");
  254. colour_opened --;
  255. }
  256. else if (temp2 == 0xDA){ // Flash (blink), global for a window.
  257. // TODO: Implement
  258. }
  259. else if (temp2 == 0xDB){ // Rainbow, global for a window.
  260. // TODO: Implement
  261. }
  262. else if (temp2 == 0xDC){
  263. AddText(dialog);
  264. logger_->Log("<pause_ok >");
  265. }
  266. else if (temp2 == 0xDD){
  267. u16 wait = GetU16LE(offset + 1);
  268. ++ offset;
  269. ++ offset;
  270. AddText(dialog);
  271. logger_->Log("<pause time=\"" + IntToString(wait) + "\" />");
  272. }
  273. else{
  274. if (JAPANESE_CHARS_FE[temp2] != 0x0000 && english == false){
  275. dialog.push_back(JAPANESE_CHARS_FE[temp2] >> 8);
  276. dialog.push_back(JAPANESE_CHARS_FE[temp2] & 0xFF);
  277. }
  278. else{
  279. AddText(dialog);
  280. logger_->Log("[MISSING 0xFE " + HexToString(temp2, 2, '0') + "]");
  281. }
  282. }
  283. }
  284. else{
  285. if (japanese_chars[temp] != 0x0000 && english == false){
  286. // dialog.push_back(japanese_chars[temp]);
  287. }
  288. else if (ENGLISH_CHARS[temp] != 0x0000 && english == true){
  289. dialog.push_back(ENGLISH_CHARS[temp]);
  290. }
  291. else{
  292. AddText(dialog);
  293. if (temp == 0xa9) logger_->Log("..."); // TODO Verify.
  294. else logger_->Log("[MISSING CHAR " + HexToString(temp, 2, '0') + "]");
  295. }
  296. }
  297. }
  298. // Close unclosed color tags;
  299. for (; colour_opened > 0; colour_opened --) logger_->Log("</colour>");
  300. AddText(dialog);
  301. logger_->Log("</dialog>\n\n");
  302. tag_open_ = false;
  303. }
  304. }
  305. void FieldTextWriter::End(){
  306. if (logger_) logger_->Log("</texts>\n");
  307. logger_.reset();
  308. }
  309. void FieldTextWriter::AddText(std::vector< unsigned char >& text){
  310. if (text.empty()) return;
  311. std::string str(reinterpret_cast<char*>(text.data()), text.size());
  312. str = Ogre::StringUtil::replaceAll(str, "&", "&amp;");
  313. str = Ogre::StringUtil::replaceAll(str, "\"", "&quot;");
  314. str = Ogre::StringUtil::replaceAll(str, "'", "&apos;");
  315. str = Ogre::StringUtil::replaceAll(str, "<", "&lt;");
  316. str = Ogre::StringUtil::replaceAll(str, ">", "&gt;");
  317. logger_->Log(str);
  318. text.clear();
  319. }