def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: game = Node.void('game') # Generic profile stuff game.add_child(Node.string('name', profile.get_str('name'))) game.add_child( Node.string('code', ID.format_extid(profile.get_int('extid')))) game.add_child(Node.u32('gamecoin_packet', profile.get_int('packet'))) game.add_child(Node.u32('gamecoin_block', profile.get_int('block'))) game.add_child(Node.u32('exp_point', profile.get_int('exp'))) game.add_child(Node.u32('m_user_cnt', profile.get_int('m_user_cnt'))) game_config = self.get_game_config() if game_config.get_bool('force_unlock_cards'): game.add_child(Node.bool_array('have_item', [True] * 512)) else: game.add_child( Node.bool_array( 'have_item', [x > 0 for x in profile.get_int_array('have_item', 512)])) if game_config.get_bool('force_unlock_songs'): game.add_child(Node.bool_array('have_note', [True] * 512)) else: game.add_child( Node.bool_array( 'have_note', [x > 0 for x in profile.get_int_array('have_note', 512)])) # Last played stuff lastdict = profile.get_dict('last') last = Node.void('last') game.add_child(last) last.set_attribute('music_id', str(lastdict.get_int('music_id'))) last.set_attribute('music_type', str(lastdict.get_int('music_type'))) last.set_attribute('sort_type', str(lastdict.get_int('sort_type'))) last.set_attribute('headphone', str(lastdict.get_int('headphone'))) last.set_attribute('hispeed', str(lastdict.get_int('hispeed'))) last.set_attribute('appeal_id', str(lastdict.get_int('appeal_id'))) last.set_attribute('frame0', str(lastdict.get_int('frame0'))) last.set_attribute('frame1', str(lastdict.get_int('frame1'))) last.set_attribute('frame2', str(lastdict.get_int('frame2'))) last.set_attribute('frame3', str(lastdict.get_int('frame3'))) last.set_attribute('frame4', str(lastdict.get_int('frame4'))) return game
def verify_game_save(self, location: str, refid: str, packet: int, block: int, exp: int) -> None: call = self.call_node() game = Node.void('game') call.add_child(game) game.set_attribute('method', 'save') game.set_attribute('refid', refid) game.set_attribute('locid', location) game.set_attribute('ver', '0') game.add_child(Node.u8('headphone', 0)) game.add_child(Node.u8('hispeed', 16)) game.add_child(Node.u16('appeal_id', 19)) game.add_child(Node.u16('frame0', 0)) game.add_child(Node.u16('frame1', 0)) game.add_child(Node.u16('frame2', 0)) game.add_child(Node.u16('frame3', 0)) game.add_child(Node.u16('frame4', 0)) last = Node.void('last') game.add_child(last) last.set_attribute('music_type', '1') last.set_attribute('music_id', '29') last.set_attribute('sort_type', '4') game.add_child(Node.u32('earned_gamecoin_packet', packet)) game.add_child(Node.u32('earned_gamecoin_block', block)) game.add_child(Node.u32('gain_exp', exp)) game.add_child(Node.u32('m_user_cnt', 0)) game.add_child(Node.bool_array('have_item', [False] * 512)) game.add_child(Node.bool_array('have_note', [False] * 512)) tracking = Node.void('tracking') game.add_child(tracking) m0 = Node.void('m0') tracking.add_child(m0) m0.add_child(Node.u8('type', 2)) m0.add_child(Node.u32('id', 5)) m0.add_child(Node.u32('score', 774566)) tracking.add_child(Node.time('p_start', Time.now() - 300)) tracking.add_child(Node.time('p_end', Time.now())) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/game")
def test_packet1(self) -> Node: root = Node.void('test') root.set_attribute('test', 'test string value') # Regular nodes root.add_child(Node.void('void_node')) root.add_child(Node.s8('s8_node', -1)) root.add_child(Node.u8('u8_node', 245)) root.add_child(Node.s16('s16_node', -8000)) root.add_child(Node.u16('u16_node', 65000)) root.add_child(Node.s32('s32_node', -2000000000)) root.add_child(Node.u32('u32_node', 4000000000)) root.add_child(Node.s64('s64_node', -1234567890000)) root.add_child(Node.u64('u64_node', 1234567890000)) root.add_child(Node.binary('bin_node', b'DEADBEEF')) root.add_child(Node.string('str_node', 'this is a string!')) root.add_child(Node.ipv4('ip4_node', '192.168.1.24')) root.add_child(Node.time('time_node', 1234567890)) root.add_child(Node.float('float_node', 2.5)) root.add_child(Node.fouru8('4u8_node', [0x20, 0x21, 0x22, 0x23])) root.add_child(Node.bool('bool_true_node', True)) root.add_child(Node.bool('bool_false_node', False)) # Array nodes root.add_child(Node.s8_array('s8_array_node', [-1, -2, 3, 4, -5])) root.add_child(Node.u8_array('u8_array_node', [245, 2, 0, 255, 1])) root.add_child(Node.s16_array('s16_array_node', [-8000, 8000])) root.add_child(Node.u16_array('u16_array_node', [65000, 1, 2, 65535])) root.add_child(Node.s32_array('s32_array_node', [-2000000000, -1])) root.add_child(Node.u32_array('u32_array_node', [4000000000, 0, 1, 2])) root.add_child( Node.s64_array('s64_array_node', [-1234567890000, -1, 1, 1337])) root.add_child( Node.u64_array('u64_array_node', [1234567890000, 123, 456, 7890])) root.add_child( Node.time_array('time_array_node', [1234567890, 98765432])) root.add_child( Node.float_array('float_array_node', [2.5, 0.0, 5.0, 20.5])) root.add_child( Node.bool_array('bool_array_node', [False, True, True, False])) # XML escaping escape = Node.string( 'escape_test', '\r\n<testing> & \'thing\' "thing" \r\nthing on new line\r\n ') escape.set_attribute('test', '<testing> & \'thing\' "thing" \r\n thing') root.add_child(escape) # Unicode unicode_node = Node.string('unicode', '今日は') unicode_node.set_attribute('unicode_attr', 'わたし') root.add_child(unicode_node) self.assertLoopback(root)
def parse_psmap(data: bytes, offset: str, rootname: str) -> Node: pe = pefile.PE(data=data, fast_load=True) root = Node.void(rootname) base = int(offset, 16) def virtual_to_physical(offset: int) -> int: for section in pe.sections: start = section.VirtualAddress + pe.OPTIONAL_HEADER.ImageBase end = start + section.SizeOfRawData if offset >= start and offset < end: return (offset - start) + section.PointerToRawData raise Exception( f'Couldn\'t find raw offset for virtual offset 0x{offset:08x}') if base >= pe.OPTIONAL_HEADER.ImageBase: # Assume this is virtual base = virtual_to_physical(base) def read_string(offset: int) -> str: # First, translate load offset in memory to disk offset offset = virtual_to_physical(offset) # Now, grab bytes until we're null-terminated bytestring = [] while data[offset] != 0: bytestring.append(data[offset]) offset = offset + 1 # Its shift-jis encoded, so decode it now return bytes(bytestring).decode('shift_jisx0213') # For recursing into nodes saved_root: List[Node] = [] saved_loc: List[int] = [] while True: chunk = data[base:(base + 16)] base = base + 16 (nodetype, mandatory, outoffset, width, nameptr, defaultptr) = struct.unpack('<BBHIII', chunk) if nodetype == 0xFF: # End of nodes, see if we should exit if len(saved_root) == 0: break else: root = saved_root.pop() oldbase = saved_loc.pop() if oldbase is not None: base = oldbase continue # Grab name, get rid of parse numbers name = read_string(nameptr) try: if name.index('#') >= 0: name = name[:name.index('#')] except ValueError: pass if nodetype == 0x00: raise Exception(f'Invalid node type 0x{nodetype:02x}') elif nodetype == 0x01: # This is a void node, so we should handle by recursing node = Node.void(name) root.add_child(node) # Recurse here saved_root.append(root) if defaultptr != 0: saved_loc.append(base) base = virtual_to_physical(defaultptr) else: saved_loc.append(None) root = node continue elif nodetype == 0x02 or nodetype == 0x43: if nodetype < 0x40: elements = int(width / 1) else: elements = width if elements > 1: node = Node.s8_array(name, [-1] * elements) else: node = Node.s8(name, -1) elif nodetype == 0x03 or nodetype == 0x44: if nodetype < 0x40: elements = int(width / 1) else: elements = width if elements > 1: node = Node.u8_array(name, [0] * elements) else: node = Node.u8(name, 0) elif nodetype == 0x04 or nodetype == 0x45: if nodetype < 0x40: elements = int(width / 2) else: elements = width if elements > 1: node = Node.s16_array(name, [-1] * elements) else: node = Node.s16(name, -1) elif nodetype == 0x05 or nodetype == 0x46: if nodetype < 0x40: elements = int(width / 2) else: elements = width if elements > 1: node = Node.u16_array(name, [0] * elements) else: node = Node.u16(name, 0) elif nodetype == 0x06 or nodetype == 0x47: if nodetype < 0x40: elements = int(width / 4) else: elements = width if elements > 1: node = Node.s32_array(name, [-1] * elements) else: node = Node.s32(name, -1) elif nodetype == 0x07 or nodetype == 0x48: if nodetype < 0x40: elements = int(width / 4) else: elements = width if elements > 1: node = Node.u32_array(name, [0] * elements) else: node = Node.u32(name, 0) elif nodetype == 0x08 or nodetype == 0x49: if nodetype < 0x40: elements = int(width / 8) else: elements = width if elements > 1: node = Node.s64_array(name, [-1] * elements) else: node = Node.s64(name, -1) elif nodetype == 0x09 or nodetype == 0x4A: if nodetype < 0x40: elements = int(width / 8) else: elements = width if elements > 1: node = Node.u64_array(name, [0] * elements) else: node = Node.u64(name, 0) elif nodetype == 0x0A: node = Node.string(name, '') elif nodetype == 0x0D: node = Node.float(name, 0.0) elif nodetype == 0x32 or nodetype == 0x6D: if nodetype < 0x40: elements = int(width / 1) else: elements = width if elements > 1: node = Node.bool_array(name, [False] * elements) else: node = Node.bool(name, False) elif nodetype == 0x2F: # Special case, this is an attribute if name[-1] != '@': raise Exception( f'Attribute name {name} expected to end with @') root.set_attribute(name[:-1], '') continue else: raise Exception(f'Unimplemented node type 0x{nodetype:02x}') # Append it root.add_child(node) return root
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: statistics = self.get_play_statistics(userid) game_config = self.get_game_config() achievements = self.data.local.user.get_achievements( self.game, self.version, userid) scores = self.data.remote.music.get_scores(self.game, self.version, userid) links = self.data.local.user.get_links(self.game, self.version, userid) root = Node.void('player') pdata = Node.void('pdata') root.add_child(pdata) base = Node.void('base') pdata.add_child(base) base.add_child(Node.s32('uid', profile.get_int('extid'))) base.add_child(Node.string('name', profile.get_str('name'))) base.add_child(Node.s16('icon_id', profile.get_int('icon'))) base.add_child(Node.s16('lv', profile.get_int('lvl'))) base.add_child(Node.s32('exp', profile.get_int('exp'))) base.add_child(Node.s16('mg', profile.get_int('mg'))) base.add_child(Node.s16('ap', profile.get_int('ap'))) base.add_child(Node.s32('pc', profile.get_int('pc'))) base.add_child(Node.s32('uattr', profile.get_int('uattr'))) last_play_date = statistics.get_int_array('last_play_date', 3) today_play_date = Time.todays_date() if (last_play_date[0] == today_play_date[0] and last_play_date[1] == today_play_date[1] and last_play_date[2] == today_play_date[2]): today_count = statistics.get_int('today_plays', 0) else: today_count = 0 con = Node.void('con') pdata.add_child(con) con.add_child(Node.s32('day', today_count)) con.add_child(Node.s32('cnt', statistics.get_int('total_plays'))) con.add_child(Node.s32('total_cnt', statistics.get_int('total_plays'))) con.add_child( Node.s32('last', statistics.get_int('last_play_timestamp'))) con.add_child(Node.s32('now', Time.now())) team = Node.void('team') pdata.add_child(team) team.add_child(Node.s32('id', profile.get_int('team_id', -1))) team.add_child(Node.string('name', profile.get_str('team_name', ''))) custom = Node.void('custom') customdict = profile.get_dict('custom') pdata.add_child(custom) custom.add_child(Node.u8('s_gls', customdict.get_int('s_gls'))) custom.add_child(Node.u8('bgm_m', customdict.get_int('bgm_m'))) custom.add_child(Node.u8('st_f', customdict.get_int('st_f'))) custom.add_child(Node.u8('st_bg', customdict.get_int('st_bg'))) custom.add_child(Node.u8('st_bg_b', customdict.get_int('st_bg_b'))) custom.add_child(Node.u8('eff_e', customdict.get_int('eff_e'))) custom.add_child(Node.u8('se_s', customdict.get_int('se_s'))) custom.add_child(Node.u8('se_s_v', customdict.get_int('se_s_v'))) custom.add_child( Node.s16('last_music_id', customdict.get_int('last_music_id'))) custom.add_child( Node.u8('last_note_grade', customdict.get_int('last_note_grade'))) custom.add_child(Node.u8('sort_type', customdict.get_int('sort_type'))) custom.add_child( Node.u8('narrowdown_type', customdict.get_int('narrowdown_type'))) custom.add_child( Node.bool('is_begginer', customdict.get_bool( 'is_begginer'))) # Yes, this is spelled right custom.add_child(Node.bool('is_tut', customdict.get_bool('is_tut'))) custom.add_child( Node.s16_array('symbol_chat_0', customdict.get_int_array('symbol_chat_0', 6))) custom.add_child( Node.s16_array('symbol_chat_1', customdict.get_int_array('symbol_chat_1', 6))) custom.add_child( Node.u8('gauge_style', customdict.get_int('gauge_style'))) custom.add_child(Node.u8('obj_shade', customdict.get_int('obj_shade'))) custom.add_child(Node.u8('obj_size', customdict.get_int('obj_size'))) custom.add_child( Node.s16_array('byword', customdict.get_int_array('byword', 2))) custom.add_child( Node.bool_array('is_auto_byword', customdict.get_bool_array('is_auto_byword', 2))) custom.add_child(Node.bool('is_tweet', customdict.get_bool('is_tweet'))) custom.add_child( Node.bool('is_link_twitter', customdict.get_bool('is_link_twitter'))) custom.add_child(Node.s16('mrec_type', customdict.get_int('mrec_type'))) custom.add_child( Node.s16('card_disp_type', customdict.get_int('card_disp_type'))) custom.add_child(Node.s16('tab_sel', customdict.get_int('tab_sel'))) custom.add_child( Node.s32_array('hidden_param', customdict.get_int_array('hidden_param', 20))) released = Node.void('released') pdata.add_child(released) for item in achievements: if item.type[:5] != 'item_': continue itemtype = int(item.type[5:]) if game_config.get_bool('force_unlock_songs') and itemtype == 0: # Don't echo unlocks when we're force unlocking, we'll do it later continue info = Node.void('info') released.add_child(info) info.add_child(Node.u8('type', itemtype)) info.add_child(Node.u16('id', item.id)) info.add_child(Node.u16('param', item.data.get_int('param'))) if game_config.get_bool('force_unlock_songs'): ids: Dict[int, int] = {} songs = self.data.local.music.get_all_songs( self.game, self.version) for song in songs: if song.id not in ids: ids[song.id] = 0 if song.data.get_int('difficulty') > 0: ids[song.id] = ids[song.id] | (1 << song.chart) for songid in ids: if ids[songid] == 0: continue info = Node.void('info') released.add_child(info) info.add_child(Node.u8('type', 0)) info.add_child(Node.u16('id', songid)) info.add_child(Node.u16('param', ids[songid])) # Scores record = Node.void('record') pdata.add_child(record) for score in scores: rec = Node.void('rec') record.add_child(rec) rec.add_child(Node.u16('mid', score.id)) rec.add_child(Node.u8('ng', score.chart)) rec.add_child( Node.s32( 'point', score.data.get_dict('stats').get_int('earned_points'))) rec.add_child(Node.s32('played_time', score.timestamp)) mrec_0 = Node.void('mrec_0') rec.add_child(mrec_0) mrec_0.add_child( Node.s32('win', score.data.get_dict('stats').get_int('win'))) mrec_0.add_child( Node.s32('lose', score.data.get_dict('stats').get_int('lose'))) mrec_0.add_child( Node.s32('draw', score.data.get_dict('stats').get_int('draw'))) mrec_0.add_child( Node.u8( 'ct', self.__db_to_game_clear_type( score.data.get_int('clear_type'), score.data.get_int('combo_type')))) mrec_0.add_child( Node.s16('ar', int(score.data.get_int('achievement_rate') / 10))) mrec_0.add_child(Node.s32('bs', score.points)) mrec_0.add_child(Node.s16('mc', score.data.get_int('combo'))) mrec_0.add_child(Node.s16('bmc', score.data.get_int('miss_count'))) mrec_1 = Node.void('mrec_1') rec.add_child(mrec_1) mrec_1.add_child(Node.s32('win', 0)) mrec_1.add_child(Node.s32('lose', 0)) mrec_1.add_child(Node.s32('draw', 0)) mrec_1.add_child(Node.u8('ct', 0)) mrec_1.add_child(Node.s16('ar', 0)) mrec_1.add_child(Node.s32('bs', 0)) mrec_1.add_child(Node.s16('mc', 0)) mrec_1.add_child(Node.s16('bmc', -1)) # Comment (seems unused?) pdata.add_child(Node.string('cmnt', '')) # Rivals rival = Node.void('rival') pdata.add_child(rival) slotid = 0 for link in links: if link.type != 'rival': continue rprofile = self.get_profile(link.other_userid) if rprofile is None: continue r = Node.void('r') rival.add_child(r) r.add_child(Node.u8('slot_id', slotid)) r.add_child(Node.string('name', rprofile.get_str('name'))) r.add_child(Node.s32('id', rprofile.get_int('extid'))) r.add_child(Node.bool('friend', True)) r.add_child(Node.bool('locked', False)) r.add_child(Node.s32('rc', 0)) slotid = slotid + 1 # Glass points glass = Node.void('glass') pdata.add_child(glass) for item in achievements: if item.type != 'glass': continue g = Node.void('g') glass.add_child(g) g.add_child(Node.s32('id', item.id)) g.add_child(Node.s32('exp', item.data.get_int('exp'))) # Favorite music fav_music_slot = Node.void('fav_music_slot') pdata.add_child(fav_music_slot) for item in achievements: if item.type != 'music': continue slot = Node.void('slot') fav_music_slot.add_child(slot) slot.add_child(Node.u8('slot_id', item.id)) slot.add_child(Node.s16('music_id', item.data.get_int('music_id'))) narrow_down = Node.void('narrow_down') pdata.add_child(narrow_down) narrow_down.add_child( Node.s32_array('adv_param', [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ])) return root
def verify_player_write(self, refid: str, extid: int, loc: str, records: List[Dict[str, int]], scores: List[Dict[str, int]]) -> int: call = self.call_node() player = Node.void('player') call.add_child(player) player.set_attribute('method', 'write') player.add_child(Node.string('rid', refid)) player.add_child(Node.string('lid', loc)) player.add_child(Node.u64('begin_time', Time.now() * 1000)) player.add_child(Node.u64('end_time', Time.now() * 1000)) pdata = Node.void('pdata') player.add_child(pdata) base = Node.void('base') pdata.add_child(base) base.add_child(Node.s32('uid', extid)) base.add_child(Node.string('name', self.NAME)) base.add_child(Node.s16('icon_id', 0)) base.add_child(Node.s16('lv', 1)) base.add_child(Node.s32('exp', 0)) base.add_child(Node.s16('mg', 0)) base.add_child(Node.s16('ap', 0)) base.add_child(Node.s32('pc', 0)) base.add_child(Node.s32('uattr', 0)) con = Node.void('con') pdata.add_child(con) con.add_child(Node.s32('day', 0)) con.add_child(Node.s32('cnt', 0)) con.add_child(Node.s32('total_cnt', 0)) con.add_child(Node.s32('last', 0)) con.add_child(Node.s32('now', 0)) custom = Node.void('custom') pdata.add_child(custom) custom.add_child(Node.u8('s_gls', 0)) custom.add_child(Node.u8('bgm_m', 0)) custom.add_child(Node.u8('st_f', 0)) custom.add_child(Node.u8('st_bg', 0)) custom.add_child(Node.u8('st_bg_b', 100)) custom.add_child(Node.u8('eff_e', 0)) custom.add_child(Node.u8('se_s', 0)) custom.add_child(Node.u8('se_s_v', 100)) custom.add_child(Node.s16('last_music_id', 85)) custom.add_child(Node.u8('last_note_grade', 0)) custom.add_child(Node.u8('sort_type', 0)) custom.add_child(Node.u8('narrowdown_type', 0)) custom.add_child(Node.bool('is_begginer', False)) custom.add_child(Node.bool('is_tut', False)) custom.add_child(Node.s16_array('symbol_chat_0', [0, 1, 2, 3, 4, 5])) custom.add_child(Node.s16_array('symbol_chat_1', [0, 1, 2, 3, 4, 5])) custom.add_child(Node.u8('gauge_style', 0)) custom.add_child(Node.u8('obj_shade', 0)) custom.add_child(Node.u8('obj_size', 0)) custom.add_child(Node.s16_array('byword', [0, 0])) custom.add_child(Node.bool_array('is_auto_byword', [True, True])) custom.add_child(Node.bool('is_tweet', False)) custom.add_child(Node.bool('is_link_twitter', False)) custom.add_child(Node.s16('mrec_type', 0)) custom.add_child(Node.s16('card_disp_type', 0)) custom.add_child(Node.s16('tab_sel', 0)) custom.add_child( Node.s32_array( 'hidden_param', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0])) pdata.add_child(Node.void('released')) pdata.add_child(Node.void('rival')) pdata.add_child(Node.void('glass')) pdata.add_child(Node.void('fav_music_slot')) lincle_link_4 = Node.void('lincle_link_4') pdata.add_child(lincle_link_4) lincle_link_4.add_child(Node.u32('qpro_add', 0)) lincle_link_4.add_child(Node.u32('glass_add', 0)) lincle_link_4.add_child(Node.bool('for_iidx_0_0', False)) lincle_link_4.add_child(Node.bool('for_iidx_0_1', False)) lincle_link_4.add_child(Node.bool('for_iidx_0_2', False)) lincle_link_4.add_child(Node.bool('for_iidx_0_3', False)) lincle_link_4.add_child(Node.bool('for_iidx_0_4', False)) lincle_link_4.add_child(Node.bool('for_iidx_0_5', False)) lincle_link_4.add_child(Node.bool('for_iidx_0_6', False)) lincle_link_4.add_child(Node.bool('for_iidx_0', False)) lincle_link_4.add_child(Node.bool('for_iidx_1', False)) lincle_link_4.add_child(Node.bool('for_iidx_2', False)) lincle_link_4.add_child(Node.bool('for_iidx_3', False)) lincle_link_4.add_child(Node.bool('for_iidx_4', False)) lincle_link_4.add_child(Node.bool('for_rb_0_0', False)) lincle_link_4.add_child(Node.bool('for_rb_0_1', False)) lincle_link_4.add_child(Node.bool('for_rb_0_2', False)) lincle_link_4.add_child(Node.bool('for_rb_0_3', False)) lincle_link_4.add_child(Node.bool('for_rb_0_4', False)) lincle_link_4.add_child(Node.bool('for_rb_0_5', False)) lincle_link_4.add_child(Node.bool('for_rb_0_6', False)) lincle_link_4.add_child(Node.bool('for_rb_0', False)) lincle_link_4.add_child(Node.bool('for_rb_1', False)) lincle_link_4.add_child(Node.bool('for_rb_2', False)) lincle_link_4.add_child(Node.bool('for_rb_3', False)) lincle_link_4.add_child(Node.bool('for_rb_4', False)) # First, filter down to only records that are also in the battle log def key(thing: Dict[str, int]) -> str: return f'{thing["id"]}-{thing["chart"]}' updates = [key(score) for score in scores] sortedrecords = { key(record): record for record in records if key(record) in updates } # Now, see what records need updating and update them for score in scores: if key(score) in sortedrecords: # Had a record, need to merge record = sortedrecords[key(score)] else: # First time playing record = { 'clear_type': 0, 'achievement_rate': 0, 'score': 0, 'combo': 0, 'miss_count': 999999999, } sortedrecords[key(score)] = { 'id': score['id'], 'chart': score['chart'], 'clear_type': max(record['clear_type'], score['clear_type']), 'achievement_rate': max(record['achievement_rate'], score['achievement_rate']), 'score': max(record['score'], score['score']), 'combo': max(record['combo'], score['combo']), 'miss_count': min(record['miss_count'], score['miss_count']), } # Finally, send the records and battle logs recordnode = Node.void('record') pdata.add_child(recordnode) blog = Node.void('blog') pdata.add_child(blog) for (_, record) in sortedrecords.items(): rec = Node.void('rec') recordnode.add_child(rec) rec.add_child(Node.u16('mid', record['id'])) rec.add_child(Node.u8('ng', record['chart'])) rec.add_child(Node.s32('point', 2)) rec.add_child(Node.s32('played_time', Time.now())) mrec_0 = Node.void('mrec_0') rec.add_child(mrec_0) mrec_0.add_child(Node.s32('win', 1)) mrec_0.add_child(Node.s32('lose', 0)) mrec_0.add_child(Node.s32('draw', 0)) mrec_0.add_child(Node.u8('ct', record['clear_type'])) mrec_0.add_child(Node.s16('ar', record['achievement_rate'])) mrec_0.add_child(Node.s32('bs', record['score'])) mrec_0.add_child(Node.s16('mc', record['combo'])) mrec_0.add_child(Node.s16('bmc', record['miss_count'])) mrec_1 = Node.void('mrec_1') rec.add_child(mrec_1) mrec_1.add_child(Node.s32('win', 0)) mrec_1.add_child(Node.s32('lose', 0)) mrec_1.add_child(Node.s32('draw', 0)) mrec_1.add_child(Node.u8('ct', 0)) mrec_1.add_child(Node.s16('ar', 0)) mrec_1.add_child(Node.s32('bs', 0)) mrec_1.add_child(Node.s16('mc', 0)) mrec_1.add_child(Node.s16('bmc', -1)) scoreid = 0 for score in scores: log = Node.void('log') blog.add_child(log) log.add_child(Node.u8('id', scoreid)) log.add_child(Node.u16('mid', score['id'])) log.add_child(Node.u8('ng', score['chart'])) log.add_child(Node.u8('mt', 0)) log.add_child(Node.u8('rt', 0)) log.add_child(Node.s32('ruid', 0)) myself = Node.void('myself') log.add_child(myself) myself.add_child(Node.s16('mg', 0)) myself.add_child(Node.s16('ap', 0)) myself.add_child(Node.u8('ct', score['clear_type'])) myself.add_child(Node.s32('s', score['score'])) myself.add_child(Node.s16('ar', score['achievement_rate'])) rival = Node.void('rival') log.add_child(rival) rival.add_child(Node.s16('mg', 0)) rival.add_child(Node.s16('ap', 0)) rival.add_child(Node.u8('ct', 2)) rival.add_child(Node.s32('s', 177)) rival.add_child(Node.s16('ar', 500)) log.add_child(Node.s32('time', Time.now())) scoreid = scoreid + 1 # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/player/uid") self.assert_path(resp, "response/player/time") return resp.child_value('player/uid')