def _unpack_multipacket_header(payload_offset, packet): if payload_offset == 9: # GoldSrc pkt_byte, = _unpack_from('<B', packet, 8) return pkt_byte >> 2, pkt_byte & 0xF, False # idx, total, compressed elif payload_offset in (10, 12, 18): # Source pkt_id, num_pkts, pkt_idx, = _unpack_from('<LBB', packet, 4) return pkt_idx, num_pkts, (pkt_id & 0x80000000) != 0 # idx, total, compressed else: raise RuntimeError("Unexpected payload_offset - %d" % payload_offset)
def random(self): """Get the next random number in the range [0.0, 1.0).""" value = _unpack_from('Q', self._cache, self._pos)[0] * FLOAT_CONV_CONSTANT self._pos += 8 if self._pos >= self._cache_size: self.refresh() return value
def load_record_from_buffer(self, memview, start): start = super(_BsaHashedRecord, self).load_record_from_buffer(memview, start) for f, a in izip(self.__class__.formats, self.__class__.__slots__): setattr(self, a, _unpack_from(f[0], memview, start)[0]) start += f[1] return start
def a2s_rules(server_addr, timeout=2, challenge=0, binary=False): """Get rules from server :param server_addr: (ip, port) for the server :type server_addr: tuple :param timeout: (optional) timeout in seconds :type timeout: float :param challenge: (optional) challenge number :type challenge: int :param binary: (optional) return rules as raw bytes :type binary: bool :raises: :class:`RuntimeError`, :class:`socket.timeout` :returns: a list of rules :rtype: :class:`dict` """ ss = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ss.connect(server_addr) ss.settimeout(timeout) # request challenge number if challenge in (-1, 0): ss.send(_pack('<lci', -1, b'V', challenge)) try: _, header, challenge = _unpack_from('<lcl', ss.recv(512)) except: ss.close() raise if header != b'A': raise RuntimeError("Unexpected challenge response") # request player info ss.send(_pack('<lci', -1, b'V', challenge)) try: data = StructReader(_handle_a2s_response(ss)) finally: ss.close() header, num_rules = data.unpack('<4xcH') if header != b'E': raise RuntimeError("Invalid response header - %s" % repr(header)) rules = {} while len(rules) != num_rules: name = data.read_cstring(binary=binary) value = data.read_cstring(binary=binary) if not binary: if _re_match(r'^\-?[0-9]+$', value): value = int(value) elif _re_match(r'^\-?[0-9]+\.[0-9]+$', value): value = float(value) rules[name] = value return rules
def a2s_rules(server_addr, timeout=2, challenge=0): """Get rules from server :param server_addr: (ip, port) for the server :type server_addr: tuple :param timeout: (optional) timeout in seconds :type timeout: float :param challenge: (optional) challenge number :type challenge: int :raises: :class:`RuntimeError`, :class:`socket.timeout` :returns: a list of players :rtype: :class:`list` """ ss = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ss.connect(server_addr) ss.settimeout(timeout) # request challenge number if challenge in (-1, 0): ss.send(_pack('<lci', -1, b'V', challenge)) try: _, header, challenge = _unpack_from('<lcl', ss.recv(512)) except: ss.close() raise if header != b'A': raise RuntimeError("Unexpected challenge response") # request player info ss.send(_pack('<lci', -1, b'V', challenge)) try: data = StructReader(_handle_a2s_response(ss)) finally: ss.close() header, num_rules = data.unpack('<4xcH') if header != b'E': raise RuntimeError("Invalid reponse header - %s" % repr(header)) rules = {} while len(rules) != num_rules: name = data.read_cstring() value = data.read_cstring() if _re_match(r'^\-?[0-9]+$', value): value = int(value) elif _re_match(r'^\-?[0-9]+\.[0-9]+$', value): value = float(value) rules[name] = value return rules
def unpack(self, format_text): """Unpack bytes using struct modules format :param format_text: struct's module format :type format_text: :class:`str` :return data: result from :func:`struct.unpack_from` :rtype: :class:`tuple` """ data = _unpack_from(format_text, self.data, self.offset) self.offset += _calcsize(format_text) return data
def _handle_a2s_response(sock): packet = sock.recv(2048) header, = _unpack_from('<l', packet) if header == -1: # single packet response return packet elif header == -2: # multi packet response sock.settimeout(0.3) return _handle_a2s_multi_packet_response(sock, packet) else: raise RuntimeError("Invalid reponse header - %d" % header)
def _handle_a2s_multi_packet_response(sock, packet): packets, payload_offset = [packet], -1 # locate first packet and handle out of order packets while payload_offset == -1: # locate payload offset in uncompressed packet payload_offset = packet.find(b'\xff\xff\xff\xff', 0, 18) # locate payload offset in compressed packet if payload_offset == -1: payload_offset = packet.find(b'BZh', 0, 21) # if we still haven't found the offset receive the next packet if payload_offset == -1: packet = sock.recv(2048) packets.append(packet) # read header pkt_idx, num_pkts, compressed = _unpack_multipacket_header( payload_offset, packet) if pkt_idx != 0: raise RuntimeError("Unexpected first packet index") # recv any remaining packets while len(packets) < num_pkts: packets.append(sock.recv(2048)) # ensure packets are in correct order packets = sorted(map( lambda pkt: (_unpack_multipacket_header(payload_offset, pkt)[0], pkt), packets, ), key=lambda x: x[0]) # reconstruct full response data = b''.join(map(lambda x: x[1][payload_offset:], packets)) # decompress response if needed if compressed: size, checksum = _unpack_from('<ll', packet, 10) data = _bz2_decompress(data) if len(data) != size: raise RuntimeError("Response size mismatch - %d %d" % (len(data), size)) if checksum != crc32(data): raise RuntimeError("Response checksum mismatch - %d %d" % (checksum, crc32(data))) return data
def _handle_a2s_multi_packet_response(sock, packet): packets, payload_offset = [packet], -1 # locate first packet and handle out of order packets while payload_offset == -1: # locate payload offset in uncompressed packet payload_offset = packet.find(b'\xff\xff\xff\xff', 0, 18) # locate payload offset in compressed packet if payload_offset == -1: payload_offset = packet.find(b'BZh', 0, 21) # if we still haven't found the offset receive the next packet if payload_offset == -1: packet = sock.recv(2048) packets.append(packet) # read header pkt_idx, num_pkts, compressed = _unpack_multipacket_header(payload_offset, packet) if pkt_idx != 0: raise RuntimeError("Unexpected first packet index") # recv any remaining packets while len(packets) < num_pkts: packets.append(sock.recv(2048)) # ensure packets are in correct order packets = sorted(map(lambda pkt: (_unpack_multipacket_header(payload_offset, pkt)[0], pkt), packets, ), key=lambda x: x[0]) # reconstruct full response data = b''.join(map(lambda x: x[1][payload_offset:], packets)) # decompress response if needed if compressed: size, checksum = _unpack_from('<ll', packet, 10) data = _bz2_decompress(data) if len(data) != size: raise RuntimeError("Response size mismatch - %d %d" % (len(data), size)) if checksum != crc32(data): raise RuntimeError("Response checksum mismatch - %d %d" % (checksum, crc32(data))) return data
def _load_bsa_light(self): my_header = self.bsa_header # type: Ba2Header with open(u'%s' % self.abs_path, u'rb') as bsa_file: # load the header from input stream my_header.load_header(bsa_file, self.bsa_name) # load the file names block bsa_file.seek(my_header.ba2_name_table_offset) file_names_block = memoryview(bsa_file.read()) # close the file _filenames = [] for index in xrange(my_header.ba2_num_files): name_size = _unpack_from(u'H', file_names_block)[0] filename = _decode_path( file_names_block[2:name_size + 2].tobytes(), self.bsa_name) _filenames.append(filename) file_names_block = file_names_block[name_size + 2:] self._filenames = _filenames
def _load_bsa(self): with open(u'%s' % self.abs_path, u'rb') as bsa_file: # load the header from input stream my_header = self.bsa_header # type: Ba2Header my_header.load_header(bsa_file, self.bsa_name) # load the folder records from input stream if my_header.ba2_files_type == b'GNRL': file_record_type = Ba2FileRecordGeneral else: file_record_type = Ba2FileRecordTexture file_records = [] for __ in xrange(my_header.ba2_num_files): rec = file_record_type() rec.load_record(bsa_file) file_records.append(rec) # load the file names block bsa_file.seek(my_header.ba2_name_table_offset) file_names_block = memoryview(bsa_file.read()) # close the file current_folder_name = current_folder = None for index in xrange(my_header.ba2_num_files): name_size = _unpack_from(u'H', file_names_block)[0] filename = _decode_path( file_names_block[2:name_size + 2].tobytes(), self.bsa_name) file_names_block = file_names_block[name_size + 2:] folder_dex = filename.rfind(u'\\') if folder_dex == -1: folder_name = u'' else: folder_name = filename[:folder_dex] if current_folder_name != folder_name: current_folder = self.bsa_folders.setdefault(folder_name, Ba2Folder()) current_folder_name = folder_name current_folder.folder_assets[filename[folder_dex + 1:]] = \ file_records[index]
def unpack_int64(packed_bytes: bytes, offset = 0): return _unpack_from("=q", packed_bytes, offset)[0] def unpack_float(packed_bytes: bytes, offset = 0): return _unpack_from("=f", packed_bytes, offset)[0]
def load_record_from_buffer(self, memview, start): f, f_size = _HashedRecord.formats[0] self.record_hash, = _unpack_from(f, memview, start) return start + f_size
def unpack_uint16_le(packed_bytes: bytes, offset = 0): return _unpack_from("<H", packed_bytes, offset)[0] def unpack_uint32_le(packed_bytes: bytes, offset = 0): return _unpack_from("<I", packed_bytes, offset)[0]
def unpack_int16_be(packed_bytes: bytes, offset = 0): return _unpack_from(">h", packed_bytes, offset)[0] def unpack_int32_be(packed_bytes: bytes, offset = 0): return _unpack_from(">i", packed_bytes, offset)[0]
def unpack_uint64(packed_bytes: bytes, offset = 0): return _unpack_from("=Q", packed_bytes, offset)[0] def unpack_int8(packed_bytes: bytes, offset = 0): return _unpack_from("=b", packed_bytes, offset)[0]
def unpack_from(fmt, view, _unpack_from=_unpack_from): # noqa return _unpack_from(fmt, view.tobytes()) # <- memoryview
def unpack_float_le(packed_bytes: bytes, offset = 0): return _unpack_from("<f", packed_bytes, offset)[0] def unpack_double_le(packed_bytes: bytes, offset = 0): return _unpack_from("<d", packed_bytes, offset)[0]
def a2s_players(server_addr, timeout=2, challenge=0): """Get list of players and their info :param server_addr: (ip, port) for the server :type server_addr: tuple :param timeout: (optional) timeout in seconds :type timeout: float :param challenge: (optional) challenge number :type challenge: int :raises: :class:`RuntimeError`, :class:`socket.timeout` :returns: a list of players :rtype: :class:`list` """ ss = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ss.connect(server_addr) ss.settimeout(timeout) # request challenge number header = None if challenge in (-1, 0): ss.send(_pack('<lci', -1, b'U', challenge)) try: data = ss.recv(512) _, header, challenge = _unpack_from('<lcl', data) except: ss.close() raise if header not in b'AD': # work around for CSGO sending only max players raise RuntimeError("Unexpected challenge response - %s" % repr(header)) # request player info if header == b'D': # work around for CSGO sending only max players data = StructReader(data) else: ss.send(_pack('<lci', -1, b'U', challenge)) try: data = StructReader(_handle_a2s_response(ss)) finally: ss.close() header, num_players = data.unpack('<4xcB') if header != b'D': raise RuntimeError("Invalid reponse header - %s" % repr(header)) players = [] while len(players) < num_players: player = dict() player['index'] = data.unpack('<B')[0] player['name'] = data.read_cstring() player['score'], player['duration'] = data.unpack('<lf') players.append(player) if data.rlen() / 8 == num_players: # assume the ship server for player in players: player['deaths'], player['money'] = data.unpack('<ll') return players
def unpack_double_le(packed_bytes: bytes, offset = 0): return _unpack_from("<d", packed_bytes, offset)[0] def unpack_uint8_be(packed_bytes: bytes, offset = 0): return _unpack_from(">B", packed_bytes, offset)[0]
def unpack_uint16(packed_bytes: bytes, offset = 0): return _unpack_from("=H", packed_bytes, offset)[0] def unpack_uint32(packed_bytes: bytes, offset = 0): return _unpack_from("=I", packed_bytes, offset)[0]
def unpack_uint16_be(packed_bytes: bytes, offset = 0): return _unpack_from(">H", packed_bytes, offset)[0] def unpack_uint32_be(packed_bytes: bytes, offset = 0): return _unpack_from(">I", packed_bytes, offset)[0]
def unpack_float_be(packed_bytes: bytes, offset = 0): return _unpack_from(">f", packed_bytes, offset)[0] def unpack_double_be(packed_bytes: bytes, offset = 0): return _unpack_from(">d", packed_bytes, offset)[0]
def unpack_int64_be(packed_bytes: bytes, offset = 0): return _unpack_from(">q", packed_bytes, offset)[0] def unpack_float_be(packed_bytes: bytes, offset = 0): return _unpack_from(">f", packed_bytes, offset)[0]
def unpack_int64_le(packed_bytes: bytes, offset = 0): return _unpack_from("<q", packed_bytes, offset)[0] def unpack_float_le(packed_bytes: bytes, offset = 0): return _unpack_from("<f", packed_bytes, offset)[0]
def unpack_int16_le(packed_bytes: bytes, offset = 0): return _unpack_from("<h", packed_bytes, offset)[0] def unpack_int32_le(packed_bytes: bytes, offset = 0): return _unpack_from("<i", packed_bytes, offset)[0]
def unpack_int16(packed_bytes: bytes, offset = 0): return _unpack_from("=h", packed_bytes, offset)[0] def unpack_int32(packed_bytes: bytes, offset = 0): return _unpack_from("=i", packed_bytes, offset)[0]
def unpack_uint64_le(packed_bytes: bytes, offset = 0): return _unpack_from("<Q", packed_bytes, offset)[0] def unpack_int8_le(packed_bytes: bytes, offset = 0): return _unpack_from("<b", packed_bytes, offset)[0]
def unpack_uint64_be(packed_bytes: bytes, offset = 0): return _unpack_from(">Q", packed_bytes, offset)[0] def unpack_int8_be(packed_bytes: bytes, offset = 0): return _unpack_from(">b", packed_bytes, offset)[0]