2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 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.
14 Doom 3 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.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
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.
26 ===========================================================================
29 #include "precompiled.h"
32 idStrPool idDict::globalKeys;
33 idStrPool idDict::globalValues;
39 clear existing key/value pairs and copy all key/value pairs from other
42 idDict &idDict::operator=( const idDict &other ) {
45 // check for assignment to self
46 if ( this == &other ) {
53 argHash = other.argHash;
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 );
67 copy all key value pairs without removing existing key/value pairs not present in the other dict
70 void idDict::Copy( const idDict &other ) {
74 // check for assignment to self
75 if ( this == &other ) {
82 found = (int *) _alloca16( other.args.Num() * sizeof( int ) );
83 for ( i = 0; i < n; i++ ) {
84 found[i] = FindKeyIndex( other.args[i].GetKey() );
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 );
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 ) );
106 idDict::TransferKeyValues
108 clear existing key/value pairs and transfer key/value pairs from other
111 void idDict::TransferKeyValues( idDict &other ) {
114 if ( this == &other ) {
118 if ( other.args.Num() && other.args[0].key->GetPool() != &globalKeys ) {
119 common->FatalError( "idDict::TransferKeyValues: can't transfer values across a DLL boundary" );
125 n = other.args.Num();
127 for ( i = 0; i < n; i++ ) {
128 args[i].key = other.args[i].key;
129 args[i].value = other.args[i].value;
131 argHash = other.argHash;
134 other.argHash.Free();
142 bool idDict::Parse( idParser &parser ) {
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() );
156 if ( !parser.ReadToken( &token2 ) ) {
157 parser.Error( "Unexpected end of file" );
160 if ( FindKey( token ) ) {
161 parser.Warning( "'%s' already defined", token.c_str() );
164 Set( token, token2 );
166 if ( !parser.ReadToken( &token ) ) {
167 parser.Error( "Unexpected end of file" );
179 void idDict::SetDefaults( const idDict *dict ) {
181 const idKeyValue *kv, *def;
184 n = dict->args.Num();
185 for( i = 0; i < n; i++ ) {
186 def = &dict->args[i];
187 kv = FindKey( def->GetKey() );
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 ) );
201 void idDict::Clear( void ) {
204 for( i = 0; i < args.Num(); i++ ) {
205 globalKeys.FreeString( args[i].key );
206 globalValues.FreeString( args[i].value );
218 void idDict::Print() const {
223 for( i = 0; i < n; i++ ) {
224 idLib::common->Printf( "%s = %s\n", args[i].GetKey().c_str(), args[i].GetValue().c_str() );
228 int KeyCompare( const idKeyValue *a, const idKeyValue *b ) {
229 return idStr::Cmp( a->GetKey(), b->GetKey() );
237 int idDict::Checksum( void ) const {
241 idList<idKeyValue> sorted = args;
242 sorted.Sort( KeyCompare );
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() );
249 CRC32_FinishChecksum( ret );
258 size_t idDict::Allocated( void ) const {
262 size = args.Allocated() + argHash.Allocated();
263 for( i = 0; i < args.Num(); i++ ) {
264 size += args[i].Size();
275 void idDict::Set( const char *key, const char *value ) {
279 if ( key == NULL || key[0] == '\0' ) {
283 i = FindKeyIndex( key );
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 );
290 kv.key = globalKeys.AllocString( key );
291 kv.value = globalValues.AllocString( value );
292 argHash.Add( argHash.GenerateKey( kv.GetKey(), false ), args.Append( kv ) );
301 bool idDict::GetFloat( const char *key, const char *defaultString, float &out ) const {
305 found = GetString( key, defaultString, &s );
315 bool idDict::GetInt( const char *key, const char *defaultString, int &out ) const {
319 found = GetString( key, defaultString, &s );
329 bool idDict::GetBool( const char *key, const char *defaultString, bool &out ) const {
333 found = GetString( key, defaultString, &s );
334 out = ( atoi( s ) != 0 );
343 bool idDict::GetAngles( const char *key, const char *defaultString, idAngles &out ) const {
347 if ( !defaultString ) {
348 defaultString = "0 0 0";
351 found = GetString( key, defaultString, &s );
353 sscanf( s, "%f %f %f", &out.pitch, &out.yaw, &out.roll );
362 bool idDict::GetVector( const char *key, const char *defaultString, idVec3 &out ) const {
366 if ( !defaultString ) {
367 defaultString = "0 0 0";
370 found = GetString( key, defaultString, &s );
372 sscanf( s, "%f %f %f", &out.x, &out.y, &out.z );
381 bool idDict::GetVec2( const char *key, const char *defaultString, idVec2 &out ) const {
385 if ( !defaultString ) {
386 defaultString = "0 0";
389 found = GetString( key, defaultString, &s );
391 sscanf( s, "%f %f", &out.x, &out.y );
400 bool idDict::GetVec4( const char *key, const char *defaultString, idVec4 &out ) const {
404 if ( !defaultString ) {
405 defaultString = "0 0 0 0";
408 found = GetString( key, defaultString, &s );
410 sscanf( s, "%f %f %f %f", &out.x, &out.y, &out.z, &out.w );
419 bool idDict::GetMatrix( const char *key, const char *defaultString, idMat3 &out ) const {
423 if ( !defaultString ) {
424 defaultString = "1 0 0 0 1 0 0 0 1";
427 found = GetString( key, defaultString, &s );
428 out.Identity(); // sccanf has a bug in it on Mac OS 9. Sigh.
429 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 );
438 static void WriteString( const char *s, idFile *f ) {
439 int len = strlen( s );
440 if ( len >= MAX_STRING_CHARS-1 ) {
441 idLib::common->Error( "idDict::WriteToFileHandle: bad string" );
443 f->Write( s, strlen(s) + 1 );
451 const idKeyValue *idDict::FindKey( const char *key ) const {
454 if ( key == NULL || key[0] == '\0' ) {
455 idLib::common->DWarning( "idDict::FindKey: empty key" );
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 ) {
474 int idDict::FindKeyIndex( const char *key ) const {
476 if ( key == NULL || key[0] == '\0' ) {
477 idLib::common->DWarning( "idDict::FindKeyIndex: empty key" );
481 int hash = argHash.GenerateKey( key, false );
482 for ( int i = argHash.First( hash ); i != -1; i = argHash.Next( i ) ) {
483 if ( args[i].GetKey().Icmp( key ) == 0 ) {
496 void idDict::Delete( const char *key ) {
499 hash = argHash.GenerateKey( key, false );
500 for ( i = argHash.First( hash ); i != -1; i = argHash.Next( i ) ) {
501 if ( args[i].GetKey().Icmp( key ) == 0 ) {
502 globalKeys.FreeString( args[i].key );
503 globalValues.FreeString( args[i].value );
504 args.RemoveIndex( i );
505 argHash.RemoveIndex( hash, i );
511 // make sure all keys can still be found in the hash index
512 for ( i = 0; i < args.Num(); i++ ) {
513 assert( FindKey( args[i].GetKey() ) != NULL );
523 const idKeyValue *idDict::MatchPrefix( const char *prefix, const idKeyValue *lastMatch ) const {
529 len = strlen( prefix );
533 start = args.FindIndex( *lastMatch );
534 assert( start >= 0 );
540 for( i = start + 1; i < args.Num(); i++ ) {
541 if ( !args[i].GetKey().Icmpn( prefix, len ) ) {
553 const char *idDict::RandomPrefix( const char *prefix, idRandom &random ) const {
555 const int MAX_RANDOM_KEYS = 2048;
556 const char *list[MAX_RANDOM_KEYS];
557 const idKeyValue *kv;
560 for ( count = 0, kv = MatchPrefix( prefix ); kv && count < MAX_RANDOM_KEYS; kv = MatchPrefix( prefix, kv ) ) {
561 list[count++] = kv->GetValue().c_str();
563 return list[random.RandomInt( count )];
568 idDict::WriteToFileHandle
571 void idDict::WriteToFileHandle( idFile *f ) const {
572 int c = LittleLong( args.Num() );
573 f->Write( &c, sizeof( c ) );
574 for ( int i = 0; i < args.Num(); i++ ) { // don't loop on the swapped count use the original
575 WriteString( args[i].GetKey().c_str(), f );
576 WriteString( args[i].GetValue().c_str(), f );
585 static idStr ReadString( idFile *f ) {
586 char str[MAX_STRING_CHARS];
589 for ( len = 0; len < MAX_STRING_CHARS; len++ ) {
590 f->Read( (void *)&str[len], 1 );
591 if ( str[len] == 0 ) {
595 if ( len == MAX_STRING_CHARS ) {
596 idLib::common->Error( "idDict::ReadFromFileHandle: bad string" );
604 idDict::ReadFromFileHandle
607 void idDict::ReadFromFileHandle( idFile *f ) {
613 f->Read( &c, sizeof( c ) );
615 for ( int i = 0; i < c; i++ ) {
616 key = ReadString( f );
617 val = ReadString( f );
627 void idDict::Init( void ) {
628 globalKeys.SetCaseSensitive( false );
629 globalValues.SetCaseSensitive( true );
637 void idDict::Shutdown( void ) {
639 globalValues.Clear();
644 idDict::ShowMemoryUsage_f
647 void idDict::ShowMemoryUsage_f( const idCmdArgs &args ) {
648 idLib::common->Printf( "%5d KB in %d keys\n", globalKeys.Size() >> 10, globalKeys.Num() );
649 idLib::common->Printf( "%5d KB in %d values\n", globalValues.Size() >> 10, globalValues.Num() );
657 // NOTE: the const wonkyness is required to make msvc happy
659 ID_INLINE int idListSortCompare( const idPoolStr * const *a, const idPoolStr * const *b ) {
660 return (*a)->Icmp( **b );
668 void idDict::ListKeys_f( const idCmdArgs &args ) {
670 idList<const idPoolStr *> keyStrings;
672 for ( i = 0; i < globalKeys.Num(); i++ ) {
673 keyStrings.Append( globalKeys[i] );
676 for ( i = 0; i < keyStrings.Num(); i++ ) {
677 idLib::common->Printf( "%s\n", keyStrings[i]->c_str() );
679 idLib::common->Printf( "%5d keys\n", keyStrings.Num() );
687 void idDict::ListValues_f( const idCmdArgs &args ) {
689 idList<const idPoolStr *> valueStrings;
691 for ( i = 0; i < globalValues.Num(); i++ ) {
692 valueStrings.Append( globalValues[i] );
695 for ( i = 0; i < valueStrings.Num(); i++ ) {
696 idLib::common->Printf( "%s\n", valueStrings[i]->c_str() );
698 idLib::common->Printf( "%5d values\n", valueStrings.Num() );