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