translatedb.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376
  1. /*
  2. ** Command & Conquer Renegade(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. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : LevelEdit *
  23. * *
  24. * $Archive:: /Commando/Code/wwtranslatedb/translatedb.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 1/28/02 4:05p $*
  29. * *
  30. * $Revision:: 18 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "translatedb.h"
  36. #include <windows.h>
  37. #include <string.h>
  38. #include "persist.h"
  39. #include "persistfactory.h"
  40. #include "chunkio.h"
  41. #include "translatedbids.h"
  42. #include "rawfile.h"
  43. #include "textfile.h"
  44. #include "tdbcategories.h"
  45. #include "definition.h"
  46. #include "definitionmgr.h"
  47. #include "definitionclassids.h"
  48. ///////////////////////////////////////////////////////////////////////
  49. // Local prototypes
  50. ///////////////////////////////////////////////////////////////////////
  51. static int Build_List_From_String (const char *buffer, const char *delimiter, StringClass **string_list);
  52. ///////////////////////////////////////////////////////////////////////
  53. // Global singleton instance
  54. ///////////////////////////////////////////////////////////////////////
  55. TranslateDBClass _TheTranslateDB;
  56. ///////////////////////////////////////////////////////////////////////
  57. // Static member initialization
  58. ///////////////////////////////////////////////////////////////////////
  59. TDB_OBJ_LIST TranslateDBClass::m_ObjectList;
  60. HashTemplateClass<StringClass,TDBObjClass*> TranslateDBClass::m_ObjectHash;
  61. TDB_CATEGORY_LIST TranslateDBClass::m_CategoryList;
  62. uint32 TranslateDBClass::m_VersionNumber = 100;
  63. uint32 TranslateDBClass::m_LanguageID = TranslateDBClass::LANGID_ENGLISH;
  64. bool TranslateDBClass::IsSingleLanguageExport = false;
  65. TranslateDBClass::FILTER_OPT TranslateDBClass::FilterType = FILTER_DISABLED;
  66. uint32 TranslateDBClass::FilterCategoryID = 0xFFFFFFFF;
  67. ///////////////////////////////////////////////////////////////////////
  68. // Constants
  69. ///////////////////////////////////////////////////////////////////////
  70. enum
  71. {
  72. CHUNKID_VARIABLES = 0x07141200,
  73. CHUNKID_OBJECTS,
  74. CHUNKID_CATEGORIES
  75. };
  76. enum
  77. {
  78. VARID_VERSION_NUMBER = 0x01,
  79. VARID_LANGUAGE_ID
  80. };
  81. const WCHAR * STRING_NOT_FOUND = L"TDBERR";
  82. const char * ENGLISH_STRING_NOT_FOUND = "TDBERR";
  83. ///////////////////////////////////////////////////////////////////////
  84. //
  85. // Chunk_ID
  86. //
  87. ///////////////////////////////////////////////////////////////////////
  88. uint32
  89. TranslateDBClass::Chunk_ID (void) const
  90. {
  91. return CHUNKID_TRANSLATE_DB;
  92. }
  93. ///////////////////////////////////////////////////////////////////////
  94. //
  95. // Initialize
  96. //
  97. ///////////////////////////////////////////////////////////////////////
  98. void
  99. TranslateDBClass::Initialize (void)
  100. {
  101. m_ObjectList.Set_Growth_Step (1000);
  102. return ;
  103. }
  104. ///////////////////////////////////////////////////////////////////////
  105. //
  106. // Shutdown
  107. //
  108. ///////////////////////////////////////////////////////////////////////
  109. void
  110. TranslateDBClass::Shutdown (void)
  111. {
  112. Free_Objects ();
  113. Free_Categories ();
  114. return ;
  115. }
  116. ///////////////////////////////////////////////////////////////////////
  117. //
  118. // Free_Categories
  119. //
  120. ///////////////////////////////////////////////////////////////////////
  121. void
  122. TranslateDBClass::Free_Categories (void)
  123. {
  124. //
  125. // Loop over and free all the translation categories
  126. //
  127. for (int index = 0; index < m_CategoryList.Count (); index ++) {
  128. TDBCategoryClass *category = m_CategoryList[index];
  129. if (category != NULL) {
  130. delete category;
  131. }
  132. }
  133. m_CategoryList.Delete_All ();
  134. return ;
  135. }
  136. ///////////////////////////////////////////////////////////////////////
  137. //
  138. // Free_Objects
  139. //
  140. ///////////////////////////////////////////////////////////////////////
  141. void
  142. TranslateDBClass::Free_Objects (void)
  143. {
  144. //
  145. // Loop over and free all the translation objects
  146. //
  147. for (int index = 0; index < m_ObjectList.Count (); index ++) {
  148. TDBObjClass *translate_obj = m_ObjectList[index];
  149. if (translate_obj != NULL) {
  150. delete translate_obj;
  151. }
  152. }
  153. m_ObjectList.Delete_All ();
  154. // Remove the stuff from the hash table as well..
  155. m_ObjectHash.Remove_All();
  156. return ;
  157. }
  158. ///////////////////////////////////////////////////////////////////////
  159. //
  160. // Contains_Data
  161. //
  162. ///////////////////////////////////////////////////////////////////////
  163. bool
  164. TranslateDBClass::Contains_Data (void) const
  165. {
  166. return true;
  167. }
  168. ///////////////////////////////////////////////////////////////////////
  169. //
  170. // Save
  171. //
  172. ///////////////////////////////////////////////////////////////////////
  173. bool
  174. TranslateDBClass::Save (ChunkSaveClass &csave)
  175. {
  176. bool retval = true;
  177. csave.Begin_Chunk (CHUNKID_VARIABLES);
  178. WRITE_MICRO_CHUNK (csave, VARID_VERSION_NUMBER, m_VersionNumber);
  179. WRITE_MICRO_CHUNK (csave, VARID_LANGUAGE_ID, m_LanguageID);
  180. csave.End_Chunk ();
  181. csave.Begin_Chunk (CHUNKID_CATEGORIES);
  182. //
  183. // Loop over and save all the translation categories
  184. //
  185. for (int index = 0; index < m_CategoryList.Count (); index ++) {
  186. TDBCategoryClass *category = m_CategoryList[index];
  187. //
  188. // Save this category
  189. //
  190. if (category != NULL) {
  191. csave.Begin_Chunk (category->Get_Factory ().Chunk_ID ());
  192. category->Get_Factory ().Save (csave, category);
  193. csave.End_Chunk ();
  194. }
  195. }
  196. csave.End_Chunk ();
  197. csave.Begin_Chunk (CHUNKID_OBJECTS);
  198. //
  199. // Loop over and save all the translation objects
  200. //
  201. for (index = 0; index < m_ObjectList.Count (); index ++) {
  202. TDBObjClass *translate_obj = m_ObjectList[index];
  203. //
  204. // Save this translation object
  205. //
  206. if (translate_obj != NULL) {
  207. //
  208. // Check to ensure this category isn't filtered out...
  209. //
  210. bool is_equal = (translate_obj->Get_Category_ID () == FilterCategoryID);
  211. if ( FilterType == FILTER_DISABLED ||
  212. (FilterType == FILTER_IF_EQUAL && is_equal == false) ||
  213. (FilterType == FILTER_IF_NOT_EQUAL && is_equal))
  214. {
  215. csave.Begin_Chunk (translate_obj->Get_Factory ().Chunk_ID ());
  216. translate_obj->Get_Factory ().Save (csave, translate_obj);
  217. csave.End_Chunk ();
  218. }
  219. }
  220. }
  221. csave.End_Chunk ();
  222. return retval;
  223. }
  224. ///////////////////////////////////////////////////////////////////////
  225. //
  226. // Load
  227. //
  228. ///////////////////////////////////////////////////////////////////////
  229. bool
  230. TranslateDBClass::Load (ChunkLoadClass &cload)
  231. {
  232. Free_Objects ();
  233. Free_Categories ();
  234. bool retval = true;
  235. while (cload.Open_Chunk ()) {
  236. switch (cload.Cur_Chunk_ID ()) {
  237. //
  238. // Load all the presets from this chunk
  239. //
  240. case CHUNKID_VARIABLES:
  241. retval &= Load_Variables (cload);
  242. break;
  243. case CHUNKID_OBJECTS:
  244. retval &= Load_Objects (cload);
  245. break;
  246. case CHUNKID_CATEGORIES:
  247. retval &= Load_Categories (cload);
  248. break;
  249. }
  250. cload.Close_Chunk ();
  251. }
  252. Validate_Data ();
  253. return retval;
  254. }
  255. ///////////////////////////////////////////////////////////////////////
  256. //
  257. // Load_Categories
  258. //
  259. ///////////////////////////////////////////////////////////////////////
  260. bool
  261. TranslateDBClass::Load_Categories (ChunkLoadClass &cload)
  262. {
  263. bool retval = true;
  264. while (cload.Open_Chunk ()) {
  265. //
  266. // Load this object from the chunk (if possible)
  267. //
  268. PersistFactoryClass *factory = SaveLoadSystemClass::Find_Persist_Factory (cload.Cur_Chunk_ID ());
  269. if (factory != NULL) {
  270. TDBCategoryClass *category = (TDBCategoryClass *)factory->Load (cload);
  271. if (category != NULL) {
  272. Add_Category (category, false);
  273. }
  274. }
  275. cload.Close_Chunk ();
  276. }
  277. return retval;
  278. }
  279. ///////////////////////////////////////////////////////////////////////
  280. //
  281. // Load_Objects
  282. //
  283. ///////////////////////////////////////////////////////////////////////
  284. bool
  285. TranslateDBClass::Load_Objects (ChunkLoadClass &cload)
  286. {
  287. bool retval = true;
  288. while (cload.Open_Chunk ()) {
  289. //
  290. // Load this object from the chunk (if possible)
  291. //
  292. PersistFactoryClass *factory = SaveLoadSystemClass::Find_Persist_Factory (cload.Cur_Chunk_ID ());
  293. if (factory != NULL) {
  294. TDBObjClass *translate_obj = (TDBObjClass *)factory->Load (cload);
  295. if (translate_obj != NULL) {
  296. Add_Object (translate_obj);
  297. }
  298. }
  299. cload.Close_Chunk ();
  300. }
  301. return retval;
  302. }
  303. ///////////////////////////////////////////////////////////////////////
  304. //
  305. // Load_Variables
  306. //
  307. ///////////////////////////////////////////////////////////////////////
  308. bool
  309. TranslateDBClass::Load_Variables (ChunkLoadClass &cload)
  310. {
  311. bool retval = true;
  312. while (cload.Open_Micro_Chunk ()) {
  313. switch (cload.Cur_Micro_Chunk_ID ()) {
  314. READ_MICRO_CHUNK (cload, VARID_VERSION_NUMBER, m_VersionNumber);
  315. READ_MICRO_CHUNK (cload, VARID_LANGUAGE_ID, m_LanguageID);
  316. }
  317. cload.Close_Micro_Chunk ();
  318. }
  319. return retval;
  320. }
  321. ///////////////////////////////////////////////////////////////////////
  322. //
  323. // Validate_Data
  324. //
  325. ///////////////////////////////////////////////////////////////////////
  326. void
  327. TranslateDBClass::Validate_Data (void)
  328. {
  329. if (m_CategoryList.Count () == 0) {
  330. //
  331. // Create the default category
  332. //
  333. TDBCategoryClass *category = new TDBCategoryClass;
  334. category->Set_Name ("Default");
  335. m_CategoryList.Add (category);
  336. }
  337. return ;
  338. }
  339. ///////////////////////////////////////////////////////////////////////
  340. //
  341. // Export_Table
  342. //
  343. ///////////////////////////////////////////////////////////////////////
  344. void
  345. TranslateDBClass::Export_Table (const char *filename)
  346. {
  347. //
  348. // Create the file
  349. //
  350. HANDLE file = ::CreateFile (filename,
  351. GENERIC_WRITE,
  352. 0,
  353. NULL,
  354. CREATE_ALWAYS,
  355. 0L,
  356. NULL);
  357. WWASSERT (file != INVALID_HANDLE_VALUE);
  358. if (file != INVALID_HANDLE_VALUE) {
  359. TextFileClass file_obj;
  360. file_obj.Attach (file);
  361. //
  362. // Loop over all the translation objects and write a tab delimited
  363. // entry for each one
  364. //
  365. for (int index = 0; index < m_ObjectList.Count (); index ++) {
  366. TDBObjClass *object = m_ObjectList[index];
  367. if (object != NULL && object->As_StringTwiddlerClass () == NULL) {
  368. StringClass english_string = object->Get_English_String ();
  369. int length = english_string.Get_Length ();
  370. for (int index = 0; index < length; index ++) {
  371. if (english_string[index] == '\n') {
  372. english_string[index] = ' ';
  373. }
  374. }
  375. //
  376. // Lookup the string's category
  377. //
  378. StringClass category_name;
  379. TDBCategoryClass *category = Find_Category (object->Get_Category_ID ());
  380. if (category != NULL) {
  381. category_name = category->Get_Name ();
  382. }
  383. //
  384. // Lookup the string's sound preset
  385. //
  386. StringClass sound_preset_name;
  387. DefinitionClass *definition = DefinitionMgrClass::Find_Definition (object->Get_Sound_ID (), false);
  388. if (definition != NULL) {
  389. sound_preset_name = definition->Get_Name ();
  390. }
  391. //
  392. // Write a tab delimited entry for this object
  393. //
  394. StringClass text_entry;
  395. text_entry = category_name;
  396. text_entry += "\t";
  397. text_entry += object->Get_ID_Desc ();
  398. text_entry += "\t";
  399. text_entry += english_string;
  400. text_entry += "\t";
  401. text_entry += sound_preset_name;
  402. file_obj.Write_Line (text_entry);
  403. }
  404. }
  405. //
  406. // Close the file
  407. //
  408. file_obj.Detach ();
  409. ::CloseHandle (file);
  410. }
  411. return ;
  412. }
  413. ///////////////////////////////////////////////////////////////////////
  414. //
  415. // Export_C_Header
  416. //
  417. ///////////////////////////////////////////////////////////////////////
  418. void
  419. TranslateDBClass::Export_C_Header (const char *filename)
  420. {
  421. //
  422. // Create the file
  423. //
  424. HANDLE file = ::CreateFile (filename,
  425. GENERIC_WRITE,
  426. 0,
  427. NULL,
  428. CREATE_ALWAYS,
  429. 0L,
  430. NULL);
  431. WWASSERT (file != INVALID_HANDLE_VALUE);
  432. if (file != INVALID_HANDLE_VALUE) {
  433. TextFileClass file_obj;
  434. file_obj.Attach (file);
  435. //
  436. // Wtite the 'C' style header framework
  437. //
  438. file_obj.Write_Line ("#if defined(_MSC_VER)");
  439. file_obj.Write_Line ("#pragma once");
  440. file_obj.Write_Line ("#endif");
  441. file_obj.Write_Line ("");
  442. file_obj.Write_Line ("#ifndef __STRING_IDS_H");
  443. file_obj.Write_Line ("#define __STRING_IDS_H");
  444. file_obj.Write_Line ("");
  445. StringClass version_line;
  446. version_line.Format ("#define STRINGS_VER %d", m_VersionNumber);
  447. file_obj.Write_Line (version_line);
  448. file_obj.Write_Line ("");
  449. file_obj.Write_Line ("// TRANSLATEDB: Begin ID Block");
  450. //
  451. // Loop over all the translation objects and write a #define to the
  452. // header file for each one...
  453. //
  454. for (int index = 0; index < m_ObjectList.Count (); index ++) {
  455. TDBObjClass *object = m_ObjectList[index];
  456. if (object != NULL) {
  457. //
  458. // Write a #define for this object's ID
  459. //
  460. StringClass id_entry;
  461. id_entry.Format ("#define %s %d", (const char *)object->Get_ID_Desc (), object->Get_ID ());
  462. file_obj.Write_Line (id_entry);
  463. }
  464. }
  465. file_obj.Write_Line ("// TRANSLATEDB: End ID Block");
  466. file_obj.Write_Line ("");
  467. file_obj.Write_Line ("#endif //__STRING_IDS_H");
  468. //
  469. // Close the file
  470. //
  471. file_obj.Detach ();
  472. ::CloseHandle (file);
  473. }
  474. return ;
  475. }
  476. ///////////////////////////////////////////////////////////////////////
  477. //
  478. // Import_C_Header
  479. //
  480. ///////////////////////////////////////////////////////////////////////
  481. void
  482. TranslateDBClass::Import_C_Header (const char *filename)
  483. {
  484. //
  485. // Create the file
  486. //
  487. HANDLE file = ::CreateFile (filename,
  488. GENERIC_READ,
  489. FILE_SHARE_READ,
  490. NULL,
  491. OPEN_EXISTING,
  492. 0L,
  493. NULL);
  494. WWASSERT (file != INVALID_HANDLE_VALUE);
  495. if (file != INVALID_HANDLE_VALUE) {
  496. TextFileClass file_obj;
  497. file_obj.Attach (file);
  498. StringClass line;
  499. bool found_id_block = false;
  500. //
  501. // Look for the start of the ID block
  502. //
  503. while (found_id_block == false && file_obj.Read_Line (line)) {
  504. found_id_block = (line.Compare_No_Case ("// TRANSLATEDB: Begin ID Block") == 0);
  505. }
  506. if (found_id_block) {
  507. //
  508. // Read each ID define from the header file
  509. //
  510. bool found_end_block = false;
  511. while (found_end_block == false && file_obj.Read_Line (line)) {
  512. if (::strnicmp (line, "#define ", 8) == 0) {
  513. //
  514. // Break the #define into its parts
  515. //
  516. int word_breaks[4] = { 0 };
  517. int curr_break = 0;
  518. //
  519. // Find out where each word begins and ends
  520. //
  521. int count = line.Get_Length ();
  522. for (int index = 8; curr_break < 4 && index < count; index ++) {
  523. bool is_whitespace = (line[index] == ' ' || line[index] == '\t');
  524. if (is_whitespace == (curr_break & 1) || index == count - 1) {
  525. word_breaks[curr_break ++] = index;
  526. }
  527. }
  528. //
  529. // Did we encounter the right number of words?
  530. //
  531. if (curr_break == 4) {
  532. //
  533. // Isolate the words
  534. //
  535. char id_desc_text[64] = { 0 };
  536. char id_text[64] = { 0 };
  537. ::strncpy (id_desc_text, line.Peek_Buffer () + word_breaks[0], word_breaks[1] - word_breaks[0]);
  538. ::strncpy (id_text, line.Peek_Buffer () + word_breaks[2], word_breaks[3] - word_breaks[2]);
  539. //
  540. // Convert the ID string to a number
  541. //
  542. uint32 id = ::atoi (id_text);
  543. //
  544. // Do we already have this object?
  545. //
  546. TDBObjClass *object = Find_Object (id);
  547. if (object == NULL) {
  548. object = Find_Object (id_desc_text);
  549. if (object == NULL) {
  550. //
  551. // If we didn't already have this object, then create
  552. // a new object and add it to the system
  553. //
  554. object = new TDBObjClass;
  555. object->Set_ID_Desc (id_desc_text);
  556. Add_Object (object);
  557. }
  558. }
  559. }
  560. } else {
  561. //
  562. // Check for block-end conditions
  563. //
  564. found_end_block = (line.Compare_No_Case ("// TRANSLATEDB: End ID Block") == 0);
  565. }
  566. }
  567. }
  568. //
  569. // Close the file
  570. //
  571. file_obj.Detach ();
  572. ::CloseHandle (file);
  573. }
  574. return ;
  575. }
  576. ///////////////////////////////////////////////////////////////////////
  577. //
  578. // Get_Object_Count
  579. //
  580. ///////////////////////////////////////////////////////////////////////
  581. int
  582. TranslateDBClass::Get_Object_Count (void)
  583. {
  584. return m_ObjectList.Count ();
  585. }
  586. ///////////////////////////////////////////////////////////////////////
  587. //
  588. // Get_Object
  589. //
  590. ///////////////////////////////////////////////////////////////////////
  591. TDBObjClass *
  592. TranslateDBClass::Get_Object (int index)
  593. {
  594. TDBObjClass *object = NULL;
  595. WWASSERT (index >= 0 && index < m_ObjectList.Count ());
  596. if (index >= 0 && index < m_ObjectList.Count ()) {
  597. object = m_ObjectList[index];
  598. }
  599. return object;
  600. }
  601. ///////////////////////////////////////////////////////////////////////
  602. //
  603. // Find_Unique_ID
  604. //
  605. ///////////////////////////////////////////////////////////////////////
  606. uint32
  607. TranslateDBClass::Find_Unique_ID (void)
  608. {
  609. uint32 new_id = ID_MIN + m_ObjectList.Count ();
  610. //
  611. // Return the 'id' of the first empty slot in the list
  612. //
  613. for (int index = 0; index < m_ObjectList.Count (); index ++) {
  614. if (m_ObjectList[index] == NULL) {
  615. new_id = (ID_MIN + index);
  616. break;
  617. }
  618. }
  619. return new_id;
  620. }
  621. ///////////////////////////////////////////////////////////////////////
  622. //
  623. // Add_Category
  624. //
  625. ///////////////////////////////////////////////////////////////////////
  626. TDBCategoryClass *
  627. TranslateDBClass::Add_Category (const char *name)
  628. {
  629. //
  630. // Create the new category
  631. //
  632. TDBCategoryClass *category = new TDBCategoryClass;
  633. category->Set_Name (name);
  634. //
  635. // Add the category to our list and return a pointer to the caller
  636. //
  637. Add_Category (category);
  638. return category;
  639. }
  640. ///////////////////////////////////////////////////////////////////////
  641. //
  642. // Add_Category
  643. //
  644. ///////////////////////////////////////////////////////////////////////
  645. bool
  646. TranslateDBClass::Add_Category (TDBCategoryClass *new_category, bool assign_id)
  647. {
  648. bool retval = false;
  649. WWASSERT (new_category != NULL);
  650. if (new_category != NULL) {
  651. //
  652. // Assign this category an ID (if necessary)
  653. //
  654. if (assign_id && new_category->Get_ID () == 0) {
  655. uint32 new_id = 1;
  656. for (int index = 0; index < m_CategoryList.Count (); index ++) {
  657. uint32 curr_id = m_CategoryList[index]->Get_ID ();
  658. new_id = max (curr_id + 1, new_id);
  659. }
  660. new_category->Set_ID (new_id);
  661. }
  662. //
  663. // Add the new category to our list
  664. //
  665. m_CategoryList.Add (new_category);
  666. retval = true;
  667. }
  668. return retval;
  669. }
  670. ///////////////////////////////////////////////////////////////////////
  671. //
  672. // Remove_Category
  673. //
  674. ///////////////////////////////////////////////////////////////////////
  675. bool
  676. TranslateDBClass::Remove_Category (int index)
  677. {
  678. bool retval = false;
  679. //
  680. // Make sure this index is in our range
  681. //
  682. WWASSERT (index >= 0 && index < m_CategoryList.Count ());
  683. if (index >= 0 && index < m_CategoryList.Count ()) {
  684. //
  685. // Free the object that was in this slot
  686. //
  687. TDBCategoryClass *category = m_CategoryList[index];
  688. if (category != NULL) {
  689. //
  690. // Remove all objects from the category
  691. //
  692. int category_id = category->Get_ID ();
  693. for ( TDBObjClass *object = Get_First_Object (category_id);
  694. object != NULL;
  695. object = Get_Next_Object (category_id, object))
  696. {
  697. object->Set_Category_ID (CATEGORY_DEFAULT);
  698. }
  699. delete category;
  700. }
  701. //
  702. // Remove the entry from our list
  703. //
  704. m_CategoryList.Delete (index);
  705. retval = true;
  706. }
  707. return retval;
  708. }
  709. ///////////////////////////////////////////////////////////////////////
  710. //
  711. // Add_Object
  712. //
  713. ///////////////////////////////////////////////////////////////////////
  714. bool
  715. TranslateDBClass::Add_Object (TDBObjClass *new_obj)
  716. {
  717. bool retval = false;
  718. WWASSERT (new_obj != NULL);
  719. if (new_obj != NULL) {
  720. //
  721. // Try to find an unused ID for the object (if necessary)
  722. //
  723. if (new_obj->Get_ID () < ID_MIN) {
  724. new_obj->Set_ID (Find_Unique_ID ());
  725. }
  726. //
  727. // Determine where in the list this object should
  728. // be inserted
  729. //
  730. int new_id = new_obj->Get_ID ();
  731. int obj_index = new_id - ID_MIN;
  732. //
  733. // Grow the list up to the number of elements we need
  734. //
  735. while (m_ObjectList.Count () <= obj_index) {
  736. m_ObjectList.Add (NULL);
  737. }
  738. //
  739. // Add the object to our list
  740. //
  741. m_ObjectList[obj_index] = new_obj;
  742. // Insert object to the hash table as well...
  743. StringClass lower_case_name(new_obj->Get_ID_Desc(),true);
  744. _strlwr(lower_case_name.Peek_Buffer());
  745. m_ObjectHash.Insert(lower_case_name,new_obj);
  746. retval = true;
  747. }
  748. return retval;
  749. }
  750. ///////////////////////////////////////////////////////////////////////
  751. //
  752. // Remove_Object
  753. //
  754. ///////////////////////////////////////////////////////////////////////
  755. bool
  756. TranslateDBClass::Remove_Object (int index)
  757. {
  758. bool retval = false;
  759. //
  760. // Make sure this index is in our range
  761. //
  762. WWASSERT (index >= 0 && index < m_ObjectList.Count ());
  763. if (index >= 0 && index < m_ObjectList.Count ()) {
  764. //
  765. // Free the object that was in this slot
  766. //
  767. TDBObjClass *object = m_ObjectList[index];
  768. if (object != NULL) {
  769. // Remove the object from the hash table
  770. StringClass lower_case_name(object->Get_ID_Desc(),true);
  771. _strlwr(lower_case_name.Peek_Buffer());
  772. m_ObjectHash.Remove(lower_case_name);
  773. delete object;
  774. }
  775. //
  776. // Remove the pointer from the list and re-assign IDs
  777. //
  778. m_ObjectList[index] = NULL;
  779. retval = true;
  780. }
  781. return retval;
  782. }
  783. //////////////////////////////////////////////////////////////
  784. //
  785. // Remove_All
  786. //
  787. //////////////////////////////////////////////////////////////
  788. void
  789. TranslateDBClass::Remove_All (void)
  790. {
  791. Free_Objects ();
  792. return ;
  793. }
  794. //////////////////////////////////////////////////////////////
  795. //
  796. // Get_Version_Number
  797. //
  798. //////////////////////////////////////////////////////////////
  799. uint32
  800. TranslateDBClass::Get_Version_Number (void)
  801. {
  802. return m_VersionNumber;
  803. }
  804. //////////////////////////////////////////////////////////////
  805. //
  806. // Update_Version
  807. //
  808. //////////////////////////////////////////////////////////////
  809. void
  810. TranslateDBClass::Update_Version (void)
  811. {
  812. m_VersionNumber ++;
  813. return ;
  814. }
  815. ///////////////////////////////////////////////////////////////////////
  816. //
  817. // Get_Category_Count
  818. //
  819. ///////////////////////////////////////////////////////////////////////
  820. int
  821. TranslateDBClass::Get_Category_Count (void)
  822. {
  823. return m_CategoryList.Count ();
  824. }
  825. ///////////////////////////////////////////////////////////////////////
  826. //
  827. // Get_Category
  828. //
  829. ///////////////////////////////////////////////////////////////////////
  830. TDBCategoryClass *
  831. TranslateDBClass::Get_Category (int index)
  832. {
  833. TDBCategoryClass *category = NULL;
  834. WWASSERT (index >= 0 && index < m_CategoryList.Count ());
  835. if (index >= 0 && index < m_CategoryList.Count ()) {
  836. category = m_CategoryList[index];
  837. }
  838. return category;
  839. }
  840. ///////////////////////////////////////////////////////////////////////
  841. //
  842. // Find_Category
  843. //
  844. ///////////////////////////////////////////////////////////////////////
  845. TDBCategoryClass *
  846. TranslateDBClass::Find_Category (uint32 id)
  847. {
  848. TDBCategoryClass *category = NULL;
  849. //
  850. // Loop over all the categories until we've found a matching ID
  851. //
  852. for (int index = 0; index < m_CategoryList.Count (); index ++) {
  853. if (m_CategoryList[index]->Get_ID () == id) {
  854. category = m_CategoryList[index];
  855. break;
  856. }
  857. }
  858. return category;
  859. }
  860. ///////////////////////////////////////////////////////////////////////
  861. //
  862. // Find_Category
  863. //
  864. ///////////////////////////////////////////////////////////////////////
  865. TDBCategoryClass *
  866. TranslateDBClass::Find_Category (const char *name)
  867. {
  868. TDBCategoryClass *category = NULL;
  869. //
  870. // Loop over all the categories until we've found a matching name
  871. //
  872. for (int index = 0; index < m_CategoryList.Count (); index ++) {
  873. if (m_CategoryList[index]->Get_Name ().Compare_No_Case (name) == 0) {
  874. category = m_CategoryList[index];
  875. break;
  876. }
  877. }
  878. return category;
  879. }
  880. ///////////////////////////////////////////////////////////////////////
  881. //
  882. // Get_First_Object
  883. //
  884. ///////////////////////////////////////////////////////////////////////
  885. TDBObjClass *
  886. TranslateDBClass::Get_First_Object (uint32 category_id)
  887. {
  888. TDBObjClass *object = NULL;
  889. //
  890. // Loop over the objects we know about and return the first
  891. // one that matches the given category ID
  892. //
  893. for (int index = 0; index < m_ObjectList.Count (); index ++) {
  894. TDBObjClass *curr_obj = m_ObjectList[index];
  895. if (curr_obj != NULL) {
  896. //
  897. // Does this object belong to the category we are enumerating?
  898. //
  899. if (curr_obj->Get_Category_ID () == category_id) {
  900. object = curr_obj;
  901. break;
  902. }
  903. }
  904. }
  905. return object;
  906. }
  907. ///////////////////////////////////////////////////////////////////////
  908. //
  909. // Get_Next_Object
  910. //
  911. ///////////////////////////////////////////////////////////////////////
  912. TDBObjClass *
  913. TranslateDBClass::Get_Next_Object (uint32 category_id, TDBObjClass *curr_obj)
  914. {
  915. //
  916. // Sanity check
  917. //
  918. if (curr_obj == NULL) {
  919. return NULL;
  920. }
  921. TDBObjClass *object = NULL;
  922. //
  923. // Determine where in the list to start looking
  924. //
  925. int start_index = (curr_obj->Get_ID () - ID_MIN) + 1;
  926. //
  927. // Loop over the objects we know about and return the first
  928. // one that matches the given category ID
  929. //
  930. for (int index = start_index; index < m_ObjectList.Count (); index ++) {
  931. TDBObjClass *curr_obj = m_ObjectList[index];
  932. if (curr_obj != NULL) {
  933. //
  934. // Does this object belong to the category we are enumerating?
  935. //
  936. if (curr_obj->Get_Category_ID () == category_id) {
  937. object = curr_obj;
  938. break;
  939. }
  940. }
  941. }
  942. return object;
  943. }
  944. /////////////////////////////////////////////////////////////////////////////
  945. //
  946. // Convert_Chars_To_Newline
  947. //
  948. /////////////////////////////////////////////////////////////////////////////
  949. void
  950. Convert_Chars_To_Newline (StringClass &string)
  951. {
  952. StringClass retval;
  953. int count = string.Get_Length ();
  954. //
  955. // Copy characters between the strings
  956. //
  957. for (int index = 0; index < count; index ++) {
  958. if (index + 1 < count && string[index] == '\\' && string[index + 1] == 'n') {
  959. retval += '\n';
  960. index ++;
  961. } else {
  962. retval += string[index];
  963. }
  964. }
  965. string = retval;
  966. return ;
  967. }
  968. ///////////////////////////////////////////////////////////////////////
  969. //
  970. // Import_Strings
  971. //
  972. ///////////////////////////////////////////////////////////////////////
  973. void
  974. TranslateDBClass::Import_Strings (const char *filename)
  975. {
  976. //
  977. // Open the file
  978. //
  979. HANDLE file = ::CreateFile ( filename,
  980. GENERIC_READ,
  981. FILE_SHARE_READ,
  982. NULL,
  983. OPEN_EXISTING,
  984. 0L,
  985. NULL);
  986. WWASSERT (file != INVALID_HANDLE_VALUE);
  987. if (file != INVALID_HANDLE_VALUE) {
  988. //
  989. // Attach this file to a text file class for easier parsing
  990. //
  991. TextFileClass file_obj;
  992. file_obj.Attach (file);
  993. //
  994. // Keep reading data from the file until we've reached the end
  995. // of the file or the data becomes corrupt
  996. //
  997. bool keep_going = true;
  998. StringClass line;
  999. while (keep_going && file_obj.Read_Line (line)) {
  1000. //
  1001. // Convert the string to an array of values
  1002. //
  1003. StringClass *value_array = NULL;
  1004. int value_count = Build_List_From_String (line, "\t", &value_array);
  1005. //
  1006. // We can't keep going unless we've got at least 3 columns of information
  1007. //
  1008. if (value_count >= 3) {
  1009. //
  1010. // Lookup the category this entry belongs to
  1011. //
  1012. TDBCategoryClass *category = Find_Category (value_array[0]);
  1013. //
  1014. // Create the entry
  1015. //
  1016. TDBObjClass *new_obj = new TDBObjClass;
  1017. new_obj->Set_Category_ID (category ? category->Get_ID () : 0);
  1018. new_obj->Set_ID_Desc (value_array[1]);
  1019. //
  1020. // Set the english string
  1021. //
  1022. StringClass english_string = value_array[2];
  1023. ::Convert_Chars_To_Newline (english_string);
  1024. new_obj->Set_English_String (english_string);
  1025. //
  1026. // If necessary, lookup the sound definition that goes with this new entry
  1027. // and plug in its ID.
  1028. //
  1029. if (value_count >= 4) {
  1030. DefinitionClass *definition = DefinitionMgrClass::Find_Typed_Definition (value_array[3], CLASSID_SOUND, false);
  1031. if (definition != NULL) {
  1032. new_obj->Set_Sound_ID (definition->Get_ID ());
  1033. }
  1034. }
  1035. //
  1036. // Add this object to the database
  1037. //
  1038. Add_Object (new_obj);
  1039. }
  1040. //
  1041. // Cleanup
  1042. //
  1043. if (value_array != NULL) {
  1044. delete [] value_array;
  1045. value_array = NULL;
  1046. }
  1047. }
  1048. }
  1049. return ;
  1050. }
  1051. ///////////////////////////////////////////////////////////////////////
  1052. //
  1053. // Build_List_From_String
  1054. //
  1055. ///////////////////////////////////////////////////////////////////////
  1056. int Build_List_From_String
  1057. (
  1058. const char * buffer,
  1059. const char * delimiter,
  1060. StringClass ** string_list
  1061. )
  1062. {
  1063. int count = 0;
  1064. WWASSERT (buffer != NULL);
  1065. WWASSERT (delimiter != NULL);
  1066. WWASSERT (string_list != NULL);
  1067. if ((buffer != NULL) &&
  1068. (delimiter != NULL) &&
  1069. (string_list != NULL))
  1070. {
  1071. int delim_len = ::strlen (delimiter);
  1072. //
  1073. // Determine how many entries there will be in the list
  1074. //
  1075. for (const char *entry = buffer;
  1076. (entry != NULL) && (entry[1] != 0);
  1077. entry = ::strstr (entry, delimiter))
  1078. {
  1079. //
  1080. // Move past the current delimiter (if necessary)
  1081. //
  1082. if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
  1083. entry += delim_len;
  1084. }
  1085. // Increment the count of entries
  1086. count ++;
  1087. }
  1088. if (count > 0) {
  1089. //
  1090. // Allocate enough StringClass objects to hold all the strings in the list
  1091. //
  1092. (*string_list) = new StringClass[count];
  1093. //
  1094. // Parse the string and pull out its entries.
  1095. //
  1096. count = 0;
  1097. for (entry = buffer;
  1098. (entry != NULL) && (entry[1] != 0);
  1099. entry = ::strstr (entry, delimiter))
  1100. {
  1101. //
  1102. // Move past the current delimiter (if necessary)
  1103. //
  1104. if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
  1105. entry += delim_len;
  1106. }
  1107. //
  1108. // Copy this entry into its own string
  1109. //
  1110. StringClass entry_string = entry;
  1111. char *delim_start = ::strstr (entry_string, delimiter);
  1112. if (delim_start != NULL) {
  1113. delim_start[0] = 0;
  1114. }
  1115. //
  1116. // Add this entry to our list
  1117. //
  1118. if ((entry_string.Get_Length () > 0) || (count == 0)) {
  1119. (*string_list)[count++] = entry_string;
  1120. }
  1121. }
  1122. } else if (delim_len > 0) {
  1123. count = 1;
  1124. (*string_list) = new StringClass[count];
  1125. (*string_list)[0] = buffer;
  1126. }
  1127. }
  1128. //
  1129. // Return the number of entries in our list
  1130. //
  1131. return count;
  1132. }
  1133. ///////////////////////////////////////////////////////////////////////
  1134. //
  1135. // Set_Export_Filter
  1136. //
  1137. ///////////////////////////////////////////////////////////////////////
  1138. void
  1139. TranslateDBClass::Set_Export_Filter (FILTER_OPT filter, uint32 category_id)
  1140. {
  1141. FilterType = filter;
  1142. FilterCategoryID = category_id;
  1143. return ;
  1144. }