Subversion Repositories Games.Chess Giants

Rev

Rev 136 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
// inifile.c
2
 
3
#include "common.h"
4
 
5
 
6
// WARNING: INI SECTION NAMES ARE CASE SENSITIVE, BUT KEY NAMES ARE NOT!
7
 
8
 
9
// internal definitions
10
#define MAX_KEY_SIZE 124
11
#define MAX_VALUE_SIZE 3968
12
#define DICTIONARY_PAGE_SIZE 128
13
#define INI_SECTION_SEPARATOR ']'
14
#define INI_INVALID_KEY (wchar_t *) 0x7fffffff
15
#define WSIZEOF(a) (sizeof (a) / sizeof (wchar_t))
16
 
17
 
18
// dictionary structure definitions
19
typedef struct dictionary_entry_s
20
{
21
   wchar_t key[MAX_KEY_SIZE]; // key string
22
   unsigned long hash; // key hash value
23
   wchar_t value[MAX_VALUE_SIZE]; // value string
24
} dictionary_entry_t;
25
 
26
 
27
typedef struct dictionary_s
28
{
29
   int size; // storage size
30
   dictionary_entry_t *entries; // array of entries (mallocated)
31
   int entry_count; // number of entries in dictionary
32
} dictionary_t;
33
 
34
 
35
// dictionary function prototypes
36
static dictionary_t *Dictionary_CreateDictionary (int size);
37
static void Dictionary_DestroyDictionary (dictionary_t *dictionary);
38
static dictionary_entry_t *Dictionary_GetKey (dictionary_t *dictionary, wchar_t *key);
39
static wchar_t *Dictionary_ReadKey (dictionary_t *dictionary, wchar_t *key, wchar_t *default_value);
40
static void Dictionary_WriteKey (dictionary_t *dictionary, wchar_t *key, wchar_t *value);
41
static void Dictionary_DeleteKey (dictionary_t *dictionary, wchar_t *key);
42
static unsigned long Dictionary_ComputeHashValueForKey (wchar_t *key);
43
 
44
 
45
// ini file function prototypes
46
void *INIFile_NewINIFile (void);
47
int INIFile_GetNumberOfSections (void *ini_data);
48
wchar_t *INIFile_GetSectionName (void *ini_data, int section_index);
49
int INIFile_GetNumberOfEntries (void *ini_data, wchar_t *section);
50
wchar_t *INIFile_GetEntryName (void *ini_data, wchar_t *section, int entry_index);
51
unsigned char INIFile_ReadEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char default_value);
52
long INIFile_ReadEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long default_value);
53
unsigned long INIFile_ReadEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long default_value);
54
double INIFile_ReadEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double default_value);
55
wchar_t *INIFile_ReadEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *default_value);
56
void INIFile_WriteEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char value);
57
void INIFile_WriteEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long value);
58
void INIFile_WriteEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long value);
59
void INIFile_WriteEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double value);
60
void INIFile_WriteEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *value);
61
void INIFile_DeleteEntry (void *ini_data, wchar_t *section, wchar_t *entry);
62
void INIFile_DeleteSection (void *ini_data, wchar_t *section);
63
void *INIFile_LoadINIFile (const wchar_t *filename);
64
unsigned char INIFile_SaveINIFile (const wchar_t *filename, void *ini_data);
65
void INIFile_FreeINIFile (void *ini_data);
66
 
67
 
68
// internal variables
69
static wchar_t *INIFile_default_section = L"__default";
70
static wchar_t line_buffer[MAX_KEY_SIZE + MAX_VALUE_SIZE];
71
static wchar_t temp_key[MAX_KEY_SIZE];
72
static wchar_t number_as_string[256];
73
static wchar_t temp_section[MAX_KEY_SIZE];
74
static wchar_t temp_entry[MAX_KEY_SIZE];
75
static wchar_t temp_value[MAX_VALUE_SIZE];
76
 
77
 
78
static dictionary_t *Dictionary_CreateDictionary (int size)
79
{
80
   // this function allocates a new dictionary object of given size and returns it. If you do
81
   // not know in advance (roughly) the number of entries in the dictionary, give size = 0.
82
 
83
   dictionary_t *dictionary;
84
 
85
   // if no size was specified, allocate space for one page
86
   if (size < DICTIONARY_PAGE_SIZE)
87
      size = DICTIONARY_PAGE_SIZE;
88
 
89
   // allocate one instance of the dictionary and blank it out
90
   dictionary = (dictionary_t *) SAFE_malloc (1, sizeof (dictionary_t), true);
91
   dictionary->size = size; // dictionary can currently hold size entries
92
 
93
   // allocate space for the dictionary entries and zero all the crap out
94
   dictionary->entries = (dictionary_entry_t *) SAFE_malloc (size, sizeof (dictionary_entry_t), true);
95
 
96
   return (dictionary); // finished, return a pointer to the dictionary object
97
}
98
 
99
 
