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"
}
}