def verify_game_shop(self, location: str) -> None: call = self.call_node() game = Node.void('game_3') call.add_child(game) game.set_attribute('method', 'shop') game.set_attribute('ver', '0') game.add_child(Node.string('locid', location)) game.add_child(Node.string('regcode', '.')) game.add_child(Node.string('locname', '')) game.add_child(Node.u8('loctype', 0)) game.add_child(Node.string('cstcode', '')) game.add_child(Node.string('cpycode', '')) game.add_child(Node.s32('latde', 0)) game.add_child(Node.s32('londe', 0)) game.add_child(Node.u8('accu', 0)) game.add_child(Node.string('linid', '.')) game.add_child(Node.u8('linclass', 0)) game.add_child(Node.ipv4('ipaddr', '0.0.0.0')) game.add_child(Node.string('hadid', '00010203040506070809')) game.add_child(Node.string('licid', '00010203040506070809')) game.add_child(Node.string('actid', self.pcbid)) game.add_child(Node.s8('appstate', 0)) game.add_child(Node.s8('c_need', 1)) game.add_child(Node.s8('c_credit', 2)) game.add_child(Node.s8('s_credit', 2)) game.add_child(Node.bool('free_p', True)) game.add_child(Node.bool('close', False)) game.add_child(Node.s32('close_t', 1380)) game.add_child(Node.u32('playc', 0)) game.add_child(Node.u32('playn', 0)) game.add_child(Node.u32('playe', 0)) game.add_child(Node.u32('test_m', 0)) game.add_child(Node.u32('service', 0)) game.add_child(Node.bool('paseli', True)) game.add_child(Node.u32('update', 0)) game.add_child(Node.string('shopname', '')) game.add_child(Node.bool('newpc', False)) game.add_child(Node.s32('s_paseli', 206)) game.add_child(Node.s32('monitor', 1)) game.add_child(Node.string('romnumber', '-')) game.add_child( Node.string('etc', 'TaxMode:1,BasicRate:100/1,FirstFree:0')) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/game_3/nxt_time")
def handle_info_rb5_info_read_shop_ranking_request(self, request: Node) -> Node: start_music_id = request.child_value('min') end_music_id = request.child_value('max') root = Node.void('info') shop_score = Node.void('shop_score') root.add_child(shop_score) shop_score.add_child(Node.s32('time', Time.now())) profiles: Dict[UserID, ValidatedDict] = {} for songid in range(start_music_id, end_music_id + 1): allscores = self.data.local.music.get_all_scores( self.game, self.version, songid=songid, ) for ng in [ self.CHART_TYPE_BASIC, self.CHART_TYPE_MEDIUM, self.CHART_TYPE_HARD, self.CHART_TYPE_SPECIAL, ]: scores = sorted( [score for score in allscores if score[1].chart == ng], key=lambda score: score[1].points, reverse=True, ) for i in range(len(scores)): userid, score = scores[i] if userid not in profiles: profiles[userid] = self.get_any_profile(userid) profile = profiles[userid] data = Node.void('data') shop_score.add_child(data) data.add_child(Node.s32('rank', i + 1)) data.add_child(Node.s16('music_id', songid)) data.add_child(Node.s8('note_grade', score.chart)) data.add_child(Node.s8('clear_type', self._db_to_game_clear_type(score.data.get_int('clear_type')))) data.add_child(Node.s32('user_id', profile.get_int('extid'))) data.add_child(Node.s16('icon_id', profile.get_dict('config').get_int('icon_id'))) data.add_child(Node.s32('score', score.points)) data.add_child(Node.s32('time', score.timestamp)) data.add_child(Node.string('name', profile.get_str('name'))) return root
def verify_playerdata_new(self, ref_id: str) -> None: call = self.call_node() # Construct node playerdata = Node.void('playerdata') call.add_child(playerdata) playerdata.set_attribute('method', 'new') playerdata.add_child(Node.string('ref_id', ref_id)) playerdata.add_child(Node.string('name', self.NAME)) playerdata.add_child(Node.string('shop_name', '')) playerdata.add_child(Node.s8('pref', 51)) # Swap with server resp = self.exchange('pnm20/playerdata', call) # Verify nodes that cause crashes if they don't exist self.assert_path(resp, "response/playerdata/base/g_pm_id") self.assert_path(resp, "response/playerdata/base/name") self.assert_path(resp, "response/playerdata/base/my_best") self.assert_path(resp, "response/playerdata/base/latest_music") self.assert_path(resp, "response/playerdata/base/clear_medal") self.assert_path(resp, "response/playerdata/base/clear_medal_sub") self.assert_path(resp, "response/playerdata/player_card") self.assert_path(resp, "response/playerdata/player_card_ex") self.assert_path(resp, "response/playerdata/hiscore") self.assert_path(resp, "response/playerdata/netvs") self.assert_path(resp, "response/playerdata/sp_data") self.assert_path(resp, "response/playerdata/reflec_data") self.assert_path(resp, "response/playerdata/navigate")
def handle_player23_read_request(self, request: Node) -> Node: refid = request.child_value('ref_id') root = self.get_profile_by_refid(refid, self.OLD_PROFILE_FALLTHROUGH) if root is None: root = Node.void('player23') root.add_child(Node.s8('result', 2)) return root
def verify_game_play_e(self, location: str, refid: str) -> None: call = self.call_node() game = Node.void('game_3') call.add_child(game) game.set_attribute('ver', '0') game.set_attribute('method', 'play_e') game.add_child(Node.string('dataid', refid)) game.add_child(Node.s8('mode', 2)) game.add_child(Node.s16('track_num', 3)) game.add_child(Node.s32('s_coin', 0)) game.add_child(Node.s32('s_paseli', 0)) game.add_child(Node.s16('blaster_count', 0)) game.add_child(Node.s16('blaster_cartridge', 0)) game.add_child(Node.string('locid', location)) game.add_child(Node.u16('drop_frame', 396)) game.add_child(Node.u16('drop_frame_max', 396)) game.add_child(Node.u16('drop_count', 1)) game.add_child(Node.string('etc', 'StoryID:0,StoryPrg:0,PrgPrm:0')) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/game_3")
def verify_game_get(self) -> None: call = self.call_node() # Construct node game = Node.void('game') call.add_child(game) game.set_attribute('location_id', 'JP-1') game.set_attribute('method', 'get') game.add_child(Node.s8('event', 0)) # Swap with server resp = self.exchange('pnm20/game', call) # Verify that response is correct self.assert_path(resp, "response/game") for name in [ 'ir_phase', 'music_open_phase', 'collabo_phase', 'personal_event_phase', 'shop_event_phase', 'netvs_phase', 'card_phase', 'other_phase', 'local_matching_enable', 'n_matching_sec', 'l_matching_sec', 'is_check_cpu', 'week_no', ]: node = resp.child('game').child(name) if node is None: raise Exception( 'Missing node \'{}\' in response!'.format(name)) if node.data_type != 's32': raise Exception( 'Node \'{}\' has wrong data type!'.format(name)) sel_ranking = resp.child('game').child('sel_ranking') up_ranking = resp.child('game').child('up_ranking') for nodepair in [('sel_ranking', sel_ranking), ('up_ranking', up_ranking)]: name = nodepair[0] node = nodepair[1] if node is None: raise Exception( 'Missing node \'{}\' in response!'.format(name)) if node.data_type != 's16': raise Exception( 'Node \'{}\' has wrong data type!'.format(name)) if not node.is_array: raise Exception('Node \'{}\' is not array!'.format(name)) if len(node.value) != 5: raise Exception( 'Node \'{}\' is wrong array length!'.format(name))
def verify_game_active(self) -> None: call = self.call_node() # Construct node game = Node.void('game') call.add_child(game) game.set_attribute('method', 'active') # Add what Pop'n 20 would add after full unlock game.set_attribute('method', 'active') game.add_child(Node.s8('event', 0)) game.add_child(Node.s8('card_use', 0)) game.add_child(Node.string('name', '')) game.add_child(Node.string('location_id', 'JP-1')) game.add_child(Node.string('shop_name_facility', '.')) game.add_child(Node.s8('pref', 50)) game.add_child(Node.string('shop_addr', '127.0.0.1 10000')) game.add_child(Node.string('shop_name', '')) game.add_child(Node.string('eacoin_price', '200,260,200,200')) game.add_child(Node.s8('eacoin_available', 1)) game.add_child(Node.s8('dipswitch', 0)) game.add_child(Node.u8('max_stage', 1)) game.add_child(Node.u8('difficult', 4)) game.add_child(Node.s8('free_play', 1)) game.add_child(Node.s8('event_mode', 0)) game.add_child(Node.s8('popn_card', 0)) game.add_child(Node.s16('close_time', -1)) game.add_child(Node.s32('game_phase', 2)) game.add_child(Node.s32('net_phase', 0)) game.add_child(Node.s32('event_phase', 0)) game.add_child(Node.s32('card_phase', 3)) game.add_child(Node.s32('ir_phase', 0)) game.add_child(Node.u8('coin_to_credit', 1)) game.add_child(Node.u8('credit_to_start', 2)) game.add_child(Node.s32('sound_attract', 100)) game.add_child(Node.s8('bookkeeping', 0)) game.add_child(Node.s8('set_clock', 0)) game.add_child(Node.s32('local_clock_sec', 80011)) game.add_child(Node.s32('revision_sec', 0)) game.add_child(Node.string('crash_log', '')) # Swap with server resp = self.exchange('pnm20/game', call) # Verify that response is correct self.assert_path(resp, "response/game/@status")
def handle_player23_new_request(self, request: Node) -> Node: refid = request.child_value('ref_id') name = request.child_value('name') root = self.new_profile_by_refid(refid, name) if root is None: root = Node.void('player23') root.add_child(Node.s8('result', 2)) return root
def format_conversion(self, userid: UserID, profile: ValidatedDict) -> Node: # Circular import, ugh from bemani.backend.popn.lapistoria import PopnMusicLapistoria root = Node.void('playerdata') root.add_child(Node.string('name', profile.get_str('name', 'なし'))) root.add_child(Node.s16('chara', profile.get_int('chara', -1))) root.add_child(Node.s32('option', profile.get_int('option', 0))) root.add_child(Node.s8('result', 1)) 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 = score.data.get_int('medal') music = Node.void('music') root.add_child(music) music.add_child(Node.s16('music_num', score.id)) music.add_child(Node.u8('sheet_num', { self.CHART_TYPE_EASY: PopnMusicLapistoria.GAME_CHART_TYPE_EASY, self.CHART_TYPE_NORMAL: PopnMusicLapistoria.GAME_CHART_TYPE_NORMAL, self.CHART_TYPE_HYPER: PopnMusicLapistoria.GAME_CHART_TYPE_HYPER, self.CHART_TYPE_EX: PopnMusicLapistoria.GAME_CHART_TYPE_EX, }[score.chart])) music.add_child(Node.s16('cnt', score.plays)) music.add_child(Node.s32('score', 0)) music.add_child(Node.u8('clear_type', 0)) music.add_child(Node.s32('old_score', points)) music.add_child(Node.u8('old_clear_type', { self.PLAY_MEDAL_CIRCLE_FAILED: PopnMusicLapistoria.GAME_PLAY_MEDAL_CIRCLE_FAILED, self.PLAY_MEDAL_DIAMOND_FAILED: PopnMusicLapistoria.GAME_PLAY_MEDAL_DIAMOND_FAILED, self.PLAY_MEDAL_STAR_FAILED: PopnMusicLapistoria.GAME_PLAY_MEDAL_STAR_FAILED, self.PLAY_MEDAL_EASY_CLEAR: PopnMusicLapistoria.GAME_PLAY_MEDAL_EASY_CLEAR, self.PLAY_MEDAL_CIRCLE_CLEARED: PopnMusicLapistoria.GAME_PLAY_MEDAL_CIRCLE_CLEARED, self.PLAY_MEDAL_DIAMOND_CLEARED: PopnMusicLapistoria.GAME_PLAY_MEDAL_DIAMOND_CLEARED, self.PLAY_MEDAL_STAR_CLEARED: PopnMusicLapistoria.GAME_PLAY_MEDAL_STAR_CLEARED, self.PLAY_MEDAL_CIRCLE_FULL_COMBO: PopnMusicLapistoria.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO, self.PLAY_MEDAL_DIAMOND_FULL_COMBO: PopnMusicLapistoria.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO, self.PLAY_MEDAL_STAR_FULL_COMBO: PopnMusicLapistoria.GAME_PLAY_MEDAL_STAR_FULL_COMBO, self.PLAY_MEDAL_PERFECT: PopnMusicLapistoria.GAME_PLAY_MEDAL_PERFECT, }[medal])) return root
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_player24_start(self, ref_id: str, loc: str) -> None: call = self.call_node() # Construct node player24 = Node.void('player24') call.add_child(player24) player24.set_attribute('loc_id', loc) player24.set_attribute('ref_id', ref_id) player24.set_attribute('method', 'start') player24.set_attribute('start_type', '0') pcb_card = Node.void('pcb_card') player24.add_child(pcb_card) pcb_card.add_child(Node.s8('card_enable', 1)) pcb_card.add_child(Node.s8('card_soldout', 0)) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.__verify_common('player24', resp)
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 verify_rb4pzlcmt_write(self, loc: str, extid: int) -> None: call = self.call_node() info = Node.void('info') info.set_attribute('method', 'rb4pzlcmt_write') info.add_child(Node.s32('uid', extid)) info.add_child(Node.string('name', self.NAME)) info.add_child(Node.s16('icon', 0)) info.add_child(Node.s8('bln', 0)) info.add_child(Node.string('lid', loc)) info.add_child(Node.s8('pref', 51)) info.add_child(Node.s32('time', int(time.time()))) info.add_child(Node.string('comment', 'アメ〜〜!')) info.add_child(Node.bool('is_tweet', False)) call.add_child(info) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/info/@status")
def handle_player_rb5_player_read_score_request(self, request: Node) -> Node: refid = request.child_value('rid') userid = self.data.remote.user.from_refid(self.game, self.version, refid) if userid is None: scores: List[Score] = [] else: scores = self.data.remote.music.get_scores(self.game, self.version, userid) root = Node.void('player') pdata = Node.void('pdata') root.add_child(pdata) record = Node.void('record') pdata.add_child(record) for score in scores: rec = Node.void('rec') record.add_child(rec) rec.add_child(Node.s16('mid', score.id)) rec.add_child(Node.s8('ntgrd', score.chart)) rec.add_child(Node.s32('pc', score.plays)) rec.add_child(Node.s8('ct', self._db_to_game_clear_type(score.data.get_int('clear_type')))) rec.add_child(Node.s16('ar', score.data.get_int('achievement_rate'))) rec.add_child(Node.s16('scr', score.points)) rec.add_child(Node.s16('ms', score.data.get_int('miss_count'))) rec.add_child(Node.s16( 'param', self._db_to_game_combo_type(score.data.get_int('combo_type')) + score.data.get_int('param'), )) rec.add_child(Node.s32('bscrt', score.timestamp)) rec.add_child(Node.s32('bart', score.data.get_int('best_achievement_rate_time'))) rec.add_child(Node.s32('bctt', score.data.get_int('best_clear_type_time'))) rec.add_child(Node.s32('bmst', score.data.get_int('best_miss_count_time'))) rec.add_child(Node.s32('time', score.data.get_int('last_played_time'))) rec.add_child(Node.s32('k_flag', score.data.get_int('kflag'))) return root
def verify_player24_read_score( self, ref_id: str) -> Dict[str, Dict[int, Dict[int, int]]]: call = self.call_node() # Construct node player24 = Node.void('player24') call.add_child(player24) player24.set_attribute('method', 'read_score') player24.add_child(Node.string('ref_id', ref_id)) player24.add_child(Node.s8('pref', 51)) # Swap with server resp = self.exchange('', call) # Verify defaults self.assert_path(resp, "response/player24/@status") # Grab scores scores: Dict[int, Dict[int, int]] = {} medals: Dict[int, Dict[int, int]] = {} ranks: Dict[int, Dict[int, int]] = {} for child in resp.child('player24').children: if child.name != 'music': continue musicid = child.child_value('music_num') chart = child.child_value('sheet_num') score = child.child_value('score') medal = child.child_value('clear_type') rank = child.child_value('clear_rank') if musicid not in scores: scores[musicid] = {} if musicid not in medals: medals[musicid] = {} if musicid not in ranks: ranks[musicid] = {} scores[musicid][chart] = score medals[musicid][chart] = medal ranks[musicid][chart] = rank return { 'scores': scores, 'medals': medals, 'ranks': ranks, }
def verify_gametop_get_mdata(self, jid: int) -> Dict[str, List[Dict[str, Any]]]: call = self.call_node() # Construct node gametop = Node.void('gametop') call.add_child(gametop) gametop.set_attribute('method', 'get_mdata') retry = Node.s32('retry', 0) gametop.add_child(retry) data = Node.void('data') gametop.add_child(data) player = Node.void('player') data.add_child(player) player.add_child(Node.s32('jid', jid)) # Technically the game sends this same packet 3 times, one with # each value 1, 2, 3 here. Unclear why, but we won't emulate it. player.add_child(Node.s8('mdata_ver', 1)) player.add_child(Node.bool('rival', False)) # Swap with server resp = self.exchange('', call) # Parse out scores self.assert_path(resp, "response/gametop/data/player/mdata_list") ret = {} for musicdata in resp.child('gametop/data/player/mdata_list').children: if musicdata.name != 'musicdata': raise Exception('Unexpected node in playdata!') music_id = musicdata.attribute('music_id') scores_by_chart: List[Dict[str, int]] = [{}, {}, {}] def extract_cnts(name: str, val: List[int]) -> None: scores_by_chart[0][name] = val[0] scores_by_chart[1][name] = val[1] scores_by_chart[2][name] = val[2] extract_cnts('plays', musicdata.child_value('play_cnt')) extract_cnts('clears', musicdata.child_value('clear_cnt')) extract_cnts('full_combos', musicdata.child_value('fc_cnt')) extract_cnts('excellents', musicdata.child_value('ex_cnt')) extract_cnts('score', musicdata.child_value('score')) extract_cnts('medal', musicdata.child_value('clear')) ret[music_id] = scores_by_chart return ret
def verify_game_active(self) -> None: call = self.call_node() # Construct node game = Node.void('game') call.add_child(game) game.set_attribute('method', 'active') # Add minimum amount of stuff so server accepts game.add_child(Node.s8('event', 0)) # Swap with server resp = self.exchange('pnm20/game', call) # Verify that response is correct self.assert_path(resp, "response/game/@status")
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') call.add_child(game) game.set_attribute('method', 'sv4_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.u8('ars_option', 0)) game.add_child(Node.u8('notes_option', 0)) game.add_child(Node.u8('early_late_disp', 0)) game.add_child(Node.s32('draw_adjust', 0)) game.add_child(Node.u8('eff_c_left', 0)) game.add_child(Node.u8('eff_c_right', 1)) 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) game.add_child(Node.s16('skill_name_id', 0)) game.add_child(Node.s16('skill_base_id', 0)) game.add_child(Node.s16('skill_name', 0)) 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', 1)) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/game")
def verify_player24_new(self, ref_id: str) -> None: call = self.call_node() # Construct node player24 = Node.void('player24') call.add_child(player24) player24.set_attribute('method', 'new') player24.add_child(Node.string('ref_id', ref_id)) player24.add_child(Node.string('name', self.NAME)) player24.add_child(Node.s8('pref', 51)) # Swap with server resp = self.exchange('', call) # Verify nodes self.__verify_profile(resp)
def format_conversion(self, userid: UserID, profile: ValidatedDict) -> Node: root = Node.void('player23') root.add_child(Node.string('name', profile.get_str('name', 'なし'))) root.add_child(Node.s16('chara', profile.get_int('chara', -1))) root.add_child(Node.s8('result', 1)) scores = self.data.remote.music.get_scores(self.game, self.version, userid) for score in scores: # 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 = score.data.get_int('medal') music = Node.void('music') root.add_child(music) music.add_child(Node.s16('music_num', score.id)) music.add_child(Node.u8('sheet_num', { self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY, self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL, self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER, self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX, }[score.chart])) music.add_child(Node.s32('score', points)) music.add_child(Node.u8('clear_type', { 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_EASY_CLEAR, 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, }[medal])) music.add_child(Node.s16('cnt', score.plays)) return root
def verify_player22_new(self, ref_id: str) -> None: call = self.call_node() # Construct node player22 = Node.void('player22') call.add_child(player22) player22.set_attribute('method', 'new') player22.add_child(Node.string('ref_id', ref_id)) player22.add_child(Node.string('name', self.NAME)) player22.add_child(Node.string('shop_name', '')) player22.add_child(Node.s8('pref', 51)) # Swap with server resp = self.exchange('', call) # Verify nodes that cause crashes if they don't exist self.assert_path(resp, "response/player22/account")
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_player_rb5_player_read_rival_score_5(self, extid: int) -> None: call = self.call_node() player = Node.void('player') player.set_attribute('method', 'rb5_player_read_rival_score_5') player.add_child(Node.s32('uid', extid)) player.add_child(Node.s32('music_id', 6)) player.add_child(Node.s8('note_grade', 0)) call.add_child(player) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/player/@status") # Verify that we got a score if the extid is nonzero if extid != 0: self.assert_path(resp, "response/player/player_select_score/user_id") self.assert_path(resp, "response/player/player_select_score/name") self.assert_path(resp, "response/player/player_select_score/m_score") self.assert_path( resp, "response/player/player_select_score/m_scoreTime") self.assert_path(resp, "response/player/player_select_score/m_iconID") if resp.child_value( 'player/player_select_score/name') != self.NAME: raise Exception( f'Invalid name {resp.child_value("player/player_select_score/name")} returned on score read!' ) if resp.child_value('player/player_select_score/user_id') != extid: raise Exception( f'Invalid name {resp.child_value("player/player_select_score/user_id")} returned on score read!' )
def verify_player_rb5_player_read_rival_ranking_data_5(self, extid: int) -> None: call = self.call_node() player = Node.void('player') player.set_attribute('method', 'rb5_player_read_rival_ranking_data_5') player.add_child(Node.s32('uid', extid)) player.add_child(Node.s8('mgid', -1)) call.add_child(player) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/player/rival_data/rl/uid") self.assert_path(resp, "response/player/rival_data/rl/nm") self.assert_path(resp, "response/player/rival_data/rl/ic") self.assert_path(resp, "response/player/rival_data/rl/sl/mid") self.assert_path(resp, "response/player/rival_data/rl/sl/m") self.assert_path(resp, "response/player/rival_data/rl/sl/t") if resp.child_value('player/rival_data/rl/nm') != self.NAME: raise Exception( f'Invalid name {resp.child_value("player/rival_data/rl/nm")} returned on rival ranking read!' )
def verify_event_w_add_comment(self, loc: str, extid: int) -> None: call = self.call_node() event_w = Node.void('event_w') call.add_child(event_w) event_w.set_attribute('method', 'add_comment') event_w.add_child(Node.s32('uid', extid)) event_w.add_child(Node.string('p_name', self.NAME)) event_w.add_child(Node.s32('exp', 0)) event_w.add_child(Node.s32('customize', 0)) event_w.add_child(Node.s32('tid', 0)) event_w.add_child(Node.string('t_name', '')) event_w.add_child(Node.string('lid', loc)) event_w.add_child(Node.string('s_name', '')) event_w.add_child(Node.s8('pref', 51)) event_w.add_child(Node.s32('time', Time.now())) event_w.add_child(Node.string('comment', 'アメ〜〜!')) event_w.add_child(Node.bool('is_tweet', False)) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/event_w/@status")
def verify_playerdata_new(self, ref_id: str) -> None: call = self.call_node() # Construct node playerdata = Node.void('playerdata') call.add_child(playerdata) playerdata.set_attribute('method', 'new') playerdata.add_child(Node.string('ref_id', ref_id)) playerdata.add_child(Node.string('name', self.NAME)) playerdata.add_child(Node.string('shop_name', '')) playerdata.add_child(Node.s8('pref', 51)) playerdata.add_child(Node.s8('gakuen', 2)) playerdata.add_child(Node.s8('zoo', 1)) playerdata.add_child(Node.s8('floor_infection', 1)) playerdata.add_child(Node.s8('triple_journey', 1)) playerdata.add_child(Node.s8('baseball', 1)) # Swap with server resp = self.exchange('pnm20/playerdata', call) # Verify nodes that cause crashes if they don't exist self.assert_path(resp, "response/playerdata/base")
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_rb5_player_write_5( self, refid: str, loc: str, scores: List[Dict[str, int]] = [], rivals: List[int] = [], mycourse: Optional[List[Dict[str, int]]] = None, ) -> int: call = self.call_node() player = Node.void('player') call.add_child(player) player.set_attribute('method', 'rb5_player_write_5') 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('wmode', 0)) account.add_child(Node.u8('gmode', 0)) account.add_child(Node.s16('ver', 0)) account.add_child(Node.bool('pp', False)) account.add_child(Node.bool('ps', False)) account.add_child(Node.bool('continue', False)) account.add_child(Node.bool('firstfree', False)) 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('mg', 0)) base.add_child(Node.s32('ap', 0)) base.add_child(Node.s32('uattr', 0)) base.add_child(Node.s32('money', 0)) base.add_child(Node.bool('is_tut', False)) base.add_child(Node.s32('class', -1)) base.add_child(Node.s32('class_ar', 0)) base.add_child(Node.s32('skill_point', 0)) 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', 1)) log.add_child(Node.s8('mt', 0)) log.add_child(Node.s8('rt', 0)) log.add_child(Node.s8('ct', score['clear_type'])) log.add_child(Node.s16('param', score['combo_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.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)) log.add_child(Node.s16('g_gauge', 5000)) log.add_child(Node.s32('k_flag', 0)) index = index + 1 rivalnode = Node.void('rival') pdata.add_child(rivalnode) for rival in rivals: r = Node.void('r') rivalnode.add_child(r) r.add_child(Node.s32('id', rival)) r.add_child(Node.s8('regist_type', 3)) if mycourse is not None: mycoursenode = Node.void('mycourse') pdata.add_child(mycoursenode) mycoursenode.add_child(Node.s16('mycourse_id', 1)) mycoursenode.add_child( Node.s32('music_id_1', mycourse[0]['music_id'])) mycoursenode.add_child( Node.s16('note_grade_1', mycourse[0]['note_grade'])) mycoursenode.add_child(Node.s32('score_1', mycourse[0]['score'])) mycoursenode.add_child( Node.s32('music_id_2', mycourse[1]['music_id'])) mycoursenode.add_child( Node.s16('note_grade_2', mycourse[1]['note_grade'])) mycoursenode.add_child(Node.s32('score_2', mycourse[1]['score'])) mycoursenode.add_child( Node.s32('music_id_3', mycourse[2]['music_id'])) mycoursenode.add_child( Node.s16('note_grade_3', mycourse[2]['note_grade'])) mycoursenode.add_child(Node.s32('score_3', mycourse[2]['score'])) mycoursenode.add_child( Node.s32('music_id_4', mycourse[3]['music_id'])) mycoursenode.add_child( Node.s16('note_grade_4', mycourse[3]['note_grade'])) mycoursenode.add_child(Node.s32('score_4', mycourse[3]['score'])) mycoursenode.add_child(Node.s32('insert_time', -1)) mycoursenode.add_child(Node.s32('def_music_id_1', -1)) mycoursenode.add_child(Node.s16('def_note_grade_1', -1)) mycoursenode.add_child(Node.s32('def_music_id_2', -1)) mycoursenode.add_child(Node.s16('def_note_grade_2', -1)) mycoursenode.add_child(Node.s32('def_music_id_3', -1)) mycoursenode.add_child(Node.s16('def_note_grade_3', -1)) mycoursenode.add_child(Node.s32('def_music_id_4', -1)) mycoursenode.add_child(Node.s16('def_note_grade_4', -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')
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: root = Node.void('player22') # Result root.add_child(Node.s8('result', 0)) # Set up account account = Node.void('account') root.add_child(account) account.add_child(Node.string('name', profile.get_str('name', 'なし'))) account.add_child( Node.string('g_pm_id', ID.format_extid(profile.get_int('extid')))) account.add_child(Node.s8('tutorial', profile.get_int('tutorial', -1))) account.add_child( Node.s16('read_news', profile.get_int('read_news', 0))) account.add_child(Node.s8('staff', 0)) account.add_child(Node.s8('is_conv', 0)) account.add_child(Node.s16('item_type', 0)) account.add_child(Node.s16('item_id', 0)) account.add_child( Node.s16_array('license_data', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])) # 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 account.add_child(Node.u8('active_fr_num', 0)) # TODO: Hook up rivals code? account.add_child( Node.s16('total_play_cnt', statistics.get_int('total_plays', 0))) account.add_child(Node.s16('today_play_cnt', today_count)) account.add_child( Node.s16('consecutive_days', statistics.get_int('consecutive_days', 0))) account.add_child( Node.s16('total_days', statistics.get_int('total_days', 0))) account.add_child(Node.s16('interval_day', 0)) # Add scores section last_played = [ x[0] for x in self.data.local.music.get_last_played( self.game, self.version, userid, 5) ] most_played = [ x[0] for x in self.data.local.music.get_most_played( self.game, self.version, userid, 10) ] while len(last_played) < 5: last_played.append(-1) while len(most_played) < 10: most_played.append(-1) account.add_child(Node.s16_array('my_best', most_played)) account.add_child(Node.s16_array('latest_music', last_played)) scores = self.data.remote.music.get_scores(self.game, self.version, userid) for score in scores: # 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 = score.data.get_int('medal') music = Node.void('music') root.add_child(music) music.add_child(Node.s16('music_num', score.id)) music.add_child( Node.u8( 'sheet_num', { self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY, self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL, self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER, self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX, }[score.chart])) music.add_child(Node.s16('cnt', score.plays)) music.add_child(Node.s32('score', points)) music.add_child( Node.u8( 'clear_type', { 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_EASY_CLEAR, 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, }[medal])) music.add_child(Node.s32('old_score', 0)) music.add_child(Node.u8('old_clear_type', 0)) # 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.u32('netvs_play_cnt', 0)) for dialog in [0, 1, 2, 3, 4, 5]: # TODO: Configure this, maybe? netvs.add_child(Node.string('dialog', f'dialog#{dialog}')) # Set up config config = Node.void('config') root.add_child(config) config.add_child(Node.u8('mode', profile.get_int('mode', 0))) config.add_child(Node.s16('chara', profile.get_int('chara', -1))) config.add_child(Node.s16('music', profile.get_int('music', -1))) config.add_child(Node.u8('sheet', profile.get_int('sheet', 0))) config.add_child(Node.s8('category', profile.get_int('category', 1))) config.add_child( Node.s8('sub_category', profile.get_int('sub_category', -1))) config.add_child( Node.s8('chara_category', profile.get_int('chara_category', -1))) config.add_child(Node.s16('story_id', profile.get_int('story_id', -1))) config.add_child( Node.s16('course_id', profile.get_int('course_id', -1))) config.add_child( Node.s8('course_folder', profile.get_int('course_folder', -1))) config.add_child( Node.s8('story_folder', profile.get_int('story_folder', -1))) config.add_child( Node.s8('ms_banner_disp', profile.get_int('ms_banner_disp'))) config.add_child( Node.s8('ms_down_info', profile.get_int('ms_down_info'))) config.add_child( Node.s8('ms_side_info', profile.get_int('ms_side_info'))) config.add_child( Node.s8('ms_raise_type', profile.get_int('ms_raise_type'))) config.add_child(Node.s8('ms_rnd_type', profile.get_int('ms_rnd_type'))) # Set up option option_dict = profile.get_dict('option') option = Node.void('option') root.add_child(option) option.add_child( Node.s16('hispeed', option_dict.get_int('hispeed', 10))) option.add_child(Node.u8('popkun', option_dict.get_int('popkun', 0))) option.add_child( Node.bool('hidden', option_dict.get_bool('hidden', False))) option.add_child( Node.s16('hidden_rate', option_dict.get_int('hidden_rate', -1))) option.add_child( Node.bool('sudden', option_dict.get_bool('sudden', False))) option.add_child( Node.s16('sudden_rate', option_dict.get_int('sudden_rate', -1))) option.add_child(Node.s8('randmir', option_dict.get_int('randmir', 0))) option.add_child( Node.s8('gauge_type', option_dict.get_int('gauge_type', 0))) option.add_child(Node.u8('ojama_0', option_dict.get_int('ojama_0', 0))) option.add_child(Node.u8('ojama_1', option_dict.get_int('ojama_1', 0))) option.add_child( Node.bool('forever_0', option_dict.get_bool('forever_0', False))) option.add_child( Node.bool('forever_1', option_dict.get_bool('forever_1', False))) option.add_child( Node.bool('full_setting', option_dict.get_bool('full_setting', False))) # Set up info info = Node.void('info') root.add_child(info) info.add_child(Node.u16('ep', profile.get_int('ep', 0))) info.add_child(Node.u16('ap', profile.get_int('ap', 0))) # Set up custom_cate custom_cate = Node.void('custom_cate') root.add_child(custom_cate) custom_cate.add_child(Node.s8('valid', 0)) custom_cate.add_child(Node.s8('lv_min', -1)) custom_cate.add_child(Node.s8('lv_max', -1)) custom_cate.add_child(Node.s8('medal_min', -1)) custom_cate.add_child(Node.s8('medal_max', -1)) custom_cate.add_child(Node.s8('friend_no', -1)) custom_cate.add_child(Node.s8('score_flg', -1)) # Set up customize customize_dict = profile.get_dict('customize') customize = Node.void('customize') root.add_child(customize) customize.add_child( Node.u16('effect', customize_dict.get_int('effect'))) customize.add_child( Node.u16('hukidashi', customize_dict.get_int('hukidashi'))) customize.add_child(Node.u16('font', customize_dict.get_int('font'))) customize.add_child( Node.u16('comment_1', customize_dict.get_int('comment_1'))) customize.add_child( Node.u16('comment_2', customize_dict.get_int('comment_2'))) # Set up achievements achievements = self.data.local.user.get_achievements( self.game, self.version, userid) for achievement in achievements: if achievement.type == 'item': itemtype = achievement.data.get_int('type') param = achievement.data.get_int('param') item = Node.void('item') root.add_child(item) item.add_child(Node.u8('type', itemtype)) item.add_child(Node.u16('id', achievement.id)) item.add_child(Node.u16('param', param)) item.add_child(Node.bool('is_new', False)) elif achievement.type == 'achievement': count = achievement.data.get_int('count') ach_node = Node.void('achievement') root.add_child(ach_node) ach_node.add_child(Node.u8('type', achievement.id)) ach_node.add_child(Node.u32('count', count)) elif achievement.type == 'chara': friendship = achievement.data.get_int('friendship') chara = Node.void('chara_param') root.add_child(chara) chara.add_child(Node.u16('chara_id', achievement.id)) chara.add_child(Node.u16('friendship', friendship)) elif achievement.type == 'story': chapter = achievement.data.get_int('chapter') gauge = achievement.data.get_int('gauge') cleared = achievement.data.get_bool('cleared') clear_chapter = achievement.data.get_int('clear_chapter') story = Node.void('story') root.add_child(story) story.add_child(Node.u32('story_id', achievement.id)) story.add_child(Node.u32('chapter_id', chapter)) story.add_child(Node.u16('gauge_point', gauge)) story.add_child(Node.bool('is_cleared', cleared)) story.add_child(Node.u32('clear_chapter', clear_chapter)) 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