100
static void Dictionary_DestroyDictionary (dictionary_t *dictionary)
101
{
102
   // frees a dictionary object and all memory associated to it.
103
 
104
   if (dictionary == NULL)
105
      return; // consistency check
106
 
107
   SAFE_free ((void **) &dictionary->entries); // free the entries array
108
   SAFE_free ((void **) &dictionary); // and finally, free the dictionary itself
109
 
110
   return; // finished
111
}
112
 
113
 
114
static dictionary_entry_t *Dictionary_GetKey (dictionary_t *dictionary, wchar_t *key)
115
{
116
   // this function locates a key in a dictionary and returns a pointer to it, or a NULL
117
   // pointer if no such key can be found in dictionary. The returned pointer points to data
118
   // internal to the dictionary object, do not try to free or modify it.
119
 
120
   unsigned long hash;
121
   register int entry_index;
122
 
123
   hash = Dictionary_ComputeHashValueForKey (key); // get the hash value for key
124
 
125
   // cycle through all entries in the dictionary
126
   for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++)
127
   {
128
      if (dictionary->entries[entry_index].key[0] == 0)
129
         continue; // skip empty slots
130
 
131
      // compare hash AND string, to avoid hash collisions
132
      if ((hash == dictionary->entries[entry_index].hash) && (wcscmp (key, dictionary->entries[entry_index].key) == 0))
133
         return (&dictionary->entries[entry_index]); // return a pointer to the key if we find it
134
   }
135
 
136
   return (NULL); // else return a NULL pointer
137
}
138
 
139
 
140
static wchar_t *Dictionary_ReadKey (dictionary_t *dictionary, wchar_t *key, wchar_t *default_value)
141
{
142
   // this function locates a key in a dictionary and returns a pointer to its value, or the
143
   // passed 'def' pointer if no such key can be found in dictionary. The returned char pointer
144
   // points to data internal to the dictionary object, do not try to free or modify it.
145
 
146
   dictionary_entry_t *element;
147
 
148
   element = Dictionary_GetKey (dictionary, key); // query the dictionary for the key
149
   if (element != NULL)
150
      return (element->value); // if we found a valid key, return the value associed to it
151
 
152
   return (default_value); // else return the default value
153
}
154
 
155
 
156
static void Dictionary_WriteKey (dictionary_t *dictionary, wchar_t *key, wchar_t *value)
157
{
158
   // sets a value in a dictionary. If the given key is found in the dictionary, the associated
159
   // value is replaced by the provided one. If the key cannot be found in the dictionary, it
160
   // is added to it.
161
 
162
   unsigned long hash;
163
   register int entry_index;
164
 
165
   hash = Dictionary_ComputeHashValueForKey (key); // compute hash for this key
166
 
167
   // for each entry in the dictionary...
168
   for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++)
169
   {
170
      if (dictionary->entries[entry_index].key[0] == 0)
171
          continue; // skip empty slots
172
 
173
      // does that key have the same hash value AND the same name ?
174
      if ((hash == dictionary->entries[entry_index].hash) && (wcscmp (key, dictionary->entries[entry_index].key) == 0))
175
      {
176
         // we've found the right key: modify its value and return
177
 
178
         // have we provided a valid value ?
179
         if (value != NULL)
180
            wcscpy_s (dictionary->entries[entry_index].value, WSIZEOF (dictionary->entries[entry_index].value), value); // copy the value string
181
         else
182
            dictionary->entries[entry_index].value[0] = 0; // reset the value string
183
 
184
         return; // value has been modified: return
185
      }
186
   }
187
 
188
   // here either the dictionary was empty, or we couldn't find a similar value: add a new one
189
 
190
   // cycle through all entries in the dictionary...
191
   for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++)
192
      if (dictionary->entries[entry_index].key[0] == 0)
193
         break; // and stop at the first empty one we find
194
 
195
   // no empty entry found, see if dictionary needs to grow or if there's still room left
196
   if (entry_index == dictionary->size)
197
   {
198
      // mallocate some more space for the dictionary and zero all that crap out
199
      dictionary->entries = (dictionary_entry_t *) SAFE_realloc (dictionary->entries, dictionary->size, dictionary->size + DICTIONARY_PAGE_SIZE, sizeof (dictionary_entry_t), false); // alloc 1 more page
200
      dictionary->size += DICTIONARY_PAGE_SIZE; // increase dictionary size
201
   }
202
 
203
   // copy the new key
204
 
205
   wcscpy_s (dictionary->entries[entry_index].key, WSIZEOF (dictionary->entries[entry_index].key), key); // copy the key string...
206
   dictionary->entries[entry_index].hash = hash; // ...and store the hash value
207
 
208
   // have we provided a valid value ?
209
   if (value != NULL)
210
      wcscpy_s (dictionary->entries[entry_index].value, WSIZEOF (dictionary->entries[entry_index].value), value); // copy the value string
211
   else
212
      dictionary->entries[entry_index].value[0] = 0; // reset the value string
213
 
214
   dictionary->entry_count++; // there is one more entry in the dictionary now
215
   return;
216
}
217
 
218
 
