CacheReader Helpers¶
The primitives every decoder in this library is built out of: big-endian integer reads that advance a shared offset, the cache’s variable width number encodings, and the colour and shift conversions its formats rely on.
Read N Bits¶
function Read8Bits(constref data: TByteArray; var offset: Integer): Integer;
function Read16Bits(constref data: TByteArray; var offset: Integer): Integer;
function Read24Bits(constref data: TByteArray; var offset: Integer): Integer;
function Read32Bits(constref data: TByteArray; var offset: Integer): Integer;
function Read64Bits(constref data: TByteArray; var offset: Integer): Int64;
Reads the next N amount of bits from data as an Integer.
offset is the position where we want to start reading from data and gets permanently modified
so future reads continue reading from where the integer that was read finished.
All reads are big-endian (most significant byte first) and offset always advances by the number
of bytes consumed (N/8), landing exactly on the first byte of whatever comes next.
For example, imagine the following data represented in hexadecimal and that our starting offset is 3:
Data: 4A F3 02 C1 01 06 FF AB 2A C2 DF D7 66
Offset: ^^
If we use Read16Bits the following happens:
Data: 4A F3 02 C1 01 06 FF AB 2A C2 DF D7 66
Offset: ¯¯ ¯¯ ^^
Our result will be $C101 (49409 in decimal), and offset will now be 5.
Now right after we use Read32Bits:
Data: 4A F3 02 C1 01 06 FF AB 2A C2 DF D7 66
Offset: ¯¯ ¯¯ ¯¯ ¯¯ ^^
The result will be $06FFAB2A (117370154 in decimal) and our offsetwill now be at 9.
Read16SignedBits¶
function Read16SignedBits(constref data: TByteArray; var offset: Integer): Integer;
Reads the next 16 bits from data the same way Read16Bits does, but reinterprets the
result as a signed two’s complement value instead of an unsigned one.
offset behaves identically to the other ReadNBits functions: it advances by 2 bytes,
landing on the first byte of whatever comes next.
Unsigned 16-bit values range from 0 to 65535. Signed 16-bit values represent that same
range of bit patterns as -32768 to 32767 instead, by treating any value > 32767 as
negative: subtracting 65536 from it recovers the correctly signed result.
For example, the raw bytes $FFFF read as an unsigned Read16Bits give 65535,
but read as Read16SignedBits give -1.
Read8SignedBits¶
function Read8SignedBits(constref data: TByteArray; var offset: Integer): Integer;
Reads the next 8 bits from data the same way Read8Bits does, but reinterprets the
result as a signed two’s complement value instead of an unsigned one.
Unsigned 8-bit values range from 0 to 255. Signed 8-bit values represent that same range
of bit patterns as -128 to 127 instead, by treating any value > 127 as negative:
subtracting 256 from it recovers the correctly signed result.
ReadBigSmart¶
function ReadBigSmart(constref data: TByteArray; var offset: Integer): Integer;
Reads an id that may or may not fit in 16 bits.
The top bit of the first byte says which: clear means the id was written as 2 bytes, set means
it was written as 4 with that flag bit masked back off. offset advances by whichever was read.
ReadBigSmart2¶
function ReadBigSmart2(constref data: TByteArray; var offset: Integer): Integer;
ReadBigSmart for fields that are allowed to be absent.
The 2 byte form reserves 32767 to mean “no id”, which is returned as -1. The 4 byte form
has no such reservation and is read exactly as ReadBigSmart reads it.
ReadShortSmart¶
function ReadShortSmart(constref data: TByteArray; var offset: Integer): Integer;
Reads a signed value written in as few bytes as it needed.
The top bit of the first byte picks the width: clear is a single byte biased by -64, covering
-64 to 63; set is two bytes biased by -$C000, covering -16384 to 16383.
ReadUnsignedShortSmart¶
function ReadUnsignedShortSmart(constref data: TByteArray; var offset: Integer): Integer;
ReadShortSmart without the bias, so the value is never negative.
A single byte covers 0 to 127 and two bytes, with the top bit stripped, cover 0 to 32767.
ReadUnsignedIntSmartShortCompat¶
function ReadUnsignedIntSmartShortCompat(constref data: TByteArray; var offset: Integer): Integer;
Reads a value that can exceed what ReadUnsignedShortSmart alone can express.
32767 is a continuation marker rather than a value: every one of them adds 32767 to the
running total and another smart is read, until one comes back that is not the marker.
ReadVarInt2¶
function ReadVarInt2(constref data: TByteArray; var offset: Integer): Integer;
Reads a value written as a run of 7-bit groups, least significant group first.
Each byte contributes its low 7 bits to the result, in order; a set top bit says another byte follows. This is how TDBRowDefinition.Create reads a row’s table id - unlike the other smart encodings above, the byte count is not capped at two.
TByteArray.ReadCString¶
function TByteArray.ReadCString(var offset: Integer): String;
Reads the NUL terminated string starting at offset.
offset is left on the byte after the terminator, so a following read continues with whatever
comes next. Running off the end of the array ends the string as a terminator would.
Char.ToHex¶
function Char.ToHex(): Integer;
The value of a single hexadecimal digit, in either case. Anything else reads as 0.
CacheString2Hash¶
function CacheString2Hash(const s: String): Integer;
The hash the cache stores in place of a name.
Archives and files are named by this hash rather than by text, so looking one up by name means hashing the name and comparing - see TReferenceTable.ArchiveID.
CacheString2UTF8¶
function CacheString2UTF8(const s: String): String;
Turns a string as the cache stores it, one byte per character, into UTF-8.
TColor.ToHSLEx¶
function TColor.ToHSLEx(): TColorHSLEx;
Splits a colour into hue, saturation and lightness.
The result carries each component twice: as the 0.0 to 1.0 doubles the conversion works in,
and as the 0 to 255 integers the cache’s own colour packing expects. Ground colours need
both - the doubles feed the hue multiplier that weights blending, the integers are packed
straight into a palette index.
The channels are read back to front on purpose: the cache stores its colours as RGB while TColor is BGR, so the blue channel holds what this conversion treats as red.
ArithmeticShr¶
function ArithmeticShr(value, bits: Integer): Integer;
Right shift that keeps the sign of value.
Pascal’s shr is a logical shift, so shifting a negative number turns it into a large positive
one. Where a value’s sign is meaningful - a surface normal pointing away from the light, say -
this halves it the way the original client’s >> does.