local byte = string.byte
local tointeger, tonumber, tostring, type, error = math.tointeger, tonumber, tostring, type, error
local function argerror(lib, name, n, msg)
error("bad argument #" .. n .. " to '" .. lib .. "." .. name .. "' (" .. msg .. ")", 0)
end
local function typename(v, given)
if not given then return "no value" end
return type(v)
end
local function checkstring(lib, name, n, v, given)
local t = type(v)
if t == "string" then return v end
if t == "number" then return tostring(v) end
argerror(lib, name, n, "string expected, got " .. typename(v, given))
end
local function checkint(lib, name, n, v, given)
if type(v) == "number" then
local i = tointeger(v)
if i then return i end
argerror(lib, name, n, "number has no integer representation")
end
if type(v) == "string" then
local x = tonumber(v)
if x and tointeger(x) then return tointeger(x) end
end
argerror(lib, name, n, "number expected, got " .. typename(v, given))
end
local function cardinal(s, p, n, le)
if p < 1 or p + n - 1 > #s then return nil end
local v = 0
if le then
for i = p + n - 1, p, -1 do v = v * 256 + byte(s, i) end
else
for i = p, p + n - 1 do v = v * 256 + byte(s, i) end
end
return v
end
local function signed(v, n)
if v and v >= 1 << (8 * n - 1) then return v - (1 << (8 * n)) end
return v
end
local readers = {}
for n = 1, 4 do
readers["readcardinal" .. n] = { n, function(s, p) return cardinal(s, p, n) end }
readers["readinteger" .. n] = { n, function(s, p) return signed(cardinal(s, p, n), n) end }
readers["readcardinal" .. n .. "le"] = { n, function(s, p) return cardinal(s, p, n, true) end }
readers["readinteger" .. n .. "le"] = { n, function(s, p) return signed(cardinal(s, p, n, true), n) end }
end
readers.readfixed2 = { 2, function(s, p)
local a = cardinal(s, p, 1)
local b = cardinal(s, p + 1, 1)
if not (a and b) then return nil end
return a + b / 256.0
end }
readers.readfixed4 = { 4, function(s, p)
local a = signed(cardinal(s, p, 2), 2)
local b = cardinal(s, p + 2, 2)
if not (a and b) then return nil end
return a + b / 65536.0
end }
readers.read2dot14 = { 2, function(s, p)
local v = signed(cardinal(s, p, 2), 2)
return v and v / 16384.0
end }
local function bytes(s, p, n)
if p < 1 then return end
local last = p + n - 1
if last > #s then last = #s end
if last < p then return end
return byte(s, p, last)
end
local function table_of(read, s, p, count, width)
local t = {}
for i = 1, count do
local v = read(s, p + (i - 1) * width)
if v == nil then break end
t[i] = v
end
return t
end
local sio = {}
for name, r in pairs(readers) do
local read = r[2]
sio[name] = function(...)
local s, p = ...
local k = select("#", ...)
s = checkstring("sio", name, 1, s, k >= 1)
p = checkint("sio", name, 2, p, k >= 2)
return (read(s, p))
end
end
sio.readbytes = function(...)
local s, p, n = ...
local k = select("#", ...)
s = checkstring("sio", "readbytes", 1, s, k >= 1)
p = checkint("sio", "readbytes", 2, p, k >= 2)
n = checkint("sio", "readbytes", 3, n, k >= 3)
return bytes(s, p, n)
end
sio.readbytetable = function(...)
local s, p, n = ...
local k = select("#", ...)
s = checkstring("sio", "readbytetable", 1, s, k >= 1)
p = checkint("sio", "readbytetable", 2, p, k >= 2)
n = checkint("sio", "readbytetable", 3, n, k >= 3)
return { bytes(s, p, n) }
end
for _, kind in ipairs { "cardinal", "integer" } do
local name = "read" .. kind .. "table"
sio[name] = function(...)
local s, p, n, w = ...
local k = select("#", ...)
s = checkstring("sio", name, 1, s, k >= 1)
p = checkint("sio", name, 2, p, k >= 2)
n = checkint("sio", name, 3, n, k >= 3)
w = checkint("sio", name, 4, w, k >= 4)
local r = readers["read" .. kind .. w]
if not r then return {} end
return table_of(r[2], s, p, n, w)
end
end
local fio = {}
local function fread(f, n)
local s = f:read(n)
return s or ""
end
for name, r in pairs(readers) do
local width, read = r[1], r[2]
fio[name] = function(f)
local s = fread(f, width)
return (read(s, 1))
end
end
fio.readbytes = function(f, n)
local s = fread(f, n)
return bytes(s, 1, n)
end
fio.readbytetable = function(f, n)
local s = fread(f, n)
return { bytes(s, 1, n) }
end
for _, kind in ipairs { "cardinal", "integer" } do
fio["read" .. kind .. "table"] = function(f, n, w)
local r = readers["read" .. kind .. w]
if not r then return {} end
local s = fread(f, n * w)
return table_of(r[2], s, 1, n, w)
end
end
fio.readline = function(f)
return f:read("l")
end
fio.getposition = function(f)
return f:seek()
end
fio.setposition = function(f, p)
f:seek("set", p)
end
fio.skipposition = function(f, n)
f:seek("cur", n)
end
return sio, fio