fixed the last commit to set and check the target more often, thanks fuzzie
[gemrb.git] / gemrb / core / Variables.cpp
blob178bafa0f6614372efe105253db4ca7dae5285af
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 /////////////////////////////////////////////////////////////////////////////
27 // private inlines
28 inline bool Variables::MyCopyKey(char*& dest, const char* key) const
30 int i, j;
32 //use j
33 for (i = 0,j = 0; key[i] && j < MAX_VARIABLE_LENGTH - 1; i++)
34 if (key[i] != ' ') {
35 j++;
37 dest = (char *) malloc(j + 1);
38 if (!dest) {
39 return false;
41 for (i = 0,j = 0; key[i] && j < MAX_VARIABLE_LENGTH - 1; i++) {
42 if (key[i] != ' ') {
43 dest[j++] = (char) tolower( key[i] );
46 dest[j] = 0;
47 return true;
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
55 if (key[i] != ' ')
56 nHash = ( nHash << 5 ) + nHash + tolower( key[i] );
58 return nHash;
60 /////////////////////////////////////////////////////////////////////////////
61 // functions
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)
73 break;
74 assert( pAssocRet != NULL ); // must find something
76 Variables::MyAssoc* pAssocNext;
77 if (( pAssocNext = pAssocRet->pNext ) == NULL) {
78 // go to next bucket
79 for (unsigned int nBucket = pAssocRet->nHashValue + 1;
80 nBucket < m_nHashTableSize;
81 nBucket++)
82 if (( pAssocNext = m_pHashTable[nBucket] ) != NULL)
83 break;
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 );
97 m_pHashTable = NULL;
98 m_nHashTableSize = nHashTableSize; // default size
99 m_nCount = 0;
100 m_lParseKey = false;
101 m_pFreeList = NULL;
102 m_pBlocks = NULL;
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) {
116 // free hash table
117 free(m_pHashTable);
118 m_pHashTable = NULL;
121 if (bAllocNow) {
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];
135 pAssoc != NULL;
136 pAssoc = pAssoc->pNext) {
137 if (fun) {
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;
146 if (pAssoc->key) {
147 free(pAssoc->key);
148 pAssoc->key = NULL;
154 // free hash table
155 free(m_pHashTable);
156 m_pHashTable = NULL;
158 m_nCount = 0;
159 m_pFreeList = NULL;
160 MemBlock* p = m_pBlocks;
161 while (p != NULL) {
162 MemBlock* pNext = p->pNext;
163 free(p);
164 p = pNext;
166 m_pBlocks = NULL;
169 Variables::~Variables()
171 RemoveAll(NULL);
174 Variables::MyAssoc* Variables::NewAssoc(const char* key)
176 if (m_pFreeList == NULL) {
177 // add another block
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;
193 m_nCount++;
194 assert( m_nCount > 0 ); // make sure we don't overflow
195 if (m_lParseKey) {
196 MyCopyKey( pAssoc->key, key );
197 } else {
198 int len;
199 len = strnlen( key, MAX_VARIABLE_LENGTH - 1 );
200 pAssoc->key = (char *) malloc(len + 1);
201 if (pAssoc->key) {
202 memcpy( pAssoc->key, key, len );
203 pAssoc->key[len] = 0;
206 #ifdef _DEBUG
207 pAssoc->Value.nValue = 0xcccccccc; //invalid value
208 pAssoc->nHashValue = 0xcccccccc; //invalid value
209 #endif
210 return pAssoc;
213 void Variables::FreeAssoc(Variables::MyAssoc* pAssoc)
215 if (pAssoc->key) {
216 free(pAssoc->key);
217 pAssoc->key = NULL;
219 pAssoc->pNext = m_pFreeList;
220 m_pFreeList = pAssoc;
221 m_nCount--;
222 assert( m_nCount >= 0 ); // make sure we don't underflow
224 // if no more elements, cleanup completely
225 if (m_nCount == 0) {
226 RemoveAll(NULL);
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) {
236 return NULL;
239 // see if it exists
240 Variables::MyAssoc* pAssoc;
241 for (pAssoc = m_pHashTable[nHash];
242 pAssoc != NULL;
243 pAssoc = pAssoc->pNext) {
244 if (!strnicmp( pAssoc->key, key, MAX_VARIABLE_LENGTH )) {
245 return pAssoc;
249 return NULL;
252 int Variables::GetValueLength(const char* key) const
254 unsigned int nHash;
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
265 unsigned int nHash;
266 assert( m_type == GEM_VARIABLES_STRING );
267 Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
268 if (pAssoc == NULL) {
269 dest[0] = 0;
270 return false; // not in map
273 strncpy( dest, pAssoc->Value.sValue, MaxLength );
274 return true;
277 bool Variables::Lookup(const char* key, char *&dest) const
279 unsigned int nHash;
280 assert(m_type==GEM_VARIABLES_STRING);
281 Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
282 if (pAssoc == NULL) {
283 return false;
284 } // not in map
286 dest = pAssoc->Value.sValue;
287 return true;
290 bool Variables::Lookup(const char* key, void *&dest) const
292 unsigned int nHash;
293 assert(m_type==GEM_VARIABLES_POINTER);
294 Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
295 if (pAssoc == NULL) {
296 return false;
297 } // not in map
299 dest = pAssoc->Value.pValue;
300 return true;
303 bool Variables::Lookup(const char* key, ieDword& rValue) const
305 unsigned int nHash;
306 assert(m_type==GEM_VARIABLES_INT);
307 Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
308 if (pAssoc == NULL) {
309 return false;
310 } // not in map
312 rValue = pAssoc->Value.nValue;
313 return true;
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);
321 SetAt(key, str);
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)
333 unsigned int nHash;
334 Variables::MyAssoc* pAssoc;
336 assert(strlen(key)<256);
338 #ifdef _DEBUG
339 // for Avenger, debugging memory issues
340 assert((unsigned char)key[0]!=0xcd);
341 #endif
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;
353 } else {
354 if (pAssoc->Value.sValue) {
355 free( pAssoc->Value.sValue );
356 pAssoc->Value.sValue = 0;
360 //set value only if we have a key
361 if (pAssoc->key) {
362 pAssoc->Value.sValue = value;
363 pAssoc->nHashValue = nHash;
367 void Variables::SetAt(const char* key, void* value)
369 unsigned int nHash;
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;
382 } else {
383 if (pAssoc->Value.sValue) {
384 free( pAssoc->Value.sValue );
385 pAssoc->Value.sValue = 0;
389 //set value only if we have a key
390 if (pAssoc->key) {
391 pAssoc->Value.pValue = value;
392 pAssoc->nHashValue = nHash;
398 void Variables::SetAt(const char* key, ieDword value)
400 unsigned int nHash;
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
415 if (pAssoc->key) {
416 pAssoc->Value.nValue = value;
417 pAssoc->nHashValue = nHash;
421 void Variables::Remove(const char* key)
423 unsigned int nHash;
424 Variables::MyAssoc* pAssoc;
426 pAssoc = GetAssocAt( key, nHash );
427 if (!pAssoc) return; // not in there
429 if (pAssoc == m_pHashTable[nHash]) {
430 // head
431 m_pHashTable[nHash] = pAssoc->pNext;
432 } else {
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) {
439 prev = prev->pNext;
440 assert( prev != NULL );
442 prev->pNext = pAssoc->pNext;
444 pAssoc->pNext = 0;
445 FreeAssoc(pAssoc);
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 );
453 FileStream fs;
454 if (!fs.Open( nPath, true )) {
455 return;
458 char buffer[41];
459 ieDword value;
460 buffer[40] = 0;
461 ieVariable varname;
463 // first value is useless
464 if (!fs.Read(buffer, 40)) return;
465 if (fs.ReadDword(&value) != 4) return;
467 while (fs.Remains()) {
468 // read data
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);