1# Copyright (c) 2019, Stefan Grönke 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted providing that the following conditions 6# are met: 7# 1. Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# 2. Redistributions in binary form must reproduce the above copyright 10# notice, this list of conditions and the following disclaimer in the 11# documentation and/or other materials provided with the distribution. 12# 13# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 17# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 21# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 22# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23# POSSIBILITY OF SUCH DAMAGE. 24import ctypes 25import typing 26import struct 27 28 29class CtlType: 30 min_size = 0 31 data: bytes 32 ctype: typing.Optional[type] = None 33 unpack_format: typing.Optional[str] = None 34 35 def __init__(self, data: bytes, size: int) -> None: 36 self.data = data 37 self.size = size 38 39 @property 40 def amount(self): 41 if self.min_size == 0: 42 return 1 43 return int(self.size / self.min_size) 44 45 @property 46 def value(self) -> typing.Any: 47 if self.unpack_format is None: 48 return self.data.value 49 values = list(struct.unpack( 50 f"<{self.unpack_format * self.amount}", 51 self.data 52 )) 53 if len(values) == 1: 54 return values[0] 55 return values 56 57 def __str__(self) -> str: 58 if self.amount == 1: 59 return self.__tostring(self.value) 60 return " ".join([self.__tostring(x) for x in self.value]) 61 62 @staticmethod 63 def __tostring(value: typing.Any) -> str: 64 if isinstance(value, bytes) is True: 65 return value.decode() 66 return str(value) 67 68 69class NODE(CtlType): 70 ctype = ctypes.c_uint 71 min_size = ctypes.sizeof(ctypes.c_uint) 72 unpack_format = "I" 73 74 75class INT(CtlType): 76 ctype = ctypes.c_int 77 min_size = ctypes.sizeof(ctypes.c_int) 78 unpack_format = "i" 79 80 81class STRING(CtlType): 82 83 @property 84 def value(self) -> str: 85 return self.data.value.decode() 86 87 88class S64(CtlType): 89 ctype = ctypes.c_int64 90 min_size = ctypes.sizeof(ctypes.c_int64) 91 unpack_format = "q" 92 93 94class STRUCT(CtlType): 95 pass 96 97 98class OPAQUE(CtlType): 99 pass 100 101 102class UINT(CtlType): 103 ctype = ctypes.c_uint 104 min_size = ctypes.sizeof(ctypes.c_uint) 105 unpack_format = "I" 106 107 108class LONG(CtlType): 109 ctype = ctypes.c_long 110 min_size = ctypes.sizeof(ctypes.c_long) 111 unpack_format = "q" 112 113 114class ULONG(CtlType): 115 ctype = ctypes.c_ulong 116 min_size = ctypes.sizeof(ctypes.c_ulong) 117 unpack_format = "Q" 118 119 120class U64(CtlType): 121 ctype = ctypes.c_uint64 122 min_size = ctypes.sizeof(ctypes.c_uint64) 123 unpack_format = "Q" 124 125 126class U8(CtlType): 127 ctype = ctypes.c_uint8 128 min_size = ctypes.sizeof(ctypes.c_uint8) 129 unpack_format = "B" 130 131 132class U16(CtlType): 133 ctype = ctypes.c_uint16 134 min_size = ctypes.sizeof(ctypes.c_uint16) 135 unpack_format = "H" 136 137 138class S8(CtlType): 139 ctype = ctypes.c_int8 140 min_size = ctypes.sizeof(ctypes.c_int8) 141 unpack_format = "b" 142 143 144class S16(CtlType): 145 ctype = ctypes.c_int16 146 min_size = ctypes.sizeof(ctypes.c_int16) 147 unpack_format = "h" 148 149 150class S32(CtlType): 151 ctype = ctypes.c_int32 152 min_size = ctypes.sizeof(ctypes.c_int32) 153 unpack_format = "i" 154 155 156class U32(CtlType): 157 ctype = ctypes.c_uint32 158 min_size = ctypes.sizeof(ctypes.c_uint32) 159 unpack_format = "I" 160 161 162def identify_type(kind: int, fmt: bytes) -> CtlType: 163 ctl_type = kind & 0xF 164 if ctl_type == 1: 165 return NODE 166 elif ctl_type == 2: 167 return INT 168 elif ctl_type == 3: 169 return STRING 170 elif ctl_type == 4: 171 return S64 172 elif ctl_type == 5: 173 # return STRUCT if (fmt[0:1] == b"S") else OPAQUE 174 return OPAQUE 175 elif ctl_type == 6: 176 return UINT 177 elif ctl_type == 7: 178 return LONG 179 elif ctl_type == 8: 180 return ULONG 181 elif ctl_type == 9: 182 return U64 183 elif ctl_type == 10: 184 return U8 185 elif ctl_type == 11: 186 return U16 187 elif ctl_type == 12: 188 return S8 189 elif ctl_type == 13: 190 return S16 191 elif ctl_type == 14: 192 return S32 193 elif ctl_type == 15: 194 return U32 195 else: 196 raise Exception(f"Invalid ctl_type: {ctl_type}") 197