def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: game = Node.void('game') # Generic profile stuff game.add_child(Node.string('name', profile.get_str('name'))) game.add_child( Node.string('code', ID.format_extid(profile.get_int('extid')))) game.add_child(Node.u32('gamecoin_packet', profile.get_int('packet'))) game.add_child(Node.u32('gamecoin_block', profile.get_int('block'))) game.add_child(Node.u32('exp_point', profile.get_int('exp'))) game.add_child(Node.u32('m_user_cnt', profile.get_int('m_user_cnt'))) game_config = self.get_game_config() if game_config.get_bool('force_unlock_cards'): game.add_child(Node.bool_array('have_item', [True] * 512)) else: game.add_child( Node.bool_array( 'have_item', [x > 0 for x in profile.get_int_array('have_item', 512)])) if game_config.get_bool('force_unlock_songs'): game.add_child(Node.bool_array('have_note', [True] * 512)) else: game.add_child( Node.bool_array( 'have_note', [x > 0 for x in profile.get_int_array('have_note', 512)])) # Last played stuff lastdict = profile.get_dict('last') last = Node.void('last') game.add_child(last) last.set_attribute('music_id', str(lastdict.get_int('music_id'))) last.set_attribute('music_type', str(lastdict.get_int('music_type'))) last.set_attribute('sort_type', str(lastdict.get_int('sort_type'))) last.set_attribute('headphone', str(lastdict.get_int('headphone'))) last.set_attribute('hispeed', str(lastdict.get_int('hispeed'))) last.set_attribute('appeal_id', str(lastdict.get_int('appeal_id'))) last.set_attribute('frame0', str(lastdict.get_int('frame0'))) last.set_attribute('frame1', str(lastdict.get_int('frame1'))) last.set_attribute('frame2', str(lastdict.get_int('frame2'))) last.set_attribute('frame3', str(lastdict.get_int('frame3'))) last.set_attribute('frame4', str(lastdict.get_int('frame4'))) return game
def 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 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 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 format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: root = Node.void('game') # Look up play stats we bridge to every mix play_stats = self.get_play_statistics(userid) # Basic game settings root.add_child(Node.string('seq', '')) root.add_child(Node.u32('code', profile.get_int('extid'))) root.add_child(Node.string('name', profile.get_str('name'))) root.add_child(Node.u8('area', profile.get_int('area', 51))) root.add_child(Node.u32('cnt_s', play_stats.get_int('single_plays'))) root.add_child(Node.u32('cnt_d', play_stats.get_int('double_plays'))) root.add_child(Node.u32('cnt_b', play_stats.get_int('battle_plays'))) # This could be wrong, its a guess root.add_child(Node.u32('cnt_m0', play_stats.get_int('cnt_m0'))) root.add_child(Node.u32('cnt_m1', play_stats.get_int('cnt_m1'))) root.add_child(Node.u32('cnt_m2', play_stats.get_int('cnt_m2'))) root.add_child(Node.u32('cnt_m3', play_stats.get_int('cnt_m3'))) root.add_child(Node.u32('cnt_m4', play_stats.get_int('cnt_m4'))) root.add_child(Node.u32('cnt_m5', play_stats.get_int('cnt_m5'))) root.add_child(Node.u32('exp', play_stats.get_int('exp'))) root.add_child(Node.u32('exp_o', profile.get_int('exp_o'))) root.add_child(Node.u32('star', profile.get_int('star'))) root.add_child(Node.u32('star_c', profile.get_int('star_c'))) root.add_child(Node.u8('combo', profile.get_int('combo', 0))) root.add_child(Node.u8('timing_diff', profile.get_int('early_late', 0))) # Character stuff chara = Node.void('chara') root.add_child(chara) chara.set_attribute('my', str(profile.get_int('chara', 30))) root.add_child(Node.u16_array('chara_opt', profile.get_int_array('chara_opt', 96, [208] * 96))) # Drill rankings if 'title_gr' in profile: title_gr = Node.void('title_gr') root.add_child(title_gr) title_grdict = profile.get_dict('title_gr') if 't' in title_grdict: title_gr.set_attribute('t', str(title_grdict.get_int('t'))) if 's' in title_grdict: title_gr.set_attribute('s', str(title_grdict.get_int('s'))) if 'd' in title_grdict: title_gr.set_attribute('d', str(title_grdict.get_int('d'))) # Calorie mode if 'weight' in profile: workouts = self.data.local.user.get_time_based_achievements( self.game, self.version, userid, achievementtype='workout', since=Time.now() - Time.SECONDS_IN_DAY, ) total = sum([w.data.get_int('calories') for w in workouts]) workout = Node.void('workout') root.add_child(workout) workout.set_attribute('weight', str(profile.get_int('weight'))) workout.set_attribute('day', str(total)) workout.set_attribute('disp', '1') # Daily play counts last_play_date = play_stats.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 = play_stats.get_int('today_plays', 0) else: today_count = 0 daycount = Node.void('daycount') root.add_child(daycount) daycount.set_attribute('playcount', str(today_count)) # Daily combo stuff, unknown how this works dailycombo = Node.void('dailycombo') root.add_child(dailycombo) dailycombo.set_attribute('daily_combo', str(0)) dailycombo.set_attribute('daily_combo_lv', str(0)) # Last cursor settings last = Node.void('last') root.add_child(last) lastdict = profile.get_dict('last') last.set_attribute('rival1', str(lastdict.get_int('rival1', -1))) last.set_attribute('rival2', str(lastdict.get_int('rival2', -1))) last.set_attribute('rival3', str(lastdict.get_int('rival3', -1))) last.set_attribute('fri', str(lastdict.get_int('rival1', -1))) # This literally goes to the same memory in X3 last.set_attribute('style', str(lastdict.get_int('style'))) last.set_attribute('mode', str(lastdict.get_int('mode'))) last.set_attribute('cate', str(lastdict.get_int('cate'))) last.set_attribute('sort', str(lastdict.get_int('sort'))) last.set_attribute('mid', str(lastdict.get_int('mid'))) last.set_attribute('mtype', str(lastdict.get_int('mtype'))) last.set_attribute('cid', str(lastdict.get_int('cid'))) last.set_attribute('ctype', str(lastdict.get_int('ctype'))) last.set_attribute('sid', str(lastdict.get_int('sid'))) # Result stars result_star = Node.void('result_star') root.add_child(result_star) result_stars = profile.get_int_array('result_stars', 9) for i in range(9): result_star.set_attribute(f'slot{i + 1}', str(result_stars[i])) # Target stuff target = Node.void('target') root.add_child(target) target.set_attribute('flag', str(profile.get_int('target_flag'))) target.set_attribute('setnum', str(profile.get_int('target_setnum'))) # Groove gauge level-ups gr_s = Node.void('gr_s') root.add_child(gr_s) index = 1 for entry in profile.get_int_array('gr_s', 5): gr_s.set_attribute(f'gr{index}', str(entry)) index = index + 1 gr_d = Node.void('gr_d') root.add_child(gr_d) index = 1 for entry in profile.get_int_array('gr_d', 5): gr_d.set_attribute(f'gr{index}', str(entry)) index = index + 1 # Options in menus root.add_child(Node.s16_array('opt', profile.get_int_array('opt', 16))) root.add_child(Node.s16_array('opt_ex', profile.get_int_array('opt_ex', 16))) # Unlock flags root.add_child(Node.u8_array('flag', profile.get_int_array('flag', 256, [1] * 256))) # Ranking display? root.add_child(Node.u16_array('rank', profile.get_int_array('rank', 100))) # Rivals links = self.data.local.user.get_links(self.game, self.version, userid) for link in links: if link.type[:7] != 'friend_': continue pos = int(link.type[7:]) friend = self.get_profile(link.other_userid) play_stats = self.get_play_statistics(link.other_userid) if friend is not None: friendnode = Node.void('friend') root.add_child(friendnode) friendnode.set_attribute('pos', str(pos)) friendnode.set_attribute('vs', '0') friendnode.set_attribute('up', '0') friendnode.add_child(Node.u32('code', friend.get_int('extid'))) friendnode.add_child(Node.string('name', friend.get_str('name'))) friendnode.add_child(Node.u8('area', friend.get_int('area', 51))) friendnode.add_child(Node.u32('exp', play_stats.get_int('exp'))) friendnode.add_child(Node.u32('star', friend.get_int('star'))) # Drill rankings if 'title' in friend: title = Node.void('title') friendnode.add_child(title) titledict = friend.get_dict('title') if 't' in titledict: title.set_attribute('t', str(titledict.get_int('t'))) if 's' in titledict: title.set_attribute('s', str(titledict.get_int('s'))) if 'd' in titledict: title.set_attribute('d', str(titledict.get_int('d'))) if 'title_gr' in friend: title_gr = Node.void('title_gr') friendnode.add_child(title_gr) title_grdict = friend.get_dict('title_gr') if 't' in title_grdict: title_gr.set_attribute('t', str(title_grdict.get_int('t'))) if 's' in title_grdict: title_gr.set_attribute('s', str(title_grdict.get_int('s'))) if 'd' in title_grdict: title_gr.set_attribute('d', str(title_grdict.get_int('d'))) # Groove gauge level-ups gr_s = Node.void('gr_s') friendnode.add_child(gr_s) index = 1 for entry in friend.get_int_array('gr_s', 5): gr_s.set_attribute(f'gr{index}', str(entry)) index = index + 1 gr_d = Node.void('gr_d') friendnode.add_child(gr_d) index = 1 for entry in friend.get_int_array('gr_d', 5): gr_d.set_attribute(f'gr{index}', str(entry)) index = index + 1 # Play area areas = profile.get_int_array('play_area', 55) play_area = Node.void('play_area') root.add_child(play_area) for i in range(len(areas)): play_area.set_attribute(f'play_cnt{i}', str(areas[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 format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: root = Node.void('player23') # Mark this as a current profile root.add_child(Node.s8('result', 0)) # Account stuff account = Node.void('account') root.add_child(account) account.add_child( Node.string('g_pm_id', self.format_extid( profile.get_int('extid')))) # Eclale formats on its own account.add_child(Node.string('name', profile.get_str('name', 'なし'))) account.add_child(Node.s8('tutorial', profile.get_int('tutorial'))) account.add_child(Node.s16('area_id', profile.get_int('area_id'))) account.add_child(Node.s16('lumina', profile.get_int('lumina', 300))) account.add_child(Node.s16('read_news', profile.get_int('read_news'))) account.add_child( Node.bool('welcom_pack', profile.get_bool('welcom_pack'))) account.add_child( Node.s16_array('medal_set', profile.get_int_array('medal_set', 4))) account.add_child( Node.s16_array('nice', profile.get_int_array('nice', 30, [-1] * 30))) account.add_child( Node.s16_array( 'favorite_chara', profile.get_int_array('favorite_chara', 20, [-1] * 20))) account.add_child( Node.s16_array('special_area', profile.get_int_array('special_area', 8))) account.add_child( Node.s16_array( 'chocolate_charalist', profile.get_int_array('chocolate_charalist', 5, [-1] * 5))) account.add_child( Node.s16_array('teacher_setting', profile.get_int_array('teacher_setting', 10))) # Stuff we never change account.add_child(Node.s8('staff', 0)) account.add_child(Node.s16('item_type', 0)) account.add_child(Node.s16('item_id', 0)) account.add_child(Node.s8('is_conv', 0)) account.add_child(Node.bool('meteor_flg', True)) account.add_child(Node.s16_array('license_data', [-1] * 20)) # Add statistics section last_played = [ x[0] for x in self.data.local.music.get_last_played( self.game, self.music_version, userid, 5) ] most_played = [ x[0] for x in self.data.local.music.get_most_played( self.game, self.music_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)) # Number of rivals that are active for this version. links = self.data.local.user.get_links(self.game, self.version, userid) rivalcount = 0 for link in links: if link.type != 'rival': continue if not self.has_profile(link.other_userid): continue # This profile is valid. rivalcount += 1 account.add_child(Node.u8('active_fr_num', rivalcount)) # player 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 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)) # Set up info node info = Node.void('info') root.add_child(info) info.add_child(Node.u16('ep', profile.get_int('ep'))) # Set up last information config = Node.void('config') root.add_child(config) config.add_child(Node.u8('mode', profile.get_int('mode'))) 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'))) 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('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('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'))) # Player options option = Node.void('option') option_dict = profile.get_dict('option') root.add_child(option) option.add_child(Node.s16('hispeed', option_dict.get_int('hispeed'))) option.add_child(Node.u8('popkun', option_dict.get_int('popkun'))) option.add_child(Node.bool('hidden', option_dict.get_bool('hidden'))) option.add_child( Node.s16('hidden_rate', option_dict.get_int('hidden_rate'))) option.add_child(Node.bool('sudden', option_dict.get_bool('sudden'))) option.add_child( Node.s16('sudden_rate', option_dict.get_int('sudden_rate'))) option.add_child(Node.s8('randmir', option_dict.get_int('randmir'))) option.add_child( Node.s8('gauge_type', option_dict.get_int('gauge_type'))) option.add_child(Node.u8('ojama_0', option_dict.get_int('ojama_0'))) option.add_child(Node.u8('ojama_1', option_dict.get_int('ojama_1'))) option.add_child( Node.bool('forever_0', option_dict.get_bool('forever_0'))) option.add_child( Node.bool('forever_1', option_dict.get_bool('forever_1'))) option.add_child( Node.bool('full_setting', option_dict.get_bool('full_setting'))) option.add_child(Node.u8('judge', option_dict.get_int('judge'))) # Unknown custom category stuff? 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 achievements achievements = self.data.local.user.get_achievements( self.game, self.version, userid) for achievement in achievements: if achievement.type[:5] == 'item_': itemtype = int(achievement.type[5:]) param = achievement.data.get_int('param') is_new = achievement.data.get_bool('is_new') item = Node.void('item') root.add_child(item) # Type is the type of unlock/item. Type 0 is song unlock in Eclale. # In this case, the id is the song ID according to the game. Unclear # what the param is supposed to be, but i've seen 8 and 0. Might be # what chart is available? 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', is_new)) 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 == 'medal': level = achievement.data.get_int('level') exp = achievement.data.get_int('exp') set_count = achievement.data.get_int('set_count') get_count = achievement.data.get_int('get_count') medal = Node.void('medal') root.add_child(medal) medal.add_child(Node.s16('medal_id', achievement.id)) medal.add_child(Node.s16('level', level)) medal.add_child(Node.s32('exp', exp)) medal.add_child(Node.s32('set_count', set_count)) medal.add_child(Node.s32('get_count', get_count)) # Unknown customizations customize = Node.void('customize') root.add_child(customize) customize.add_child( Node.u16('effect_left', profile.get_int('effect_left'))) customize.add_child( Node.u16('effect_center', profile.get_int('effect_center'))) customize.add_child( Node.u16('effect_right', profile.get_int('effect_right'))) customize.add_child(Node.u16('hukidashi', profile.get_int('hukidashi'))) customize.add_child(Node.u16('comment_1', profile.get_int('comment_1'))) customize.add_child(Node.u16('comment_2', profile.get_int('comment_2'))) # NetVS section netvs = Node.void('netvs') root.add_child(netvs) netvs.add_child(Node.s16_array('record', [0] * 6)) netvs.add_child(Node.string('dialog', '')) netvs.add_child(Node.string('dialog', '')) netvs.add_child(Node.string('dialog', '')) netvs.add_child(Node.string('dialog', '')) netvs.add_child(Node.string('dialog', '')) netvs.add_child(Node.string('dialog', '')) netvs.add_child(Node.s8_array('ojama_condition', [0] * 74)) netvs.add_child(Node.s8_array('set_ojama', [0] * 3)) netvs.add_child(Node.s8_array('set_recommend', [0] * 3)) netvs.add_child(Node.u32('netvs_play_cnt', 0)) # Event stuff event = Node.void('event') root.add_child(event) event.add_child( Node.s16('enemy_medal', profile.get_int('event_enemy_medal'))) event.add_child(Node.s16('hp', profile.get_int('event_hp'))) # Stamp stuff stamp = Node.void('stamp') root.add_child(stamp) stamp.add_child(Node.s16('stamp_id', profile.get_int('stamp_id'))) stamp.add_child(Node.s16('cnt', profile.get_int('stamp_cnt'))) return root
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: statistics = self.get_play_statistics(userid) game_config = self.get_game_config() achievements = self.data.local.user.get_achievements( self.game, self.version, userid) 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: root = Node.void('game') # Look up play stats we bridge to every mix play_stats = self.get_play_statistics(userid) # Basic game settings root.add_child(Node.string('seq', '')) root.add_child(Node.u32('code', profile.get_int('extid'))) root.add_child(Node.string('name', profile.get_str('name'))) root.add_child(Node.u8('area', profile.get_int('area', 51))) root.add_child(Node.u32('cnt_s', play_stats.get_int('single_plays'))) root.add_child(Node.u32('cnt_d', play_stats.get_int('double_plays'))) root.add_child(Node.u32('cnt_b', play_stats.get_int('battle_plays'))) # This could be wrong, its a guess root.add_child(Node.u32('cnt_m0', play_stats.get_int('cnt_m0'))) root.add_child(Node.u32('cnt_m1', play_stats.get_int('cnt_m1'))) root.add_child(Node.u32('cnt_m2', play_stats.get_int('cnt_m2'))) root.add_child(Node.u32('cnt_m3', play_stats.get_int('cnt_m3'))) root.add_child(Node.u32('exp', play_stats.get_int('exp'))) root.add_child(Node.u32('exp_o', profile.get_int('exp_o'))) root.add_child(Node.u32('star', profile.get_int('star'))) root.add_child(Node.u32('star_c', profile.get_int('star_c'))) root.add_child(Node.u8('combo', profile.get_int('combo', 0))) root.add_child(Node.u8('timing_diff', profile.get_int('early_late', 0))) # Character stuff chara = Node.void('chara') root.add_child(chara) if 'chara' in profile: chara.set_attribute('my', str(profile.get_int('chara'))) root.add_child(Node.u8_array('chara_opt', profile.get_int_array('chara_opt', 96))) # Drill rankings if 'title' in profile: title = Node.void('title') root.add_child(title) titledict = profile.get_dict('title') if 't' in titledict: title.set_attribute('t', str(titledict.get_int('t'))) if 's' in titledict: title.set_attribute('s', str(titledict.get_int('s'))) if 'd' in titledict: title.set_attribute('d', str(titledict.get_int('d'))) if 'title_gr' in profile: title_gr = Node.void('title_gr') root.add_child(title_gr) title_grdict = profile.get_dict('title_gr') if 't' in title_grdict: title_gr.set_attribute('t', str(title_grdict.get_int('t'))) if 's' in title_grdict: title_gr.set_attribute('s', str(title_grdict.get_int('s'))) if 'd' in title_grdict: title_gr.set_attribute('d', str(title_grdict.get_int('d'))) # Event progrses if 'event' in profile: event = Node.void('event') root.add_child(event) event_dict = profile.get_dict('event') if 'diff_sum' in event_dict: event.set_attribute('diff_sum', str(event_dict.get_int('diff_sum'))) if 'welcome' in event_dict: event.set_attribute('welcome', str(event_dict.get_int('welcome'))) if 'e_flags' in event_dict: event.set_attribute('e_flags', str(event_dict.get_int('e_flags'))) if 'e_panel' in profile: e_panel = Node.void('e_panel') root.add_child(e_panel) e_panel_dict = profile.get_dict('e_panel') if 'play_id' in e_panel_dict: e_panel.set_attribute('play_id', str(e_panel_dict.get_int('play_id'))) e_panel.add_child(Node.u8_array('cell', e_panel_dict.get_int_array('cell', 24))) e_panel.add_child(Node.u8_array('panel_state', e_panel_dict.get_int_array('panel_state', 6))) if 'e_pix' in profile: e_pix = Node.void('e_pix') root.add_child(e_pix) e_pix_dict = profile.get_dict('e_pix') if 'max_distance' in e_pix_dict: e_pix.set_attribute('max_distance', str(e_pix_dict.get_int('max_distance'))) if 'max_planet' in e_pix_dict: e_pix.set_attribute('max_planet', str(e_pix_dict.get_int('max_planet'))) if 'total_distance' in e_pix_dict: e_pix.set_attribute('total_distance', str(e_pix_dict.get_int('total_distance'))) if 'total_planet' in e_pix_dict: e_pix.set_attribute('total_planet', str(e_pix_dict.get_int('total_planet'))) if 'border_character' in e_pix_dict: e_pix.set_attribute('border_character', str(e_pix_dict.get_int('border_character'))) if 'border_balloon' in e_pix_dict: e_pix.set_attribute('border_balloon', str(e_pix_dict.get_int('border_balloon'))) if 'border_music_aftr' in e_pix_dict: e_pix.set_attribute('border_music_aftr', str(e_pix_dict.get_int('border_music_aftr'))) if 'border_music_meii' in e_pix_dict: e_pix.set_attribute('border_music_meii', str(e_pix_dict.get_int('border_music_meii'))) if 'border_music_dirt' in e_pix_dict: e_pix.set_attribute('border_music_dirt', str(e_pix_dict.get_int('border_music_dirt'))) if 'flags' in e_pix_dict: e_pix.set_attribute('flags', str(e_pix_dict.get_int('flags'))) # Calorie mode if 'weight' in profile: workouts = self.data.local.user.get_time_based_achievements( self.game, self.version, userid, achievementtype='workout', since=Time.now() - Time.SECONDS_IN_DAY, ) total = sum([w.data.get_int('calories') for w in workouts]) workout = Node.void('workout') root.add_child(workout) workout.set_attribute('weight', str(profile.get_int('weight'))) workout.set_attribute('day', str(total)) workout.set_attribute('disp', '1') # Last cursor settings last = Node.void('last') root.add_child(last) lastdict = profile.get_dict('last') last.set_attribute('fri', str(lastdict.get_int('fri'))) last.set_attribute('style', str(lastdict.get_int('style'))) last.set_attribute('mode', str(lastdict.get_int('mode'))) last.set_attribute('cate', str(lastdict.get_int('cate'))) last.set_attribute('sort', str(lastdict.get_int('sort'))) last.set_attribute('mid', str(lastdict.get_int('mid'))) last.set_attribute('mtype', str(lastdict.get_int('mtype'))) last.set_attribute('cid', str(lastdict.get_int('cid'))) last.set_attribute('ctype', str(lastdict.get_int('ctype'))) last.set_attribute('sid', str(lastdict.get_int('sid'))) # Groove gauge level-ups gr_s = Node.void('gr_s') root.add_child(gr_s) index = 1 for entry in profile.get_int_array('gr_s', 5): gr_s.set_attribute(f'gr{index}', str(entry)) index = index + 1 gr_d = Node.void('gr_d') root.add_child(gr_d) index = 1 for entry in profile.get_int_array('gr_d', 5): gr_d.set_attribute(f'gr{index}', str(entry)) index = index + 1 # Options in menus root.add_child(Node.s16_array('opt', profile.get_int_array('opt', 16))) root.add_child(Node.s16_array('opt_ex', profile.get_int_array('opt_ex', 16))) # Unlock flags root.add_child(Node.u8_array('flag', profile.get_int_array('flag', 256, [1] * 256))) # Ranking display? root.add_child(Node.u16_array('rank', profile.get_int_array('rank', 100))) # Rivals links = self.data.local.user.get_links(self.game, self.version, userid) for link in links: if link.type[:7] != 'friend_': continue pos = int(link.type[7:]) friend = self.get_profile(link.other_userid) play_stats = self.get_play_statistics(link.other_userid) if friend is not None: friendnode = Node.void('friend') root.add_child(friendnode) friendnode.set_attribute('pos', str(pos)) friendnode.set_attribute('vs', '0') friendnode.set_attribute('up', '0') friendnode.add_child(Node.u32('code', friend.get_int('extid'))) friendnode.add_child(Node.string('name', friend.get_str('name'))) friendnode.add_child(Node.u8('area', friend.get_int('area', 51))) friendnode.add_child(Node.u32('exp', play_stats.get_int('exp'))) friendnode.add_child(Node.u32('star', friend.get_int('star'))) # Drill rankings if 'title' in friend: title = Node.void('title') friendnode.add_child(title) titledict = friend.get_dict('title') if 't' in titledict: title.set_attribute('t', str(titledict.get_int('t'))) if 's' in titledict: title.set_attribute('s', str(titledict.get_int('s'))) if 'd' in titledict: title.set_attribute('d', str(titledict.get_int('d'))) if 'title_gr' in friend: title_gr = Node.void('title_gr') friendnode.add_child(title_gr) title_grdict = friend.get_dict('title_gr') if 't' in title_grdict: title_gr.set_attribute('t', str(title_grdict.get_int('t'))) if 's' in title_grdict: title_gr.set_attribute('s', str(title_grdict.get_int('s'))) if 'd' in title_grdict: title_gr.set_attribute('d', str(title_grdict.get_int('d'))) # Groove gauge level-ups gr_s = Node.void('gr_s') friendnode.add_child(gr_s) index = 1 for entry in friend.get_int_array('gr_s', 5): gr_s.set_attribute(f'gr{index}', str(entry)) index = index + 1 gr_d = Node.void('gr_d') friendnode.add_child(gr_d) index = 1 for entry in friend.get_int_array('gr_d', 5): gr_d.set_attribute(f'gr{index}', str(entry)) index = index + 1 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'))) 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