Common_localize.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code 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. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Common_local.h"
  23. idCVar com_product_lang_ext( "com_product_lang_ext", "1", CVAR_INTEGER | CVAR_SYSTEM | CVAR_ARCHIVE, "Extension to use when creating language files." );
  24. /*
  25. =================
  26. LoadMapLocalizeData
  27. =================
  28. */
  29. typedef idHashTable<idStrList> ListHash;
  30. void LoadMapLocalizeData(ListHash& listHash) {
  31. idStr fileName = "map_localize.cfg";
  32. const char *buffer = NULL;
  33. idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
  34. if ( fileSystem->ReadFile( fileName, (void**)&buffer ) > 0 ) {
  35. src.LoadMemory( buffer, strlen(buffer), fileName );
  36. if ( src.IsLoaded() ) {
  37. idStr classname;
  38. idToken token;
  39. while ( src.ReadToken( &token ) ) {
  40. classname = token;
  41. src.ExpectTokenString( "{" );
  42. idStrList list;
  43. while ( src.ReadToken( &token) ) {
  44. if ( token == "}" ) {
  45. break;
  46. }
  47. list.Append(token);
  48. }
  49. listHash.Set(classname, list);
  50. }
  51. }
  52. fileSystem->FreeFile( (void*)buffer );
  53. }
  54. }
  55. void LoadGuiParmExcludeList(idStrList& list) {
  56. idStr fileName = "guiparm_exclude.cfg";
  57. const char *buffer = NULL;
  58. idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
  59. if ( fileSystem->ReadFile( fileName, (void**)&buffer ) > 0 ) {
  60. src.LoadMemory( buffer, strlen(buffer), fileName );
  61. if ( src.IsLoaded() ) {
  62. idStr classname;
  63. idToken token;
  64. while ( src.ReadToken( &token ) ) {
  65. list.Append(token);
  66. }
  67. }
  68. fileSystem->FreeFile( (void*)buffer );
  69. }
  70. }
  71. bool TestMapVal(idStr& str) {
  72. //Already Localized?
  73. if(str.Find("#str_") != -1) {
  74. return false;
  75. }
  76. return true;
  77. }
  78. bool TestGuiParm(const char* parm, const char* value, idStrList& excludeList) {
  79. idStr testVal = value;
  80. //Already Localized?
  81. if(testVal.Find("#str_") != -1) {
  82. return false;
  83. }
  84. //Numeric
  85. if(testVal.IsNumeric()) {
  86. return false;
  87. }
  88. //Contains ::
  89. if(testVal.Find("::") != -1) {
  90. return false;
  91. }
  92. //Contains /
  93. if(testVal.Find("/") != -1) {
  94. return false;
  95. }
  96. if(excludeList.Find(testVal)) {
  97. return false;
  98. }
  99. return true;
  100. }
  101. void GetFileList(const char* dir, const char* ext, idStrList& list) {
  102. //Recurse Subdirectories
  103. idStrList dirList;
  104. Sys_ListFiles(dir, "/", dirList);
  105. for(int i = 0; i < dirList.Num(); i++) {
  106. if(dirList[i] == "." || dirList[i] == "..") {
  107. continue;
  108. }
  109. idStr fullName = va("%s/%s", dir, dirList[i].c_str());
  110. GetFileList(fullName, ext, list);
  111. }
  112. idStrList fileList;
  113. Sys_ListFiles(dir, ext, fileList);
  114. for(int i = 0; i < fileList.Num(); i++) {
  115. idStr fullName = va("%s/%s", dir, fileList[i].c_str());
  116. list.Append(fullName);
  117. }
  118. }
  119. int LocalizeMap(const char* mapName, idLangDict &langDict, ListHash& listHash, idStrList& excludeList, bool writeFile) {
  120. common->Printf("Localizing Map '%s'\n", mapName);
  121. int strCount = 0;
  122. idMapFile map;
  123. if ( map.Parse(mapName, false, false ) ) {
  124. int count = map.GetNumEntities();
  125. for ( int j = 0; j < count; j++ ) {
  126. idMapEntity *ent = map.GetEntity( j );
  127. if ( ent ) {
  128. idStr classname = ent->epairs.GetString("classname");
  129. //Hack: for info_location
  130. bool hasLocation = false;
  131. idStrList* list;
  132. listHash.Get(classname, &list);
  133. if(list) {
  134. for(int k = 0; k < list->Num(); k++) {
  135. idStr val = ent->epairs.GetString((*list)[k], "");
  136. if(val.Length() && classname == "info_location" && (*list)[k] == "location") {
  137. hasLocation = true;
  138. }
  139. if(val.Length() && TestMapVal(val)) {
  140. if(!hasLocation || (*list)[k] == "location") {
  141. //Localize it!!!
  142. strCount++;
  143. ent->epairs.Set( (*list)[k], langDict.AddString( val ) );
  144. }
  145. }
  146. }
  147. }
  148. listHash.Get("all", &list);
  149. if(list) {
  150. for(int k = 0; k < list->Num(); k++) {
  151. idStr val = ent->epairs.GetString((*list)[k], "");
  152. if(val.Length() && TestMapVal(val)) {
  153. //Localize it!!!
  154. strCount++;
  155. ent->epairs.Set( (*list)[k], langDict.AddString( val ) );
  156. }
  157. }
  158. }
  159. //Localize the gui_parms
  160. const idKeyValue* kv = ent->epairs.MatchPrefix("gui_parm");
  161. while( kv ) {
  162. if(TestGuiParm(kv->GetKey(), kv->GetValue(), excludeList)) {
  163. //Localize It!
  164. strCount++;
  165. ent->epairs.Set( kv->GetKey(), langDict.AddString( kv->GetValue() ) );
  166. }
  167. kv = ent->epairs.MatchPrefix( "gui_parm", kv );
  168. }
  169. }
  170. }
  171. if(writeFile && strCount > 0) {
  172. //Before we write the map file lets make a backup of the original
  173. idStr file = fileSystem->RelativePathToOSPath(mapName);
  174. idStr bak = file.Left(file.Length() - 4);
  175. bak.Append(".bak_loc");
  176. fileSystem->CopyFile( file, bak );
  177. map.Write( mapName, ".map" );
  178. }
  179. }
  180. common->Printf("Count: %d\n", strCount);
  181. return strCount;
  182. }
  183. /*
  184. =================
  185. LocalizeMaps_f
  186. =================
  187. */
  188. CONSOLE_COMMAND( localizeMaps, "localize maps", NULL ) {
  189. if ( args.Argc() < 2 ) {
  190. common->Printf( "Usage: localizeMaps <count | dictupdate | all> <map>\n" );
  191. return;
  192. }
  193. int strCount = 0;
  194. bool count = false;
  195. bool dictUpdate = false;
  196. bool write = false;
  197. if ( idStr::Icmp( args.Argv(1), "count" ) == 0 ) {
  198. count = true;
  199. } else if ( idStr::Icmp( args.Argv(1), "dictupdate" ) == 0 ) {
  200. count = true;
  201. dictUpdate = true;
  202. } else if ( idStr::Icmp( args.Argv(1), "all" ) == 0 ) {
  203. count = true;
  204. dictUpdate = true;
  205. write = true;
  206. } else {
  207. common->Printf( "Invalid Command\n" );
  208. common->Printf( "Usage: localizeMaps <count | dictupdate | all>\n" );
  209. return;
  210. }
  211. idLangDict strTable;
  212. idStr filename = va("strings/english%.3i.lang", com_product_lang_ext.GetInteger());
  213. {
  214. // I think this is equivalent...
  215. const byte * buffer = NULL;
  216. int len = fileSystem->ReadFile( filename, (void**)&buffer );
  217. if ( verify( len > 0 ) ) {
  218. strTable.Load( buffer, len, filename );
  219. }
  220. fileSystem->FreeFile( (void *)buffer );
  221. // ... to this
  222. //if ( strTable.Load( filename ) == false) {
  223. // //This is a new file so set the base index
  224. // strTable.SetBaseID(com_product_lang_ext.GetInteger()*100000);
  225. //}
  226. }
  227. common->SetRefreshOnPrint( true );
  228. ListHash listHash;
  229. LoadMapLocalizeData(listHash);
  230. idStrList excludeList;
  231. LoadGuiParmExcludeList(excludeList);
  232. if(args.Argc() == 3) {
  233. strCount += LocalizeMap(args.Argv(2), strTable, listHash, excludeList, write);
  234. } else {
  235. idStrList files;
  236. GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files);
  237. for ( int i = 0; i < files.Num(); i++ ) {
  238. idStr file = fileSystem->OSPathToRelativePath(files[i]);
  239. strCount += LocalizeMap(file, strTable, listHash, excludeList, write);
  240. }
  241. }
  242. if(count) {
  243. common->Printf("Localize String Count: %d\n", strCount);
  244. }
  245. common->SetRefreshOnPrint( false );
  246. if(dictUpdate) {
  247. strTable.Save( filename );
  248. }
  249. }
  250. /*
  251. =================
  252. LocalizeGuis_f
  253. =================
  254. */
  255. CONSOLE_COMMAND( localizeGuis, "localize guis", NULL ) {
  256. if ( args.Argc() != 2 ) {
  257. common->Printf( "Usage: localizeGuis <all | gui>\n" );
  258. return;
  259. }
  260. idLangDict strTable;
  261. idStr filename = va("strings/english%.3i.lang", com_product_lang_ext.GetInteger());
  262. {
  263. // I think this is equivalent...
  264. const byte * buffer = NULL;
  265. int len = fileSystem->ReadFile( filename, (void**)&buffer );
  266. if ( verify( len > 0 ) ) {
  267. strTable.Load( buffer, len, filename );
  268. }
  269. fileSystem->FreeFile( (void *)buffer );
  270. // ... to this
  271. //if(strTable.Load( filename ) == false) {
  272. // //This is a new file so set the base index
  273. // strTable.SetBaseID(com_product_lang_ext.GetInteger()*100000);
  274. //}
  275. }
  276. idFileList *files;
  277. if ( idStr::Icmp( args.Argv(1), "all" ) == 0 ) {
  278. idStr game = cvarSystem->GetCVarString( "game_expansion" );
  279. if(game.Length()) {
  280. files = fileSystem->ListFilesTree( "guis", "*.gui", true, game );
  281. } else {
  282. files = fileSystem->ListFilesTree( "guis", "*.gui", true );
  283. }
  284. for ( int i = 0; i < files->GetNumFiles(); i++ ) {
  285. commonLocal.LocalizeGui( files->GetFile( i ), strTable );
  286. }
  287. fileSystem->FreeFileList( files );
  288. if(game.Length()) {
  289. files = fileSystem->ListFilesTree( "guis", "*.pd", true, game );
  290. } else {
  291. files = fileSystem->ListFilesTree( "guis", "*.pd", true, "d3xp" );
  292. }
  293. for ( int i = 0; i < files->GetNumFiles(); i++ ) {
  294. commonLocal.LocalizeGui( files->GetFile( i ), strTable );
  295. }
  296. fileSystem->FreeFileList( files );
  297. } else {
  298. commonLocal.LocalizeGui( args.Argv(1), strTable );
  299. }
  300. strTable.Save( filename );
  301. }
  302. CONSOLE_COMMAND( localizeGuiParmsTest, "Create test files that show gui parms localized and ignored.", NULL ) {
  303. common->SetRefreshOnPrint( true );
  304. idFile *localizeFile = fileSystem->OpenFileWrite( "gui_parm_localize.csv" );
  305. idFile *noLocalizeFile = fileSystem->OpenFileWrite( "gui_parm_nolocalize.csv" );
  306. idStrList excludeList;
  307. LoadGuiParmExcludeList(excludeList);
  308. idStrList files;
  309. GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files);
  310. for ( int i = 0; i < files.Num(); i++ ) {
  311. common->Printf("Testing Map '%s'\n", files[i].c_str());
  312. idMapFile map;
  313. idStr file = fileSystem->OSPathToRelativePath(files[i]);
  314. if ( map.Parse(file, false, false ) ) {
  315. int count = map.GetNumEntities();
  316. for ( int j = 0; j < count; j++ ) {
  317. idMapEntity *ent = map.GetEntity( j );
  318. if ( ent ) {
  319. const idKeyValue* kv = ent->epairs.MatchPrefix("gui_parm");
  320. while( kv ) {
  321. if(TestGuiParm(kv->GetKey(), kv->GetValue(), excludeList)) {
  322. idStr out = va("%s,%s,%s\r\n", kv->GetValue().c_str(), kv->GetKey().c_str(), file.c_str());
  323. localizeFile->Write( out.c_str(), out.Length() );
  324. } else {
  325. idStr out = va("%s,%s,%s\r\n", kv->GetValue().c_str(), kv->GetKey().c_str(), file.c_str());
  326. noLocalizeFile->Write( out.c_str(), out.Length() );
  327. }
  328. kv = ent->epairs.MatchPrefix( "gui_parm", kv );
  329. }
  330. }
  331. }
  332. }
  333. }
  334. fileSystem->CloseFile( localizeFile );
  335. fileSystem->CloseFile( noLocalizeFile );
  336. common->SetRefreshOnPrint( false );
  337. }
  338. CONSOLE_COMMAND( localizeMapsTest, "Create test files that shows which strings will be localized.", NULL ) {
  339. ListHash listHash;
  340. LoadMapLocalizeData(listHash);
  341. common->SetRefreshOnPrint( true );
  342. idFile *localizeFile = fileSystem->OpenFileWrite( "map_localize.csv" );
  343. idStrList files;
  344. GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files);
  345. for ( int i = 0; i < files.Num(); i++ ) {
  346. common->Printf("Testing Map '%s'\n", files[i].c_str());
  347. idMapFile map;
  348. idStr file = fileSystem->OSPathToRelativePath(files[i]);
  349. if ( map.Parse(file, false, false ) ) {
  350. int count = map.GetNumEntities();
  351. for ( int j = 0; j < count; j++ ) {
  352. idMapEntity *ent = map.GetEntity( j );
  353. if ( ent ) {
  354. //Temp code to get a list of all entity key value pairs
  355. /*idStr classname = ent->epairs.GetString("classname");
  356. if(classname == "worldspawn" || classname == "func_static" || classname == "light" || classname == "speaker" || classname.Left(8) == "trigger_") {
  357. continue;
  358. }
  359. for( int i = 0; i < ent->epairs.GetNumKeyVals(); i++) {
  360. const idKeyValue* kv = ent->epairs.GetKeyVal(i);
  361. idStr out = va("%s,%s,%s,%s\r\n", classname.c_str(), kv->GetKey().c_str(), kv->GetValue().c_str(), file.c_str());
  362. localizeFile->Write( out.c_str(), out.Length() );
  363. }*/
  364. idStr classname = ent->epairs.GetString("classname");
  365. //Hack: for info_location
  366. bool hasLocation = false;
  367. idStrList* list;
  368. listHash.Get(classname, &list);
  369. if(list) {
  370. for(int k = 0; k < list->Num(); k++) {
  371. idStr val = ent->epairs.GetString((*list)[k], "");
  372. if(classname == "info_location" && (*list)[k] == "location") {
  373. hasLocation = true;
  374. }
  375. if(val.Length() && TestMapVal(val)) {
  376. if(!hasLocation || (*list)[k] == "location") {
  377. idStr out = va("%s,%s,%s\r\n", val.c_str(), (*list)[k].c_str(), file.c_str());
  378. localizeFile->Write( out.c_str(), out.Length() );
  379. }
  380. }
  381. }
  382. }
  383. listHash.Get("all", &list);
  384. if(list) {
  385. for(int k = 0; k < list->Num(); k++) {
  386. idStr val = ent->epairs.GetString((*list)[k], "");
  387. if(val.Length() && TestMapVal(val)) {
  388. idStr out = va("%s,%s,%s\r\n", val.c_str(), (*list)[k].c_str(), file.c_str());
  389. localizeFile->Write( out.c_str(), out.Length() );
  390. }
  391. }
  392. }
  393. }
  394. }
  395. }
  396. }
  397. fileSystem->CloseFile( localizeFile );
  398. common->SetRefreshOnPrint( false );
  399. }
  400. /*
  401. ===============
  402. idCommonLocal::LocalizeSpecificMapData
  403. ===============
  404. */
  405. void idCommonLocal::LocalizeSpecificMapData( const char *fileName, idLangDict &langDict, const idLangDict &replaceArgs ) {
  406. idStr out, ws, work;
  407. idMapFile map;
  408. if ( map.Parse( fileName, false, false ) ) {
  409. int count = map.GetNumEntities();
  410. for ( int i = 0; i < count; i++ ) {
  411. idMapEntity *ent = map.GetEntity( i );
  412. if ( ent ) {
  413. for ( int j = 0; j < replaceArgs.GetNumKeyVals(); j++ ) {
  414. const idLangKeyValue *kv = replaceArgs.GetKeyVal( j );
  415. const char *temp = ent->epairs.GetString( kv->key );
  416. if ( ( temp != NULL ) && *temp ) {
  417. idStr val = kv->value;
  418. if ( val == temp ) {
  419. ent->epairs.Set( kv->key, langDict.AddString( temp ) );
  420. }
  421. }
  422. }
  423. }
  424. }
  425. map.Write( fileName, ".map" );
  426. }
  427. }
  428. /*
  429. ===============
  430. idCommonLocal::LocalizeMapData
  431. ===============
  432. */
  433. void idCommonLocal::LocalizeMapData( const char *fileName, idLangDict &langDict ) {
  434. const char *buffer = NULL;
  435. idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
  436. common->SetRefreshOnPrint( true );
  437. if ( fileSystem->ReadFile( fileName, (void**)&buffer ) > 0 ) {
  438. src.LoadMemory( buffer, strlen(buffer), fileName );
  439. if ( src.IsLoaded() ) {
  440. common->Printf( "Processing %s\n", fileName );
  441. idStr mapFileName;
  442. idToken token, token2;
  443. idLangDict replaceArgs;
  444. while ( src.ReadToken( &token ) ) {
  445. mapFileName = token;
  446. replaceArgs.Clear();
  447. src.ExpectTokenString( "{" );
  448. while ( src.ReadToken( &token) ) {
  449. if ( token == "}" ) {
  450. break;
  451. }
  452. if ( src.ReadToken( &token2 ) ) {
  453. if ( token2 == "}" ) {
  454. break;
  455. }
  456. replaceArgs.AddKeyVal( token, token2 );
  457. }
  458. }
  459. common->Printf( " localizing map %s...\n", mapFileName.c_str() );
  460. LocalizeSpecificMapData( mapFileName, langDict, replaceArgs );
  461. }
  462. }
  463. fileSystem->FreeFile( (void*)buffer );
  464. }
  465. common->SetRefreshOnPrint( false );
  466. }
  467. /*
  468. ===============
  469. idCommonLocal::LocalizeGui
  470. ===============
  471. */
  472. void idCommonLocal::LocalizeGui( const char *fileName, idLangDict &langDict ) {
  473. idStr out, ws, work;
  474. const char *buffer = NULL;
  475. out.Empty();
  476. int k;
  477. char ch;
  478. char slash = '\\';
  479. char tab = 't';
  480. char nl = 'n';
  481. idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
  482. if ( fileSystem->ReadFile( fileName, (void**)&buffer ) > 0 ) {
  483. src.LoadMemory( buffer, strlen(buffer), fileName );
  484. if ( src.IsLoaded() ) {
  485. idFile *outFile = fileSystem->OpenFileWrite( fileName );
  486. common->Printf( "Processing %s\n", fileName );
  487. const bool captureToImage = false;
  488. UpdateScreen( captureToImage );
  489. idToken token;
  490. while( src.ReadToken( &token ) ) {
  491. src.GetLastWhiteSpace( ws );
  492. out += ws;
  493. if ( token.type == TT_STRING ) {
  494. out += va( "\"%s\"", token.c_str() );
  495. } else {
  496. out += token;
  497. }
  498. if ( out.Length() > 200000 ) {
  499. outFile->Write( out.c_str(), out.Length() );
  500. out = "";
  501. }
  502. work = token.Right( 6 );
  503. if ( token.Icmp( "text" ) == 0 || work.Icmp( "::text" ) == 0 || token.Icmp( "choices" ) == 0 ) {
  504. if ( src.ReadToken( &token ) ) {
  505. // see if already exists, if so save that id to this position in this file
  506. // otherwise add this to the list and save the id to this position in this file
  507. src.GetLastWhiteSpace( ws );
  508. out += ws;
  509. token = langDict.AddString( token );
  510. out += "\"";
  511. for ( k = 0; k < token.Length(); k++ ) {
  512. ch = token[k];
  513. if ( ch == '\t' ) {
  514. out += slash;
  515. out += tab;
  516. } else if ( ch == '\n' || ch == '\r' ) {
  517. out += slash;
  518. out += nl;
  519. } else {
  520. out += ch;
  521. }
  522. }
  523. out += "\"";
  524. }
  525. } else if ( token.Icmp( "comment" ) == 0 ) {
  526. if ( src.ReadToken( &token ) ) {
  527. // need to write these out by hand to preserve any \n's
  528. // see if already exists, if so save that id to this position in this file
  529. // otherwise add this to the list and save the id to this position in this file
  530. src.GetLastWhiteSpace( ws );
  531. out += ws;
  532. out += "\"";
  533. for ( k = 0; k < token.Length(); k++ ) {
  534. ch = token[k];
  535. if ( ch == '\t' ) {
  536. out += slash;
  537. out += tab;
  538. } else if ( ch == '\n' || ch == '\r' ) {
  539. out += slash;
  540. out += nl;
  541. } else {
  542. out += ch;
  543. }
  544. }
  545. out += "\"";
  546. }
  547. }
  548. }
  549. outFile->Write( out.c_str(), out.Length() );
  550. fileSystem->CloseFile( outFile );
  551. }
  552. fileSystem->FreeFile( (void*)buffer );
  553. }
  554. }