# core/sfp_structures.py """ Defines the ctypes Structures for the Simple Fragmentation Protocol (SFP) and the application-specific image data format. This centralizes data structure definitions, allowing them to be shared between the transport layer (which uses SFPHeader) and the application layer (which interprets the image-specific structures). """ import ctypes # --- SFP Transport Layer Structures --- class SFPHeader(ctypes.Structure): """Structure representing the SFP header (32 bytes).""" _pack_ = 1 _fields_ = [ ("SFP_MARKER", ctypes.c_uint8), ("SFP_DIRECTION", ctypes.c_uint8), ("SFP_PROT_VER", ctypes.c_uint8), ("SFP_PT_SPARE", ctypes.c_uint8), ("SFP_TAG", ctypes.c_uint8), ("SFP_SRC", ctypes.c_uint8), ("SFP_FLOW", ctypes.c_uint8), ("SFP_TID", ctypes.c_uint8), ("SFP_FLAGS", ctypes.c_uint8), ("SFP_WIN", ctypes.c_uint8), ("SFP_ERR", ctypes.c_uint8), ("SFP_ERR_INFO", ctypes.c_uint8), ("SFP_TOTFRGAS", ctypes.c_uint16), ("SFP_FRAG", ctypes.c_uint16), ("SFP_RECTYPE", ctypes.c_uint8), ("SFP_RECSPARE", ctypes.c_uint8), ("SFP_PLDAP", ctypes.c_uint8), ("SFP_PLEXT", ctypes.c_uint8), ("SFP_RECCOUNTER", ctypes.c_uint16), ("SFP_PLSIZE", ctypes.c_uint16), ("SFP_TOTSIZE", ctypes.c_uint32), ("SFP_PLOFFSET", ctypes.c_uint32), ] @classmethod def size(cls): """Returns the size of the structure in bytes.""" return ctypes.sizeof(cls) @staticmethod def get_field_offset(field_name: str) -> int: """Returns the byte offset of a specific field within the structure.""" try: return getattr(SFPHeader, field_name).offset except AttributeError: return -1 # --- Application Layer (Image Format) Structures --- class DataTag(ctypes.Structure): """Structure representing a generic data tag (8 bytes).""" _pack_ = 1 _fields_ = [ ("ID", ctypes.c_uint8 * 2), ("VALID", ctypes.c_uint8), ("VERSION", ctypes.c_uint8), ("SIZE", ctypes.c_uint32), ] @classmethod def size(cls): """Returns the size of the structure in bytes.""" return ctypes.sizeof(cls) class HeaderData(ctypes.Structure): """Structure representing the HEADER_DATA block (~48 bytes).""" _pack_ = 1 _fields_ = [ ("TYPE", ctypes.c_uint8), ("SUBTYPE", ctypes.c_uint8), ("LOGCOLORS", ctypes.c_uint8), ("IMG_RESERVED", ctypes.c_uint8), ("PRODINFO", ctypes.c_uint32 * 2), ("TOD", ctypes.c_uint16 * 2), ("RESERVED", ctypes.c_uint32), ("FCOUNTER", ctypes.c_uint32), ("TIMETAG", ctypes.c_uint32), ("NOMINALFRATE", ctypes.c_uint16), ("FRAMETAG", ctypes.c_uint16), ("OX", ctypes.c_uint16), ("OY", ctypes.c_uint16), ("DX", ctypes.c_uint16), ("DY", ctypes.c_uint16), ("STRIDE", ctypes.c_uint16), ("BPP", ctypes.c_uint8), ("COMP", ctypes.c_uint8), ("SPARE", ctypes.c_uint16), ("PALTYPE", ctypes.c_uint8), ("GAP", ctypes.c_uint8), ] @classmethod def size(cls): """Returns the size of the structure in bytes.""" return ctypes.sizeof(cls) class GeoData(ctypes.Structure): """Structure representing the GEO_DATA block (~80 bytes).""" _pack_ = 1 _fields_ = [ ("INVMASK", ctypes.c_uint32), ("ORIENTATION", ctypes.c_float), ("LATITUDE", ctypes.c_float), ("LONGITUDE", ctypes.c_float), ("REF_X", ctypes.c_uint16), ("REF_Y", ctypes.c_uint16), ("SCALE_X", ctypes.c_float), ("SCALE_Y", ctypes.c_float), ("POI_ORIENTATION", ctypes.c_float), ("POI_LATITUDE", ctypes.c_float), ("POI_LONGITUDE", ctypes.c_float), ("POI_ALTITUDE", ctypes.c_float), ("POI_X", ctypes.c_uint16), ("POI_Y", ctypes.c_uint16), ("SPARE", ctypes.c_uint32 * 7), ] @classmethod def size(cls): """Returns the size of the structure in bytes.""" return ctypes.sizeof(cls) class ImageLeaderData(ctypes.Structure): """Represents the complete Image Leader data structure (~1320 bytes).""" _pack_ = 1 _fields_ = [ ("HEADER_TAG", DataTag), ("HEADER_DATA", HeaderData), ("GEO_TAG", DataTag), ("GEO_DATA", GeoData), ("RESERVED_TAG", DataTag), ("RESERVED_DATA", ctypes.c_uint8 * 128), ("CM_TAG", DataTag), ("COLOUR_MAP", ctypes.c_uint32 * 256), ("PIXEL_TAG", DataTag), ] @classmethod def size(cls): """Returns the size of the structure in bytes.""" return ctypes.sizeof(cls) @staticmethod def get_reserved_data_size() -> int: """Returns the static size of the RESERVED_DATA field.""" return 128 @staticmethod def get_colour_map_size() -> int: """Returns the static size of the COLOUR_MAP field in bytes.""" return 1024