219
static void Dictionary_DeleteKey (dictionary_t *dictionary, wchar_t *key)
220
{
221
   // this function deletes a key in a dictionary. Nothing is done if the key cannot be found.
222
 
223
   unsigned long hash;
224
   register int entry_index;
225
 
226
   hash = Dictionary_ComputeHashValueForKey (key); // get the hash value for key
227
 
228
   // cycle through all entries in the dictionary...
229
   for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++)
230
   {
231
      if (dictionary->entries[entry_index].key[0] == 0)
232
         continue; // skip empty slots
233
 
234
      // compare hash AND string, to avoid hash collisions
235
      if ((hash == dictionary->entries[entry_index].hash) && (wcscmp (key, dictionary->entries[entry_index].key) == 0))
236
         break; // break as soon as we've found the key we want
237
   }
238
 
239
   if (entry_index == dictionary->entry_count)
240
      return; // if key was not found, just return
241
 
242
   // clear the key, the hash and its value data
243
   memset (&dictionary->entries[entry_index], 0, sizeof (dictionary_entry_t));
244
 
245
   dictionary->entry_count--; // there is one entry less in the dictionary now
246
   return;
247
}
248
 
249
 
250
static unsigned long Dictionary_ComputeHashValueForKey (wchar_t *key)
251
{
252
   // Compute the hash key for a string. This hash function has been taken from an article in
253
   // Dr Dobbs Journal. This is normally a collision-free function, distributing keys evenly.
254
   // The key is stored anyway in the struct so that collision can be avoided by comparing the
255
   // key itself in last resort. THIS FUNCTION IS TO BE USED INTERNALLY ONLY!
256
 
257
   register unsigned long hash;
258
   int length;
259
   int char_index;
260
 
261
   hash = 0;
262
   length = (int) wcslen (key);
263
 
264
   // for each character of the string...
265
   for (char_index = 0; char_index < length; char_index++)
266
   {
267
      hash += (unsigned long) key[char_index]; // take it in account and compute the hash value
268
      hash += (hash << 10);
269
      hash ^= (hash >> 6);
270
   }
271
 
272
   hash += (hash << 3); // finalize hashing (what the hell does it do, I have no clue)
273
   hash ^= (hash >> 11);
274
   hash += (hash << 15);
275
 
276
   return (hash); // and return the hash value
277
}
278
 
279
 
280
void *INIFile_NewINIFile (void)
281
{
282
   // allocates a dictionary for a new INI file. Mostly a helper function.
283
 
284
   return ((void *) Dictionary_CreateDictionary (0));
285
}
286
 
287
 
288
int INIFile_GetNumberOfSections (void  *ini_data)
289
{
290
   // this function returns the number of sections found in a dictionary. The test to recognize
291
   // sections is done on the string stored in the dictionary: a section name is given as
292
   // "section" whereas a key is stored as "section]key", thus the test looks for entries that
293
   // do NOT contain a ']'. This clearly fails in the case a section name contains a ']',
294
   // but this should simply be avoided.
295
 
296
   int section_count;
297
   int entry_index;
298
   dictionary_t *dictionary;
299
 
300
   if (ini_data == NULL)
301
      return (0); // consistency check
302
 
303
   dictionary = (dictionary_t *) ini_data;
304
 
305
   section_count = 0; // no sections found yet
306
 
307
   // for each entry in the dictionary...
308
   for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++)
309
   {
310
      if (dictionary->entries[entry_index].key[0] == 0)
311
         continue; // skip empty slots
312
 
313
      if (wcschr (dictionary->entries[entry_index].key, INI_SECTION_SEPARATOR) == NULL)
314
         section_count++; // if this entry has NO section separator, then it's a section name
315
   }
316
 
317
   return (section_count); // return the section count we found
318
}
319
 
320
 
321
wchar_t *INIFile_GetSectionName (void *ini_data, int section_index)
322
{
323
   // this function locates the n-th section in a dictionary and returns its name as a pointer
324
   // to a string allocated inside the dictionary. Do not free or modify the returned string!!
325
   // This function returns NULL in case of error.
326
 
327
   int sections_found;
328
   int entry_index;
329
   dictionary_t *dictionary;
330
 
331
   if ((ini_data == NULL) || (section_index < 0))
332
      return (NULL); // consistency check
333
 
334
   dictionary = (dictionary_t *) ini_data;
335
 
336
   sections_found = 0; // no sections found yet
337
 
338
   // for each entry in the dictionary...
339
   for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++)
340
   {
341
      if (dictionary->entries[entry_index].key[0] == 0)
342
         continue; // skip empty slots
343
 
344
      // is this entry a section name ?
345
      if (wcschr (dictionary->entries[entry_index].key, INI_SECTION_SEPARATOR) == NULL)
346
      {
347
         // is it the section we want ?
348
         if (sections_found == section_index)
349
            return (dictionary->entries[entry_index].key); // then return its name
350
 
351
         sections_found++; // we found one section more
352
      }
353
   }
354
 
355
   return (INIFile_default_section); // section was not found, return the default section name
356
}
357
 
358
 
