LPack Library - Windower/Lua GitHub Wiki
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.
Char | Description |
---|---|
z |
zero-terminated string |
p |
string preceded by length byte |
P |
string preceded by length word |
a |
string preceded by length size_t |
A |
string |
S |
Fixed-length string followed by length) |
f |
float |
d |
double |
B |
Boolean |
n |
Lua number |
c |
char |
C |
unsigned char |
h |
short |
H |
unsigned short |
i |
int |
I |
unsigned int |
l |
long |
L |
unsigned long |
b |
Bits followed by length |
q |
Bits representing a boolean followed by length |
< |
little endian |
> |
big endian |
= |
native endian |
Functions
string.pack(format, ...)
res = string.pack(format, ...)
-
res
string -
format
string -
...
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 -
str
string -
format
string -
position
integer [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:
Bytes | Type | Description |
---|---|---|
1- 4 |
int |
Header |
5- 5 |
unsigned char |
Chat mode (say, tell, shout, etc.) |
6- 6 |
bool |
true if GM, false otherwise |
7- 8 |
unsigned short |
Zone ID (needs to be there for yells) |
9-24 |
char[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)
if id == 0x017 then
local header, mode, gm, zone, sender, message = data:unpack('iCBHS16z')
end
end)
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)
if id == 0x017 then
local header, mode, gm, zone, sender, message = data:unpack('iCBHS16z')
if mode == 0 then
local new_mode = 1
local adjusted_data = 'iCBHS16z':pack(header, new_mode, gm, zone, sender, message)
return adjusted_data
end
end
end)
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)
if id == 0x017 then
local mode = data:unpack('C', 5)
if mode == 0 then
local new_mode = 1
local adjusted_data = data:sub(1, 4) .. 'C':pack(new_mode) .. data:sub(6)
return adjusted_data
end
end
end)
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.