1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "Variables.h"
23 #include "Interface.h" // for LoadInitialValues
24 #include "System/FileStream.h" // for LoadInitialValues
26 /////////////////////////////////////////////////////////////////////////////
28 inline bool Variables::MyCopyKey(char*& dest
, const char* key
) const
33 for (i
= 0,j
= 0; key
[i
] && j
< MAX_VARIABLE_LENGTH
- 1; i
++)
37 dest
= (char *) malloc(j
+ 1);
41 for (i
= 0,j
= 0; key
[i
] && j
< MAX_VARIABLE_LENGTH
- 1; i
++) {
43 dest
[j
++] = (char) tolower( key
[i
] );
50 inline unsigned int Variables::MyCompareKey(const char* key
, const char *str
) const
54 for (i
= 0, j
= 0; str
[j
] && key
[i
] && i
< MAX_VARIABLE_LENGTH
- 1 && j
< MAX_VARIABLE_LENGTH
- 1;) {
55 char c1
= tolower(key
[i
]);
56 if (c1
== ' ') { i
++; continue; }
57 char c2
= tolower(str
[j
]);
58 if (c2
==' ') { j
++; continue; }
63 if (str
[j
] || key
[i
]) return 1;
67 inline unsigned int Variables::MyHashKey(const char* key
) const
69 unsigned int nHash
= 0;
70 for (int i
= 0; key
[i
] && i
< MAX_VARIABLE_LENGTH
; i
++) {
71 //the original engine ignores spaces in variable names
73 nHash
= ( nHash
<< 5 ) + nHash
+ tolower( key
[i
] );
77 /////////////////////////////////////////////////////////////////////////////
79 Variables::iterator
Variables::GetNextAssoc(iterator rNextPosition
, const char*& rKey
,
80 ieDword
& rValue
) const
82 assert( m_pHashTable
!= NULL
); // never call on empty map
84 Variables::MyAssoc
* pAssocRet
= ( Variables::MyAssoc
* ) rNextPosition
;
86 if (pAssocRet
== NULL
) {
87 // find the first association
88 for (unsigned int nBucket
= 0; nBucket
< m_nHashTableSize
; nBucket
++)
89 if (( pAssocRet
= m_pHashTable
[nBucket
] ) != NULL
)
91 assert( pAssocRet
!= NULL
); // must find something
93 Variables::MyAssoc
* pAssocNext
;
94 if (( pAssocNext
= pAssocRet
->pNext
) == NULL
) {
96 for (unsigned int nBucket
= pAssocRet
->nHashValue
+ 1;
97 nBucket
< m_nHashTableSize
;
99 if (( pAssocNext
= m_pHashTable
[nBucket
] ) != NULL
)
103 // fill in return data
104 rKey
= pAssocRet
->key
;
105 rValue
= pAssocRet
->Value
.nValue
;
106 return ( iterator
) pAssocNext
;
109 Variables::Variables(int nBlockSize
, int nHashTableSize
)
111 assert( nBlockSize
> 0 );
112 assert( nHashTableSize
> 16 );
115 m_nHashTableSize
= nHashTableSize
; // default size
120 m_nBlockSize
= nBlockSize
;
121 m_type
= GEM_VARIABLES_INT
;
124 void Variables::InitHashTable(unsigned int nHashSize
, bool bAllocNow
)
126 // Used to force allocation of a hash table or to override the default
127 // hash table size of (which is fairly small)
129 assert( m_nCount
== 0 );
130 assert( nHashSize
> 16 );
132 if (m_pHashTable
!= NULL
) {
139 m_pHashTable
=(Variables::MyAssoc
**) malloc(sizeof( Variables::MyAssoc
*) * nHashSize
);
140 memset( m_pHashTable
, 0, sizeof( Variables::MyAssoc
* ) * nHashSize
);
142 m_nHashTableSize
= nHashSize
;
145 void Variables::RemoveAll(ReleaseFun fun
)
147 if (m_pHashTable
!= NULL
) {
148 // destroy elements (values and keys)
149 for (unsigned int nHash
= 0; nHash
< m_nHashTableSize
; nHash
++) {
150 Variables::MyAssoc
* pAssoc
;
151 for (pAssoc
= m_pHashTable
[nHash
];
153 pAssoc
= pAssoc
->pNext
) {
155 fun((void *) pAssoc
->Value
.sValue
);
157 else if (m_type
== GEM_VARIABLES_STRING
) {
158 if (pAssoc
->Value
.sValue
) {
159 free( pAssoc
->Value
.sValue
);
160 pAssoc
->Value
.sValue
= NULL
;
177 MemBlock
* p
= m_pBlocks
;
179 MemBlock
* pNext
= p
->pNext
;
186 Variables::~Variables()
191 Variables::MyAssoc
* Variables::NewAssoc(const char* key
)
193 if (m_pFreeList
== NULL
) {
195 Variables::MemBlock
* newBlock
= ( Variables::MemBlock
* ) malloc( m_nBlockSize
*sizeof( Variables::MyAssoc
) + sizeof( Variables::MemBlock
));
196 assert( newBlock
!= NULL
); // we must have something
197 newBlock
->pNext
= m_pBlocks
;
198 m_pBlocks
= newBlock
;
200 // chain them into free list
201 Variables::MyAssoc
* pAssoc
= ( Variables::MyAssoc
* ) ( newBlock
+ 1 );
202 for (int i
= 0; i
< m_nBlockSize
; i
++) {
203 pAssoc
->pNext
= m_pFreeList
;
204 m_pFreeList
= pAssoc
++;
208 Variables::MyAssoc
* pAssoc
= m_pFreeList
;
209 m_pFreeList
= m_pFreeList
->pNext
;
211 assert( m_nCount
> 0 ); // make sure we don't overflow
213 MyCopyKey( pAssoc
->key
, key
);
216 len
= strnlen( key
, MAX_VARIABLE_LENGTH
- 1 );
217 pAssoc
->key
= (char *) malloc(len
+ 1);
219 memcpy( pAssoc
->key
, key
, len
);
220 pAssoc
->key
[len
] = 0;
224 pAssoc
->Value
.nValue
= 0xcccccccc; //invalid value
225 pAssoc
->nHashValue
= 0xcccccccc; //invalid value
230 void Variables::FreeAssoc(Variables::MyAssoc
* pAssoc
)
236 pAssoc
->pNext
= m_pFreeList
;
237 m_pFreeList
= pAssoc
;
239 assert( m_nCount
>= 0 ); // make sure we don't underflow
241 // if no more elements, cleanup completely
247 Variables::MyAssoc
* Variables::GetAssocAt(const char* key
, unsigned int& nHash
) const
248 // find association (or return NULL)
250 nHash
= MyHashKey( key
) % m_nHashTableSize
;
252 if (m_pHashTable
== NULL
) {
257 Variables::MyAssoc
* pAssoc
;
258 for (pAssoc
= m_pHashTable
[nHash
];
260 pAssoc
= pAssoc
->pNext
) {
262 if (!MyCompareKey( pAssoc
->key
, key
) ) {
266 if (!strnicmp( pAssoc
->key
, key
, MAX_VARIABLE_LENGTH
)) {
275 int Variables::GetValueLength(const char* key
) const
278 Variables::MyAssoc
* pAssoc
= GetAssocAt( key
, nHash
);
279 if (pAssoc
== NULL
) {
280 return 0; // not in map
283 return ( int ) strlen( pAssoc
->Value
.sValue
);
286 bool Variables::Lookup(const char* key
, char* dest
, int MaxLength
) const
289 assert( m_type
== GEM_VARIABLES_STRING
);
290 Variables::MyAssoc
* pAssoc
= GetAssocAt( key
, nHash
);
291 if (pAssoc
== NULL
) {
293 return false; // not in map
296 strncpy( dest
, pAssoc
->Value
.sValue
, MaxLength
);
300 bool Variables::Lookup(const char* key
, char *&dest
) const
303 assert(m_type
==GEM_VARIABLES_STRING
);
304 Variables::MyAssoc
* pAssoc
= GetAssocAt( key
, nHash
);
305 if (pAssoc
== NULL
) {
309 dest
= pAssoc
->Value
.sValue
;
313 bool Variables::Lookup(const char* key
, void *&dest
) const
316 assert(m_type
==GEM_VARIABLES_POINTER
);
317 Variables::MyAssoc
* pAssoc
= GetAssocAt( key
, nHash
);
318 if (pAssoc
== NULL
) {
322 dest
= pAssoc
->Value
.pValue
;
326 bool Variables::Lookup(const char* key
, ieDword
& rValue
) const
329 assert(m_type
==GEM_VARIABLES_INT
);
330 Variables::MyAssoc
* pAssoc
= GetAssocAt( key
, nHash
);
331 if (pAssoc
== NULL
) {
335 rValue
= pAssoc
->Value
.nValue
;
339 void Variables::SetAtCopy(const char* key
, const char* value
)
341 size_t len
= strlen(value
)+1;
342 char *str
=(char *) malloc(len
);
343 memcpy(str
,value
,len
);
347 void Variables::SetAtCopy(const char* key
, int newValue
)
349 char tmpstr
[10]; // should be enough
350 sprintf(tmpstr
, "%d", newValue
);
351 SetAtCopy(key
, tmpstr
);
354 void Variables::SetAt(const char* key
, char* value
)
357 Variables::MyAssoc
* pAssoc
;
359 assert(strlen(key
)<256);
362 // for Avenger, debugging memory issues
363 assert((unsigned char)key
[0]!=0xcd);
366 assert( m_type
== GEM_VARIABLES_STRING
);
367 if (( pAssoc
= GetAssocAt( key
, nHash
) ) == NULL
) {
368 if (m_pHashTable
== NULL
)
369 InitHashTable( m_nHashTableSize
);
371 // it doesn't exist, add a new Association
372 pAssoc
= NewAssoc( key
);
373 // put into hash table
374 pAssoc
->pNext
= m_pHashTable
[nHash
];
375 m_pHashTable
[nHash
] = pAssoc
;
377 if (pAssoc
->Value
.sValue
) {
378 free( pAssoc
->Value
.sValue
);
379 pAssoc
->Value
.sValue
= 0;
383 //set value only if we have a key
385 pAssoc
->Value
.sValue
= value
;
386 pAssoc
->nHashValue
= nHash
;
390 void Variables::SetAt(const char* key
, void* value
)
393 Variables::MyAssoc
* pAssoc
;
395 assert( m_type
== GEM_VARIABLES_POINTER
);
396 if (( pAssoc
= GetAssocAt( key
, nHash
) ) == NULL
) {
397 if (m_pHashTable
== NULL
)
398 InitHashTable( m_nHashTableSize
);
400 // it doesn't exist, add a new Association
401 pAssoc
= NewAssoc( key
);
402 // put into hash table
403 pAssoc
->pNext
= m_pHashTable
[nHash
];
404 m_pHashTable
[nHash
] = pAssoc
;
406 if (pAssoc
->Value
.sValue
) {
407 free( pAssoc
->Value
.sValue
);
408 pAssoc
->Value
.sValue
= 0;
412 //set value only if we have a key
414 pAssoc
->Value
.pValue
= value
;
415 pAssoc
->nHashValue
= nHash
;
421 void Variables::SetAt(const char* key
, ieDword value
)
424 Variables::MyAssoc
* pAssoc
;
426 assert( m_type
== GEM_VARIABLES_INT
);
427 if (( pAssoc
= GetAssocAt( key
, nHash
) ) == NULL
) {
428 if (m_pHashTable
== NULL
)
429 InitHashTable( m_nHashTableSize
);
431 // it doesn't exist, add a new Association
432 pAssoc
= NewAssoc( key
);
433 // put into hash table
434 pAssoc
->pNext
= m_pHashTable
[nHash
];
435 m_pHashTable
[nHash
] = pAssoc
;
437 //set value only if we have a key
439 pAssoc
->Value
.nValue
= value
;
440 pAssoc
->nHashValue
= nHash
;
444 void Variables::Remove(const char* key
)
447 Variables::MyAssoc
* pAssoc
;
449 pAssoc
= GetAssocAt( key
, nHash
);
450 if (!pAssoc
) return; // not in there
452 if (pAssoc
== m_pHashTable
[nHash
]) {
454 m_pHashTable
[nHash
] = pAssoc
->pNext
;
456 Variables::MyAssoc
* prev
= m_pHashTable
[nHash
];
457 // Room for optimization: make each bucket a doubly linked
458 // list to make removes from a bucket O(1).
459 // (This will have limited use in gemrb's case, because we
460 // use relatively large tables and small buckets.)
461 while (prev
->pNext
!= pAssoc
) {
463 assert( prev
!= NULL
);
465 prev
->pNext
= pAssoc
->pNext
;
471 void Variables::LoadInitialValues(const char* name
)
473 char nPath
[_MAX_PATH
];
474 // we only support PST's var.var for now
475 PathJoin( nPath
, core
->GamePath
, "var.var", NULL
);
477 if (!fs
.Open( nPath
, true )) {
486 // first value is useless
487 if (!fs
.Read(buffer
, 40)) return;
488 if (fs
.ReadDword(&value
) != 4) return;
490 while (fs
.Remains()) {
492 if (!fs
.Read(buffer
, 40)) return;
493 if (fs
.ReadDword(&value
) != 4) return;
494 // is it the type we want? if not, skip
495 if (strnicmp(buffer
, name
, 6)) continue;
496 // copy variable (types got 2 extra spaces, and the name is padded too)
497 strnspccpy(varname
,buffer
+8,32);
498 SetAt(varname
, value
);