GameText.cpp 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406
  1. /*
  2. ** Command & Conquer Generals(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. extern HWND ApplicationHWnd; ///< our application window handle
  302. if (ApplicationHWnd) {
  303. ::SetWindowTextW(ApplicationHWnd, ourName.str());
  304. }
  305. }
  306. //============================================================================
  307. // GameTextManager::deinit
  308. //============================================================================
  309. void GameTextManager::deinit( void )
  310. {
  311. if( m_stringInfo != NULL )
  312. {
  313. delete [] m_stringInfo;
  314. m_stringInfo = NULL;
  315. }
  316. if( m_stringLUT != NULL )
  317. {
  318. delete [] m_stringLUT;
  319. m_stringLUT = NULL;
  320. }
  321. m_textCount = 0;
  322. NoString *noString = m_noStringList;
  323. DEBUG_LOG(("\n*** Missing strings ***\n"));
  324. while ( noString )
  325. {
  326. DEBUG_LOG(("*** %ls ***\n", noString->text.str()));
  327. NoString *next = noString->next;
  328. delete noString;
  329. noString = next;
  330. }
  331. DEBUG_LOG(("*** End missing strings ***\n\n"));
  332. m_noStringList = NULL;
  333. m_initialized = FALSE;
  334. }
  335. //============================================================================
  336. // GameTextManager::reset
  337. //============================================================================
  338. void GameTextManager::reset( void )
  339. {
  340. if( m_mapStringInfo != NULL )
  341. {
  342. delete [] m_mapStringInfo;
  343. m_mapStringInfo = NULL;
  344. }
  345. if( m_mapStringLUT != NULL )
  346. {
  347. delete [] m_mapStringLUT;
  348. m_mapStringLUT = NULL;
  349. }
  350. }
  351. //============================================================================
  352. // GameTextManager::stripSpaces
  353. //============================================================================
  354. void GameTextManager::stripSpaces ( WideChar *string )
  355. {
  356. WideChar *str, *ptr;
  357. WideChar ch, last = 0;
  358. Int skipall = TRUE;
  359. str = ptr = string;
  360. while ( (ch = *ptr++) != 0 )
  361. {
  362. if ( ch == ' ' )
  363. {
  364. if ( last == ' ' || skipall )
  365. {
  366. continue;
  367. }
  368. }
  369. if ( ch == '\n' || ch == '\t' )
  370. {
  371. // remove last space
  372. if ( last == ' ' )
  373. {
  374. str--;
  375. }
  376. skipall = TRUE; // skip all spaces
  377. last = *str++ = ch;
  378. continue;
  379. }
  380. last = *str++ = ch;
  381. skipall = FALSE;
  382. }
  383. if ( last == ' ' )
  384. {
  385. str--;
  386. }
  387. *str = 0;
  388. }
  389. //============================================================================
  390. // GameTextManager::removeLeadingAndTrailing
  391. //============================================================================
  392. void GameTextManager::removeLeadingAndTrailing ( Char *buffer )
  393. {
  394. Char *first, *ptr;
  395. Char ch;
  396. ptr = first = buffer;
  397. while ( (ch = *first) != 0 && iswspace ( ch ))
  398. {
  399. first++;
  400. }
  401. while ( (*ptr++ = *first++) != 0 );
  402. ptr -= 2;;
  403. while ( (ptr > buffer) && (ch = *ptr) != 0 && iswspace ( ch ) )
  404. {
  405. ptr--;
  406. }
  407. ptr++;
  408. *ptr = 0;
  409. }
  410. //============================================================================
  411. // GameTextManager::readToEndOfQuote
  412. //============================================================================
  413. void GameTextManager::readToEndOfQuote( File *file, Char *in, Char *out, Char *wavefile, Int maxBufLen )
  414. {
  415. Int slash = FALSE;
  416. Int state = 0;
  417. Int line_start = FALSE;
  418. Char ch;
  419. Int ccount = 0;
  420. Int len = 0;
  421. Int done = FALSE;
  422. while ( maxBufLen )
  423. {
  424. // get next Char
  425. if ( in )
  426. {
  427. if ( (ch = *in++) == 0 )
  428. {
  429. in = NULL; // have exhausted the input m_buffer
  430. ch = readChar ( file );
  431. }
  432. }
  433. else
  434. {
  435. ch = readChar ( file );
  436. }
  437. if ( ch == EOF )
  438. {
  439. return ;
  440. }
  441. if ( ch == '\n' )
  442. {
  443. line_start = TRUE;
  444. slash = FALSE;
  445. ccount = 0;
  446. ch = ' ';
  447. }
  448. else if ( ch == '\\' && !slash)
  449. {
  450. slash = TRUE;
  451. }
  452. else if ( ch == '\\' && slash)
  453. {
  454. slash = FALSE;
  455. }
  456. else if ( ch == '"' && !slash )
  457. {
  458. break; // done
  459. }
  460. else
  461. {
  462. slash = FALSE;
  463. }
  464. if ( iswspace ( ch ))
  465. {
  466. ch = ' ';
  467. }
  468. *out++ = ch;
  469. maxBufLen--;
  470. }
  471. *out = 0;
  472. while ( !done )
  473. {
  474. // get next Char
  475. if ( in )
  476. {
  477. if ( (ch = *in++) == 0 )
  478. {
  479. in = NULL; // have exhausted the input m_buffer
  480. ch = readChar ( file );
  481. }
  482. }
  483. else
  484. {
  485. ch = readChar ( file );
  486. }
  487. if ( ch == '\n' || ch == EOF )
  488. {
  489. break;
  490. }
  491. switch ( state )
  492. {
  493. case 0:
  494. if ( iswspace ( ch ) || ch == '=' )
  495. {
  496. break;
  497. }
  498. state = 1;
  499. case 1:
  500. if ( ( ch >= 'a' && ch <= 'z') || ( ch >= 'A' && ch <='Z') || (ch >= '0' && ch <= '9') || ch == '_' )
  501. {
  502. *wavefile++ = ch;
  503. len++;
  504. break;
  505. }
  506. state = 2;
  507. case 2:
  508. break;
  509. }
  510. }
  511. *wavefile = 0;
  512. if ( len )
  513. {
  514. if ( ( ch = *(wavefile-1)) >= '0' && ch <= '9' )
  515. {
  516. *wavefile++ = 'e';
  517. *wavefile = 0;
  518. }
  519. }
  520. }
  521. //============================================================================
  522. // GameTextManager::reverseWord
  523. //============================================================================
  524. void GameTextManager::reverseWord ( Char *file, Char *lp )
  525. {
  526. Int first = TRUE;
  527. Char f, l;
  528. Int ok = TRUE ;
  529. while ( ok )
  530. {
  531. if ( file >= lp )
  532. {
  533. return;
  534. }
  535. f = *file;
  536. l = *lp;
  537. if ( first )
  538. {
  539. if ( f >= 'A' && f <= 'Z' )
  540. {
  541. if ( l >= 'a' && l <= 'z' )
  542. {
  543. f = (f - 'A') + 'a';
  544. l = (l - 'a') + 'A';
  545. }
  546. }
  547. first = FALSE;
  548. }
  549. *lp-- = f;
  550. *file++ = l;
  551. }
  552. }
  553. //============================================================================
  554. // GameTextManager::translateCopy
  555. //============================================================================
  556. void GameTextManager::translateCopy( WideChar *outbuf, Char *inbuf )
  557. {
  558. Int slash = FALSE;
  559. #if defined(_DEBUG) || defined(_INTERNAL)
  560. if ( m_jabberWockie )
  561. {
  562. static Char buffer[MAX_UITEXT_LENGTH*2];
  563. Char *firstLetter = NULL, *lastLetter;
  564. Char *b = buffer;
  565. Int formatWord = FALSE;
  566. Char ch;
  567. while ( (ch = *inbuf++) != 0 )
  568. {
  569. if ( ! (( ch >= 'a' && ch <= 'z') || ( ch >= 'A' && ch <= 'Z' )))
  570. {
  571. if ( firstLetter )
  572. {
  573. if ( !formatWord )
  574. {
  575. lastLetter = b-1;
  576. reverseWord ( firstLetter, lastLetter );
  577. }
  578. firstLetter = NULL;
  579. formatWord = FALSE;
  580. }
  581. *b++ = ch;
  582. if ( ch == '\\' )
  583. {
  584. *b++ = *inbuf++;
  585. }
  586. if ( ch == '%' )
  587. {
  588. while ( (ch = *inbuf++) != 0 && !( (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')))
  589. {
  590. *b++ = ch;
  591. }
  592. *b++ = ch;
  593. }
  594. }
  595. else
  596. {
  597. if ( !firstLetter )
  598. {
  599. firstLetter = b;
  600. }
  601. *b++ = ch;
  602. }
  603. }
  604. if ( firstLetter )
  605. {
  606. lastLetter = b-1;
  607. reverseWord ( firstLetter, lastLetter );
  608. }
  609. *b++ = 0;
  610. inbuf = buffer;
  611. }
  612. else if( m_munkee )
  613. {
  614. wcscpy(outbuf, L"Munkee");
  615. return;
  616. }
  617. #endif
  618. while( *inbuf != '\0' )
  619. {
  620. if( slash == TRUE )
  621. {
  622. slash = FALSE;
  623. switch( *inbuf )
  624. {
  625. // in case end of string is reached
  626. // should never happen!!!
  627. case '\0':
  628. return;
  629. case '\\':
  630. *outbuf++ = '\\';
  631. break;
  632. case '\'':
  633. *outbuf++ = '\'';
  634. break;
  635. case '\"':
  636. *outbuf++ = '\"';
  637. break;
  638. case '\?':
  639. *outbuf++ = '\?';
  640. break;
  641. case 't':
  642. *outbuf++ = '\t';
  643. break;
  644. case 'n':
  645. *outbuf++ = '\n';
  646. break;
  647. default:
  648. *outbuf++ = *inbuf & 0x00FF;
  649. break;
  650. }
  651. }
  652. else if( *inbuf != '\\' )
  653. {
  654. *outbuf++ = *inbuf & 0x00FF;
  655. }
  656. else
  657. slash = TRUE;
  658. inbuf++;
  659. }
  660. *outbuf= 0;
  661. }
  662. //============================================================================
  663. // GameTextManager::getStringCount
  664. //============================================================================
  665. Bool GameTextManager::getStringCount( const char *filename, Int& textCount )
  666. {
  667. Int ok = TRUE;
  668. textCount = 0;
  669. File *file;
  670. file = TheFileSystem->openFile(filename, File::READ | File::TEXT);
  671. DEBUG_LOG(("Looking in %s for string file\n", filename));
  672. if ( file == NULL )
  673. {
  674. return FALSE;
  675. }
  676. while(ok)
  677. {
  678. if( !readLine( m_buffer, sizeof( m_buffer) -1, file ) )
  679. break;
  680. removeLeadingAndTrailing ( m_buffer );
  681. if( m_buffer[0] == '"' )
  682. {
  683. Int len = strlen(m_buffer);
  684. m_buffer[ len ] = '\n';
  685. m_buffer[ len+1] = 0;
  686. readToEndOfQuote( file, &m_buffer[1], m_buffer2, m_buffer3, MAX_UITEXT_LENGTH );
  687. }
  688. else if( !stricmp( m_buffer, "END") )
  689. {
  690. textCount++;
  691. }
  692. }
  693. textCount += 500;
  694. file->close();
  695. file = NULL;
  696. return TRUE;
  697. }
  698. //============================================================================
  699. // GameTextManager::getCSFInfo
  700. //============================================================================
  701. Bool GameTextManager::getCSFInfo ( const Char *filename )
  702. {
  703. CSFHeader header;
  704. Int ok = FALSE;
  705. File *file = TheFileSystem->openFile(filename, File::READ | File::BINARY);
  706. DEBUG_LOG(("Looking in %s for compiled string file\n", filename));
  707. if ( file != NULL )
  708. {
  709. if ( file->read( &header, sizeof ( header )) == sizeof ( header ) )
  710. {
  711. if ( header.id == CSF_ID )
  712. {
  713. m_textCount = header.num_labels;
  714. if ( header.version >= 2 )
  715. {
  716. m_language = (LanguageID) header.langid;
  717. }
  718. else
  719. {
  720. m_language = LANGUAGE_ID_US;
  721. }
  722. ok = TRUE;
  723. }
  724. }
  725. file->close();
  726. file = NULL;
  727. }
  728. return ok;
  729. }
  730. //============================================================================
  731. // GameTextManager::parseCSF
  732. //============================================================================
  733. Bool GameTextManager::parseCSF( const Char *filename )
  734. {
  735. File *file;
  736. Int id;
  737. Int len;
  738. Int listCount = 0;
  739. Bool ok = FALSE;
  740. CSFHeader header;
  741. file = TheFileSystem->openFile(filename, File::READ | File::BINARY);
  742. if ( file == NULL )
  743. {
  744. return FALSE;
  745. }
  746. if ( file->read ( &header, sizeof ( CSFHeader)) != sizeof ( CSFHeader) )
  747. {
  748. return FALSE;
  749. }
  750. while( file->read ( &id, sizeof (id)) == sizeof ( id) )
  751. {
  752. Int num;
  753. Int num_strings;
  754. if ( id != CSF_LABEL )
  755. {
  756. goto quit;
  757. }
  758. file->read ( &num_strings, sizeof ( Int ));
  759. file->read ( &len, sizeof ( Int ) );
  760. if ( len )
  761. {
  762. file->read ( m_buffer, len );
  763. }
  764. m_buffer[len] = 0;
  765. m_stringInfo[listCount].label = m_buffer;
  766. if ( len > m_maxLabelLen )
  767. {
  768. m_maxLabelLen = len;
  769. }
  770. num = 0;
  771. while ( num < num_strings )
  772. {
  773. file->read ( &id, sizeof ( Int ) );
  774. if ( id != CSF_STRING && id != CSF_STRINGWITHWAVE )
  775. {
  776. goto quit;
  777. }
  778. file->read ( &len, sizeof ( Int ) );
  779. if ( len )
  780. {
  781. file->read ( m_tbuffer, len*sizeof(WideChar) );
  782. }
  783. if ( num == 0 )
  784. {
  785. // only use the first string found
  786. m_tbuffer[len] = 0;
  787. {
  788. WideChar *ptr;
  789. ptr = m_tbuffer;
  790. while ( *ptr )
  791. {
  792. *ptr = ~*ptr;
  793. ptr++;
  794. }
  795. }
  796. stripSpaces ( m_tbuffer );
  797. m_stringInfo[listCount].text = m_tbuffer;
  798. }
  799. if ( id == CSF_STRINGWITHWAVE )
  800. {
  801. file->read ( &len, sizeof ( Int ) );
  802. if ( len )
  803. {
  804. file->read ( m_buffer, len );
  805. }
  806. m_buffer[len] = 0;
  807. if ( num == 0 && len )
  808. {
  809. // only use the first string found
  810. m_stringInfo[listCount].speech = m_buffer;
  811. }
  812. }
  813. num++;
  814. }
  815. listCount++;
  816. }
  817. ok = TRUE;
  818. quit:
  819. file->close();
  820. file = NULL;
  821. return ok;
  822. }
  823. //============================================================================
  824. // GameTextManager::parseStringFile
  825. //============================================================================
  826. Bool GameTextManager::parseStringFile( const char *filename )
  827. {
  828. Int listCount = 0;
  829. Int ok = TRUE;
  830. File *file = TheFileSystem->openFile(filename, File::READ | File::TEXT);
  831. if ( file == NULL )
  832. {
  833. return FALSE;
  834. }
  835. while( ok )
  836. {
  837. Int len;
  838. if( !readLine( m_buffer, MAX_UITEXT_LENGTH, file ))
  839. {
  840. break;
  841. }
  842. removeLeadingAndTrailing ( m_buffer );
  843. if( ( *(unsigned short *)m_buffer == 0x2F2F) || !m_buffer[0]) // 0x2F2F is Hex for //
  844. continue;
  845. // make sure label is unique
  846. for ( Int i = 0; i < listCount; i++ )
  847. {
  848. if ( !stricmp ( m_stringInfo[i].label.str(), m_buffer ))
  849. {
  850. DEBUG_ASSERTCRASH ( FALSE, ("String label '%s' multiply defined!", m_buffer ));
  851. }
  852. }
  853. m_stringInfo[listCount].label = m_buffer;
  854. len = strlen ( m_buffer );
  855. if ( len > m_maxLabelLen )
  856. {
  857. m_maxLabelLen = len;
  858. }
  859. Bool readString = FALSE;
  860. while( ok )
  861. {
  862. if (!readLine ( m_buffer, sizeof(m_buffer)-1, file ))
  863. {
  864. DEBUG_ASSERTCRASH (FALSE, ("Unexpected end of string file"));
  865. ok = FALSE;
  866. goto quit;
  867. }
  868. removeLeadingAndTrailing ( m_buffer );
  869. if( m_buffer[0] == '"' )
  870. {
  871. len = strlen(m_buffer);
  872. m_buffer[ len ] = '\n';
  873. m_buffer[ len+1] = 0;
  874. readToEndOfQuote( file, &m_buffer[1], m_buffer2, m_buffer3, MAX_UITEXT_LENGTH );
  875. if ( readString )
  876. {
  877. // only one string per label allows
  878. DEBUG_ASSERTCRASH ( FALSE, ("String label '%s' has more than one string defined!", m_stringInfo[listCount].label.str()));
  879. }
  880. else
  881. {
  882. // Copy string into new home
  883. translateCopy( m_tbuffer, m_buffer2 );
  884. stripSpaces ( m_tbuffer );
  885. m_stringInfo[listCount].text = m_tbuffer ;
  886. m_stringInfo[listCount].speech = m_buffer3;
  887. readString = TRUE;
  888. }
  889. }
  890. else if ( !stricmp ( m_buffer, "END" ))
  891. {
  892. break;
  893. }
  894. }
  895. listCount++;
  896. }
  897. quit:
  898. file->close();
  899. file = NULL;
  900. return ok;
  901. }
  902. //============================================================================
  903. // GameTextManager::initMapStringFile
  904. //============================================================================
  905. void GameTextManager::initMapStringFile( const AsciiString& filename )
  906. {
  907. m_mapTextCount = 0;
  908. getStringCount( filename.str(), m_mapTextCount );
  909. m_mapStringInfo = NEW StringInfo[m_mapTextCount];
  910. parseMapStringFile( filename.str() );
  911. m_mapStringLUT = NEW StringLookUp[m_mapTextCount];
  912. StringLookUp *lut = m_mapStringLUT;
  913. StringInfo *info = m_mapStringInfo;
  914. for ( Int i = 0; i < m_mapTextCount; i++ )
  915. {
  916. lut->info = info;
  917. lut->label = &info->label;
  918. lut++;
  919. info++;
  920. }
  921. qsort( m_mapStringLUT, m_mapTextCount, sizeof(StringLookUp), compareLUT );
  922. }
  923. //============================================================================
  924. // GameTextManager::parseMapStringFile
  925. //============================================================================
  926. Bool GameTextManager::parseMapStringFile( const char *filename )
  927. {
  928. Int listCount = 0;
  929. Int ok = TRUE;
  930. File *file;
  931. file = TheFileSystem->openFile(filename, File::READ | File::TEXT);
  932. if ( file == NULL )
  933. {
  934. return FALSE;
  935. }
  936. while( ok )
  937. {
  938. Int len;
  939. if( !readLine( m_buffer, MAX_UITEXT_LENGTH, file ))
  940. {
  941. break;
  942. }
  943. removeLeadingAndTrailing ( m_buffer );
  944. if( ( *(unsigned short *)m_buffer == 0x2F2F) || !m_buffer[0]) // 0x2F2F is Hex for //
  945. continue;
  946. // make sure label is unique
  947. for ( Int i = 0; i < listCount; i++ )
  948. {
  949. if ( !stricmp ( m_mapStringInfo[i].label.str(), m_buffer ))
  950. {
  951. DEBUG_ASSERTCRASH ( FALSE, ("String label '%s' multiply defined!", m_buffer ));
  952. }
  953. }
  954. m_mapStringInfo[listCount].label = m_buffer;
  955. len = strlen ( m_buffer );
  956. if ( len > m_maxLabelLen )
  957. {
  958. m_maxLabelLen = len;
  959. }
  960. Bool readString = FALSE;
  961. while( ok )
  962. {
  963. if (!readLine ( m_buffer, sizeof(m_buffer)-1, file ))
  964. {
  965. DEBUG_ASSERTCRASH (FALSE, ("Unexpected end of string file"));
  966. ok = FALSE;
  967. goto quit;
  968. }
  969. removeLeadingAndTrailing ( m_buffer );
  970. if( m_buffer[0] == '"' )
  971. {
  972. len = strlen(m_buffer);
  973. m_buffer[ len ] = '\n';
  974. m_buffer[ len+1] = 0;
  975. readToEndOfQuote( file, &m_buffer[1], m_buffer2, m_buffer3, MAX_UITEXT_LENGTH );
  976. if ( readString )
  977. {
  978. // only one string per label allowed
  979. DEBUG_ASSERTCRASH ( FALSE, ("String label '%s' has more than one string defined!", m_stringInfo[listCount].label.str()));
  980. }
  981. else
  982. {
  983. // Copy string into new home
  984. translateCopy( m_tbuffer, m_buffer2 );
  985. stripSpaces ( m_tbuffer );
  986. UnicodeString text = UnicodeString(m_tbuffer);
  987. if (TheLanguageFilter)
  988. TheLanguageFilter->filterLine(text);
  989. m_mapStringInfo[listCount].text = text;
  990. m_mapStringInfo[listCount].speech = m_buffer3;
  991. readString = TRUE;
  992. }
  993. }
  994. else if ( !stricmp ( m_buffer, "END" ))
  995. {
  996. break;
  997. }
  998. }
  999. listCount++;
  1000. }
  1001. quit:
  1002. file->close();
  1003. file = NULL;
  1004. return ok;
  1005. }
  1006. //============================================================================
  1007. // *GameTextManager::fetch
  1008. //============================================================================
  1009. UnicodeString GameTextManager::fetch( const Char *label, Bool *exists )
  1010. {
  1011. DEBUG_ASSERTCRASH ( m_initialized, ("String Manager has not been m_initialized") );
  1012. if( m_stringInfo == NULL )
  1013. {
  1014. if( exists )
  1015. *exists = FALSE;
  1016. return m_failed;
  1017. }
  1018. StringLookUp *lookUp;
  1019. StringLookUp key;
  1020. AsciiString lb;
  1021. lb = label;
  1022. key.info = NULL;
  1023. key.label = &lb;
  1024. lookUp = (StringLookUp *) bsearch( &key, (void*) m_stringLUT, m_textCount, sizeof(StringLookUp), compareLUT );
  1025. if ( lookUp == NULL && m_mapStringLUT && m_mapTextCount )
  1026. {
  1027. lookUp = (StringLookUp *) bsearch( &key, (void*) m_mapStringLUT, m_mapTextCount, sizeof(StringLookUp), compareLUT );
  1028. }
  1029. if( lookUp == NULL )
  1030. {
  1031. // string not found
  1032. if( exists )
  1033. *exists = FALSE;
  1034. // See if we already have the missing string
  1035. UnicodeString missingString;
  1036. missingString.format(L"MISSING: '%hs'", label);
  1037. NoString *noString = m_noStringList;
  1038. while ( noString )
  1039. {
  1040. if (noString->text == missingString)
  1041. return missingString;
  1042. noString = noString->next;
  1043. }
  1044. //DEBUG_LOG(("*** MISSING:'%s' ***\n", label));
  1045. // Remember file could have been altered at this point.
  1046. noString = NEW NoString;
  1047. noString->text = missingString;
  1048. noString->next = m_noStringList;
  1049. m_noStringList = noString;
  1050. return noString->text;
  1051. }
  1052. if( exists )
  1053. *exists = TRUE;
  1054. return lookUp->info->text;
  1055. }
  1056. //============================================================================
  1057. // *GameTextManager::fetch
  1058. //============================================================================
  1059. UnicodeString GameTextManager::fetch( AsciiString label, Bool *exists )
  1060. {
  1061. return fetch(label.str(), exists);
  1062. }
  1063. //============================================================================
  1064. // GameTextManager::getStringsWithLabelPrefix
  1065. //============================================================================
  1066. AsciiStringVec& GameTextManager::getStringsWithLabelPrefix(AsciiString label)
  1067. {
  1068. m_asciiStringVec.clear();
  1069. if (m_stringLUT) {
  1070. for (int i = 0; i < m_textCount; ++i) {
  1071. if (strstr(m_stringLUT[i].label->str(), label.str()) == m_stringLUT[i].label->str()) {
  1072. m_asciiStringVec.push_back(*m_stringLUT[i].label);
  1073. }
  1074. }
  1075. }
  1076. if (m_mapStringLUT) {
  1077. for (int i = 0; i < m_mapTextCount; ++i) {
  1078. if (strstr(m_mapStringLUT[i].label->str(), label.str()) == m_mapStringLUT[i].label->str()) {
  1079. m_asciiStringVec.push_back(*m_mapStringLUT[i].label);
  1080. }
  1081. }
  1082. }
  1083. return m_asciiStringVec;
  1084. }
  1085. //============================================================================
  1086. // GameTextManager::readLine
  1087. //============================================================================
  1088. Bool GameTextManager::readLine( char *buffer, Int max, File *file )
  1089. {
  1090. Int ok = FALSE;
  1091. while ( max && file->read( buffer, 1 ) == 1 )
  1092. {
  1093. ok = TRUE;
  1094. if ( *buffer == '\n' )
  1095. {
  1096. break;
  1097. }
  1098. buffer++;
  1099. max--;
  1100. }
  1101. *buffer = 0;
  1102. return ok;
  1103. }
  1104. //============================================================================
  1105. // GameTextManager::readChar
  1106. //============================================================================
  1107. Char GameTextManager::readChar( File *file )
  1108. {
  1109. Char ch;
  1110. if ( file->read( &ch, 1 ) == 1 )
  1111. {
  1112. return ch;
  1113. }
  1114. return 0;
  1115. }
  1116. //============================================================================
  1117. // GameTextManager::compareLUT
  1118. //============================================================================
  1119. static int __cdecl compareLUT ( const void *i1, const void*i2)
  1120. {
  1121. StringLookUp *lut1 = (StringLookUp*) i1;
  1122. StringLookUp *lut2 = (StringLookUp*) i2;
  1123. return stricmp( lut1->label->str(), lut2->label->str());
  1124. }