359
int INIFile_GetNumberOfEntries (void  *ini_data, wchar_t *section)
360
{
361
   // this function returns the number of entries found in a dictionary within a given section.
362
   // The test to recognize section entries is done on the string stored in the dictionary: a
363
   // section name is given as "section" whereas a key is stored as "section]key", thus the test
364
   // looks for all entries that begin with "section]".
365
 
366
   int entry_count;
367
   int entry_index;
368
   int sectionname_length;
369
   dictionary_t *dictionary;
370
 
371
   if (ini_data == NULL)
372
      return (0); // consistency check
373
 
374
   dictionary = (dictionary_t *) ini_data;
375
   sectionname_length = wcslen (section);
376
 
377
   entry_count = 0; // no entries found yet
378
 
379
   // for each entry in the dictionary...
380
   for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++)
381
   {
382
      if (dictionary->entries[entry_index].key[0] == 0)
383
         continue; // skip empty slots
384
 
385
      if ((wcsncmp (dictionary->entries[entry_index].key, section, sectionname_length) == 0)
386
          && (dictionary->entries[entry_index].key[sectionname_length] == INI_SECTION_SEPARATOR))
387
         entry_count++; // if this entry has section name + separator, then it's one of those we want
388
   }
389
 
390
   return (entry_count); // return the number of entries we found in the given section
391
}
392
 
393
 
394
wchar_t *INIFile_GetEntryName (void *ini_data, wchar_t *section, int entry_index)
395
{
396
   // this function queries a dictionary for a key. A key as read from an ini file is given as
397
   // "section]key". If the key cannot be found, NULL is returned. The returned char pointer
398
   // points to a string allocated in the dictionary, do not free or modify it.
399
 
400
   int i;
401
   int length;
402
   int found_keys;
403
   dictionary_t *dictionary;
404
   dictionary_entry_t *entry;
405
 
406
   if (ini_data == NULL)
407
      return (NULL); // consistency check
408
 
409
   if (section == NULL)
410
      section = INIFile_default_section; // if no section was provided, use empty section name
411
 
412
   dictionary = (dictionary_t *) ini_data; // quick access to dictionary
413
 
414
   // build the key entry prefix
415
   swprintf_s (temp_key, WSIZEOF (temp_key), L"%s%c", section, INI_SECTION_SEPARATOR); // compose the key
416
   length = wcslen (temp_key);
417
 
418
   // cycle through all remaining entries
419
   found_keys = 0;
420
   for (i = 0; i < dictionary->entry_count; i++)
421
   {
422
      entry = &dictionary->entries[i]; // quick access to each entry
423
 
424
      if (wcsncmp (entry->key, temp_key, length) != 0)
425
         continue; // if this key doesn't belong to the wanted section, skip it
426
 
427
      // this key belongs to the wanted section. Is it the key we want ?
428
      if (found_keys == entry_index)
429
         return (&entry->key[length]); // if so, return its name
430
 
431
      found_keys++; // it wasn't the key we want, so increase counter and proceed to the next one
432
   }
433
 
434
   return (NULL); // if not found, return NULL
435
}
436
 
437
 
438
unsigned char INIFile_ReadEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char default_value)
439
{
440
   // this function queries a dictionary for a key. A key as read from an ini file is given as
441
   // "section]key". If the key cannot be found, the notfound value is returned. A true boolean
442
   // is found if a string starting with either 'y', 'Y', 't', 'T' or '1' is matched. A false
443
   // boolean is found if a string starting with 'n', 'N', 'f', 'F' or '0' is matched.
444
 
445
   wchar_t *value;
446
 
447
   if (ini_data == NULL)
448
      return (default_value); // consistency check
449
 
450
   // get the value as a string first
451
   value = INIFile_ReadEntryAsString ((dictionary_t *) ini_data, section, entry, INI_INVALID_KEY);
452
 
453
   if (value == INI_INVALID_KEY)
454
      return (default_value); // if the entry could not be read, return the default value
455
 
456
   // decide of the boolean's value by looking at the first character
457
   if ((toupper (value[0]) == 'T') || (toupper (value[0]) == 'Y') || (value[0] == '1'))
458
     return (1); // boolean value is TRUE
459
   else if ((toupper (value[0]) == 'F') || (toupper (value[0]) == 'N') || (value[0] == '0'))
460
     return (0); // boolean value is FALSE
461
 
462
   return (default_value); // boolean value is undefined, return default value
463
}
464
 
465
 
466
long INIFile_ReadEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long default_value)
467
{
468
   // this function queries a dictionary for a key. A key as read from an ini file is given as
469
   // "section]key". If the key cannot be found, the notfound value is returned.
470
 
471
   wchar_t *value;
472
 
473
   if (ini_data == NULL)
474
      return (default_value); // consistency check
475
 
476
   // get the value as a string first
477
   value = INIFile_ReadEntryAsString ((dictionary_t *) ini_data, section, entry, INI_INVALID_KEY);
478
 
479
   if (value == INI_INVALID_KEY)
480
      return (default_value); // if the entry could not be read, return the default value
481
 
482
   return (_wtol (value)); // else convert the value to a long integer and return it
483
}
484
 
