def verify_lobby_entry(self, location: str, extid: int) -> int: call = self.call_node() lobby = Node.void('lobby') lobby.set_attribute('method', 'entry') e = Node.void('e') lobby.add_child(e) e.add_child(Node.s32('eid', 0)) e.add_child(Node.u16('mid', 79)) e.add_child(Node.u8('ng', 0)) e.add_child(Node.s32('uid', extid)) e.add_child(Node.s32('uattr', 0)) e.add_child(Node.string('pn', self.NAME)) e.add_child(Node.s16('mg', 255)) e.add_child(Node.s32('mopt', 0)) e.add_child(Node.s32('tid', 0)) e.add_child(Node.string('tn', '')) e.add_child(Node.s32('topt', 0)) e.add_child(Node.string('lid', location)) e.add_child(Node.string('sn', '')) e.add_child(Node.u8('pref', 51)) e.add_child(Node.s8('stg', 4)) e.add_child(Node.s8('pside', 0)) e.add_child(Node.s16('eatime', 30)) e.add_child(Node.u8_array('ga', [127, 0, 0, 1])) e.add_child(Node.u16('gp', 10007)) e.add_child(Node.u8_array('la', [16, 0, 0, 0])) e.add_child(Node.u8('ver', 5)) lobby.add_child(Node.s32_array('friend', [])) call.add_child(lobby) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/lobby/interval") self.assert_path(resp, "response/lobby/interval_p") self.assert_path(resp, "response/lobby/eid") self.assert_path(resp, "response/lobby/e/eid") self.assert_path(resp, "response/lobby/e/mid") self.assert_path(resp, "response/lobby/e/ng") self.assert_path(resp, "response/lobby/e/uid") self.assert_path(resp, "response/lobby/e/uattr") self.assert_path(resp, "response/lobby/e/pn") self.assert_path(resp, "response/lobby/e/mg") self.assert_path(resp, "response/lobby/e/mopt") self.assert_path(resp, "response/lobby/e/tid") self.assert_path(resp, "response/lobby/e/tn") self.assert_path(resp, "response/lobby/e/topt") self.assert_path(resp, "response/lobby/e/lid") self.assert_path(resp, "response/lobby/e/sn") self.assert_path(resp, "response/lobby/e/pref") self.assert_path(resp, "response/lobby/e/stg") self.assert_path(resp, "response/lobby/e/pside") self.assert_path(resp, "response/lobby/e/eatime") self.assert_path(resp, "response/lobby/e/ga") self.assert_path(resp, "response/lobby/e/gp") self.assert_path(resp, "response/lobby/e/la") self.assert_path(resp, "response/lobby/e/ver") return resp.child_value('lobby/eid')
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 verify_game_save(self, location: str, refid: str, packet: int, block: int, blaster_energy: int) -> None: call = self.call_node() game = Node.void('game_3') call.add_child(game) game.set_attribute('method', 'save') game.set_attribute('ver', '0') game.add_child(Node.string('refid', refid)) game.add_child(Node.string('locid', location)) game.add_child(Node.u8('headphone', 0)) game.add_child(Node.u16('appeal_id', 1001)) game.add_child(Node.u16('comment_id', 0)) game.add_child(Node.s32('music_id', 29)) game.add_child(Node.u8('music_type', 1)) game.add_child(Node.u8('sort_type', 1)) game.add_child(Node.u8('narrow_down', 0)) game.add_child(Node.u8('gauge_option', 0)) game.add_child(Node.u32('earned_gamecoin_packet', packet)) game.add_child(Node.u32('earned_gamecoin_block', block)) item = Node.void('item') game.add_child(item) info = Node.void('info') item.add_child(info) info.add_child(Node.u32('id', 1)) info.add_child(Node.u32('type', 5)) info.add_child(Node.u32('param', 333333376)) info = Node.void('info') item.add_child(info) info.add_child(Node.u32('id', 0)) info.add_child(Node.u32('type', 5)) info.add_child(Node.u32('param', 600)) game.add_child( Node.s32_array( 'hidden_param', [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])) game.add_child(Node.s16('skill_name_id', -1)) game.add_child(Node.s32('earned_blaster_energy', blaster_energy)) game.add_child(Node.u32('blaster_count', 0)) printn = Node.void('print') game.add_child(printn) printn.add_child(Node.s32('count', 0)) ea_shop = Node.void('ea_shop') game.add_child(ea_shop) ea_shop.add_child(Node.s32('used_packet_booster', 0)) ea_shop.add_child(Node.s32('used_block_booster', 0)) game.add_child(Node.s8('start_option', 0)) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/game_3")
def handle_shopinfo_regist_request(self, request: Node) -> Node: # Update the name of this cab for admin purposes self.update_machine_name(request.child_value('shop/name')) shopinfo = Node.void('shopinfo') data = Node.void('data') shopinfo.add_child(data) data.add_child(Node.u32('cabid', 1)) data.add_child(Node.string('locationid', 'nowhere')) data.add_child(Node.u8('is_send', 1)) data.add_child(Node.s32_array( 'white_music_list', [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ], )) data.add_child(Node.u8('tax_phase', 1)) lab = Node.void('lab') data.add_child(lab) lab.add_child(Node.bool('is_open', False)) vocaloid_event = Node.void('vocaloid_event') data.add_child(vocaloid_event) vocaloid_event.add_child(Node.u8('state', 0)) vocaloid_event.add_child(Node.s32('music_id', 0)) vocaloid_event2 = Node.void('vocaloid_event2') data.add_child(vocaloid_event2) vocaloid_event2.add_child(Node.u8('state', 0)) vocaloid_event2.add_child(Node.s32('music_id', 0)) # No obnoxious 30 second wait to play. matching_off = Node.void('matching_off') data.add_child(matching_off) matching_off.add_child(Node.bool('is_open', True)) tenka = Node.void('tenka') data.add_child(tenka) tenka.add_child(Node.bool('is_participant', False)) return shopinfo
def verify_player_rb4total_bestallrank_read(self) -> None: call = self.call_node() player = Node.void('player') player.set_attribute('method', 'rb4total_bestallrank_read') player.add_child(Node.s32('uid', 0)) player.add_child(Node.s32_array('score', [897, 897, 0, 0, 0, 284])) call.add_child(player) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/player/score/rank") self.assert_path(resp, "response/player/score/score") self.assert_path(resp, "response/player/score/allrank")
def verify_lobby_read(self, location: str, extid: int) -> None: call = self.call_node() lobby = Node.void('lobby') lobby.set_attribute('method', 'read') lobby.add_child(Node.s32('uid', extid)) lobby.add_child(Node.u8('m_grade', 255)) lobby.add_child(Node.string('lid', location)) lobby.add_child(Node.s32('max', 128)) lobby.add_child(Node.s32_array('friend', [])) call.add_child(lobby) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/lobby/interval") self.assert_path(resp, "response/lobby/interval_p")
def verify_player_rb5_player_read_rank_5(self) -> None: call = self.call_node() player = Node.void('player') player.set_attribute('method', 'rb5_player_read_rank_5') player.add_child(Node.s32('uid', 0)) player.add_child(Node.s32_array('sc', [897, 897, 0, 0, 0])) player.add_child(Node.s8('mg_id', 0)) player.add_child(Node.s32('mg_sc', 220)) call.add_child(player) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/player/tbs/new_rank") self.assert_path(resp, "response/player/tbs/old_rank") self.assert_path(resp, "response/player/mng/new_rank") self.assert_path(resp, "response/player/mng/old_rank")
def verify_game_save(self, location: str, refid: str, packet: int, block: int, blaster_energy: int, appealcards: List[int]) -> None: call = self.call_node() game = Node.void('game_2') call.add_child(game) game.set_attribute('method', 'save') game.set_attribute('ver', '0') game.add_child(Node.string('refid', refid)) game.add_child(Node.string('locid', location)) game.add_child(Node.u8('headphone', 0)) game.add_child(Node.u8('hispeed', 52)) game.add_child(Node.u16('appeal_id', 1001)) game.add_child(Node.u16('comment_id', 0)) game.add_child(Node.s32('music_id', 29)) game.add_child(Node.u8('music_type', 1)) game.add_child(Node.u8('sort_type', 1)) game.add_child(Node.u8('narrow_down', 0)) game.add_child(Node.u8('gauge_option', 0)) game.add_child(Node.u32('earned_gamecoin_packet', packet)) game.add_child(Node.u32('earned_gamecoin_block', block)) game.add_child(Node.void('item')) appealcard = Node.void('appealcard') game.add_child(appealcard) for card in appealcards: info = Node.void('info') info.add_child(Node.u32('id', card)) info.add_child(Node.u32('count', 0)) appealcard.add_child(info) game.add_child( Node.s32_array( 'hidden_param', [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])) game.add_child(Node.s16('skill_name_id', -1)) game.add_child(Node.s32('earned_blaster_energy', blaster_energy)) game.add_child(Node.u32('blaster_count', 0)) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/game_2")
def verify_gametop_get_league( self, jid: int) -> Tuple[int, Tuple[int, int, int]]: call = self.call_node() # Construct node gametop = Node.void('gametop') call.add_child(gametop) gametop.set_attribute('method', 'get_league') gametop.add_child(Node.s32('retry', 0)) data = Node.void('data') gametop.add_child(data) player = Node.void('player') data.add_child(player) player.add_child(Node.s32('jid', jid)) player.add_child(Node.s32_array('rival_jid', [0] * 3)) # Swap with server resp = self.exchange('', call) # Verify expected nodes self.assert_path( resp, "response/gametop/data/league_list/league/player_list") self.assert_path(resp, "response/gametop/data/last_class") self.assert_path(resp, "response/gametop/data/last_subclass") self.assert_path(resp, "response/gametop/data/is_checked") leagueid = resp.child_value('gametop/data/league_list/league/id') playernode = None for player in resp.child( 'gametop/data/league_list/league/player_list').children: if player.child_value('jid') == jid: playernode = player break if playernode is None: raise Exception(f"Didn't find any scores for ExtID {jid}") result = playernode.child_value('result/score') if result is not None: return (leagueid, (result[0], result[1], result[2])) else: return (leagueid, (0, 0, 0))
def handle_game_request(self, request: Node) -> Optional[Node]: method = request.attribute('method') if method == 'get': # TODO: Hook these up to config so we can change this root = Node.void('game') root.add_child(Node.s32('game_phase', 2)) root.add_child(Node.s32('ir_phase', 0)) root.add_child(Node.s32('event_phase', 5)) root.add_child(Node.s32('netvs_phase', 0)) root.add_child(Node.s32('card_phase', 6)) root.add_child(Node.s32('illust_phase', 2)) root.add_child(Node.s32('psp_phase', 5)) root.add_child(Node.s32('other_phase', 1)) root.add_child(Node.s32('jubeat_phase', 1)) root.add_child(Node.s32('public_phase', 3)) root.add_child(Node.s32('kac_phase', 2)) root.add_child(Node.s32('local_matching', 1)) root.add_child(Node.s32('n_matching_sec', 60)) root.add_child(Node.s32('l_matching_sec', 60)) root.add_child(Node.s32('is_check_cpu', 0)) root.add_child(Node.s32('week_no', 0)) root.add_child(Node.s32_array('ng_illust', [0] * 10)) root.add_child(Node.s16_array('sel_ranking', [-1] * 10)) root.add_child(Node.s16_array('up_ranking', [-1] * 10)) return root if method == 'active': # Update the name of this cab for admin purposes self.update_machine_name(request.child_value('shop_name')) return Node.void('game') if method == 'taxphase': return Node.void('game') # Invalid method return None
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) links = self.data.local.user.get_links(self.game, self.version, userid) rprofiles: Dict[UserID, ValidatedDict] = {} root = Node.void('player') pdata = Node.void('pdata') root.add_child(pdata) # Account time info 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 # Previous account info previous_version = self.previous_version() if previous_version: succeeded = previous_version.has_profile(userid) else: succeeded = False # Account info account = Node.void('account') pdata.add_child(account) account.add_child(Node.s32('usrid', profile.get_int('extid'))) account.add_child(Node.s32('tpc', statistics.get_int('total_plays', 0))) account.add_child(Node.s32('dpc', today_count)) account.add_child(Node.s32('crd', 1)) account.add_child(Node.s32('brd', 1)) account.add_child(Node.s32('tdc', statistics.get_int('total_days', 0))) account.add_child(Node.s32('intrvld', 0)) account.add_child(Node.s16('ver', 0)) account.add_child(Node.bool('succeed', succeeded)) account.add_child(Node.u64('pst', 0)) account.add_child(Node.u64('st', Time.now() * 1000)) account.add_child(Node.s32('opc', 0)) account.add_child(Node.s32('lpc', 0)) account.add_child(Node.s32('cpc', 0)) account.add_child(Node.s32('mpc', 0)) # Base profile info base = Node.void('base') pdata.add_child(base) base.add_child(Node.string('name', profile.get_str('name'))) base.add_child(Node.s32('mg', profile.get_int('mg'))) base.add_child(Node.s32('ap', profile.get_int('ap'))) base.add_child(Node.string('cmnt', '')) base.add_child(Node.s32('uattr', profile.get_int('uattr'))) base.add_child(Node.s32('money', profile.get_int('money'))) base.add_child(Node.s32('tbs_5', -1)) base.add_child(Node.s32_array('tbgs_5', [-1, -1, -1, -1])) base.add_child( Node.s16_array('mlog', [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ])) base.add_child(Node.s32('class', profile.get_int('class'))) base.add_child(Node.s32('class_ar', profile.get_int('class_ar'))) base.add_child(Node.s32('skill_point', profile.get_int('skill_point'))) base.add_child(Node.bool('meteor_flg', False)) # Rivals rival = Node.void('rival') pdata.add_child(rival) slotid = 0 for link in links: if link.type != 'rival': continue if link.other_userid not in rprofiles: rprofile = self.get_profile(link.other_userid) if rprofile is None: continue rprofiles[link.other_userid] = rprofile else: rprofile = rprofiles[link.other_userid] lobbyinfo = self.data.local.lobby.get_play_session_info( self.game, self.version, link.other_userid) if lobbyinfo is None: lobbyinfo = ValidatedDict() r = Node.void('r') rival.add_child(r) r.add_child(Node.s32('slot_id', slotid)) r.add_child(Node.s32('id', rprofile.get_int('extid'))) r.add_child(Node.string('name', rprofile.get_str('name'))) r.add_child( Node.s32('icon', rprofile.get_dict('config').get_int('icon_id'))) r.add_child(Node.s32('class', rprofile.get_int('class'))) r.add_child(Node.s32('class_ar', rprofile.get_int('class_ar'))) r.add_child(Node.bool('friend', True)) r.add_child(Node.bool('target', False)) r.add_child(Node.u32('time', lobbyinfo.get_int('time'))) r.add_child(Node.u8_array('ga', lobbyinfo.get_int_array('ga', 4))) r.add_child(Node.u16('gp', lobbyinfo.get_int('gp'))) r.add_child(Node.u8_array('ipn', lobbyinfo.get_int_array('la', 4))) r.add_child( Node.u8_array('pnid', lobbyinfo.get_int_array('pnid', 16))) slotid = slotid + 1 # Configuration configdict = profile.get_dict('config') config = Node.void('config') pdata.add_child(config) config.add_child(Node.u8('msel_bgm', configdict.get_int('msel_bgm'))) config.add_child( Node.u8('narrowdown_type', configdict.get_int('narrowdown_type'))) config.add_child(Node.s16('icon_id', configdict.get_int('icon_id'))) config.add_child(Node.s16('byword_0', configdict.get_int('byword_0'))) config.add_child(Node.s16('byword_1', configdict.get_int('byword_1'))) config.add_child( Node.bool('is_auto_byword_0', configdict.get_bool('is_auto_byword_0'))) config.add_child( Node.bool('is_auto_byword_1', configdict.get_bool('is_auto_byword_1'))) config.add_child(Node.u8('mrec_type', configdict.get_int('mrec_type'))) config.add_child(Node.u8('tab_sel', configdict.get_int('tab_sel'))) config.add_child(Node.u8('card_disp', configdict.get_int('card_disp'))) config.add_child( Node.u8('score_tab_disp', configdict.get_int('score_tab_disp'))) config.add_child( Node.s16('last_music_id', configdict.get_int('last_music_id', -1))) config.add_child( Node.u8('last_note_grade', configdict.get_int('last_note_grade'))) config.add_child(Node.u8('sort_type', configdict.get_int('sort_type'))) config.add_child( Node.u8('rival_panel_type', configdict.get_int('rival_panel_type'))) config.add_child( Node.u64('random_entry_work', configdict.get_int('random_entry_work'))) config.add_child( Node.u64('custom_folder_work', configdict.get_int('custom_folder_work'))) config.add_child( Node.u8('folder_type', configdict.get_int('folder_type'))) config.add_child( Node.u8('folder_lamp_type', configdict.get_int('folder_lamp_type'))) config.add_child(Node.bool('is_tweet', configdict.get_bool('is_tweet'))) config.add_child( Node.bool('is_link_twitter', configdict.get_bool('is_link_twitter'))) # Customizations customdict = profile.get_dict('custom') custom = Node.void('custom') pdata.add_child(custom) custom.add_child(Node.u8('st_shot', customdict.get_int('st_shot'))) custom.add_child(Node.u8('st_frame', customdict.get_int('st_frame'))) custom.add_child(Node.u8('st_expl', customdict.get_int('st_expl'))) custom.add_child(Node.u8('st_bg', customdict.get_int('st_bg'))) custom.add_child( Node.u8('st_shot_vol', customdict.get_int('st_shot_vol'))) custom.add_child(Node.u8('st_bg_bri', customdict.get_int('st_bg_bri'))) custom.add_child( Node.u8('st_obj_size', customdict.get_int('st_obj_size'))) custom.add_child( Node.u8('st_jr_gauge', customdict.get_int('st_jr_gauge'))) custom.add_child( Node.u8('st_clr_gauge', customdict.get_int('st_clr_gauge'))) custom.add_child(Node.u8('st_rnd', customdict.get_int('st_rnd'))) custom.add_child( Node.u8('st_gr_gauge_type', customdict.get_int('st_gr_gauge_type'))) custom.add_child( Node.s16('voice_message_set', customdict.get_int('voice_message_set', -1))) custom.add_child( Node.u8('same_time_note_disp', customdict.get_int('same_time_note_disp'))) custom.add_child( Node.u8('st_score_disp_type', customdict.get_int('st_score_disp_type'))) custom.add_child( Node.u8('st_bonus_type', customdict.get_int('st_bonus_type'))) custom.add_child( Node.u8('st_rivalnote_type', customdict.get_int('st_rivalnote_type'))) custom.add_child( Node.u8('st_topassist_type', customdict.get_int('st_topassist_type'))) custom.add_child( Node.u8('high_speed', customdict.get_int('high_speed'))) custom.add_child(Node.u8('st_hazard', customdict.get_int('st_hazard'))) custom.add_child( Node.u8('st_clr_cond', customdict.get_int('st_clr_cond'))) custom.add_child( Node.u8('voice_message_volume', customdict.get_int('voice_message_volume'))) # Unlocks 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'))) info.add_child(Node.s32('insert_time', item.data.get_int('time'))) if game_config.get_bool('force_unlock_songs'): ids: Dict[int, int] = {} songs = self.data.local.music.get_all_songs( self.game, self.music_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])) info.add_child(Node.s32('insert_time', Time.now())) # Announcements announce = Node.void('announce') pdata.add_child(announce) for announcement in achievements: if announcement.type[:13] != 'announcement_': continue announcementtype = int(announcement.type[13:]) info = Node.void('info') announce.add_child(info) info.add_child(Node.u8('type', announcementtype)) info.add_child(Node.u16('id', announcement.id)) info.add_child( Node.u16('param', announcement.data.get_int('param'))) info.add_child( Node.bool('bneedannounce', announcement.data.get_bool('need'))) # Dojo ranking return dojo = Node.void('dojo') pdata.add_child(dojo) for entry in achievements: if entry.type != 'dojo': continue rec = Node.void('rec') dojo.add_child(rec) rec.add_child(Node.s32('class', entry.id)) rec.add_child( Node.s32('clear_type', entry.data.get_int('clear_type'))) rec.add_child(Node.s32('total_ar', entry.data.get_int('ar'))) rec.add_child(Node.s32('total_score', entry.data.get_int('score'))) rec.add_child(Node.s32('play_count', entry.data.get_int('plays'))) rec.add_child( Node.s32('last_play_time', entry.data.get_int('play_timestamp'))) rec.add_child( Node.s32('record_update_time', entry.data.get_int('record_timestamp'))) rec.add_child(Node.s32('rank', 0)) # Player Parameters player_param = Node.void('player_param') pdata.add_child(player_param) for param in achievements: if param.type[:13] != 'player_param_': continue itemtype = int(param.type[13:]) itemnode = Node.void('item') player_param.add_child(itemnode) itemnode.add_child(Node.s32('type', itemtype)) itemnode.add_child(Node.s32('bank', param.id)) itemnode.add_child( Node.s32_array('data', param.data.get_int_array('data', 256))) # Shop score for players self._add_shop_score(pdata) # My List data mylist = Node.void('mylist') pdata.add_child(mylist) listdata = Node.void('list') mylist.add_child(listdata) listdata.add_child(Node.s16('idx', 0)) listdata.add_child( Node.s16_array('mlst', profile.get_int_array('favorites', 30, [-1] * 30))) # Minigame settings minigame = Node.void('minigame') pdata.add_child(minigame) minigame.add_child(Node.s8('mgid', profile.get_int('mgid'))) minigame.add_child(Node.s32('sc', profile.get_int('mgsc'))) # Derby settings derby = Node.void('derby') pdata.add_child(derby) derby.add_child(Node.bool('is_open', False)) # Music rank points music_rank_point = Node.void('music_rank_point') pdata.add_child(music_rank_point) # yurukome list stuff yurukome_list = Node.void('yurukome_list') pdata.add_child(yurukome_list) for entry in achievements: if entry.type != 'yurukome': continue yurukome = Node.void('yurukome') yurukome_list.add_child(yurukome) yurukome.add_child(Node.s32('yurukome_id', entry.id)) # My course mode mycourse = Node.void('mycourse') mycoursedict = profile.get_dict('mycourse') pdata.add_child(mycourse) mycourse.add_child(Node.s16('mycourse_id', 1)) mycourse.add_child( Node.s32('music_id_1', mycoursedict.get_int('music_id_1', -1))) mycourse.add_child( Node.s16('note_grade_1', mycoursedict.get_int('note_grade_1', -1))) mycourse.add_child( Node.s32('score_1', mycoursedict.get_int('score_1', -1))) mycourse.add_child( Node.s32('music_id_2', mycoursedict.get_int('music_id_2', -1))) mycourse.add_child( Node.s16('note_grade_2', mycoursedict.get_int('note_grade_2', -1))) mycourse.add_child( Node.s32('score_2', mycoursedict.get_int('score_2', -1))) mycourse.add_child( Node.s32('music_id_3', mycoursedict.get_int('music_id_3', -1))) mycourse.add_child( Node.s16('note_grade_3', mycoursedict.get_int('note_grade_3', -1))) mycourse.add_child( Node.s32('score_3', mycoursedict.get_int('score_3', -1))) mycourse.add_child( Node.s32('music_id_4', mycoursedict.get_int('music_id_4', -1))) mycourse.add_child( Node.s16('note_grade_4', mycoursedict.get_int('note_grade_4', -1))) mycourse.add_child( Node.s32('score_4', mycoursedict.get_int('score_4', -1))) mycourse.add_child( Node.s32('insert_time', mycoursedict.get_int('insert_time', -1))) mycourse.add_child(Node.s32('def_music_id_1', -1)) mycourse.add_child(Node.s16('def_note_grade_1', -1)) mycourse.add_child(Node.s32('def_music_id_2', -1)) mycourse.add_child(Node.s16('def_note_grade_2', -1)) mycourse.add_child(Node.s32('def_music_id_3', -1)) mycourse.add_child(Node.s16('def_note_grade_3', -1)) mycourse.add_child(Node.s32('def_music_id_4', -1)) mycourse.add_child(Node.s16('def_note_grade_4', -1)) # Friend course scores mycourse_f = Node.void('mycourse_f') pdata.add_child(mycourse_f) for link in links: if link.type != 'rival': continue if link.other_userid not in rprofiles: rprofile = self.get_profile(link.other_userid) if rprofile is None: continue rprofiles[link.other_userid] = rprofile else: rprofile = rprofiles[link.other_userid] mycoursedict = rprofile.get_dict('mycourse') rec = Node.void('rec') mycourse_f.add_child(rec) rec.add_child(Node.s32('rival_id', rprofile.get_int('extid'))) rec.add_child(Node.s16('mycourse_id', 1)) rec.add_child( Node.s32('music_id_1', mycoursedict.get_int('music_id_1', -1))) rec.add_child( Node.s16('note_grade_1', mycoursedict.get_int('note_grade_1', -1))) rec.add_child( Node.s32('score_1', mycoursedict.get_int('score_1', -1))) rec.add_child( Node.s32('music_id_2', mycoursedict.get_int('music_id_2', -1))) rec.add_child( Node.s16('note_grade_2', mycoursedict.get_int('note_grade_2', -1))) rec.add_child( Node.s32('score_2', mycoursedict.get_int('score_2', -1))) rec.add_child( Node.s32('music_id_3', mycoursedict.get_int('music_id_3', -1))) rec.add_child( Node.s16('note_grade_3', mycoursedict.get_int('note_grade_3', -1))) rec.add_child( Node.s32('score_3', mycoursedict.get_int('score_3', -1))) rec.add_child( Node.s32('music_id_4', mycoursedict.get_int('music_id_4', -1))) rec.add_child( Node.s16('note_grade_4', mycoursedict.get_int('note_grade_4', -1))) rec.add_child( Node.s32('score_4', mycoursedict.get_int('score_4', -1))) rec.add_child( Node.s32('insert_time', mycoursedict.get_int('insert_time', -1))) return root
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: game = Node.void('game_3') # 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.s16('skill_name_id', profile.get_int('skill_name_id', -1))) game.add_child(Node.s32_array('hidden_param', profile.get_int_array('hidden_param', 20))) game.add_child(Node.u32('blaster_energy', profile.get_int('blaster_energy'))) game.add_child(Node.u32('blaster_count', profile.get_int('blaster_count'))) # Enable Ryusei Festa ryusei_festa = Node.void('ryusei_festa') game.add_child(ryusei_festa) ryusei_festa.add_child(Node.bool('ryusei_festa_trigger', True)) # Play statistics statistics = self.get_play_statistics(userid) 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 game.add_child(Node.u32('play_count', statistics.get_int('total_plays', 0))) game.add_child(Node.u32('daily_count', today_count)) game.add_child(Node.u32('play_chain', statistics.get_int('consecutive_days', 0))) # Last played stuff if 'last' in profile: lastdict = profile.get_dict('last') last = Node.void('last') game.add_child(last) last.add_child(Node.s32('music_id', lastdict.get_int('music_id', -1))) last.add_child(Node.u8('music_type', lastdict.get_int('music_type'))) last.add_child(Node.u8('sort_type', lastdict.get_int('sort_type'))) last.add_child(Node.u8('narrow_down', lastdict.get_int('narrow_down'))) last.add_child(Node.u8('headphone', lastdict.get_int('headphone'))) last.add_child(Node.u16('appeal_id', lastdict.get_int('appeal_id', 1001))) last.add_child(Node.u16('comment_id', lastdict.get_int('comment_id'))) last.add_child(Node.u8('gauge_option', lastdict.get_int('gauge_option'))) # Item unlocks itemnode = Node.void('item') game.add_child(itemnode) game_config = self.get_game_config() achievements = self.data.local.user.get_achievements(self.game, self.version, userid) 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 == self.GAME_CATALOG_TYPE_SONG: # Don't echo unlocked songs, we will add all of them later continue info = Node.void('info') itemnode.add_child(info) info.add_child(Node.u8('type', itemtype)) info.add_child(Node.u32('id', item.id)) info.add_child(Node.u32('param', item.data.get_int('param'))) if 'diff_param' in item.data: info.add_child(Node.s32('diff_param', item.data.get_int('diff_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 itemid in ids: if ids[itemid] == 0: continue info = Node.void('info') itemnode.add_child(info) info.add_child(Node.u8('type', self.GAME_CATALOG_TYPE_SONG)) info.add_child(Node.u32('id', itemid)) info.add_child(Node.u32('param', ids[itemid])) return game
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')
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: root = Node.void('playerdata') # Set up the base profile base = Node.void('base') root.add_child(base) base.add_child(Node.string('name', profile.get_str('name', 'なし'))) base.add_child( Node.string('g_pm_id', ID.format_extid(profile.get_int('extid')))) base.add_child(Node.u8('mode', profile.get_int('mode', 0))) base.add_child(Node.s8('button', profile.get_int('button', 0))) base.add_child( Node.s8('last_play_flag', profile.get_int('last_play_flag', -1))) base.add_child( Node.u8('medal_and_friend', profile.get_int('medal_and_friend', 0))) base.add_child(Node.s8('category', profile.get_int('category', -1))) base.add_child( Node.s8('sub_category', profile.get_int('sub_category', -1))) base.add_child(Node.s16('chara', profile.get_int('chara', -1))) base.add_child( Node.s8('chara_category', profile.get_int('chara_category', -1))) base.add_child(Node.u8('collabo', profile.get_int('collabo', 255))) base.add_child(Node.u8('sheet', profile.get_int('sheet', 0))) base.add_child(Node.s8('tutorial', profile.get_int('tutorial', 0))) base.add_child( Node.s32('music_open_pt', profile.get_int('music_open_pt', 0))) base.add_child(Node.s8('is_conv', -1)) base.add_child(Node.s32('option', profile.get_int('option', 0))) base.add_child(Node.s16('music', profile.get_int('music', -1))) base.add_child(Node.u16('ep', profile.get_int('ep', 0))) base.add_child( Node.s32_array('sp_color_flg', profile.get_int_array('sp_color_flg', 2))) base.add_child(Node.s32('read_news', profile.get_int('read_news', 0))) base.add_child( Node.s16('consecutive_days_coupon', profile.get_int('consecutive_days_coupon', 0))) base.add_child(Node.s8('staff', 0)) # Player card section player_card_dict = profile.get_dict('player_card') player_card = Node.void('player_card') root.add_child(player_card) player_card.add_child( Node.u8_array('title', player_card_dict.get_int_array('title', 2, [0, 1]))) player_card.add_child( Node.u8('frame', player_card_dict.get_int('frame'))) player_card.add_child(Node.u8('base', player_card_dict.get_int('base'))) player_card.add_child( Node.u8_array('seal', player_card_dict.get_int_array('seal', 2))) player_card.add_child( Node.s32_array('get_title', player_card_dict.get_int_array('get_title', 4))) player_card.add_child( Node.s32('get_frame', player_card_dict.get_int('get_frame'))) player_card.add_child( Node.s32('get_base', player_card_dict.get_int('get_base'))) player_card.add_child( Node.s32_array('get_seal', player_card_dict.get_int_array('get_seal', 2))) # Player card EX section player_card_ex = Node.void('player_card_ex') root.add_child(player_card_ex) player_card_ex.add_child( Node.s32('get_title_ex', player_card_dict.get_int('get_title_ex'))) player_card_ex.add_child( Node.s32('get_frame_ex', player_card_dict.get_int('get_frame_ex'))) player_card_ex.add_child( Node.s32('get_base_ex', player_card_dict.get_int('get_base_ex'))) player_card_ex.add_child( Node.s32('get_seal_ex', player_card_dict.get_int('get_seal_ex'))) # Statistics section and scores section statistics = self.get_play_statistics(userid) 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 base.add_child(Node.u8('active_fr_num', 0)) # TODO: Hook up rivals code? base.add_child( Node.s32('total_play_cnt', statistics.get_int('total_plays', 0))) base.add_child(Node.s16('today_play_cnt', today_count)) base.add_child( Node.s16('consecutive_days', statistics.get_int('consecutive_days', 0))) last_played = [ x[0] for x in self.data.local.music.get_last_played( self.game, self.version, userid, 3) ] most_played = [ x[0] for x in self.data.local.music.get_most_played( self.game, self.version, userid, 20) ] while len(last_played) < 3: last_played.append(-1) while len(most_played) < 20: most_played.append(-1) hiscore_array = [0] * int( (((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8) clear_medal = [0] * self.GAME_MAX_MUSIC_ID clear_medal_sub = [0] * self.GAME_MAX_MUSIC_ID scores = self.data.remote.music.get_scores(self.game, self.version, userid) for score in scores: if score.id > self.GAME_MAX_MUSIC_ID: continue # Skip any scores for chart types we don't support if score.chart not in [ self.CHART_TYPE_EASY, self.CHART_TYPE_NORMAL, self.CHART_TYPE_HYPER, self.CHART_TYPE_EX, ]: continue points = score.points clear_medal[score.id] = clear_medal[ score.id] | self.__format_medal_for_score(score) hiscore_index = (score.id * 4) + { self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION, self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION, self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER_POSITION, self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX_POSITION, }[score.chart] hiscore_byte_pos = int((hiscore_index * 17) / 8) hiscore_bit_pos = int((hiscore_index * 17) % 8) hiscore_value = points << hiscore_bit_pos hiscore_array[hiscore_byte_pos] = hiscore_array[ hiscore_byte_pos] | (hiscore_value & 0xFF) hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ( (hiscore_value >> 8) & 0xFF) hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ( (hiscore_value >> 16) & 0xFF) hiscore = bytes(hiscore_array) player_card.add_child(Node.s16_array('best_music', most_played[0:3])) base.add_child(Node.s16_array('my_best', most_played)) base.add_child(Node.s16_array('latest_music', last_played)) base.add_child(Node.u16_array('clear_medal', clear_medal)) base.add_child(Node.u8_array('clear_medal_sub', clear_medal_sub)) # Goes outside of base for some reason root.add_child(Node.binary('hiscore', hiscore)) # Net VS section netvs = Node.void('netvs') root.add_child(netvs) netvs.add_child(Node.s32_array('get_ojama', [0, 0])) netvs.add_child(Node.s32('rank_point', 0)) netvs.add_child(Node.s32('play_point', 0)) netvs.add_child(Node.s16_array('record', [0, 0, 0, 0, 0, 0])) netvs.add_child(Node.u8('rank', 0)) netvs.add_child(Node.s8_array('ojama_condition', [0] * 74)) netvs.add_child(Node.s8_array('set_ojama', [0, 0, 0])) netvs.add_child(Node.s8_array('set_recommend', [0, 0, 0])) netvs.add_child(Node.s8_array('jewelry', [0] * 15)) for dialog in [0, 1, 2, 3, 4, 5]: # TODO: Configure this, maybe? netvs.add_child(Node.string('dialog', 'dialog#{}'.format(dialog))) sp_data = Node.void('sp_data') root.add_child(sp_data) sp_data.add_child(Node.s32('sp', profile.get_int('sp', 0))) reflec_data = Node.void('reflec_data') root.add_child(reflec_data) reflec_data.add_child( Node.s8_array('reflec', profile.get_int_array('reflec', 2))) # Navigate section navigate_dict = profile.get_dict('navigate') navigate = Node.void('navigate') root.add_child(navigate) navigate.add_child(Node.s8('genre', navigate_dict.get_int('genre'))) navigate.add_child(Node.s8('image', navigate_dict.get_int('image'))) navigate.add_child(Node.s8('level', navigate_dict.get_int('level'))) navigate.add_child(Node.s8('ojama', navigate_dict.get_int('ojama'))) navigate.add_child( Node.s16('limit_num', navigate_dict.get_int('limit_num'))) navigate.add_child(Node.s8('button', navigate_dict.get_int('button'))) navigate.add_child(Node.s8('life', navigate_dict.get_int('life'))) navigate.add_child( Node.s16('progress', navigate_dict.get_int('progress'))) return root
def handle_eacoin_request(self, request: Node) -> Optional[Node]: """ Handle PASELI requests. The game will check out a session at the beginning of the game, make PASELI purchases against that session, and then close it ad the end of of a game. This handler ensures that this works for all games. """ method = request.attribute('method') if not self.config['paseli']['enabled']: # Refuse to respond, we don't have PASELI enabled print("PASELI not enabled, ignoring eacoin request") root = Node.void('eacoin') root.set_attribute('status', str(Status.NOT_ALLOWED)) return root if method == 'checkin': root = Node.void('eacoin') cardid = request.child_value('cardid') pin = request.child_value('passwd') if cardid is None or pin is None: # Refuse to return anything print("Invalid eacoin checkin request, missing cardid or pin") root.set_attribute('status', str(Status.NO_PROFILE)) return root userid = self.data.local.user.from_cardid(cardid) if userid is None: # Refuse to do anything print("No user for eacoin checkin request") root.set_attribute('status', str(Status.NO_PROFILE)) return root valid = self.data.local.user.validate_pin(userid, pin) if not valid: # Refuse to do anything print("User entered invalid pin for eacoin checkin request") root.set_attribute('status', str(Status.INVALID_PIN)) return root session = self.data.local.user.create_session(userid) if self.config['paseli']['infinite']: balance = PASELIHandler.INFINITE_PASELI_AMOUNT else: if self.config['machine']['arcade'] is None: # There's no arcade for this machine, but infinite is not # enabled, so there's no way to find a balance. balance = 0 else: balance = self.data.local.user.get_balance(userid, self.config['machine']['arcade']) root.add_child(Node.s16('sequence', 0)) root.add_child(Node.u8('acstatus', 0)) root.add_child(Node.string('acid', 'DUMMY_ID')) root.add_child(Node.string('acname', 'DUMMY_NAME')) root.add_child(Node.s32('balance', balance)) root.add_child(Node.string('sessid', session)) return root if method == 'opcheckin': root = Node.void('eacoin') passwd = request.child_value('passwd') if passwd is None: # Refuse to return anything print("Invalid eacoin checkin request, missing passwd") root.set_attribute('status', str(Status.NO_PROFILE)) return root if self.config['machine']['arcade'] is None: # Machine doesn't belong to an arcade print("Machine doesn't belong to an arcade") root.set_attribute('status', str(Status.NO_PROFILE)) return root arcade = self.data.local.machine.get_arcade(self.config['machine']['arcade']) if arcade is None: # Refuse to do anything print("No arcade for operator checkin request") root.set_attribute('status', str(Status.NO_PROFILE)) return root if arcade.pin != passwd: # Refuse to do anything print("User entered invalid pin for operator checkin request") root.set_attribute('status', str(Status.INVALID_PIN)) return root session = self.data.local.machine.create_session(arcade.id) root.add_child(Node.string('sessid', session)) return root elif method == 'consume': def make_resp(status: int, balance: int) -> Node: root = Node.void('eacoin') root.add_child(Node.u8('acstatus', status)) root.add_child(Node.u8('autocharge', 0)) root.add_child(Node.s32('balance', balance)) return root session = request.child_value('sessid') payment = request.child_value('payment') service = request.child_value('service') details = request.child_value('detail') if session is None or payment is None: # Refuse to do anything print("Invalid eacoin consume request, missing sessid or payment") return make_resp(2, 0) userid = self.data.local.user.from_session(session) if userid is None: # Refuse to do anything print("Invalid session for eacoin consume request") return make_resp(2, 0) if self.config['paseli']['infinite']: balance = PASELIHandler.INFINITE_PASELI_AMOUNT - payment else: if self.config['machine']['arcade'] is None: # There's no arcade for this machine, but infinite is not # enabled, so there's no way to find a balance, assume failed # consume payment. balance = None else: # Look up the new balance based on this delta. If there isn't enough, # we will end up returning None here and exit without performing. balance = self.data.local.user.update_balance(userid, self.config['machine']['arcade'], -payment) if balance is None: print("Not enough balance for eacoin consume request") return make_resp(1, self.data.local.user.get_balance(userid, self.config['machine']['arcade'])) else: self.data.local.network.put_event( 'paseli_transaction', { 'delta': -payment, 'balance': balance, 'service': -service, 'reason': details, 'pcbid': self.config['machine']['pcbid'], }, userid=userid, arcadeid=self.config['machine']['arcade'], ) return make_resp(0, balance) elif method == 'getlog': root = Node.void('eacoin') sessid = request.child_value('sessid') logtype = request.child_value('logtype') target = request.child_value('target') limit = request.child_value('perpage') offset = request.child_value('offset') # Try to determine whether its a user or an arcade session userid = self.data.local.user.from_session(sessid) if userid is None: arcadeid = self.data.local.machine.from_session(sessid) else: arcadeid = None # Bail out if we don't have any idea what session this is if userid is None and arcadeid is None: print("Unable to determine session type") return root # If we're a user session, also look up the current arcade # so we display only entries that happened on this arcade. if userid is not None: arcade = self.data.local.machine.get_arcade(self.config['machine']['arcade']) if arcade is None: print("Machine doesn't belong to an arcade") return root arcadeid = arcade.id # Now, look up all transactions for this specific group events = self.data.local.network.get_events( userid=userid, arcadeid=arcadeid, event='paseli_transaction', ) # Further filter it down to the current PCBID events = [event for event in events if event.data.get('pcbid') == target] # Grab the end of day today as a timestamp end_of_today = Time.end_of_today() time_format = '%Y-%m-%d %H:%M:%S' date_format = '%Y-%m-%d' # Set up common structure lognode = Node.void(logtype) topic = Node.void('topic') lognode.add_child(topic) summary = Node.void('summary') lognode.add_child(summary) # Display what day we are summed to topic.add_child(Node.string('sumdate', Time.format(Time.now(), date_format))) if logtype == 'last7days': # We show today in the today total, last 7 days prior in the week total beginning_of_today = end_of_today - Time.SECONDS_IN_DAY end_of_week = beginning_of_today beginning_of_week = end_of_week - Time.SECONDS_IN_WEEK topic.add_child(Node.string('sumfrom', Time.format(beginning_of_week, date_format))) topic.add_child(Node.string('sumto', Time.format(end_of_week, date_format))) today_total = sum([ -event.data.get_int('delta') for event in events if event.timestamp >= beginning_of_today and event.timestamp < end_of_today ]) today_total = sum([ -event.data.get_int('delta') for event in events if event.timestamp >= beginning_of_today and event.timestamp < end_of_today ]) week_txns = [ -event.data.get_int('delta') for event in events if event.timestamp >= beginning_of_week and event.timestamp < end_of_week ] week_total = sum(week_txns) if len(week_txns) > 0: week_avg = int(sum(week_txns) / len(week_txns)) else: week_avg = 0 # We display the totals for each day starting with yesterday and up through 7 days prior. # Index starts at 0 = yesterday, 1 = the day before, etc... items = [] for days in range(0, 7): end_of_day = end_of_week - (days * Time.SECONDS_IN_DAY) start_of_day = end_of_day - Time.SECONDS_IN_DAY items.append(sum([ -event.data.get_int('delta') for event in events if event.timestamp >= start_of_day and event.timestamp < end_of_day ])) topic.add_child(Node.s32('today', today_total)) topic.add_child(Node.s32('average', week_avg)) topic.add_child(Node.s32('total', week_total)) summary.add_child(Node.s32_array('items', items)) if logtype == 'last52weeks': # Start one week back, since the operator can look at last7days for newer stuff. beginning_of_today = end_of_today - Time.SECONDS_IN_DAY end_of_52_weeks = beginning_of_today - Time.SECONDS_IN_WEEK topic.add_child(Node.string('sumfrom', Time.format(end_of_52_weeks - (52 * Time.SECONDS_IN_WEEK), date_format))) topic.add_child(Node.string('sumto', Time.format(end_of_52_weeks, date_format))) # We index backwards, where index 0 = the first week back, 1 = the next week back after that, etc... items = [] for weeks in range(0, 52): end_of_range = end_of_52_weeks - (weeks * Time.SECONDS_IN_WEEK) beginning_of_range = end_of_range - Time.SECONDS_IN_WEEK items.append(sum([ -event.data.get_int('delta') for event in events if event.timestamp >= beginning_of_range and event.timestamp < end_of_range ])) summary.add_child(Node.s32_array('items', items)) if logtype == 'eachday': start_ts = Time.now() end_ts = Time.now() weekdays = [0] * 7 for event in events: event_day = Time.days_into_week(event.timestamp) weekdays[event_day] = weekdays[event_day] - event.data.get_int('delta') if event.timestamp < start_ts: start_ts = event.timestamp topic.add_child(Node.string('sumfrom', Time.format(start_ts, date_format))) topic.add_child(Node.string('sumto', Time.format(end_ts, date_format))) summary.add_child(Node.s32_array('items', weekdays)) if logtype == 'eachhour': start_ts = Time.now() end_ts = Time.now() hours = [0] * 24 for event in events: event_hour = int((event.timestamp % Time.SECONDS_IN_DAY) / Time.SECONDS_IN_HOUR) hours[event_hour] = hours[event_hour] - event.data.get_int('delta') if event.timestamp < start_ts: start_ts = event.timestamp topic.add_child(Node.string('sumfrom', Time.format(start_ts, date_format))) topic.add_child(Node.string('sumto', Time.format(end_ts, date_format))) summary.add_child(Node.s32_array('items', hours)) if logtype == 'detail': history = Node.void('history') lognode.add_child(history) # Respect details paging if offset is not None: events = events[offset:] if limit is not None: events = events[:limit] # Output the details themselves for event in events: card_no = '' if event.userid is not None: user = self.data.local.user.get_user(event.userid) if user is not None: cards = self.data.local.user.get_cards(user.id) if len(cards) > 0: card_no = CardCipher.encode(cards[0]) item = Node.void('item') history.add_child(item) item.add_child(Node.string('date', Time.format(event.timestamp, time_format))) item.add_child(Node.s32('consume', -event.data.get_int('delta'))) item.add_child(Node.s32('service', -event.data.get_int('service'))) item.add_child(Node.string('cardtype', '')) item.add_child(Node.string('cardno', ' ' * self.paseli_padding + card_no)) item.add_child(Node.string('title', '')) item.add_child(Node.string('systemid', '')) if logtype == 'lastmonths': year, month, _ = Time.todays_date() this_month = Time.timestamp_from_date(year, month) last_month = Time.timestamp_from_date(year, month - 1) month_before = Time.timestamp_from_date(year, month - 2) topic.add_child(Node.string('sumfrom', Time.format(month_before, date_format))) topic.add_child(Node.string('sumto', Time.format(this_month, date_format))) for (start, end) in [(month_before, last_month), (last_month, this_month)]: year, month, _ = Time.date_from_timestamp(start) items = [] for day in range(0, 31): begin_ts = start + (day * Time.SECONDS_IN_DAY) end_ts = begin_ts + Time.SECONDS_IN_DAY if begin_ts >= end: # Passed the end of this month items.append(0) else: # Sum up all the txns for this day items.append(sum([ -event.data.get_int('delta') for event in events if event.timestamp >= begin_ts and event.timestamp < end_ts ])) item = Node.void('item') summary.add_child(item) item.add_child(Node.s32('year', year)) item.add_child(Node.s32('month', month)) item.add_child(Node.s32_array('items', items)) root.add_child(Node.u8('processing', 0)) root.add_child(lognode) return root elif method == 'opchpass': root = Node.void('eacoin') oldpass = request.child_value('passwd') newpass = request.child_value('newpasswd') if oldpass is None or newpass is None: # Refuse to return anything print("Invalid eacoin pass change request, missing passwd") root.set_attribute('status', str(Status.NO_PROFILE)) return root if self.config['machine']['arcade'] is None: # Machine doesn't belong to an arcade print("Machine doesn't belong to an arcade") root.set_attribute('status', str(Status.NO_PROFILE)) return root arcade = self.data.local.machine.get_arcade(self.config['machine']['arcade']) if arcade is None: # Refuse to do anything print("No arcade for operator pass change request") root.set_attribute('status', str(Status.NO_PROFILE)) return root if arcade.pin != oldpass: # Refuse to do anything print("User entered invalid pin for operator pass change request") root.set_attribute('status', str(Status.INVALID_PIN)) return root arcade.pin = newpass self.data.local.machine.put_arcade(arcade) return root elif method == 'checkout': session = request.child_value('sessid') if session is not None: # Destroy the session so it can't be used for any other purchases self.data.local.user.destroy_session(session) root = Node.void('eacoin') return root elif method == 'opcheckout': session = request.child_value('sessid') if session is not None: # Destroy the session so it can't be used for any other purchases self.data.local.machine.destroy_session(session) root = Node.void('eacoin') return root # Invalid method return None
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 format_scores(self, userid: UserID, profile: ValidatedDict, scores: List[Score]) -> Node: root = Node.void('gametop') datanode = Node.void('data') root.add_child(datanode) player = Node.void('player') datanode.add_child(player) player.add_child(Node.s32('jid', profile.get_int('extid'))) playdata = Node.void('playdata') player.add_child(playdata) playdata.set_attribute('count', str(len(scores))) music = ValidatedDict() for score in scores: chart = self.db_to_game_chart(score.chart) data = music.get_dict(str(score.id)) play_cnt = data.get_int_array('play_cnt', 3) clear_cnt = data.get_int_array('clear_cnt', 3) clear_flags = data.get_int_array('clear_flags', 3) fc_cnt = data.get_int_array('fc_cnt', 3) ex_cnt = data.get_int_array('ex_cnt', 3) points = data.get_int_array('points', 3) # This means that we already assigned a value and it was greater than current # This is possible because we iterate through both hard mode and normal mode scores # and treat them equally. # TODO: generalize score merging code into a library since this does not account for # having a full combo in hard mode but not in normal. if points[chart] >= score.points: continue # Replace data for this chart type play_cnt[chart] = score.plays clear_cnt[chart] = score.data.get_int('clear_count') fc_cnt[chart] = score.data.get_int('full_combo_count') ex_cnt[chart] = score.data.get_int('excellent_count') points[chart] = score.points # Format the clear flags clear_flags[chart] = self.GAME_FLAG_BIT_PLAYED if score.data.get_int('clear_count') > 0: clear_flags[chart] |= self.GAME_FLAG_BIT_CLEARED if score.data.get_int('full_combo_count') > 0: clear_flags[chart] |= self.GAME_FLAG_BIT_FULL_COMBO if score.data.get_int('excellent_count') > 0: clear_flags[chart] |= self.GAME_FLAG_BIT_EXCELLENT # Save chart data back data.replace_int_array('play_cnt', 3, play_cnt) data.replace_int_array('clear_cnt', 3, clear_cnt) data.replace_int_array('clear_flags', 3, clear_flags) data.replace_int_array('fc_cnt', 3, fc_cnt) data.replace_int_array('ex_cnt', 3, ex_cnt) data.replace_int_array('points', 3, points) # Update the ghost (untyped) ghost = data.get('ghost', [None, None, None]) ghost[chart] = score.data.get('ghost') data['ghost'] = ghost # Save it back music.replace_dict(str(score.id), data) for scoreid in music: scoredata = music[scoreid] musicdata = Node.void('musicdata') playdata.add_child(musicdata) musicdata.set_attribute('music_id', scoreid) musicdata.add_child( Node.s32_array('play_cnt', scoredata.get_int_array('play_cnt', 3))) musicdata.add_child( Node.s32_array('clear_cnt', scoredata.get_int_array('clear_cnt', 3))) musicdata.add_child( Node.s32_array('fc_cnt', scoredata.get_int_array('fc_cnt', 3))) musicdata.add_child( Node.s32_array('ex_cnt', scoredata.get_int_array('ex_cnt', 3))) musicdata.add_child( Node.s32_array('score', scoredata.get_int_array('points', 3))) musicdata.add_child( Node.s8_array('clear', scoredata.get_int_array('clear_flags', 3))) ghosts = scoredata.get('ghost', [None, None, None]) for i in range(len(ghosts)): ghost = ghosts[i] if ghost is None: continue bar = Node.u8_array('bar', ghost) musicdata.add_child(bar) bar.set_attribute('seq', str(i)) return root
def handle_gametop_get_course_request(self, request: Node) -> Node: data = request.child('data') player = data.child('player') extid = player.child_value('jid') gametop = Node.void('gametop') data = Node.void('data') gametop.add_child(data) # Course list available course_list = Node.void('course_list') data.add_child(course_list) validcourses: List[int] = [] for course in self.get_all_courses(): coursenode = Node.void('course') course_list.add_child(coursenode) # Basic course info if course['id'] in validcourses: raise Exception('Cannot have same course ID specified twice!') validcourses.append(course['id']) coursenode.add_child(Node.s32('id', course['id'])) coursenode.add_child(Node.string('name', course['name'])) coursenode.add_child(Node.u8('level', course['level'])) # Translate internal to game def translate_req(internal_req: int) -> int: return { self.COURSE_REQUIREMENT_SCORE: self.GAME_COURSE_REQUIREMENT_SCORE, self.COURSE_REQUIREMENT_FULL_COMBO: self.GAME_COURSE_REQUIREMENT_FULL_COMBO, self.COURSE_REQUIREMENT_PERFECT_PERCENT: self.GAME_COURSE_REQUIREMENT_PERFECT_PERCENT, }.get(internal_req, 0) # Course bronze/silver/gold rules ids = [0] * 3 bronze_values = [0] * 3 silver_values = [0] * 3 gold_values = [0] * 3 slot = 0 for req in course['requirements']: req_values = course['requirements'][req] ids[slot] = translate_req(req) bronze_values[slot] = req_values[0] silver_values[slot] = req_values[1] gold_values[slot] = req_values[2] slot = slot + 1 norma = Node.void('norma') coursenode.add_child(norma) norma.add_child(Node.s32_array('norma_id', ids)) norma.add_child(Node.s32_array('bronze_value', bronze_values)) norma.add_child(Node.s32_array('silver_value', silver_values)) norma.add_child(Node.s32_array('gold_value', gold_values)) # Music list for course music_index = 0 music_list = Node.void('music_list') coursenode.add_child(music_list) for entry in course['music']: music = Node.void('music') music.set_attribute('index', str(music_index)) music_list.add_child(music) music.add_child(Node.s32('music_id', entry[0])) music.add_child(Node.u8('seq', entry[1])) music_index = music_index + 1 # Look up profile so we can load the last course played userid = self.data.remote.user.from_extid(self.game, self.version, extid) profile = self.get_profile(userid) if profile is None: profile = ValidatedDict() # Player scores for courses player_list = Node.void('player_list') data.add_child(player_list) player = Node.void('player') player_list.add_child(player) player.add_child(Node.s32('jid', extid)) result_list = Node.void('result_list') player.add_child(result_list) playercourses = self.get_courses(userid) for courseid in playercourses: if courseid not in validcourses: continue rating = { self.COURSE_RATING_FAILED: self.GAME_COURSE_RATING_FAILED, self.COURSE_RATING_BRONZE: self.GAME_COURSE_RATING_BRONZE, self.COURSE_RATING_SILVER: self.GAME_COURSE_RATING_SILVER, self.COURSE_RATING_GOLD: self.GAME_COURSE_RATING_GOLD, }[playercourses[courseid]['rating']] scores = playercourses[courseid]['scores'] result = Node.void('result') result_list.add_child(result) result.add_child(Node.s32('id', courseid)) result.add_child(Node.u8('rating', rating)) result.add_child(Node.s32_array('score', scores)) # Last course ID last_course_id = Node.s32('last_course_id', profile.get_dict('last').get_int('last_course_id', -1)) data.add_child(last_course_id) return gametop
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: root = Node.void('gametop') data = Node.void('data') root.add_child(data) player = Node.void('player') data.add_child(player) # Player info and statistics info = Node.void('info') player.add_child(info) info.add_child(Node.s16('jubility', profile.get_int('jubility'))) info.add_child(Node.s16('jubility_yday', profile.get_int('jubility_yday'))) info.add_child(Node.s32('tune_cnt', profile.get_int('tune_cnt'))) info.add_child(Node.s32('save_cnt', profile.get_int('save_cnt'))) info.add_child(Node.s32('saved_cnt', profile.get_int('saved_cnt'))) info.add_child(Node.s32('fc_cnt', profile.get_int('fc_cnt'))) info.add_child(Node.s32('ex_cnt', profile.get_int('ex_cnt'))) info.add_child(Node.s32('pf_cnt', profile.get_int('pf_cnt'))) info.add_child(Node.s32('clear_cnt', profile.get_int('clear_cnt'))) info.add_child(Node.s32('match_cnt', profile.get_int('match_cnt'))) info.add_child(Node.s32('beat_cnt', profile.get_int('beat_cnt'))) info.add_child(Node.s32('mynews_cnt', profile.get_int('mynews_cnt'))) info.add_child(Node.s32('extra_point', profile.get_int('extra_point'))) info.add_child(Node.bool('is_extra_played', profile.get_bool('is_extra_played'))) if 'total_best_score' in profile: info.add_child(Node.s32('total_best_score', profile.get_int('total_best_score'))) # Looks to be set to true when there's an old profile, stops tutorial from # happening on first load. info.add_child(Node.bool('inherit', profile.get_bool('has_old_version'))) # Not saved, but loaded info.add_child(Node.s32('mtg_entry_cnt', 123)) info.add_child(Node.s32('mtg_hold_cnt', 456)) info.add_child(Node.u8('mtg_result', 10)) # First play stuff we don't support free_first_play = Node.void('free_first_play') player.add_child(free_first_play) free_first_play.add_child(Node.bool('is_available', False)) free_first_play.add_child(Node.s32('point', 0)) free_first_play.add_child(Node.s32('point_used', 0)) # Secret unlocks item = Node.void('item') player.add_child(item) item.add_child(Node.s32_array( 'secret_list', profile.get_int_array( 'secret_list', 32, [-1] * 32, ), )) item.add_child(Node.s32_array( 'title_list', profile.get_int_array( 'title_list', 96, [-1] * 96, ), )) item.add_child(Node.s16('theme_list', profile.get_int('theme_list', -1))) item.add_child(Node.s32_array('marker_list', profile.get_int_array('marker_list', 2, [-1] * 2))) item.add_child(Node.s32_array('parts_list', profile.get_int_array('parts_list', 96, [-1] * 96))) new = Node.void('new') item.add_child(new) new.add_child(Node.s32_array( 'secret_list', profile.get_int_array( 'secret_list_new', 32, [-1] * 32, ), )) new.add_child(Node.s32_array( 'title_list', profile.get_int_array( 'title_list_new', 96, [-1] * 96, ), )) new.add_child(Node.s16('theme_list', profile.get_int('theme_list_new', -1))) new.add_child(Node.s32_array('marker_list', profile.get_int_array('marker_list_new', 2, [-1] * 2))) # Last played data, for showing cursor and such lastdict = profile.get_dict('last') last = Node.void('last') player.add_child(last) last.add_child(Node.s32('music_id', lastdict.get_int('music_id'))) last.add_child(Node.s8('marker', lastdict.get_int('marker'))) last.add_child(Node.s16('title', lastdict.get_int('title'))) last.add_child(Node.s8('theme', lastdict.get_int('theme'))) last.add_child(Node.s8('sort', lastdict.get_int('sort'))) last.add_child(Node.s8('rank_sort', lastdict.get_int('rank_sort'))) last.add_child(Node.s8('combo_disp', lastdict.get_int('combo_disp'))) last.add_child(Node.s8('seq_id', lastdict.get_int('seq_id'))) last.add_child(Node.s16('parts', lastdict.get_int('parts'))) last.add_child(Node.s8('category', lastdict.get_int('category'))) last.add_child(Node.s64('play_time', lastdict.get_int('play_time'))) last.add_child(Node.string('shopname', lastdict.get_str('shopname'))) last.add_child(Node.string('areaname', lastdict.get_str('areaname'))) last.add_child(Node.s8('expert_option', lastdict.get_int('expert_option'))) last.add_child(Node.s8('matching', lastdict.get_int('matching'))) last.add_child(Node.s8('hazard', lastdict.get_int('hazard'))) last.add_child(Node.s8('hard', lastdict.get_int('hard'))) # Miscelaneous crap player.add_child(Node.s32('session_id', 1)) player.add_child(Node.u64('event_flag', 0)) # Macchiato event macchiatodict = profile.get_dict('macchiato') macchiato = Node.void('macchiato') player.add_child(macchiato) macchiato.add_child(Node.s32('pack_id', macchiatodict.get_int('pack_id'))) macchiato.add_child(Node.u16('bean_num', macchiatodict.get_int('bean_num'))) macchiato.add_child(Node.s32('daily_milk_num', macchiatodict.get_int('daily_milk_num'))) macchiato.add_child(Node.bool('is_received_daily_milk', macchiatodict.get_bool('is_received_daily_milk'))) macchiato.add_child(Node.s32('today_tune_cnt', macchiatodict.get_int('today_tune_cnt'))) macchiato.add_child(Node.s32_array( 'daily_milk_bonus', macchiatodict.get_int_array('daily_milk_bonus', 9, [-1, -1, -1, -1, -1, -1, -1, -1, -1]), )) macchiato.add_child(Node.s32('daily_play_burst', macchiatodict.get_int('daily_play_burst'))) macchiato.add_child(Node.bool('sub_menu_is_completed', macchiatodict.get_bool('sub_menu_is_completed'))) macchiato.add_child(Node.s32('compensation_milk', macchiatodict.get_int('compensation_milk'))) macchiato.add_child(Node.s32('match_cnt', macchiatodict.get_int('match_cnt'))) # Probably never will support this macchiato_music_list = Node.void('macchiato_music_list') macchiato.add_child(macchiato_music_list) macchiato_music_list.set_attribute('count', '0') # Same with this comment macchiato.add_child(Node.s32('sub_pack_id', 0)) sub_macchiato_music_list = Node.void('sub_macchiato_music_list') macchiato.add_child(sub_macchiato_music_list) sub_macchiato_music_list.set_attribute('count', '0') # And this season_music_list = Node.void('season_music_list') macchiato.add_child(season_music_list) season_music_list.set_attribute('count', '0') # Weird, this is sent as a void with a bunch of subnodes, but returned as an int array. achievement_list = Node.void('achievement_list') macchiato.add_child(achievement_list) achievement_list.set_attribute('count', '0') # Also probably never supporting this either cow_list = Node.void('cow_list') macchiato.add_child(cow_list) cow_list.set_attribute('count', '0') # No news, ever. news = Node.void('news') player.add_child(news) news.add_child(Node.s16('checked', 0)) # No rival support, yet. rivallist = Node.void('rivallist') player.add_child(rivallist) rivallist.set_attribute('count', '0') # Full combo daily challenge. entry = self.data.local.game.get_time_sensitive_settings(self.game, self.version, 'fc_challenge') if entry is None: entry = ValidatedDict() # Figure out if we've played these songs start_time, end_time = self.data.local.network.get_schedule_duration('daily') today_attempts = self.data.local.music.get_all_attempts(self.game, self.version, userid, entry.get_int('today', -1), timelimit=start_time) whim_attempts = self.data.local.music.get_all_attempts(self.game, self.version, userid, entry.get_int('whim', -1), timelimit=start_time) challenge = Node.void('challenge') player.add_child(challenge) today = Node.void('today') challenge.add_child(today) today.add_child(Node.s32('music_id', entry.get_int('today', -1))) today.add_child(Node.u8('state', 0x40 if len(today_attempts) > 0 else 0x0)) whim = Node.void('whim') challenge.add_child(whim) whim.add_child(Node.s32('music_id', entry.get_int('whim', -1))) whim.add_child(Node.u8('state', 0x40 if len(whim_attempts) > 0 else 0x0)) # Sane defaults for unknown nodes only_now_music = Node.void('only_now_music') player.add_child(only_now_music) only_now_music.set_attribute('count', '0') lab_edit_seq = Node.void('lab_edit_seq') player.add_child(lab_edit_seq) lab_edit_seq.set_attribute('count', '0') kac_music = Node.void('kac_music') player.add_child(kac_music) kac_music.set_attribute('count', '0') history = Node.void('history') player.add_child(history) history.set_attribute('count', '0') share_music = Node.void('share_music') player.add_child(share_music) share_music.set_attribute('count', '0') bonus_music = Node.void('bonus_music') player.add_child(bonus_music) bonus_music.set_attribute('count', '0') bingo = Node.void('bingo') player.add_child(bingo) reward = Node.void('reward') bingo.add_child(reward) reward.add_child(Node.s32('total', 0)) reward.add_child(Node.s32('point', 0)) group = Node.void('group') player.add_child(group) group.add_child(Node.s32('group_id', 0)) # Basic profile info player.add_child(Node.string('name', profile.get_str('name', 'なし'))) player.add_child(Node.s32('jid', profile.get_int('extid'))) player.add_child(Node.string('refid', profile.get_str('refid'))) # Miscelaneous history stuff data.add_child(Node.u8('termver', 16)) data.add_child(Node.u32('season_etime', 0)) data.add_child(Node.s32_array( 'white_music_list', [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ], )) data.add_child(Node.s32_array( 'open_music_list', [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ], )) # Unsupported collaboration events with other games collabo_info = Node.void('collabo_info') data.add_child(collabo_info) # Unsupported policy break stuff policy_break = Node.void('policy_break') collabo_info.add_child(policy_break) policy_break.set_attribute('type', '1') policy_break.add_child(Node.u8('state', 1)) policy_break.add_child(Node.bool('is_report_end', False)) # Unsupported vocaloid stuff vocaloid_event = Node.void('vocaloid_event') collabo_info.add_child(vocaloid_event) vocaloid_event.set_attribute('type', '1') vocaloid_event.add_child(Node.u8('state', 0)) vocaloid_event.add_child(Node.s32('music_id', 0)) # Unsupported vocaloid stuff vocaloid_event2 = Node.void('vocaloid_event2') collabo_info.add_child(vocaloid_event2) vocaloid_event2.set_attribute('type', '1') vocaloid_event2.add_child(Node.u8('state', 0)) vocaloid_event2.add_child(Node.s32('music_id', 0)) # Maybe it is possible to turn off internet matching here? lab = Node.void('lab') data.add_child(lab) lab.add_child(Node.bool('is_open', False)) matching_off = Node.void('matching_off') data.add_child(matching_off) matching_off.add_child(Node.bool('is_open', True)) return root
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: root = Node.void('gametop') data = Node.void('data') root.add_child(data) player = Node.void('player') data.add_child(player) # Player info and statistics info = Node.void('info') player.add_child(info) info.add_child(Node.s16('jubility', profile.get_int('jubility'))) info.add_child(Node.s16('jubility_yday', profile.get_int('jubility_yday'))) info.add_child(Node.s32('tune_cnt', profile.get_int('tune_cnt'))) info.add_child(Node.s32('save_cnt', profile.get_int('save_cnt'))) info.add_child(Node.s32('saved_cnt', profile.get_int('saved_cnt'))) info.add_child(Node.s32('fc_cnt', profile.get_int('fc_cnt'))) info.add_child(Node.s32('ex_cnt', profile.get_int('ex_cnt'))) info.add_child(Node.s32('pf_cnt', profile.get_int('pf_cnt'))) info.add_child(Node.s32('clear_cnt', profile.get_int('clear_cnt'))) info.add_child(Node.s32('match_cnt', profile.get_int('match_cnt'))) info.add_child(Node.s32('beat_cnt', profile.get_int('beat_cnt'))) info.add_child(Node.s32('mynews_cnt', profile.get_int('mynews_cnt'))) if 'total_best_score' in profile: info.add_child(Node.s32('total_best_score', profile.get_int('total_best_score'))) # Looks to be set to true when there's an old profile, stops tutorial from # happening on first load. info.add_child(Node.bool('inherit', profile.get_bool('has_old_version'))) # Not saved, but loaded info.add_child(Node.s32('mtg_entry_cnt', 123)) info.add_child(Node.s32('mtg_hold_cnt', 456)) info.add_child(Node.u8('mtg_result', 10)) # Secret unlocks item = Node.void('item') player.add_child(item) item.add_child(Node.s32_array( 'secret_list', profile.get_int_array( 'secret_list', 32, [-1] * 32, ), )) item.add_child(Node.s32_array( 'title_list', profile.get_int_array( 'title_list', 96, [-1] * 96, ), )) item.add_child(Node.s16('theme_list', profile.get_int('theme_list', -1))) item.add_child(Node.s32_array('marker_list', profile.get_int_array('marker_list', 2, [-1] * 2))) item.add_child(Node.s32_array('parts_list', profile.get_int_array('parts_list', 96, [-1] * 96))) new = Node.void('new') item.add_child(new) new.add_child(Node.s32_array( 'secret_list', profile.get_int_array( 'secret_list_new', 32, [-1] * 32, ), )) new.add_child(Node.s32_array( 'title_list', profile.get_int_array( 'title_list_new', 96, [-1] * 96, ), )) new.add_child(Node.s16('theme_list', profile.get_int('theme_list_new', -1))) new.add_child(Node.s32_array('marker_list', profile.get_int_array('marker_list_new', 2, [-1] * 2))) # Last played data, for showing cursor and such lastdict = profile.get_dict('last') last = Node.void('last') player.add_child(last) last.add_child(Node.s32('music_id', lastdict.get_int('music_id'))) last.add_child(Node.s8('marker', lastdict.get_int('marker'))) last.add_child(Node.s16('title', lastdict.get_int('title'))) last.add_child(Node.s8('theme', lastdict.get_int('theme'))) last.add_child(Node.s8('sort', lastdict.get_int('sort'))) last.add_child(Node.s8('rank_sort', lastdict.get_int('rank_sort'))) last.add_child(Node.s8('combo_disp', lastdict.get_int('combo_disp'))) last.add_child(Node.s8('seq_id', lastdict.get_int('seq_id'))) last.add_child(Node.s16('parts', lastdict.get_int('parts'))) last.add_child(Node.s8('category', lastdict.get_int('category'))) last.add_child(Node.s64('play_time', lastdict.get_int('play_time'))) last.add_child(Node.string('shopname', lastdict.get_str('shopname'))) last.add_child(Node.string('areaname', lastdict.get_str('areaname'))) # Miscelaneous crap player.add_child(Node.s32('session_id', 1)) # Maybe hook this up? Unsure what it does, is it like IIDX dailies? today_music = Node.void('today_music') player.add_child(today_music) today_music.add_child(Node.s32('music_id', 0)) # No news, ever. news = Node.void('news') player.add_child(news) news.add_child(Node.s16('checked', 0)) # No rival support, yet. rivallist = Node.void('rivallist') player.add_child(rivallist) rivallist.set_attribute('count', '0') mylist = Node.void('mylist') player.add_child(mylist) mylist.set_attribute('count', '0') # No collaboration support yet. collabo = Node.void('collabo') player.add_child(collabo) collabo.add_child(Node.bool('success', False)) collabo.add_child(Node.bool('completed', False)) # Daily FC challenge. entry = self.data.local.game.get_time_sensitive_settings(self.game, self.version, 'fc_challenge') if entry is None: entry = ValidatedDict() # Figure out if we've played these songs start_time, end_time = self.data.local.network.get_schedule_duration('daily') today_attempts = self.data.local.music.get_all_attempts(self.game, self.version, userid, entry.get_int('today', -1), timelimit=start_time) challenge = Node.void('challenge') player.add_child(challenge) today = Node.void('today') challenge.add_child(today) today.add_child(Node.s32('music_id', entry.get_int('today', -1))) today.add_child(Node.u8('state', 0x40 if len(today_attempts) > 0 else 0x0)) onlynow = Node.void('onlynow') challenge.add_child(onlynow) onlynow.add_child(Node.s32('magic_no', 0)) onlynow.add_child(Node.s16('cycle', 0)) # Bistro event bistro = Node.void('bistro') player.add_child(bistro) # Presumably these can affect the speed of the event info_1 = Node.void('info') bistro.add_child(info_1) info_1.add_child(Node.float('delicious_rate', 1.0)) info_1.add_child(Node.float('favorite_rate', 1.0)) bistro.add_child(Node.s32('carry_over', profile.get_int('bistro_carry_over'))) # Your chef dude, I guess? chefdict = profile.get_dict('chef') chef = Node.void('chef') bistro.add_child(chef) chef.add_child(Node.s32('id', chefdict.get_int('id', 1))) chef.add_child(Node.u8('ability', chefdict.get_int('ability', 2))) chef.add_child(Node.u8('remain', chefdict.get_int('remain', 30))) chef.add_child(Node.u8('rate', chefdict.get_int('rate', 1))) # Routes, similar to story mode in Pop'n I guess? routes = [ { 'id': 50000284, 'price': 20, 'satisfaction': 10, 'favorite': True, }, { 'id': 50000283, 'price': 20, 'satisfaction': 20, 'favorite': False, }, { 'id': 50000282, 'price': 30, 'satisfaction': 10, 'favorite': False, }, { 'id': 50000275, 'price': 10, 'satisfaction': 55, 'favorite': False, }, { 'id': 50000274, 'price': 40, 'satisfaction': 40, 'favorite': False, }, { 'id': 50000273, 'price': 80, 'satisfaction': 60, 'favorite': False, }, { 'id': 50000272, 'price': 70, 'satisfaction': 60, 'favorite': False, }, { 'id': 50000271, 'price': 90, 'satisfaction': 80, 'favorite': False, }, { 'id': 50000270, 'price': 90, 'satisfaction': 20, 'favorite': False, }, ] for route_no in range(len(routes)): routedata = routes[route_no] route = Node.void('route') bistro.add_child(route) route.set_attribute('no', str(route_no)) music = Node.void('music') route.add_child(music) music.add_child(Node.s32('id', routedata['id'])) music.add_child(Node.u16('price', routedata['price'])) music.add_child(Node.s32('price_s32', routedata['price'])) # Look up any updated satisfaction stored by the game routesaved = self.data.local.user.get_achievement(self.game, self.version, userid, route_no + 1, 'route') if routesaved is None: routesaved = ValidatedDict() satisfaction = routesaved.get_int('satisfaction', routedata['satisfaction']) gourmates = Node.void('gourmates') route.add_child(gourmates) gourmates.add_child(Node.s32('id', route_no + 1)) gourmates.add_child(Node.u8('favorite', 1 if routedata['favorite'] else 0)) gourmates.add_child(Node.u16('satisfaction', satisfaction)) gourmates.add_child(Node.s32('satisfaction_s32', satisfaction)) # Sane defaults for unknown nodes only_now_music = Node.void('only_now_music') player.add_child(only_now_music) only_now_music.set_attribute('count', '0') requested_music = Node.void('requested_music') player.add_child(requested_music) requested_music.set_attribute('count', '0') kac_music = Node.void('kac_music') player.add_child(kac_music) kac_music.set_attribute('count', '0') history = Node.void('history') player.add_child(history) history.set_attribute('count', '0') # Basic profile info player.add_child(Node.string('name', profile.get_str('name', 'なし'))) player.add_child(Node.s32('jid', profile.get_int('extid'))) player.add_child(Node.string('refid', profile.get_str('refid'))) # Miscelaneous history stuff data.add_child(Node.u8('termver', 16)) data.add_child(Node.u32('season_etime', 0)) data.add_child(Node.s32('bistro_last_music_id', 0)) data.add_child(Node.s32_array( 'white_music_list', [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ], )) data.add_child(Node.s32_array( 'old_music_list', [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ], )) data.add_child(Node.s32_array( 'open_music_list', [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ], )) # Unsupported collaboration events with other games collabo_info = Node.void('collabo_info') data.add_child(collabo_info) # Unsupported marathon stuff run_run_marathon = Node.void('run_run_marathon') collabo_info.add_child(run_run_marathon) run_run_marathon.set_attribute('type', '1') run_run_marathon.add_child(Node.u8('state', 1)) run_run_marathon.add_child(Node.bool('is_report_end', True)) # Unsupported policy break stuff policy_break = Node.void('policy_break') collabo_info.add_child(policy_break) policy_break.set_attribute('type', '1') policy_break.add_child(Node.u8('state', 1)) policy_break.add_child(Node.bool('is_report_end', False)) # Unsupported vocaloid stuff vocaloid_event = Node.void('vocaloid_event') collabo_info.add_child(vocaloid_event) vocaloid_event.set_attribute('type', '1') vocaloid_event.add_child(Node.u8('state', 0)) vocaloid_event.add_child(Node.s32('music_id', 0)) # No obnoxious 30 second wait to play. matching_off = Node.void('matching_off') data.add_child(matching_off) matching_off.add_child(Node.bool('is_open', True)) return root
def handle_player_rb5_player_read_rank_5_request(self, request: Node) -> Node: # This gives us a 6-integer array mapping to user scores for the following: # [total score, basic chart score, medium chart score, hard chart score, # special chart score]. It also returns the previous rank, but this is # not used in-game as far as I can tell. current_scores = request.child_value('sc') current_minigame_score = request.child_value('mg_sc') # First, grab all scores on the network for this version. all_scores = self.data.remote.music.get_all_scores( self.game, self.music_version) # Now grab all participating users that had scores all_users = {userid for (userid, score) in all_scores} # Now, group the scores by user, so we can add up the totals, only including # scores where the user at least cleared the song. scores_by_user = { userid: [ score for (uid, score) in all_scores if uid == userid and score.data.get_int('clear_type') >= self.CLEAR_TYPE_CLEARED ] for userid in all_users } # Now grab all user profiles for this game all_profiles = { profile[0]: profile[1] for profile in self.data.remote.user.get_all_profiles( self.game, self.version) } # Now, sum up the scores into the five categories that the game expects. total_scores = sorted( [ sum([score.points for score in scores]) for userid, scores in scores_by_user.items() ], reverse=True, ) basic_scores = sorted( [ sum([ score.points for score in scores if score.chart == self.CHART_TYPE_BASIC ]) for userid, scores in scores_by_user.items() ], reverse=True, ) medium_scores = sorted( [ sum([ score.points for score in scores if score.chart == self.CHART_TYPE_MEDIUM ]) for userid, scores in scores_by_user.items() ], reverse=True, ) hard_scores = sorted([ sum([ score.points for score in scores if score.chart == self.CHART_TYPE_HARD ]) for userid, scores in scores_by_user.items() ], ) special_scores = sorted( [ sum([ score.points for score in scores if score.chart == self.CHART_TYPE_SPECIAL ]) for userid, scores in scores_by_user.items() ], reverse=True, ) minigame_scores = sorted( [ all_profiles.get(userid, ValidatedDict()).get_int('mgsc') for userid in all_users ], reverse=True, ) # Guarantee that a zero score is at the end of every list, so that it makes # the algorithm for figuring out place have no edge case. total_scores.append(0) basic_scores.append(0) medium_scores.append(0) hard_scores.append(0) special_scores.append(0) minigame_scores.append(0) # Now, figure out where we fit based on the scores sent from the game. user_place = [1, 1, 1, 1, 1, 1] which_score = [ total_scores, basic_scores, medium_scores, hard_scores, special_scores, minigame_scores, ] earned_scores = current_scores + [current_minigame_score] for i in range(len(user_place)): earned_score = earned_scores[i] scores = which_score[i] for score in scores: if earned_score >= score: break user_place[i] = user_place[i] + 1 # Separate out minigame rank from scores minigame_rank = user_place[-1] user_place = user_place[:-1] root = Node.void('player') # Populate current ranking. tbs = Node.void('tbs') root.add_child(tbs) tbs.add_child(Node.s32_array('new_rank', user_place)) tbs.add_child(Node.s32_array('old_rank', [-1, -1, -1, -1, -1])) # Populate current minigame ranking (LOL). mng = Node.void('mng') root.add_child(mng) mng.add_child(Node.s32('new_rank', minigame_rank)) mng.add_child(Node.s32('old_rank', -1)) return root
def format_scores(self, userid: UserID, profile: ValidatedDict, scores: List[Score]) -> Node: scores = self.data.remote.music.get_scores(self.game, self.version, userid) root = Node.void('gametop') datanode = Node.void('data') root.add_child(datanode) player = Node.void('player') datanode.add_child(player) playdata = Node.void('playdata') player.add_child(playdata) playdata.set_attribute('count', str(len(scores))) music = ValidatedDict() for score in scores: data = music.get_dict(str(score.id)) play_cnt = data.get_int_array('play_cnt', 3) clear_cnt = data.get_int_array('clear_cnt', 3) clear_flags = data.get_int_array('clear_flags', 3) fc_cnt = data.get_int_array('fc_cnt', 3) ex_cnt = data.get_int_array('ex_cnt', 3) points = data.get_int_array('points', 3) # Replace data for this chart type play_cnt[score.chart] = score.plays clear_cnt[score.chart] = score.data.get_int('clear_count') fc_cnt[score.chart] = score.data.get_int('full_combo_count') ex_cnt[score.chart] = score.data.get_int('excellent_count') points[score.chart] = score.points # Format the clear flags clear_flags[score.chart] = self.GAME_FLAG_BIT_PLAYED if score.data.get_int('clear_count') > 0: clear_flags[score.chart] |= self.GAME_FLAG_BIT_CLEARED if score.data.get_int('full_combo_count') > 0: clear_flags[score.chart] |= self.GAME_FLAG_BIT_FULL_COMBO if score.data.get_int('excellent_count') > 0: clear_flags[score.chart] |= self.GAME_FLAG_BIT_EXCELLENT # Save chart data back data.replace_int_array('play_cnt', 3, play_cnt) data.replace_int_array('clear_cnt', 3, clear_cnt) data.replace_int_array('clear_flags', 3, clear_flags) data.replace_int_array('fc_cnt', 3, fc_cnt) data.replace_int_array('ex_cnt', 3, ex_cnt) data.replace_int_array('points', 3, points) # Update the ghost (untyped) ghost = data.get('ghost', [None, None, None]) ghost[score.chart] = score.data.get('ghost') data['ghost'] = ghost # Save it back music.replace_dict(str(score.id), data) for scoreid in music: scoredata = music[scoreid] musicdata = Node.void('musicdata') playdata.add_child(musicdata) musicdata.set_attribute('music_id', scoreid) musicdata.add_child(Node.s32_array('play_cnt', scoredata.get_int_array('play_cnt', 3))) musicdata.add_child(Node.s32_array('clear_cnt', scoredata.get_int_array('clear_cnt', 3))) musicdata.add_child(Node.s32_array('fc_cnt', scoredata.get_int_array('fc_cnt', 3))) musicdata.add_child(Node.s32_array('ex_cnt', scoredata.get_int_array('ex_cnt', 3))) musicdata.add_child(Node.s32_array('score', scoredata.get_int_array('points', 3))) musicdata.add_child(Node.s8_array('clear', scoredata.get_int_array('clear_flags', 3))) ghosts = scoredata.get('ghost', [None, None, None]) for i in range(len(ghosts)): ghost = ghosts[i] if ghost is None: continue bar = Node.u8_array('bar', ghost) musicdata.add_child(bar) bar.set_attribute('seq', str(i)) return root
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: root = Node.void('playerdata') # Set up the base profile base = Node.void('base') root.add_child(base) base.add_child(Node.string('name', profile.get_str('name', 'なし'))) base.add_child( Node.string('g_pm_id', ID.format_extid(profile.get_int('extid')))) base.add_child(Node.u8('mode', profile.get_int('mode', 0))) base.add_child(Node.s8('button', profile.get_int('button', 0))) base.add_child( Node.s8('last_play_flag', profile.get_int('last_play_flag', -1))) base.add_child( Node.u8('medal_and_friend', profile.get_int('medal_and_friend', 0))) base.add_child(Node.s8('category', profile.get_int('category', -1))) base.add_child( Node.s8('sub_category', profile.get_int('sub_category', -1))) base.add_child(Node.s16('chara', profile.get_int('chara', -1))) base.add_child( Node.s8('chara_category', profile.get_int('chara_category', -1))) base.add_child(Node.u8('collabo', 255)) base.add_child(Node.u8('sheet', profile.get_int('sheet', 0))) base.add_child(Node.s8('tutorial', profile.get_int('tutorial', 0))) base.add_child( Node.s8('music_open_pt', profile.get_int('music_open_pt', 0))) base.add_child(Node.s8('is_conv', -1)) base.add_child(Node.s32('option', profile.get_int('option', 0))) base.add_child(Node.s16('music', profile.get_int('music', -1))) base.add_child(Node.u16('ep', profile.get_int('ep', 0))) base.add_child( Node.s32_array('sp_color_flg', profile.get_int_array('sp_color_flg', 2))) base.add_child(Node.s32('read_news', profile.get_int('read_news', 0))) base.add_child( Node.s16('consecutive_days_coupon', profile.get_int('consecutive_days_coupon', 0))) base.add_child(Node.s8('staff', 0)) # These are probably from an old event, but if they aren't present and defaulted, # then different songs show up in the Zoo event. base.add_child( Node.u16_array( 'gitadora_point', profile.get_int_array('gitadora_point', 3, [2000, 2000, 2000]))) base.add_child( Node.u8('gitadora_select', profile.get_int('gitadora_select', 2))) # Statistics section and scores section statistics = self.get_play_statistics(userid) 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 base.add_child(Node.u8('active_fr_num', 0)) # TODO: Hook up rivals code? base.add_child( Node.s32('total_play_cnt', statistics.get_int('total_plays', 0))) base.add_child(Node.s16('today_play_cnt', today_count)) base.add_child( Node.s16('consecutive_days', statistics.get_int('consecutive_days', 0))) last_played = [ x[0] for x in self.data.local.music.get_last_played( self.game, self.version, userid, 3) ] most_played = [ x[0] for x in self.data.local.music.get_most_played( self.game, self.version, userid, 20) ] while len(last_played) < 3: last_played.append(-1) while len(most_played) < 20: most_played.append(-1) hiscore_array = [0] * int( (((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8) clear_medal = [0] * self.GAME_MAX_MUSIC_ID clear_medal_sub = [0] * self.GAME_MAX_MUSIC_ID scores = self.data.remote.music.get_scores(self.game, self.version, userid) for score in scores: if score.id > self.GAME_MAX_MUSIC_ID: continue # Skip any scores for chart types we don't support if score.chart not in [ self.CHART_TYPE_EASY, self.CHART_TYPE_NORMAL, self.CHART_TYPE_HYPER, self.CHART_TYPE_EX, ]: continue points = score.points medal = { self.PLAY_MEDAL_CIRCLE_FAILED: self.GAME_PLAY_MEDAL_CIRCLE_FAILED, self.PLAY_MEDAL_DIAMOND_FAILED: self.GAME_PLAY_MEDAL_DIAMOND_FAILED, self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED, self.PLAY_MEDAL_EASY_CLEAR: self.GAME_PLAY_MEDAL_CIRCLE_CLEARED, # Map approximately self.PLAY_MEDAL_CIRCLE_CLEARED: self.GAME_PLAY_MEDAL_CIRCLE_CLEARED, self.PLAY_MEDAL_DIAMOND_CLEARED: self.GAME_PLAY_MEDAL_DIAMOND_CLEARED, self.PLAY_MEDAL_STAR_CLEARED: self.GAME_PLAY_MEDAL_STAR_CLEARED, self.PLAY_MEDAL_CIRCLE_FULL_COMBO: self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO, self.PLAY_MEDAL_DIAMOND_FULL_COMBO: self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO, self.PLAY_MEDAL_STAR_FULL_COMBO: self.GAME_PLAY_MEDAL_STAR_FULL_COMBO, self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT, }[score.data.get_int('medal')] clear_medal[score.id] = clear_medal[score.id] | (medal << (score.chart * 4)) hiscore_index = (score.id * 4) + { self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION, self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION, self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER_POSITION, self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX_POSITION, }[score.chart] hiscore_byte_pos = int((hiscore_index * 17) / 8) hiscore_bit_pos = int((hiscore_index * 17) % 8) hiscore_value = points << hiscore_bit_pos hiscore_array[hiscore_byte_pos] = hiscore_array[ hiscore_byte_pos] | (hiscore_value & 0xFF) hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ( (hiscore_value >> 8) & 0xFF) hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ( (hiscore_value >> 16) & 0xFF) hiscore = bytes(hiscore_array) base.add_child(Node.s16_array('my_best', most_played)) base.add_child(Node.s16_array('latest_music', last_played)) base.add_child(Node.u16_array('clear_medal', clear_medal)) base.add_child(Node.u8_array('clear_medal_sub', clear_medal_sub)) # Goes outside of base for some reason root.add_child(Node.binary('hiscore', hiscore)) # Avatar section avatar_dict = profile.get_dict('avatar') avatar = Node.void('avatar') root.add_child(avatar) avatar.add_child(Node.u8('hair', avatar_dict.get_int('hair', 0))) avatar.add_child(Node.u8('face', avatar_dict.get_int('face', 0))) avatar.add_child(Node.u8('body', avatar_dict.get_int('body', 0))) avatar.add_child(Node.u8('effect', avatar_dict.get_int('effect', 0))) avatar.add_child(Node.u8('object', avatar_dict.get_int('object', 0))) avatar.add_child( Node.u8_array('comment', avatar_dict.get_int_array('comment', 2))) avatar.add_child( Node.s32_array('get_hair', avatar_dict.get_int_array('get_hair', 2))) avatar.add_child( Node.s32_array('get_face', avatar_dict.get_int_array('get_face', 2))) avatar.add_child( Node.s32_array('get_body', avatar_dict.get_int_array('get_body', 2))) avatar.add_child( Node.s32_array('get_effect', avatar_dict.get_int_array('get_effect', 2))) avatar.add_child( Node.s32_array('get_object', avatar_dict.get_int_array('get_object', 2))) avatar.add_child( Node.s32_array('get_comment_over', avatar_dict.get_int_array('get_comment_over', 3))) avatar.add_child( Node.s32_array('get_comment_under', avatar_dict.get_int_array('get_comment_under', 3))) # Avatar add section avatar_add_dict = profile.get_dict('avatar_add') avatar_add = Node.void('avatar_add') root.add_child(avatar_add) avatar_add.add_child( Node.s32_array('get_hair', avatar_add_dict.get_int_array('get_hair', 2))) avatar_add.add_child( Node.s32_array('get_face', avatar_add_dict.get_int_array('get_face', 2))) avatar_add.add_child( Node.s32_array('get_body', avatar_add_dict.get_int_array('get_body', 2))) avatar_add.add_child( Node.s32_array('get_effect', avatar_add_dict.get_int_array('get_effect', 2))) avatar_add.add_child( Node.s32_array('get_object', avatar_add_dict.get_int_array('get_object', 2))) avatar_add.add_child( Node.s32_array( 'get_comment_over', avatar_add_dict.get_int_array('get_comment_over', 2))) avatar_add.add_child( Node.s32_array( 'get_comment_under', avatar_add_dict.get_int_array('get_comment_under', 2))) avatar_add.add_child( Node.s32_array('new_hair', avatar_add_dict.get_int_array('new_hair', 2))) avatar_add.add_child( Node.s32_array('new_face', avatar_add_dict.get_int_array('new_face', 2))) avatar_add.add_child( Node.s32_array('new_body', avatar_add_dict.get_int_array('new_body', 2))) avatar_add.add_child( Node.s32_array('new_effect', avatar_add_dict.get_int_array('new_effect', 2))) avatar_add.add_child( Node.s32_array('new_object', avatar_add_dict.get_int_array('new_object', 2))) avatar_add.add_child( Node.s32_array( 'new_comment_over', avatar_add_dict.get_int_array('new_comment_over', 2))) avatar_add.add_child( Node.s32_array( 'new_comment_under', avatar_add_dict.get_int_array('new_comment_under', 2))) # Net VS section netvs = Node.void('netvs') root.add_child(netvs) netvs.add_child(Node.s32('rank_point', 0)) netvs.add_child(Node.s16_array('record', [0, 0, 0, 0, 0, 0])) netvs.add_child(Node.u8('rank', 0)) netvs.add_child(Node.s8('vs_rank_old', 0)) netvs.add_child(Node.s8_array('ojama_condition', [0] * 74)) netvs.add_child(Node.s8_array('set_ojama', [0, 0, 0])) netvs.add_child(Node.s8_array('set_recommend', [0, 0, 0])) netvs.add_child(Node.u8('netvs_play_cnt', 0)) for dialog in [0, 1, 2, 3, 4, 5]: # TODO: Configure this, maybe? netvs.add_child(Node.string('dialog', 'dialog#{}'.format(dialog))) sp_data = Node.void('sp_data') root.add_child(sp_data) sp_data.add_child(Node.s32('sp', profile.get_int('sp', 0))) gakuen = Node.void('gakuen_data') root.add_child(gakuen) gakuen.add_child(Node.s32('music_list', -1)) saucer = Node.void('flying_saucer') root.add_child(saucer) saucer.add_child(Node.s32('music_list', -1)) saucer.add_child(Node.s32('tune_count', -1)) saucer.add_child(Node.u32('clear_norma', 0)) saucer.add_child(Node.u32('clear_norma_add', 0)) zoo_dict = profile.get_dict('zoo') zoo = Node.void('zoo') root.add_child(zoo) zoo.add_child( Node.u16_array('point', zoo_dict.get_int_array('point', 5))) zoo.add_child( Node.s32_array('music_list', zoo_dict.get_int_array('music_list', 2))) zoo.add_child( Node.s8_array('today_play_flag', zoo_dict.get_int_array('today_play_flag', 4))) triple = Node.void('triple_journey') root.add_child(triple) triple.add_child(Node.s32('music_list', -1)) triple.add_child( Node.s32_array('boss_damage', [65534, 65534, 65534, 65534])) triple.add_child(Node.s32_array('boss_stun', [0, 0, 0, 0])) triple.add_child(Node.s32('magic_gauge', 0)) triple.add_child(Node.s32('today_party', 0)) triple.add_child(Node.bool('union_magic', False)) triple.add_child(Node.float('base_attack_rate', 1.0)) triple.add_child(Node.s32('iidx_play_num', 0)) triple.add_child(Node.s32('reflec_play_num', 0)) triple.add_child(Node.s32('voltex_play_num', 0)) triple.add_child(Node.bool('iidx_play_flg', True)) triple.add_child(Node.bool('reflec_play_flg', True)) triple.add_child(Node.bool('voltex_play_flg', True)) ios = Node.void('ios') root.add_child(ios) ios.add_child(Node.s32('continueRightAnswer', 30)) ios.add_child(Node.s32('totalRightAnswer', 30)) kac2013 = Node.void('kac2013') root.add_child(kac2013) kac2013.add_child(Node.s8('music_num', 0)) kac2013.add_child(Node.s16('music', 0)) kac2013.add_child(Node.u8('sheet', 0)) baseball = Node.void('baseball_data') root.add_child(baseball) baseball.add_child(Node.s64('music_list', -1)) for id in [3, 5, 7]: node = Node.void('floor_infection') root.add_child(node) node.add_child(Node.s32('infection_id', id)) node.add_child(Node.s32('music_list', -1)) return 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 verify_player_write(self, refid: str, loc: str, scores: List[Dict[str, int]]) -> int: call = self.call_node() player = Node.void('player') call.add_child(player) player.set_attribute('method', 'write') pdata = Node.void('pdata') player.add_child(pdata) account = Node.void('account') pdata.add_child(account) account.add_child(Node.s32('usrid', 0)) account.add_child(Node.s32('plyid', 0)) account.add_child(Node.s32('tpc', 1)) account.add_child(Node.s32('dpc', 1)) account.add_child(Node.s32('crd', 1)) account.add_child(Node.s32('brd', 1)) account.add_child(Node.s32('tdc', 1)) account.add_child(Node.string('rid', refid)) account.add_child(Node.string('lid', loc)) account.add_child(Node.u8('mode', 0)) account.add_child(Node.s16('ver', 5)) account.add_child(Node.bool('pp', True)) account.add_child(Node.bool('ps', True)) account.add_child(Node.s16('pay', 0)) account.add_child(Node.s16('pay_pc', 0)) account.add_child(Node.u64('st', int(time.time() * 1000))) base = Node.void('base') pdata.add_child(base) base.add_child(Node.string('name', self.NAME)) base.add_child(Node.s32('exp', 0)) base.add_child(Node.s32('lv', 1)) base.add_child(Node.s32('mg', -1)) base.add_child(Node.s32('ap', -1)) base.add_child(Node.s32_array('hidden_param', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])) base.add_child(Node.bool('is_tut', True)) stglog = Node.void('stglog') pdata.add_child(stglog) index = 0 for score in scores: log = Node.void('log') stglog.add_child(log) log.add_child(Node.s8('stg', index)) log.add_child(Node.s16('mid', score['id'])) log.add_child(Node.s8('ng', score['chart'])) log.add_child(Node.s8('col', 0)) log.add_child(Node.s8('mt', 7)) log.add_child(Node.s8('rt', 0)) log.add_child(Node.s8('ct', score['clear_type'])) log.add_child(Node.s16('grd', 0)) log.add_child(Node.s16('ar', score['achievement_rate'])) log.add_child(Node.s16('sc', score['score'])) log.add_child(Node.s16('jt_jst', 0)) log.add_child(Node.s16('jt_grt', 0)) log.add_child(Node.s16('jt_gd', 0)) log.add_child(Node.s16('jt_ms', score['miss_count'])) log.add_child(Node.s16('jt_jr', 0)) log.add_child(Node.s16('cmb', score['combo'])) log.add_child(Node.s16('exp', 0)) log.add_child(Node.s32('r_uid', 0)) log.add_child(Node.s32('r_plyid', 0)) log.add_child(Node.s8('r_stg', 0)) log.add_child(Node.s8('r_ct', -1)) log.add_child(Node.s16('r_sc', 0)) log.add_child(Node.s16('r_grd', 0)) log.add_child(Node.s16('r_ar', 0)) log.add_child(Node.s8('r_cpuid', -1)) log.add_child(Node.s32('time', int(time.time()))) log.add_child(Node.s8('decide', 0)) index = index + 1 # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/player/uid") return resp.child_value('player/uid')