--[[ foo_struct = { { name = "field1", typ = T_UINT32, cond = { { cond = COND_EQUALS, value = 0x12345678 } } }, { name = "field2", typ = T_STRING, length = 4, cond = { { cond = COND_EQ, value = "test" } } }, { name = "field3", typ = T_STRINGZ }, } ]]-- struct_processor = { T_STRINGZ = 0, T_UINT8 = 1, T_UINT16 = 2, T_UINT32 = 3, T_FLOAT = 4, T_DOUBLE = 5, T_INT8 = 6, T_INT16 = 7, T_INT32 = 8, T_STRING = 9, T_STRINGN8 = 10, COND_EQ = 1, COND_GE = 2, COND_GT = 3, COND_LE = 4, COND_LT = 5, COND_NE = 6, } function struct_processor:read_string(fin, length) local r, i, c = "" if length then for i = 1, length do c = fin:readU8() r = r .. string.char(c) end else while true do c = fin:readU8() if c == 0 then break end r = r .. string.char(c) end end return r end function struct_processor:read_value(fin, v) local value if v.typ == self.T_UINT8 then value = fin:readU8() elseif v.typ == self.T_UINT16 then value = fin:readU16() elseif v.typ == self.T_UINT32 then value = fin:readU32() elseif v.typ == self.T_FLOAT then value = fin:readFloat() elseif v.typ == self.T_DOUBLE then value = fin:readDouble() elseif v.typ == self.T_INT8 then value = fin:readU8() if value > 128 then value = value - 256 end elseif v.typ == self.T_INT16 then value = fin:readU16() if value >= 32768 then value = value - 65536 end elseif v.typ == self.T_INT32 then value = fin:readU32() if value >= 2147483648 then value = value - 4294967296 end elseif v.typ == self.T_STRING then if type(v.length) ~= "number" then error "String length not specified in structure" end value = self:read_string(fin, v.length) elseif v.typ == self.T_STRINGZ then value = self:read_string(fin) elseif v.typ == self.T_STRINGN8 then local length = fin:readU8() value = self:read_string(fin, length) else error "Type unknown in structure" end return value end function struct_processor:write_value(fout, v, value) if v.typ == self.T_UINT8 then fout:writeU8(value) elseif v.typ == self.T_UINT16 then fout:writeU16(value) elseif v.typ == self.T_UINT32 then fout:writeU16(value) elseif v.typ == self.T_FLOAT then fout:writeFloat(value) elseif v.typ == self.T_DOUBLE then fout:writeDouble(value) elseif v.typ == self.T_INT8 then if value < 0 then value = value + 256 end fout:writeU8(value) elseif v.typ == self.T_INT16 then if value < 0 then value = value + 65536 end fout:writeU16(value) elseif v.typ == self.T_INT32 then if value < 0 then value = value + 4294967296 end fout:writeU32(value) elseif v.typ == self.T_STRING then if type(v.length) ~= "number" then error "String length not specified in structure" end for i = 1, v.length do fout:writeU8(value:byte(i)) end elseif v.typ == self.T_STRINGZ then for i = 1, #value do fout:writeU8(value:byte(i)) end fout:writeU8(0) elseif v.typ == self.T_STRINGN8 then fout:writeU8(#value) for i = 1, #value do fout:writeU8(value:byte(i)) end else error "Type unknown in structure" end end function struct_processor:check_structure(struct, values) for k, v in pairs(struct) do if v.cond then local gr, r, _, c = true, false for _, c in pairs(v.cond) do if type(c.value) == "nil" and type(c.field) == "nil" then error "Needs a comparison value" end local cvalue = c.value or struct[c.field] if c.cond == self.COND_EQ then r = value == cvalue elseif c.cond == self.COND_GE then r = value >= cvalue elseif c.cond == self.COND_GT then r = value > cvalue elseif c.cond == self.COND_LE then r = value <= cvalue elseif c.cond == self.COND_LT then r = value < cvalue elseif c.cond == self.COND_NE then r = value ~= cvalue else error "Condition type unknown" end gr = gr and r end if not gr then error("Conditions for field `" .. v.name .. "' not satisfied with value = " .. value) end end end end function struct_processor:read_structure(fin, struct) local k, v, value local values = {} for k, v in pairs(struct) do if v.count and v.count >= 2 then value = {} for i = 1, v.count do value[i] = self:read_value(fin, v) end end values[v.name] = value end self:check_structure(struct, values) return values end function struct_processor:write_structure(fout, struct, values) local k, v, value, i self:check_structure(struct, values) for k, v in pairs(struct) do value = values[v.name] if v.count and v.count >= 2 then for i = 1, v.count do self:write_value(fout, v, value[i]) end else self:write_value(fout, v, value) end end end function struct_processor:struct_sizeof(struct) local k, v, r, count r = 0 for k, v in pairs(struct) do if v.count and v.count >= 2 then count = v.count else count = 1 end if v.typ == self.T_UINT8 then r = r + 1 * count elseif v.typ == self.T_UINT16 then r = r + 2 * count elseif v.typ == self.T_UINT32 then r = r + 4 * count elseif v.typ == self.T_FLOAT then r = r + 4 * count elseif v.typ == self.T_DOUBLE then r = r + 8 * count elseif v.typ == self.T_INT8 then r = r + 1 * count elseif v.typ == self.T_INT16 then r = r + 2 * count elseif v.typ == self.T_INT32 then r = r + 4 * count elseif v.typ == self.T_STRING then if type(v.length) ~= "number" then error "String length not specified in structure" end r = r + v.length * count elseif v.typ == self.T_STRINGZ then error "Size of a structure with an ASCIIZ string is unknown." elseif v.typ == self.T_STRINGN8 then error "Size of a structure with an arbitrary-sized string is unknown." else error "Type unknown in structure" end end return r end