485
 
486
unsigned long INIFile_ReadEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long default_value)
487
{
488
   // this function queries a dictionary for a key. A key as read from an ini file is given as
489
   // "section]key". If the key cannot be found, the notfound value is returned.
490
 
491
   wchar_t *value;
492
 
493
   if (ini_data == NULL)
494
      return (default_value); // consistency check
495
 
496
   // get the value as a string first
497
   value = INIFile_ReadEntryAsString ((dictionary_t *) ini_data, section, entry, INI_INVALID_KEY);
498
 
499
   if (value == INI_INVALID_KEY)
500
      return (default_value); // if the entry could not be read, return the default value
501
 
502
   return (wcstoul (value, NULL, 10)); // else convert the value to a long integer and return it
503
}
504
 
505
 
506
double INIFile_ReadEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double default_value)
507
{
508
   // this function queries a dictionary for a key. A key as read from an ini file is given as
509
   // "section]key". If the key cannot be found, the notfound value is returned.
510
 
511
   wchar_t *value;
512
 
513
   if (ini_data == NULL)
514
      return (default_value); // consistency check
515
 
516
   // get the value as a string first
517
   value = INIFile_ReadEntryAsString ((dictionary_t *) ini_data, section, entry, INI_INVALID_KEY);
518
 
519
   if (value == INI_INVALID_KEY)
520
      return (default_value); // if the entry could not be read, return the default value
521
 
522
   return (_wtof (value)); // else convert the value to a double precision number and return it
523
}
524
 
525
 
526
wchar_t *INIFile_ReadEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *default_value)
527
{
528
   // this function queries a dictionary for a key. A key as read from an ini file is given as
529
   // "section]key". If the key cannot be found, the pointer passed as 'def' is returned. The
530
   // returned char pointer points to a string allocated in the dictionary, do not free or
531
   // modify it.
532
 
533
   int length;
534
   int i;
535
   wchar_t *value;
536
 
537
   if (ini_data == NULL)
538
      return (default_value); // consistency check
539
 
540
   if (section == NULL)
541
      section = INIFile_default_section; // if no section was provided, use empty section name
542
 
543
   // if we were given an entry, build a key as section]entry
544
   if (entry != NULL)
545
   {
546
      swprintf_s (temp_key, WSIZEOF (temp_key), L"%s%c%s", section, INI_SECTION_SEPARATOR, entry); // compose the key
547
 
548
      i = (int) wcslen (temp_key); // get the key string length
549
      if (i < WSIZEOF (temp_key))
550
         length = i;
551
      else
552
         length = WSIZEOF (temp_key) - 1; // clamp it to a max value
553
 
554
      // for each character in the string after the section separator...
555
      for (i = (int) wcslen (section) + 1; i < length; i++)
556
         temp_key[i] = towlower (temp_key[i]); // convert it to lowercase
557
      temp_key[i] = 0; // terminate the string
558
   }
559
 
560
   // else it must be a section name
561
   else
562
      wcscpy_s (temp_key, WSIZEOF (temp_key), section); // copy the name into the key
563
 
564
   value = Dictionary_ReadKey ((dictionary_t *) ini_data, temp_key, default_value); // query the dictionary...
565
   return (value); // ...and return the value
566
}
567
 
568
 
569
void INIFile_WriteEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char value)
570
{
571
   // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is
572
   // modified to contain the provided value. If it cannot be found, it is created.
573
 
574
   // according the boolean's value, write the equivalent string
575
   if (value)
576
      INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, L"true"); // write the new entry
577
   else
578
      INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, L"false"); // write the new entry
579
 
580
   return; // finished
581
}
582
 
583
 
584
void INIFile_WriteEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long value)
585
{
586
   // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is
587
   // modified to contain the provided value. If it cannot be found, it is created.
588
 
589
   // build the long integer value equivalent string (use printf facility)
590
   swprintf_s (number_as_string, WSIZEOF (number_as_string), L"%ld", value);
591
 
592
   INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry
593
   return; // finished
594
}
595
 
596
 
597
void INIFile_WriteEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long value)
598
{
599
   // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is
600
   // modified to contain the provided value. If it cannot be found, it is created.
601
 
602
   // build the long integer value equivalent string (use printf facility)
603
   swprintf_s (number_as_string, WSIZEOF (number_as_string), L"%lu", value);
604
 
605
   INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry
606
   return; // finished
607
}
608
 
609
 
610
void INIFile_WriteEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double value)
611
{
612
   // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is
613
   // modified to contain the provided value. If it cannot be found, it is created.
614
 
615
   // build the long integer value equivalent string (use printf facility)
616
   swprintf_s (number_as_string, WSIZEOF (number_as_string), L"%g", value);
617
 
618
   INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry
619
   return; // finished
620
}
621
 
622
 
