v9.7.2: Ebonhold Database integration and core logic refinements
This commit is contained in:
@@ -0,0 +1,483 @@
|
||||
-- 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)
|
||||
--print("Writing " .. val .. " at " .. self._pointer)
|
||||
self._bin[self._pointer] = stringchar(val)
|
||||
--if val > 255 or val < 0 then
|
||||
-- stringchar(val) -- error
|
||||
--end
|
||||
self._pointer = self._pointer + 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()
|
||||
self._pointer = self._pointer + 1
|
||||
return self._bin[self._pointer-1]
|
||||
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()
|
||||
--if self._pointer-1 > unpack_limit then
|
||||
--for i=1,self._pointer-1 do
|
||||
--print(self._bin[i])
|
||||
-- self._bin[i] = stringchar(self._bin[i])
|
||||
--end
|
||||
--return table.concat(self._bin)
|
||||
--end
|
||||
--return stringchar(unpack(self._bin))
|
||||
return table.concat(self._bin)
|
||||
end
|
||||
|
||||
function QuestieStreamLib:Load(bin)
|
||||
self._pointer = 1
|
||||
if self._mode == "raw" or self._mode == "raw_assert" then
|
||||
self._bin = bin
|
||||
else
|
||||
self._bin = {stringbyte(bin, 1, -1)}
|
||||
end
|
||||
self._level = 0
|
||||
end
|
||||
Reference in New Issue
Block a user