UPDATE: Read a few posts down.
The brres resource groups/nodes have posed problems for us, due to that fact that we didn't know how to generate the values in order to add/remove nodes as we pleased. This is particularly useful for models, because any given model might have more or less bones/polygons/textures.
I believe we may have figured it out, and with some dedication a person could build their own brres files from scratch. Using the following function, you can get a better idea of how the game processes node look-ups.
Each resource group is organized like a binary tree. Each node has an ID value that contains an index and bit number for the binary comparison. It also contains left/right index values for traversing the tree.
The root node in a group has an ID of 0xFFFF, and points to the first node in the tree. The last node in the tree points back to the root node.
So here it is (all values are big-endian):
Code:
struct ResourceGroup
{
int dataLength;
int nodeCount;
ResourceNode rootNode; //root node has an ID of 0xFFFF
}
struct ResourceNode //each node is 16 bytes long
{
ushort nodeId;
ushort padding;
ushort leftIndex;
ushort rightIndex;
int stringOffset;
int dataOffset;
}
ResourceNode* FindResource(ResourceGroup* group, string nodeName)
{
int strLen = nodeName.Length; //length of node name we're searching for
ResourceNode* rootEntry = &group->rootEntry;
ResourceNode* lastEntry = rootEntry;
ResourceNode* currentEntry = rootEntry[rootEntry->leftIndex]; //get first node
//Continue until last id is greater than current id
while(lastEntry->nodeId > currentEntry->nodeId)
{
lastEntry = currentEntry;
int charIndex = (currentEntry->nodeId & 0xFFF8) >> 3;
int charBit = currentEntry->nodeId & 7;
//If index is too high, continue to next size group
//If bit compared is zero, traverse left. Otherwise traverse right
if((charIndex >= strlen) || ((nodeName[charIndex] >> charBit) & 1 == 0))
currentEntry = rootEntry[currentEntry->leftIndex];
else
currentEntry = rootEntry[currentEntry->rightIndex];
}
//Compare the strings to make sure we've found a match
byte* stringAddress = (byte*)group + currentEntry->stringOffset;
if(nodeName != String(stringAddress))
return null;
else
return currentEntry;
}