Dict.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944
  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 "precompiled.h"
  21. #pragma hdrstop
  22. idStrPool idDict::globalKeys;
  23. idStrPool idDict::globalValues;
  24. /*
  25. ================
  26. idDict::operator=
  27. clear existing key/value pairs and copy all key/value pairs from other
  28. ================
  29. */
  30. idDict &idDict::operator=( const idDict &other ) {
  31. int i;
  32. // check for assignment to self
  33. if ( this == &other ) {
  34. return *this;
  35. }
  36. Clear();
  37. args = other.args;
  38. argHash = other.argHash;
  39. for ( i = 0; i < args.Num(); i++ ) {
  40. args[i].key = globalKeys.CopyString( args[i].key );
  41. args[i].value = globalValues.CopyString( args[i].value );
  42. }
  43. return *this;
  44. }
  45. /*
  46. ================
  47. idDict::Copy
  48. copy all key value pairs without removing existing key/value pairs not present in the other dict
  49. ================
  50. */
  51. void idDict::Copy( const idDict &other ) {
  52. int i, n, *found;
  53. idKeyValue kv;
  54. // check for assignment to self
  55. if ( this == &other ) {
  56. return;
  57. }
  58. n = other.args.Num();
  59. if ( args.Num() ) {
  60. found = (int *) _alloca16( other.args.Num() * sizeof( int ) );
  61. for ( i = 0; i < n; i++ ) {
  62. found[i] = FindKeyIndex( other.args[i].GetKey() );
  63. }
  64. } else {
  65. found = NULL;
  66. }
  67. for ( i = 0; i < n; i++ ) {
  68. if ( found && found[i] != -1 ) {
  69. // first set the new value and then free the old value to allow proper self copying
  70. const idPoolStr *oldValue = args[found[i]].value;
  71. args[found[i]].value = globalValues.CopyString( other.args[i].value );
  72. globalValues.FreeString( oldValue );
  73. } else {
  74. kv.key = globalKeys.CopyString( other.args[i].key );
  75. kv.value = globalValues.CopyString( other.args[i].value );
  76. argHash.Add( argHash.GenerateKey( kv.GetKey(), false ), args.Append( kv ) );
  77. }
  78. }
  79. }
  80. /*
  81. ================
  82. idDict::TransferKeyValues
  83. clear existing key/value pairs and transfer key/value pairs from other
  84. ================
  85. */
  86. void idDict::TransferKeyValues( idDict &other ) {
  87. int i, n;
  88. if ( this == &other ) {
  89. return;
  90. }
  91. if ( other.args.Num() && other.args[0].key->GetPool() != &globalKeys ) {
  92. common->FatalError( "idDict::TransferKeyValues: can't transfer values across a DLL boundary" );
  93. return;
  94. }
  95. Clear();
  96. n = other.args.Num();
  97. args.SetNum( n );
  98. for ( i = 0; i < n; i++ ) {
  99. args[i].key = other.args[i].key;
  100. args[i].value = other.args[i].value;
  101. }
  102. argHash = other.argHash;
  103. other.args.Clear();
  104. other.argHash.Free();
  105. }
  106. /*
  107. ================
  108. idDict::Parse
  109. ================
  110. */
  111. bool idDict::Parse( idParser &parser ) {
  112. idToken token;
  113. idToken token2;
  114. bool errors;
  115. errors = false;
  116. parser.ExpectTokenString( "{" );
  117. parser.ReadToken( &token );
  118. while( ( token.type != TT_PUNCTUATION ) || ( token != "}" ) ) {
  119. if ( token.type != TT_STRING ) {
  120. parser.Error( "Expected quoted string, but found '%s'", token.c_str() );
  121. }
  122. if ( !parser.ReadToken( &token2 ) ) {
  123. parser.Error( "Unexpected end of file" );
  124. }
  125. if ( FindKey( token ) ) {
  126. parser.Warning( "'%s' already defined", token.c_str() );
  127. errors = true;
  128. }
  129. Set( token, token2 );
  130. if ( !parser.ReadToken( &token ) ) {
  131. parser.Error( "Unexpected end of file" );
  132. }
  133. }
  134. return !errors;
  135. }
  136. /*
  137. ================
  138. idDict::SetDefaults
  139. ================
  140. */
  141. void idDict::SetDefaults( const idDict *dict ) {
  142. int i, n;
  143. const idKeyValue *kv, *def;
  144. idKeyValue newkv;
  145. n = dict->args.Num();
  146. for( i = 0; i < n; i++ ) {
  147. def = &dict->args[i];
  148. kv = FindKey( def->GetKey() );
  149. if ( !kv ) {
  150. newkv.key = globalKeys.CopyString( def->key );
  151. newkv.value = globalValues.CopyString( def->value );
  152. argHash.Add( argHash.GenerateKey( newkv.GetKey(), false ), args.Append( newkv ) );
  153. }
  154. }
  155. }
  156. /*
  157. ================
  158. idDict::Clear
  159. ================
  160. */
  161. void idDict::Clear() {
  162. int i;
  163. for( i = 0; i < args.Num(); i++ ) {
  164. globalKeys.FreeString( args[i].key );
  165. globalValues.FreeString( args[i].value );
  166. }
  167. args.Clear();
  168. argHash.Free();
  169. }
  170. /*
  171. ================
  172. idDict::Print
  173. ================
  174. */
  175. void idDict::Print() const {
  176. int i;
  177. int n;
  178. n = args.Num();
  179. for( i = 0; i < n; i++ ) {
  180. idLib::common->Printf( "%s = %s\n", args[i].GetKey().c_str(), args[i].GetValue().c_str() );
  181. }
  182. }
  183. int KeyCompare( const idKeyValue *a, const idKeyValue *b ) {
  184. return idStr::Cmp( a->GetKey(), b->GetKey() );
  185. }
  186. /*
  187. ================
  188. idDict::Checksum
  189. ================
  190. */
  191. int idDict::Checksum() const {
  192. unsigned long ret;
  193. int i, n;
  194. idList<idKeyValue> sorted = args;
  195. sorted.SortWithTemplate( idSort_KeyValue() );
  196. n = sorted.Num();
  197. CRC32_InitChecksum( ret );
  198. for( i = 0; i < n; i++ ) {
  199. CRC32_UpdateChecksum( ret, sorted[i].GetKey().c_str(), sorted[i].GetKey().Length() );
  200. CRC32_UpdateChecksum( ret, sorted[i].GetValue().c_str(), sorted[i].GetValue().Length() );
  201. }
  202. CRC32_FinishChecksum( ret );
  203. return ret;
  204. }
  205. /*
  206. ================
  207. idDict::Allocated
  208. ================
  209. */
  210. size_t idDict::Allocated() const {
  211. int i;
  212. size_t size;
  213. size = args.Allocated() + argHash.Allocated();
  214. for( i = 0; i < args.Num(); i++ ) {
  215. size += args[i].Size();
  216. }
  217. return size;
  218. }
  219. /*
  220. ================
  221. idDict::Set
  222. ================
  223. */
  224. void idDict::Set( const char *key, const char *value ) {
  225. int i;
  226. idKeyValue kv;
  227. if ( key == NULL || key[0] == '\0' ) {
  228. return;
  229. }
  230. i = FindKeyIndex( key );
  231. if ( i != -1 ) {
  232. // first set the new value and then free the old value to allow proper self copying
  233. const idPoolStr *oldValue = args[i].value;
  234. args[i].value = globalValues.AllocString( value );
  235. globalValues.FreeString( oldValue );
  236. } else {
  237. kv.key = globalKeys.AllocString( key );
  238. kv.value = globalValues.AllocString( value );
  239. argHash.Add( argHash.GenerateKey( kv.GetKey(), false ), args.Append( kv ) );
  240. }
  241. }
  242. /*
  243. ================
  244. idDict::GetFloat
  245. ================
  246. */
  247. bool idDict::GetFloat( const char *key, const char *defaultString, float &out ) const {
  248. const char *s;
  249. bool found;
  250. found = GetString( key, defaultString, &s );
  251. out = atof( s );
  252. return found;
  253. }
  254. /*
  255. ================
  256. idDict::GetInt
  257. ================
  258. */
  259. bool idDict::GetInt( const char *key, const char *defaultString, int &out ) const {
  260. const char *s;
  261. bool found;
  262. found = GetString( key, defaultString, &s );
  263. out = atoi( s );
  264. return found;
  265. }
  266. /*
  267. ================
  268. idDict::GetBool
  269. ================
  270. */
  271. bool idDict::GetBool( const char *key, const char *defaultString, bool &out ) const {
  272. const char *s;
  273. bool found;
  274. found = GetString( key, defaultString, &s );
  275. out = ( atoi( s ) != 0 );
  276. return found;
  277. }
  278. /*
  279. ================
  280. idDict::GetFloat
  281. ================
  282. */
  283. bool idDict::GetFloat( const char *key, const float defaultFloat, float &out ) const {
  284. const idKeyValue *kv = FindKey( key );
  285. if ( kv ) {
  286. out = atof( kv->GetValue() );
  287. return true;
  288. } else {
  289. out = defaultFloat;
  290. return false;
  291. }
  292. }
  293. /*
  294. ================
  295. idDict::GetInt
  296. ================
  297. */
  298. bool idDict::GetInt( const char *key, const int defaultInt, int &out ) const {
  299. const idKeyValue *kv = FindKey( key );
  300. if ( kv ) {
  301. out = atoi( kv->GetValue() );
  302. return true;
  303. } else {
  304. out = defaultInt;
  305. return false;
  306. }
  307. }
  308. /*
  309. ================
  310. idDict::GetBool
  311. ================
  312. */
  313. bool idDict::GetBool( const char *key, const bool defaultBool, bool &out ) const {
  314. const idKeyValue *kv = FindKey( key );
  315. if ( kv ) {
  316. out = ( atoi( kv->GetValue() ) != 0 );
  317. return true;
  318. } else {
  319. out = defaultBool;
  320. return false;
  321. }
  322. }
  323. /*
  324. ================
  325. idDict::GetAngles
  326. ================
  327. */
  328. bool idDict::GetAngles( const char *key, const char *defaultString, idAngles &out ) const {
  329. bool found;
  330. const char *s;
  331. if ( !defaultString ) {
  332. defaultString = "0 0 0";
  333. }
  334. found = GetString( key, defaultString, &s );
  335. out.Zero();
  336. sscanf( s, "%f %f %f", &out.pitch, &out.yaw, &out.roll );
  337. return found;
  338. }
  339. /*
  340. ================
  341. idDict::GetVector
  342. ================
  343. */
  344. bool idDict::GetVector( const char *key, const char *defaultString, idVec3 &out ) const {
  345. bool found;
  346. const char *s;
  347. if ( !defaultString ) {
  348. defaultString = "0 0 0";
  349. }
  350. found = GetString( key, defaultString, &s );
  351. out.Zero();
  352. sscanf( s, "%f %f %f", &out.x, &out.y, &out.z );
  353. return found;
  354. }
  355. /*
  356. ================
  357. idDict::GetVec2
  358. ================
  359. */
  360. bool idDict::GetVec2( const char *key, const char *defaultString, idVec2 &out ) const {
  361. bool found;
  362. const char *s;
  363. if ( !defaultString ) {
  364. defaultString = "0 0";
  365. }
  366. found = GetString( key, defaultString, &s );
  367. out.Zero();
  368. sscanf( s, "%f %f", &out.x, &out.y );
  369. return found;
  370. }
  371. /*
  372. ================
  373. idDict::GetVec4
  374. ================
  375. */
  376. bool idDict::GetVec4( const char *key, const char *defaultString, idVec4 &out ) const {
  377. bool found;
  378. const char *s;
  379. if ( !defaultString ) {
  380. defaultString = "0 0 0 0";
  381. }
  382. found = GetString( key, defaultString, &s );
  383. out.Zero();
  384. sscanf( s, "%f %f %f %f", &out.x, &out.y, &out.z, &out.w );
  385. return found;
  386. }
  387. /*
  388. ================
  389. idDict::GetMatrix
  390. ================
  391. */
  392. bool idDict::GetMatrix( const char *key, const char *defaultString, idMat3 &out ) const {
  393. const char *s;
  394. bool found;
  395. if ( !defaultString ) {
  396. defaultString = "1 0 0 0 1 0 0 0 1";
  397. }
  398. found = GetString( key, defaultString, &s );
  399. out.Identity(); // sccanf has a bug in it on Mac OS 9. Sigh.
  400. sscanf( s, "%f %f %f %f %f %f %f %f %f", &out[0].x, &out[0].y, &out[0].z, &out[1].x, &out[1].y, &out[1].z, &out[2].x, &out[2].y, &out[2].z );
  401. return found;
  402. }
  403. /*
  404. ================
  405. WriteString
  406. ================
  407. */
  408. static void WriteString( const char *s, idFile *f ) {
  409. int len = strlen( s );
  410. if ( len >= MAX_STRING_CHARS-1 ) {
  411. idLib::common->Error( "idDict::WriteToFileHandle: bad string" );
  412. }
  413. f->Write( s, strlen(s) + 1 );
  414. }
  415. /*
  416. ================
  417. idDict::FindKey
  418. ================
  419. */
  420. const idKeyValue *idDict::FindKey( const char *key ) const {
  421. int i, hash;
  422. if ( key == NULL || key[0] == '\0' ) {
  423. idLib::common->DWarning( "idDict::FindKey: empty key" );
  424. return NULL;
  425. }
  426. hash = argHash.GenerateKey( key, false );
  427. for ( i = argHash.First( hash ); i != -1; i = argHash.Next( i ) ) {
  428. if ( args[i].GetKey().Icmp( key ) == 0 ) {
  429. return &args[i];
  430. }
  431. }
  432. return NULL;
  433. }
  434. /*
  435. ================
  436. idDict::FindKeyIndex
  437. ================
  438. */
  439. int idDict::FindKeyIndex( const char *key ) const {
  440. if ( key == NULL || key[0] == '\0' ) {
  441. idLib::common->DWarning( "idDict::FindKeyIndex: empty key" );
  442. return 0;
  443. }
  444. int hash = argHash.GenerateKey( key, false );
  445. for ( int i = argHash.First( hash ); i != -1; i = argHash.Next( i ) ) {
  446. if ( args[i].GetKey().Icmp( key ) == 0 ) {
  447. return i;
  448. }
  449. }
  450. return -1;
  451. }
  452. /*
  453. ================
  454. idDict::Delete
  455. ================
  456. */
  457. void idDict::Delete( const char *key ) {
  458. int hash, i;
  459. hash = argHash.GenerateKey( key, false );
  460. for ( i = argHash.First( hash ); i != -1; i = argHash.Next( i ) ) {
  461. if ( args[i].GetKey().Icmp( key ) == 0 ) {
  462. globalKeys.FreeString( args[i].key );
  463. globalValues.FreeString( args[i].value );
  464. args.RemoveIndex( i );
  465. argHash.RemoveIndex( hash, i );
  466. break;
  467. }
  468. }
  469. #if 0
  470. // make sure all keys can still be found in the hash index
  471. for ( i = 0; i < args.Num(); i++ ) {
  472. assert( FindKey( args[i].GetKey() ) != NULL );
  473. }
  474. #endif
  475. }
  476. /*
  477. ================
  478. idDict::MatchPrefix
  479. ================
  480. */
  481. const idKeyValue *idDict::MatchPrefix( const char *prefix, const idKeyValue *lastMatch ) const {
  482. int i;
  483. int len;
  484. int start;
  485. assert( prefix );
  486. len = strlen( prefix );
  487. start = -1;
  488. if ( lastMatch ) {
  489. start = args.FindIndex( *lastMatch );
  490. assert( start >= 0 );
  491. if ( start < 1 ) {
  492. start = 0;
  493. }
  494. }
  495. for( i = start + 1; i < args.Num(); i++ ) {
  496. if ( !args[i].GetKey().Icmpn( prefix, len ) ) {
  497. return &args[i];
  498. }
  499. }
  500. return NULL;
  501. }
  502. /*
  503. ================
  504. idDict::RandomPrefix
  505. ================
  506. */
  507. const char *idDict::RandomPrefix( const char *prefix, idRandom &random ) const {
  508. int count;
  509. const int MAX_RANDOM_KEYS = 2048;
  510. const char *list[MAX_RANDOM_KEYS];
  511. const idKeyValue *kv;
  512. list[0] = "";
  513. for ( count = 0, kv = MatchPrefix( prefix ); kv != NULL && count < MAX_RANDOM_KEYS; kv = MatchPrefix( prefix, kv ) ) {
  514. list[count++] = kv->GetValue().c_str();
  515. }
  516. return list[random.RandomInt( count )];
  517. }
  518. /*
  519. ================
  520. idDict::WriteToFileHandle
  521. ================
  522. */
  523. void idDict::WriteToFileHandle( idFile *f ) const {
  524. int c = LittleLong( args.Num() );
  525. f->Write( &c, sizeof( c ) );
  526. for ( int i = 0; i < args.Num(); i++ ) { // don't loop on the swapped count use the original
  527. WriteString( args[i].GetKey().c_str(), f );
  528. WriteString( args[i].GetValue().c_str(), f );
  529. }
  530. }
  531. /*
  532. ================
  533. ReadString
  534. ================
  535. */
  536. static idStr ReadString( idFile *f ) {
  537. char str[MAX_STRING_CHARS];
  538. int len;
  539. for ( len = 0; len < MAX_STRING_CHARS; len++ ) {
  540. f->Read( (void *)&str[len], 1 );
  541. if ( str[len] == 0 ) {
  542. break;
  543. }
  544. }
  545. if ( len == MAX_STRING_CHARS ) {
  546. idLib::common->Error( "idDict::ReadFromFileHandle: bad string" );
  547. }
  548. return idStr( str );
  549. }
  550. /*
  551. ================
  552. idDict::ReadFromFileHandle
  553. ================
  554. */
  555. void idDict::ReadFromFileHandle( idFile *f ) {
  556. int c;
  557. idStr key, val;
  558. Clear();
  559. f->Read( &c, sizeof( c ) );
  560. c = LittleLong( c );
  561. for ( int i = 0; i < c; i++ ) {
  562. key = ReadString( f );
  563. val = ReadString( f );
  564. Set( key, val );
  565. }
  566. }
  567. /*
  568. ========================
  569. idDict::Serialize
  570. ========================
  571. */
  572. void idDict::Serialize( idSerializer & ser ) {
  573. if ( ser.IsReading() ) {
  574. Clear();
  575. }
  576. int num = args.Num();
  577. ser.SerializePacked( num );
  578. for ( int i = 0; i < num; i++ ) {
  579. idStr key;
  580. idStr val;
  581. if ( ser.IsWriting() ) {
  582. key = args[i].GetKey();
  583. val = args[i].GetValue();
  584. }
  585. ser.SerializeString( key );
  586. ser.SerializeString( val );
  587. if ( ser.IsReading() ) {
  588. Set( key.c_str(), val.c_str() );
  589. }
  590. }
  591. }
  592. /*
  593. ================
  594. idDict::WriteToIniFile
  595. ================
  596. */
  597. void idDict::WriteToIniFile( idFile * f ) const {
  598. // make a copy so we don't affect the checksum of the original dict
  599. idList< idKeyValue > sortedArgs( args );
  600. sortedArgs.SortWithTemplate( idSort_KeyValue() );
  601. idList< idStr > prefixList;
  602. idTempArray< int > prefixIndex( sortedArgs.Num() ); // for each keyValue in the args, this is an index into which prefix it uses.
  603. // 0 means no prefix, otherwise, it's an index + (-1) into prefixList
  604. // we do this so we can print all the non-prefix based pairs first
  605. idStr prevPrefix = "";
  606. idStr skipFirstLine = "";
  607. // Scan for all the prefixes
  608. for ( int i = 0; i < sortedArgs.Num(); i++ ) {
  609. const idKeyValue * kv = &sortedArgs[i];
  610. int slashPosition = kv->GetKey().Last( '/' );
  611. if ( slashPosition != idStr::INVALID_POSITION ) {
  612. idStr prefix = kv->GetKey().Mid( 0, slashPosition );
  613. if ( prefix != prevPrefix ) {
  614. prevPrefix = prefix;
  615. prefixList.Append( prefix );
  616. }
  617. prefixIndex[i] = prefixList.Num();
  618. } else {
  619. prefixIndex[i] = 0;
  620. // output all the prefix-less first
  621. idStr str = va( "%s=%s\n", kv->GetKey().c_str(), idStr::CStyleQuote( kv->GetValue() ) );
  622. f->Write( (void *)str.c_str(), str.Length() );
  623. skipFirstLine = "\n";
  624. }
  625. }
  626. int prevPrefixIndex = 0;
  627. int prefixLength = 0;
  628. // output all the rest without their prefix
  629. for ( int i = 0; i < sortedArgs.Num(); i++ ) {
  630. if ( prefixIndex[i] == 0 ) {
  631. continue;
  632. }
  633. if ( prefixIndex[i] != prevPrefixIndex ) {
  634. prevPrefixIndex = prefixIndex[i];
  635. prefixLength = prefixList[prevPrefixIndex - 1].Length() + 1; // to skip past the '/' too
  636. // output prefix
  637. idStr str = va( "%s[%s]\n", skipFirstLine.c_str(), prefixList[prevPrefixIndex - 1].c_str() );
  638. f->Write( (void *)str.c_str(), str.Length() );
  639. }
  640. const idKeyValue * kv = &sortedArgs[i];
  641. idStr str = va( "%s=%s\n", kv->GetKey().c_str() + prefixLength, idStr::CStyleQuote( kv->GetValue() ) );
  642. f->Write( (void *)str.c_str(), str.Length() );
  643. }
  644. }
  645. /*
  646. ================
  647. idDict::ReadFromIniFile
  648. ================
  649. */
  650. bool idDict::ReadFromIniFile( idFile * f ) {
  651. int length = f->Length();
  652. idTempArray< char > buffer( length );
  653. if ( (int)f->Read( buffer.Ptr(), length ) != length ) {
  654. return false;
  655. }
  656. buffer[length-1] = NULL; // Since the .ini files are not null terminated, make sure we mark where the end of the .ini file is in our read buffer
  657. idLexer parser( LEXFL_NOFATALERRORS | LEXFL_ALLOWPATHNAMES /*| LEXFL_ONLYSTRINGS */);
  658. idStr name = f->GetName();
  659. name.Append( " dictionary INI reader" );
  660. if ( !parser.LoadMemory( ( const char* )buffer.Ptr(), length, name.c_str() ) ) {
  661. return false;
  662. }
  663. idToken token;
  664. idToken token2;
  665. idStr prefix = "";
  666. idStr valueStr;
  667. bool success = true;
  668. Clear();
  669. const punctuation_t ini_punctuations[] = {
  670. { "[", P_SQBRACKETOPEN },
  671. { "]", P_SQBRACKETCLOSE },
  672. { "=", P_ASSIGN },
  673. { NULL, 0 }
  674. };
  675. parser.SetPunctuations( ini_punctuations );
  676. while ( success && !parser.EndOfFile() ) {
  677. if ( parser.PeekTokenType( TT_PUNCTUATION, P_SQBRACKETOPEN, &token ) ) {
  678. success = success && parser.ExpectTokenType( TT_PUNCTUATION, P_SQBRACKETOPEN, &token );
  679. success = success && parser.ReadToken( &token );
  680. prefix = token.c_str();
  681. prefix.Append( '/' );
  682. success = success && parser.ExpectTokenType( TT_PUNCTUATION, P_SQBRACKETCLOSE, &token );
  683. }
  684. if ( !parser.PeekTokenType( TT_NAME, 0, &token ) ) {
  685. // end of file most likely
  686. break;
  687. }
  688. success = success && parser.ExpectTokenType( TT_NAME, 0, &token );
  689. success = success && parser.ExpectTokenType( TT_PUNCTUATION, P_ASSIGN, &token2 );
  690. success = success && ( parser.ParseRestOfLine( valueStr ) != NULL );
  691. valueStr = idStr::CStyleUnQuote( valueStr );
  692. idStr key = va( "%s%s", prefix.c_str(), token.c_str() );
  693. if ( FindKey( key.c_str() ) ) {
  694. parser.Warning( "'%s' already defined", key.c_str() );
  695. }
  696. Set( key.c_str(), valueStr.c_str() );
  697. }
  698. return success;
  699. }
  700. CONSOLE_COMMAND( TestDictIniFile, "Tests the writing/reading of various items in a dict to/from an ini file", 0 ) {
  701. // Write to the file
  702. idFile * file = fileSystem->OpenFileWrite( "idDict_ini_test.ini" );
  703. if ( file == NULL ) {
  704. idLib::Printf( "[^1FAILED^0] Couldn't open file for writing.\n" );
  705. return;
  706. }
  707. idDict vars;
  708. vars.SetInt( "section1/section3/a", -1 );
  709. vars.SetInt( "section1/section3/b", 0 );
  710. vars.SetInt( "section1/section3/c", 3 );
  711. vars.SetFloat( "section2/d", 4.0f );
  712. vars.SetFloat( "section2/e", -5.0f );
  713. vars.SetBool( "section2/f", true );
  714. vars.SetBool( "section1/g", false );
  715. vars.Set( "section1/h", "test1" );
  716. vars.Set( "i", "1234" );
  717. vars.SetInt( "j", 9 );
  718. vars.WriteToIniFile( file );
  719. delete file;
  720. // Read from the file
  721. file = fileSystem->OpenFileRead( "idDict_ini_test.ini" );
  722. if ( file == NULL ) {
  723. idLib::Printf( "[^1FAILED^0] Couldn't open file for reading.\n" );
  724. }
  725. idDict readVars;
  726. readVars.ReadFromIniFile( file );
  727. delete file;
  728. if ( vars.Checksum() != readVars.Checksum() ) {
  729. idLib::Printf( "[^1FAILED^0] Dictionaries do not match.\n" );
  730. } else {
  731. idLib::Printf( "[^2PASSED^0] Dictionaries match.\n" );
  732. }
  733. // Output results
  734. for ( int i = 0; i < readVars.GetNumKeyVals(); i++ ) {
  735. const idKeyValue * kv = readVars.GetKeyVal( i );
  736. idLib::Printf( "%s=%s\n", kv->GetKey().c_str(), kv->GetValue().c_str() );
  737. }
  738. }
  739. /*
  740. ================
  741. idDict::Init
  742. ================
  743. */
  744. void idDict::Init() {
  745. globalKeys.SetCaseSensitive( false );
  746. globalValues.SetCaseSensitive( true );
  747. }
  748. /*
  749. ================
  750. idDict::Shutdown
  751. ================
  752. */
  753. void idDict::Shutdown() {
  754. globalKeys.Clear();
  755. globalValues.Clear();
  756. }
  757. /*
  758. ================
  759. idDict::ShowMemoryUsage_f
  760. ================
  761. */
  762. void idDict::ShowMemoryUsage_f( const idCmdArgs &args ) {
  763. idLib::common->Printf( "%5d KB in %d keys\n", globalKeys.Size() >> 10, globalKeys.Num() );
  764. idLib::common->Printf( "%5d KB in %d values\n", globalValues.Size() >> 10, globalValues.Num() );
  765. }
  766. /*
  767. ================
  768. idDict::ListKeys_f
  769. ================
  770. */
  771. void idDict::ListKeys_f( const idCmdArgs &args ) {
  772. idLib::Printf( "Not implemented due to sort impl issues.\n" );
  773. //int i;
  774. //idList<const idPoolStr *> keyStrings;
  775. //for ( i = 0; i < globalKeys.Num(); i++ ) {
  776. // keyStrings.Append( globalKeys[i] );
  777. //}
  778. //keyStrings.SortWithTemplate( idSort_PoolStrPtr() );
  779. //for ( i = 0; i < keyStrings.Num(); i++ ) {
  780. // idLib::common->Printf( "%s\n", keyStrings[i]->c_str() );
  781. //}
  782. //idLib::common->Printf( "%5d keys\n", keyStrings.Num() );
  783. }
  784. /*
  785. ================
  786. idDict::ListValues_f
  787. ================
  788. */
  789. void idDict::ListValues_f( const idCmdArgs &args ) {
  790. idLib::Printf( "Not implemented due to sort impl issues.\n" );
  791. //int i;
  792. //idList<const idPoolStr *> valueStrings;
  793. //for ( i = 0; i < globalValues.Num(); i++ ) {
  794. // valueStrings.Append( globalValues[i] );
  795. //}
  796. //valueStrings.SortWithTemplate( idSort_PoolStrPtr() );
  797. //for ( i = 0; i < valueStrings.Num(); i++ ) {
  798. // idLib::common->Printf( "%s\n", valueStrings[i]->c_str() );
  799. //}
  800. //idLib::common->Printf( "%5d values\n", valueStrings.Num() );
  801. }