KI handling in new code
chars.keyitems is an 896-byte (7168-bit) binary long object.
When key items are sent from server to client, they arrive in seven separate packets (0 through 6). I suppose this is to keep packet size smaller. But as a consequence, the KIs are stored weirdly in this BLOB.
First, we break those 7168 bits into seven equal pieces, that represents one packet worth of information. Let's call these bundles. Each bundle is 128 bytes, or 1024 bits. The first 512 bits of this bundle say whether the player owns one of 512 keys items. If the bit is on, the player owns that key item. Off, they don't. The next 512 bits of the bundle say whether the player has viewed that key item. Have they read its description. Therefore, each bundle tells the client about 512 distinct key items. And seven bundles are sent.
Let's say you want to know whether the player owns the TAVNAZIAN COOKBOOK. That's keyItemId 622. First, you determine which bundle it's in via integer division. 622 / 512 = 1. With 512 KIs per bundle, this would be in bundle 1 (second bundle). Then we use modular arithmetic to determine which bit in the bundle we care about. 622 % 512 = 110. If this bit is on, player owns the cookbook.
KI handling in old code
Previously, the KI LOB was stored as 320 bytes (2560 bits) of ownership bits, followed by 320 bytes (2560 bits) of viewing bits. Only 2560 key items could be encoded in this LOB, compared to 3584 in the new format. These bits would be sent to the client in 5 bundles rather than 7. So both the expected length of the LOB has increased, and order in which the KI bits are bundled in the LOB has changed.