I actually made this like 0.5 years ago, but I'm not sure why I had a problem making it. I looked at my old code as reference, and remade it from scratch in like an hour or so.
Note to self: modify binary functions to ability to write/read from a string, not only a file. (so that u can easily put other type of data into the tree in this script)
Explanation:
For those who don't know what a tree is in this case, it is just some sort of element (what I call a node) with sub-elements (sub-nodes, aka the "children" nodes of the "parent" node). Each of these nodes can store one string of information. I'll supply an example for stats:
Player Stats
Soldier
Kills:Total
1246
Kills:Weapon
1
3
58
542
39
41
10
17
416
0
81
28
1
9
Deaths:Total
1380
Major
Kills:Total
4968
Kills:Weapon
120
195
301
485
198
894
1981
199
323
82
114
28
12
36
Deaths-Total
9939
"Player Stats" is the root node (the first node, (should) contain everything)
Each child of this node is a player, value of which being the player's nickname. There are two for examples, Soldier and Major
In each player's node, you will find three example stat categories: Kills:Total, Kills:Weapon, and Deaths:Total. Each supply information as described. Kills:Total has one child, which is an integer saying the total number of kills this dude got. Same thing with Deaths:Total but with deaths. Kills:Weapon has 14 children, to represent the 14 weapons menu weapons (not all weapons, this was just an example so I didn't bother including every one), and the order respectively represents the same weapons as listed in the weapons menu.
Technically, we don't need to even put the nodes "Kills:Total", "Kills:Weapon", and "Deaths:Total" because we know the number of children in each one is a fixed number, and having these would just increase the file size. But having them is nice for clarity to help understand whats going on.
EDIT:
For those who want to know the file format, here it is:
[value : string][child location : ]
[children count : uint16][child location : array<uint16>]
type string is prefixed by uint16 representing the string's length.
type array is prefixed by uint16 representing the array's length (not size, but number of children)
This pattern just repeats throughout the database.
Another way to put it:
( [strlen : uint16] [str : char]* [children count : uint16] [child location : uint16]* )*
whereas * means it repeats.
I'm not sure if there is a standard or whatever to how I should style it; same goes along with string, I'm not sure if it is a standard to null-terminate it, but in case you need the null character inside the string (in case it is another database, possible a tree, or anything), I did not do that.