def __init__(self, const): self.const = const super().__init__( c.IfThenElse( c.this._building, c.Select(c.Bytes(len(self.const)), c.Pass), Optional(c.Const(const)), ) )
def test_fieldin(): test_struct = struct.XStruct( 'a' / construct.Int32ub, 'b' / construct.IfThenElse(adapters.FieldIn('a', [1, 2, 3]), construct.Int32ub, construct.Int16ub)) assert test_struct(a=1, b=2).build() == b'\x00\x00\x00\x01\x00\x00\x00\x02' assert test_struct(a=2, b=2).build() == b'\x00\x00\x00\x02\x00\x00\x00\x02' assert test_struct(a=3, b=2).build() == b'\x00\x00\x00\x03\x00\x00\x00\x02' assert test_struct(a=4, b=2).build() == b'\x00\x00\x00\x04\x00\x02'
class IfThenElse(cst.TContainerMixin): @dataclasses.dataclass class Then(cst.TContainerMixin): then_1: int = cst.sfield(cs.Int16sb) then_2: int = cst.sfield(cs.Int16sb) @dataclasses.dataclass class Else(cst.TContainerMixin): else_1: int = cst.sfield(cs.Int8sb) else_2: int = cst.sfield(cs.Int8sb) else_3: int = cst.sfield(cs.Int8sb) else_4: int = cst.sfield(cs.Int8sb) choice: int = cst.sfield(cs.Int8ub) if_then_else: t.Union[Then, Else] = cst.sfield( cs.IfThenElse(cs.this.choice == 0, cst.TStruct(Then), cst.TStruct(Else)))
construct.Rename('main', CREDENTIAL_DEC_MAIN), construct.If( lambda ctx: ctx.header.unk_type == 2, construct.Array(lambda ctx: ctx.header.unk_blocks, CREDENTIAL_DEC_BLOCK_ENC))) # VAULT file structs. VAULT_ATTRIBUTE_ENCRYPTED = construct.Struct( 'VAULT_ATTRIBUTE_ENCRYPTED', construct.Byte('has_iv'), construct.IfThenElse( '', lambda ctx: ctx.has_iv, construct.Embed( construct.Struct( 'encrypted', construct.ULInt32('iv_size'), construct.Bytes('iv', lambda ctx: ctx.iv_size), construct.Bytes('data', lambda ctx: ctx.size - 1 - 4 - ctx.iv_size))), construct.Embed( construct.Struct('encrypted', construct.Bytes('data', lambda ctx: ctx.size - 1))))) VAULT_ATTRIBUTE = construct.Struct( 'VAULT_ATTRIBUTE', construct.ULInt32('id'), construct.ULInt32('attr_unknown_1'), construct.ULInt32('attr_unknown_2'), construct.ULInt32('attr_unknown_3'), # Ok, this is bad, but till now I have not understood how to distinguish # the different structs used. Actually the last ATTRIBUTE is different. # Usually we have 6 more bytes zeroed, not always aligned: otherwise,
"counts" / C.Int16ul, "imageCount" / C.Computed(lambda this: this.counts & 0x3FF if this.version in swizzableFormats else this.counts >> 8), #C.Int8ul,#12 "mipCount" / C.Computed(lambda this: (this.counts >> 12 if this.version in swizzableFormats else this .counts & 0xFF)), #C.Int8ul,#4 "format" / C.Int32ul, "swizzleControl" / C.Int32sl, #C.Const(1,C.Int32ul), "cubemapMarker" / C.Int32ul, "unkn04" / C.Int8ul[2], "NULL0" / C.Const(0, C.Int16ul), "swizzleData" / C.If( lambda ctx: ctx.version in swizzableFormats, C.IfThenElse(lambda ctx: ctx.version in swizzledFormats, swizzleData, swizzleNull)), "textureHeaders" / mipData[C.this.mipCount][C.this.imageCount], "start" / C.Tell, "data" / C.GreedyBytes, ) TEXHeader = _TEXHeader #.compile() def expandBlockData(texhead, swizzle): texs = [] for image in texhead.textureHeaders: mips = [] for mipsTex in image: start = mipsTex.mipOffset - texhead.start end = start + (mipsTex.compressedSize if swizzle else mipsTex.uncompressedSize)
"frameDataOffset" / C.Int64sl, "frameCount" / C.Int64sl, "frameIndexOffset" / C.Int64sl, "frameIndexCount" / C.Int64sl, "dataOffset" / C.Int64sl, "mapCount" / C.Int64sl, "unkn32_0" / C.Float32l, "unkn32_1" / C.Float32l, "unkn3" / C.Int64sl, "frameData" / C.Pointer(C.this.frameDataOffset, Primary[C.this.frameCount]), "frameIndex" / C.Pointer(C.this.frameIndexOffset, C.Int32sl[C.this.frameIndexCount]), #32-Padded to next "mapIndices" / C.IfThenElse(C.this.frameCount != 0, C.Pointer(C.this.dataOffset, C.Int32sl[4]), C.Int32sl[0]), ) def defaultMapIndices(l): return l + [0] * (4 - len(l)) StringHead = C.Struct("blank" / C.Int64sl, "stringOffset" / C.Int64sl, "type" / C.Int32sl) StringBody = C.Struct("string" / C.CString("utf-8"), ) StringData = C.Struct( "blank" / C.Int64sl, "stringOffset" / C.Int64sl, "type" / C.Int32sl, "string" / C.Pointer(C.this.stringOffset, C.CString("utf-8")), "padding" / C.Optional(C.Const(0, C.Int32sl)))
"src_addressing_mode" / addressing_mode_t, )) mac_header_t = ct.Struct( "fcf" / fcf_t, "seqnum" / ct.Hex(ct.Int8ul), "dst_addr" / ct.Switch( lambda ctx: int(ctx.fcf.dst_addressing_mode), { int(addressing_mode_t.SHORT): short_addr_t, int(addressing_mode_t.LONG): long_addr_t, }), "src_addr" / ct.If( lambda ctx: is_address_present(ctx.fcf.src_addressing_mode), ct.Struct( "pan_id" / ct.IfThenElse( lambda ctx: ctx._.fcf.pan_id_comp and is_address_present( ctx._.fcf.dst_addressing_mode), ct.Computed(ct.this._.dst_addr.pan_id), ct.Hex(ct.Int16ul)), "addr" / ct.Switch( lambda ctx: int(ctx._.fcf.src_addressing_mode), { int(addressing_mode_t.SHORT): ct.Hex(ct.Int16ul), int(addressing_mode_t.LONG): ct.Hex(ct.Int64ul) }))), ) mpdu_t = ct.Struct( "mac" / mac_header_t, "pdu_offset" / ct.Tell, "pdu" / ct.ExprAdapter(ct.HexDump(ct.GreedyBytes), ct.obj_[:-2], ct.obj_ + "AA"), ct.Seek(-2, ct.io.SEEK_CUR), "fcs_offset" / ct.Tell, ct.If(ct.this.pdu_offset > ct.this.fcs_offset, ct.Error), "fcs" / ct.Hex(ct.Int16ul))
def struct(cls): return construct.Struct( "signature" / construct.Const(b"vf"), "version" / _LMArchiveVersionValidator(construct.Int32ul), "count" / construct.Int32ul, "filenames" / construct.Array( construct.this.count, construct.IfThenElse( construct.this.version >= 100, construct.Prefixed( construct.Int32ul, construct.Transformed( construct.GreedyString('cp932'), LMObfuscator().transform_bytes, None, LMObfuscator().transform_bytes, None, ), ), construct.PascalString(construct.Int32ul, 'cp932'), )), "offsets" / construct.Array( construct.this.count + 1, construct.Struct( "offset_low" / construct.IfThenElse( construct.this._.version >= 100, construct.Transformed( construct.Int32ul, LMObfuscator().transform_int, 4, LMObfuscator().transform_int, 4, ), construct.Int32ul, ), # offset_high always 0 if ver < 101 "offset_high" / construct.IfThenElse( construct.this._.version >= 101, construct.Transformed( construct.Int32ul, LMObfuscator().transform_int, 4, LMObfuscator().transform_int_high, 4, ), construct.Int32ul, ), ), ), "compress_types" / construct.Array(construct.this.count, construct.Enum(construct.Byte, LMCompressType)), "unk1s" / construct.Array( construct.this.count, # construct.Transformed( # construct.Int32ul, # LMObfuscator().transform_int, # 4, # LMObfuscator().transform_int, # 4, # ), construct.Int32ul, ), "checksums" / construct.Array( construct.this.count, construct.Int32ul, ), "encrypt_flags" / construct.Array( construct.this.count, construct.Byte, ), )
class FlacReader: FRAME_HEADER = construct.Struct( 'frame_header', construct.Bits('sync', 14), construct.Bits('reserved', 2), construct.Bits('block_size', 4), construct.Bits('sample_rate', 4), construct.Bits('channel_assignment', 4), construct.Bits('bits_per_sample', 3), construct.Padding(1), construct.IfThenElse( 'total_channels', lambda ctx1: ctx1['channel_assignment'] <= 7, construct.Value('c', lambda ctx2: ctx2['channel_assignment'] + 1), construct.Value('c', lambda ctx3: 2)), UTF8('frame_number'), construct.IfThenElse( 'extended_block_size', lambda ctx1: ctx1['block_size'] == 6, construct.Bits('b', 8), construct.If(lambda ctx2: ctx2['block_size'] == 7, construct.Bits('b', 16))), construct.IfThenElse( 'extended_sample_rate', lambda ctx1: ctx1['sample_rate'] == 12, construct.Bits('s', 8), construct.If(lambda ctx2: ctx2['sample_rate'] in (13, 14), construct.Bits('s', 16))), construct.Bits('crc8', 8)) UNARY = construct.Struct( 'unary', construct.RepeatUntil(lambda obj, ctx: obj == '\x01', construct.Field('bytes', 1)), construct.Value('value', lambda ctx: len(ctx['bytes']) - 1)) SUBFRAME_HEADER = construct.Struct( 'subframe_header', construct.Padding(1), construct.Bits('subframe_type', 6), construct.Flag('has_wasted_bits_per_sample'), construct.IfThenElse('wasted_bits_per_sample', lambda ctx: ctx['has_wasted_bits_per_sample'], PlusOne(Unary('value')), construct.Value('value', lambda ctx2: 0))) GET_BLOCKSIZE_FROM_STREAMINFO = -1 GET_8BIT_BLOCKSIZE_FROM_END_OF_HEADER = -2 GET_16BIT_BLOCKSIZE_FROM_END_OF_HEADER = -3 BLOCK_SIZE = (GET_BLOCKSIZE_FROM_STREAMINFO, 192, 576, 1152, 2304, 4608, GET_8BIT_BLOCKSIZE_FROM_END_OF_HEADER, GET_16BIT_BLOCKSIZE_FROM_END_OF_HEADER, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768) GET_SAMPLE_SIZE_FROM_STREAMINFO = -1 SAMPLE_SIZE = (GET_SAMPLE_SIZE_FROM_STREAMINFO, 8, 12, None, 16, 20, 24, None) def FIXED0(subframe, residual, i): subframe.insert(i, residual[i]) def FIXED1(subframe, residual, i): subframe.insert(i, subframe[i - 1] + residual[i]) def FIXED2(subframe, residual, i): subframe.insert(i, ((2 * subframe[i - 1]) - subframe[i - 2] + \ residual[i])) def FIXED3(subframe, residual, i): subframe.insert(i, ((3 * subframe[i - 1]) - (3 * subframe[i - 2]) + \ subframe[i - 3] + residual[i])) def FIXED4(subframe, residual, i): subframe.insert(i, ((4 * subframe[i - 1]) - (6 * subframe[i - 2]) + \ (4 * subframe[i - 3]) - subframe[i - 4] + residual[i])) #iterates over all of the channels, in order def MERGE_INDEPENDENT(channel_list): channel_data = [iter(c) for c in channel_list] while (True): for channel in channel_data: yield channel.next() def MERGE_LEFT(channel_list): channel_left = iter(channel_list[0]) channel_side = iter(channel_list[1]) while (True): left = channel_left.next() side = channel_side.next() yield left yield left - side def MERGE_RIGHT(channel_list): channel_side = iter(channel_list[0]) channel_right = iter(channel_list[1]) while (True): side = channel_side.next() right = channel_right.next() yield side + right yield right def MERGE_MID(channel_list): channel_mid = iter(channel_list[0]) channel_side = iter(channel_list[1]) while (True): mid = channel_mid.next() side = channel_side.next() mid = mid << 1 mid |= (side & 0x1) yield (mid + side) >> 1 yield (mid - side) >> 1 CHANNEL_FUNCTIONS = (MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_LEFT, MERGE_RIGHT, MERGE_MID) FIXED_FUNCTIONS = (FIXED0, FIXED1, FIXED2, FIXED3, FIXED4) def __init__(self, flac_stream): self.stream = BufferedStream(flac_stream) self.streaminfo = None self.bitstream = None #ensure the file starts with 'fLaC' self.read_stream_marker() #initialize self.bitstream self.begin_bitstream() #find self.streaminfo in case we need it self.read_metadata_blocks() def close(self): if (self.bitstream != None): self.bitstream.close() else: self.stream.close() def read_stream_marker(self): if (self.stream.read(4) != 'fLaC'): raise FlacStreamException('invalid stream marker') def read_metadata_blocks(self): block = audiotools.FlacAudio.METADATA_BLOCK_HEADER.parse_stream( self.stream) while (block.last_block == 0): if (block.block_type == 0): self.streaminfo = audiotools.FlacAudio.STREAMINFO.parse_stream( self.stream) else: self.stream.seek(block.block_length, 1) block = audiotools.FlacAudio.METADATA_BLOCK_HEADER.parse_stream( self.stream) self.stream.seek(block.block_length, 1) def begin_bitstream(self): import bitstream #self.bitstream = construct.BitStreamReader(self.stream) self.bitstream = bitstream.BitStreamReader(self.stream) def read_frame(self): self.stream.reset_buffer() try: header = FlacReader.FRAME_HEADER.parse_stream(self.bitstream) except construct.core.FieldError: return "" if (header.sync != 0x3FFE): raise FlacStreamException('invalid sync') if (crc8(self.stream.getvalue()[0:-1]) != header.crc8): raise FlacStreamException('crc8 checksum failed') #block_size tells us how many samples we need from each subframe block_size = FlacReader.BLOCK_SIZE[header.block_size] if (block_size == self.GET_BLOCKSIZE_FROM_STREAMINFO): block_size = self.streaminfo.maximum_blocksize elif ((block_size == self.GET_8BIT_BLOCKSIZE_FROM_END_OF_HEADER) or (block_size == self.GET_16BIT_BLOCKSIZE_FROM_END_OF_HEADER)): block_size = header.extended_block_size + 1 #grab subframe data as 32-bit array objects subframe_data = [] for channel_number in xrange(header.total_channels): subframe_data.append( self.read_subframe(header, block_size, channel_number)) crc16sum = crc16(self.stream.getvalue()) #try to byte-align the stream if (len(self.bitstream.buffer) > 0): self.bitstream.read(len(self.bitstream.buffer)) if (crc16sum != construct.Bits('crc16', 16).parse_stream( self.bitstream)): raise FlacStreamException('crc16 checksum failed') #convert our list of subframe data arrays into #a string of sample data if (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == 16): merged_frames = array.array( 'h', FlacReader.CHANNEL_FUNCTIONS[header.channel_assignment]( subframe_data)) if (audiotools.BIG_ENDIAN): merged_frames.byteswap() return merged_frames.tostring() elif (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == 8): merged_frames = array.array( 'b', FlacReader.CHANNEL_FUNCTIONS[header.channel_assignment]( subframe_data)) return merged_frames.tostring() else: if (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == \ self.GET_SAMPLE_SIZE_FROM_STREAMINFO): bits_per_sample = self.streaminfo.bits_per_sample + 1 elif (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == None): raise FlacStreamException('invalid bits per sample') else: bits_per_sample = FlacReader.SAMPLE_SIZE[ header.bits_per_sample] stream = construct.GreedyRepeater( construct.BitStruct( 'bits', construct.Bits('value', bits_per_sample, swapped=True, signed=True))) return stream.build([ construct.Container(value=v) for v in FlacReader.CHANNEL_FUNCTIONS[ header.channel_assignment](subframe_data) ]) def read_subframe(self, frame_header, block_size, channel_number): subframe_header = \ FlacReader.SUBFRAME_HEADER.parse_stream(self.bitstream) #figure out the bits-per-sample of this subframe if ((frame_header.channel_assignment == 8) and (channel_number == 1)): #if channel is stored as left+difference #and this is the difference, add 1 bit bits_per_sample = FlacReader.SAMPLE_SIZE[ frame_header.bits_per_sample] + 1 elif ((frame_header.channel_assignment == 9) and (channel_number == 0)): #if channel is stored as difference+right #and this is the difference, add 1 bit bits_per_sample = FlacReader.SAMPLE_SIZE[ frame_header.bits_per_sample] + 1 elif ((frame_header.channel_assignment == 10) and (channel_number == 1)): #if channel is stored as average+difference #and this is the difference, add 1 bit bits_per_sample = FlacReader.SAMPLE_SIZE[ frame_header.bits_per_sample] + 1 else: #otherwise, use the number from the frame header bits_per_sample = FlacReader.SAMPLE_SIZE[ frame_header.bits_per_sample] if (subframe_header.has_wasted_bits_per_sample): bits_per_sample -= subframe_header.wasted_bits_per_sample if (subframe_header.subframe_type == 0): subframe = self.read_subframe_constant(block_size, bits_per_sample) elif (subframe_header.subframe_type == 1): subframe = self.read_subframe_verbatim(block_size, bits_per_sample) elif ((subframe_header.subframe_type & 0x38) == 0x08): subframe = self.read_subframe_fixed( subframe_header.subframe_type & 0x07, block_size, bits_per_sample) elif ((subframe_header.subframe_type & 0x20) == 0x20): subframe = self.read_subframe_lpc( (subframe_header.subframe_type & 0x1F) + 1, block_size, bits_per_sample) else: raise FlacStreamException('invalid subframe type') if (subframe_header.has_wasted_bits_per_sample): return array.array('i', [ i << subframe_header.wasted_bits_per_sample for i in subframe ]) else: return subframe def read_subframe_constant(self, block_size, bits_per_sample): sample = construct.Bits('b', bits_per_sample).parse_stream(self.bitstream) subframe = array.array('i', [sample] * block_size) return subframe def read_subframe_verbatim(self, block_size, bits_per_sample): return array.array( 'i', construct.StrictRepeater( block_size, construct.Bits("samples", bits_per_sample, signed=True)).parse_stream(self.bitstream)) def read_subframe_fixed(self, order, block_size, bits_per_sample): samples = construct.StrictRepeater( order, construct.Bits("warm_up_samples", bits_per_sample, signed=True)) subframe = array.array('i', samples.parse_stream(self.bitstream)) residual = self.read_residual(block_size, order) fixed_func = self.FIXED_FUNCTIONS[order] for i in xrange(len(subframe), block_size): fixed_func(subframe, residual, i) return subframe def read_subframe_lpc(self, order, block_size, bits_per_sample): samples = construct.StrictRepeater( order, construct.Bits("warm_up_samples", bits_per_sample, signed=True)) subframe = array.array('i', samples.parse_stream(self.bitstream)) lpc_precision = construct.Bits('lpc_precision', 4).parse_stream( self.bitstream) + 1 lpc_shift = construct.Bits('lpc_shift', 5).parse_stream(self.bitstream) coefficients = array.array( 'i', construct.StrictRepeater( order, construct.Bits('coefficients', lpc_precision, signed=True)).parse_stream(self.bitstream)) residual = self.read_residual(block_size, order) for i in xrange(len(subframe), block_size): subframe.insert(i, (sum( [coefficients[j] * subframe[i - j - 1] for j in xrange(0,len(coefficients))]) >> lpc_shift) + \ residual[i]) return subframe def read_residual(self, block_size, predictor_order): rice = array.array('i') #add some dummy rice so that the Rice index matches #that of the rest of the subframe for i in xrange(predictor_order): rice.append(0) coding_method = self.bitstream.read(2) if (coding_method == '\x00\x00'): rice2 = False elif (coding_method == '\x00\x01'): rice2 = True else: raise FlacStreamException('invalid residual coding method') partition_order = construct.Bits('partition_order', 4).parse_stream(self.bitstream) if (partition_order > 0): total_samples = ((block_size / 2**partition_order) - predictor_order) rice.extend(self.read_encoded_rice(total_samples, rice2)) for i in xrange(1, 2**partition_order): total_samples = (block_size / 2**partition_order) rice.extend(self.read_encoded_rice(total_samples, rice2)) else: rice.extend( self.read_encoded_rice(block_size - predictor_order, rice2)) return rice def read_encoded_rice(self, total_samples, rice2=False): bin_to_int = construct.lib.binary.bin_to_int samples = array.array('i') if (not rice2): rice_parameter = construct.Bits('rice_parameter', 4).parse_stream(self.bitstream) else: rice_parameter = construct.Bits('rice_parameter', 5).parse_stream(self.bitstream) if (rice_parameter != 0xF): #a Rice encoded residual for x in xrange(total_samples): #count the number of 0 bits before the next 1 bit #(unary encoding) #to find our most significant bits msb = 0 s = self.bitstream.read(1) while (s != '\x01'): msb += 1 s = self.bitstream.read(1) #grab the proper number of least significant bits lsb = bin_to_int(self.bitstream.read(rice_parameter)) #combine msb and lsb to get the Rice-encoded value value = (msb << rice_parameter) | lsb if ((value & 0x1) == 0x1): #negative samples.append(-(value >> 1) - 1) else: #positive samples.append(value >> 1) else: #unencoded residual bits_per_sample = construct.Bits('escape_code', 5).parse_stream(self.bitstream) sample = construct.Bits("sample", bits_per_sample, signed=True) for x in xrange(total_samples): samples.append(sample.parse_stream(self.bitstream)) return samples
"frame_width" / construct.Int16ul, # + 0x14 "frame_height" / construct.Int16ul, # + 0x16 "end_gdv_header" / construct.Tell, ) # # GDV file # gdv_file = construct.Struct( "gdv_header" / gdv_header, # 768-byte palette if the video is palettized (image type indicates 8 bits/pixel) "palette" / construct.If(lambda ctx: ctx.gdv_header.image_type.video_depth == "PIXEL_8_BITS", construct.OnDemandPointer(lambda ctx: ctx.gdv_header.end_gdv_header, construct.Array(0x300, "palette" / construct.Int8ul))), "start_chunks" / construct.IfThenElse(lambda ctx: ctx.gdv_header.image_type.video_depth == "PIXEL_8_BITS", construct.Computed(lambda ctx: ctx.gdv_header.end_gdv_header + 0x300), construct.Computed(lambda ctx: ctx.gdv_header.end_gdv_header)), "chunks" / construct.OnDemandPointer(lambda ctx: ctx.start_chunks, construct.Array(lambda ctx: ctx.gdv_header.nb_frames, gdv_chunk) ), ) ########################## # # Video decoder # ########################## # # 32-bit bit queue & stream reader #
def __init__(self, websocket, mode): self._websocket = websocket self._mode = mode self._inbound_packet = construct.Struct( "type" / construct.Enum( construct.Int8ul, setup=0, killed=1, kill=2, remove=3, sync=4, club_collision=5, wall_collision=6, set_leaderboard=7, set_target_dim=8 ), "payload" / construct.Switch( construct.this.type, { "setup": construct.Struct( "server_version" / construct.CString(encoding="utf8"), construct.Check(lambda ctx: ctx.server_version == CLIENT_VERSION), "syncer_value" / construct.Int32ul, "game_mode" / construct.Mapping( construct.Int8ul, { 0: Mode.ffa, 1: Mode.tdm }, dict() # This packet is only received ), "setup_value" / construct.Int32ul ), "killed": construct.Pass, "kill": construct.Struct("stamp" / construct.Int32ul), "remove": construct.Struct( "enqueue_param" / construct.Int32ul, # TODO: Figure out purpose "player_id" / construct.Int32ul ), "sync": construct.Struct( "timestamp" / construct.Int32ul, "remove_count" / construct.Int32ul, "removal_array" / construct.Array( construct.this.remove_count, construct.Int32ul ), "sync_count" / construct.Int32ul, "sync_array" / construct.Array( construct.this.sync_count, construct.Struct( "player_id" / construct.Int32ul, "player_state" / physical_state, "mace_state" / physical_state, "mace_radius" / construct.Float32l ) ) ), "club_collision": construct.Struct( "enqueue_param" / construct.Int32ul, # TODO: Figure out purpose "p" / vector2d, # TODO: Figure out purpose "i" / construct.Float32l, # TODO: Figure out purpose "first_id" / construct.Int32ul, "first_state" / physical_state, "second_id" / construct.Int32ul, "second_state" / physical_state ), "wall_collision": construct.Struct( "enqueue_param" / construct.Int32ul, # TODO: Figure out purpose "p" / vector2d, # TODO: Figure out purpose "i" / construct.Float32l, # TODO: Figure out purpose "player_id" / construct.Int32ul, "player_state" / physical_state, "mace_radius" / construct.Float32l ), "set_leaderboard": construct.Struct( "player_count" / construct.Int32ul, "total" / construct.Int32ul, # TODO: Figure out purpose, construct.IfThenElse( lambda ctx: self._mode == Mode.ffa, construct.Struct( # FFA "count" / construct.Int8ul, "first_entry_id" / construct.Int32ul, "leaderboard" / construct.Array( construct.this.count, construct.Struct( "name" / construct.CString(encoding="utf8"), "score" / construct.Int32ul, ) ), "king_name" / construct.CString(encoding="utf8"), "king_score" / construct.Int32ul, "place" / construct.Int32ul, "score" / construct.Int32ul ), construct.Array( 3, construct.Struct( "id" / construct.Int8ul, "score" / construct.Int32ul, "count" / construct.Int32ul ) ) ) ), "set_target_dim": construct.Struct("target_dim" / vector2d) } ) ) self._outbound_packet = construct.Struct( "type" / construct.Enum( construct.Int8ul, play=0, direction=1, move_up=2, move_down=3, move_left=4, move_right=5, stop_move_up=6, stop_move_down=7, stop_move_left=8, stop_move_right=9 ), "payload" / construct.Switch( construct.this.type, { "play": construct.Pass # TODO: Implement the rest of the packets } ) )