text.c 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219
  1. /* -*- mode: c; tab-width: 4; c-basic-offset: 3; c-file-style: "linux" -*- */
  2. //
  3. // Copyright (c) 2008, Wei Mingzhi <whistler_wmz@users.sf.net>.
  4. // All rights reserved.
  5. //
  6. // Portions based on PALx Project by palxex.
  7. // Copyright (c) 2006-2008, Pal Lockheart <palxex@gmail.com>.
  8. //
  9. // This file is part of SDLPAL.
  10. //
  11. // SDLPAL is free software: you can redistribute it and/or modify
  12. // it under the terms of the GNU General Public License as published by
  13. // the Free Software Foundation, either version 3 of the License, or
  14. // (at your option) any later version.
  15. //
  16. // This program is distributed in the hope that it will be useful,
  17. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. // GNU General Public License for more details.
  20. //
  21. // You should have received a copy of the GNU General Public License
  22. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. //
  24. // Modified by Lou Yihua <louyihua@21cn.com> with Unicode support, 2015
  25. //
  26. #include "main.h"
  27. #define FONT_COLOR_DEFAULT 0x4F
  28. #define FONT_COLOR_YELLOW 0x2D
  29. #define FONT_COLOR_RED 0x1A
  30. #define FONT_COLOR_CYAN 0x8D
  31. #define FONT_COLOR_CYAN_ALT 0x8C
  32. BOOL g_fUpdatedInBattle = FALSE;
  33. #define INCLUDE_CODEPAGE_H
  34. #include "codepage.h"
  35. #ifndef PAL_CLASSIC
  36. static const WCHAR* gc_rgszAdditionalWords[CP_MAX][6] = {
  37. { L"\x6230\x9B25\x901F\x5EA6", L"\x4E00", L"\x4E8C", L"\x4E09", L"\x56DB", L"\x4E94" },
  38. { L"\x6218\x6597\x901F\x5EA6", L"\x4E00", L"\x4E8C", L"\x4E09", L"\x56DB", L"\x4E94" },
  39. { L"\x6226\x95D8\x901F\x5EA6", L"\x4E00", L"\x4E8C", L"\x4E09", L"\x56DB", L"\x4E94" },
  40. };
  41. static const WCHAR** g_rgszAdditionalWords;
  42. #endif
  43. typedef struct tagTEXTLIB
  44. {
  45. LPWSTR* lpWordBuf;
  46. LPWSTR* lpMsgBuf;
  47. int nWords;
  48. int nMsgs;
  49. int nCurrentDialogLine;
  50. BYTE bCurrentFontColor;
  51. PAL_POS posIcon;
  52. PAL_POS posDialogTitle;
  53. PAL_POS posDialogText;
  54. BYTE bDialogPosition;
  55. BYTE bIcon;
  56. int iDelayTime;
  57. BOOL fUserSkip;
  58. BOOL fPlayingRNG;
  59. BYTE bufDialogIcons[282];
  60. } TEXTLIB, *LPTEXTLIB;
  61. static TEXTLIB g_TextLib;
  62. INT
  63. PAL_InitText(
  64. VOID
  65. )
  66. /*++
  67. Purpose:
  68. Initialize the in-game texts.
  69. Parameters:
  70. None.
  71. Return value:
  72. 0 = success.
  73. -1 = memory allocation error.
  74. --*/
  75. {
  76. FILE *fpMsg, *fpWord;
  77. DWORD *offsets;
  78. LPBYTE temp;
  79. LPWSTR tmp;
  80. int wlen, wpos;
  81. int i;
  82. //
  83. // Open the message and word data files.
  84. //
  85. fpMsg = UTIL_OpenRequiredFile("m.msg");
  86. fpWord = UTIL_OpenRequiredFile("word.dat");
  87. //
  88. // See how many words we have
  89. //
  90. fseek(fpWord, 0, SEEK_END);
  91. i = ftell(fpWord);
  92. //
  93. // Each word has 10 or 16 bytes
  94. //
  95. g_TextLib.nWords = (i + (gpGlobals->dwWordLength - 1)) / gpGlobals->dwWordLength;
  96. //
  97. // Read the words
  98. //
  99. temp = (LPBYTE)malloc(i);
  100. if (temp == NULL)
  101. {
  102. fclose(fpWord);
  103. fclose(fpMsg);
  104. return -1;
  105. }
  106. fseek(fpWord, 0, SEEK_SET);
  107. fread(temp, i, 1, fpWord);
  108. //
  109. // Close the words file
  110. //
  111. fclose(fpWord);
  112. // Split the words and do code page conversion
  113. for (i = 0, wlen = 0; i < g_TextLib.nWords; i++)
  114. {
  115. int base = i * gpGlobals->dwWordLength;
  116. int pos = base + gpGlobals->dwWordLength - 1;
  117. while (pos >= base && temp[pos] == ' ') temp[pos--] = 0;
  118. wlen += PAL_MultiByteToWideChar(temp + base, gpGlobals->dwWordLength, NULL, 0) + 1;
  119. }
  120. g_TextLib.lpWordBuf = (LPWSTR*)malloc(g_TextLib.nWords * sizeof(LPWSTR));
  121. tmp = (LPWSTR)malloc(wlen * sizeof(WCHAR));
  122. for (i = 0, wpos = 0; i < g_TextLib.nWords; i++)
  123. {
  124. int l;
  125. g_TextLib.lpWordBuf[i] = tmp + wpos;
  126. l = PAL_MultiByteToWideChar(temp + i * gpGlobals->dwWordLength, gpGlobals->dwWordLength, g_TextLib.lpWordBuf[i], wlen - wpos);
  127. if (l > 0 && g_TextLib.lpWordBuf[i][l - 1] == '1')
  128. g_TextLib.lpWordBuf[i][l - 1] = 0;
  129. g_TextLib.lpWordBuf[i][l] = 0;
  130. wpos += l + 1;
  131. }
  132. free(temp);
  133. //
  134. // Read the message offsets. The message offsets are in SSS.MKF #3
  135. //
  136. i = PAL_MKFGetChunkSize(3, gpGlobals->f.fpSSS) / sizeof(DWORD);
  137. g_TextLib.nMsgs = i - 1;
  138. offsets = (LPDWORD)malloc(i * sizeof(DWORD));
  139. if (offsets == NULL)
  140. {
  141. free(g_TextLib.lpWordBuf);
  142. fclose(fpMsg);
  143. return -1;
  144. }
  145. PAL_MKFReadChunk((LPBYTE)(offsets), i * sizeof(DWORD), 3, gpGlobals->f.fpSSS);
  146. //
  147. // Read the messages.
  148. //
  149. fseek(fpMsg, 0, SEEK_END);
  150. i = ftell(fpMsg);
  151. temp = (LPBYTE)malloc(i);
  152. if (temp == NULL)
  153. {
  154. free(offsets);
  155. free(g_TextLib.lpWordBuf[0]);
  156. free(g_TextLib.lpWordBuf);
  157. fclose(fpMsg);
  158. return -1;
  159. }
  160. fseek(fpMsg, 0, SEEK_SET);
  161. fread(temp, 1, i, fpMsg);
  162. fclose(fpMsg);
  163. // Split messages and do code page conversion here
  164. for (i = 0, wlen = 0; i < g_TextLib.nMsgs; i++)
  165. {
  166. wlen += PAL_MultiByteToWideChar(temp + SWAP32(offsets[i]), SWAP32(offsets[i + 1]) - SWAP32(offsets[i]), NULL, 0) + 1;
  167. }
  168. g_TextLib.lpMsgBuf = (LPWSTR*)malloc(g_TextLib.nMsgs * sizeof(LPWSTR));
  169. tmp = (LPWSTR)malloc(wlen * sizeof(WCHAR));
  170. for (i = 0, wpos = 0; i < g_TextLib.nMsgs; i++)
  171. {
  172. int l;
  173. g_TextLib.lpMsgBuf[i] = tmp + wpos;
  174. l = PAL_MultiByteToWideChar(temp + SWAP32(offsets[i]), SWAP32(offsets[i + 1]) - SWAP32(offsets[i]), g_TextLib.lpMsgBuf[i], wlen - wpos);
  175. g_TextLib.lpMsgBuf[i][l] = 0;
  176. wpos += l + 1;
  177. }
  178. free(temp);
  179. free(offsets);
  180. #ifndef PAL_CLASSIC
  181. g_rgszAdditionalWords = gc_rgszAdditionalWords[gpGlobals->iCodePage];
  182. #endif
  183. g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
  184. g_TextLib.bIcon = 0;
  185. g_TextLib.posIcon = 0;
  186. g_TextLib.nCurrentDialogLine = 0;
  187. g_TextLib.iDelayTime = 3;
  188. g_TextLib.posDialogTitle = PAL_XY(12, 8);
  189. g_TextLib.posDialogText = PAL_XY(44, 26);
  190. g_TextLib.bDialogPosition = kDialogUpper;
  191. g_TextLib.fUserSkip = FALSE;
  192. PAL_MKFReadChunk(g_TextLib.bufDialogIcons, 282, 12, gpGlobals->f.fpDATA);
  193. return 0;
  194. }
  195. VOID
  196. PAL_FreeText(
  197. VOID
  198. )
  199. /*++
  200. Purpose:
  201. Free the memory used by the texts.
  202. Parameters:
  203. None.
  204. Return value:
  205. None.
  206. --*/
  207. {
  208. if (g_TextLib.lpMsgBuf != NULL)
  209. {
  210. free(g_TextLib.lpMsgBuf[0]);
  211. free(g_TextLib.lpMsgBuf);
  212. g_TextLib.lpMsgBuf = NULL;
  213. }
  214. if (g_TextLib.lpWordBuf != NULL)
  215. {
  216. free(g_TextLib.lpWordBuf[0]);
  217. free(g_TextLib.lpWordBuf);
  218. g_TextLib.lpWordBuf = NULL;
  219. }
  220. }
  221. LPCWSTR
  222. PAL_GetWord(
  223. WORD wNumWord
  224. )
  225. /*++
  226. Purpose:
  227. Get the specified word.
  228. Parameters:
  229. [IN] wNumWord - the number of the requested word.
  230. Return value:
  231. Pointer to the requested word. NULL if not found.
  232. --*/
  233. {
  234. #ifndef PAL_CLASSIC
  235. if (wNumWord >= PAL_ADDITIONAL_WORD_FIRST)
  236. {
  237. return g_rgszAdditionalWords[wNumWord - PAL_ADDITIONAL_WORD_FIRST];
  238. }
  239. #endif
  240. if (wNumWord >= g_TextLib.nWords)
  241. {
  242. return NULL;
  243. }
  244. return g_TextLib.lpWordBuf[wNumWord];
  245. }
  246. LPCWSTR
  247. PAL_GetMsg(
  248. WORD wNumMsg
  249. )
  250. /*++
  251. Purpose:
  252. Get the specified message.
  253. Parameters:
  254. [IN] wNumMsg - the number of the requested message.
  255. Return value:
  256. Pointer to the requested message. NULL if not found.
  257. --*/
  258. {
  259. return (wNumMsg >= g_TextLib.nMsgs) ? NULL : g_TextLib.lpMsgBuf[wNumMsg];
  260. }
  261. VOID
  262. PAL_DrawText(
  263. LPCWSTR lpszText,
  264. PAL_POS pos,
  265. BYTE bColor,
  266. BOOL fShadow,
  267. BOOL fUpdate
  268. )
  269. /*++
  270. Purpose:
  271. Draw text on the screen.
  272. Parameters:
  273. [IN] lpszText - the text to be drawn.
  274. [IN] pos - Position of the text.
  275. [IN] bColor - Color of the text.
  276. [IN] fShadow - TRUE if the text is shadowed or not.
  277. [IN] fUpdate - TRUE if update the screen area.
  278. Return value:
  279. None.
  280. --*/
  281. {
  282. SDL_Rect rect, urect;
  283. rect.x = PAL_X(pos);
  284. rect.y = PAL_Y(pos);
  285. urect.x = rect.x;
  286. urect.y = rect.y;
  287. urect.h = gpGlobals->fUseEmbeddedFonts ? 16 : 17;
  288. urect.w = 0;
  289. while (*lpszText)
  290. {
  291. //
  292. // Draw the character
  293. //
  294. int char_width = PAL_CharWidth(*lpszText);
  295. if (fShadow)
  296. {
  297. PAL_DrawCharOnSurface(*lpszText, gpScreen, PAL_XY(rect.x + 1, rect.y + 1), 0);
  298. PAL_DrawCharOnSurface(*lpszText, gpScreen, PAL_XY(rect.x + 1, rect.y), 0);
  299. }
  300. PAL_DrawCharOnSurface(*lpszText, gpScreen, PAL_XY(rect.x, rect.y), bColor);
  301. lpszText++;
  302. rect.x += char_width;
  303. urect.w += char_width;
  304. }
  305. //
  306. // Update the screen area
  307. //
  308. if (fUpdate && urect.w > 0)
  309. {
  310. if (gpGlobals->fIsWIN95)
  311. {
  312. urect.w++;
  313. if (urect.x + urect.w > 320)
  314. {
  315. urect.w = 320 - urect.x;
  316. }
  317. }
  318. VIDEO_UpdateScreen(&urect);
  319. }
  320. }
  321. VOID
  322. PAL_DialogSetDelayTime(
  323. INT iDelayTime
  324. )
  325. /*++
  326. Purpose:
  327. Set the delay time for dialog.
  328. Parameters:
  329. [IN] iDelayTime - the delay time to be set.
  330. Return value:
  331. None.
  332. --*/
  333. {
  334. g_TextLib.iDelayTime = iDelayTime;
  335. }
  336. VOID
  337. PAL_StartDialog(
  338. BYTE bDialogLocation,
  339. BYTE bFontColor,
  340. INT iNumCharFace,
  341. BOOL fPlayingRNG
  342. )
  343. /*++
  344. Purpose:
  345. Start a new dialog.
  346. Parameters:
  347. [IN] bDialogLocation - the location of the text on the screen.
  348. [IN] bFontColor - the font color of the text.
  349. [IN] iNumCharFace - number of the character face in RGM.MKF.
  350. [IN] fPlayingRNG - whether we are playing a RNG video or not.
  351. Return value:
  352. None.
  353. --*/
  354. {
  355. PAL_LARGE BYTE buf[16384];
  356. SDL_Rect rect;
  357. if (gpGlobals->fInBattle && !g_fUpdatedInBattle)
  358. {
  359. //
  360. // Update the screen in battle, or the graphics may seem messed up
  361. //
  362. VIDEO_UpdateScreen(NULL);
  363. g_fUpdatedInBattle = TRUE;
  364. }
  365. g_TextLib.bIcon = 0;
  366. g_TextLib.posIcon = 0;
  367. g_TextLib.nCurrentDialogLine = 0;
  368. g_TextLib.posDialogTitle = PAL_XY(12, 8);
  369. g_TextLib.fUserSkip = FALSE;
  370. if (bFontColor != 0)
  371. {
  372. g_TextLib.bCurrentFontColor = bFontColor;
  373. }
  374. if (fPlayingRNG && iNumCharFace)
  375. {
  376. VIDEO_BackupScreen();
  377. g_TextLib.fPlayingRNG = TRUE;
  378. }
  379. switch (bDialogLocation)
  380. {
  381. case kDialogUpper:
  382. if (iNumCharFace > 0)
  383. {
  384. //
  385. // Display the character face at the upper part of the screen
  386. //
  387. if (PAL_MKFReadChunk(buf, 16384, iNumCharFace, gpGlobals->f.fpRGM) > 0)
  388. {
  389. rect.w = PAL_RLEGetWidth((LPCBITMAPRLE)buf);
  390. rect.h = PAL_RLEGetHeight((LPCBITMAPRLE)buf);
  391. rect.x = 48 - rect.w / 2;
  392. rect.y = 55 - rect.h / 2;
  393. if (rect.x < 0)
  394. {
  395. rect.x = 0;
  396. }
  397. if (rect.y < 0)
  398. {
  399. rect.y = 0;
  400. }
  401. PAL_RLEBlitToSurface((LPCBITMAPRLE)buf, gpScreen, PAL_XY(rect.x, rect.y));
  402. if (rect.x < 0)
  403. {
  404. rect.x = 0;
  405. }
  406. if (rect.y < 0)
  407. {
  408. rect.y = 0;
  409. }
  410. VIDEO_UpdateScreen(&rect);
  411. }
  412. }
  413. g_TextLib.posDialogTitle = PAL_XY(iNumCharFace > 0 ? 80 : 12, 8);
  414. g_TextLib.posDialogText = PAL_XY(iNumCharFace > 0 ? 96 : 44, 26);
  415. break;
  416. case kDialogCenter:
  417. g_TextLib.posDialogText = PAL_XY(80, 40);
  418. break;
  419. case kDialogLower:
  420. if (iNumCharFace > 0)
  421. {
  422. //
  423. // Display the character face at the lower part of the screen
  424. //
  425. if (PAL_MKFReadChunk(buf, 16384, iNumCharFace, gpGlobals->f.fpRGM) > 0)
  426. {
  427. rect.x = 270 - PAL_RLEGetWidth((LPCBITMAPRLE)buf) / 2;
  428. rect.y = 144 - PAL_RLEGetHeight((LPCBITMAPRLE)buf) / 2;
  429. PAL_RLEBlitToSurface((LPCBITMAPRLE)buf, gpScreen, PAL_XY(rect.x, rect.y));
  430. VIDEO_UpdateScreen(NULL);
  431. }
  432. }
  433. g_TextLib.posDialogTitle = PAL_XY(iNumCharFace > 0 ? 4 : 12, 108);
  434. g_TextLib.posDialogText = PAL_XY(iNumCharFace > 0 ? 20 : 44, 126);
  435. break;
  436. case kDialogCenterWindow:
  437. g_TextLib.posDialogText = PAL_XY(160, 40);
  438. break;
  439. }
  440. g_TextLib.bDialogPosition = bDialogLocation;
  441. }
  442. static VOID
  443. PAL_DialogWaitForKey(
  444. VOID
  445. )
  446. /*++
  447. Purpose:
  448. Wait for player to press a key after showing a dialog.
  449. Parameters:
  450. None.
  451. Return value:
  452. None.
  453. --*/
  454. {
  455. PAL_LARGE SDL_Color palette[256];
  456. SDL_Color *pCurrentPalette, t;
  457. int i;
  458. //
  459. // get the current palette
  460. //
  461. pCurrentPalette = PAL_GetPalette(gpGlobals->wNumPalette, gpGlobals->fNightPalette);
  462. memcpy(palette, pCurrentPalette, sizeof(palette));
  463. if (g_TextLib.bDialogPosition != kDialogCenterWindow &&
  464. g_TextLib.bDialogPosition != kDialogCenter)
  465. {
  466. //
  467. // show the icon
  468. //
  469. LPCBITMAPRLE p = PAL_SpriteGetFrame(g_TextLib.bufDialogIcons, g_TextLib.bIcon);
  470. if (p != NULL)
  471. {
  472. SDL_Rect rect;
  473. rect.x = PAL_X(g_TextLib.posIcon);
  474. rect.y = PAL_Y(g_TextLib.posIcon);
  475. rect.w = 16;
  476. rect.h = 16;
  477. PAL_RLEBlitToSurface(p, gpScreen, g_TextLib.posIcon);
  478. VIDEO_UpdateScreen(&rect);
  479. }
  480. }
  481. PAL_ClearKeyState();
  482. while (TRUE)
  483. {
  484. UTIL_Delay(100);
  485. if (g_TextLib.bDialogPosition != kDialogCenterWindow &&
  486. g_TextLib.bDialogPosition != kDialogCenter)
  487. {
  488. //
  489. // palette shift
  490. //
  491. t = palette[0xF9];
  492. for (i = 0xF9; i < 0xFE; i++)
  493. {
  494. palette[i] = palette[i + 1];
  495. }
  496. palette[0xFE] = t;
  497. VIDEO_SetPalette(palette);
  498. }
  499. if (g_InputState.dwKeyPress != 0)
  500. {
  501. break;
  502. }
  503. }
  504. if (g_TextLib.bDialogPosition != kDialogCenterWindow &&
  505. g_TextLib.bDialogPosition != kDialogCenter)
  506. {
  507. PAL_SetPalette(gpGlobals->wNumPalette, gpGlobals->fNightPalette);
  508. }
  509. PAL_ClearKeyState();
  510. g_TextLib.fUserSkip = FALSE;
  511. }
  512. VOID
  513. PAL_ShowDialogText(
  514. LPCWSTR lpszText
  515. )
  516. /*++
  517. Purpose:
  518. Show one line of the dialog text.
  519. Parameters:
  520. [IN] lpszText - the text to be shown.
  521. Return value:
  522. None.
  523. --*/
  524. {
  525. SDL_Rect rect;
  526. int x, y;
  527. PAL_ClearKeyState();
  528. g_TextLib.bIcon = 0;
  529. if (gpGlobals->fInBattle && !g_fUpdatedInBattle)
  530. {
  531. //
  532. // Update the screen in battle, or the graphics may seem messed up
  533. //
  534. VIDEO_UpdateScreen(NULL);
  535. g_fUpdatedInBattle = TRUE;
  536. }
  537. if (g_TextLib.nCurrentDialogLine > 3)
  538. {
  539. //
  540. // The rest dialogs should be shown in the next page.
  541. //
  542. PAL_DialogWaitForKey();
  543. g_TextLib.nCurrentDialogLine = 0;
  544. VIDEO_RestoreScreen();
  545. VIDEO_UpdateScreen(NULL);
  546. }
  547. x = PAL_X(g_TextLib.posDialogText);
  548. y = PAL_Y(g_TextLib.posDialogText) + g_TextLib.nCurrentDialogLine * 18;
  549. if (g_TextLib.bDialogPosition == kDialogCenterWindow)
  550. {
  551. //
  552. // The text should be shown in a small window at the center of the screen
  553. //
  554. #ifndef PAL_CLASSIC
  555. if (gpGlobals->fInBattle && g_Battle.BattleResult == kBattleResultOnGoing)
  556. {
  557. PAL_BattleUIShowText(lpszText, 1400);
  558. }
  559. else
  560. #endif
  561. {
  562. PAL_POS pos;
  563. LPBOX lpBox;
  564. int i, w = wcslen(lpszText), len = 0;
  565. for (i = 0; i < w; i++)
  566. len += PAL_CharWidth(lpszText[i]) >> 3;
  567. //
  568. // Create the window box
  569. //
  570. pos = PAL_XY(PAL_X(g_TextLib.posDialogText) - len * 4, PAL_Y(g_TextLib.posDialogText));
  571. lpBox = PAL_CreateSingleLineBox(pos, (len + 1) / 2, TRUE);
  572. rect.x = PAL_X(pos);
  573. rect.y = PAL_Y(pos);
  574. rect.w = 320 - rect.x * 2 + 32;
  575. rect.h = 64;
  576. //
  577. // Show the text on the screen
  578. //
  579. pos = PAL_XY(PAL_X(pos) + 8 + ((len & 1) << 2), PAL_Y(pos) + 10);
  580. PAL_DrawText(lpszText, pos, 0, FALSE, FALSE);
  581. VIDEO_UpdateScreen(&rect);
  582. PAL_DialogWaitForKey();
  583. //
  584. // Delete the box
  585. //
  586. PAL_DeleteBox(lpBox);
  587. VIDEO_UpdateScreen(&rect);
  588. PAL_EndDialog();
  589. }
  590. }
  591. else
  592. {
  593. int len = wcslen(lpszText);
  594. if (g_TextLib.nCurrentDialogLine == 0 &&
  595. g_TextLib.bDialogPosition != kDialogCenter &&
  596. (lpszText[len - 1] == 0xff1a ||
  597. lpszText[len - 1] == 0x2236 || // Special case for Pal WIN95 Simplified Chinese version
  598. lpszText[len - 1] == ':')
  599. )
  600. {
  601. //
  602. // name of character
  603. //
  604. PAL_DrawText(lpszText, g_TextLib.posDialogTitle, FONT_COLOR_CYAN_ALT, TRUE, TRUE);
  605. }
  606. else
  607. {
  608. //
  609. // normal texts
  610. //
  611. WCHAR text[2];
  612. if (!g_TextLib.fPlayingRNG && g_TextLib.nCurrentDialogLine == 0)
  613. {
  614. //
  615. // Save the screen before we show the first line of dialog
  616. //
  617. VIDEO_BackupScreen();
  618. }
  619. while (lpszText != NULL && *lpszText != '\0')
  620. {
  621. switch (*lpszText)
  622. {
  623. case '-':
  624. //
  625. // Set the font color to Cyan
  626. //
  627. if (g_TextLib.bCurrentFontColor == FONT_COLOR_CYAN)
  628. {
  629. g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
  630. }
  631. else
  632. {
  633. g_TextLib.bCurrentFontColor = FONT_COLOR_CYAN;
  634. }
  635. lpszText++;
  636. break;
  637. case '\'':
  638. // !PAL_WIN95
  639. //
  640. // Set the font color to Red
  641. //
  642. if (g_TextLib.bCurrentFontColor == FONT_COLOR_RED)
  643. {
  644. g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
  645. }
  646. else
  647. {
  648. g_TextLib.bCurrentFontColor = FONT_COLOR_RED;
  649. }
  650. lpszText++;
  651. break;
  652. case '\"':
  653. //
  654. // Set the font color to Yellow
  655. //
  656. if (g_TextLib.bCurrentFontColor == FONT_COLOR_YELLOW)
  657. {
  658. g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
  659. }
  660. else
  661. {
  662. g_TextLib.bCurrentFontColor = FONT_COLOR_YELLOW;
  663. }
  664. lpszText++;
  665. break;
  666. case '$':
  667. //
  668. // Set the delay time of text-displaying
  669. //
  670. g_TextLib.iDelayTime = wcstol(lpszText + 1, NULL, 10) * 10 / 7;
  671. lpszText += 3;
  672. break;
  673. case '~':
  674. //
  675. // Delay for a period and quit
  676. //
  677. UTIL_Delay(wcstol(lpszText + 1, NULL, 10) * 80 / 7);
  678. g_TextLib.nCurrentDialogLine = 0;
  679. g_TextLib.fUserSkip = FALSE;
  680. return; // don't go further
  681. case ')':
  682. //
  683. // Set the waiting icon
  684. //
  685. g_TextLib.bIcon = 1;
  686. lpszText++;
  687. break;
  688. case '(':
  689. //
  690. // Set the waiting icon
  691. //
  692. g_TextLib.bIcon = 2;
  693. lpszText++;
  694. break;
  695. case '\\':
  696. lpszText++;
  697. default:
  698. text[0] = *lpszText++;
  699. text[1] = 0;
  700. PAL_DrawText(text, PAL_XY(x, y), g_TextLib.bCurrentFontColor, TRUE, TRUE);
  701. x += PAL_CharWidth(text[0]);
  702. if (!g_TextLib.fUserSkip)
  703. {
  704. PAL_ClearKeyState();
  705. UTIL_Delay(g_TextLib.iDelayTime * 8);
  706. if (g_InputState.dwKeyPress & (kKeySearch | kKeyMenu))
  707. {
  708. //
  709. // User pressed a key to skip the dialog
  710. //
  711. g_TextLib.fUserSkip = TRUE;
  712. }
  713. }
  714. }
  715. }
  716. g_TextLib.posIcon = PAL_XY(x, y);
  717. g_TextLib.nCurrentDialogLine++;
  718. }
  719. }
  720. }
  721. VOID
  722. PAL_ClearDialog(
  723. BOOL fWaitForKey
  724. )
  725. /*++
  726. Purpose:
  727. Clear the state of the dialog.
  728. Parameters:
  729. [IN] fWaitForKey - whether wait for any key or not.
  730. Return value:
  731. None.
  732. --*/
  733. {
  734. if (g_TextLib.nCurrentDialogLine > 0 && fWaitForKey)
  735. {
  736. PAL_DialogWaitForKey();
  737. }
  738. g_TextLib.nCurrentDialogLine = 0;
  739. if (g_TextLib.bDialogPosition == kDialogCenter)
  740. {
  741. g_TextLib.posDialogTitle = PAL_XY(12, 8);
  742. g_TextLib.posDialogText = PAL_XY(44, 26);
  743. g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
  744. g_TextLib.bDialogPosition = kDialogUpper;
  745. }
  746. }
  747. VOID
  748. PAL_EndDialog(
  749. VOID
  750. )
  751. /*++
  752. Purpose:
  753. Ends a dialog.
  754. Parameters:
  755. None.
  756. Return value:
  757. None.
  758. --*/
  759. {
  760. PAL_ClearDialog(TRUE);
  761. //
  762. // Set some default parameters, as there are some parts of script
  763. // which doesn't have a "start dialog" instruction before showing the dialog.
  764. //
  765. g_TextLib.posDialogTitle = PAL_XY(12, 8);
  766. g_TextLib.posDialogText = PAL_XY(44, 26);
  767. g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
  768. g_TextLib.bDialogPosition = kDialogUpper;
  769. g_TextLib.fUserSkip = FALSE;
  770. g_TextLib.fPlayingRNG = FALSE;
  771. }
  772. BOOL
  773. PAL_IsInDialog(
  774. VOID
  775. )
  776. /*++
  777. Purpose:
  778. Check if there are dialog texts on the screen.
  779. Parameters:
  780. None.
  781. Return value:
  782. TRUE if there are dialog texts on the screen, FALSE if not.
  783. --*/
  784. {
  785. return (g_TextLib.nCurrentDialogLine != 0);
  786. }
  787. BOOL
  788. PAL_DialogIsPlayingRNG(
  789. VOID
  790. )
  791. /*++
  792. Purpose:
  793. Check if the script used the RNG playing parameter when displaying texts.
  794. Parameters:
  795. None.
  796. Return value:
  797. TRUE if the script used the RNG playing parameter, FALSE if not.
  798. --*/
  799. {
  800. return g_TextLib.fPlayingRNG;
  801. }
  802. INT
  803. PAL_MultiByteToWideCharCP(
  804. CODEPAGE cp,
  805. LPCSTR mbs,
  806. int mbslength,
  807. LPWSTR wcs,
  808. int wcslength
  809. )
  810. /*++
  811. Purpose:
  812. Convert multi-byte string into the corresponding unicode string.
  813. Parameters:
  814. [IN] cp - Code page for conversion.
  815. [IN] mbs - Pointer to the multi-byte string.
  816. [IN] mbslength - Length of the multi-byte string, or -1 for auto-detect.
  817. [IN] wcs - Pointer to the wide string buffer.
  818. [IN] wcslength - Length of the wide string buffer.
  819. Return value:
  820. The length of converted wide string. If mbslength is set to -1, the returned
  821. value includes the terminal null-char; otherwise, the null-char is not included.
  822. If wcslength is set to 0, wcs can be set to NULL and the return value is the
  823. required length of the wide string buffer.
  824. --*/
  825. {
  826. int i = 0, j = 0, state = 0, wlen = 0, null = 0;
  827. if (mbslength == -1)
  828. {
  829. mbslength = strlen(mbs);
  830. null = 1;
  831. }
  832. if (!wcs)
  833. {
  834. switch (cp)
  835. {
  836. case CP_SHIFTJIS:
  837. for (i = 0; i < mbslength && mbs[i]; i++)
  838. {
  839. if (state == 0)
  840. {
  841. if ((BYTE)mbs[i] <= 0x80 || (BYTE)mbs[i] >= 0xfd || ((BYTE)mbs[i] >= 0xa0 && (BYTE)mbs[i] <= 0xdf))
  842. wlen++;
  843. else
  844. state = 1;
  845. }
  846. else
  847. {
  848. wlen++;
  849. state = 0;
  850. }
  851. }
  852. return wlen + null + state;
  853. case CP_GBK:
  854. case CP_BIG5:
  855. for (i = 0; i < mbslength && mbs[i]; i++)
  856. {
  857. if (state == 0)
  858. {
  859. if ((BYTE)mbs[i] <= 0x80 || (BYTE)mbs[i] == 0xff)
  860. wlen++;
  861. else
  862. state = 1;
  863. }
  864. else
  865. {
  866. wlen++;
  867. state = 0;
  868. }
  869. }
  870. return wlen + null + state;
  871. default:
  872. return -1;
  873. }
  874. }
  875. else
  876. {
  877. WCHAR invalid_char;
  878. switch (cp)
  879. {
  880. case CP_SHIFTJIS:
  881. invalid_char = 0x30fb;
  882. for (i = 0; i < mbslength && wlen < wcslength && mbs[i]; i++)
  883. {
  884. if (state == 0)
  885. {
  886. if ((BYTE)mbs[i] <= 0x80)
  887. wcs[wlen++] = mbs[i];
  888. else if ((BYTE)mbs[i] >= 0xa0 && (BYTE)mbs[i] <= 0xdf)
  889. wcs[wlen++] = cptbl_jis_half[(BYTE)mbs[i] - 0xa0];
  890. else if ((BYTE)mbs[i] == 0xfd)
  891. wcs[wlen++] = 0xf8f1;
  892. else if ((BYTE)mbs[i] == 0xfe)
  893. wcs[wlen++] = 0xf8f2;
  894. else if ((BYTE)mbs[i] == 0xff)
  895. wcs[wlen++] = 0xf8f3;
  896. else
  897. state = 1;
  898. }
  899. else
  900. {
  901. if ((BYTE)mbs[i] < 0x40)
  902. wcs[wlen++] = 0x30fb;
  903. else if ((BYTE)mbs[i - 1] < 0xa0)
  904. wcs[wlen++] = cptbl_jis[(BYTE)mbs[i - 1] - 0x81][(BYTE)mbs[i] - 0x40];
  905. else
  906. wcs[wlen++] = cptbl_jis[(BYTE)mbs[i - 1] - 0xc1][(BYTE)mbs[i] - 0x40];
  907. state = 0;
  908. }
  909. }
  910. break;
  911. case CP_GBK:
  912. invalid_char = 0x3f;
  913. for (i = 0; i < mbslength && wlen < wcslength && mbs[i]; i++)
  914. {
  915. if (state == 0)
  916. {
  917. if ((BYTE)mbs[i] < 0x80)
  918. wcs[wlen++] = mbs[i];
  919. else if ((BYTE)mbs[i] == 0x80)
  920. wcs[wlen++] = 0x20ac;
  921. else if ((BYTE)mbs[i] == 0xff)
  922. wcs[wlen++] = 0xf8f5;
  923. else
  924. state = 1;
  925. }
  926. else
  927. {
  928. if ((BYTE)mbs[i] < 0x40)
  929. wcs[wlen++] = invalid_char;
  930. else
  931. wcs[wlen++] = cptbl_gbk[(BYTE)mbs[i - 1] - 0x81][(BYTE)mbs[i] - 0x40];
  932. state = 0;
  933. }
  934. }
  935. break;
  936. case CP_BIG5:
  937. invalid_char = 0x3f;
  938. for (i = 0; i < mbslength && wlen < wcslength && mbs[i]; i++)
  939. {
  940. if (state == 0)
  941. {
  942. if ((BYTE)mbs[i] <= 0x80)
  943. wcs[wlen++] = mbs[i];
  944. else if ((BYTE)mbs[i] == 0xff)
  945. wcs[wlen++] = 0xf8f8;
  946. else
  947. state = 1;
  948. }
  949. else
  950. {
  951. if ((BYTE)mbs[i] < 0x40 || ((BYTE)mbs[i] >= 0x7f && (BYTE)mbs[i] <= 0xa0))
  952. wcs[wlen++] = invalid_char;
  953. else if ((BYTE)mbs[i] <= 0x7e)
  954. wcs[wlen++] = cptbl_big5[(BYTE)mbs[i - 1] - 0x81][(BYTE)mbs[i] - 0x40];
  955. else
  956. wcs[wlen++] = cptbl_big5[(BYTE)mbs[i - 1] - 0x81][(BYTE)mbs[i] - 0x60];
  957. state = 0;
  958. }
  959. }
  960. break;
  961. default:
  962. return -1;
  963. }
  964. if (state == 1 && wlen < wcslength)
  965. {
  966. wcs[wlen++] = invalid_char;
  967. }
  968. if (null)
  969. {
  970. if (wlen < wcslength)
  971. wcs[wlen++] = 0;
  972. else
  973. wcs[wlen - 1] = 0;
  974. }
  975. return wlen;
  976. }
  977. }
  978. INT
  979. PAL_MultiByteToWideChar(
  980. LPCSTR mbs,
  981. int mbslength,
  982. LPWSTR wcs,
  983. int wcslength
  984. )
  985. /*++
  986. Purpose:
  987. Convert multi-byte string into the corresponding unicode string.
  988. Parameters:
  989. [IN] mbs - Pointer to the multi-byte string.
  990. [IN] mbslength - Length of the multi-byte string, or -1 for auto-detect.
  991. [IN] wcs - Pointer to the wide string buffer.
  992. [IN] wcslength - Length of the wide string buffer.
  993. Return value:
  994. The length of converted wide string. If mbslength is set to -1, the returned
  995. value includes the terminal null-char; otherwise, the null-char is not included.
  996. If wcslength is set to 0, wcs can be set to NULL and the return value is the
  997. required length of the wide string buffer.
  998. --*/
  999. {
  1000. return PAL_MultiByteToWideCharCP(gpGlobals->iCodePage, mbs, mbslength, wcs, wcslength);
  1001. }
  1002. WCHAR
  1003. PAL_GetInvalidChar(
  1004. CODEPAGE iCodePage
  1005. )
  1006. {
  1007. switch(iCodePage)
  1008. {
  1009. case CP_BIG5: return 0x3f;
  1010. case CP_GBK: return 0x3f;
  1011. case CP_SHIFTJIS: return 0x30fb;
  1012. default: return 0;
  1013. }
  1014. }