623
void INIFile_WriteEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *value)
624
{
625
   // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is
626
   // modified to contain the provided value. If it cannot be found, it is created.
627
 
628
   int length;
629
   int i;
630
 
631
   if (ini_data == NULL)
632
      return; // consistency check
633
 
634
   if (section == NULL)
635
      section = INIFile_default_section; // if no section was provided, use empty section name
636
 
637
   // if we were given an entry, build a key as section#entry
638
   if (entry != NULL)
639
   {
640
      Dictionary_WriteKey ((dictionary_t *) ini_data, section, NULL); // create the section if it doesn't exist
641
 
642
      swprintf_s (temp_key, WSIZEOF (temp_key), L"%s%c%s", section, INI_SECTION_SEPARATOR, entry); // compose the key
643
 
644
      i = (int) wcslen (temp_key); // get the key string length
645
      if (i < WSIZEOF (temp_key))
646
         length = i;
647
      else
648
         length = WSIZEOF (temp_key) - 1; // clamp it to a max value
649
 
650
      // for each character in the string after the section separator...
651
      for (i = (int) wcslen (section) + 1; i < length; i++)
652
         temp_key[i] = towlower (temp_key[i]); // convert it to lowercase
653
      temp_key[i] = 0; // terminate the string
654
   }
655
 
656
   // else it must be a section name
657
   else
658
      wcscpy_s (temp_key, WSIZEOF (temp_key), section); // copy the name into the key
659
 
660
   Dictionary_WriteKey ((dictionary_t *) ini_data, temp_key, value); // write the new key in the dictionary
661
   return; // finished
662
}
663
 
664
 
665
void INIFile_DeleteEntry (void *ini_data, wchar_t *section, wchar_t *entry)
666
{
667
   // deletes an entry in the dictionary
668
 
669
   int length;
670
   int i;
671
 
672
   if (ini_data == NULL)
673
      return; // consistency check
674
 
675
   if (section == NULL)
676
      section = INIFile_default_section; // if no section was provided, use empty section name
677
 
678
   // if we were given an entry, build a key as section#entry
679
   if (entry != NULL)
680
   {
681
      swprintf_s (temp_key, WSIZEOF (temp_key), L"%s%c%s", section, INI_SECTION_SEPARATOR, entry); // compose the key
682
 
683
      i = (int) wcslen (temp_key); // get the key string length
684
      if (i < WSIZEOF (temp_key))
685
         length = i;
686
      else
687
         length = WSIZEOF (temp_key) - 1; // clamp it to a max value
688
 
689
      // for each character in the string after the section separator...
690
      for (i = (int) wcslen (section) + 1; i < length; i++)
691
         temp_key[i] = towlower (temp_key[i]); // convert it to lowercase
692
      temp_key[i] = 0; // terminate the string
693
   }
694
 
695
   // else it must be a section name
696
   else
697
      wcscpy_s (temp_key, WSIZEOF (temp_key), section); // copy the name into the key
698
 
699
   Dictionary_DeleteKey ((dictionary_t *) ini_data, temp_key);
700
   return;
701
}
702
 
703
 
704
void INIFile_DeleteSection (void *ini_data, wchar_t *section)
705
{
706
   // deletes a whole INI section in the dictionary
707
 
708
   int length;
709
   int i;
710
   dictionary_t *dictionary;
711
 
712
   if (ini_data == NULL)
713
      return; // consistency check
714
 
715
   if (section == NULL)
716
      section = INIFile_default_section; // if no section was provided, use empty section name
717
 
718
   dictionary = (dictionary_t *) ini_data;
719
 
720
   swprintf_s (temp_key, WSIZEOF (temp_key), L"%s%c", section, INI_SECTION_SEPARATOR); // compose the key
721
   length = (int) wcslen (temp_key); // get the key string length
722
 
723
   // for each entry in the dictionary...
724
   for (i = 0; i < dictionary->entry_count; i++)
725
   {
726
      if (dictionary->entries[i].key[0] == 0)
727
         continue; // skip empty slots
728
 
729
      // does this entry belong to the section we want ?
730
      if (wcsncmp (dictionary->entries[i].key, temp_key, length) == 0)
731
         Dictionary_DeleteKey (dictionary, dictionary->entries[i].key); // yes, delete it
732
   }
733
 
734
   Dictionary_DeleteKey (dictionary, section); // and finally delete the section name itself
735
   return;
736
}
737
 
738
 
