- // inifile.c 
-   
- #include "common.h" 
-   
-   
- // WARNING: INI SECTION NAMES ARE CASE SENSITIVE, BUT KEY NAMES ARE NOT! 
-   
-   
- // internal definitions 
- #define DICTIONARY_PAGE_SIZE 128 
- #define STRING_MAXSIZE 65536 
- #define INI_SECTION_SEPARATOR ']' 
- #define INI_INVALID_KEY (wchar_t *) 0x7fffffff 
- #define WSIZEOF(a) (sizeof (a) / sizeof (wchar_t)) 
-   
-   
- // dictionary structure definitions 
- typedef struct dictionary_entry_s 
- { 
-    wchar_t *key; // key string (mallocated) 
-    unsigned long hash; // key hash value 
-    wchar_t *value; // value string (mallocated) 
- } dictionary_entry_t; 
-   
-   
- typedef struct dictionary_s 
- { 
-    int size; // storage size 
-    dictionary_entry_t *entries; // array of entries (mallocated) 
-    int entry_count; // number of entries in dictionary 
- } dictionary_t; 
-   
-   
- // dictionary function prototypes 
- static dictionary_t *Dictionary_CreateDictionary (int size); 
- static void Dictionary_DestroyDictionary (dictionary_t *dictionary); 
- static dictionary_entry_t *Dictionary_GetKey (dictionary_t *dictionary, wchar_t *key); 
- static wchar_t *Dictionary_ReadKey (dictionary_t *dictionary, wchar_t *key, wchar_t *default_value); 
- static void Dictionary_WriteKey (dictionary_t *dictionary, wchar_t *key, wchar_t *value); 
- static void Dictionary_DeleteKey (dictionary_t *dictionary, wchar_t *key); 
- static unsigned long Dictionary_ComputeHashValueForKey (wchar_t *key); 
-   
-   
- // ini file function prototypes 
- void *INIFile_NewINIFile (void); 
- int INIFile_GetNumberOfSections (void *ini_data); 
- wchar_t *INIFile_GetSectionName (void *ini_data, int section_index); 
- int INIFile_GetNumberOfEntries (void *ini_data, wchar_t *section); 
- wchar_t *INIFile_GetEntryName (void *ini_data, wchar_t *section, int entry_index); 
- unsigned char INIFile_ReadEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char default_value); 
- long INIFile_ReadEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long default_value); 
- unsigned long INIFile_ReadEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long default_value); 
- double INIFile_ReadEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double default_value); 
- wchar_t *INIFile_ReadEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *default_value); 
- void INIFile_WriteEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char value); 
- void INIFile_WriteEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long value); 
- void INIFile_WriteEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long value); 
- void INIFile_WriteEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double value); 
- void INIFile_WriteEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *value); 
- void INIFile_DeleteEntry (void *ini_data, wchar_t *section, wchar_t *entry); 
- void INIFile_DeleteSection (void *ini_data, wchar_t *section); 
- void *INIFile_LoadINIFile (const wchar_t *filename); 
- unsigned char INIFile_SaveINIFile (const wchar_t *filename, void *ini_data); 
- void INIFile_FreeINIFile (void *ini_data); 
-   
-   
- // internal variables 
- static wchar_t *INIFile_default_section = L"__default"; // MUST BE A READ-WRITE VARIABLE AND NOT A DEFINE 
-   
-   
- static dictionary_t *Dictionary_CreateDictionary (int size) 
- { 
-    // this function allocates a new dictionary object of given size and returns it. If you do 
-    // not know in advance (roughly) the number of entries in the dictionary, give size = 0. 
-   
-    dictionary_t *dictionary; 
-   
-    // if no size was specified, allocate space for one page 
-    if (size < DICTIONARY_PAGE_SIZE) 
-       size = DICTIONARY_PAGE_SIZE; 
-   
-    // allocate one instance of the dictionary and blank it out 
-    dictionary = (dictionary_t *) SAFE_malloc (1, sizeof (dictionary_t), true); 
-    dictionary->size = size; // dictionary can currently hold size entries 
-   
-    // allocate space for the dictionary entries and zero all the crap out 
-    dictionary->entries = (dictionary_entry_t *) SAFE_malloc (size, sizeof (dictionary_entry_t), true); 
-   
-    return (dictionary); // finished, return a pointer to the dictionary object 
- } 
-   
-   
- static void Dictionary_DestroyDictionary (dictionary_t *dictionary) 
- { 
-    // frees a dictionary object and all memory associated to it. 
-   
-    int entry_index; 
-   
-    if (dictionary == NULL) 
-       return; // consistency check 
-   
-    for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) 
-    { 
-       SAFE_free ((void **) &dictionary->entries[entry_index].key); // free all dictionary keys 
-       SAFE_free ((void **) &dictionary->entries[entry_index].value); // free all dictionary values 
-    } 
-   
-    SAFE_free ((void **) &dictionary->entries); // free the entries array 
-    SAFE_free ((void **) &dictionary); // and finally, free the dictionary itself 
-   
-    return; // finished 
- } 
-   
-   
- static dictionary_entry_t *Dictionary_GetKey (dictionary_t *dictionary, wchar_t *key) 
- { 
-    // this function locates a key in a dictionary and returns a pointer to it, or a NULL 
-    // pointer if no such key can be found in dictionary. The returned pointer points to data 
-    // internal to the dictionary object, do not try to free or modify it. 
-   
-    unsigned long hash; 
-    register int entry_index; 
-   
-    hash = Dictionary_ComputeHashValueForKey (key); // get the hash value for key 
-   
-    // cycle through all entries in the dictionary 
-    for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) 
-    { 
-       if (dictionary->entries[entry_index].key[0] == 0) 
-          continue; // skip empty slots 
-   
-       // compare hash AND string, to avoid hash collisions 
-       if ((hash == dictionary->entries[entry_index].hash) && (wcscmp (key, dictionary->entries[entry_index].key) == 0)) 
-          return (&dictionary->entries[entry_index]); // return a pointer to the key if we find it 
-    } 
-   
-    return (NULL); // else return a NULL pointer 
- } 
-   
-   
- static wchar_t *Dictionary_ReadKey (dictionary_t *dictionary, wchar_t *key, wchar_t *default_value) 
- { 
-    // this function locates a key in a dictionary and returns a pointer to its value, or the 
-    // passed 'def' pointer if no such key can be found in dictionary. The returned char pointer 
-    // points to data internal to the dictionary object, do not try to free or modify it. 
-   
-    dictionary_entry_t *element; 
-   
-    element = Dictionary_GetKey (dictionary, key); // query the dictionary for the key 
-    if (element != NULL) 
-       return (element->value); // if we found a valid key, return the value associed to it 
-   
-    return (default_value); // else return the default value 
- } 
-   
-   
- static void Dictionary_WriteKey (dictionary_t *dictionary, wchar_t *key, wchar_t *value) 
- { 
-    // sets a value in a dictionary. If the given key is found in the dictionary, the associated 
-    // value is replaced by the provided one. If the key cannot be found in the dictionary, it 
-    // is added to it. 
-   
-    unsigned long hash; 
-    register int entry_index; 
-    size_t bufsize; 
-   
-    hash = Dictionary_ComputeHashValueForKey (key); // compute hash for this key 
-   
-    // for each entry in the dictionary... 
-    for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) 
-    { 
-       if (dictionary->entries[entry_index].key[0] == 0) 
-           continue; // skip empty slots 
-   
-       // does that key have the same hash value AND the same name ? 
-       if ((hash == dictionary->entries[entry_index].hash) && (wcscmp (key, dictionary->entries[entry_index].key) == 0)) 
-       { 
-          // we've found the right key: modify its value and return 
-   
-          if (value == NULL) 
-             value = L""; // if a null value was specified, use an empty string 
-   
-          bufsize = wcslen (value) + 1; 
-          dictionary->entries[entry_index].value = (wchar_t *) SAFE_realloc (dictionary->entries[entry_index].value, 0, bufsize, sizeof (wchar_t), false); 
-          wcscpy_s (dictionary->entries[entry_index].value, bufsize, value); // copy the value string 
-   
-          return; // value has been modified: return 
-       } 
-    } 
-   
-    // here either the dictionary was empty, or we couldn't find a similar value: add a new one 
-   
-    // cycle through all entries in the dictionary... 
-    for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) 
-       if (dictionary->entries[entry_index].key[0] == 0) 
-          break; // and stop at the first empty one we find 
-   
-    // no empty entry found, see if dictionary needs to grow or if there's still room left 
-    if (entry_index == dictionary->size) 
-    { 
-       // mallocate some more space for the dictionary and zero all that crap out 
-       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 
-       dictionary->size += DICTIONARY_PAGE_SIZE; // increase dictionary size 
-    } 
-   
-    // copy the new key 
-    bufsize = wcslen (key) + 1; 
-    dictionary->entries[entry_index].key = (wchar_t *) SAFE_malloc (bufsize, sizeof (wchar_t), false); 
-    wcscpy_s (dictionary->entries[entry_index].key, bufsize, key); // copy the key string... 
-    dictionary->entries[entry_index].hash = hash; // ...and store the hash value 
-   
-    // copy the new value 
-    if (value == NULL) 
-       value = L""; // if a null value was specified, use an empty string 
-    bufsize = wcslen (value) + 1; 
-    dictionary->entries[entry_index].value = (wchar_t *) SAFE_malloc (bufsize, sizeof (wchar_t), false); 
-    wcscpy_s (dictionary->entries[entry_index].value, bufsize, value); // copy the value string 
-   
-    dictionary->entry_count++; // there is one more entry in the dictionary now 
-    return; 
- } 
-   
-   
- static void Dictionary_DeleteKey (dictionary_t *dictionary, wchar_t *key) 
- { 
-    // this function deletes a key in a dictionary. Nothing is done if the key cannot be found. 
-   
-    unsigned long hash; 
-    register int entry_index; 
-   
-    hash = Dictionary_ComputeHashValueForKey (key); // get the hash value for key 
-   
-    // cycle through all entries in the dictionary... 
-    for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) 
-    { 
-       if (dictionary->entries[entry_index].key[0] == 0) 
-          continue; // skip empty slots 
-   
-       // compare hash AND string, to avoid hash collisions 
-       if ((hash == dictionary->entries[entry_index].hash) && (wcscmp (key, dictionary->entries[entry_index].key) == 0)) 
-          break; // break as soon as we've found the key we want 
-    } 
-   
-    if (entry_index == dictionary->entry_count) 
-       return; // if key was not found, just return 
-   
-    // free its buffers, then clear the key, the hash and its value data 
-    SAFE_free ((void **) &dictionary->entries[entry_index].key); 
-    SAFE_free ((void **) &dictionary->entries[entry_index].value); 
-    memset (&dictionary->entries[entry_index], 0, sizeof (dictionary_entry_t)); 
-   
-    dictionary->entry_count--; // there is one entry less in the dictionary now 
-    return; 
- } 
-   
-   
- static unsigned long Dictionary_ComputeHashValueForKey (wchar_t *key) 
- { 
-    // Compute the hash key for a string. This hash function has been taken from an article in 
-    // Dr Dobbs Journal. This is normally a collision-free function, distributing keys evenly. 
-    // The key is stored anyway in the struct so that collision can be avoided by comparing the 
-    // key itself in last resort. THIS FUNCTION IS TO BE USED INTERNALLY ONLY! 
-   
-    register unsigned long hash; 
-    int length; 
-    int char_index; 
-   
-    hash = 0; 
-    length = (int) wcslen (key); 
-   
-    // for each character of the string... 
-    for (char_index = 0; char_index < length; char_index++) 
-    { 
-       hash += (unsigned long) key[char_index]; // take it in account and compute the hash value 
-       hash += (hash << 10); 
-       hash ^= (hash >> 6); 
-    } 
-   
-    hash += (hash << 3); // finalize hashing (what the hell does it do, I have no clue) 
-    hash ^= (hash >> 11); 
-    hash += (hash << 15); 
-   
-    return (hash); // and return the hash value 
- } 
-   
-   
- void *INIFile_NewINIFile (void) 
- { 
-    // allocates a dictionary for a new INI file. Mostly a helper function. 
-   
-    return ((void *) Dictionary_CreateDictionary (0)); 
- } 
-   
-   
- int INIFile_GetNumberOfSections (void  *ini_data) 
- { 
-    // this function returns the number of sections found in a dictionary. The test to recognize 
-    // sections is done on the string stored in the dictionary: a section name is given as 
-    // "section" whereas a key is stored as "section]key", thus the test looks for entries that 
-    // do NOT contain a ']'. This clearly fails in the case a section name contains a ']', 
-    // but this should simply be avoided. 
-   
-    int section_count; 
-    int entry_index; 
-    dictionary_t *dictionary; 
-   
-    if (ini_data == NULL) 
-       return (0); // consistency check 
-   
-    dictionary = (dictionary_t *) ini_data; 
-   
-    section_count = 0; // no sections found yet 
-   
-    // for each entry in the dictionary... 
-    for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) 
-    { 
-       if (dictionary->entries[entry_index].key[0] == 0) 
-          continue; // skip empty slots 
-   
-       if (wcschr (dictionary->entries[entry_index].key, INI_SECTION_SEPARATOR) == NULL) 
-          section_count++; // if this entry has NO section separator, then it's a section name 
-    } 
-   
-    return (section_count); // return the section count we found 
- } 
-   
-   
- wchar_t *INIFile_GetSectionName (void *ini_data, int section_index) 
- { 
-    // this function locates the n-th section in a dictionary and returns its name as a pointer 
-    // to a string allocated inside the dictionary. Do not free or modify the returned string!! 
-    // This function returns NULL in case of error. 
-   
-    int sections_found; 
-    int entry_index; 
-    dictionary_t *dictionary; 
-   
-    if ((ini_data == NULL) || (section_index < 0)) 
-       return (NULL); // consistency check 
-   
-    dictionary = (dictionary_t *) ini_data; 
-   
-    sections_found = 0; // no sections found yet 
-   
-    // for each entry in the dictionary... 
-    for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) 
-    { 
-       if (dictionary->entries[entry_index].key[0] == 0) 
-          continue; // skip empty slots 
-   
-       // is this entry a section name ? 
-       if (wcschr (dictionary->entries[entry_index].key, INI_SECTION_SEPARATOR) == NULL) 
-       { 
-          // is it the section we want ? 
-          if (sections_found == section_index) 
-             return (dictionary->entries[entry_index].key); // then return its name 
-   
-          sections_found++; // we found one section more 
-       } 
-    } 
-   
-    return (INIFile_default_section); // section was not found, return the default section name 
- } 
-   
-   
- int INIFile_GetNumberOfEntries (void  *ini_data, wchar_t *section) 
- { 
-    // this function returns the number of entries found in a dictionary within a given section. 
-    // The test to recognize section entries is done on the string stored in the dictionary: a 
-    // section name is given as "section" whereas a key is stored as "section]key", thus the test 
-    // looks for all entries that begin with "section]". 
-   
-    int entry_count; 
-    int entry_index; 
-    int sectionname_length; 
-    dictionary_t *dictionary; 
-   
-    if (ini_data == NULL) 
-       return (0); // consistency check 
-   
-    dictionary = (dictionary_t *) ini_data; 
-    sectionname_length = wcslen (section); 
-   
-    entry_count = 0; // no entries found yet 
-   
-    // for each entry in the dictionary... 
-    for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) 
-    { 
-       if (dictionary->entries[entry_index].key[0] == 0) 
-          continue; // skip empty slots 
-   
-       if ((wcsncmp (dictionary->entries[entry_index].key, section, sectionname_length) == 0) 
-           && (dictionary->entries[entry_index].key[sectionname_length] == INI_SECTION_SEPARATOR)) 
-          entry_count++; // if this entry has section name + separator, then it's one of those we want 
-    } 
-   
-    return (entry_count); // return the number of entries we found in the given section 
- } 
-   
-   
- wchar_t *INIFile_GetEntryName (void *ini_data, wchar_t *section, int entry_index) 
- { 
-    // this function queries a dictionary for a key. A key as read from an ini file is given as 
-    // "section]key". If the key cannot be found, NULL is returned. The returned char pointer 
-    // points to a string allocated in the dictionary, do not free or modify it. 
-   
-    int i; 
-    int length; 
-    int found_keys; 
-    dictionary_t *dictionary; 
-    dictionary_entry_t *entry; 
-   
-    if (ini_data == NULL) 
-       return (NULL); // consistency check 
-   
-    if (section == NULL) 
-       section = INIFile_default_section; // if no section was provided, use empty section name 
-   
-    dictionary = (dictionary_t *) ini_data; // quick access to dictionary 
-   
-    length = wcslen (section); 
-   
-    // cycle through all remaining entries 
-    found_keys = 0; 
-    for (i = 0; i < dictionary->entry_count; i++) 
-    { 
-       entry = &dictionary->entries[i]; // quick access to each entry 
-   
-       if ((wcsncmp (entry->key, section, length) != 0) || (entry->key[length] != INI_SECTION_SEPARATOR)) 
-          continue; // if this key doesn't belong to the wanted section, skip it 
-   
-       // this key belongs to the wanted section. Is it the key we want ? 
-       if (found_keys == entry_index) 
-          return (&entry->key[length + 1]); // if so, return its name 
-   
-       found_keys++; // it wasn't the key we want, so increase counter and proceed to the next one 
-    } 
-   
-    return (NULL); // if not found, return NULL 
- } 
-   
-   
- unsigned char INIFile_ReadEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char default_value) 
- { 
-    // this function queries a dictionary for a key. A key as read from an ini file is given as 
-    // "section]key". If the key cannot be found, the notfound value is returned. A true boolean 
-    // is found if a string starting with either 'y', 'Y', 't', 'T' or '1' is matched. A false 
-    // boolean is found if a string starting with 'n', 'N', 'f', 'F' or '0' is matched. 
-   
-    wchar_t *value; 
-   
-    if (ini_data == NULL) 
-       return (default_value); // consistency check 
-   
-    // get the value as a string first 
-    value = INIFile_ReadEntryAsString ((dictionary_t *) ini_data, section, entry, INI_INVALID_KEY); 
-   
-    if (value == INI_INVALID_KEY) 
-       return (default_value); // if the entry could not be read, return the default value 
-   
-    // decide of the boolean's value by looking at the first character 
-    if ((toupper (value[0]) == 'T') || (toupper (value[0]) == 'Y') || (value[0] == '1')) 
-      return (1); // boolean value is TRUE 
-    else if ((toupper (value[0]) == 'F') || (toupper (value[0]) == 'N') || (value[0] == '0')) 
-      return (0); // boolean value is FALSE 
-   
-    return (default_value); // boolean value is undefined, return default value 
- } 
-   
-   
- long INIFile_ReadEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long default_value) 
- { 
-    // this function queries a dictionary for a key. A key as read from an ini file is given as 
-    // "section]key". If the key cannot be found, the notfound value is returned. 
-   
-    wchar_t *value; 
-   
-    if (ini_data == NULL) 
-       return (default_value); // consistency check 
-   
-    // get the value as a string first 
-    value = INIFile_ReadEntryAsString ((dictionary_t *) ini_data, section, entry, INI_INVALID_KEY); 
-   
-    if (value == INI_INVALID_KEY) 
-       return (default_value); // if the entry could not be read, return the default value 
-   
-    return (_wtol (value)); // else convert the value to a long integer and return it 
- } 
-   
-   
- unsigned long INIFile_ReadEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long default_value) 
- { 
-    // this function queries a dictionary for a key. A key as read from an ini file is given as 
-    // "section]key". If the key cannot be found, the notfound value is returned. 
-   
-    wchar_t *value; 
-   
-    if (ini_data == NULL) 
-       return (default_value); // consistency check 
-   
-    // get the value as a string first 
-    value = INIFile_ReadEntryAsString ((dictionary_t *) ini_data, section, entry, INI_INVALID_KEY); 
-   
-    if (value == INI_INVALID_KEY) 
-       return (default_value); // if the entry could not be read, return the default value 
-   
-    return (wcstoul (value, NULL, 10)); // else convert the value to a long integer and return it 
- } 
-   
-   
- double INIFile_ReadEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double default_value) 
- { 
-    // this function queries a dictionary for a key. A key as read from an ini file is given as 
-    // "section]key". If the key cannot be found, the notfound value is returned. 
-   
-    wchar_t *value; 
-   
-    if (ini_data == NULL) 
-       return (default_value); // consistency check 
-   
-    // get the value as a string first 
-    value = INIFile_ReadEntryAsString ((dictionary_t *) ini_data, section, entry, INI_INVALID_KEY); 
-   
-    if (value == INI_INVALID_KEY) 
-       return (default_value); // if the entry could not be read, return the default value 
-   
-    return (_wtof (value)); // else convert the value to a double precision number and return it 
- } 
-   
-   
- wchar_t *INIFile_ReadEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *default_value) 
- { 
-    // this function queries a dictionary for a key. A key as read from an ini file is given as 
-    // "section]key". If the key cannot be found, the pointer passed as 'def' is returned. The 
-    // returned char pointer points to a string allocated in the dictionary, do not free or 
-    // modify it. 
-   
-    size_t length; 
-    size_t i; 
-    size_t keysize; 
-    wchar_t *key; 
-    wchar_t *value; 
-   
-    if (ini_data == NULL) 
-       return (default_value); // consistency check 
-   
-    if (section == NULL) 
-       section = INIFile_default_section; // if no section was provided, use empty section name 
-   
-    // allocate some space for the dictionary key name 
-    keysize = wcslen (section) + 1 + wcslen (entry) + 1; 
-    key = (wchar_t *) SAFE_malloc (keysize, sizeof (wchar_t), false); 
-   
-    // if we were given an entry, build a key as section]entry 
-    if (entry != NULL) 
-    { 
-       swprintf_s (key, keysize, L"%s%c%s", section, INI_SECTION_SEPARATOR, entry); // compose the key 
-   
-       i = wcslen (key); // get the key string length 
-       if (i < keysize) 
-          length = i; 
-       else 
-          length = keysize - 1; // clamp it to a max value 
-   
-       // for each character in the string after the section separator... 
-       for (i = wcslen (section) + 1; i < length; i++) 
-          key[i] = towlower (key[i]); // convert it to lowercase 
-       key[i] = 0; // terminate the string 
-    } 
-   
-    // else it must be a section name 
-    else 
-       wcscpy_s (key, keysize, section); // copy the name into the key 
-   
-    value = Dictionary_ReadKey ((dictionary_t *) ini_data, key, default_value); // query the dictionary... 
-    SAFE_free ((void **) &key); // free the key name, we no longer need it 
-    return (value); // ...and return the value 
- } 
-   
-   
- void INIFile_WriteEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char value) 
- { 
-    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is 
-    // modified to contain the provided value. If it cannot be found, it is created. 
-   
-    // according the boolean's value, write the equivalent string 
-    if (value) 
-       INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, L"true"); // write the new entry 
-    else 
-       INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, L"false"); // write the new entry 
-   
-    return; // finished 
- } 
-   
-   
- void INIFile_WriteEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long value) 
- { 
-    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is 
-    // modified to contain the provided value. If it cannot be found, it is created. 
-   
-    wchar_t *number_as_string; 
-   
-    // allocate some space for the number's string representation 
-    number_as_string = (wchar_t *) SAFE_malloc (32, sizeof (wchar_t), false); 
-   
-    // build the long integer value equivalent string (use printf facility) 
-    swprintf_s (number_as_string, 32, L"%ld", value); 
-    INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry 
-   
-    SAFE_free ((void **) &number_as_string); // free the number's string representation, we no longer need it 
-    return; // finished 
- } 
-   
-   
- void INIFile_WriteEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long value) 
- { 
-    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is 
-    // modified to contain the provided value. If it cannot be found, it is created. 
-   
-    wchar_t *number_as_string; 
-   
-    // allocate some space for the number's string representation 
-    number_as_string = (wchar_t *) SAFE_malloc (32, sizeof (wchar_t), false); 
-   
-    // build the long integer value equivalent string (use printf facility) 
-    swprintf_s (number_as_string, 32, L"%lu", value); 
-    INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry 
-   
-    SAFE_free ((void **) &number_as_string); // free the number's string representation, we no longer need it 
-    return; // finished 
- } 
-   
-   
- void INIFile_WriteEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double value) 
- { 
-    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is 
-    // modified to contain the provided value. If it cannot be found, it is created. 
-   
-    wchar_t *number_as_string; 
-   
-    // allocate some space for the number's string representation 
-    number_as_string = (wchar_t *) SAFE_malloc (64, sizeof (wchar_t), false); 
-   
-    // build the long integer value equivalent string (use printf facility) 
-    swprintf_s (number_as_string, 64, L"%g", value); 
-    INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry 
-   
-    SAFE_free ((void **) &number_as_string); // free the number's string representation, we no longer need it 
-    return; // finished 
- } 
-   
-   
- void INIFile_WriteEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *value) 
- { 
-    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is 
-    // modified to contain the provided value. If it cannot be found, it is created. 
-   
-    size_t length; 
-    size_t i; 
-    size_t keysize; 
-    wchar_t *key; 
-   
-    if (ini_data == NULL) 
-       return; // consistency check 
-   
-    if (section == NULL) 
-       section = INIFile_default_section; // if no section was provided, use empty section name 
-   
-    // allocate some space for the dictionary key name 
-    keysize = wcslen (section) + 1 + (entry != NULL ? wcslen (entry) + 1 : 0); 
-    key = (wchar_t *) SAFE_malloc (keysize, sizeof (wchar_t), false); 
-   
-    // if we were given an entry, build a key as section#entry 
-    if (entry != NULL) 
-    { 
-       Dictionary_WriteKey ((dictionary_t *) ini_data, section, NULL); // create the section if it doesn't exist 
-   
-       swprintf_s (key, keysize, L"%s%c%s", section, INI_SECTION_SEPARATOR, entry); // compose the key 
-   
-       i = wcslen (key); // get the key string length 
-       if (i < keysize) 
-          length = i; 
-       else 
-          length = keysize - 1; // clamp it to a max value 
-   
-       // for each character in the string after the section separator... 
-       for (i = wcslen (section) + 1; i < length; i++) 
-          key[i] = towlower (key[i]); // convert it to lowercase 
-       key[i] = 0; // terminate the string 
-    } 
-   
-    // else it must be a section name 
-    else 
-       wcscpy_s (key, keysize, section); // copy the name into the key 
-   
-    Dictionary_WriteKey ((dictionary_t *) ini_data, key, value); // write the new key in the dictionary 
-    SAFE_free ((void **) &key); // free the key name, we no longer need it 
-    return; // finished 
- } 
-   
-   
- void INIFile_DeleteEntry (void *ini_data, wchar_t *section, wchar_t *entry) 
- { 
-    // deletes an entry in the dictionary 
-   
-    size_t length; 
-    size_t i; 
-    size_t keysize; 
-    wchar_t *key; 
-   
-    if (ini_data == NULL) 
-       return; // consistency check 
-   
-    if (section == NULL) 
-       section = INIFile_default_section; // if no section was provided, use empty section name 
-   
-    // allocate some space for the dictionary key name 
-    keysize = wcslen (section) + 1 + wcslen (entry) + 1; 
-    key = (wchar_t *) SAFE_malloc (keysize, sizeof (wchar_t), false); 
-   
-    // if we were given an entry, build a key as section#entry 
-    if (entry != NULL) 
-    { 
-       swprintf_s (key, keysize, L"%s%c%s", section, INI_SECTION_SEPARATOR, entry); // compose the key 
-   
-       i = wcslen (key); // get the key string length 
-       if (i < keysize) 
-          length = i; 
-       else 
-          length = keysize - 1; // clamp it to a max value 
-   
-       // for each character in the string after the section separator... 
-       for (i = wcslen (section) + 1; i < length; i++) 
-          key[i] = towlower (key[i]); // convert it to lowercase 
-       key[i] = 0; // terminate the string 
-    } 
-   
-    // else it must be a section name 
-    else 
-       wcscpy_s (key, keysize, section); // copy the name into the key 
-   
-    Dictionary_DeleteKey ((dictionary_t *) ini_data, key); 
-    SAFE_free ((void **) &key); // free the key name, we no longer need it 
-    return; 
- } 
-   
-   
- void INIFile_DeleteSection (void *ini_data, wchar_t *section) 
- { 
-    // deletes a whole INI section in the dictionary 
-   
-    int length; 
-    int i; 
-    dictionary_t *dictionary; 
-    dictionary_entry_t *entry; 
-   
-    if (ini_data == NULL) 
-       return; // consistency check 
-   
-    if (section == NULL) 
-       section = INIFile_default_section; // if no section was provided, use empty section name 
-   
-    dictionary = (dictionary_t *) ini_data; 
-   
-    length = (int) wcslen (section); // get the section string length 
-   
-    // for each entry in the dictionary... 
-    for (i = 0; i < dictionary->entry_count; i++) 
-    { 
-       entry = &dictionary->entries[i]; // quick access to each entry 
-   
-       if (entry->key[0] == 0) 
-          continue; // skip empty slots 
-   
-       // does this entry belong to the section we want ? 
-       if ((wcsncmp (entry->key, section, length) == 0) && (entry->key[length] == INI_SECTION_SEPARATOR)) 
-          Dictionary_DeleteKey (dictionary, entry->key); // yes, delete it 
-    } 
-   
-    Dictionary_DeleteKey (dictionary, section); // and finally delete the section name itself 
-    return; 
- } 
-   
-   
- void *INIFile_LoadINIFile (const wchar_t *filename) 
- { 
-    // this is the parser for ini files. This function is called, providing the name of the file 
-    // to be read. It returns a dictionary object that should not be accessed directly, but 
-    // through accessor functions instead. 
-   
-    wchar_t *line_buffer; 
-    FILE *fp; 
-    int fieldstart; 
-    int fieldstop; 
-    int length; 
-    int i; 
-    dictionary_t *dictionary; 
-    wchar_t *current_section; 
-    wchar_t *current_entry; 
-    wchar_t *current_value; 
-   
-    // try to open the INI file in ASCII read-only mode 
-    fp = _wfsopen (filename, L"r, ccs=UNICODE", _SH_DENYNO); 
-    if (fp == NULL) 
-       return (NULL); // cancel if file not found 
-   
-    dictionary = Dictionary_CreateDictionary (0); 
-   
-    // allocate some space for the current section, entry and values 
-    current_section = (wchar_t *) SAFE_malloc (STRING_MAXSIZE, sizeof (wchar_t), false); 
-    current_entry = (wchar_t *) SAFE_malloc (STRING_MAXSIZE, sizeof (wchar_t), false); 
-    current_value = (wchar_t *) SAFE_malloc (STRING_MAXSIZE, sizeof (wchar_t), false); 
-   
-    // set the default section for orphaned entries and add it to the dictionary 
-    wcscpy_s (current_section, STRING_MAXSIZE, INIFile_default_section); 
-    INIFile_WriteEntryAsString (dictionary, current_section, NULL, NULL); 
-   
-    // allocate some space for a line buffer 
-    line_buffer = (wchar_t *) SAFE_malloc (STRING_MAXSIZE + STRING_MAXSIZE, sizeof (wchar_t), false); 
-   
-    // read line per line... 
-    while (fgetws (line_buffer, STRING_MAXSIZE + STRING_MAXSIZE, fp) != NULL) 
-    { 
-       length = (int) wcslen (line_buffer); // get line length 
-   
-       while ((length > 0) && ((line_buffer[length - 1] == '\n') || (line_buffer[length - 1] == '\r'))) 
-          length--; // discard trailing newlines 
-   
-       fieldstart = 0; // let's now strip leading blanks 
-       while ((fieldstart < length) && iswspace (line_buffer[fieldstart])) 
-          fieldstart++; // ignore any tabs or spaces, going forward from the start 
-   
-       fieldstop = length - 1; // let's now strip trailing blanks 
-       while ((fieldstop >= 0) && iswspace (line_buffer[fieldstop])) 
-          fieldstop--; // ignore any tabs or spaces, going backwards from the end 
-   
-       for (i = fieldstart; i <= fieldstop; i++) 
-          line_buffer[i - fieldstart] = line_buffer[i]; // recopy line buffer without the spaces 
-       line_buffer[i - fieldstart] = 0; // and terminate the string 
-   
-       if ((line_buffer[0] == ';') || (line_buffer[0] == '#') || (line_buffer[0] == 0)) 
-          continue; // skip comment lines 
-   
-       // is it a valid section name ? 
-       if (swscanf_s (line_buffer, L"[%[^]]", current_section, STRING_MAXSIZE) == 1) 
-       { 
-          length = (int) wcslen (current_section); // get the section string length 
-   
-          fieldstart = 0; // let's now strip leading blanks 
-          while ((fieldstart < length) && iswspace (current_section[fieldstart])) 
-             fieldstart++; // ignore any tabs or spaces, going forward from the start 
-   
-          fieldstop = length - 1; // let's now strip trailing blanks 
-          while ((fieldstop >= 0) && iswspace (current_section[fieldstop])) 
-             fieldstop--; // ignore any tabs or spaces, going backwards from the end 
-   
-          for (i = fieldstart; i <= fieldstop; i++) 
-             current_section[i - fieldstart] = current_section[i]; // recopy section name w/out spaces 
-          current_section[i - fieldstart] = 0; // and terminate the string 
-   
-          INIFile_WriteEntryAsString (dictionary, current_section, NULL, NULL); // add to dictionary 
-       } 
-   
-       // else is it a valid entry/value pair that is enclosed between quotes? 
-       else if (swscanf_s (line_buffer, L"%[^=] = \"%[^\"]\"", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2) 
-       { 
-          length = (int) wcslen (current_entry); // get the entry string length 
-   
-          fieldstart = 0; // let's now strip leading blanks 
-          while ((fieldstart < length) && iswspace (current_entry[fieldstart])) 
-             fieldstart++; // ignore any tabs or spaces, going forward from the start 
-   
-          fieldstop = length - 1; // let's now strip trailing blanks 
-          while ((fieldstop >= 0) && iswspace (current_entry[fieldstop])) 
-             fieldstop--; // ignore any tabs or spaces, going backwards from the end 
-   
-          for (i = fieldstart; i <= fieldstop; i++) 
-             current_entry[i - fieldstart] = towlower (current_entry[i]); // recopy entry name w/out spaces 
-          current_entry[i - fieldstart] = 0; // and terminate the string 
-   
-          // when value is enclosed between quotes, DO NOT strip the blanks 
-   
-          // sscanf cannot handle "" or '' as empty value, this is done here 
-          if ((wcscmp (current_value, L"\"\"") == 0) || (wcscmp (current_value, L"''") == 0)) 
-             current_value[0] = 0; // empty string 
-   
-          INIFile_WriteEntryAsString (dictionary, current_section, current_entry, current_value); // add to dictionary 
-       } 
-   
-       // else is it a valid entry/value pair without quotes ? 
-       else if ((swscanf_s (line_buffer, L"%[^=] = '%[^\']'", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2) 
-                || (swscanf_s (line_buffer, L"%[^=] = %[^;#]", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2)) 
-       { 
-          length = (int) wcslen (current_entry); // get the entry string length 
-   
-          fieldstart = 0; // let's now strip leading blanks 
-          while ((fieldstart < length) && iswspace (current_entry[fieldstart])) 
-             fieldstart++; // ignore any tabs or spaces, going forward from the start 
-   
-          fieldstop = length - 1; // let's now strip trailing blanks 
-          while ((fieldstop >= 0) && iswspace (current_entry[fieldstop])) 
-             fieldstop--; // ignore any tabs or spaces, going backwards from the end 
-   
-          for (i = fieldstart; i <= fieldstop; i++) 
-             current_entry[i - fieldstart] = towlower (current_entry[i]); // recopy entry name w/out spaces 
-          current_entry[i - fieldstart] = 0; // and terminate the string 
-   
-          length = (int) wcslen (current_value); // get the value string length 
-   
-          fieldstart = 0; // let's now strip leading blanks 
-          while ((fieldstart < length) && iswspace (current_value[fieldstart])) 
-             fieldstart++; // ignore any tabs or spaces, going forward from the start 
-   
-          fieldstop = length - 1; // let's now strip trailing blanks 
-          while ((fieldstop >= 0) && iswspace (current_value[fieldstop])) 
-             fieldstop--; // ignore any tabs or spaces, going backwards from the end 
-   
-          for (i = fieldstart; i <= fieldstop; i++) 
-             current_value[i - fieldstart] = current_value[i]; // recopy entry name w/out spaces 
-          current_value[i - fieldstart] = 0; // and terminate the string 
-   
-          // sscanf cannot handle "" or '' as empty value, this is done here 
-          if ((wcscmp (current_value, L"\"\"") == 0) || (wcscmp (current_value, L"''") == 0)) 
-             current_value[0] = 0; // empty string 
-   
-          INIFile_WriteEntryAsString (dictionary, current_section, current_entry, current_value); // add to dictionary 
-       } 
-    } 
-   
-    fclose (fp); // finished, close the file 
-    SAFE_free ((void **) &line_buffer); // free the line buffer we allocated at the beginning of the function 
-    SAFE_free ((void **) ¤t_value); // free the current value buffer we allocated at the beginning of the function 
-    SAFE_free ((void **) ¤t_entry); // free the current entry buffer we allocated at the beginning of the function 
-    SAFE_free ((void **) ¤t_section); // free the current section buffer we allocated at the beginning of the function 
-   
-    return ((void *) dictionary); // and return a pointer to it 
- } 
-   
-   
- unsigned char INIFile_SaveINIFile (const wchar_t *filename, void *ini_data) 
- { 
-    // this function dumps a given dictionary into a loadable ini file. 
-   
-    FILE *fp; 
-    int section_index; 
-    wchar_t *section_name; 
-    int section_count; 
-    int entry_index; 
-    int length; 
-    dictionary_t *dictionary; 
-    dictionary_entry_t *entry; 
-   
-    // try to open the INI file in ASCII write mode 
-    fp = _wfsopen (filename, L"w, ccs=UNICODE", _SH_DENYNO); 
-    if (fp == NULL) 
-       return (0); // cancel if unable to open file 
-   
-    // get a hand on the INI data dictionary 
-    dictionary = (dictionary_t *) ini_data; 
-   
-    // keep only the file name for the comment 
-    if (wcsrchr (filename, '/') != NULL) 
-       filename = wcsrchr (filename, '/') + 1; 
-    else if (wcsrchr (filename, '\\') != NULL) 
-       filename = wcsrchr (filename, '\\') + 1; 
-   
-    // print the INI file name as a comment 
-    fwprintf (fp, L"# %s\n", filename); 
-   
-    // get the number of sections there are in this INI dictionary 
-    section_count = INIFile_GetNumberOfSections (dictionary); 
-   
-    // for each section... 
-    for (section_index = 0; section_index < section_count; section_index++) 
-    { 
-       section_name = INIFile_GetSectionName (dictionary, section_index); // read section name 
-   
-       // is it the default section ? 
-       if (wcscmp (section_name, INIFile_default_section) == 0) 
-          fwprintf (fp, L"\n"); // don't put the default section's name in the INI file 
-       else 
-          fwprintf (fp, L"\n[%s]\n", section_name); // dump all other sections into the INI file 
-   
-       length = (int) wcslen (section_name); 
-   
-       // then for each entry in the dictionary... 
-       for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) 
-       { 
-          entry = &dictionary->entries[entry_index]; // quick access to entry 
-   
-          if (entry->key[0] == 0) 
-             continue; // skip empty slots 
-   
-          // does this key belong to the section we want ? if so, dump it 
-          if ((wcsncmp (entry->key, section_name, length) == 0) && (entry->key[length] == INI_SECTION_SEPARATOR)) 
-          { 
-             // if key starts or ends with a space, enclose it between quotes, else don't 
-             if ((entry->value[0] != 0) 
-                 && (iswspace (entry->value[0]) || iswspace (entry->value[wcslen (entry->value) - 1]))) 
-                fwprintf (fp, L"%s = \"%s\"\n", &entry->key[length + 1], entry->value); 
-             else 
-                fwprintf (fp, L"%s = %s\n", &entry->key[length + 1], entry->value); 
-          } 
-       } 
-    } 
-   
-    fclose (fp); // finished, close the file 
-   
-    return (1); // and return TRUE as the save occured successfully 
- } 
-   
-   
- void INIFile_FreeINIFile (void *ini_data) 
- { 
-    // this function frees the dictionary object used to store an INI file data 
-   
-    if (ini_data == NULL) 
-       return; // consistency check 
-   
-    Dictionary_DestroyDictionary ((dictionary_t *) ini_data); // just destroy the dictionary 
-    return; // finished 
- } 
-