839 lines
18 KiB
C++
839 lines
18 KiB
C++
|
// Copyright (C) 2007 Id Software, Inc.
|
||
|
//
|
||
|
|
||
|
#include "precompiled.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
|
||
|
#define new DEBUG_NEW
|
||
|
#endif
|
||
|
|
||
|
idStrPool* idDict::globalKeys = NULL;
|
||
|
idStrPool* idDict::globalValues = NULL;
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::operator=
|
||
|
|
||
|
clear existing key/value pairs and copy all key/value pairs from other
|
||
|
================
|
||
|
*/
|
||
|
idDict &idDict::operator=( const idDict &other ) {
|
||
|
int i;
|
||
|
|
||
|
// check for assignment to self
|
||
|
if ( this == &other ) {
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Clear();
|
||
|
|
||
|
args = other.args;
|
||
|
argHash = other.argHash;
|
||
|
|
||
|
for ( i = 0; i < args.Num(); i++ ) {
|
||
|
args[ i ].key = globalKeys->CopyString( args[i].key );
|
||
|
args[ i ].value = globalValues->CopyString( args[i].value );
|
||
|
}
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::Copy
|
||
|
|
||
|
copy all key value pairs without removing existing key/value pairs not present in the other dict
|
||
|
================
|
||
|
*/
|
||
|
void idDict::Copy( const idDict &other ) {
|
||
|
int i, n, *found;
|
||
|
idKeyValue kv;
|
||
|
|
||
|
// check for assignment to self
|
||
|
if ( this == &other ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
n = other.args.Num();
|
||
|
|
||
|
if ( args.Num() ) {
|
||
|
found = (int *) _alloca16( other.args.Num() * sizeof( int ) );
|
||
|
for ( i = 0; i < n; i++ ) {
|
||
|
found[i] = FindKeyIndex( other.args[i].GetKey() );
|
||
|
}
|
||
|
} else {
|
||
|
found = NULL;
|
||
|
}
|
||
|
|
||
|
for ( i = 0; i < n; i++ ) {
|
||
|
if ( found && found[i] != -1 ) {
|
||
|
// first set the new value and then free the old value to allow proper self copying
|
||
|
const idPoolStr *oldValue = args[found[i]].value;
|
||
|
args[found[i]].value = globalValues->CopyString( other.args[i].value );
|
||
|
globalValues->FreeString( oldValue );
|
||
|
} else {
|
||
|
kv.key = globalKeys->CopyString( other.args[i].key );
|
||
|
kv.value = globalValues->CopyString( other.args[i].value );
|
||
|
argHash.Add( argHash.GenerateKey( kv.GetKey(), false ), args.Append( kv ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::TransferKeyValues
|
||
|
|
||
|
clear existing key/value pairs and transfer key/value pairs from other
|
||
|
================
|
||
|
*/
|
||
|
void idDict::TransferKeyValues( idDict &other ) {
|
||
|
int i, n;
|
||
|
|
||
|
if ( this == &other ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Clear();
|
||
|
|
||
|
n = other.args.Num();
|
||
|
args.SetNum( n );
|
||
|
for ( i = 0; i < n; i++ ) {
|
||
|
args[i].key = other.args[i].key;
|
||
|
args[i].value = other.args[i].value;
|
||
|
}
|
||
|
argHash = other.argHash;
|
||
|
|
||
|
other.args.Clear();
|
||
|
other.argHash.Free();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::Parse
|
||
|
================
|
||
|
*/
|
||
|
bool idDict::Parse( idLexer &parser ) {
|
||
|
idToken token;
|
||
|
idToken token2;
|
||
|
bool errors;
|
||
|
|
||
|
errors = false;
|
||
|
|
||
|
if ( !parser.ExpectTokenString( "{" ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
parser.ReadToken( &token );
|
||
|
while( ( token.type != TT_PUNCTUATION ) || ( token != "}" ) ) {
|
||
|
if ( token.type != TT_STRING ) {
|
||
|
parser.Error( "Expected quoted string, but found '%s'", token.c_str() );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !parser.ReadToken( &token2 ) ) {
|
||
|
parser.Error( "Unexpected end of file" );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( FindKey( token ) ) {
|
||
|
parser.Warning( "'%s' already defined", token.c_str() );
|
||
|
errors = true;
|
||
|
}
|
||
|
Set( token, token2 );
|
||
|
|
||
|
if ( !parser.ReadToken( &token ) ) {
|
||
|
parser.Error( "Unexpected end of file" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return !errors;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::Parse
|
||
|
================
|
||
|
*/
|
||
|
bool idDict::Parse( idParser &parser ) {
|
||
|
idToken token;
|
||
|
idToken token2;
|
||
|
bool errors;
|
||
|
|
||
|
errors = false;
|
||
|
|
||
|
if ( !parser.ExpectTokenString( "{" ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
parser.ReadToken( &token );
|
||
|
while( ( token.type != TT_PUNCTUATION ) || ( token != "}" ) ) {
|
||
|
if ( token.type != TT_STRING ) {
|
||
|
parser.Error( "Expected quoted string, but found '%s'", token.c_str() );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !parser.ReadToken( &token2 ) ) {
|
||
|
parser.Error( "Unexpected end of file" );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( FindKey( token ) ) {
|
||
|
parser.Warning( "'%s' already defined", token.c_str() );
|
||
|
errors = true;
|
||
|
}
|
||
|
Set( token, token2 );
|
||
|
|
||
|
if ( !parser.ReadToken( &token ) ) {
|
||
|
parser.Error( "Unexpected end of file" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return !errors;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::SetDefaults
|
||
|
================
|
||
|
*/
|
||
|
void idDict::SetDefaults( const idDict *dict ) {
|
||
|
int i, n;
|
||
|
const idKeyValue *kv, *def;
|
||
|
idKeyValue newkv;
|
||
|
|
||
|
n = dict->args.Num();
|
||
|
for( i = 0; i < n; i++ ) {
|
||
|
def = &dict->args[i];
|
||
|
kv = FindKey( def->GetKey() );
|
||
|
if ( !kv ) {
|
||
|
newkv.key = globalKeys->CopyString( def->key );
|
||
|
newkv.value = globalValues->CopyString( def->value );
|
||
|
argHash.Add( argHash.GenerateKey( newkv.GetKey(), false ), args.Append( newkv ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::Clear
|
||
|
================
|
||
|
*/
|
||
|
void idDict::Clear( void ) {
|
||
|
int i;
|
||
|
|
||
|
for( i = 0; i < args.Num(); i++ ) {
|
||
|
globalKeys->FreeString( args[i].key );
|
||
|
globalValues->FreeString( args[i].value );
|
||
|
}
|
||
|
|
||
|
args.Clear();
|
||
|
argHash.Free();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::Print
|
||
|
================
|
||
|
*/
|
||
|
void idDict::Print() const {
|
||
|
int i;
|
||
|
int n;
|
||
|
|
||
|
n = args.Num();
|
||
|
for( i = 0; i < n; i++ ) {
|
||
|
idLib::common->Printf( "%s = %s\n", args[i].GetKey().c_str(), args[i].GetValue().c_str() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int KeyCompare( const idKeyValue *a, const idKeyValue *b ) {
|
||
|
return idStr::Cmp( a->GetKey(), b->GetKey() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::Checksum
|
||
|
================
|
||
|
*/
|
||
|
unsigned int idDict::Checksum( void ) const {
|
||
|
unsigned long ret;
|
||
|
int i, n;
|
||
|
|
||
|
idList<idKeyValue> sorted = args;
|
||
|
sorted.Sort( KeyCompare );
|
||
|
n = sorted.Num();
|
||
|
CRC32_InitChecksum( ret );
|
||
|
for( i = 0; i < n; i++ ) {
|
||
|
CRC32_UpdateChecksum( ret, sorted[i].GetKey().c_str(), sorted[i].GetKey().Length() );
|
||
|
CRC32_UpdateChecksum( ret, sorted[i].GetValue().c_str(), sorted[i].GetValue().Length() );
|
||
|
}
|
||
|
CRC32_FinishChecksum( ret );
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::Allocated
|
||
|
================
|
||
|
*/
|
||
|
size_t idDict::Allocated( void ) const {
|
||
|
int i;
|
||
|
size_t size;
|
||
|
|
||
|
size = args.Allocated() + argHash.Allocated();
|
||
|
for( i = 0; i < args.Num(); i++ ) {
|
||
|
size += args[i].Size();
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::Set
|
||
|
================
|
||
|
*/
|
||
|
bool idDict::Set( const char *key, const char *value ) {
|
||
|
int i;
|
||
|
idKeyValue kv;
|
||
|
|
||
|
assert( key );
|
||
|
|
||
|
/* if ( key == NULL || key[0] == '\0' ) {
|
||
|
return;
|
||
|
}*/
|
||
|
|
||
|
i = FindKeyIndex( key );
|
||
|
if ( i != -1 ) {
|
||
|
// allocstring is more expensive than this simple check
|
||
|
if( !args[i].value->Icmp( value ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// first set the new value and then free the old value to allow proper self copying
|
||
|
const idPoolStr *oldValue = args[i].value;
|
||
|
args[i].value = globalValues->AllocString( value );
|
||
|
globalValues->FreeString( oldValue );
|
||
|
} else {
|
||
|
kv.key = globalKeys->AllocString( key );
|
||
|
kv.value = globalValues->AllocString( value );
|
||
|
argHash.Add( argHash.GenerateKey( kv.GetKey(), false ), args.Append( kv ) );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::Set
|
||
|
================
|
||
|
*/
|
||
|
bool idDict::Set( int index, const char *value ) {
|
||
|
assert( index >= 0 && index < args.Num() );
|
||
|
|
||
|
// allocstring is more expensive than this simple check
|
||
|
if( !args[ index ].value->Icmp( value ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// first set the new value and then free the old value to allow proper self copying
|
||
|
const idPoolStr *oldValue = args[index].value;
|
||
|
args[index].value = globalValues->AllocString( value );
|
||
|
globalValues->FreeString( oldValue );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::GetFloat
|
||
|
================
|
||
|
*/
|
||
|
bool idDict::GetFloat( const char *key, const char *defaultString, float &out ) const {
|
||
|
const char *s;
|
||
|
bool found;
|
||
|
|
||
|
found = GetString( key, defaultString, &s );
|
||
|
out = atof( s );
|
||
|
return found;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::GetInt
|
||
|
================
|
||
|
*/
|
||
|
bool idDict::GetInt( const char *key, const char *defaultString, int &out ) const {
|
||
|
const char *s;
|
||
|
bool found;
|
||
|
|
||
|
found = GetString( key, defaultString, &s );
|
||
|
out = atoi( s );
|
||
|
return found;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::GetBool
|
||
|
================
|
||
|
*/
|
||
|
bool idDict::GetBool( const char *key, const char *defaultString, bool &out ) const {
|
||
|
const char *s;
|
||
|
bool found;
|
||
|
|
||
|
found = GetString( key, defaultString, &s );
|
||
|
out = ( atoi( s ) != 0 );
|
||
|
return found;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::GetAngles
|
||
|
================
|
||
|
*/
|
||
|
bool idDict::GetAngles( const char *key, const char *defaultString, idAngles &out ) const {
|
||
|
bool found;
|
||
|
const char *s;
|
||
|
|
||
|
if ( !defaultString ) {
|
||
|
defaultString = "0 0 0";
|
||
|
}
|
||
|
|
||
|
found = GetString( key, defaultString, &s );
|
||
|
out.Zero();
|
||
|
sscanf( s, "%f %f %f", &out.pitch, &out.yaw, &out.roll );
|
||
|
return found;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::GetVector
|
||
|
================
|
||
|
*/
|
||
|
bool idDict::GetVector( const char *key, const char *defaultString, idVec3 &out ) const {
|
||
|
bool found;
|
||
|
const char *s;
|
||
|
|
||
|
if ( !defaultString ) {
|
||
|
defaultString = "0 0 0";
|
||
|
}
|
||
|
|
||
|
found = GetString( key, defaultString, &s );
|
||
|
out.Zero();
|
||
|
sscanf( s, "%f %f %f", &out.x, &out.y, &out.z );
|
||
|
return found;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::GetVec2
|
||
|
================
|
||
|
*/
|
||
|
bool idDict::GetVec2( const char *key, const char *defaultString, idVec2 &out ) const {
|
||
|
bool found;
|
||
|
const char *s;
|
||
|
|
||
|
if ( !defaultString ) {
|
||
|
defaultString = "0 0";
|
||
|
}
|
||
|
|
||
|
found = GetString( key, defaultString, &s );
|
||
|
out.Zero();
|
||
|
sscanf( s, "%f %f", &out.x, &out.y );
|
||
|
return found;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::GetVec4
|
||
|
================
|
||
|
*/
|
||
|
bool idDict::GetVec4( const char *key, const char *defaultString, idVec4 &out ) const {
|
||
|
bool found;
|
||
|
const char *s;
|
||
|
|
||
|
if ( !defaultString ) {
|
||
|
defaultString = "0 0 0 0";
|
||
|
}
|
||
|
|
||
|
found = GetString( key, defaultString, &s );
|
||
|
out.Zero();
|
||
|
sscanf( s, "%f %f %f %f", &out.x, &out.y, &out.z, &out.w );
|
||
|
return found;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::GetMatrix
|
||
|
================
|
||
|
*/
|
||
|
bool idDict::GetMatrix( const char *key, const char *defaultString, idMat3 &out ) const {
|
||
|
const char *s;
|
||
|
bool found;
|
||
|
|
||
|
if ( !defaultString ) {
|
||
|
defaultString = "1 0 0 0 1 0 0 0 1";
|
||
|
}
|
||
|
|
||
|
found = GetString( key, defaultString, &s );
|
||
|
out.Identity(); // sccanf has a bug in it on Mac OS 9. Sigh.
|
||
|
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 );
|
||
|
return found;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
WriteString
|
||
|
================
|
||
|
*/
|
||
|
static void WriteString( const char *s, idFile *f ) {
|
||
|
int len = idStr::Length( s );
|
||
|
if ( len >= MAX_STRING_CHARS-1 ) {
|
||
|
idLib::common->Error( "idDict::WriteToFileHandle: bad string" );
|
||
|
}
|
||
|
f->Write( s, idStr::Length( s ) + 1 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::FindKey
|
||
|
================
|
||
|
*/
|
||
|
const idKeyValue *idDict::FindKey( const char *key ) const {
|
||
|
int i, hash;
|
||
|
|
||
|
if ( key == NULL || key[0] == '\0' ) {
|
||
|
idLib::common->Warning( "idDict::FindKey: empty key" );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
hash = argHash.GenerateKey( key, false );
|
||
|
for ( i = argHash.GetFirst( hash ); i != idHashIndex::NULL_INDEX; i = argHash.GetNext( i ) ) {
|
||
|
if ( args[i].GetKey().Icmp( key ) == 0 ) {
|
||
|
return &args[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::GenerateKey
|
||
|
================
|
||
|
*/
|
||
|
int idDict::GenerateKey( const char *key ) {
|
||
|
int hash = argHash.GenerateKey( key, false );
|
||
|
for ( int i = argHash.GetFirst( hash ); i != -1; i = argHash.GetNext( i ) ) {
|
||
|
if ( args[i].GetKey().Icmp( key ) == 0 ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int keyIndex;
|
||
|
|
||
|
idKeyValue kv;
|
||
|
kv.key = globalKeys->AllocString( key );
|
||
|
kv.value = globalValues->AllocString( "" );
|
||
|
keyIndex = args.Append( kv );
|
||
|
argHash.Add( argHash.GenerateKey( kv.GetKey(), false ), keyIndex );
|
||
|
|
||
|
return keyIndex;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::FindKeyIndex
|
||
|
================
|
||
|
*/
|
||
|
int idDict::FindKeyIndex( const char *key ) const {
|
||
|
/* if ( key == NULL || key[0] == '\0' ) {
|
||
|
idLib::common->Warning( "idDict::FindKeyIndex: empty key" );
|
||
|
return 0;
|
||
|
}*/
|
||
|
|
||
|
int hash = argHash.GenerateKey( key, false );
|
||
|
for ( int i = argHash.GetFirst( hash ); i != idHashIndex::NULL_INDEX; i = argHash.GetNext( i ) ) {
|
||
|
if ( args[i].GetKey().Icmp( key ) == 0 ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::Delete
|
||
|
================
|
||
|
*/
|
||
|
void idDict::Delete( const char *key ) {
|
||
|
int hash, i;
|
||
|
|
||
|
hash = argHash.GenerateKey( key, false );
|
||
|
for ( i = argHash.GetFirst( hash ); i != -1; i = argHash.GetNext( i ) ) {
|
||
|
if ( args[i].GetKey().Icmp( key ) == 0 ) {
|
||
|
globalKeys->FreeString( args[i].key );
|
||
|
globalValues->FreeString( args[i].value );
|
||
|
args.RemoveIndex( i );
|
||
|
argHash.RemoveIndex( hash, i );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
// make sure all keys can still be found in the hash index
|
||
|
for ( i = 0; i < args.Num(); i++ ) {
|
||
|
assert( FindKey( args[i].GetKey() ) != NULL );
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::MatchPrefix
|
||
|
================
|
||
|
*/
|
||
|
const idKeyValue *idDict::MatchPrefix( const char *prefix, const idKeyValue *lastMatch ) const {
|
||
|
int i;
|
||
|
int len;
|
||
|
int start;
|
||
|
|
||
|
assert( prefix );
|
||
|
len = idStr::Length( prefix );
|
||
|
|
||
|
start = -1;
|
||
|
if ( lastMatch ) {
|
||
|
start = args.FindIndex( *lastMatch );
|
||
|
assert( start >= 0 );
|
||
|
if ( start < 1 ) {
|
||
|
start = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for( i = start + 1; i < args.Num(); i++ ) {
|
||
|
if ( !args[i].GetKey().Icmpn( prefix, len ) ) {
|
||
|
return &args[i];
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::RandomPrefix
|
||
|
================
|
||
|
*/
|
||
|
const char *idDict::RandomPrefix( const char *prefix, idRandom &random ) const {
|
||
|
int count;
|
||
|
const int MAX_RANDOM_KEYS = 2048;
|
||
|
const char *list[MAX_RANDOM_KEYS];
|
||
|
const idKeyValue *kv;
|
||
|
|
||
|
list[0] = "";
|
||
|
for ( count = 0, kv = MatchPrefix( prefix ); kv && count < MAX_RANDOM_KEYS; kv = MatchPrefix( prefix, kv ) ) {
|
||
|
list[count++] = kv->GetValue().c_str();
|
||
|
}
|
||
|
return list[random.RandomInt( count )];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::WriteToFileHandle
|
||
|
================
|
||
|
*/
|
||
|
void idDict::WriteToFileHandle( idFile *f ) const {
|
||
|
int c = LittleLong( args.Num() );
|
||
|
f->Write( &c, sizeof( c ) );
|
||
|
for ( int i = 0; i < args.Num(); i++ ) { // don't loop on the swapped count use the original
|
||
|
WriteString( args[i].GetKey().c_str(), f );
|
||
|
WriteString( args[i].GetValue().c_str(), f );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
ReadString
|
||
|
================
|
||
|
*/
|
||
|
static idStr ReadString( idFile *f ) {
|
||
|
char str[MAX_STRING_CHARS];
|
||
|
int len;
|
||
|
|
||
|
for ( len = 0; len < MAX_STRING_CHARS; len++ ) {
|
||
|
f->Read( (void *)&str[len], 1 );
|
||
|
if ( str[len] == 0 ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if ( len == MAX_STRING_CHARS ) {
|
||
|
idLib::common->Error( "idDict::ReadFromFileHandle: bad string" );
|
||
|
}
|
||
|
|
||
|
return idStr( str );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::ReadFromFileHandle
|
||
|
================
|
||
|
*/
|
||
|
void idDict::ReadFromFileHandle( idFile *f ) {
|
||
|
int c;
|
||
|
idStr key, val;
|
||
|
|
||
|
Clear();
|
||
|
|
||
|
f->Read( &c, sizeof( c ) );
|
||
|
c = LittleLong( c );
|
||
|
for ( int i = 0; i < c; i++ ) {
|
||
|
key = ReadString( f );
|
||
|
val = ReadString( f );
|
||
|
Set( key, val );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::Init
|
||
|
================
|
||
|
*/
|
||
|
void idDict::Init( void ) {
|
||
|
if ( !globalKeys ) {
|
||
|
globalKeys = new idStrPool( 1024 );
|
||
|
globalKeys->SetCaseSensitive( false );
|
||
|
}
|
||
|
|
||
|
if ( !globalValues ) {
|
||
|
globalValues = new idStrPool( 1024 );
|
||
|
globalValues->SetCaseSensitive( true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::Shutdown
|
||
|
================
|
||
|
*/
|
||
|
void idDict::Shutdown( void ) {
|
||
|
if ( globalKeys ) {
|
||
|
globalKeys->Clear();
|
||
|
delete globalKeys;
|
||
|
globalKeys = NULL;
|
||
|
}
|
||
|
|
||
|
if ( globalValues ) {
|
||
|
globalValues->Clear();
|
||
|
delete globalValues;
|
||
|
globalValues = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::ShowMemoryUsage_f
|
||
|
================
|
||
|
*/
|
||
|
void idDict::ShowMemoryUsage_f( const idCmdArgs &args ) {
|
||
|
idLib::common->Printf( "%5d KB in %d keys\n", globalKeys->Size() >> 10, globalKeys->Num() );
|
||
|
idLib::common->Printf( "%5d KB in %d values\n", globalValues->Size() >> 10, globalValues->Num() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDictStringSortCmp
|
||
|
================
|
||
|
*/
|
||
|
// NOTE: the const wonkyness is required to make msvc happy
|
||
|
template<>
|
||
|
ID_INLINE int idListSortCompare( const idPoolStr * const *a, const idPoolStr * const *b ) {
|
||
|
return (*a)->Icmp( **b );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::ListKeys_f
|
||
|
================
|
||
|
*/
|
||
|
void idDict::ListKeys_f( const idCmdArgs &args ) {
|
||
|
int i;
|
||
|
idList<const idPoolStr *> keyStrings;
|
||
|
|
||
|
for ( i = 0; i < globalKeys->Num(); i++ ) {
|
||
|
keyStrings.Append( ( *globalKeys )[ i ] );
|
||
|
}
|
||
|
keyStrings.Sort();
|
||
|
for ( i = 0; i < keyStrings.Num(); i++ ) {
|
||
|
idLib::common->Printf( "%s\n", keyStrings[i]->c_str() );
|
||
|
}
|
||
|
idLib::common->Printf( "%5d keys\n", keyStrings.Num() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDict::ListValues_f
|
||
|
================
|
||
|
*/
|
||
|
void idDict::ListValues_f( const idCmdArgs &args ) {
|
||
|
int i;
|
||
|
idList<const idPoolStr *> valueStrings;
|
||
|
|
||
|
for ( i = 0; i < globalValues->Num(); i++ ) {
|
||
|
valueStrings.Append( ( *globalValues )[ i ] );
|
||
|
}
|
||
|
valueStrings.Sort();
|
||
|
for ( i = 0; i < valueStrings.Num(); i++ ) {
|
||
|
idLib::common->Printf( "%s\n", valueStrings[i]->c_str() );
|
||
|
}
|
||
|
idLib::common->Printf( "%5d values\n", valueStrings.Num() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idDict::Write
|
||
|
write the key value pairs to a file
|
||
|
============
|
||
|
*/
|
||
|
bool idDict::WriteIndented( idFile* file, bool indentFirst ) const {
|
||
|
sdTextUtil::GetInstance().Write( file, "{\n", indentFirst );
|
||
|
|
||
|
sdTextUtil::GetInstance().Indent( file );
|
||
|
for( int i = 0; i < GetNumKeyVals(); i++ ) {
|
||
|
const idKeyValue* kv = GetKeyVal( i );
|
||
|
sdTextUtil::GetInstance().Write( file, va( "\"%s\"\t\"%s\"\n", kv->GetKey().c_str(), kv->GetValue().c_str() ));
|
||
|
}
|
||
|
sdTextUtil::GetInstance().Unindent( file );
|
||
|
|
||
|
sdTextUtil::GetInstance().Write( file, "}\n" );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idDict::SetGlobalPools
|
||
|
============
|
||
|
*/
|
||
|
void idDict::SetGlobalPools( idStrPool* _globalKeys, idStrPool* _globalValues ) {
|
||
|
if( globalValues || globalKeys ) {
|
||
|
return;
|
||
|
}
|
||
|
/*
|
||
|
assert( globalKeys == NULL );
|
||
|
assert( globalValues == NULL );
|
||
|
*/
|
||
|
globalKeys = _globalKeys;
|
||
|
globalValues = _globalValues;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idDict::GetGlobalPools
|
||
|
============
|
||
|
*/
|
||
|
void idDict::GetGlobalPools( idStrPool*& _globalKeys, idStrPool*& _globalValues ) {
|
||
|
_globalKeys = globalKeys;
|
||
|
_globalValues = globalValues;
|
||
|
}
|