739
void *INIFile_LoadINIFile (const wchar_t *filename)
740
{
741
   // this is the parser for ini files. This function is called, providing the name of the file
742
   // to be read. It returns a dictionary object that should not be accessed directly, but
743
   // through accessor functions instead.
744
 
745
   FILE *fp;
746
   int fieldstart;
747
   int fieldstop;
748
   int length;
749
   int i;
750
   dictionary_t *dictionary;
751
 
752
   // try to open the INI file in ASCII read-only mode
753
   fp = _wfsopen (filename, L"r, ccs=UNICODE", _SH_DENYNO);
754
   if (fp == NULL)
755
      return (NULL); // cancel if file not found
756
 
757
   dictionary = Dictionary_CreateDictionary (0);
758
 
759
   // set the default section for orphaned entries and add it to the dictionary
760
   wcscpy_s (temp_section, WSIZEOF (temp_section), INIFile_default_section);
761
   INIFile_WriteEntryAsString (dictionary, temp_section, NULL, NULL);
762
 
763
   // read line per line...
764
   while (fgetws (line_buffer, WSIZEOF (line_buffer), fp) != NULL)
765
   {
766
      length = (int) wcslen (line_buffer); // get line length
767
 
768
      while ((length > 0) && ((line_buffer[length - 1] == '\n') || (line_buffer[length - 1] == '\r')))
769
         length--; // discard trailing newlines
770
 
771
      fieldstart = 0; // let's now strip leading blanks
772
      while ((fieldstart < length) && iswspace (line_buffer[fieldstart]))
773
         fieldstart++; // ignore any tabs or spaces, going forward from the start
774
 
775
      fieldstop = length - 1; // let's now strip trailing blanks
776
      while ((fieldstop >= 0) && iswspace (line_buffer[fieldstop]))
777
         fieldstop--; // ignore any tabs or spaces, going backwards from the end
778
 
779
      for (i = fieldstart; i <= fieldstop; i++)
780
         line_buffer[i - fieldstart] = line_buffer[i]; // recopy line buffer without the spaces
781
      line_buffer[i - fieldstart] = 0; // and terminate the string
782
 
783
      if ((line_buffer[0] == ';') || (line_buffer[0] == '#') || (line_buffer[0] == 0))
784
         continue; // skip comment lines
785
 
786
      // is it a valid section name ?
787
      if (swscanf_s (line_buffer, L"[%[^]]", temp_section, WSIZEOF (temp_section)) == 1)
788
      {
789
         length = (int) wcslen (temp_section); // get the section string length
790
 
791
         fieldstart = 0; // let's now strip leading blanks
792
         while ((fieldstart < length) && iswspace (temp_section[fieldstart]))
793
            fieldstart++; // ignore any tabs or spaces, going forward from the start
794
 
795
         fieldstop = length - 1; // let's now strip trailing blanks
796
         while ((fieldstop >= 0) && iswspace (temp_section[fieldstop]))
797
            fieldstop--; // ignore any tabs or spaces, going backwards from the end
798
 
799
         for (i = fieldstart; i <= fieldstop; i++)
800
            temp_section[i - fieldstart] = temp_section[i]; // recopy section name w/out spaces
801
         temp_section[i - fieldstart] = 0; // and terminate the string
802
 
803
         INIFile_WriteEntryAsString (dictionary, temp_section, NULL, NULL); // add to dictionary
804
      }
805
 
806
      // else is it a valid entry/value pair that is enclosed between quotes?
807
      else if (swscanf_s (line_buffer, L"%[^=] = \"%[^\"]\"", temp_entry, WSIZEOF (temp_entry), temp_value, WSIZEOF (temp_value)) == 2)
808
      {
809
         length = (int) wcslen (temp_entry); // get the entry string length
810
 
811
         fieldstart = 0; // let's now strip leading blanks
812
         while ((fieldstart < length) && iswspace (temp_entry[fieldstart]))
813
            fieldstart++; // ignore any tabs or spaces, going forward from the start
814
 
815
         fieldstop = length - 1; // let's now strip trailing blanks
816
         while ((fieldstop >= 0) && iswspace (temp_entry[fieldstop]))
817
            fieldstop--; // ignore any tabs or spaces, going backwards from the end
818
 
819
         for (i = fieldstart; i <= fieldstop; i++)
820
            temp_entry[i - fieldstart] = towlower (temp_entry[i]); // recopy entry name w/out spaces
821
         temp_entry[i - fieldstart] = 0; // and terminate the string
822
 
823
         // when value is enclosed between quotes, DO NOT strip the blanks
824
 
825
         // sscanf cannot handle "" or '' as empty value, this is done here
826
         if ((wcscmp (temp_value, L"\"\"") == 0) || (wcscmp (temp_value, L"''") == 0))
827
            temp_value[0] = 0; // empty string
828
 
829
         INIFile_WriteEntryAsString (dictionary, temp_section, temp_entry, temp_value); // add to dictionary
830
      }
831
 
832
      // else is it a valid entry/value pair without quotes ?
833
      else if ((swscanf_s (line_buffer, L"%[^=] = '%[^\']'", temp_entry, WSIZEOF (temp_entry), temp_value, WSIZEOF (temp_value)) == 2)
834
               || (swscanf_s (line_buffer, L"%[^=] = %[^;#]", temp_entry, WSIZEOF (temp_entry), temp_value, WSIZEOF (temp_value)) == 2))
835
      {
836
         length = (int) wcslen (temp_entry); // get the entry string length
837
 
838
         fieldstart = 0; // let's now strip leading blanks
839
         while ((fieldstart < length) && iswspace (temp_entry[fieldstart]))
840
            fieldstart++; // ignore any tabs or spaces, going forward from the start
841
 
842
         fieldstop = length - 1; // let's now strip trailing blanks
843
         while ((fieldstop >= 0) && iswspace (temp_entry[fieldstop]))
844
            fieldstop--; // ignore any tabs or spaces, going backwards from the end
845
 
846
         for (i = fieldstart; i <= fieldstop; i++)
847
            temp_entry[i - fieldstart] = towlower (temp_entry[i]); // recopy entry name w/out spaces
848
         temp_entry[i - fieldstart] = 0; // and terminate the string
849
 
850
         length = (int) wcslen (temp_value); // get the value string length
851
 
852
         fieldstart = 0; // let's now strip leading blanks
853
         while ((fieldstart < length) && iswspace (temp_value[fieldstart]))
854
            fieldstart++; // ignore any tabs or spaces, going forward from the start
855
 
856
         fieldstop = length - 1; // let's now strip trailing blanks
857
         while ((fieldstop >= 0) && iswspace (temp_value[fieldstop]))
858
            fieldstop--; // ignore any tabs or spaces, going backwards from the end
859
 
860
         for (i = fieldstart; i <= fieldstop; i++)
861
            temp_value[i - fieldstart] = temp_value[i]; // recopy entry name w/out spaces
862
         temp_value[i - fieldstart] = 0; // and terminate the string
863
 
864
         // sscanf cannot handle "" or '' as empty value, this is done here
865
         if ((wcscmp (temp_value, L"\"\"") == 0) || (wcscmp (temp_value, L"''") == 0))
866
            temp_value[0] = 0; // empty string
867
 
868
         INIFile_WriteEntryAsString (dictionary, temp_section, temp_entry, temp_value); // add to dictionary
869
      }
870
   }
871
 
872
   fclose (fp); // finished, close the file
873
 
874
   return ((void *) dictionary); // and return
875
}
876
 
