class Class(binobj.Struct): n_items = fields.UInt16(endian="little") items = fields.Array(fields.UInt16(endian="little"), count=n_items) @n_items.computes def _n_items(self, all_fields): return len(all_fields["items"])
class SimpleBMPFileHeader(binobj.Struct): """A class representing a specific kind of Windows Bitmap file (.bmp). For the sake of simplicity this spec assumes the file uses the legacy DIB header. Validation will fail if this isn't true, even if the BMP file itself is valid. """ magic = fields.Bytes(const=b"BM", discard=True) file_size = fields.UInt32() _reserved = fields.Bytes(const=b"\0\0\0\0", discard=True) pixels_offset = fields.UInt32() # Legacy DIB header (BITMAPINFOHEADER) header_size = fields.UInt32(const=40, discard=True) image_width = fields.Int32() image_height = fields.Int32() n_color_planes = fields.UInt16(const=1) n_bits_per_pixel = fields.UInt16() compression_method = fields.UInt32() bitmap_size = fields.UInt32() v_resolution = fields.Int32() h_resolution = fields.Int32() n_palette_colors = fields.UInt32() n_important_colors = fields.UInt32()
class CPIOFileHeader(binobj.Struct): magic = fields.UInt16(const=0o070707, discard=True, endian="little") device_id = fields.UInt16(default=0, endian="little") inumber = fields.UInt16(default=0, endian="little") mode = fields.UInt16(default=0o644, endian="little") owner_uid = fields.UInt16(default=0, endian="little") owner_gid = fields.UInt16(default=0, endian="little") n_links = fields.UInt16(default=0, endian="little") device_version = fields.UInt16(default=0, endian="little") modified_time = fields.Timestamp32(default=_NOW, tz_aware=True, endian="little") name_size = fields.UInt16(endian="little") file_size = fields.UInt32(endian="little") filename = fields.StringZ(encoding="utf-8") _filename_padding = fields.Bytes( const=b"\0", discard=True, present=lambda f, *_: f["name_size"] % 2 ) data = fields.Bytes(size=file_size) @name_size.computes def _name_size(self, all_fields): return len((all_fields["filename"] + "\0").encode("utf-8")) @file_size.computes def _file_size(self, all_fields): return len(all_fields["data"])
class BasicPresentStruct(binobj.Struct): flags = fields.UInt16(endian="little") thing = fields.StringZ(present=lambda f, *_: f["flags"] & 0x8000) other_thing = fields.UInt16(const=0x1234, endian="little") @flags.computes def _flags(self, all_values): existing = all_values.get("flags", 0) if "thing" in all_values: return existing | 0x8000 return existing
class StructWithArgs(binobj.Struct): def __init__(self, required, **values): super().__init__(**values) self.required = required field_1 = fields.UInt16(endian="little") field_2 = fields.UInt32(endian="big")
class FieldsUnionContainer(binobj.Struct): data_type = fields.UInt8() item = fields.Union( fields.StringZ(), fields.UInt16(endian="little"), load_decider=fields_load_decider, dump_decider=fields_dump_decider, )
class WAVFormatChunk(binobj.Struct): chunk_id = fields.Bytes(const=b"fmt ") size = fields.UInt32(const=16, endian="little") audio_format = fields.UInt16(endian="little") n_channels = fields.UInt16(endian="little") sample_rate = fields.UInt32(endian="little") byte_rate = fields.UInt32(endian="little") block_alignment = fields.UInt16(endian="little") bits_per_sample = fields.UInt16(endian="little") @byte_rate.computes def _byte_rate(self, all_fields): return (all_fields["sample_rate"] * all_fields["n_channels"] * all_fields["bits_per_sample"] // 8) @block_alignment.computes def _block_alignment(self, all_fields): return all_fields["n_channels"] * all_fields["bits_per_sample"] // 8
class CircularReferenceComputedSize(binobj.Struct): """A struct with a size field and array that reference each other.""" count = fields.UInt16(endian="big") stuff = fields.Array(fields.StringZ(), count=count) @count.computes def compute_count(self, all_fields): return len(all_fields["stuff"])
def test_array__sentinel(): """Test deserializing a sequence that has a sentinel terminator.""" def halt(_seq, _str, values, _context, _loaded_fields): return values and (values[-1] == 0xDEAD) sequence = fields.Array(fields.UInt16(endian="little"), halt_check=halt) result = sequence.from_bytes(b"\x00\x00\xff\x00\xad\xde\xff\xff", exact=False) assert result == [0, 0xFF, 0xDEAD]
class FAT12BootSector(binobj.Struct): jump = fields.Bytes(const=b"\xeb\x3c\x90") oem_name = fields.String(size=8, default="mkdosfs", pad_byte=b" ", encoding="ascii") bytes_per_sector = fields.UInt16(default=512) sectors_per_cluster = fields.UInt8() reserved_sectors = fields.UInt16(default=1) num_fats = fields.UInt8(default=2) max_root_entries = fields.UInt16(default=240) total_logical_sectors_16 = fields.UInt16() media_descriptor = fields.UInt8() sectors_per_fat = fields.UInt16() sectors_per_track = fields.UInt16() num_heads = fields.UInt16() num_hidden_sectors = fields.UInt32(default=0) total_logical_sectors_32 = fields.UInt32() drive_number = fields.UInt8() _reserved = fields.Bytes(const=b"\0", discard=True) _ex_boot_signature = fields.Bytes(const=b"\x29", discard=True) volume_id = fields.UInt32(default=lambda: random.randrange(2**32)) volume_label = fields.String(size=11) fs_type = fields.String(size=8, default="FAT12", pad_byte=b" ", encoding="ascii") boot_code = fields.Bytes(size=448, default=b"\xcc" * 448) boot_signature = fields.Bytes(const=b"\x55\xaa") @property def total_logical_sectors(self): return self.total_logical_sectors_16 or self.total_logical_sectors_32 @total_logical_sectors.setter def total_logical_sectors(self, total_sectors): if total_sectors < 1 or total_sectors >= 2**32: raise ValueError("Total sectors must be in [1, 2^32). Got: %d" % total_sectors) if total_sectors < 65535: self.total_logical_sectors_16 = total_sectors self.total_logical_sectors_32 = 0 else: self.total_logical_sectors_16 = 0 self.total_logical_sectors_32 = total_sectors
class StringZTestStruct(binobj.Struct): header = fields.UInt32() string = fields.StringZ() trailer = fields.UInt16()
class _BrokenClass(binobj.Struct): normal_field = fields.UInt16() pep526_field: fields.StringZ
class Class(binobj.Struct): n_items = fields.UInt16(endian="little") @decorators.validates(n_items) def _n_items(self, all_fields): pass
class Class(binobj.Struct): blah = fields.UInt16(const=1234) @blah.computes def _blah(self, all_fields): return 5678
class BasicStructWithArray(binobj.Struct): header = fields.String(const="ABC") numbers = fields.Array(fields.UInt16(endian="big"), count=2) trailer = fields.String(const="XYZ")
class BasicStructWithArray(binobj.Struct): """A basic structure with a sized array.""" header = fields.Bytes(const=b"ABC") numbers = fields.Array(fields.UInt16(endian="big"), count=2) trailer = fields.Bytes(const=b"XYZ")
class UnionItemB(binobj.Struct): _id = fields.UInt8(const=0x7F) other = fields.UInt16(endian="little")