Skip to content
Stjepan Bakrac edited this page Apr 23, 2022 · 1 revision

Source: https://github.com/LuaDist/lpack/

Adds functions to pack and unpack binary strings based on a provided format string. Note that this has been adjusted from the original source for this module in a some ways to make it more appropriate for our use case. This will be mentioned where applicable.

Usage

require('pack')

Formatting

The format string uses one character to represent one type, optionally followed by a number to denote the amount. The following table lists all recognized characters. Note that b was changed to C from the original source. Furthermore, b, q, B and S were newly added.

CharDescription
zzero-terminated string
pstring preceded by length byte
Pstring preceded by length word
astring preceded by length size_t
Astring
SFixed-length string followed by length)
ffloat
ddouble
BBoolean
nLua number
cchar
Cunsigned char
hshort
Hunsigned short
iint
Iunsigned int
llong
Lunsigned long
bBits followed by length
qBits representing a boolean followed by length
<little endian
>big endian
=native endian

Functions

string.pack(format, ...)

res=string.pack(format, ...)
  • resstring

  • formatstring

  • ...any

Packs the provided arguments ... into a binary string according to format and returns the result res.

string.unpack(str, format, position)

...=string.unpack(str, format, position)
  • ...any

  • strstring

  • formatstring

  • positioninteger [optional]

Unpacks the provided binary string str according to format and returns all the unpacked results .... Note that this does not return the next unread position in the string as the first value, unlike the original source.

Examples

Packet data is passed to addons as a binary string containing certain data. To read it, we have to extract certain pieces of it and convert it to a correct type. string.unpack can be used just for that.

The incoming 0x017 packet has incoming chat information. The following lists its bytes and what they mean:

BytesTypeDescription
1- 4intHeader
5- 5unsigned charChat mode (say, tell, shout, etc.)
6- 6booltrue if GM, false otherwise
7- 8unsigned shortZone ID (needs to be there for yells)
9-24char[16]Sender Name
25- *char*Arbitrary length message

To get this data out of the string, we need an appropriate formatting string. We can see in the table under Formatting that int uses the character i, unsigned char uses C, bool uses B, unsigned short uses H, char[16] is a 16 byte long string, i.e. a fixed-size string, so we can use S16 and finally char* is an arbitrary length string, which means it's zero-terminated and we can use z. Since they all appear right next to each other, we can just concatenate the characters and get the formatting string we need: iCBHS16z

Now we can write the following addon:

require('pack')
windower.register_event('incoming chunk', function(id, data)
ifid==0x017thenlocalheader, mode, gm, zone, sender, message=data:unpack('iCBHS16z')
endend)

Now all the variables hold the respective values that were encoded in the binary string data. We can use this to adjust the result as well. For example, we can change the mode from say (ID 0) to shout (ID 1), so that say text appears in the shout color and shows up in the shout tab:

require('pack')
windower.register_event('incoming chunk', function(id, data)
ifid==0x017thenlocalheader, mode, gm, zone, sender, message=data:unpack('iCBHS16z')
ifmode==0thenlocalnew_mode=1localadjusted_data='iCBHS16z':pack(header, new_mode, gm, zone, sender, message)
returnadjusted_dataendendend)

We still have many unused variables in this case. string.unpack allows us to only extract data from a certain position, which we can use to only extract the mode, which is an unsigned char (i.e. C) that starts at position 5. That way the previous can be shortened a bit by just extracting the mode and splicing it into the middle of the data binary string:

require('pack')
windower.register_event('incoming chunk', function(id, data)
ifid==0x017thenlocalmode=data:unpack('C', 5)
ifmode==0thenlocalnew_mode=1localadjusted_data=data:sub(1, 4) ..'C':pack(new_mode) ..data:sub(6)
returnadjusted_dataendendend)

Note that this is just demonstrative for how string.pack and string.unpack can be used. For general packet reading and manipulation you may want to use the Packets library.

Clone this wiki locally