C-style structs for Python
Convert C struct/union definitions into Python classes with methods for serializing/deserializing. The usage is very simple: create a class subclassing cstruct.MemCStruct and add a C struct/union definition as a string in the struct field. The C struct/union definition is parsed at runtime and the struct format string is generated. The class offers the method "unpack" for deserializing an array of bytes into a Python object and the method "pack" for serializing the values into an array of bytes.
The following program reads the DOS partition information from a disk.
#!/usr/bin/env python
import cstruct
class Position(cstruct.MemCStruct):
__byte_order__ = cstruct.LITTLE_ENDIAN
__struct__ = """
unsigned char head;
unsigned char sector;
unsigned char cyl;
"""
class Partition(cstruct.MemCStruct):
__byte_order__ = cstruct.LITTLE_ENDIAN
__struct__ = """
#define ACTIVE_FLAG 0x80
unsigned char status; /* 0x80 - active */
struct Position start;
unsigned char partition_type;
struct Position end;
unsigned int start_sect; /* starting sector counting from 0 */
unsigned int sectors; /* nr of sectors in partition */
"""
def print_info(self):
print(f"bootable: {'Y' if self.status & cstruct.getdef('ACTIVE_FLAG') else 'N'}")
print(f"partition_type: {self.partition_type:02X}")
print(f"start: head: {self.start.head:X} sectory: {self.start.sector:X} cyl: {self.start.cyl:X}")
print(f"end: head: {self.end.head:X} sectory: {self.end.sector:X} cyl: {self.end.cyl:X}")
print(f"starting sector: {self.start_sect:08x}")
print(f"size MB: {self.sectors / 2 / 1024}")
class MBR(cstruct.MemCStruct):
__byte_order__ = cstruct.LITTLE_ENDIAN
__struct__ = """
#define MBR_SIZE 512
#define MBR_DISK_SIGNATURE_SIZE 4
#define MBR_USUALY_NULLS_SIZE 2
#define MBR_SIGNATURE_SIZE 2
#define MBR_BOOT_SIGNATURE 0xaa55
#define MBR_PARTITIONS_NUM 4
#define MBR_PARTITIONS_SIZE (sizeof(Partition) * MBR_PARTITIONS_NUM)
#define MBR_UNUSED_SIZE (MBR_SIZE - MBR_DISK_SIGNATURE_SIZE - MBR_USUALY_NULLS_SIZE - MBR_PARTITIONS_SIZE - MBR_SIGNATURE_SIZE)
char unused[MBR_UNUSED_SIZE];
unsigned char disk_signature[MBR_DISK_SIGNATURE_SIZE];
unsigned char usualy_nulls[MBR_USUALY_NULLS_SIZE];
struct Partition partitions[MBR_PARTITIONS_NUM];
uint16 signature;
"""
@property
def disk_signature_str(self):
return "".join(reversed([f"{x:02x}" for x in self.disk_signature]))
def print_info(self):
print(f"disk signature: {self.disk_signature_str}")
for i, partition in enumerate(self.partitions):
print("")
print(f"partition: {i}")
partition.print_info()
disk = "mbr"
with open(disk, "rb") as f:
mbr = MBR()
mbr.unpack(f)
mbr.print_info()