131 lines
2.9 KiB
Lua
Executable File
131 lines
2.9 KiB
Lua
Executable File
-- read formats
|
|
-- return a value if applicable, which will be packed
|
|
-- otherwise return nil
|
|
-- Copyright © 2008 Ben "ToxicFrog" Kelly; see COPYING
|
|
|
|
-- load operations common to both read and write, and set __index so that
|
|
-- requests for, say, read.seekto will succeed
|
|
local name = (...):gsub('%.[^%.]+$', '')
|
|
local struct = require (name)
|
|
local common = require (name..".common")
|
|
local read = setmetatable({}, { __index = common })
|
|
local fp = require (name..".fp")
|
|
|
|
-- boolean
|
|
-- true if any bit is 1, false otherwise
|
|
function read.b(fd, w)
|
|
return read.u(fd, w) ~= 0
|
|
end
|
|
|
|
-- counted string
|
|
-- a string immediately prefaced with its length as a uint
|
|
function read.c(fd, w)
|
|
w = read.u(fd, w)
|
|
return read.s(fd, w)
|
|
end
|
|
|
|
-- float
|
|
-- this is callout to the floating-point read/write module, if installed
|
|
function read.f(fd, w)
|
|
if not fp.r[w] then
|
|
error("struct.unpack: illegal floating point width")
|
|
end
|
|
|
|
return fp.r[w](read.s(fd,w))
|
|
end
|
|
|
|
-- signed int of w bytes
|
|
function read.i(fd, w)
|
|
local i = read.u(fd, w)
|
|
if i >= 2^(w*8 - 1) then
|
|
return i - 2^(w*8)
|
|
end
|
|
return i
|
|
end
|
|
|
|
-- bitmask of w bytes
|
|
-- we need to read and unpack it as a string, not an unsigned, because otherwise
|
|
-- we're limited to 52 bits
|
|
function read.m(fd, w)
|
|
local buf = read.s(fd, w)
|
|
local mask = {}
|
|
|
|
local sof = (read.is_bigendian and w or 1)
|
|
local eof = (read.is_bigendian and 1 or w)
|
|
local dir = (read.is_bigendian and -1 or 1)
|
|
|
|
for i=sof,eof,dir do
|
|
local byte = buf:sub(i,i):byte()
|
|
local bits = struct.explode(byte)
|
|
for i=1,8 do
|
|
mask[#mask+1] = bits[i] or false
|
|
end
|
|
end
|
|
return mask
|
|
end
|
|
|
|
-- fixed point bit aligned
|
|
-- w is in the form d.f, where d is the number of bits in the integer part
|
|
-- and f the number of bits in the fractional part
|
|
function read.P(fd, dp, fp)
|
|
if (dp+fp) % 8 ~= 0 then
|
|
error "total width of fixed point value must be byte multiple"
|
|
end
|
|
return read.u(fd, (dp+fp)/8)/(2^fp)
|
|
end
|
|
|
|
-- fixed point byte aligned
|
|
function read.p(fd, dp, fp)
|
|
return read.P(fd, dp*8, fp*8)
|
|
end
|
|
|
|
-- string
|
|
-- reads exactly w bytes of data and returns them verbatim
|
|
function read.s(fd, w)
|
|
return fd:read(w or 0)
|
|
end
|
|
|
|
-- unsigned int
|
|
function read.u(fd, w)
|
|
local u = 0
|
|
local s = read.s(fd, w)
|
|
|
|
-- the "is_bigendian" setting is provided by struct.common
|
|
local sof = (read.is_bigendian and 1 or w)
|
|
local eof = (read.is_bigendian and w or 1)
|
|
local dir = (read.is_bigendian and 1 or -1)
|
|
|
|
for i=sof,eof,dir do
|
|
u = u * 2^8 + s:sub(i,i):byte()
|
|
end
|
|
|
|
return u
|
|
end
|
|
|
|
-- skip/pad
|
|
-- reads w bytes and discards them
|
|
function read.x(fd, w)
|
|
fd:read(w)
|
|
return nil
|
|
end
|
|
|
|
-- null-terminated string
|
|
-- if w is omitted, reads up to and including the first nul, and returns everything
|
|
-- except that nul
|
|
-- otherwise, reads exactly w bytes and returns everything up to the first nul
|
|
function read.z(fd, w)
|
|
if w then
|
|
return read.s(fd, w):match('^%Z*')
|
|
end
|
|
|
|
local buf = ""
|
|
local c = read.s(fd, 1)
|
|
while #c > 0 and c ~= string.char(0) do
|
|
buf = buf..c
|
|
c = read.s(fd, 1)
|
|
end
|
|
return buf
|
|
end
|
|
|
|
return read
|