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 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 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 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 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 ComputedLengthStruct(binobj.Struct): """A struct whose length can be computed if values are defined.""" int_value = fields.UInt32() value = fields.StringZ() @value.computes def _compute_value(self, all_fields): return str(all_fields["int_value"])
class WAVDataChunk(binobj.Struct): chunk_id = fields.Bytes(const=b"data") size = fields.UInt32(endian="little")
class WAVFileHeader(binobj.Struct): riff_header = fields.Bytes(const=b"RIFF") size = fields.UInt32(endian="little") file_format = fields.Bytes(const=b"WAVE")
class StructWithFieldOverrides(binobj.Struct): one = fields.UInt32(endian=NONDEFAULT_ENDIANNESS) two = fields.Int32(endian=sys.byteorder)
def test_dump__use_default_callable(): """Test dumping when the default value is a callable.""" field = fields.UInt32(name="field", default=lambda: 0x1234, endian="big") assert field.to_bytes() == b"\x00\x00\x12\x34"
def test_dump__use_default_value(): """Test dumping when the default value is a constant.""" field = fields.UInt32(name="field", default=0xDEADBEEF, endian="big") assert field.to_bytes() == b"\xde\xad\xbe\xef"
class StringZTestStruct(binobj.Struct): header = fields.UInt32() string = fields.StringZ() trailer = fields.UInt16()