c4d003e58f
- Repoint remote to Xurkon/Questie-X - Add QuestiePluginAPI, QuestieServer, QuestieLearnerComms modules - Add Questie-X.toc, Questie-X-Classic.toc, Questie-X-TBC.toc, Questie-X-Turtle.toc - Remove embedded Database/Ascension and Database/Ebonhold (migrated to plugins) - Add Plugins/ directory with NTFS junctions for Questie-X-AscensionDB and Questie-X-EbonholdDB - Add LibDeflate, XXH_Lua_Lib, LibDBIcon-1.0, LibDataBroker-1.1 - Update README: Questie-X branding, logo, plugin install guide, plugin API docs - Update CHANGELOG: v1.1.4 entry documenting all architectural changes - Update .gitignore: exclude plugin junctions, __pycache__, .agents, debug files - Various module updates: corrections, map, tracker, tooltips, quest, options
490 lines
14 KiB
Lua
490 lines
14 KiB
Lua
-- small binary stream library with "base 89" decoder (credit to Aero for the algorithm)
|
|
---@class QuestieStreamLib
|
|
local QuestieStreamLib = QuestieLoader:CreateModule("QuestieStreamLib");
|
|
|
|
local tinsert = table.insert
|
|
|
|
local unpack_limit = 4096 -- wow api limits unpack to somewhere between 7000-8000
|
|
|
|
-- reduce cpu time by less global space lookups
|
|
local band = bit.band
|
|
local lshift = bit.lshift
|
|
local rshift = bit.rshift
|
|
local stringchar = string.char
|
|
local stringbyte = string.byte
|
|
local stringsub = string.sub
|
|
|
|
-- shift level table
|
|
local QSL_dltab = {};
|
|
QSL_dltab[stringbyte("x")] = 0;
|
|
QSL_dltab[stringbyte("y")] = 1;
|
|
QSL_dltab[stringbyte("z")] = 2;
|
|
|
|
-- translation table
|
|
local QSL_dttab = {};
|
|
QSL_dttab[123] = 1;
|
|
QSL_dttab[124] = 6;
|
|
QSL_dttab[125] = 59;
|
|
|
|
local QSL_ltab = {};
|
|
local QSL_ttab = {};
|
|
QSL_ltab[0] = string.byte("x");
|
|
QSL_ltab[1] = string.byte("y");
|
|
QSL_ltab[2] = string.byte("z");
|
|
QSL_ttab[34] = 123;
|
|
QSL_ttab[39] = 124;
|
|
QSL_ttab[92] = 125;
|
|
|
|
local StreamPool = {}
|
|
|
|
local function _StreamReset(self)
|
|
self._pointer = 1
|
|
self._bin = {{}}
|
|
self._level = 0
|
|
end
|
|
|
|
local function _StreamFinished(self)
|
|
self:reset()
|
|
tinsert(StreamPool, self)
|
|
end
|
|
|
|
function QuestieStreamLib:GetStream(mode) -- returns a new stream
|
|
if not mode then
|
|
Questie:Error("QuestieStreamLib: Stream encoding mode is not defined.")
|
|
error("Stream encoding mode is not defined.")
|
|
end
|
|
|
|
local stream = tremove(StreamPool)
|
|
if stream and stream._mode == mode then
|
|
return stream
|
|
else
|
|
stream = {}
|
|
end
|
|
for k,v in pairs(QuestieStreamLib) do -- copy functions to new object
|
|
stream[k] = v
|
|
end
|
|
stream.reset = _StreamReset
|
|
stream.finished = _StreamFinished -- its best to call this (not required) when done using the stream
|
|
|
|
stream:reset()
|
|
stream._mode = mode
|
|
|
|
if mode == "raw" or mode == "raw_assert" then
|
|
stream.ReadByte = QuestieStreamLib._ReadByte_raw
|
|
stream.ReadShort = QuestieStreamLib._ReadShort_raw
|
|
stream.ReadInt24 = QuestieStreamLib._ReadInt24_raw
|
|
stream.ReadInt = QuestieStreamLib._ReadInt_raw
|
|
stream.ReadInt12Pair = QuestieStreamLib._ReadInt12Pair_raw
|
|
stream.ReadTinyString = QuestieStreamLib._ReadTinyString_raw
|
|
stream.ReadShortString = QuestieStreamLib._ReadShortString_raw
|
|
stream.ReadTinyStringNil = QuestieStreamLib._ReadTinyStringNil_raw
|
|
|
|
if mode == "raw" then
|
|
stream.WriteByte = QuestieStreamLib._writeByte
|
|
stream.WriteShort = QuestieStreamLib._WriteShort
|
|
stream.WriteInt = QuestieStreamLib._WriteInt
|
|
stream.WriteInt24 = QuestieStreamLib._WriteInt24
|
|
stream.WriteInt12Pair = QuestieStreamLib._WriteInt12Pair
|
|
stream.WriteLong = QuestieStreamLib._WriteLong
|
|
else
|
|
stream.WriteByte = QuestieStreamLib._writeByte_assert
|
|
stream.WriteShort = QuestieStreamLib._WriteShort_assert
|
|
stream.WriteInt = QuestieStreamLib._WriteInt_assert
|
|
stream.WriteInt24 = QuestieStreamLib._WriteInt24_assert
|
|
stream.WriteInt12Pair = QuestieStreamLib._WriteInt12Pair_assert
|
|
stream.WriteLong = QuestieStreamLib._WriteLong_assert
|
|
end
|
|
stream.WriteTinyString = QuestieStreamLib._WriteTinyString
|
|
else
|
|
if mode == "1short" then
|
|
stream.ReadByte = QuestieStreamLib._ReadByte_1short
|
|
stream.ReadTinyString = QuestieStreamLib._ReadTinyString
|
|
stream.WriteByte = QuestieStreamLib._WriteByte_1short
|
|
stream.WriteTinyString = QuestieStreamLib._WriteTinyString
|
|
else
|
|
stream.ReadByte = QuestieStreamLib._ReadByte_b89
|
|
stream.ReadTinyString = QuestieStreamLib._ReadTinyString_b89
|
|
stream.WriteByte = QuestieStreamLib._WriteByte_b89
|
|
stream.WriteTinyString = QuestieStreamLib._WriteTinyString_b89
|
|
end
|
|
stream.ReadShort = QuestieStreamLib._ReadShort
|
|
stream.ReadInt24 = QuestieStreamLib._ReadInt24
|
|
stream.ReadInt = QuestieStreamLib._ReadInt
|
|
stream.ReadInt12Pair = QuestieStreamLib._ReadInt12Pair
|
|
stream.ReadTinyString = QuestieStreamLib._ReadTinyString
|
|
stream.ReadShortString = QuestieStreamLib._ReadShortString
|
|
stream.ReadTinyStringNil = QuestieStreamLib._ReadTinyStringNil
|
|
|
|
stream.WriteShort = QuestieStreamLib._WriteShort
|
|
stream.WriteInt = QuestieStreamLib._WriteInt
|
|
stream.WriteInt24 = QuestieStreamLib._WriteInt24
|
|
stream.WriteInt12Pair = QuestieStreamLib._WriteInt12Pair
|
|
stream.WriteLong = QuestieStreamLib._WriteLong
|
|
end
|
|
|
|
return stream
|
|
end
|
|
|
|
function QuestieStreamLib:_writeByte(val)
|
|
local p = self._pointer
|
|
local chunkId = math.floor((p - 1) / 100000) + 1
|
|
local localIdx = ((p - 1) % 100000) + 1
|
|
|
|
if not self._bin[chunkId] then
|
|
self._bin[chunkId] = {}
|
|
end
|
|
|
|
self._bin[chunkId][localIdx] = stringchar(val)
|
|
self._pointer = p + 1
|
|
end
|
|
|
|
function QuestieStreamLib:_writeByte_assert(val)
|
|
assert(type(val) == "number", "Not a number")
|
|
assert(val >= 0, "Underflow")
|
|
assert(val <= 255, "Overflow") -- 8 bits
|
|
self:_writeByte(val)
|
|
end
|
|
|
|
function QuestieStreamLib:_readByte()
|
|
local p = self._pointer
|
|
self._pointer = p + 1
|
|
if type(self._bin) == "string" then
|
|
return stringbyte(self._bin, p)
|
|
else
|
|
local chunkId = math.floor((p - 1) / 100000) + 1
|
|
local localIdx = ((p - 1) % 100000) + 1
|
|
if self._bin[chunkId] then
|
|
return stringbyte(self._bin[chunkId][localIdx] or "\0")
|
|
end
|
|
return 0
|
|
end
|
|
end
|
|
|
|
function QuestieStreamLib:SetPointer(pointer)
|
|
self._pointer = pointer;
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadByte_b89()
|
|
local v = self:_readByte()
|
|
if QSL_dltab[v] then
|
|
self._level = QSL_dltab[v];
|
|
v = self:_readByte()
|
|
end
|
|
if QSL_dttab[v] then
|
|
return self._level * 86 + QSL_dttab[v];
|
|
else
|
|
return self._level * 86 + v - 33;
|
|
end
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteByte_b89(e)
|
|
if not e then return end
|
|
local level = math.floor(e / 86);
|
|
if self._level ~= level then
|
|
self._level = level
|
|
self:_writeByte(QSL_ltab[level])
|
|
end
|
|
local chr = (e % 86) + 33
|
|
if QSL_ttab[chr] then
|
|
self:_writeByte(QSL_ttab[chr])
|
|
else
|
|
self:_writeByte(chr)
|
|
end
|
|
end
|
|
|
|
local _1short_control = 237
|
|
local _1short_zero = 238
|
|
|
|
local _1short_control_table = {
|
|
_1short_control, _1short_zero
|
|
}
|
|
|
|
local _1short_control_table_reversed = {};
|
|
for k,v in pairs(_1short_control_table) do _1short_control_table_reversed[v] = k end
|
|
|
|
function QuestieStreamLib:_WriteByte_1short(e)
|
|
if _1short_control_table_reversed[e] then
|
|
self:_writeByte(_1short_control)
|
|
self:_writeByte(_1short_control_table_reversed[e])
|
|
elseif e == 0 then
|
|
self:_writeByte(_1short_zero)
|
|
else
|
|
self:_writeByte(e)
|
|
end
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadByte_1short()
|
|
local v = self:_readByte()
|
|
if v == _1short_zero then
|
|
return 0
|
|
elseif v == _1short_control then
|
|
v = self:_readByte()
|
|
return _1short_control_table[v]
|
|
end
|
|
|
|
return v
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadByte_raw()
|
|
local p = self._pointer
|
|
self._pointer = p + 1
|
|
return stringbyte(self._bin, p)
|
|
end
|
|
|
|
function QuestieStreamLib:ReadBytes(count)
|
|
local ret = {};
|
|
for i=1, count do
|
|
tinsert(ret, self:ReadByte());
|
|
end
|
|
return unpack(ret)
|
|
end
|
|
|
|
function QuestieStreamLib:ReadShorts(count)
|
|
local ret = {};
|
|
for i=1, count do
|
|
tinsert(ret, self:ReadShort());
|
|
end
|
|
return unpack(ret)
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadShort()
|
|
return lshift(self:ReadByte(), 8) + self:ReadByte();
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadShort_raw()
|
|
local p = self._pointer
|
|
self._pointer = p + 2
|
|
local a,b = stringbyte(self._bin, p, p+1)
|
|
return a*256 + b
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadInt12Pair()
|
|
local a = self:ReadByte()
|
|
return self:ReadByte() + lshift(band(a, 15), 8), self:ReadByte() + lshift(band(a, 240), 4)
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadInt12Pair_raw()
|
|
local p = self._pointer
|
|
self._pointer = p + 3
|
|
local a,b,c = stringbyte(self._bin, p, p+2)
|
|
local low4bit = a % 16
|
|
return low4bit * 256 + b, (a - low4bit) * 16 + c
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadInt24()
|
|
return lshift(self:ReadByte(), 16) + lshift(self:ReadByte(), 8) + self:ReadByte();
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadInt24_raw()
|
|
local p = self._pointer
|
|
self._pointer = p + 3
|
|
local a,b,c = stringbyte(self._bin, p, p+2)
|
|
return a*65536 + b*256 + c
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadInt()
|
|
return lshift(self:ReadByte(), 24) + lshift(self:ReadByte(), 16) + lshift(self:ReadByte(), 8) + self:ReadByte();
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadInt_raw()
|
|
local p = self._pointer
|
|
self._pointer = p + 4
|
|
local a,b,c,d = stringbyte(self._bin, p, p+3)
|
|
return a*16777216 + b*65536 + c*256 + d
|
|
end
|
|
|
|
function QuestieStreamLib:ReadLong()
|
|
return lshift(self:ReadByte(), 56) +lshift(self:ReadByte(), 48) +lshift(self:ReadByte(), 40) +lshift(self:ReadByte(), 32) +lshift(self:ReadByte(), 24) + lshift(self:ReadByte(), 16) + lshift(self:ReadByte(), 8) + self:ReadByte();
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadTinyString()
|
|
local length = self:ReadByte()
|
|
local ret = {};
|
|
for i = 1, length do
|
|
tinsert(ret, self:ReadByte()) -- slightly better lua code is slightly better
|
|
end
|
|
return stringchar(unpack(ret))
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadTinyString_b89()
|
|
local length = self:ReadByte()
|
|
local ret = {};
|
|
for i = 1, length do
|
|
tinsert(ret, self:_readByte()) -- slightly better lua code is slightly better
|
|
end
|
|
return stringchar(unpack(ret))
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadTinyString_raw()
|
|
local p = self._pointer
|
|
local length = stringbyte(self._bin, p)
|
|
p = p + 1
|
|
self._pointer = p + length
|
|
return stringsub(self._bin, p, p+length-1)
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadTinyStringNil_raw()
|
|
local p = self._pointer
|
|
local length = stringbyte(self._bin, p)
|
|
p = p + 1
|
|
if length == 0 then
|
|
self._pointer = p
|
|
return nil
|
|
end
|
|
self._pointer = p + length
|
|
return stringsub(self._bin, p, p+length-1)
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadTinyStringNil()
|
|
local length = self:ReadByte()
|
|
if length == 0 then return nil end
|
|
local ret = {};
|
|
for i = 1, length do
|
|
tinsert(ret, self:ReadByte()) -- slightly better lua code is slightly better
|
|
end
|
|
return stringchar(unpack(ret))
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadShortString()
|
|
local length = self:ReadShort()
|
|
local ret = {};
|
|
if length > unpack_limit then
|
|
for i = 1, length do
|
|
tinsert(ret, stringchar(self:ReadByte()))
|
|
end
|
|
return table.concat(ret)
|
|
else
|
|
for i = 1, length do
|
|
tinsert(ret, self:ReadByte()) -- slightly better lua code is slightly better
|
|
end
|
|
return stringchar(unpack(ret))
|
|
end
|
|
end
|
|
|
|
function QuestieStreamLib:_ReadShortString_raw()
|
|
local p = self._pointer
|
|
local a,b = stringbyte(self._bin, p, p+1)
|
|
local length = a*256 + b
|
|
p = p + 2
|
|
self._pointer = p + length
|
|
return stringsub(self._bin, p, p+length-1)
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteShort(val)
|
|
--print("wshort: " .. val);
|
|
self:WriteByte(rshift(val, 8) % 256);
|
|
self:WriteByte(val % 256);
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteShort_assert(val)
|
|
assert(type(val) == "number", "Not a number")
|
|
assert(val >= 0, "Underflow")
|
|
assert(val <= 65535, "Overflow") -- 16 bits
|
|
self:_WriteShort(val)
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteInt(val)
|
|
self:WriteByte(rshift(val, 24) % 256);
|
|
self:WriteByte(rshift(val, 16) % 256);
|
|
self:WriteByte(rshift(val, 8) % 256);
|
|
self:WriteByte(val % 256);
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteInt_assert(val)
|
|
assert(type(val) == "number", "Not a number")
|
|
assert(val >= 0, "Underflow")
|
|
assert(val <= 4294967295, "Overflow") -- 32 bits
|
|
self:_WriteInt(val)
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteInt24(val)
|
|
--print("wi24: " .. val);
|
|
self:WriteByte(rshift(val, 16) % 256);
|
|
self:WriteByte(rshift(val, 8) % 256);
|
|
self:WriteByte(val % 256);
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteInt24_assert(val)
|
|
assert(type(val) == "number", "Not a number")
|
|
assert(val >= 0, "Underflow")
|
|
assert(val <= 99999999, "Overflow") -- 24 bits
|
|
self:_WriteInt24(val)
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteInt12Pair(val1, val2)
|
|
self:WriteByte(band(rshift(val1, 8), 15) + lshift(band(rshift(val2, 8), 15), 4))
|
|
self:WriteByte(val1 % 256)
|
|
self:WriteByte(val2 % 256)
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteInt12Pair_assert(val1, val2)
|
|
assert(type(val1) == "number", "Not a number")
|
|
assert(val1 >= 0, "Underflow")
|
|
assert(val1 <= 4095, "Overflow") -- 12 bits
|
|
|
|
assert(type(val2) == "number", "Not a number")
|
|
assert(val2 >= 0, "Underflow")
|
|
assert(val2 <= 4095, "Overflow") -- 12 bits
|
|
|
|
self:_WriteInt12Pair(val1, val2)
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteLong(val)
|
|
self:WriteByte(rshift(val, 56) % 256);
|
|
self:WriteByte(rshift(val, 48) % 256);
|
|
self:WriteByte(rshift(val, 40) % 256);
|
|
self:WriteByte(rshift(val, 32) % 256);
|
|
self:WriteByte(rshift(val, 24) % 256);
|
|
self:WriteByte(rshift(val, 16) % 256);
|
|
self:WriteByte(rshift(val, 8) % 256);
|
|
self:WriteByte(val % 256);
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteLong_assert(val)
|
|
assert(type(val) == "number", "Not a number")
|
|
assert(val >= 0, "Underflow")
|
|
assert(val <= 18446744073709551615, "Overflow") -- 64 bits
|
|
|
|
self:_WriteLong(val)
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteTinyString(val)
|
|
local length = string.len(val)
|
|
self:WriteByte(length);
|
|
for i=1, length do
|
|
self:WriteByte(stringbyte(val, i));
|
|
end
|
|
end
|
|
|
|
function QuestieStreamLib:_WriteTinyString_b89(val)
|
|
local length = string.len(val)
|
|
self:WriteByte(length);
|
|
for i=1, length do
|
|
self:_writeByte(stringbyte(val, i));
|
|
end
|
|
end
|
|
|
|
function QuestieStreamLib:WriteShortString(val)
|
|
local length = string.len(val)
|
|
self:WriteShort(length);
|
|
for i=1, length do
|
|
self:WriteByte(stringbyte(val, i));
|
|
end
|
|
end
|
|
|
|
|
|
|
|
function QuestieStreamLib:Save()
|
|
local chunks = {}
|
|
for i=1, #self._bin do
|
|
table.insert(chunks, table.concat(self._bin[i]))
|
|
end
|
|
return table.concat(chunks)
|
|
end
|
|
|
|
function QuestieStreamLib:Load(bin)
|
|
self._pointer = 1
|
|
self._bin = bin
|
|
self._level = 0
|
|
end
|