factored out the EFFv2 saving into EFFImporter
[gemrb.git] / gemrb / core / Variables.cpp
blob2393d3b1535ba85ac77ea1c2522be278ef1a1fb0
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::MyCompareKey(const char* key, const char *str) const
52 int i,j;
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; }
59 if (c1!=c2) return 1;
60 i++;
61 j++;
63 if (str[j] || key[i]) return 1;
64 return 0;
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
72 if (key[i] != ' ')
73 nHash = ( nHash << 5 ) + nHash + tolower( key[i] );
75 return nHash;
77 /////////////////////////////////////////////////////////////////////////////
78 // functions
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)
90 break;
91 assert( pAssocRet != NULL ); // must find something
93 Variables::MyAssoc* pAssocNext;
94 if (( pAssocNext = pAssocRet->pNext ) == NULL) {
95 // go to next bucket
96 for (unsigned int nBucket = pAssocRet->nHashValue + 1;
97 nBucket < m_nHashTableSize;
98 nBucket++)
99 if (( pAssocNext = m_pHashTable[nBucket] ) != NULL)
100 break;
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 );
114 m_pHashTable = NULL;
115 m_nHashTableSize = nHashTableSize; // default size
116 m_nCount = 0;
117 m_lParseKey = false;
118 m_pFreeList = NULL;
119 m_pBlocks = NULL;
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) {
133 // free hash table
134 free(m_pHashTable);
135 m_pHashTable = NULL;
138 if (bAllocNow) {
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];
152 pAssoc != NULL;
153 pAssoc = pAssoc->pNext) {
154 if (fun) {
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;
163 if (pAssoc->key) {
164 free(pAssoc->key);
165 pAssoc->key = NULL;
171 // free hash table
172 free(m_pHashTable);
173 m_pHashTable = NULL;
175 m_nCount = 0;
176 m_pFreeList = NULL;
177 MemBlock* p = m_pBlocks;
178 while (p != NULL) {
179 MemBlock* pNext = p->pNext;
180 free(p);
181 p = pNext;
183 m_pBlocks = NULL;
186 Variables::~Variables()
188 RemoveAll(NULL);
191 Variables::MyAssoc* Variables::NewAssoc(const char* key)
193 if (m_pFreeList == NULL) {
194 // add another block
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;
210 m_nCount++;
211 assert( m_nCount > 0 ); // make sure we don't overflow
212 if (m_lParseKey) {
213 MyCopyKey( pAssoc->key, key );
214 } else {
215 int len;
216 len = strnlen( key, MAX_VARIABLE_LENGTH - 1 );
217 pAssoc->key = (char *) malloc(len + 1);
218 if (pAssoc->key) {
219 memcpy( pAssoc->key, key, len );
220 pAssoc->key[len] = 0;
223 #ifdef _DEBUG
224 pAssoc->Value.nValue = 0xcccccccc; //invalid value
225 pAssoc->nHashValue = 0xcccccccc; //invalid value
226 #endif
227 return pAssoc;
230 void Variables::FreeAssoc(Variables::MyAssoc* pAssoc)
232 if (pAssoc->key) {
233 free(pAssoc->key);
234 pAssoc->key = NULL;
236 pAssoc->pNext = m_pFreeList;
237 m_pFreeList = pAssoc;
238 m_nCount--;
239 assert( m_nCount >= 0 ); // make sure we don't underflow
241 // if no more elements, cleanup completely
242 if (m_nCount == 0) {
243 RemoveAll(NULL);
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) {
253 return NULL;
256 // see if it exists
257 Variables::MyAssoc* pAssoc;
258 for (pAssoc = m_pHashTable[nHash];
259 pAssoc != NULL;
260 pAssoc = pAssoc->pNext) {
261 if (m_lParseKey) {
262 if (!MyCompareKey( pAssoc->key, key) ) {
263 return pAssoc;
265 } else {
266 if (!strnicmp( pAssoc->key, key, MAX_VARIABLE_LENGTH )) {
267 return pAssoc;
272 return NULL;
275 int Variables::GetValueLength(const char* key) const
277 unsigned int nHash;
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
288 unsigned int nHash;
289 assert( m_type == GEM_VARIABLES_STRING );
290 Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
291 if (pAssoc == NULL) {
292 dest[0] = 0;
293 return false; // not in map
296 strncpy( dest, pAssoc->Value.sValue, MaxLength );
297 return true;
300 bool Variables::Lookup(const char* key, char *&dest) const
302 unsigned int nHash;
303 assert(m_type==GEM_VARIABLES_STRING);
304 Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
305 if (pAssoc == NULL) {
306 return false;
307 } // not in map
309 dest = pAssoc->Value.sValue;
310 return true;
313 bool Variables::Lookup(const char* key, void *&dest) const
315 unsigned int nHash;
316 assert(m_type==GEM_VARIABLES_POINTER);
317 Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
318 if (pAssoc == NULL) {
319 return false;
320 } // not in map
322 dest = pAssoc->Value.pValue;
323 return true;
326 bool Variables::Lookup(const char* key, ieDword& rValue) const
328 unsigned int nHash;
329 assert(m_type==GEM_VARIABLES_INT);
330 Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
331 if (pAssoc == NULL) {
332 return false;
333 } // not in map
335 rValue = pAssoc->Value.nValue;
336 return true;
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);
344 SetAt(key, str);
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)
356 unsigned int nHash;
357 Variables::MyAssoc* pAssoc;
359 assert(strlen(key)<256);
361 #ifdef _DEBUG
362 // for Avenger, debugging memory issues
363 assert((unsigned char)key[0]!=0xcd);
364 #endif
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;
376 } else {
377 if (pAssoc->Value.sValue) {
378 free( pAssoc->Value.sValue );
379 pAssoc->Value.sValue = 0;
383 //set value only if we have a key
384 if (pAssoc->key) {
385 pAssoc->Value.sValue = value;
386 pAssoc->nHashValue = nHash;
390 void Variables::SetAt(const char* key, void* value)
392 unsigned int nHash;
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;
405 } else {
406 if (pAssoc->Value.sValue) {
407 free( pAssoc->Value.sValue );
408 pAssoc->Value.sValue = 0;
412 //set value only if we have a key
413 if (pAssoc->key) {
414 pAssoc->Value.pValue = value;
415 pAssoc->nHashValue = nHash;
421 void Variables::SetAt(const char* key, ieDword value)
423 unsigned int nHash;
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
438 if (pAssoc->key) {
439 pAssoc->Value.nValue = value;
440 pAssoc->nHashValue = nHash;
444 void Variables::Remove(const char* key)
446 unsigned int nHash;
447 Variables::MyAssoc* pAssoc;
449 pAssoc = GetAssocAt( key, nHash );
450 if (!pAssoc) return; // not in there
452 if (pAssoc == m_pHashTable[nHash]) {
453 // head
454 m_pHashTable[nHash] = pAssoc->pNext;
455 } else {
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) {
462 prev = prev->pNext;
463 assert( prev != NULL );
465 prev->pNext = pAssoc->pNext;
467 pAssoc->pNext = 0;
468 FreeAssoc(pAssoc);
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 );
476 FileStream fs;
477 if (!fs.Open( nPath, true )) {
478 return;
481 char buffer[41];
482 ieDword value;
483 buffer[40] = 0;
484 ieVariable varname;
486 // first value is useless
487 if (!fs.Read(buffer, 40)) return;
488 if (fs.ReadDword(&value) != 4) return;
490 while (fs.Remains()) {
491 // read data
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);