DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 }