GameText.cpp 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. //----------------------------------------------------------------------------
  24. //
  25. // Westwood Studios Pacific.
  26. //
  27. // Confidential Information
  28. // Copyright(C) 2001 - All Rights Reserved
  29. //
  30. //----------------------------------------------------------------------------
  31. //
  32. // Project: RTS3
  33. //
  34. // File name: GameText.cpp
  35. //
  36. // Created: 11/07/01
  37. //
  38. //----------------------------------------------------------------------------
  39. //----------------------------------------------------------------------------
  40. // Includes
  41. //----------------------------------------------------------------------------
  42. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  43. #include "GameClient/GameText.h"
  44. #include "Common/Language.h"
  45. #include "Common/Registry.h"
  46. #include "GameClient/LanguageFilter.h"
  47. #include "Common/Debug.h"
  48. #include "Common/UnicodeString.h"
  49. #include "Common/AsciiString.h"
  50. #include "Common/GlobalData.h"
  51. #include "Common/File.h"
  52. #include "Common/FileSystem.h"
  53. #ifdef _INTERNAL
  54. // for occasional debugging...
  55. //#pragma optimize("", off)
  56. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  57. #endif
  58. //----------------------------------------------------------------------------
  59. // Externals
  60. //----------------------------------------------------------------------------
  61. #if defined(_DEBUG) || defined(_INTERNAL)
  62. Bool g_useStringFile = TRUE;
  63. #endif
  64. //----------------------------------------------------------------------------
  65. // Defines
  66. //----------------------------------------------------------------------------
  67. #define CSF_ID ( ('C'<<24) | ('S'<<16) | ('F'<<8) | (' ') )
  68. #define CSF_LABEL ( ('L'<<24) | ('B'<<16) | ('L'<<8) | (' ') )
  69. #define CSF_STRING ( ('S'<<24) | ('T'<<16) | ('R'<<8) | (' ') )
  70. #define CSF_STRINGWITHWAVE ( ('S'<<24) | ('T'<<16) | ('R'<<8) | ('W') )
  71. #define CSF_VERSION 3
  72. #define STRING_FILE 0
  73. #define CSF_FILE 1
  74. #define MAX_UITEXT_LENGTH (10*1024)
  75. //----------------------------------------------------------------------------
  76. // Private Types
  77. //----------------------------------------------------------------------------
  78. //===============================
  79. // StringInfo
  80. //===============================
  81. struct StringInfo
  82. {
  83. AsciiString label;
  84. UnicodeString text;
  85. AsciiString speech;
  86. };
  87. struct StringLookUp
  88. {
  89. AsciiString *label;
  90. StringInfo *info;
  91. };
  92. //===============================
  93. // CSFHeader
  94. //===============================
  95. struct CSFHeader
  96. {
  97. Int id;
  98. Int version;
  99. Int num_labels;
  100. Int num_strings;
  101. Int skip;
  102. Int langid;
  103. };
  104. //===============================
  105. // struct NoString
  106. //===============================
  107. struct NoString
  108. {
  109. struct NoString *next;
  110. UnicodeString text;
  111. };
  112. //===============================
  113. // GameTextManager
  114. //===============================
  115. class GameTextManager : public GameTextInterface
  116. {
  117. public:
  118. GameTextManager();
  119. virtual ~GameTextManager();
  120. virtual void init( void ); ///< Initlaizes the text system
  121. virtual void deinit( void ); ///< De-initlaizes the text system
  122. virtual void update( void ) {}; ///< update text manager
  123. virtual void reset( void ); ///< Resets the text system
  124. virtual UnicodeString fetch( const Char *label, Bool *exists = NULL ); ///< Returns the associated labeled unicode text
  125. virtual UnicodeString fetch( AsciiString label, Bool *exists = NULL ); ///< Returns the associated labeled unicode text
  126. virtual AsciiStringVec& getStringsWithLabelPrefix(AsciiString label);
  127. virtual void initMapStringFile( const AsciiString& filename );
  128. protected:
  129. Int m_textCount;
  130. Int m_maxLabelLen;
  131. Char m_buffer[MAX_UITEXT_LENGTH];
  132. Char m_buffer2[MAX_UITEXT_LENGTH];
  133. Char m_buffer3[MAX_UITEXT_LENGTH];
  134. WideChar m_tbuffer[MAX_UITEXT_LENGTH*2];
  135. StringInfo *m_stringInfo;
  136. StringLookUp *m_stringLUT;
  137. Bool m_initialized;
  138. #if defined(_DEBUG) || defined(_INTERNAL)
  139. Bool m_jabberWockie;
  140. Bool m_munkee;
  141. #endif
  142. NoString *m_noStringList;
  143. Int m_useStringFile;
  144. LanguageID m_language;
  145. UnicodeString m_failed;
  146. StringInfo *m_mapStringInfo;
  147. StringLookUp *m_mapStringLUT;
  148. Int m_mapTextCount;
  149. /// m_asciiStringVec will be altered every time that getStringsWithLabelPrefix is called,
  150. /// so don't simply store a pointer to it.
  151. AsciiStringVec m_asciiStringVec;
  152. void stripSpaces ( WideChar *string );
  153. void removeLeadingAndTrailing ( Char *m_buffer );
  154. void readToEndOfQuote( File *file, Char *in, Char *out, Char *wavefile, Int maxBufLen );
  155. void reverseWord ( Char *file, Char *lp );
  156. void translateCopy( WideChar *outbuf, Char *inbuf );
  157. Bool getStringCount( const Char *filename, Int& textCount );
  158. Bool getCSFInfo ( const Char *filename );
  159. Bool parseCSF( const Char *filename );
  160. Bool parseStringFile( const char *filename );
  161. Bool parseMapStringFile( const char *filename );
  162. Bool readLine( char *buffer, Int max, File *file );
  163. Char readChar( File *file );
  164. };
  165. static int _cdecl compareLUT ( const void *, const void*);
  166. //----------------------------------------------------------------------------
  167. // Private Data
  168. //----------------------------------------------------------------------------
  169. //----------------------------------------------------------------------------
  170. // Public Data
  171. //----------------------------------------------------------------------------
  172. GameTextInterface *TheGameText = NULL;
  173. //----------------------------------------------------------------------------
  174. // Private Prototypes
  175. //----------------------------------------------------------------------------
  176. //----------------------------------------------------------------------------
  177. // Private Functions
  178. //----------------------------------------------------------------------------
  179. //----------------------------------------------------------------------------
  180. // Public Functions
  181. //----------------------------------------------------------------------------
  182. //============================================================================
  183. // CreateGameTextInterface
  184. //============================================================================
  185. GameTextInterface* CreateGameTextInterface( void )
  186. {
  187. return NEW GameTextManager;
  188. }
  189. //============================================================================
  190. // GameTextManager::GameTextManager
  191. //============================================================================
  192. GameTextManager::GameTextManager()
  193. : m_textCount(0),
  194. m_maxLabelLen(0),
  195. m_stringInfo(NULL),
  196. m_stringLUT(NULL),
  197. m_initialized(FALSE),
  198. m_noStringList(NULL),
  199. #if defined(_DEBUG) || defined(_INTERNAL)
  200. m_jabberWockie(FALSE),
  201. m_munkee(FALSE),
  202. m_useStringFile(g_useStringFile),
  203. #else
  204. m_useStringFile(TRUE),
  205. #endif
  206. m_mapStringInfo(NULL),
  207. m_mapStringLUT(NULL),
  208. m_failed(L"***FATAL*** String Manager failed to initilaize properly")
  209. {
  210. // Added By Sadullah Nader
  211. // Initializations missing and needed
  212. for(Int i=0; i < MAX_UITEXT_LENGTH; i++)
  213. {
  214. m_buffer[i] = 0;
  215. m_buffer2[i] = 0;
  216. m_buffer3[i] = 0;
  217. }
  218. //
  219. }
  220. //============================================================================
  221. // GameTextManager::~GameTextManager
  222. //============================================================================
  223. GameTextManager::~GameTextManager()
  224. {
  225. deinit();
  226. }
  227. //============================================================================
  228. // GameTextManager::init
  229. //============================================================================
  230. extern const Char *g_strFile;
  231. extern const Char *g_csfFile;
  232. void GameTextManager::init( void )
  233. {
  234. AsciiString csfFile;
  235. csfFile.format(g_csfFile, GetRegistryLanguage().str());
  236. Int format;
  237. if ( m_initialized )
  238. {
  239. return;
  240. }
  241. m_initialized = TRUE;
  242. m_maxLabelLen = 0;
  243. #if defined(_DEBUG) || defined(_INTERNAL)
  244. if(TheGlobalData)
  245. {
  246. m_jabberWockie = TheGlobalData->m_jabberOn;
  247. m_munkee = TheGlobalData->m_munkeeOn;
  248. }
  249. #endif
  250. if ( m_useStringFile && getStringCount( g_strFile, m_textCount ) )
  251. {
  252. format = STRING_FILE;
  253. }
  254. else if ( getCSFInfo ( csfFile.str() ) )
  255. {
  256. format = CSF_FILE;
  257. }
  258. else
  259. {
  260. return;
  261. }
  262. if( (m_textCount == 0) )
  263. {
  264. return;
  265. }
  266. //Allocate StringInfo Array
  267. m_stringInfo = NEW StringInfo[m_textCount];
  268. if( m_stringInfo == NULL )
  269. {
  270. deinit();
  271. return;
  272. }
  273. if ( format == STRING_FILE )
  274. {
  275. if( parseStringFile( g_strFile ) == FALSE )
  276. {
  277. deinit();
  278. return;
  279. }
  280. }
  281. else
  282. {
  283. if ( !parseCSF ( csfFile.str() ) )
  284. {
  285. deinit();
  286. return;
  287. }
  288. }
  289. m_stringLUT = NEW StringLookUp[m_textCount];
  290. StringLookUp *lut = m_stringLUT;
  291. StringInfo *info = m_stringInfo;
  292. for ( Int i = 0; i < m_textCount; i++ )
  293. {
  294. lut->info = info;
  295. lut->label = &info->label;
  296. lut++;
  297. info++;
  298. }
  299. qsort( m_stringLUT, m_textCount, sizeof(StringLookUp), compareLUT );
  300. UnicodeString ourName = fetch("GUI:Command&ConquerGenerals");
  301. AsciiString ourNameA;
  302. ourNameA.translate(ourName); //get ASCII version for Win 9x
  303. extern HWND ApplicationHWnd; ///< our application window handle
  304. if (ApplicationHWnd) {
  305. //Set it twice because Win 9x does not support SetWindowTextW.
  306. ::SetWindowText(ApplicationHWnd, ourNameA.str());
  307. ::SetWindowTextW(ApplicationHWnd, ourName.str());
  308. }
  309. }
  310. //============================================================================
  311. // GameTextManager::deinit
  312. //============================================================================
  313. void GameTextManager::deinit( void )
  314. {
  315. if( m_stringInfo != NULL )
  316. {
  317. delete [] m_stringInfo;
  318. m_stringInfo = NULL;
  319. }
  320. if( m_stringLUT != NULL )
  321. {
  322. delete [] m_stringLUT;
  323. m_stringLUT = NULL;
  324. }
  325. m_textCount = 0;
  326. NoString *noString = m_noStringList;
  327. DEBUG_LOG(("\n*** Missing strings ***\n"));
  328. while ( noString )
  329. {
  330. DEBUG_LOG(("*** %ls ***\n", noString->text.str()));
  331. NoString *next = noString->next;
  332. delete noString;
  333. noString = next;
  334. }
  335. DEBUG_LOG(("*** End missing strings ***\n\n"));
  336. m_noStringList = NULL;
  337. m_initialized = FALSE;
  338. }
  339. //============================================================================
  340. // GameTextManager::reset
  341. //============================================================================
  342. void GameTextManager::reset( void )
  343. {
  344. if( m_mapStringInfo != NULL )
  345. {
  346. delete [] m_mapStringInfo;
  347. m_mapStringInfo = NULL;
  348. }
  349. if( m_mapStringLUT != NULL )
  350. {
  351. delete [] m_mapStringLUT;
  352. m_mapStringLUT = NULL;
  353. }
  354. }
  355. //============================================================================
  356. // GameTextManager::stripSpaces
  357. //============================================================================
  358. void GameTextManager::stripSpaces ( WideChar *string )
  359. {
  360. WideChar *str, *ptr;
  361. WideChar ch, last = 0;
  362. Int skipall = TRUE;
  363. str = ptr = string;
  364. while ( (ch = *ptr++) != 0 )
  365. {
  366. if ( ch == ' ' )
  367. {
  368. if ( last == ' ' || skipall )
  369. {
  370. continue;
  371. }
  372. }
  373. if ( ch == '\n' || ch == '\t' )
  374. {
  375. // remove last space
  376. if ( last == ' ' )
  377. {
  378. str--;
  379. }
  380. skipall = TRUE; // skip all spaces
  381. last = *str++ = ch;
  382. continue;
  383. }
  384. last = *str++ = ch;
  385. skipall = FALSE;
  386. }
  387. if ( last == ' ' )
  388. {
  389. str--;
  390. }
  391. *str = 0;
  392. }
  393. //============================================================================
  394. // GameTextManager::removeLeadingAndTrailing
  395. //============================================================================
  396. void GameTextManager::removeLeadingAndTrailing ( Char *buffer )
  397. {
  398. Char *first, *ptr;
  399. Char ch;
  400. ptr = first = buffer;
  401. while ( (ch = *first) != 0 && iswspace ( ch ))
  402. {
  403. first++;
  404. }
  405. while ( (*ptr++ = *first++) != 0 );
  406. ptr -= 2;;
  407. while ( (ptr > buffer) && (ch = *ptr) != 0 && iswspace ( ch ) )
  408. {
  409. ptr--;
  410. }
  411. ptr++;
  412. *ptr = 0;
  413. }
  414. //============================================================================
  415. // GameTextManager::readToEndOfQuote
  416. //============================================================================
  417. void GameTextManager::readToEndOfQuote( File *file, Char *in, Char *out, Char *wavefile, Int maxBufLen )
  418. {
  419. Int slash = FALSE;
  420. Int state = 0;
  421. Int line_start = FALSE;
  422. Char ch;
  423. Int ccount = 0;
  424. Int len = 0;
  425. Int done = FALSE;
  426. while ( maxBufLen )
  427. {
  428. // get next Char
  429. if ( in )
  430. {
  431. if ( (ch = *in++) == 0 )
  432. {
  433. in = NULL; // have exhausted the input m_buffer
  434. ch = readChar ( file );
  435. }
  436. }
  437. else
  438. {
  439. ch = readChar ( file );
  440. }
  441. if ( ch == EOF )
  442. {
  443. return ;
  444. }
  445. if ( ch == '\n' )
  446. {
  447. line_start = TRUE;
  448. slash = FALSE;
  449. ccount = 0;
  450. ch = ' ';
  451. }
  452. else if ( ch == '\\' && !slash)
  453. {
  454. slash = TRUE;
  455. }
  456. else if ( ch == '\\' && slash)
  457. {
  458. slash = FALSE;
  459. }
  460. else if ( ch == '"' && !slash )
  461. {
  462. break; // done
  463. }
  464. else
  465. {
  466. slash = FALSE;
  467. }
  468. if ( iswspace ( ch ))
  469. {
  470. ch = ' ';
  471. }
  472. *out++ = ch;
  473. maxBufLen--;
  474. }
  475. *out = 0;
  476. while ( !done )
  477. {
  478. // get next Char
  479. if ( in )
  480. {
  481. if ( (ch = *in++) == 0 )
  482. {
  483. in = NULL; // have exhausted the input m_buffer
  484. ch = readChar ( file );
  485. }
  486. }
  487. else
  488. {
  489. ch = readChar ( file );
  490. }
  491. if ( ch == '\n' || ch == EOF )
  492. {
  493. break;
  494. }
  495. switch ( state )
  496. {
  497. case 0:
  498. if ( iswspace ( ch ) || ch == '=' )
  499. {
  500. break;
  501. }
  502. state = 1;
  503. case 1:
  504. if ( ( ch >= 'a' && ch <= 'z') || ( ch >= 'A' && ch <='Z') || (ch >= '0' && ch <= '9') || ch == '_' )
  505. {
  506. *wavefile++ = ch;
  507. len++;
  508. break;
  509. }
  510. state = 2;
  511. case 2:
  512. break;
  513. }
  514. }
  515. *wavefile = 0;
  516. if ( len )
  517. {
  518. if ( ( ch = *(wavefile-1)) >= '0' && ch <= '9' )
  519. {
  520. *wavefile++ = 'e';
  521. *wavefile = 0;
  522. }
  523. }
  524. }
  525. //============================================================================
  526. // GameTextManager::reverseWord
  527. //============================================================================
  528. void GameTextManager::reverseWord ( Char *file, Char *lp )
  529. {
  530. Int first = TRUE;
  531. Char f, l;
  532. Int ok = TRUE ;
  533. while ( ok )
  534. {
  535. if ( file >= lp )
  536. {
  537. return;
  538. }
  539. f = *file;
  540. l = *lp;
  541. if ( first )
  542. {
  543. if ( f >= 'A' && f <= 'Z' )
  544. {
  545. if ( l >= 'a' && l <= 'z' )
  546. {
  547. f = (f - 'A') + 'a';
  548. l = (l - 'a') + 'A';
  549. }
  550. }
  551. first = FALSE;
  552. }
  553. *lp-- = f;
  554. *file++ = l;
  555. }
  556. }
  557. //============================================================================
  558. // GameTextManager::translateCopy
  559. //============================================================================
  560. void GameTextManager::translateCopy( WideChar *outbuf, Char *inbuf )
  561. {
  562. Int slash = FALSE;
  563. #if defined(_DEBUG) || defined(_INTERNAL)
  564. if ( m_jabberWockie )
  565. {
  566. static Char buffer[MAX_UITEXT_LENGTH*2];
  567. Char *firstLetter = NULL, *lastLetter;
  568. Char *b = buffer;
  569. Int formatWord = FALSE;
  570. Char ch;
  571. while ( (ch = *inbuf++) != 0 )
  572. {
  573. if ( ! (( ch >= 'a' && ch <= 'z') || ( ch >= 'A' && ch <= 'Z' )))
  574. {
  575. if ( firstLetter )
  576. {
  577. if ( !formatWord )
  578. {
  579. lastLetter = b-1;
  580. reverseWord ( firstLetter, lastLetter );
  581. }
  582. firstLetter = NULL;
  583. formatWord = FALSE;
  584. }
  585. *b++ = ch;
  586. if ( ch == '\\' )
  587. {
  588. *b++ = *inbuf++;
  589. }
  590. if ( ch == '%' )
  591. {
  592. while ( (ch = *inbuf++) != 0 && !( (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')))
  593. {
  594. *b++ = ch;
  595. }
  596. *b++ = ch;
  597. }
  598. }
  599. else
  600. {
  601. if ( !firstLetter )
  602. {
  603. firstLetter = b;
  604. }
  605. *b++ = ch;
  606. }
  607. }
  608. if ( firstLetter )
  609. {
  610. lastLetter = b-1;
  611. reverseWord ( firstLetter, lastLetter );
  612. }
  613. *b++ = 0;
  614. inbuf = buffer;
  615. }
  616. else if( m_munkee )
  617. {
  618. wcscpy(outbuf, L"Munkee");
  619. return;
  620. }
  621. #endif
  622. while( *inbuf != '\0' )
  623. {
  624. if( slash == TRUE )
  625. {
  626. slash = FALSE;
  627. switch( *inbuf )
  628. {
  629. // in case end of string is reached
  630. // should never happen!!!
  631. case '\0':
  632. return;
  633. case '\\':
  634. *outbuf++ = '\\';
  635. break;
  636. case '\'':
  637. *outbuf++ = '\'';
  638. break;
  639. case '\"':
  640. *outbuf++ = '\"';
  641. break;
  642. case '\?':
  643. *outbuf++ = '\?';
  644. break;
  645. case 't':
  646. *outbuf++ = '\t';
  647. break;
  648. case 'n':
  649. *outbuf++ = '\n';
  650. break;
  651. default:
  652. *outbuf++ = *inbuf & 0x00FF;
  653. break;
  654. }
  655. }
  656. else if( *inbuf != '\\' )
  657. {
  658. *outbuf++ = *inbuf & 0x00FF;
  659. }
  660. else
  661. slash = TRUE;
  662. inbuf++;
  663. }
  664. *outbuf= 0;
  665. }
  666. //============================================================================
  667. // GameTextManager::getStringCount
  668. //============================================================================
  669. Bool GameTextManager::getStringCount( const char *filename, Int& textCount )
  670. {
  671. Int ok = TRUE;
  672. textCount = 0;
  673. File *file;
  674. file = TheFileSystem->openFile(filename, File::READ | File::TEXT);
  675. DEBUG_LOG(("Looking in %s for string file\n", filename));
  676. if ( file == NULL )
  677. {
  678. return FALSE;
  679. }
  680. while(ok)
  681. {
  682. if( !readLine( m_buffer, sizeof( m_buffer) -1, file ) )
  683. break;
  684. removeLeadingAndTrailing ( m_buffer );
  685. if( m_buffer[0] == '"' )
  686. {
  687. Int len = strlen(m_buffer);
  688. m_buffer[ len ] = '\n';
  689. m_buffer[ len+1] = 0;
  690. readToEndOfQuote( file, &m_buffer[1], m_buffer2, m_buffer3, MAX_UITEXT_LENGTH );
  691. }
  692. else if( !stricmp( m_buffer, "END") )
  693. {
  694. textCount++;
  695. }
  696. }
  697. textCount += 500;
  698. file->close();
  699. file = NULL;
  700. return TRUE;
  701. }
  702. //============================================================================
  703. // GameTextManager::getCSFInfo
  704. //============================================================================
  705. Bool GameTextManager::getCSFInfo ( const Char *filename )
  706. {
  707. CSFHeader header;
  708. Int ok = FALSE;
  709. File *file = TheFileSystem->openFile(filename, File::READ | File::BINARY);
  710. DEBUG_LOG(("Looking in %s for compiled string file\n", filename));
  711. if ( file != NULL )
  712. {
  713. if ( file->read( &header, sizeof ( header )) == sizeof ( header ) )
  714. {
  715. if ( header.id == CSF_ID )
  716. {
  717. m_textCount = header.num_labels;
  718. if ( header.version >= 2 )
  719. {
  720. m_language = (LanguageID) header.langid;
  721. }
  722. else
  723. {
  724. m_language = LANGUAGE_ID_US;
  725. }
  726. ok = TRUE;
  727. }
  728. }
  729. file->close();
  730. file = NULL;
  731. }
  732. return ok;
  733. }
  734. //============================================================================
  735. // GameTextManager::parseCSF
  736. //============================================================================
  737. Bool GameTextManager::parseCSF( const Char *filename )
  738. {
  739. File *file;
  740. Int id;
  741. Int len;
  742. Int listCount = 0;
  743. Bool ok = FALSE;
  744. CSFHeader header;
  745. file = TheFileSystem->openFile(filename, File::READ | File::BINARY);
  746. if ( file == NULL )
  747. {
  748. return FALSE;
  749. }
  750. if ( file->read ( &header, sizeof ( CSFHeader)) != sizeof ( CSFHeader) )
  751. {
  752. return FALSE;
  753. }
  754. while( file->read ( &id, sizeof (id)) == sizeof ( id) )
  755. {
  756. Int num;
  757. Int num_strings;
  758. if ( id != CSF_LABEL )
  759. {
  760. goto quit;
  761. }
  762. file->read ( &num_strings, sizeof ( Int ));
  763. file->read ( &len, sizeof ( Int ) );
  764. if ( len )
  765. {
  766. file->read ( m_buffer, len );
  767. }
  768. m_buffer[len] = 0;
  769. m_stringInfo[listCount].label = m_buffer;
  770. if ( len > m_maxLabelLen )
  771. {
  772. m_maxLabelLen = len;
  773. }
  774. num = 0;
  775. while ( num < num_strings )
  776. {
  777. file->read ( &id, sizeof ( Int ) );
  778. if ( id != CSF_STRING && id != CSF_STRINGWITHWAVE )
  779. {
  780. goto quit;
  781. }
  782. file->read ( &len, sizeof ( Int ) );
  783. if ( len )
  784. {
  785. file->read ( m_tbuffer, len*sizeof(WideChar) );
  786. }
  787. if ( num == 0 )
  788. {
  789. // only use the first string found
  790. m_tbuffer[len] = 0;
  791. {
  792. WideChar *ptr;
  793. ptr = m_tbuffer;
  794. while ( *ptr )
  795. {
  796. *ptr = ~*ptr;
  797. ptr++;
  798. }
  799. }
  800. stripSpaces ( m_tbuffer );
  801. m_stringInfo[listCount].text = m_tbuffer;
  802. }
  803. if ( id == CSF_STRINGWITHWAVE )
  804. {
  805. file->read ( &len, sizeof ( Int ) );
  806. if ( len )
  807. {
  808. file->read ( m_buffer, len );
  809. }
  810. m_buffer[len] = 0;
  811. if ( num == 0 && len )
  812. {
  813. // only use the first string found
  814. m_stringInfo[listCount].speech = m_buffer;
  815. }
  816. }
  817. num++;
  818. }
  819. listCount++;
  820. }
  821. ok = TRUE;
  822. quit:
  823. file->close();
  824. file = NULL;
  825. return ok;
  826. }
  827. //============================================================================
  828. // GameTextManager::parseStringFile
  829. //============================================================================
  830. Bool GameTextManager::parseStringFile( const char *filename )
  831. {
  832. Int listCount = 0;
  833. Int ok = TRUE;
  834. File *file = TheFileSystem->openFile(filename, File::READ | File::TEXT);
  835. if ( file == NULL )
  836. {
  837. return FALSE;
  838. }
  839. while( ok )
  840. {
  841. Int len;
  842. if( !readLine( m_buffer, MAX_UITEXT_LENGTH, file ))
  843. {
  844. break;
  845. }
  846. removeLeadingAndTrailing ( m_buffer );
  847. if( ( *(unsigned short *)m_buffer == 0x2F2F) || !m_buffer[0]) // 0x2F2F is Hex for //
  848. continue;
  849. // make sure label is unique
  850. for ( Int i = 0; i < listCount; i++ )
  851. {
  852. if ( !stricmp ( m_stringInfo[i].label.str(), m_buffer ))
  853. {
  854. DEBUG_ASSERTCRASH ( FALSE, ("String label '%s' multiply defined!", m_buffer ));
  855. }
  856. }
  857. m_stringInfo[listCount].label = m_buffer;
  858. len = strlen ( m_buffer );
  859. if ( len > m_maxLabelLen )
  860. {
  861. m_maxLabelLen = len;
  862. }
  863. Bool readString = FALSE;
  864. while( ok )
  865. {
  866. if (!readLine ( m_buffer, sizeof(m_buffer)-1, file ))
  867. {
  868. DEBUG_ASSERTCRASH (FALSE, ("Unexpected end of string file"));
  869. ok = FALSE;
  870. goto quit;
  871. }
  872. removeLeadingAndTrailing ( m_buffer );
  873. if( m_buffer[0] == '"' )
  874. {
  875. len = strlen(m_buffer);
  876. m_buffer[ len ] = '\n';
  877. m_buffer[ len+1] = 0;
  878. readToEndOfQuote( file, &m_buffer[1], m_buffer2, m_buffer3, MAX_UITEXT_LENGTH );
  879. if ( readString )
  880. {
  881. // only one string per label allows
  882. DEBUG_ASSERTCRASH ( FALSE, ("String label '%s' has more than one string defined!", m_stringInfo[listCount].label.str()));
  883. }
  884. else
  885. {
  886. // Copy string into new home
  887. translateCopy( m_tbuffer, m_buffer2 );
  888. stripSpaces ( m_tbuffer );
  889. m_stringInfo[listCount].text = m_tbuffer ;
  890. m_stringInfo[listCount].speech = m_buffer3;
  891. readString = TRUE;
  892. }
  893. }
  894. else if ( !stricmp ( m_buffer, "END" ))
  895. {
  896. break;
  897. }
  898. }
  899. listCount++;
  900. }
  901. quit:
  902. file->close();
  903. file = NULL;
  904. return ok;
  905. }
  906. //============================================================================
  907. // GameTextManager::initMapStringFile
  908. //============================================================================
  909. void GameTextManager::initMapStringFile( const AsciiString& filename )
  910. {
  911. m_mapTextCount = 0;
  912. getStringCount( filename.str(), m_mapTextCount );
  913. m_mapStringInfo = NEW StringInfo[m_mapTextCount];
  914. parseMapStringFile( filename.str() );
  915. m_mapStringLUT = NEW StringLookUp[m_mapTextCount];
  916. StringLookUp *lut = m_mapStringLUT;
  917. StringInfo *info = m_mapStringInfo;
  918. for ( Int i = 0; i < m_mapTextCount; i++ )
  919. {
  920. lut->info = info;
  921. lut->label = &info->label;
  922. lut++;
  923. info++;
  924. }
  925. qsort( m_mapStringLUT, m_mapTextCount, sizeof(StringLookUp), compareLUT );
  926. }
  927. //============================================================================
  928. // GameTextManager::parseMapStringFile
  929. //============================================================================
  930. Bool GameTextManager::parseMapStringFile( const char *filename )
  931. {
  932. Int listCount = 0;
  933. Int ok = TRUE;
  934. File *file;
  935. file = TheFileSystem->openFile(filename, File::READ | File::TEXT);
  936. if ( file == NULL )
  937. {
  938. return FALSE;
  939. }
  940. while( ok )
  941. {
  942. Int len;
  943. if( !readLine( m_buffer, MAX_UITEXT_LENGTH, file ))
  944. {
  945. break;
  946. }
  947. removeLeadingAndTrailing ( m_buffer );
  948. if( ( *(unsigned short *)m_buffer == 0x2F2F) || !m_buffer[0]) // 0x2F2F is Hex for //
  949. continue;
  950. // make sure label is unique
  951. for ( Int i = 0; i < listCount; i++ )
  952. {
  953. if ( !stricmp ( m_mapStringInfo[i].label.str(), m_buffer ))
  954. {
  955. DEBUG_ASSERTCRASH ( FALSE, ("String label '%s' multiply defined!", m_buffer ));
  956. }
  957. }
  958. m_mapStringInfo[listCount].label = m_buffer;
  959. len = strlen ( m_buffer );
  960. if ( len > m_maxLabelLen )
  961. {
  962. m_maxLabelLen = len;
  963. }
  964. Bool readString = FALSE;
  965. while( ok )
  966. {
  967. if (!readLine ( m_buffer, sizeof(m_buffer)-1, file ))
  968. {
  969. DEBUG_ASSERTCRASH (FALSE, ("Unexpected end of string file"));
  970. ok = FALSE;
  971. goto quit;
  972. }
  973. removeLeadingAndTrailing ( m_buffer );
  974. if( m_buffer[0] == '"' )
  975. {
  976. len = strlen(m_buffer);
  977. m_buffer[ len ] = '\n';
  978. m_buffer[ len+1] = 0;
  979. readToEndOfQuote( file, &m_buffer[1], m_buffer2, m_buffer3, MAX_UITEXT_LENGTH );
  980. if ( readString )
  981. {
  982. // only one string per label allowed
  983. DEBUG_ASSERTCRASH ( FALSE, ("String label '%s' has more than one string defined!", m_stringInfo[listCount].label.str()));
  984. }
  985. else
  986. {
  987. // Copy string into new home
  988. translateCopy( m_tbuffer, m_buffer2 );
  989. stripSpaces ( m_tbuffer );
  990. UnicodeString text = UnicodeString(m_tbuffer);
  991. if (TheLanguageFilter)
  992. TheLanguageFilter->filterLine(text);
  993. m_mapStringInfo[listCount].text = text;
  994. m_mapStringInfo[listCount].speech = m_buffer3;
  995. readString = TRUE;
  996. }
  997. }
  998. else if ( !stricmp ( m_buffer, "END" ))
  999. {
  1000. break;
  1001. }
  1002. }
  1003. listCount++;
  1004. }
  1005. quit:
  1006. file->close();
  1007. file = NULL;
  1008. return ok;
  1009. }
  1010. //============================================================================
  1011. // *GameTextManager::fetch
  1012. //============================================================================
  1013. UnicodeString GameTextManager::fetch( const Char *label, Bool *exists )
  1014. {
  1015. DEBUG_ASSERTCRASH ( m_initialized, ("String Manager has not been m_initialized") );
  1016. if( m_stringInfo == NULL )
  1017. {
  1018. if( exists )
  1019. *exists = FALSE;
  1020. return m_failed;
  1021. }
  1022. StringLookUp *lookUp;
  1023. StringLookUp key;
  1024. AsciiString lb;
  1025. lb = label;
  1026. key.info = NULL;
  1027. key.label = &lb;
  1028. lookUp = (StringLookUp *) bsearch( &key, (void*) m_stringLUT, m_textCount, sizeof(StringLookUp), compareLUT );
  1029. if ( lookUp == NULL && m_mapStringLUT && m_mapTextCount )
  1030. {
  1031. lookUp = (StringLookUp *) bsearch( &key, (void*) m_mapStringLUT, m_mapTextCount, sizeof(StringLookUp), compareLUT );
  1032. }
  1033. if( lookUp == NULL )
  1034. {
  1035. // string not found
  1036. if( exists )
  1037. *exists = FALSE;
  1038. // See if we already have the missing string
  1039. UnicodeString missingString;
  1040. missingString.format(L"MISSING: '%hs'", label);
  1041. NoString *noString = m_noStringList;
  1042. while ( noString )
  1043. {
  1044. if (noString->text == missingString)
  1045. return missingString;
  1046. noString = noString->next;
  1047. }
  1048. //DEBUG_LOG(("*** MISSING:'%s' ***\n", label));
  1049. // Remember file could have been altered at this point.
  1050. noString = NEW NoString;
  1051. noString->text = missingString;
  1052. noString->next = m_noStringList;
  1053. m_noStringList = noString;
  1054. return noString->text;
  1055. }
  1056. if( exists )
  1057. *exists = TRUE;
  1058. return lookUp->info->text;
  1059. }
  1060. //============================================================================
  1061. // *GameTextManager::fetch
  1062. //============================================================================
  1063. UnicodeString GameTextManager::fetch( AsciiString label, Bool *exists )
  1064. {
  1065. return fetch(label.str(), exists);
  1066. }
  1067. //============================================================================
  1068. // GameTextManager::getStringsWithLabelPrefix
  1069. //============================================================================
  1070. AsciiStringVec& GameTextManager::getStringsWithLabelPrefix(AsciiString label)
  1071. {
  1072. m_asciiStringVec.clear();
  1073. if (m_stringLUT) {
  1074. for (int i = 0; i < m_textCount; ++i) {
  1075. if (strstr(m_stringLUT[i].label->str(), label.str()) == m_stringLUT[i].label->str()) {
  1076. m_asciiStringVec.push_back(*m_stringLUT[i].label);
  1077. }
  1078. }
  1079. }
  1080. if (m_mapStringLUT) {
  1081. for (int i = 0; i < m_mapTextCount; ++i) {
  1082. if (strstr(m_mapStringLUT[i].label->str(), label.str()) == m_mapStringLUT[i].label->str()) {
  1083. m_asciiStringVec.push_back(*m_mapStringLUT[i].label);
  1084. }
  1085. }
  1086. }
  1087. return m_asciiStringVec;
  1088. }
  1089. //============================================================================
  1090. // GameTextManager::readLine
  1091. //============================================================================
  1092. Bool GameTextManager::readLine( char *buffer, Int max, File *file )
  1093. {
  1094. Int ok = FALSE;
  1095. while ( max && file->read( buffer, 1 ) == 1 )
  1096. {
  1097. ok = TRUE;
  1098. if ( *buffer == '\n' )
  1099. {
  1100. break;
  1101. }
  1102. buffer++;
  1103. max--;
  1104. }
  1105. *buffer = 0;
  1106. return ok;
  1107. }
  1108. //============================================================================
  1109. // GameTextManager::readChar
  1110. //============================================================================
  1111. Char GameTextManager::readChar( File *file )
  1112. {
  1113. Char ch;
  1114. if ( file->read( &ch, 1 ) == 1 )
  1115. {
  1116. return ch;
  1117. }
  1118. return 0;
  1119. }
  1120. //============================================================================
  1121. // GameTextManager::compareLUT
  1122. //============================================================================
  1123. static int __cdecl compareLUT ( const void *i1, const void*i2)
  1124. {
  1125. StringLookUp *lut1 = (StringLookUp*) i1;
  1126. StringLookUp *lut2 = (StringLookUp*) i2;
  1127. return stricmp( lut1->label->str(), lut2->label->str());
  1128. }