fix: add lua 5.0 compatibility shims

This commit is contained in:
Xurkon
2026-06-04 21:54:36 -05:00
parent c3ea62f5bc
commit f8b3972e70
4 changed files with 195 additions and 28 deletions
+143
View File
@@ -19,6 +19,111 @@ if not math.mod then
end end
end end
-- Shim for Lua 5.0 where the bit library may be missing.
-- Questie uses band/bor/bxor/lshift/rshift in core runtime code, so we provide
-- a pure-Lua fallback when the host does not expose one.
if not bit or not bit.band or not bit.bor or not bit.bxor or not bit.lshift or not bit.rshift then
local bitlib = bit or {}
local U32 = 4294967296
local U32_MAX = 4294967295
local floor = math.floor
local mod = math.mod or function(a, b)
return a - floor(a / b) * b
end
local function normalizeU32(n)
n = tonumber(n) or 0
n = floor(n)
if n < 0 then
n = U32 + mod(n, U32)
elseif n >= U32 then
n = mod(n, U32)
end
return n
end
local function bitAt(n, mask)
return mod(floor(n / mask), 2)
end
local function band32(a, b)
a = normalizeU32(a)
b = normalizeU32(b)
local result = 0
local mask = 1
for _ = 1, 32 do
if bitAt(a, mask) == 1 and bitAt(b, mask) == 1 then
result = result + mask
end
mask = mask * 2
end
return normalizeU32(result)
end
local function bor32(a, b)
a = normalizeU32(a)
b = normalizeU32(b)
local result = 0
local mask = 1
for _ = 1, 32 do
if bitAt(a, mask) == 1 or bitAt(b, mask) == 1 then
result = result + mask
end
mask = mask * 2
end
return normalizeU32(result)
end
local function bxor32(a, b)
a = normalizeU32(a)
b = normalizeU32(b)
local result = 0
local mask = 1
for _ = 1, 32 do
if bitAt(a, mask) ~= bitAt(b, mask) then
result = result + mask
end
mask = mask * 2
end
return normalizeU32(result)
end
local function lshift32(a, disp)
a = normalizeU32(a)
disp = tonumber(disp) or 0
if disp <= 0 then
return normalizeU32(math.floor(a / (2 ^ (-disp))))
elseif disp >= 32 then
return 0
end
return normalizeU32(a * (2 ^ disp))
end
local function rshift32(a, disp)
a = normalizeU32(a)
disp = tonumber(disp) or 0
if disp <= 0 then
return normalizeU32(a * (2 ^ (-disp)))
elseif disp >= 32 then
return 0
end
return normalizeU32(floor(a / (2 ^ disp)))
end
local function bnot32(a)
return normalizeU32(U32_MAX - normalizeU32(a))
end
bitlib.band = bitlib.band or band32
bitlib.bor = bitlib.bor or bor32
bitlib.bxor = bitlib.bxor or bxor32
bitlib.lshift = bitlib.lshift or lshift32
bitlib.rshift = bitlib.rshift or rshift32
bitlib.bnot = bitlib.bnot or bnot32
bit = bitlib
_G.bit = bitlib
end
-- Shim for Lua 5.0 where string.match is missing. -- Shim for Lua 5.0 where string.match is missing.
-- Supports up to 5 captures (sufficient for all Questie uses). -- Supports up to 5 captures (sufficient for all Questie uses).
if not string.match then if not string.match then
@@ -57,6 +162,44 @@ if not select then
end end
end end
-- Shim for Lua 5.0 where strsplit is missing.
if not strsplit then
strsplit = function(separator, text, max)
if text == nil then
return nil
end
if separator == "" then
return text
end
local results = {}
local resultCount = 0
local startPos = 1
local maxSplits = tonumber(max)
while true do
if maxSplits and resultCount >= (maxSplits - 1) then
resultCount = resultCount + 1
results[resultCount] = string.sub(text, startPos)
break
end
local sepStart, sepEnd = string.find(text, separator, startPos, true)
if not sepStart then
resultCount = resultCount + 1
results[resultCount] = string.sub(text, startPos)
break
end
resultCount = resultCount + 1
results[resultCount] = string.sub(text, startPos, sepStart - 1)
startPos = sepEnd + 1
end
return unpack(results, 1, resultCount)
end
end
-- The only public class except for Questie -- The only public class except for Questie
---@class QuestieLoader ---@class QuestieLoader
QuestieLoader = QuestieLoader or {} QuestieLoader = QuestieLoader or {}
+5 -3
View File
@@ -6,6 +6,8 @@ local QuestieSerializer = QuestieLoader:CreateModule("QuestieSerializer");
---@type QuestieStreamLib ---@type QuestieStreamLib
local QuestieStreamLib = QuestieLoader:ImportModule("QuestieStreamLib"); local QuestieStreamLib = QuestieLoader:ImportModule("QuestieStreamLib");
local mod = math.mod
function QuestieSerializer:Hash(value) function QuestieSerializer:Hash(value)
if not value or type(value) ~= "string" or (string.len(value) <= 0) then if not value or type(value) ~= "string" or (string.len(value) <= 0) then
@@ -70,14 +72,14 @@ local function floatBitsToInt(n)
else else
expo = expo + 0x7E expo = expo + 0x7E
mant = floor((mant * 2.0 - 1.0) * ldexp(0.5, 24)) mant = floor((mant * 2.0 - 1.0) * ldexp(0.5, 24))
return _pack(sign + floor(expo / 0x2), (expo % 0x2) * 0x80 + floor(mant / 0x10000), floor(mant / 0x100) % 0x100, mant % 0x100) return _pack(sign + floor(expo / 0x2), mod(expo, 0x2) * 0x80 + floor(mant / 0x10000), mod(floor(mant / 0x100), 0x100), mod(mant, 0x100))
end end
end end
local function intBitsToFloat(int) local function intBitsToFloat(int)
local b1, b2, b3, b4 = _unpack(int) local b1, b2, b3, b4 = _unpack(int)
local sign = b1 > 0x7F local sign = b1 > 0x7F
local expo = (b1 % 0x80) * 0x2 + floor(b2 / 0x80) local expo = mod(b1, 0x80) * 0x2 + floor(b2 / 0x80)
local mant = ((b2 % 0x80) * 0x100 + b3) * 0x100 + b4 local mant = ((mod(b2, 0x80)) * 0x100 + b3) * 0x100 + b4
if sign then if sign then
sign = -1 sign = -1
else else
+25 -24
View File
@@ -10,6 +10,7 @@ local unpack_limit = 4096 -- wow api limits unpack to somewhere between 7000-800
local band = bit.band local band = bit.band
local lshift = bit.lshift local lshift = bit.lshift
local rshift = bit.rshift local rshift = bit.rshift
local mod = math.mod
local stringchar = string.char local stringchar = string.char
local stringbyte = string.byte local stringbyte = string.byte
local stringsub = string.sub local stringsub = string.sub
@@ -128,7 +129,7 @@ end
function QuestieStreamLib:_writeByte(val) function QuestieStreamLib:_writeByte(val)
local p = self._pointer local p = self._pointer
local chunkId = math.floor((p - 1) / 100000) + 1 local chunkId = math.floor((p - 1) / 100000) + 1
local localIdx = ((p - 1) % 100000) + 1 local localIdx = mod(p - 1, 100000) + 1
if not self._bin[chunkId] then if not self._bin[chunkId] then
self._bin[chunkId] = {} self._bin[chunkId] = {}
@@ -152,7 +153,7 @@ function QuestieStreamLib:_readByte()
return stringbyte(self._bin, p) return stringbyte(self._bin, p)
else else
local chunkId = math.floor((p - 1) / 100000) + 1 local chunkId = math.floor((p - 1) / 100000) + 1
local localIdx = ((p - 1) % 100000) + 1 local localIdx = mod(p - 1, 100000) + 1
if self._bin[chunkId] then if self._bin[chunkId] then
return stringbyte(self._bin[chunkId][localIdx] or "\0") return stringbyte(self._bin[chunkId][localIdx] or "\0")
end end
@@ -184,7 +185,7 @@ function QuestieStreamLib:_WriteByte_b89(e)
self._level = level self._level = level
self:_writeByte(QSL_ltab[level]) self:_writeByte(QSL_ltab[level])
end end
local chr = (e % 86) + 33 local chr = mod(e, 86) + 33
if QSL_ttab[chr] then if QSL_ttab[chr] then
self:_writeByte(QSL_ttab[chr]) self:_writeByte(QSL_ttab[chr])
else else
@@ -267,7 +268,7 @@ function QuestieStreamLib:_ReadInt12Pair_raw()
local p = self._pointer local p = self._pointer
self._pointer = p + 3 self._pointer = p + 3
local a,b,c = stringbyte(self._bin, p, p+2) local a,b,c = stringbyte(self._bin, p, p+2)
local low4bit = a % 16 local low4bit = mod(a, 16)
return low4bit * 256 + b, (a - low4bit) * 16 + c return low4bit * 256 + b, (a - low4bit) * 16 + c
end end
@@ -372,8 +373,8 @@ end
function QuestieStreamLib:_WriteShort(val) function QuestieStreamLib:_WriteShort(val)
--print("wshort: " .. val); --print("wshort: " .. val);
self:WriteByte(rshift(val, 8) % 256); self:WriteByte(mod(rshift(val, 8), 256));
self:WriteByte(val % 256); self:WriteByte(mod(val, 256));
end end
function QuestieStreamLib:_WriteShort_assert(val) function QuestieStreamLib:_WriteShort_assert(val)
@@ -384,10 +385,10 @@ function QuestieStreamLib:_WriteShort_assert(val)
end end
function QuestieStreamLib:_WriteInt(val) function QuestieStreamLib:_WriteInt(val)
self:WriteByte(rshift(val, 24) % 256); self:WriteByte(mod(rshift(val, 24), 256));
self:WriteByte(rshift(val, 16) % 256); self:WriteByte(mod(rshift(val, 16), 256));
self:WriteByte(rshift(val, 8) % 256); self:WriteByte(mod(rshift(val, 8), 256));
self:WriteByte(val % 256); self:WriteByte(mod(val, 256));
end end
function QuestieStreamLib:_WriteInt_assert(val) function QuestieStreamLib:_WriteInt_assert(val)
@@ -399,9 +400,9 @@ end
function QuestieStreamLib:_WriteInt24(val) function QuestieStreamLib:_WriteInt24(val)
--print("wi24: " .. val); --print("wi24: " .. val);
self:WriteByte(rshift(val, 16) % 256); self:WriteByte(mod(rshift(val, 16), 256));
self:WriteByte(rshift(val, 8) % 256); self:WriteByte(mod(rshift(val, 8), 256));
self:WriteByte(val % 256); self:WriteByte(mod(val, 256));
end end
function QuestieStreamLib:_WriteInt24_assert(val) function QuestieStreamLib:_WriteInt24_assert(val)
@@ -413,8 +414,8 @@ end
function QuestieStreamLib:_WriteInt12Pair(val1, val2) function QuestieStreamLib:_WriteInt12Pair(val1, val2)
self:WriteByte(band(rshift(val1, 8), 15) + lshift(band(rshift(val2, 8), 15), 4)) self:WriteByte(band(rshift(val1, 8), 15) + lshift(band(rshift(val2, 8), 15), 4))
self:WriteByte(val1 % 256) self:WriteByte(mod(val1, 256))
self:WriteByte(val2 % 256) self:WriteByte(mod(val2, 256))
end end
function QuestieStreamLib:_WriteInt12Pair_assert(val1, val2) function QuestieStreamLib:_WriteInt12Pair_assert(val1, val2)
@@ -430,14 +431,14 @@ function QuestieStreamLib:_WriteInt12Pair_assert(val1, val2)
end end
function QuestieStreamLib:_WriteLong(val) function QuestieStreamLib:_WriteLong(val)
self:WriteByte(rshift(val, 56) % 256); self:WriteByte(mod(rshift(val, 56), 256));
self:WriteByte(rshift(val, 48) % 256); self:WriteByte(mod(rshift(val, 48), 256));
self:WriteByte(rshift(val, 40) % 256); self:WriteByte(mod(rshift(val, 40), 256));
self:WriteByte(rshift(val, 32) % 256); self:WriteByte(mod(rshift(val, 32), 256));
self:WriteByte(rshift(val, 24) % 256); self:WriteByte(mod(rshift(val, 24), 256));
self:WriteByte(rshift(val, 16) % 256); self:WriteByte(mod(rshift(val, 16), 256));
self:WriteByte(rshift(val, 8) % 256); self:WriteByte(mod(rshift(val, 8), 256));
self:WriteByte(val % 256); self:WriteByte(mod(val, 256));
end end
function QuestieStreamLib:_WriteLong_assert(val) function QuestieStreamLib:_WriteLong_assert(val)
@@ -476,7 +477,7 @@ end
function QuestieStreamLib:Save() function QuestieStreamLib:Save()
local chunks = {} local chunks = {}
for i=1, #self._bin do for i=1, table.getn(self._bin) do
table.insert(chunks, table.concat(self._bin[i])) table.insert(chunks, table.concat(self._bin[i]))
end end
return table.concat(chunks) return table.concat(chunks)
+22 -1
View File
@@ -206,7 +206,7 @@ describe("Audit Pass 9 - file-by-file findings (snapshot at HEAD)", function()
it("[9.1->11.1] CORRECTED: % modulo IS used (the 'avoided' claim was wrong)", function() it("[9.1->11.1] CORRECTED: % modulo IS used (the 'avoided' claim was wrong)", function()
-- Pass 8-10 wrongly said % modulo = 0. Real modulo operators exist in -- Pass 8-10 wrongly said % modulo = 0. Real modulo operators exist in
-- Turtle-TOC files and are 5.0 parse errors that math.mod cannot rescue. -- Turtle-TOC files and are 5.0 parse errors that math.mod cannot rescue.
assert.is_true(has(read("Modules/QuestieStream.lua"), " % 256")) assert.is_true(has(read("Modules/QuestieStream.lua"), "mod(val, 256)"))
assert.is_true(has(read("Modules/QuestiePlayer.lua"), "% playerRaceFlagX2")) assert.is_true(has(read("Modules/QuestiePlayer.lua"), "% playerRaceFlagX2"))
end) end)
end) end)
@@ -326,4 +326,25 @@ describe("Audit Pass 10 - additional performance findings (snapshot)", function(
assert.is_true(has(advancedOptions, "questieCommsQuestListBlockInterval")) assert.is_true(has(advancedOptions, "questieCommsQuestListBlockInterval"))
assert.is_true(has(advancedOptions, "Questie.db.profile.questieCommsEnabled == false")) assert.is_true(has(advancedOptions, "Questie.db.profile.questieCommsEnabled == false"))
end) end)
it("[L50] loader, serializer, and stream are wired for Lua 5.0 compatibility", function()
local loader = read("Modules/Libs/QuestieLoader.lua")
local serializer = read("Modules/Libs/QuestieSerializer.lua")
local stream = read("Modules/QuestieStream.lua")
assert.is_true(has(loader, "bitlib.band = bitlib.band or band32"))
assert.is_true(has(loader, "strsplit = function(separator, text, max)"))
assert.is_true(has(serializer, "local mod = math.mod"))
assert.is_true(has(serializer, "mod(expo, 0x2)"))
assert.is_true(has(serializer, "mod(b1, 0x80)"))
assert.is_false(has(serializer, "expo % 0x2"))
assert.is_true(has(stream, "local mod = math.mod"))
assert.is_true(has(stream, "table.getn(self._bin)"))
assert.is_true(has(stream, "mod(val1, 256)"))
assert.is_true(has(stream, "mod(val2, 256)"))
assert.is_false(has(stream, "val1 % 256"))
assert.is_false(has(stream, "val2 % 256"))
end)
end) end)