View Single Post
Author Message
statistician
Member
Join Date: Jan 2016
Location: Finland
Old 02-04-2016 , 02:14   [STOCK] merge KeyValues
Reply With Quote #1

Here's a stock to merge two KeyValues together (and a couple others that are used).

Code:
#define MAX_SECTION_LENGTH    128

/**
 * Returns whether ot not the current key is an empty tree (section).
 *
 * @param kv    KeyValues handle.
 * @return      True if current key is a tree and is empty, false otherwise.
 */
stock bool KvIsEmptyTree(Handle kv) {
    if (KvGotoFirstSubKey(kv, false)) {
        KvGoBack(kv);
        return false;
    }

    char value[128];
    KvGetString(kv, NULL_STRING, value, sizeof(value), "#novalue");
    return StrEqual(value, "#novalue"); // is it an empty tree
}

/**
 * Returns whether ot not the current key is a tree (section).
 *
 * @param kv    KeyValues handle.
 * @return      True if current key is a tree (section), false otherwise.
 */
stock bool KvIsTree(Handle kv) {
    if (KvGotoFirstSubKey(kv, false)) {
        KvGoBack(kv);
        return true;
    }

    char value[128];
    KvGetString(kv, NULL_STRING, value, sizeof(value), "#novalue");
    return StrEqual(value, "#novalue"); // is it an empty tree
}

/**
 * Jumps to a key that is on the same level as the current one,
 * if such a key exists.
 *
 * @param kv    KeyValues handle.
 * @return      True if the key existed, false otherwise.
 */
stock bool KvJumpToSiblingKey(Handle kv, char[] target) {
    char start[MAX_SECTION_LENGTH];
    KvGetSectionName(kv, start, sizeof(start));

    if (KvNodesInStack(kv) == 0)
        return StrEqual(start, target);

    KvGoBack(kv);
    if (KvJumpToKey(kv, target))
        return true;

    KvJumpToKey(kv, start);
    return false;
}

/**
 * Merges two KeyValues into one.
 *
 * @param kv_to         KeyValues handle to which new information should be written.
 * @param kv_form       KeyValues handle from which new information should be copied.
 * @noreturn
 */
stock void KvMerge(Handle kv_to, Handle kv_from) {
    char key_from[MAX_SECTION_LENGTH], value_from[MAX_SECTION_LENGTH];
    do {
        KvGetSectionName(kv_from, key_from, sizeof(key_from));

        if (KvIsEmptyTree(kv_from)) {
            KvGoBack(kv_to);
            KvJumpToKey(kv_to, key_from, true); 
        } else if (KvIsTree(kv_from)) {
            KvGotoFirstSubKey(kv_from, false);

            if (!KvJumpToSiblingKey(kv_to, key_from)) {
                KvJumpToKey(kv_to, key_from, true);
                KvGetSectionName(kv_from, key_from, sizeof(key_from));
                if (KvIsTree(kv_from)) {
                    KvJumpToKey(kv_to, key_from, true);
                    KvGoBack(kv_to);
                } else {
                    KvGetString(kv_from, NULL_STRING, value_from, sizeof(value_from));
                    KvSetString(kv_to, key_from, value_from);
                }
            }

            KvGotoFirstSubKey(kv_to, false);

            KvMerge(kv_to, kv_from);

            KvGoBack(kv_from);
            KvGoBack(kv_to);
        } else {
            KvGetString(kv_from, NULL_STRING, value_from, sizeof(value_from));
            KvGoBack(kv_to);
            KvSetString(kv_to, key_from, value_from);
            KvGotoFirstSubKey(kv_to, false);
        }
    } while (KvGotoNextKey(kv_from, false));
}
I haven't tested merging extensively, but it worked for the cases I did test. Let me know if you have suggestions or notice any problems.

Example:

kv1:

Code:
"KeyValues"
{
    "key1"    "value"

    "names" {
        "name1"    "bob"
        "name2"    "james"
    }
}
kv2:

Code:
"KeyValues"
{
    "key2"    "value"

    "names" {
        "name3"    "mike"
        "name4"    "henry"
    }
}
If I do KvMerge(kv1, kv2), kv1 is now:

Code:
"KeyValues"
{
    "key1"    "value"
    "key2"    "value"

    "names" {
        "name1"    "bob"
        "name2"    "james"
        "name3"    "mike"
        "name4"    "henry"
    }
}

Last edited by statistician; 02-04-2016 at 02:44.
statistician is offline