877
 
878
unsigned char INIFile_SaveINIFile (const wchar_t *filename, void *ini_data)
879
{
880
   // this function dumps a given dictionary into a loadable ini file.
881
 
882
   FILE *fp;
883
   int section_index;
884
   wchar_t *section_name;
885
   int section_count;
886
   int entry_index;
887
   int length;
888
   dictionary_t *dictionary;
889
   dictionary_entry_t *entry;
890
 
891
   // try to open the INI file in ASCII write mode
892
   fp = _wfsopen (filename, L"w, ccs=UNICODE", _SH_DENYNO);
893
   if (fp == NULL)
894
      return (0); // cancel if unable to open file
895
 
896
   // get a hand on the INI data dictionary
897
   dictionary = (dictionary_t *) ini_data;
898
 
899
   // keep only the file name for the comment
900
   if (wcsrchr (filename, '/') != NULL)
901
      filename = wcsrchr (filename, '/') + 1;
902
   else if (wcsrchr (filename, '\\') != NULL)
903
      filename = wcsrchr (filename, '\\') + 1;
904
 
905
   // print the INI file name as a comment
906
   fwprintf (fp, L"# %s\n", filename);
907
 
908
   // get the number of sections there are in this INI dictionary
909
   section_count = INIFile_GetNumberOfSections (dictionary);
910
 
911
   // for each section...
912
   for (section_index = 0; section_index < section_count; section_index++)
913
   {
914
      section_name = INIFile_GetSectionName (dictionary, section_index); // read section name
915
 
916
      // is it the default section ?
917
      if (wcscmp (section_name, INIFile_default_section) == 0)
918
         fwprintf (fp, L"\n"); // don't put the default section's name in the INI file
919
      else
920
         fwprintf (fp, L"\n[%s]\n", section_name); // dump all other sections into the INI file
921
 
922
      // build the section identifier to be used when looking up the dictionary
923
      swprintf_s (temp_key, WSIZEOF (temp_key), L"%s%c", section_name, INI_SECTION_SEPARATOR);
924
      length = (int) wcslen (temp_key);
925
 
926
      // then for each entry in the dictionary...
927
      for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++)
928
      {
929
         entry = &dictionary->entries[entry_index]; // quick access to entry
930
 
931
         if (entry->key[0] == 0)
932
            continue; // skip empty slots
933
 
934
         // does this key belong to the section we want ? if so, dump it
935
         if (wcsncmp (entry->key, temp_key, length) == 0)
936
         {
937
            // if key starts or ends with a space, enclose it between quotes, else don't
938
            if ((entry->value[0] != 0)
939
                && (iswspace (entry->value[0]) || iswspace (entry->value[wcslen (entry->value) - 1])))
940
               fwprintf (fp, L"%s = \"%s\"\n", entry->key + length, entry->value);
941
            else
942
               fwprintf (fp, L"%s = %s\n", entry->key + length, entry->value);
943
         }
944
      }
945
   }
946
 
947
   fclose (fp); // finished, close the file
948
 
949
   return (1); // and return TRUE as the save occured successfully
950
}
951
 
952
 
953
void INIFile_FreeINIFile (void *ini_data)
954
{
955
   // this function frees the dictionary object used to store an INI file data
956
 
957
   if (ini_data == NULL)
958
      return; // consistency check
959
 
960
   Dictionary_DestroyDictionary ((dictionary_t *) ini_data); // just destroy the dictionary
961
   return; // finished
962
}