local struct = {} function struct.pack(format, ...) local stream = {} local vars = {...} local endianness = true for i = 1, format:len() do local opt = format:sub(i, i) if opt == '<' then endianness = true elseif opt == '>' then endianness = false elseif opt:find('[bBhHiIlL]') then local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1 local val = tonumber(table.remove(vars, 1)) local bytes = {} for _ = 1, n do table.insert(bytes, string.char(val % (2 ^ 8))) val = math.floor(val / (2 ^ 8)) end if not endianness then table.insert(stream, string.reverse(table.concat(bytes))) else table.insert(stream, table.concat(bytes)) end elseif opt:find('[fd]') then local val = tonumber(table.remove(vars, 1)) local sign = 0 if val < 0 then sign = 1 val = -val end local mantissa, exponent = math.frexp(val) if val == 0 then mantissa = 0 exponent = 0 else mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, (opt == 'd') and 53 or 24) exponent = exponent + ((opt == 'd') and 1022 or 126) end local bytes = {} if opt == 'd' then val = mantissa for _ = 1, 6 do table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) val = math.floor(val / (2 ^ 8)) end else table.insert(bytes, string.char(math.floor(mantissa) % (2 ^ 8))) val = math.floor(mantissa / (2 ^ 8)) table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) val = math.floor(val / (2 ^ 8)) end table.insert(bytes, string.char(math.floor(exponent * ((opt == 'd') and 16 or 128) + val) % (2 ^ 8))) val = math.floor((exponent * ((opt == 'd') and 16 or 128) + val) / (2 ^ 8)) table.insert(bytes, string.char(math.floor(sign * 128 + val) % (2 ^ 8))) if not endianness then table.insert(stream, string.reverse(table.concat(bytes))) else table.insert(stream, table.concat(bytes)) end elseif opt == 's' then table.insert(stream, tostring(table.remove(vars, 1))) table.insert(stream, string.char(0)) elseif opt == 'c' then local n = format:sub(i + 1):match('%d+') local str = tostring(table.remove(vars, 1)) local len = tonumber(n) if len <= 0 then len = str:len() end if len - str:len() > 0 then str = str .. string.rep(' ', len - str:len()) end table.insert(stream, str:sub(1, len)) end end return table.concat(stream) end function struct.unpack(format, stream, pos) local vars = {} local iterator = pos or 1 local endianness = true for i = 1, format:len() do local opt = format:sub(i, i) if opt == '<' then endianness = true elseif opt == '>' then endianness = false elseif opt:find('[bBhHiIlL]') then local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1 local signed = opt:lower() == opt local val = 0 for j = 1, n do local byte = string.byte(stream:sub(iterator, iterator)) if endianness then val = val + byte * (2 ^ ((j - 1) * 8)) else val = val + byte * (2 ^ ((n - j) * 8)) end iterator = iterator + 1 end if signed and val >= 2 ^ (n * 8 - 1) then val = val - 2 ^ (n * 8) end table.insert(vars, math.floor(val)) elseif opt:find('[fd]') then local n = (opt == 'd') and 8 or 4 local x = stream:sub(iterator, iterator + n - 1) iterator = iterator + n if not endianness then x = string.reverse(x) end local sign = 1 local mantissa = string.byte(x, (opt == 'd') and 7 or 3) % ((opt == 'd') and 16 or 128) for j = n - 2, 1, -1 do mantissa = mantissa * (2 ^ 8) + string.byte(x, j) end if string.byte(x, n) > 127 then sign = -1 end local exponent = (string.byte(x, n) % 128) * ((opt == 'd') and 16 or 2) + math.floor(string.byte(x, n - 1) / ((opt == 'd') and 16 or 128)) if exponent == 0 then table.insert(vars, 0.0) else mantissa = (math.ldexp(mantissa, (opt == 'd') and -52 or -23) + 1) * sign table.insert(vars, math.ldexp(mantissa, exponent - ((opt == 'd') and 1023 or 127))) end elseif opt == 's' then local bytes = {} for j = iterator, stream:len() do if stream:sub(j,j) == string.char(0) or stream:sub(j) == '' then break end table.insert(bytes, stream:sub(j, j)) end local str = table.concat(bytes) iterator = iterator + str:len() + 1 table.insert(vars, str) elseif opt == 'c' then local n = format:sub(i + 1):match('%d+') local len = tonumber(n) if len <= 0 then len = table.remove(vars) end table.insert(vars, stream:sub(iterator, iterator + len - 1)) iterator = iterator + len end end return unpack(vars) end return struct