text.c 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377
  1. /* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
  2. //
  3. // Copyright (c) 2009-2011, Wei Mingzhi <whistler_wmz@users.sf.net>.
  4. // Copyright (c) 2011-2017, SDLPAL development team.
  5. // All rights reserved.
  6. //
  7. // This file is part of SDLPAL.
  8. //
  9. // SDLPAL is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU General Public License as published by
  11. // the Free Software Foundation, either version 3 of the License, or
  12. // (at your option) any later version.
  13. //
  14. // This program is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. // GNU General Public License for more details.
  18. //
  19. // You should have received a copy of the GNU General Public License
  20. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. //
  22. // Portions based on PALx Project by palxex.
  23. // Copyright (c) 2006-2008, Pal Lockheart <palxex@gmail.com>.
  24. //
  25. #include "main.h"
  26. #include <errno.h>
  27. #include <wctype.h>
  28. #define FONT_COLOR_DEFAULT 0x4F
  29. #define FONT_COLOR_YELLOW 0x2D
  30. #define FONT_COLOR_RED 0x1A
  31. #define FONT_COLOR_CYAN 0x8D
  32. #define FONT_COLOR_CYAN_ALT 0x8C
  33. BOOL g_fUpdatedInBattle = FALSE;
  34. #define MESSAGE_MAX_BUFFER_SIZE 512
  35. #define INCLUDE_CODEPAGE_H
  36. #include "codepage.h"
  37. #ifndef PAL_CLASSIC
  38. # define ATB_WORD_COUNT 6
  39. static LPWSTR gc_rgszAdditionalWords[CP_MAX][ATB_WORD_COUNT] = {
  40. { L"\x6230\x9B25\x901F\x5EA6", L"\x4E00", L"\x4E8C", L"\x4E09", L"\x56DB", L"\x4E94" },
  41. { L"\x6218\x6597\x901F\x5EA6", L"\x4E00", L"\x4E8C", L"\x4E09", L"\x56DB", L"\x4E94" },
  42. //{ L"\x6226\x95D8\x901F\x5EA6", L"\x4E00", L"\x4E8C", L"\x4E09", L"\x56DB", L"\x4E94" },
  43. };
  44. static LPWSTR gc_rgszDefaultAdditionalWords[ATB_WORD_COUNT] = { NULL, L"\xFF11", L"\xFF12", L"\xFF13", L"\xFF14", L"\xFF15" };
  45. #endif
  46. #define SDLPAL_EXTRA_WORD_COUNT 1
  47. static LPWSTR gc_rgszSDLPalWords[CP_MAX][SDLPAL_EXTRA_WORD_COUNT] = {
  48. { L"\x8FD4\x56DE\x8A2D\x5B9A" },
  49. { L"\x8FD4\x56DE\x8BBE\x7F6E" },
  50. };
  51. LPWSTR g_rcCredits[12];
  52. TEXTLIB g_TextLib;
  53. PAL_FORCE_INLINE int
  54. PAL_ParseLine(
  55. char *line,
  56. char **value,
  57. int *length,
  58. int deltrail
  59. )
  60. {
  61. //
  62. // Remove the leading spaces
  63. //
  64. while (*line && iswspace(*line)) line++;
  65. //
  66. // Skip comments starting with '#'
  67. //
  68. if (*line && *line != '#')
  69. {
  70. //
  71. // Split the index and value
  72. //
  73. LPSTR val = strchr(line, '=');
  74. if (val)
  75. {
  76. //
  77. // Remove the trailing spaces
  78. //
  79. LPSTR end = line + strlen(line);
  80. int index;
  81. if (end > line && end[-1] == '\n') *(--end) = 0;
  82. if (deltrail) while (end > line && iswspace(end[-1])) *(--end) = 0;
  83. //
  84. // Parse the index and pass out value
  85. //
  86. if (sscanf(line, "%d", &index) == 1)
  87. {
  88. *value = val + 1;
  89. *length = end - *value;
  90. return index;
  91. }
  92. }
  93. }
  94. return 0;
  95. }
  96. PAL_FORCE_INLINE char *
  97. PAL_ReadOneLine(
  98. char *temp,
  99. int limit,
  100. FILE *fp
  101. )
  102. {
  103. if (fgets(temp, limit, fp))
  104. {
  105. int n = strlen(temp);
  106. if (n == limit - 1 && temp[n - 1] != '\n' && !feof(fp))
  107. {
  108. // Line too long, try to read it as a whole
  109. int nn = 2;
  110. char *tmp = strdup(temp);
  111. while (!feof(fp))
  112. {
  113. if (!(tmp = (char *)realloc(tmp, nn * limit)))
  114. {
  115. TerminateOnError("PAL_ReadOneLine(): failed to allocate memory for long line!");
  116. }
  117. if (fgets(tmp + n, limit + 1, fp))
  118. {
  119. n += strlen(tmp + n);
  120. if (n < limit - 1 || temp[n - 1] == '\n')
  121. break;
  122. else
  123. nn++;
  124. }
  125. }
  126. if (tmp[n - 1] == '\n') tmp[n - 1] = 0;
  127. return tmp;
  128. }
  129. else
  130. {
  131. while (n > 0 && (temp[n - 1] == '\n' || temp[n - 1] == '\r')) temp[--n] = 0;
  132. return temp;
  133. }
  134. }
  135. else
  136. return NULL;
  137. }
  138. static int
  139. PAL_ReadMessageFile(
  140. FILE *fp
  141. )
  142. {
  143. char temp[MESSAGE_MAX_BUFFER_SIZE];
  144. struct _msg_entry
  145. {
  146. struct _msg_entry *next;
  147. wchar_t *value;
  148. } *cur_val = NULL;
  149. struct _msg_list_entry
  150. {
  151. struct _msg_list_entry *next;
  152. struct _msg_entry *value;
  153. int index;
  154. int count;
  155. } *head = NULL, *item = NULL;
  156. struct _word_list_entry
  157. {
  158. struct _word_list_entry *next;
  159. wchar_t *value;
  160. int index;
  161. } whead = { NULL, NULL }, *witem = NULL;
  162. enum _message_state
  163. {
  164. ST_OUTSIDE,
  165. ST_SETTING,
  166. ST_DIALOG,
  167. ST_WORD,
  168. ST_DESC,
  169. ST_CREDIT,
  170. ST_LAYOUT
  171. } state = ST_OUTSIDE;
  172. int idx_cnt = 0, msg_cnt = 0, word_cnt = 0, sid, eid = -1;
  173. while (!feof(fp))
  174. {
  175. char *buffer;
  176. if ((buffer = PAL_ReadOneLine(temp, MESSAGE_MAX_BUFFER_SIZE, fp)) != NULL)
  177. {
  178. switch(state)
  179. {
  180. case ST_OUTSIDE:
  181. //
  182. // Skip comments starting with '#'
  183. //
  184. if (*buffer && *buffer != '#')
  185. {
  186. if (strncmp(buffer, "[BEGIN MESSAGE]", 15) == 0 &&
  187. sscanf(buffer + 15, "%d", &sid) == 1)
  188. {
  189. state = ST_DIALOG;
  190. //
  191. // First save values (converted wide string) into a linked list
  192. //
  193. if (head)
  194. {
  195. item->next = (struct _msg_list_entry *)UTIL_malloc(sizeof(struct _msg_list_entry));
  196. item = item->next;
  197. }
  198. else
  199. {
  200. head = (struct _msg_list_entry *)UTIL_malloc(sizeof(struct _msg_list_entry));
  201. item = head;
  202. }
  203. item->value = NULL; item->index = sid;
  204. item->count = 0; item->next = NULL; cur_val = NULL;
  205. if (idx_cnt < item->index) idx_cnt = item->index;
  206. }
  207. else if (strncmp(buffer, "[BEGIN SETTING]", 15) == 0 && !witem)
  208. {
  209. state = ST_SETTING;
  210. }
  211. else if (strncmp(buffer, "[BEGIN WORDS]", 13) == 0 && !witem)
  212. {
  213. state = ST_WORD;
  214. //
  215. // First save values (converted wide string) into a linked list
  216. //
  217. witem = &whead;
  218. }
  219. else if (strncmp(buffer, "[BEGIN DESCRIPTIONS]", 13) == 0)
  220. {
  221. state = ST_DESC;
  222. }
  223. else if (strncmp(buffer, "[BEGIN CREDITS]", 15) == 0 && !witem)
  224. {
  225. state = ST_CREDIT;
  226. }
  227. else if (strncmp(buffer, "[BEGIN LAYOUT]", 14) == 0 && !witem)
  228. {
  229. state = ST_LAYOUT;
  230. gConfig.fUseCustomScreenLayout = TRUE;
  231. }
  232. else
  233. {
  234. // Just ignore invalid lines
  235. UTIL_LogOutput(LOGLEVEL_WARNING, "PAL_ReadMessageFile(): encounter invalid line '%s'!\n", buffer);
  236. }
  237. }
  238. break;
  239. case ST_DIALOG:
  240. //
  241. // Check if to end one dialog
  242. //
  243. if (strncmp(buffer, "[END MESSAGE]", 13) == 0 &&
  244. sscanf(buffer + 13, "%d", &eid) == 1 && eid >= sid)
  245. {
  246. // End dialog
  247. state = ST_OUTSIDE;
  248. }
  249. else
  250. {
  251. if (cur_val)
  252. {
  253. cur_val->next = (struct _msg_entry *)UTIL_malloc(sizeof(struct _msg_entry));
  254. cur_val = cur_val->next;
  255. }
  256. else
  257. cur_val = (struct _msg_entry *)UTIL_malloc(sizeof(struct _msg_entry));
  258. if (strncmp(buffer, "[CLEAR MESSAGE]", 15) == 0)
  259. {
  260. cur_val->value = NULL;
  261. }
  262. else
  263. {
  264. int len = PAL_MultiByteToWideCharCP(CP_UTF_8, buffer, -1, NULL, 0);
  265. cur_val->value = (wchar_t *)UTIL_malloc(len * sizeof(wchar_t));
  266. PAL_MultiByteToWideCharCP(CP_UTF_8, buffer, -1, cur_val->value, len);
  267. msg_cnt++;
  268. }
  269. if (!item->value) item->value = cur_val;
  270. cur_val->next = NULL; item->count++;
  271. }
  272. break;
  273. case ST_WORD:
  274. //
  275. // Check if to end word list
  276. //
  277. if (strncmp(buffer, "[END WORDS]", 11) == 0)
  278. {
  279. // End word list
  280. state = ST_OUTSIDE;
  281. }
  282. else
  283. {
  284. char *v;
  285. int l, i = PAL_ParseLine(buffer, &v, &l, FALSE);
  286. if (i > 0)
  287. {
  288. int len = PAL_MultiByteToWideCharCP(CP_UTF_8, v, -1, NULL, 0);
  289. struct _word_list_entry *val = (struct _word_list_entry *)UTIL_malloc(sizeof(struct _word_list_entry));
  290. val->value = (wchar_t *)UTIL_malloc(len * sizeof(wchar_t));
  291. PAL_MultiByteToWideCharCP(CP_UTF_8, v, -1, val->value, len);
  292. val->index = i; val->next = NULL;
  293. witem->next = val; witem = witem->next;
  294. if (word_cnt < i) word_cnt = i;
  295. }
  296. }
  297. break;
  298. case ST_DESC:
  299. //
  300. // Check if to end setting list
  301. //
  302. if (strncmp(buffer, "[END DESCRIPTIONS]", 18) == 0)
  303. {
  304. // End setting list
  305. state = ST_OUTSIDE;
  306. }
  307. else
  308. {
  309. char *line = buffer;
  310. while (*buffer && iswspace(*buffer)) line++;
  311. //
  312. // Skip comments starting with '#'
  313. //
  314. if (*line && *line != '#')
  315. {
  316. //
  317. // Split the index and value
  318. //
  319. LPSTR p = strchr(line, '=');
  320. int wlen,strip_count=2;
  321. if (p)
  322. {
  323. int index;
  324. //
  325. // Remove the trailing spaces
  326. //
  327. LPSTR end = line + strlen(line);
  328. if (end > line && end[-1] == '\n') *(--end) = 0;
  329. if (FALSE) while (end > line && iswspace(end[-1])) *(--end) = 0;
  330. *p++ = '\0';
  331. while(strip_count--){
  332. if(p[strlen(p)-1]=='\r') p[strlen(p)-1]='\0';
  333. if(p[strlen(p)-1]=='\n') p[strlen(p)-1]='\0';
  334. }
  335. wlen = PAL_MultiByteToWideCharCP(CP_UTF_8, p, -1, NULL, 0);
  336. //
  337. // Parse the index and pass out value
  338. //
  339. sscanf(line, "%x", &index);
  340. LPOBJECTDESC lpObjectDesc = gpGlobals->lpObjectDesc;
  341. LPOBJECTDESC prevObjectDesc = lpObjectDesc;
  342. BOOL isFirst = gpGlobals->lpObjectDesc == NULL;
  343. while (lpObjectDesc != NULL)
  344. {
  345. if (lpObjectDesc->wObjectID == index)
  346. {
  347. break;
  348. }
  349. prevObjectDesc = lpObjectDesc;
  350. lpObjectDesc = lpObjectDesc->next;
  351. }
  352. if( !lpObjectDesc )
  353. {
  354. lpObjectDesc = UTIL_calloc(1, sizeof(OBJECTDESC));
  355. memset(lpObjectDesc,0,sizeof(OBJECTDESC));
  356. if( prevObjectDesc )
  357. prevObjectDesc->next = lpObjectDesc;
  358. }
  359. if( isFirst )
  360. gpGlobals->lpObjectDesc = lpObjectDesc;
  361. lpObjectDesc->wObjectID = index;
  362. lpObjectDesc->lpDesc = (LPWSTR)UTIL_calloc(1, wlen * sizeof(WCHAR));
  363. PAL_MultiByteToWideCharCP(CP_UTF_8, p, -1, lpObjectDesc->lpDesc, wlen);
  364. }
  365. }
  366. }
  367. break;
  368. case ST_SETTING:
  369. //
  370. // Check if to end setting list
  371. //
  372. if (strncmp(buffer, "[END SETTING]", 13) == 0)
  373. {
  374. // End setting list
  375. state = ST_OUTSIDE;
  376. }
  377. else
  378. {
  379. char *line = buffer;
  380. while (*buffer && iswspace(*buffer)) line++;
  381. //
  382. // Skip comments starting with '#'
  383. //
  384. if (*line && *line != '#')
  385. {
  386. //
  387. // Split the index and value
  388. //
  389. LPSTR val = strchr(line, '=');
  390. if (val)
  391. {
  392. char index[80];
  393. *val = '\0';
  394. //
  395. // Remove the trailing spaces
  396. //
  397. LPSTR end = line + strlen(line);
  398. if (end > line && end[-1] == '\n') *(--end) = 0;
  399. if (FALSE) while (end > line && iswspace(end[-1])) *(--end) = 0;
  400. //
  401. // Parse the index and pass out value
  402. //
  403. if (sscanf(line, "%s", index) == 1)
  404. {
  405. if (strncasecmp(index, "UseISOFont", 10) == 0)
  406. {
  407. g_TextLib.fUseISOFont = atoi(val + 1) == 1;
  408. }
  409. else if (strncasecmp(index, "FontFlavor", 10) == 0)
  410. {
  411. const char *szFontFlavors[] = {
  412. "Unifont",
  413. "SimpChin",
  414. "TradChin",
  415. "Japanese",
  416. NULL
  417. };
  418. int i = 1;
  419. while (szFontFlavors[i - 1] != NULL)
  420. {
  421. if (strcmp(val + 1, szFontFlavors[i - 1]) == 0)
  422. {
  423. g_TextLib.iFontFlavor = i;
  424. break;
  425. }
  426. i++;
  427. }
  428. }
  429. }
  430. }
  431. }
  432. }
  433. break;
  434. case ST_CREDIT:
  435. //
  436. // Check if to end credit list
  437. //
  438. if (strncmp(buffer, "[END CREDITS]", 13) == 0)
  439. {
  440. // End credit list
  441. state = ST_OUTSIDE;
  442. }
  443. else
  444. {
  445. char *v;
  446. int l, i = PAL_ParseLine(buffer, &v, &l, FALSE);
  447. if ((i == 1 || (i >= 6 && i <= 11)) && !g_rcCredits[i])
  448. {
  449. int limit = (i == 1) ? 24 * 8 : 40 * 8, w = 0, j = 0, len;
  450. if (i == 6 || i == 7)
  451. {
  452. if (PAL_PLATFORM && PAL_CREDIT && PAL_PORTYEAR)
  453. {
  454. const char *templates[] = { "${platform}", "${author}", "${year}" };
  455. const char *values[] = { PAL_PLATFORM, PAL_CREDIT, PAL_PORTYEAR };
  456. const int matchlen[] = { 11, 9, 7 };
  457. const int valuelen[] = { sizeof(PAL_PLATFORM) - 1, sizeof(PAL_CREDIT) - 1, sizeof(PAL_PORTYEAR) - 1 };
  458. char *tmp = (char *)alloca(valuelen[0] + valuelen[1] + valuelen[2] + l + 1);
  459. char *dst = tmp, *src = v;
  460. while (*src)
  461. {
  462. if (*src == '$')
  463. {
  464. int k;
  465. for (k = 0; k < 3 && strncmp(src, templates[k], matchlen[k]); k++);
  466. if (k < 3)
  467. {
  468. strcpy(dst, values[k]);
  469. dst += valuelen[k];
  470. src += matchlen[k];
  471. continue;
  472. }
  473. }
  474. *dst++ = *src++;
  475. }
  476. *dst = 0;
  477. len = PAL_MultiByteToWideCharCP(CP_UTF_8, tmp, -1, NULL, 0);
  478. g_rcCredits[i] = (wchar_t *)UTIL_malloc(len * sizeof(wchar_t));
  479. PAL_MultiByteToWideCharCP(CP_UTF_8, tmp, -1, g_rcCredits[i], len);
  480. }
  481. }
  482. else
  483. {
  484. len = PAL_MultiByteToWideCharCP(CP_UTF_8, v, -1, NULL, 0);
  485. g_rcCredits[i] = (wchar_t *)UTIL_malloc(len * sizeof(wchar_t));
  486. PAL_MultiByteToWideCharCP(CP_UTF_8, v, -1, g_rcCredits[i], len);
  487. }
  488. if (g_rcCredits[i])
  489. {
  490. // Limit the length of texts
  491. while (w < limit && j < len - 1) w += PAL_CharWidth(g_rcCredits[i][j++]);
  492. if (w >= limit) g_rcCredits[i][w > limit ? j - 1 : j] = 0;
  493. }
  494. }
  495. }
  496. break;
  497. case ST_LAYOUT:
  498. if (strncmp(buffer, "[END LAYOUT]", 12) == 0)
  499. {
  500. // End layout
  501. state = ST_OUTSIDE;
  502. }
  503. else
  504. {
  505. char *v;
  506. int x, y, f, n, l, i = PAL_ParseLine(buffer, &v, &l, FALSE);
  507. if (i >= 1 && i <= (sizeof(SCREENLAYOUT) / sizeof(PAL_POS)))
  508. {
  509. if ((n = sscanf(v, "%d,%d,%d", &x, &y, &f)) >= 2 && x < 320 && y < 200)
  510. {
  511. gConfig.ScreenLayoutArray[i - 1] = PAL_XY(x, y);
  512. if (n == 3) gConfig.ScreenLayoutFlag[i - 1] = f;
  513. }
  514. }
  515. }
  516. break;
  517. default:
  518. TerminateOnError("PAL_ReadMessageFile(): Reached an unknown state. Something really wrong may have happened!");
  519. break;
  520. }
  521. if (buffer != temp) free(buffer);
  522. }
  523. }
  524. if (msg_cnt > 0)
  525. {
  526. //
  527. // Move values from linked list to array
  528. //
  529. int idx_msg = 1;
  530. g_TextLib.nIndices = (idx_cnt += 1);
  531. g_TextLib.nMsgs = (msg_cnt += 1);
  532. g_TextLib.lpIndexBuf = (int **)UTIL_calloc(idx_cnt, sizeof(int *));
  533. g_TextLib.lpMsgBuf = (LPWSTR *)UTIL_calloc(msg_cnt, sizeof(LPWSTR));
  534. for (item = head; item; )
  535. {
  536. struct _msg_list_entry *temp = item->next;
  537. struct _msg_entry *msg = item->value;
  538. int index = 0;
  539. g_TextLib.lpIndexBuf[item->index] = (int *)UTIL_calloc(item->count + 1, sizeof(int));
  540. while (msg)
  541. {
  542. struct _msg_entry *tmp = msg->next;
  543. if (msg->value)
  544. {
  545. g_TextLib.lpIndexBuf[item->index][index++] = idx_msg;
  546. g_TextLib.lpMsgBuf[idx_msg++] = msg->value;
  547. }
  548. else
  549. g_TextLib.lpIndexBuf[item->index][index++] = 0;
  550. free(msg); msg = tmp;
  551. }
  552. g_TextLib.lpIndexBuf[item->index][item->count] = -1;
  553. free(item); item = temp;
  554. }
  555. }
  556. if (word_cnt > 0)
  557. {
  558. //
  559. // Move values from linked list to array
  560. //
  561. #ifndef PAL_CLASSIC
  562. int i;
  563. #endif
  564. if (word_cnt < MINIMAL_WORD_COUNT - 1) word_cnt = MINIMAL_WORD_COUNT - 1;
  565. g_TextLib.nWords = (word_cnt += 1);
  566. g_TextLib.lpWordBuf = (LPWSTR *)UTIL_calloc(word_cnt, sizeof(LPWSTR));
  567. for (witem = whead.next; witem; )
  568. {
  569. struct _word_list_entry *temp = witem->next;
  570. g_TextLib.lpWordBuf[witem->index] = witem->value;
  571. free(witem); witem = temp;
  572. }
  573. #ifndef PAL_CLASSIC
  574. for (i = 1; i < ATB_WORD_COUNT; i++)
  575. if (!g_TextLib.lpWordBuf[i + SYSMENU_LABEL_BATTLEMODE])
  576. g_TextLib.lpWordBuf[i + SYSMENU_LABEL_BATTLEMODE] = gc_rgszDefaultAdditionalWords[i];
  577. #endif
  578. }
  579. fclose(fp);
  580. return (msg_cnt > 0 && word_cnt > 0) ? 1 : 0;
  581. }
  582. INT
  583. PAL_InitText(
  584. VOID
  585. )
  586. /*++
  587. Purpose:
  588. Initialize the in-game texts.
  589. Parameters:
  590. None.
  591. Return value:
  592. 0 = success.
  593. -1 = memory allocation error.
  594. --*/
  595. {
  596. g_TextLib.fUseISOFont = TRUE;
  597. g_TextLib.iFontFlavor = kFontFlavorUnifont;
  598. if (gConfig.pszMsgFile)
  599. {
  600. //
  601. // Open the message, index and word data files.
  602. //
  603. FILE *fp = UTIL_OpenRequiredFileForMode(gConfig.pszMsgFile, "r");
  604. //
  605. // Read the contents of the message, index and word data files.
  606. //
  607. if (!PAL_ReadMessageFile(fp))
  608. {
  609. return -1;
  610. }
  611. else
  612. {
  613. DWORD dwWordLength = 0;
  614. int i;
  615. for (i = 1; i < g_TextLib.nWords; i++)
  616. {
  617. if (g_TextLib.lpWordBuf[i])
  618. {
  619. LPWSTR ptr = g_TextLib.lpWordBuf[i];
  620. DWORD n = 0;
  621. while (*ptr) n += PAL_CharWidth(*ptr++) >> 3;
  622. if (dwWordLength < n) dwWordLength = n;
  623. }
  624. }
  625. gConfig.dwWordLength = dwWordLength;
  626. for (i = 0; i < 12; i++)
  627. {
  628. if (!g_rcCredits[i])
  629. g_rcCredits[i] = L"";
  630. }
  631. }
  632. }
  633. else
  634. {
  635. FILE *fpMsg, *fpWord;
  636. DWORD *offsets;
  637. LPWSTR tmp;
  638. LPBYTE temp;
  639. int wpos, wlen, i;
  640. //
  641. // Open the message and word data files.
  642. //
  643. fpMsg = UTIL_OpenRequiredFile("m.msg");
  644. fpWord = UTIL_OpenRequiredFile("word.dat");
  645. //
  646. // See how many words we have
  647. //
  648. fseek(fpWord, 0, SEEK_END);
  649. i = ftell(fpWord);
  650. //
  651. // Each word has 10 bytes
  652. //
  653. g_TextLib.nWords = (i + (gConfig.dwWordLength - 1)) / gConfig.dwWordLength;
  654. if (g_TextLib.nWords < MINIMAL_WORD_COUNT) g_TextLib.nWords = MINIMAL_WORD_COUNT;
  655. //
  656. // Read the words
  657. //
  658. temp = (LPBYTE)malloc(gConfig.dwWordLength * g_TextLib.nWords);
  659. if (temp == NULL)
  660. {
  661. fclose(fpWord);
  662. fclose(fpMsg);
  663. return -1;
  664. }
  665. fseek(fpWord, 0, SEEK_SET);
  666. if (fread(temp, 1, i, fpWord) < (size_t)i)
  667. {
  668. fclose(fpWord);
  669. fclose(fpMsg);
  670. return -1;
  671. }
  672. memset(temp + i, 0, gConfig.dwWordLength * g_TextLib.nWords - i);
  673. //
  674. // Close the words file
  675. //
  676. fclose(fpWord);
  677. // Split the words and do code page conversion
  678. for (i = 0, wlen = 0; i < g_TextLib.nWords; i++)
  679. {
  680. int base = i * gConfig.dwWordLength;
  681. int pos = base + gConfig.dwWordLength - 1;
  682. while (pos >= base && temp[pos] == ' ') temp[pos--] = 0;
  683. wlen += PAL_MultiByteToWideChar((LPCSTR)temp + base, gConfig.dwWordLength, NULL, 0) + 1;
  684. }
  685. g_TextLib.lpWordBuf = (LPWSTR*)malloc(g_TextLib.nWords * sizeof(LPWSTR));
  686. if (g_TextLib.lpWordBuf == NULL)
  687. {
  688. free(temp);
  689. fclose(fpMsg);
  690. return -1;
  691. }
  692. tmp = (LPWSTR)malloc(wlen * sizeof(WCHAR));
  693. if (tmp == NULL)
  694. {
  695. free(g_TextLib.lpWordBuf);
  696. free(temp);
  697. fclose(fpMsg);
  698. return -1;
  699. }
  700. for (i = 0, wpos = 0; i < g_TextLib.nWords; i++)
  701. {
  702. int l;
  703. g_TextLib.lpWordBuf[i] = tmp + wpos;
  704. l = PAL_MultiByteToWideChar((LPCSTR)temp + i * gConfig.dwWordLength, gConfig.dwWordLength, g_TextLib.lpWordBuf[i], wlen - wpos);
  705. if (l > 0 && g_TextLib.lpWordBuf[i][l - 1] == '1')
  706. g_TextLib.lpWordBuf[i][l - 1] = 0;
  707. g_TextLib.lpWordBuf[i][l] = 0;
  708. wpos += l + 1;
  709. }
  710. free(temp);
  711. //
  712. // Read the message offsets. The message offsets are in SSS.MKF #3
  713. //
  714. i = PAL_MKFGetChunkSize(3, gpGlobals->f.fpSSS) / sizeof(DWORD);
  715. g_TextLib.nMsgs = i - 1;
  716. offsets = (LPDWORD)malloc(i * sizeof(DWORD));
  717. if (offsets == NULL)
  718. {
  719. free(g_TextLib.lpWordBuf[0]);
  720. free(g_TextLib.lpWordBuf);
  721. fclose(fpMsg);
  722. return -1;
  723. }
  724. PAL_MKFReadChunk((LPBYTE)(offsets), i * sizeof(DWORD), 3, gpGlobals->f.fpSSS);
  725. //
  726. // Read the messages.
  727. //
  728. fseek(fpMsg, 0, SEEK_END);
  729. i = ftell(fpMsg);
  730. temp = (LPBYTE)malloc(i);
  731. if (temp == NULL)
  732. {
  733. free(offsets);
  734. free(g_TextLib.lpWordBuf[0]);
  735. free(g_TextLib.lpWordBuf);
  736. fclose(fpMsg);
  737. return -1;
  738. }
  739. fseek(fpMsg, 0, SEEK_SET);
  740. if (fread(temp, 1, i, fpMsg) < (size_t)i)
  741. {
  742. free(offsets);
  743. free(g_TextLib.lpWordBuf[0]);
  744. free(g_TextLib.lpWordBuf);
  745. fclose(fpMsg);
  746. return -1;
  747. }
  748. fclose(fpMsg);
  749. // Split messages and do code page conversion here
  750. for (i = 0, wlen = 0; i < g_TextLib.nMsgs; i++)
  751. {
  752. wlen += PAL_MultiByteToWideChar((LPCSTR)temp + SDL_SwapLE32(offsets[i]), SDL_SwapLE32(offsets[i + 1]) - SDL_SwapLE32(offsets[i]), NULL, 0) + 1;
  753. }
  754. g_TextLib.lpMsgBuf = (LPWSTR*)malloc(g_TextLib.nMsgs * sizeof(LPWSTR));
  755. if (g_TextLib.lpMsgBuf == NULL)
  756. {
  757. free(g_TextLib.lpWordBuf[0]);
  758. free(g_TextLib.lpWordBuf);
  759. free(offsets);
  760. return -1;
  761. }
  762. tmp = (LPWSTR)malloc(wlen * sizeof(WCHAR));
  763. if (tmp == NULL)
  764. {
  765. free(g_TextLib.lpMsgBuf);
  766. free(g_TextLib.lpWordBuf[0]);
  767. free(g_TextLib.lpWordBuf);
  768. free(offsets);
  769. return -1;
  770. }
  771. for (i = 0, wpos = 0; i < g_TextLib.nMsgs; i++)
  772. {
  773. int l;
  774. g_TextLib.lpMsgBuf[i] = tmp + wpos;
  775. l = PAL_MultiByteToWideChar((LPCSTR)temp + SDL_SwapLE32(offsets[i]), SDL_SwapLE32(offsets[i + 1]) - SDL_SwapLE32(offsets[i]), g_TextLib.lpMsgBuf[i], wlen - wpos);
  776. g_TextLib.lpMsgBuf[i][l] = 0;
  777. wpos += l + 1;
  778. }
  779. free(temp);
  780. free(offsets);
  781. g_TextLib.lpIndexBuf = NULL;
  782. memcpy(g_TextLib.lpWordBuf + SYSMENU_LABEL_LAUNCHSETTING, gc_rgszSDLPalWords[PAL_GetCodePage()], SDLPAL_EXTRA_WORD_COUNT * sizeof(LPCWSTR));
  783. #ifndef PAL_CLASSIC
  784. memcpy(g_TextLib.lpWordBuf + SYSMENU_LABEL_BATTLEMODE, gc_rgszAdditionalWords[PAL_GetCodePage()], ATB_WORD_COUNT * sizeof(LPCWSTR));
  785. #endif
  786. g_TextLib.iFontFlavor = kFontFlavorAuto;
  787. }
  788. g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
  789. g_TextLib.bIcon = 0;
  790. g_TextLib.posIcon = 0;
  791. g_TextLib.nCurrentDialogLine = 0;
  792. g_TextLib.iDelayTime = 3;
  793. g_TextLib.posDialogTitle = PAL_XY(12, 8);
  794. g_TextLib.posDialogText = PAL_XY(44, 26);
  795. g_TextLib.bDialogPosition = kDialogUpper;
  796. g_TextLib.fUserSkip = FALSE;
  797. PAL_MKFReadChunk(g_TextLib.bufDialogIcons, 282, 12, gpGlobals->f.fpDATA);
  798. return 0;
  799. }
  800. VOID
  801. PAL_FreeText(
  802. VOID
  803. )
  804. /*++
  805. Purpose:
  806. Free the memory used by the texts.
  807. Parameters:
  808. None.
  809. Return value:
  810. None.
  811. --*/
  812. {
  813. int i;
  814. if (g_TextLib.lpMsgBuf != NULL)
  815. {
  816. if (gConfig.pszMsgFile)
  817. for(i = 0; i < g_TextLib.nMsgs; i++) free(g_TextLib.lpMsgBuf[i]);
  818. else
  819. free(g_TextLib.lpMsgBuf[0]);
  820. free(g_TextLib.lpMsgBuf);
  821. g_TextLib.lpMsgBuf = NULL;
  822. }
  823. if (g_TextLib.lpWordBuf != NULL)
  824. {
  825. if (gConfig.pszMsgFile)
  826. for(i = 0; i < g_TextLib.nWords; i++) free(g_TextLib.lpWordBuf[i]);
  827. else
  828. free(g_TextLib.lpWordBuf[0]);
  829. free(g_TextLib.lpWordBuf);
  830. g_TextLib.lpWordBuf = NULL;
  831. }
  832. if (g_TextLib.lpIndexBuf != NULL)
  833. {
  834. if (gConfig.pszMsgFile)
  835. for(i = 0; i < g_TextLib.nIndices; i++) free(g_TextLib.lpIndexBuf[i]);
  836. else
  837. free(g_TextLib.lpIndexBuf[0]);
  838. free(g_TextLib.lpIndexBuf);
  839. g_TextLib.lpIndexBuf = NULL;
  840. }
  841. }
  842. LPCWSTR
  843. PAL_GetWord(
  844. int iNumWord
  845. )
  846. /*++
  847. Purpose:
  848. Get the specified word.
  849. Parameters:
  850. [IN] wNumWord - the number of the requested word.
  851. Return value:
  852. Pointer to the requested word. NULL if not found.
  853. --*/
  854. {
  855. return (iNumWord >= g_TextLib.nWords || !g_TextLib.lpWordBuf[iNumWord]) ? L"" : g_TextLib.lpWordBuf[iNumWord];
  856. }
  857. LPCWSTR
  858. PAL_GetMsg(
  859. int iNumMsg
  860. )
  861. /*++
  862. Purpose:
  863. Get the specified message.
  864. Parameters:
  865. [IN] wNumMsg - the number of the requested message.
  866. Return value:
  867. Pointer to the requested message. NULL if not found.
  868. --*/
  869. {
  870. return (iNumMsg >= g_TextLib.nMsgs || !g_TextLib.lpMsgBuf[iNumMsg]) ? L"" : g_TextLib.lpMsgBuf[iNumMsg];
  871. }
  872. int
  873. PAL_GetMsgNum(
  874. int iIndex,
  875. int iOrder
  876. )
  877. /*++
  878. Purpose:
  879. Get the number of specified message from index & order.
  880. Parameters:
  881. [IN] iMsgIndex - index.
  882. [IN] iOrder - order inside the index.
  883. Return value:
  884. The number of message. Zero means pausing for key, and -1 means end.
  885. --*/
  886. {
  887. return (iIndex >= g_TextLib.nMsgs || !g_TextLib.lpIndexBuf[iIndex]) ? -1 : g_TextLib.lpIndexBuf[iIndex][iOrder];
  888. }
  889. VOID
  890. PAL_DrawText(
  891. LPCWSTR lpszText,
  892. PAL_POS pos,
  893. BYTE bColor,
  894. BOOL fShadow,
  895. BOOL fUpdate,
  896. BOOL fUse8x8Font
  897. )
  898. /*++
  899. Purpose:
  900. Draw text on the screen.
  901. Parameters:
  902. [IN] lpszText - the text to be drawn.
  903. [IN] pos - Position of the text.
  904. [IN] bColor - Color of the text.
  905. [IN] fShadow - TRUE if the text is shadowed or not.
  906. [IN] fUpdate - TRUE if update the screen area.
  907. [IN] fUse8x8Font - TRUE if use 8x8 font.
  908. Return value:
  909. None.
  910. --*/
  911. {
  912. SDL_Rect rect, urect;
  913. urect.x = rect.x = PAL_X(pos);
  914. urect.y = rect.y = PAL_Y(pos);
  915. urect.h = (fUse8x8Font ? 8 : PAL_FontHeight()) + (fShadow ? 1 : 0);
  916. urect.w = 0;
  917. // Handle text overflow
  918. if (rect.x >= 320) return;
  919. while (*lpszText)
  920. {
  921. //
  922. // Draw the character
  923. //
  924. int char_width = fUse8x8Font ? 8 : PAL_CharWidth(*lpszText);
  925. if (fShadow)
  926. {
  927. PAL_DrawCharOnSurface(*lpszText, gpScreen, PAL_XY(rect.x + 1, rect.y + 1), 0, fUse8x8Font);
  928. PAL_DrawCharOnSurface(*lpszText, gpScreen, PAL_XY(rect.x + 1, rect.y), 0, fUse8x8Font);
  929. }
  930. PAL_DrawCharOnSurface(*lpszText++, gpScreen, PAL_XY(rect.x, rect.y), bColor, fUse8x8Font);
  931. rect.x += char_width; urect.w += char_width;
  932. }
  933. //
  934. // Update the screen area
  935. //
  936. if (fUpdate && urect.w > 0)
  937. {
  938. if (fShadow) urect.w++;
  939. if (urect.x + urect.w > 320)
  940. {
  941. urect.w = 320 - urect.x;
  942. }
  943. VIDEO_UpdateScreen(&urect);
  944. }
  945. }
  946. VOID
  947. PAL_DialogSetDelayTime(
  948. INT iDelayTime
  949. )
  950. /*++
  951. Purpose:
  952. Set the delay time for dialog.
  953. Parameters:
  954. [IN] iDelayTime - the delay time to be set.
  955. Return value:
  956. None.
  957. --*/
  958. {
  959. g_TextLib.iDelayTime = iDelayTime;
  960. }
  961. VOID
  962. PAL_StartDialog(
  963. BYTE bDialogLocation,
  964. BYTE bFontColor,
  965. INT iNumCharFace,
  966. BOOL fPlayingRNG
  967. )
  968. /*++
  969. Purpose:
  970. Start a new dialog.
  971. Parameters:
  972. [IN] bDialogLocation - the location of the text on the screen.
  973. [IN] bFontColor - the font color of the text.
  974. [IN] iNumCharFace - number of the character face in RGM.MKF.
  975. [IN] fPlayingRNG - whether we are playing a RNG video or not.
  976. Return value:
  977. None.
  978. --*/
  979. {
  980. PAL_LARGE BYTE buf[16384];
  981. SDL_Rect rect;
  982. if (gpGlobals->fInBattle && !g_fUpdatedInBattle)
  983. {
  984. //
  985. // Update the screen in battle, or the graphics may seem messed up
  986. //
  987. VIDEO_UpdateScreen(NULL);
  988. g_fUpdatedInBattle = TRUE;
  989. }
  990. g_TextLib.bIcon = 0;
  991. g_TextLib.posIcon = 0;
  992. g_TextLib.nCurrentDialogLine = 0;
  993. g_TextLib.posDialogTitle = PAL_XY(12, 8);
  994. g_TextLib.fUserSkip = FALSE;
  995. if (bFontColor != 0)
  996. {
  997. g_TextLib.bCurrentFontColor = bFontColor;
  998. }
  999. if (fPlayingRNG && iNumCharFace)
  1000. {
  1001. VIDEO_BackupScreen(gpScreen);
  1002. g_TextLib.fPlayingRNG = TRUE;
  1003. }
  1004. switch (bDialogLocation)
  1005. {
  1006. case kDialogUpper:
  1007. if (iNumCharFace > 0)
  1008. {
  1009. //
  1010. // Display the character face at the upper part of the screen
  1011. //
  1012. if (PAL_MKFReadChunk(buf, 16384, iNumCharFace, gpGlobals->f.fpRGM) > 0)
  1013. {
  1014. rect.w = PAL_RLEGetWidth((LPCBITMAPRLE)buf);
  1015. rect.h = PAL_RLEGetHeight((LPCBITMAPRLE)buf);
  1016. rect.x = 48 - rect.w / 2;
  1017. rect.y = 55 - rect.h / 2;
  1018. if (rect.x < 0)
  1019. {
  1020. rect.x = 0;
  1021. }
  1022. if (rect.y < 0)
  1023. {
  1024. rect.y = 0;
  1025. }
  1026. PAL_RLEBlitToSurface((LPCBITMAPRLE)buf, gpScreen, PAL_XY(rect.x, rect.y));
  1027. if (rect.x < 0)
  1028. {
  1029. rect.x = 0;
  1030. }
  1031. if (rect.y < 0)
  1032. {
  1033. rect.y = 0;
  1034. }
  1035. VIDEO_UpdateScreen(&rect);
  1036. }
  1037. }
  1038. g_TextLib.posDialogTitle = PAL_XY(iNumCharFace > 0 ? 80 : 12, 8);
  1039. g_TextLib.posDialogText = PAL_XY(iNumCharFace > 0 ? 96 : 44, 26);
  1040. break;
  1041. case kDialogCenter:
  1042. g_TextLib.posDialogText = PAL_XY(80, 40);
  1043. break;
  1044. case kDialogLower:
  1045. if (iNumCharFace > 0)
  1046. {
  1047. //
  1048. // Display the character face at the lower part of the screen
  1049. //
  1050. if (PAL_MKFReadChunk(buf, 16384, iNumCharFace, gpGlobals->f.fpRGM) > 0)
  1051. {
  1052. rect.x = 270 - PAL_RLEGetWidth((LPCBITMAPRLE)buf) / 2;
  1053. rect.y = 144 - PAL_RLEGetHeight((LPCBITMAPRLE)buf) / 2;
  1054. PAL_RLEBlitToSurface((LPCBITMAPRLE)buf, gpScreen, PAL_XY(rect.x, rect.y));
  1055. VIDEO_UpdateScreen(NULL);
  1056. }
  1057. }
  1058. g_TextLib.posDialogTitle = PAL_XY(iNumCharFace > 0 ? 4 : 12, 108);
  1059. g_TextLib.posDialogText = PAL_XY(iNumCharFace > 0 ? 20 : 44, 126);
  1060. break;
  1061. case kDialogCenterWindow:
  1062. g_TextLib.posDialogText = PAL_XY(160, 40);
  1063. break;
  1064. }
  1065. g_TextLib.bDialogPosition = bDialogLocation;
  1066. }
  1067. static VOID
  1068. PAL_DialogWaitForKeyWithMaximumSeconds(
  1069. FLOAT fMaxSeconds
  1070. )
  1071. /*++
  1072. Purpose:
  1073. Wait for player to press a key after showing a dialog.
  1074. Parameters:
  1075. None.
  1076. Return value:
  1077. None.
  1078. --*/
  1079. {
  1080. PAL_LARGE SDL_Color palette[256];
  1081. SDL_Color *pCurrentPalette, t;
  1082. int i;
  1083. uint32_t dwBeginningTicks = SDL_GetTicks();
  1084. //
  1085. // get the current palette
  1086. //
  1087. pCurrentPalette = PAL_GetPalette(gpGlobals->wNumPalette, gpGlobals->fNightPalette);
  1088. memcpy(palette, pCurrentPalette, sizeof(palette));
  1089. if (g_TextLib.bDialogPosition != kDialogCenterWindow &&
  1090. g_TextLib.bDialogPosition != kDialogCenter)
  1091. {
  1092. //
  1093. // show the icon
  1094. //
  1095. LPCBITMAPRLE p = PAL_SpriteGetFrame(g_TextLib.bufDialogIcons, g_TextLib.bIcon);
  1096. if (p != NULL)
  1097. {
  1098. SDL_Rect rect;
  1099. rect.x = PAL_X(g_TextLib.posIcon);
  1100. rect.y = PAL_Y(g_TextLib.posIcon);
  1101. rect.w = 16;
  1102. rect.h = 16;
  1103. PAL_RLEBlitToSurface(p, gpScreen, g_TextLib.posIcon);
  1104. VIDEO_UpdateScreen(&rect);
  1105. }
  1106. }
  1107. PAL_ClearKeyState();
  1108. while (TRUE)
  1109. {
  1110. UTIL_Delay(100);
  1111. if (g_TextLib.bDialogPosition != kDialogCenterWindow &&
  1112. g_TextLib.bDialogPosition != kDialogCenter)
  1113. {
  1114. //
  1115. // palette shift
  1116. //
  1117. t = palette[0xF9];
  1118. for (i = 0xF9; i < 0xFE; i++)
  1119. {
  1120. palette[i] = palette[i + 1];
  1121. }
  1122. palette[0xFE] = t;
  1123. VIDEO_SetPalette(palette);
  1124. }
  1125. if (fabs(fMaxSeconds) > FLT_EPSILON && SDL_GetTicks() - dwBeginningTicks > 1000 * fMaxSeconds)
  1126. {
  1127. break;
  1128. }
  1129. if (g_InputState.dwKeyPress != 0)
  1130. {
  1131. break;
  1132. }
  1133. }
  1134. if (g_TextLib.bDialogPosition != kDialogCenterWindow &&
  1135. g_TextLib.bDialogPosition != kDialogCenter)
  1136. {
  1137. PAL_SetPalette(gpGlobals->wNumPalette, gpGlobals->fNightPalette);
  1138. }
  1139. PAL_ClearKeyState();
  1140. g_TextLib.fUserSkip = FALSE;
  1141. }
  1142. static VOID
  1143. PAL_DialogWaitForKey(
  1144. VOID
  1145. )
  1146. {
  1147. PAL_DialogWaitForKeyWithMaximumSeconds(0);
  1148. }
  1149. VOID
  1150. PAL_ShowDialogText(
  1151. LPCWSTR lpszText
  1152. )
  1153. /*++
  1154. Purpose:
  1155. Show one line of the dialog text.
  1156. Parameters:
  1157. [IN] lpszText - the text to be shown.
  1158. Return value:
  1159. None.
  1160. --*/
  1161. {
  1162. SDL_Rect rect;
  1163. int x, y;
  1164. PAL_ClearKeyState();
  1165. g_TextLib.bIcon = 0;
  1166. if (gpGlobals->fInBattle && !g_fUpdatedInBattle)
  1167. {
  1168. //
  1169. // Update the screen in battle, or the graphics may seem messed up
  1170. //
  1171. VIDEO_UpdateScreen(NULL);
  1172. g_fUpdatedInBattle = TRUE;
  1173. }
  1174. if (g_TextLib.nCurrentDialogLine > 3)
  1175. {
  1176. //
  1177. // The rest dialogs should be shown in the next page.
  1178. //
  1179. PAL_DialogWaitForKey();
  1180. g_TextLib.nCurrentDialogLine = 0;
  1181. VIDEO_RestoreScreen(gpScreen);
  1182. VIDEO_UpdateScreen(NULL);
  1183. }
  1184. x = PAL_X(g_TextLib.posDialogText);
  1185. y = PAL_Y(g_TextLib.posDialogText) + g_TextLib.nCurrentDialogLine * 18;
  1186. if (g_TextLib.bDialogPosition == kDialogCenterWindow)
  1187. {
  1188. //
  1189. // The text should be shown in a small window at the center of the screen
  1190. //
  1191. #ifndef PAL_CLASSIC
  1192. if (gpGlobals->fInBattle && g_Battle.BattleResult == kBattleResultOnGoing)
  1193. {
  1194. PAL_BattleUIShowText(lpszText, 1400);
  1195. }
  1196. else
  1197. #endif
  1198. {
  1199. PAL_POS pos;
  1200. LPBOX lpBox;
  1201. int i, w = wcslen(lpszText), len = 0;
  1202. for (i = 0; i < w; i++)
  1203. len += PAL_CharWidth(lpszText[i]) >> 3;
  1204. //
  1205. // Create the window box
  1206. //
  1207. pos = PAL_XY(PAL_X(g_TextLib.posDialogText) - len * 4, PAL_Y(g_TextLib.posDialogText));
  1208. // Follow behavior of original version
  1209. lpBox = PAL_CreateSingleLineBoxWithShadow(pos, (len + 1) / 2, FALSE, 0);
  1210. rect.x = PAL_X(pos);
  1211. rect.y = PAL_Y(pos);
  1212. rect.w = 320 - rect.x * 2 + 32;
  1213. rect.h = 64;
  1214. //
  1215. // Show the text on the screen
  1216. //
  1217. pos = PAL_XY(PAL_X(pos) + 8 + ((len & 1) << 2), PAL_Y(pos) + 10);
  1218. PAL_DrawText(lpszText, pos, 0, FALSE, FALSE, FALSE);
  1219. VIDEO_UpdateScreen(&rect);
  1220. PAL_DialogWaitForKeyWithMaximumSeconds(1.4);
  1221. //
  1222. // Delete the box
  1223. //
  1224. PAL_DeleteBox(lpBox);
  1225. VIDEO_UpdateScreen(&rect);
  1226. PAL_EndDialog();
  1227. }
  1228. }
  1229. else
  1230. {
  1231. int len = wcslen(lpszText);
  1232. if (g_TextLib.nCurrentDialogLine == 0 &&
  1233. g_TextLib.bDialogPosition != kDialogCenter &&
  1234. (lpszText[len - 1] == 0xff1a ||
  1235. lpszText[len - 1] == 0x2236 || // Special case for Pal WIN95 Simplified Chinese version
  1236. lpszText[len - 1] == ':')
  1237. )
  1238. {
  1239. //
  1240. // name of character
  1241. //
  1242. PAL_DrawText(lpszText, g_TextLib.posDialogTitle, FONT_COLOR_CYAN_ALT, TRUE, TRUE, FALSE);
  1243. }
  1244. else
  1245. {
  1246. //
  1247. // normal texts
  1248. //
  1249. WCHAR text[2];
  1250. if (!g_TextLib.fPlayingRNG && g_TextLib.nCurrentDialogLine == 0)
  1251. {
  1252. //
  1253. // Save the screen before we show the first line of dialog
  1254. //
  1255. VIDEO_BackupScreen(gpScreen);
  1256. }
  1257. while (lpszText != NULL && *lpszText != '\0')
  1258. {
  1259. switch (*lpszText)
  1260. {
  1261. case '-':
  1262. //
  1263. // Set the font color to Cyan
  1264. //
  1265. if (g_TextLib.bCurrentFontColor == FONT_COLOR_CYAN)
  1266. {
  1267. g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
  1268. }
  1269. else
  1270. {
  1271. g_TextLib.bCurrentFontColor = FONT_COLOR_CYAN;
  1272. }
  1273. lpszText++;
  1274. break;
  1275. #if 0
  1276. /* Not used */
  1277. case '\'':
  1278. //
  1279. // Set the font color to Red
  1280. //
  1281. if (g_TextLib.bCurrentFontColor == FONT_COLOR_RED)
  1282. {
  1283. g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
  1284. }
  1285. else
  1286. {
  1287. g_TextLib.bCurrentFontColor = FONT_COLOR_RED;
  1288. }
  1289. lpszText++;
  1290. break;
  1291. #endif
  1292. case '\"':
  1293. //
  1294. // Set the font color to Yellow
  1295. //
  1296. if (g_TextLib.bCurrentFontColor == FONT_COLOR_YELLOW)
  1297. {
  1298. g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
  1299. }
  1300. else
  1301. {
  1302. g_TextLib.bCurrentFontColor = FONT_COLOR_YELLOW;
  1303. }
  1304. lpszText++;
  1305. break;
  1306. case '$':
  1307. //
  1308. // Set the delay time of text-displaying
  1309. //
  1310. g_TextLib.iDelayTime = wcstol(lpszText + 1, NULL, 10) * 10 / 7;
  1311. lpszText += 3;
  1312. break;
  1313. case '~':
  1314. //
  1315. // Delay for a period and quit
  1316. //
  1317. if (g_TextLib.fUserSkip)
  1318. {
  1319. VIDEO_UpdateScreen(NULL);
  1320. }
  1321. UTIL_Delay(wcstol(lpszText + 1, NULL, 10) * 80 / 7);
  1322. g_TextLib.nCurrentDialogLine = 0;
  1323. g_TextLib.fUserSkip = FALSE;
  1324. return; // don't go further
  1325. case ')':
  1326. //
  1327. // Set the waiting icon
  1328. //
  1329. g_TextLib.bIcon = 1;
  1330. lpszText++;
  1331. break;
  1332. case '(':
  1333. //
  1334. // Set the waiting icon
  1335. //
  1336. g_TextLib.bIcon = 2;
  1337. lpszText++;
  1338. break;
  1339. case '\\':
  1340. lpszText++;
  1341. default:
  1342. text[0] = *lpszText++;
  1343. text[1] = 0;
  1344. // Update the screen on each draw operation is time-consuming, so disable it if user want to skip
  1345. PAL_DrawText(text, PAL_XY(x, y), g_TextLib.bCurrentFontColor, TRUE, !g_TextLib.fUserSkip, FALSE);
  1346. x += PAL_CharWidth(text[0]);
  1347. if (!g_TextLib.fUserSkip)
  1348. {
  1349. PAL_ClearKeyState();
  1350. UTIL_Delay(g_TextLib.iDelayTime * 8);
  1351. if (g_InputState.dwKeyPress & (kKeySearch | kKeyMenu))
  1352. {
  1353. //
  1354. // User pressed a key to skip the dialog
  1355. //
  1356. g_TextLib.fUserSkip = TRUE;
  1357. }
  1358. }
  1359. }
  1360. }
  1361. // and update the full screen at once after all texts are drawn
  1362. if (g_TextLib.fUserSkip)
  1363. {
  1364. VIDEO_UpdateScreen(NULL);
  1365. }
  1366. g_TextLib.posIcon = PAL_XY(x, y);
  1367. g_TextLib.nCurrentDialogLine++;
  1368. }
  1369. }
  1370. }
  1371. VOID
  1372. PAL_ClearDialog(
  1373. BOOL fWaitForKey
  1374. )
  1375. /*++
  1376. Purpose:
  1377. Clear the state of the dialog.
  1378. Parameters:
  1379. [IN] fWaitForKey - whether wait for any key or not.
  1380. Return value:
  1381. None.
  1382. --*/
  1383. {
  1384. if (g_TextLib.nCurrentDialogLine > 0 && fWaitForKey)
  1385. {
  1386. PAL_DialogWaitForKey();
  1387. }
  1388. g_TextLib.nCurrentDialogLine = 0;
  1389. if (g_TextLib.bDialogPosition == kDialogCenter)
  1390. {
  1391. g_TextLib.posDialogTitle = PAL_XY(12, 8);
  1392. g_TextLib.posDialogText = PAL_XY(44, 26);
  1393. g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
  1394. g_TextLib.bDialogPosition = kDialogUpper;
  1395. }
  1396. }
  1397. VOID
  1398. PAL_EndDialog(
  1399. VOID
  1400. )
  1401. /*++
  1402. Purpose:
  1403. Ends a dialog.
  1404. Parameters:
  1405. None.
  1406. Return value:
  1407. None.
  1408. --*/
  1409. {
  1410. PAL_ClearDialog(TRUE);
  1411. //
  1412. // Set some default parameters, as there are some parts of script
  1413. // which doesn't have a "start dialog" instruction before showing the dialog.
  1414. //
  1415. g_TextLib.posDialogTitle = PAL_XY(12, 8);
  1416. g_TextLib.posDialogText = PAL_XY(44, 26);
  1417. g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
  1418. g_TextLib.bDialogPosition = kDialogUpper;
  1419. g_TextLib.fUserSkip = FALSE;
  1420. g_TextLib.fPlayingRNG = FALSE;
  1421. }
  1422. BOOL
  1423. PAL_IsInDialog(
  1424. VOID
  1425. )
  1426. /*++
  1427. Purpose:
  1428. Check if there are dialog texts on the screen.
  1429. Parameters:
  1430. None.
  1431. Return value:
  1432. TRUE if there are dialog texts on the screen, FALSE if not.
  1433. --*/
  1434. {
  1435. return (g_TextLib.nCurrentDialogLine != 0);
  1436. }
  1437. BOOL
  1438. PAL_DialogIsPlayingRNG(
  1439. VOID
  1440. )
  1441. /*++
  1442. Purpose:
  1443. Check if the script used the RNG playing parameter when displaying texts.
  1444. Parameters:
  1445. None.
  1446. Return value:
  1447. TRUE if the script used the RNG playing parameter, FALSE if not.
  1448. --*/
  1449. {
  1450. return g_TextLib.fPlayingRNG;
  1451. }
  1452. WCHAR
  1453. PAL_GetInvalidChar(
  1454. CODEPAGE uCodePage
  1455. )
  1456. {
  1457. switch (uCodePage)
  1458. {
  1459. case CP_BIG5: return 0x3f;
  1460. case CP_GBK: return 0x3f;
  1461. //case CP_SHIFTJIS: return 0x30fb;
  1462. case CP_UTF_8: return 0x3f;
  1463. case CP_UCS: return 0x3f;
  1464. default: return 0;
  1465. }
  1466. }
  1467. static CODEPAGE g_codepage = CP_UTF_8;
  1468. CODEPAGE
  1469. PAL_GetCodePage(
  1470. void
  1471. )
  1472. {
  1473. return g_codepage;
  1474. }
  1475. void
  1476. PAL_SetCodePage(
  1477. CODEPAGE uCodePage
  1478. )
  1479. {
  1480. g_codepage = uCodePage;
  1481. }
  1482. CODEPAGE
  1483. PAL_DetectCodePageForString(
  1484. const char * text,
  1485. int text_len,
  1486. CODEPAGE default_cp,
  1487. int * probability
  1488. )
  1489. {
  1490. // Try to convert the content of word.dat with different codepages,
  1491. // and use the codepage with minimal inconvertible characters
  1492. // Works fine currently for detecting Simplified Chinese & Traditional Chinese.
  1493. // Since we're using language files to support additional languages, this detection
  1494. // should be fine for us now.
  1495. int min_invalids = INT_MAX;
  1496. if (text && text_len > 0)
  1497. {
  1498. // The file to be detected should not contain characters outside these ranges
  1499. const static int valid_ranges[][2] = {
  1500. { 0x4E00, 0x9FFF }, // CJK Unified Ideographs
  1501. { 0x3400, 0x4DBF }, // CJK Unified Ideographs Extension A
  1502. { 0xF900, 0xFAFF }, // CJK Compatibility Ideographs
  1503. { 0x0020, 0x007E }, // Basic ASCII
  1504. { 0x3000, 0x301E }, // CJK Symbols
  1505. { 0xFF01, 0xFF5E }, // Fullwidth Forms
  1506. };
  1507. for (CODEPAGE i = CP_BIG5; i <= CP_GBK; i++)
  1508. {
  1509. int invalids, length = PAL_MultiByteToWideCharCP(i, text, text_len, NULL, 0);
  1510. WCHAR *wbuf = (WCHAR *)malloc(length * sizeof(WCHAR));
  1511. PAL_MultiByteToWideCharCP(i, text, text_len, wbuf, length);
  1512. for (int j = invalids = 0; j < length; j++)
  1513. {
  1514. int score = 1;
  1515. for (int k = 0; k < sizeof(valid_ranges) / sizeof(valid_ranges[0]); k++)
  1516. {
  1517. if (wbuf[j] >= valid_ranges[k][0] &&
  1518. wbuf[j] <= valid_ranges[k][1])
  1519. {
  1520. score = 0;
  1521. break;
  1522. }
  1523. }
  1524. invalids += score;
  1525. }
  1526. // code page with less invalid chars wins
  1527. if (invalids < min_invalids)
  1528. {
  1529. min_invalids = invalids;
  1530. default_cp = i;
  1531. }
  1532. free(wbuf);
  1533. }
  1534. }
  1535. if (probability)
  1536. {
  1537. if (min_invalids < text_len / 2)
  1538. *probability = (text_len / 2 - min_invalids) * 200 / text_len;
  1539. else
  1540. *probability = 0;
  1541. }
  1542. return default_cp;
  1543. }
  1544. INT
  1545. PAL_MultiByteToWideCharCP(
  1546. CODEPAGE cp,
  1547. LPCSTR mbs,
  1548. int mbslength,
  1549. LPWSTR wcs,
  1550. int wcslength
  1551. )
  1552. /*++
  1553. Purpose:
  1554. Convert multi-byte string into the corresponding unicode string.
  1555. Parameters:
  1556. [IN] cp - Code page for conversion.
  1557. [IN] mbs - Pointer to the multi-byte string.
  1558. [IN] mbslength - Length of the multi-byte string, or -1 for auto-detect.
  1559. [IN] wcs - Pointer to the wide string buffer.
  1560. [IN] wcslength - Length of the wide string buffer.
  1561. Return value:
  1562. The length of converted wide string. If mbslength is set to -1, the returned
  1563. value includes the terminal null-char; otherwise, the null-char is not included.
  1564. If wcslength is set to 0, wcs can be set to NULL and the return value is the
  1565. required length of the wide string buffer.
  1566. --*/
  1567. {
  1568. int i = 0, state = 0, wlen = 0, null = 0;
  1569. if (mbslength == -1)
  1570. {
  1571. mbslength = strlen(mbs);
  1572. null = 1;
  1573. }
  1574. if (!wcs)
  1575. {
  1576. switch (cp)
  1577. {
  1578. //case CP_SHIFTJIS:
  1579. // for (i = 0; i < mbslength && mbs[i]; i++)
  1580. // {
  1581. // if (state == 0)
  1582. // {
  1583. // if ((BYTE)mbs[i] <= 0x80 || (BYTE)mbs[i] >= 0xfd || ((BYTE)mbs[i] >= 0xa0 && (BYTE)mbs[i] <= 0xdf))
  1584. // wlen++;
  1585. // else
  1586. // state = 1;
  1587. // }
  1588. // else
  1589. // {
  1590. // wlen++;
  1591. // state = 0;
  1592. // }
  1593. // }
  1594. // break;
  1595. case CP_GBK:
  1596. case CP_BIG5:
  1597. for (i = 0; i < mbslength && mbs[i]; i++)
  1598. {
  1599. if (state == 0)
  1600. {
  1601. if ((BYTE)mbs[i] <= 0x80 || (BYTE)mbs[i] == 0xff)
  1602. wlen++;
  1603. else
  1604. state = 1;
  1605. }
  1606. else
  1607. {
  1608. wlen++;
  1609. state = 0;
  1610. }
  1611. }
  1612. break;
  1613. case CP_UTF_8:
  1614. for (i = 0; i < mbslength && mbs[i]; i++)
  1615. {
  1616. if (state == 0)
  1617. {
  1618. if ((BYTE)mbs[i] >= 0x80)
  1619. {
  1620. BYTE s = (BYTE)mbs[i] << 1;
  1621. while (s >= 0x80) { state++; s <<= 1; }
  1622. if (state < 1 || state > 3)
  1623. {
  1624. state = 0;
  1625. wlen++;
  1626. }
  1627. }
  1628. else
  1629. wlen++;
  1630. }
  1631. else
  1632. {
  1633. if ((BYTE)mbs[i] >= 0x80 && (BYTE)mbs[i] < 0xc0)
  1634. {
  1635. if (--state == 0) wlen++;
  1636. }
  1637. else
  1638. {
  1639. state = 0; wlen++;
  1640. }
  1641. }
  1642. }
  1643. break;
  1644. case CP_UCS:
  1645. i = mbslength;
  1646. wlen = mbslength/2;
  1647. break;
  1648. default:
  1649. return -1;
  1650. }
  1651. if (i < mbslength && !mbs[i]) null = 1;
  1652. return wlen + null + (state != 0);
  1653. }
  1654. else
  1655. {
  1656. WCHAR invalid_char;
  1657. switch (cp)
  1658. {
  1659. //case CP_SHIFTJIS:
  1660. // invalid_char = 0x30fb;
  1661. // for (i = 0; i < mbslength && wlen < wcslength && mbs[i]; i++)
  1662. // {
  1663. // if (state == 0)
  1664. // {
  1665. // if ((BYTE)mbs[i] <= 0x80)
  1666. // wcs[wlen++] = mbs[i];
  1667. // else if ((BYTE)mbs[i] >= 0xa0 && (BYTE)mbs[i] <= 0xdf)
  1668. // wcs[wlen++] = cptbl_jis_half[(BYTE)mbs[i] - 0xa0];
  1669. // else if ((BYTE)mbs[i] == 0xfd)
  1670. // wcs[wlen++] = 0xf8f1;
  1671. // else if ((BYTE)mbs[i] == 0xfe)
  1672. // wcs[wlen++] = 0xf8f2;
  1673. // else if ((BYTE)mbs[i] == 0xff)
  1674. // wcs[wlen++] = 0xf8f3;
  1675. // else
  1676. // state = 1;
  1677. // }
  1678. // else
  1679. // {
  1680. // if ((BYTE)mbs[i] < 0x40)
  1681. // wcs[wlen++] = 0x30fb;
  1682. // else if ((BYTE)mbs[i - 1] < 0xa0)
  1683. // wcs[wlen++] = cptbl_jis[(BYTE)mbs[i - 1] - 0x81][(BYTE)mbs[i] - 0x40];
  1684. // else
  1685. // wcs[wlen++] = cptbl_jis[(BYTE)mbs[i - 1] - 0xc1][(BYTE)mbs[i] - 0x40];
  1686. // state = 0;
  1687. // }
  1688. // }
  1689. // break;
  1690. case CP_GBK:
  1691. invalid_char = 0x3f;
  1692. for (i = 0; i < mbslength && wlen < wcslength && mbs[i]; i++)
  1693. {
  1694. if (state == 0)
  1695. {
  1696. if ((BYTE)mbs[i] < 0x80)
  1697. wcs[wlen++] = mbs[i];
  1698. else if ((BYTE)mbs[i] == 0x80)
  1699. wcs[wlen++] = 0x20ac;
  1700. else if ((BYTE)mbs[i] == 0xff)
  1701. wcs[wlen++] = 0xf8f5;
  1702. else
  1703. state = 1;
  1704. }
  1705. else
  1706. {
  1707. if ((BYTE)mbs[i] < 0x40)
  1708. wcs[wlen++] = invalid_char;
  1709. else
  1710. wcs[wlen++] = cptbl_gbk[(BYTE)mbs[i - 1] - 0x81][(BYTE)mbs[i] - 0x40];
  1711. state = 0;
  1712. }
  1713. }
  1714. break;
  1715. case CP_BIG5:
  1716. invalid_char = 0x3f;
  1717. for (i = 0; i < mbslength && wlen < wcslength && mbs[i]; i++)
  1718. {
  1719. if (state == 0)
  1720. {
  1721. if ((BYTE)mbs[i] <= 0x80)
  1722. wcs[wlen++] = mbs[i];
  1723. else if ((BYTE)mbs[i] == 0xff)
  1724. wcs[wlen++] = 0xf8f8;
  1725. else
  1726. state = 1;
  1727. }
  1728. else
  1729. {
  1730. if ((BYTE)mbs[i] < 0x40 || ((BYTE)mbs[i] >= 0x7f && (BYTE)mbs[i] <= 0xa0))
  1731. wcs[wlen++] = invalid_char;
  1732. else if ((BYTE)mbs[i] <= 0x7e)
  1733. wcs[wlen++] = cptbl_big5[(BYTE)mbs[i - 1] - 0x81][(BYTE)mbs[i] - 0x40];
  1734. else
  1735. wcs[wlen++] = cptbl_big5[(BYTE)mbs[i - 1] - 0x81][(BYTE)mbs[i] - 0x60];
  1736. state = 0;
  1737. }
  1738. }
  1739. break;
  1740. case CP_UTF_8:
  1741. invalid_char = 0x3f;
  1742. for (i = 0; i < mbslength && wlen < wcslength && mbs[i]; i++)
  1743. {
  1744. if (state == 0)
  1745. {
  1746. if ((BYTE)mbs[i] >= 0x80)
  1747. {
  1748. BYTE s = (BYTE)mbs[i] << 1;
  1749. while (s >= 0x80) { state++; s <<= 1; }
  1750. if (state < 1 || state > 3)
  1751. {
  1752. state = 0;
  1753. wcs[wlen++] = invalid_char;
  1754. }
  1755. else
  1756. {
  1757. wcs[wlen] = s >> (state + 1);
  1758. }
  1759. }
  1760. else
  1761. wcs[wlen++] = mbs[i];
  1762. }
  1763. else
  1764. {
  1765. if ((BYTE)mbs[i] >= 0x80 && (BYTE)mbs[i] < 0xc0)
  1766. {
  1767. wcs[wlen] <<= 6;
  1768. wcs[wlen] |= (BYTE)mbs[i] & 0x3f;
  1769. if (--state == 0) wlen++;
  1770. }
  1771. else
  1772. {
  1773. state = 0;
  1774. wcs[wlen++] = invalid_char;
  1775. }
  1776. }
  1777. }
  1778. break;
  1779. case CP_UCS:
  1780. for (i = 0; i < mbslength && wlen < wcslength; i+=2){
  1781. uint8_t *ptr = (uint8_t*)&wcs[wlen++];
  1782. *(ptr+1)=mbs[i];
  1783. *ptr =mbs[i+1];
  1784. }
  1785. break;
  1786. default:
  1787. return -1;
  1788. }
  1789. if (state != 0 && wlen < wcslength)
  1790. {
  1791. wcs[wlen++] = invalid_char;
  1792. }
  1793. if (null || (i < mbslength && !mbs[i]))
  1794. {
  1795. if (wlen < wcslength)
  1796. wcs[wlen++] = 0;
  1797. else
  1798. wcs[wlen - 1] = 0;
  1799. }
  1800. return wlen;
  1801. }
  1802. }
  1803. INT
  1804. PAL_MultiByteToWideChar(
  1805. LPCSTR mbs,
  1806. int mbslength,
  1807. LPWSTR wcs,
  1808. int wcslength
  1809. )
  1810. /*++
  1811. Purpose:
  1812. Convert multi-byte string into the corresponding unicode string.
  1813. Parameters:
  1814. [IN] mbs - Pointer to the multi-byte string.
  1815. [IN] mbslength - Length of the multi-byte string, or -1 for auto-detect.
  1816. [IN] wcs - Pointer to the wide string buffer.
  1817. [IN] wcslength - Length of the wide string buffer.
  1818. Return value:
  1819. The length of converted wide string. If mbslength is set to -1, the returned
  1820. value includes the terminal null-char; otherwise, the null-char is not included.
  1821. If wcslength is set to 0, wcs can be set to NULL and the return value is the
  1822. required length of the wide string buffer.
  1823. --*/
  1824. {
  1825. return PAL_MultiByteToWideCharCP(g_codepage, mbs, mbslength, wcs, wcslength);
  1826. }
  1827. INT
  1828. PAL_swprintf(
  1829. LPWSTR buffer,
  1830. size_t count,
  1831. LPCWSTR format,
  1832. ...
  1833. )
  1834. /*++
  1835. Purpose:
  1836. Formatted wide-character output conversion that output Chinese characters correctly.
  1837. This function supported a subset of format strings that are commonly supported by
  1838. various C libraries, which can be formalized as following:
  1839. %[flags] [width] [.precision] [{h | l | ll}] type
  1840. When handling '%c' and '%s', this function follows the Linux's library convention,
  1841. which means '%c' and '%s' always output multi-byte strings, and '%lc' and '%ls'
  1842. always output wide-char strings.
  1843. Parameters:
  1844. [IN] buffer - Storage location for output.
  1845. [IN] count - Length of the output buffer in characters including the termination null one.
  1846. [IN] format - Format-control string.
  1847. [IN] ... - Optional arguments.
  1848. Return value:
  1849. The length of outputed wide string, not including the termination null character.
  1850. --*/
  1851. {
  1852. va_list ap;
  1853. const WCHAR * const format_end = format + wcslen(format);
  1854. const WCHAR * const buffer_end = buffer + count - 1;
  1855. WCHAR chr_buf[2] = { 0, 0 };
  1856. LPCWSTR fmt_start = NULL;
  1857. LPWSTR cur_fmt = NULL;
  1858. size_t fmt_len = 0;
  1859. int state, precision = 0, width = 0;
  1860. int left_aligned = 0, wide = 0, narrow = 0;
  1861. int width_var = 0, precision_var = 0, precision_defined = 0;
  1862. // Buffer & length check
  1863. if (buffer == NULL || format == NULL)
  1864. {
  1865. errno = EINVAL;
  1866. return -1;
  1867. }
  1868. if (buffer_end <= buffer)
  1869. return 0;
  1870. va_start(ap, format);
  1871. count = 0; state = 0;
  1872. while (buffer < buffer_end && format < format_end)
  1873. {
  1874. switch (state)
  1875. {
  1876. case 0: // Outside format spec
  1877. if (*format != L'%')
  1878. {
  1879. *buffer++ = *format++;
  1880. count++;
  1881. }
  1882. else
  1883. {
  1884. fmt_start = format++;
  1885. left_aligned = wide = narrow = 0;
  1886. precision_var = width_var = 0;
  1887. precision_defined = 0;
  1888. state = 1;
  1889. }
  1890. continue;
  1891. case 1: // [flags]
  1892. switch (*format)
  1893. {
  1894. case L'-':
  1895. left_aligned = 1;
  1896. case L'+':
  1897. case L' ':
  1898. case L'#':
  1899. case L'0':
  1900. format++;
  1901. continue;
  1902. default:
  1903. state = 2;
  1904. width = width_var = 0;
  1905. }
  1906. case 2: // [width]
  1907. switch (*format)
  1908. {
  1909. case '0':
  1910. case '1':
  1911. case '2':
  1912. case '3':
  1913. case '4':
  1914. case '5':
  1915. case '6':
  1916. case '7':
  1917. case '8':
  1918. case '9':
  1919. if (width >= 0)
  1920. width = width * 10 + (*format - L'0');
  1921. format++;
  1922. continue;
  1923. case '*':
  1924. if (width == 0)
  1925. width_var = 1;
  1926. format++;
  1927. continue;
  1928. case '.':
  1929. format++;
  1930. precision = precision_var = 0;
  1931. precision_defined = 1;
  1932. state = 3;
  1933. continue;
  1934. default:
  1935. state = 4;
  1936. continue;
  1937. }
  1938. case 3: // [.precision]
  1939. switch (*format)
  1940. {
  1941. case '0':
  1942. case '1':
  1943. case '2':
  1944. case '3':
  1945. case '4':
  1946. case '5':
  1947. case '6':
  1948. case '7':
  1949. case '8':
  1950. case '9':
  1951. if (precision >= 0)
  1952. precision = precision * 10 + (*format - L'0');
  1953. format++;
  1954. continue;
  1955. case '*':
  1956. if (precision == 0)
  1957. precision_var = 1;
  1958. format++;
  1959. continue;
  1960. default:
  1961. state = 4;
  1962. }
  1963. case 4: // [{h | l | ll}]
  1964. switch (*format)
  1965. {
  1966. case 'l': if (narrow == 0) wide++; format++; continue;
  1967. case 'h': if (wide == 0) narrow++; format++; continue;
  1968. default: state = 5;
  1969. }
  1970. case 5: // type
  1971. if (*format == 'c' || *format == 's')
  1972. {
  1973. // We handle char & str specially
  1974. LPWSTR buf;
  1975. size_t len;
  1976. int i;
  1977. // Check width
  1978. if (width_var)
  1979. {
  1980. width = va_arg(ap, int);
  1981. left_aligned = (width < 0);
  1982. width = left_aligned ? -width : width;
  1983. }
  1984. // Although precision has no meaning to '%c' output, however
  1985. // the argument still needs to be read if '.*' is provided
  1986. if (precision_var)
  1987. precision = va_arg(ap, int);
  1988. else if (!precision_defined)
  1989. precision = INT_MAX;
  1990. if (*format == 's')
  1991. {
  1992. // For ANSI string, convert it through PAL_MultiByteToWideChar
  1993. // To improve effciency, here just test the length and left
  1994. // actual conversion later directly into the output buffer
  1995. if (wide)
  1996. {
  1997. buf = va_arg(ap, LPWSTR);
  1998. len = wcslen(buf);
  1999. }
  2000. else
  2001. {
  2002. buf = (LPWSTR)va_arg(ap, LPSTR);
  2003. len = PAL_MultiByteToWideChar((LPCSTR)buf, -1, NULL, 0) - 1;
  2004. }
  2005. }
  2006. else
  2007. {
  2008. // For ANSI character, put it into the internal buffer
  2009. if (wide)
  2010. chr_buf[0] = va_arg(ap, WCHAR);
  2011. else
  2012. chr_buf[0] = va_arg(ap, int);
  2013. buf = chr_buf; len = 1;
  2014. }
  2015. // Limit output length no longer then precision
  2016. if (precision > (int)len)
  2017. precision = len;
  2018. // Left-side padding
  2019. for (i = 0; !left_aligned && i < width - precision && buffer < buffer_end; i++)
  2020. *buffer++ = L' ', count++;
  2021. // Do not overflow the output buffer
  2022. if (buffer + precision > buffer_end)
  2023. precision = buffer_end - buffer;
  2024. // Convert or copy string (char) into output buffer
  2025. if (*format == 's' && !wide)
  2026. PAL_MultiByteToWideChar((LPCSTR)buf, -1, buffer, precision);
  2027. else
  2028. wcsncpy(buffer, buf, precision);
  2029. buffer += precision; count += precision;
  2030. // Right-side padding
  2031. for (i = 0; left_aligned && i < width - precision && buffer < buffer_end; i++)
  2032. *buffer++ = L' ', count++;
  2033. }
  2034. else
  2035. {
  2036. // For other types, pass them directly into vswprintf
  2037. int cur_cnt = 0;
  2038. va_list apd;
  2039. // We copy this argument's format string into internal buffer
  2040. if (fmt_len < (size_t)(format - fmt_start + 1))
  2041. cur_fmt = realloc(cur_fmt, ((fmt_len = format - fmt_start + 1) + 1) * sizeof(WCHAR));
  2042. wcsncpy(cur_fmt, fmt_start, fmt_len);
  2043. cur_fmt[fmt_len] = L'\0';
  2044. // And pass it into vswprintf to get the output
  2045. va_copy(apd, ap);
  2046. cur_cnt = vswprintf(buffer, buffer_end - buffer, cur_fmt, apd);
  2047. va_end(apd);
  2048. buffer += cur_cnt; count += cur_cnt;
  2049. // Then we need to move the argument pointer into next one
  2050. // Check if width/precision should be read from arguments
  2051. if (width_var) va_arg(ap, int);
  2052. if (precision_var) va_arg(ap, int);
  2053. // Move pointer to pass the actual value argument
  2054. switch (*format)
  2055. {
  2056. case 'd':
  2057. case 'i':
  2058. case 'o':
  2059. case 'u':
  2060. case 'x':
  2061. case 'X':
  2062. if (wide == 1)
  2063. va_arg(ap, long);
  2064. else if (wide >= 2)
  2065. va_arg(ap, long long);
  2066. else
  2067. va_arg(ap, int);
  2068. break;
  2069. case 'e':
  2070. case 'E':
  2071. case 'f':
  2072. case 'g':
  2073. case 'G':
  2074. case 'a':
  2075. case 'A':
  2076. va_arg(ap, double);
  2077. break;
  2078. case 'p':
  2079. case 'n':
  2080. va_arg(ap, void*);
  2081. break;
  2082. }
  2083. }
  2084. state = 0;
  2085. format++;
  2086. break;
  2087. }
  2088. }
  2089. // If the format string is malformed, try to copy it into the dest buffer
  2090. if (state && buffer < buffer_end)
  2091. {
  2092. int fmt_len = format - fmt_start;
  2093. int buf_len = buffer_end - buffer;
  2094. if (fmt_len <= buf_len)
  2095. {
  2096. wcsncpy(buffer, fmt_start, buf_len);
  2097. buffer += fmt_len;
  2098. }
  2099. else
  2100. {
  2101. wcsncpy(buffer, fmt_start, buf_len);
  2102. buffer += buf_len;
  2103. }
  2104. }
  2105. // NULL-terminate the string
  2106. *buffer = L'\0';
  2107. va_end(ap);
  2108. return count;
  2109. }