LibCharacterKnowledge

Saved Variables

Encoding

All data is stored using a 6-bit encoding scheme that is similar to Base64, but with a different dictionary. Whereas standard Base64's dictionary is A-Za-z0-9+/, the dictionary used here is 0-9A-Za-z#%.

Bitfields

Knowledge is stored by index in bitfields. The first bit will represent the knowledge state for the first item of that type, and so on.

The byte position, when looking at the encoded string, is little-endian, so the first byte will contain information for the first 6 items, but the bit position within each byte is big-endian. This was done so that positions will remain consistent regardless of how many bytes are handled at once. For efficiency, LibCharacterKnowledge encodes and decodes data using 30-bit (5-byte) words, and this endianness ensures that the results will be identical if the data is instead decoded one byte at a time.

To translate from item index to item ID, it is necessary to then refer to the masterList section, where the IDs of all of the items are stored. The number of bytes used to encode each item is stored in the fieldSize entry. (In the future, if the range of item IDs grows sufficiently large, fieldSize can potentially change.)

Finally, the mapping between index and item ID is not guaranteed to be stable between major updates. For example, in the Update 47 PTS, ZOS removed a furnishing plan from the itemization table (this plan had never dropped in-game), which means that all of the items appearing after this removed item will have their indices shifted down by one once Update 47 goes live. There is special code in LibCharacterKnowledge to seamlessly handle any changes in indexing between major updates, but for someone who is trying to read the data manually, the important implication of all this is that knowledge data must be interpreted using the item master list from the the same patch. For example, knowledge data encoded in Update 46 must be read using the item master list from Update 46, not a list from Update 45 or 47.

Example

Let's take a look at the 38th recipe here:

["recipes"] = "qAvjDTgOPzmD%%%wAE8ZL%tAl%%%%%%%%%%x%%z%%%%x#lx%%tz%%%%%#F%%%%%%%%%%%%%V%%%%%%%%%%%%s%%%aF%%pm800000",

Since each byte represents 6 items, the 38th recipe will be encoded within the 7th byte ("g"), which decodes to 42 in decimal or 101010 in binary. Since the order within each encoded byte is big-endian, we are looking at the 2nd-highest order bit, which in this case is 0.

To determine which recipe that is, it is now necessary to refer to the recipes entry in the Update 46 masterList; the first 120 bytes of that are reproduced here:

B7VB7ZB7aB7bB7cB7dB7eB7fB7gB7hB7iB7jB7lB7mB7nB7oB7pB7qB7rB7tB7uB7vB7wB7xB7yB7zB7%B80B81B82B83B84B85B86B87B88B89B8BB8CB8D

The fieldSize for Update 46 is 3 and the 38th byte triplet is B8B, which decodes to 45579.

The recipe with item ID 45579 is Kaveh Stout, so this character does not know Kaveh Stout.

Recommendations

It should be clear by now that this data format was optimized for in-game use and is ill-suited for external consumption. For people who wish to use LibCharacterKnowledge data outside of the game, it might be easier to write an addon that uses LibCharacterKnowledge's APIs to read and then reformat this data into a form that is more suitable for your specific needs.