#warning-ignore-all:shadowed_variable #warning-ignore-all:unused_argument # leftover_bits is array of form [count, value] # array is used for reference semantics as get and put operations may mutate it class StructType: func get_value(buffer: StreamPeer, leftover_bits: Array): assert(false, 'Deserialization not implemented') func put_value(buffer: StreamPeer, value, leftover_bits: Array): assert(false, 'Serialization not implemented') class U8 extends StructType: func get_value(buffer: StreamPeer, leftover_bits: Array): return buffer.get_u8() func put_value(buffer: StreamPeer, value, leftover_bits: Array): buffer.put_u8(value) class S8 extends StructType: func get_value(buffer: StreamPeer, leftover_bits: Array): return buffer.get_8() func put_value(buffer: StreamPeer, value, leftover_bits: Array): buffer.put_8(value) class U16 extends StructType: func get_value(buffer: StreamPeer, leftover_bits: Array): return buffer.get_u16() func put_value(buffer: StreamPeer, value, leftover_bits: Array): buffer.put_u16(value) class S16 extends StructType: func get_value(buffer: StreamPeer, leftover_bits: Array): return buffer.get_16() func put_value(buffer: StreamPeer, value, leftover_bits: Array): buffer.put_16(value) class U24 extends StructType: func get_value(buffer: StreamPeer, leftover_bits: Array): return buffer.get_u16() | (buffer.get_u8() << 16) func put_value(buffer: StreamPeer, value, leftover_bits: Array): buffer.put_u16(value & 0xFFFF) buffer.put_u8(value >> 16) class S24 extends StructType: func get_value(buffer: StreamPeer, leftover_bits: Array): var unsigned = buffer.get_u16() | (buffer.get_u8() << 16) return unsigned - (2 * (unsigned & 0x800000)) func put_value(buffer: StreamPeer, value, leftover_bits: Array): var unsigned = value % 0x1000000 buffer.put_u16(unsigned & 0xFFFF) buffer.put_u8(unsigned >> 16) class U32 extends StructType: func get_value(buffer: StreamPeer, leftover_bits: Array): return buffer.get_u32() func put_value(buffer: StreamPeer, value, leftover_bits: Array): buffer.put_u32(value) class S32 extends StructType: func get_value(buffer: StreamPeer, leftover_bits: Array): return buffer.get_32() func put_value(buffer: StreamPeer, value, leftover_bits: Array): buffer.put_32(value) class UBits extends StructType: var bits = 8 func _init(bits: int): self.bits = bits func get_value(buffer: StreamPeer, leftover_bits: Array): while leftover_bits[0] < bits: leftover_bits[1] |= buffer.get_u8() << leftover_bits[0] leftover_bits[0] += 8 var value = leftover_bits[1] & ((1 << bits)-1) leftover_bits[1] = leftover_bits[1] >> bits leftover_bits[0] -= bits return value func put_value(buffer: StreamPeer, value, leftover_bits: Array): leftover_bits[1] |= value << leftover_bits[0] leftover_bits[0] += bits while leftover_bits[0] >= 8: buffer.put_8(leftover_bits[1] & 0xFF) leftover_bits[0] -= 8 leftover_bits[1] = leftover_bits[1] >> 8 class Struct extends StructType: var members := [] # Array of [name, StructType] func get_value(buffer: StreamPeer, leftover_bits: Array): var result = {} for member in members: var key: String = member[0] var structType: StructType = member[1] result[key] = structType.get_value(buffer, leftover_bits) return result func put_value(buffer: StreamPeer, value, leftover_bits: Array): for member in members: var key: String = member[0] var structType: StructType = member[1] if not (key in value): print_debug('Key "%s" missing from value supplied' % key) return structType.put_value(buffer, value[key], leftover_bits) class StructArrayType extends StructType: var count: int var structType: StructType func _init(count, structType) -> void: self.count = count self.structType = structType func get_value(buffer: StreamPeer, leftover_bits: Array): # Might be a bit too much branching but oh well if self.structType is U8: var result = PoolByteArray() # Slight optimization over calling the method for i in self.count: result.append(buffer.get_u8()) return result var result = [] for i in self.count: result.append(self.structType.get_value(buffer, leftover_bits)) return result func put_value(buffer: StreamPeer, value, leftover_bits: Array): if len(value) < self.count: print_debug('Not enough values supplied') return for i in self.count: self.structType.put_value(buffer, value[i], leftover_bits) static func get_base_structarraytypes() -> Dictionary: return { 'u8': U8.new(), 'u16': U16.new(), 'u24': U24.new(), 'u32': U32.new(), 's8': S8.new(), 's16': S16.new(), 's24': S24.new(), 's32': S32.new(), } static func get_structarraytype(type: String, existing_structs: Dictionary): var tokens := type.split(' ', false) var t: String = tokens[-1] var inner_type if t in existing_structs: inner_type = existing_structs[t] elif t[0] == 'u': var b := int(t.substr(1)) if b > 0: inner_type = UBits.new(b) existing_structs['u%d'%b] = inner_type # Cache it for future use if not inner_type: print_debug('typestring "%s" has no matches for "%s" in existing structs' % [type, t]) return var l := len(tokens) if l == 1: return inner_type # Our parsing goal is to turn 'a of b of c of d' into StructArrayType, b>, a> # Our strategy is to parse backwards over the tokens, changing inner_type at each point # a of b of c of (d) # a of b of (c of d) # a of (b of c of d) # (a of b of c of d) # done var i := l-2 while i > -1: match tokens[i]: 'of': i -= 1 var l1 = int(tokens[i]) if l1 > 1: inner_type = StructArrayType.new(l1, inner_type) # Might be worth caching these later on if we use them more i -= 1 var k: print_debug('Invalid keyword used in type designator: "%s"' % k) return return inner_type static func parse_struct_definitions_from_tsv_file(tsv_file: File, existing_structs: Dictionary) -> void: var current_struct: Struct var line_num := 0 # Currently only used for step-through debugging var l := tsv_file.get_len() while tsv_file.get_position() < l: var line := tsv_file.get_csv_line('\t') line_num += 1 var size = line.size() if size < 2: continue # Size is at least 2 var type := line[0] var label := line[1] if type == 'struct': # New struct declaration current_struct = Struct.new() existing_structs[label] = current_struct elif type and label: # TODO: Maybe store the trailing comments somewhere? current_struct.members.append([label, get_structarraytype(type, existing_structs)]) static func parse_struct_definitions_from_tsv_filename(filename: String, existing_structs: Dictionary) -> int: var file := File.new() match file.open(filename, File.READ): OK: parse_struct_definitions_from_tsv_file(file, existing_structs) return OK var error: print_debug('Error reading tsv '+filename+' - %d'%error) return error