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::MyHashKey(const char* key
) const
52 unsigned int nHash
= 0;
53 for (int i
= 0; i
< key
[i
] && i
< MAX_VARIABLE_LENGTH
; i
++) {
54 //the original engine ignores spaces in variable names
56 nHash
= ( nHash
<< 5 ) + nHash
+ tolower( key
[i
] );
60 /////////////////////////////////////////////////////////////////////////////
62 Variables::iterator
Variables::GetNextAssoc(iterator rNextPosition
, const char*& rKey
,
63 ieDword
& rValue
) const
65 assert( m_pHashTable
!= NULL
); // never call on empty map
67 Variables::MyAssoc
* pAssocRet
= ( Variables::MyAssoc
* ) rNextPosition
;
69 if (pAssocRet
== NULL
) {
70 // find the first association
71 for (unsigned int nBucket
= 0; nBucket
< m_nHashTableSize
; nBucket
++)
72 if (( pAssocRet
= m_pHashTable
[nBucket
] ) != NULL
)
74 assert( pAssocRet
!= NULL
); // must find something
76 Variables::MyAssoc
* pAssocNext
;
77 if (( pAssocNext
= pAssocRet
->pNext
) == NULL
) {
79 for (unsigned int nBucket
= pAssocRet
->nHashValue
+ 1;
80 nBucket
< m_nHashTableSize
;
82 if (( pAssocNext
= m_pHashTable
[nBucket
] ) != NULL
)
86 // fill in return data
87 rKey
= pAssocRet
->key
;
88 rValue
= pAssocRet
->Value
.nValue
;
89 return ( iterator
) pAssocNext
;
92 Variables::Variables(int nBlockSize
, int nHashTableSize
)
94 assert( nBlockSize
> 0 );
95 assert( nHashTableSize
> 16 );
98 m_nHashTableSize
= nHashTableSize
; // default size
103 m_nBlockSize
= nBlockSize
;
104 m_type
= GEM_VARIABLES_INT
;
107 void Variables::InitHashTable(unsigned int nHashSize
, bool bAllocNow
)
109 // Used to force allocation of a hash table or to override the default
110 // hash table size of (which is fairly small)
112 assert( m_nCount
== 0 );
113 assert( nHashSize
> 16 );
115 if (m_pHashTable
!= NULL
) {
122 m_pHashTable
=(Variables::MyAssoc
**) malloc(sizeof( Variables::MyAssoc
*) * nHashSize
);
123 memset( m_pHashTable
, 0, sizeof( Variables::MyAssoc
* ) * nHashSize
);
125 m_nHashTableSize
= nHashSize
;
128 void Variables::RemoveAll(ReleaseFun fun
)
130 if (m_pHashTable
!= NULL
) {
131 // destroy elements (values and keys)
132 for (unsigned int nHash
= 0; nHash
< m_nHashTableSize
; nHash
++) {
133 Variables::MyAssoc
* pAssoc
;
134 for (pAssoc
= m_pHashTable
[nHash
];
136 pAssoc
= pAssoc
->pNext
) {
138 fun((void *) pAssoc
->Value
.sValue
);
140 else if (m_type
== GEM_VARIABLES_STRING
) {
141 if (pAssoc
->Value
.sValue
) {
142 free( pAssoc
->Value
.sValue
);
143 pAssoc
->Value
.sValue
= NULL
;
160 MemBlock
* p
= m_pBlocks
;
162 MemBlock
* pNext
= p
->pNext
;
169 Variables::~Variables()
174 Variables::MyAssoc
* Variables::NewAssoc(const char* key
)
176 if (m_pFreeList
== NULL
) {
178 Variables::MemBlock
* newBlock
= ( Variables::MemBlock
* ) malloc( m_nBlockSize
*sizeof( Variables::MyAssoc
) + sizeof( Variables::MemBlock
));
179 assert( newBlock
!= NULL
); // we must have something
180 newBlock
->pNext
= m_pBlocks
;
181 m_pBlocks
= newBlock
;
183 // chain them into free list
184 Variables::MyAssoc
* pAssoc
= ( Variables::MyAssoc
* ) ( newBlock
+ 1 );
185 for (int i
= 0; i
< m_nBlockSize
; i
++) {
186 pAssoc
->pNext
= m_pFreeList
;
187 m_pFreeList
= pAssoc
++;
191 Variables::MyAssoc
* pAssoc
= m_pFreeList
;
192 m_pFreeList
= m_pFreeList
->pNext
;
194 assert( m_nCount
> 0 ); // make sure we don't overflow
196 MyCopyKey( pAssoc
->key
, key
);
199 len
= strnlen( key
, MAX_VARIABLE_LENGTH
- 1 );
200 pAssoc
->key
= (char *) malloc(len
+ 1);
202 memcpy( pAssoc
->key
, key
, len
);
203 pAssoc
->key
[len
] = 0;
207 pAssoc
->Value
.nValue
= 0xcccccccc; //invalid value
208 pAssoc
->nHashValue
= 0xcccccccc; //invalid value
213 void Variables::FreeAssoc(Variables::MyAssoc
* pAssoc
)
219 pAssoc
->pNext
= m_pFreeList
;
220 m_pFreeList
= pAssoc
;
222 assert( m_nCount
>= 0 ); // make sure we don't underflow
224 // if no more elements, cleanup completely
230 Variables::MyAssoc
* Variables::GetAssocAt(const char* key
, unsigned int& nHash
) const
231 // find association (or return NULL)
233 nHash
= MyHashKey( key
) % m_nHashTableSize
;
235 if (m_pHashTable
== NULL
) {
240 Variables::MyAssoc
* pAssoc
;
241 for (pAssoc
= m_pHashTable
[nHash
];
243 pAssoc
= pAssoc
->pNext
) {
244 if (!strnicmp( pAssoc
->key
, key
, MAX_VARIABLE_LENGTH
)) {
252 int Variables::GetValueLength(const char* key
) const
255 Variables::MyAssoc
* pAssoc
= GetAssocAt( key
, nHash
);
256 if (pAssoc
== NULL
) {
257 return 0; // not in map
260 return ( int ) strlen( pAssoc
->Value
.sValue
);
263 bool Variables::Lookup(const char* key
, char* dest
, int MaxLength
) const
266 assert( m_type
== GEM_VARIABLES_STRING
);
267 Variables::MyAssoc
* pAssoc
= GetAssocAt( key
, nHash
);
268 if (pAssoc
== NULL
) {
270 return false; // not in map
273 strncpy( dest
, pAssoc
->Value
.sValue
, MaxLength
);
277 bool Variables::Lookup(const char* key
, char *&dest
) const
280 assert(m_type
==GEM_VARIABLES_STRING
);
281 Variables::MyAssoc
* pAssoc
= GetAssocAt( key
, nHash
);
282 if (pAssoc
== NULL
) {
286 dest
= pAssoc
->Value
.sValue
;
290 bool Variables::Lookup(const char* key
, void *&dest
) const
293 assert(m_type
==GEM_VARIABLES_POINTER
);
294 Variables::MyAssoc
* pAssoc
= GetAssocAt( key
, nHash
);
295 if (pAssoc
== NULL
) {
299 dest
= pAssoc
->Value
.pValue
;
303 bool Variables::Lookup(const char* key
, ieDword
& rValue
) const
306 assert(m_type
==GEM_VARIABLES_INT
);
307 Variables::MyAssoc
* pAssoc
= GetAssocAt( key
, nHash
);
308 if (pAssoc
== NULL
) {
312 rValue
= pAssoc
->Value
.nValue
;
316 void Variables::SetAtCopy(const char* key
, const char* value
)
318 size_t len
= strlen(value
)+1;
319 char *str
=(char *) malloc(len
);
320 memcpy(str
,value
,len
);
324 void Variables::SetAtCopy(const char* key
, int newValue
)
326 char tmpstr
[10]; // should be enough
327 sprintf(tmpstr
, "%d", newValue
);
328 SetAtCopy(key
, tmpstr
);
331 void Variables::SetAt(const char* key
, char* value
)
334 Variables::MyAssoc
* pAssoc
;
336 assert(strlen(key
)<256);
339 // for Avenger, debugging memory issues
340 assert((unsigned char)key
[0]!=0xcd);
343 assert( m_type
== GEM_VARIABLES_STRING
);
344 if (( pAssoc
= GetAssocAt( key
, nHash
) ) == NULL
) {
345 if (m_pHashTable
== NULL
)
346 InitHashTable( m_nHashTableSize
);
348 // it doesn't exist, add a new Association
349 pAssoc
= NewAssoc( key
);
350 // put into hash table
351 pAssoc
->pNext
= m_pHashTable
[nHash
];
352 m_pHashTable
[nHash
] = pAssoc
;
354 if (pAssoc
->Value
.sValue
) {
355 free( pAssoc
->Value
.sValue
);
356 pAssoc
->Value
.sValue
= 0;
360 //set value only if we have a key
362 pAssoc
->Value
.sValue
= value
;
363 pAssoc
->nHashValue
= nHash
;
367 void Variables::SetAt(const char* key
, void* value
)
370 Variables::MyAssoc
* pAssoc
;
372 assert( m_type
== GEM_VARIABLES_POINTER
);
373 if (( pAssoc
= GetAssocAt( key
, nHash
) ) == NULL
) {
374 if (m_pHashTable
== NULL
)
375 InitHashTable( m_nHashTableSize
);
377 // it doesn't exist, add a new Association
378 pAssoc
= NewAssoc( key
);
379 // put into hash table
380 pAssoc
->pNext
= m_pHashTable
[nHash
];
381 m_pHashTable
[nHash
] = pAssoc
;
383 if (pAssoc
->Value
.sValue
) {
384 free( pAssoc
->Value
.sValue
);
385 pAssoc
->Value
.sValue
= 0;
389 //set value only if we have a key
391 pAssoc
->Value
.pValue
= value
;
392 pAssoc
->nHashValue
= nHash
;
398 void Variables::SetAt(const char* key
, ieDword value
)
401 Variables::MyAssoc
* pAssoc
;
403 assert( m_type
== GEM_VARIABLES_INT
);
404 if (( pAssoc
= GetAssocAt( key
, nHash
) ) == NULL
) {
405 if (m_pHashTable
== NULL
)
406 InitHashTable( m_nHashTableSize
);
408 // it doesn't exist, add a new Association
409 pAssoc
= NewAssoc( key
);
410 // put into hash table
411 pAssoc
->pNext
= m_pHashTable
[nHash
];
412 m_pHashTable
[nHash
] = pAssoc
;
414 //set value only if we have a key
416 pAssoc
->Value
.nValue
= value
;
417 pAssoc
->nHashValue
= nHash
;
421 void Variables::Remove(const char* key
)
424 Variables::MyAssoc
* pAssoc
;
426 pAssoc
= GetAssocAt( key
, nHash
);
427 if (!pAssoc
) return; // not in there
429 if (pAssoc
== m_pHashTable
[nHash
]) {
431 m_pHashTable
[nHash
] = pAssoc
->pNext
;
433 Variables::MyAssoc
* prev
= m_pHashTable
[nHash
];
434 // Room for optimization: make each bucket a doubly linked
435 // list to make removes from a bucket O(1).
436 // (This will have limited use in gemrb's case, because we
437 // use relatively large tables and small buckets.)
438 while (prev
->pNext
!= pAssoc
) {
440 assert( prev
!= NULL
);
442 prev
->pNext
= pAssoc
->pNext
;
448 void Variables::LoadInitialValues(const char* name
)
450 char nPath
[_MAX_PATH
];
451 // we only support PST's var.var for now
452 PathJoin( nPath
, core
->GamePath
, "var.var", NULL
);
454 if (!fs
.Open( nPath
, true )) {
463 // first value is useless
464 if (!fs
.Read(buffer
, 40)) return;
465 if (fs
.ReadDword(&value
) != 4) return;
467 while (fs
.Remains()) {
469 if (!fs
.Read(buffer
, 40)) return;
470 if (fs
.ReadDword(&value
) != 4) return;
471 // is it the type we want? if not, skip
472 if (strnicmp(buffer
, name
, 6)) continue;
473 // copy variable (types got 2 extra spaces, and the name is padded too)
474 strnspccpy(varname
,buffer
+8,32);
475 SetAt(varname
, value
);