def __handle_userload(self, userid: Optional[UserID], requestdata: Node, response: Node) -> None: has_profile: bool = False achievements: List[Achievement] = [] scores: List[Score] = [] if userid is not None: has_profile = self.has_profile(userid) achievements = self.data.local.user.get_achievements(self.game, self.version, userid) scores = self.data.remote.music.get_scores(self.game, self.music_version, userid) # Place scores into an arrangement for easier distribution to Ace. scores_by_mcode: Dict[int, List[Optional[Score]]] = {} for score in scores: if score.id not in scores_by_mcode: scores_by_mcode[score.id] = [None] * 9 scores_by_mcode[score.id][self.db_to_game_chart(score.chart)] = score # First, set new flag response.add_child(Node.bool('is_new', not has_profile)) # Now, return the scores to Ace for mcode in scores_by_mcode: music = Node.void('music') response.add_child(music) music.add_child(Node.u32('mcode', mcode)) scores_that_matter = scores_by_mcode[mcode] while scores_that_matter[-1] is None: scores_that_matter = scores_that_matter[:-1] for score in scores_that_matter: note = Node.void('note') music.add_child(note) if score is None: note.add_child(Node.u16('count', 0)) note.add_child(Node.u8('rank', 0)) note.add_child(Node.u8('clearkind', 0)) note.add_child(Node.s32('score', 0)) note.add_child(Node.s32('ghostid', 0)) else: note.add_child(Node.u16('count', score.plays)) note.add_child(Node.u8('rank', self.db_to_game_rank(score.data.get_int('rank')))) note.add_child(Node.u8('clearkind', self.db_to_game_halo(score.data.get_int('halo')))) note.add_child(Node.s32('score', score.points)) note.add_child(Node.s32('ghostid', score.key)) # Active event settings activeevents = [ 1, 3, 5, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, ] # Event reward settings rewards = { '30': { 999: 5, } } # Now handle event progress and activation. events = {ach.id: ach.data for ach in achievements if ach.type == '9999'} progress = [ach for ach in achievements if ach.type != '9999'] # Make sure we always send a babylon's adventure save event or the game won't send progress babylon_included = False for evtprogress in progress: if evtprogress.id == 999 and evtprogress.type == '30': babylon_included = True break if not babylon_included: progress.append(Achievement( 999, '30', None, { 'completed': False, 'progress': 0, }, )) for event in activeevents: # Get completion data playerstats = events.get(event, ValidatedDict({'completed': False})) # Return the data eventdata = Node.void('eventdata') response.add_child(eventdata) eventdata.add_child(Node.u32('eventid', event)) eventdata.add_child(Node.s32('eventtype', 9999)) eventdata.add_child(Node.u32('eventno', 0)) eventdata.add_child(Node.s64('condition', 0)) eventdata.add_child(Node.u32('reward', 0)) eventdata.add_child(Node.s32('comptime', 1 if playerstats.get_bool('completed') else 0)) eventdata.add_child(Node.s64('savedata', 0)) for evtprogress in progress: # Babylon's adventure progres and anything else the game sends eventdata = Node.void('eventdata') response.add_child(eventdata) eventdata.add_child(Node.u32('eventid', evtprogress.id)) eventdata.add_child(Node.s32('eventtype', int(evtprogress.type))) eventdata.add_child(Node.u32('eventno', 0)) eventdata.add_child(Node.s64('condition', 0)) eventdata.add_child(Node.u32('reward', rewards.get(evtprogress.type, {}).get(evtprogress.id))) eventdata.add_child(Node.s32('comptime', 1 if evtprogress.data.get_bool('completed') else 0)) eventdata.add_child(Node.s64('savedata', evtprogress.data.get_int('progress')))
def handle_game_3_common_request(self, request: Node) -> Node: game = Node.void('game_3') limited = Node.void('music_limited') game.add_child(limited) # Song unlock config game_config = self.get_game_config() if game_config.get_bool('force_unlock_songs'): ids = set() songs = self.data.local.music.get_all_songs( self.game, self.music_version) for song in songs: if song.data.get_int('limited') in ( self.GAME_LIMITED_LOCKED, self.GAME_LIMITED_UNLOCKABLE): ids.add((song.id, song.chart)) for (songid, chart) in ids: info = Node.void('info') limited.add_child(info) info.add_child(Node.s32('music_id', songid)) info.add_child(Node.u8('music_type', chart)) info.add_child(Node.u8('limited', self.GAME_LIMITED_UNLOCKED)) # Event config event = Node.void('event') game.add_child(event) def enable_event(eid: int) -> None: evt = Node.void('info') event.add_child(evt) evt.add_child(Node.u32('event_id', eid)) if not game_config.get_bool('disable_matching'): enable_event(143) # Matching enabled # These events are meant specifically for Museca Plus museca_plus_events = [ 140, # Agetta Moratta (vmlink_phase 3 in musicdb) 211, # News 1 212, # News 2 ] event_ids = [ 1, # Extended pedal options (no effect on Museca 1+1/2) 56, # Generator grafica icon <print 1 in musicdb> 83, # Paseli Light Start 86, # Generator grafica icon <print 2 in musicdb> 98, # Caption 2 notice (grs_grafica_caption_2.png) 105, # Makes the "Number of Layers" option visible in game settings 130, # Curator Rank 141, # Coconatsu & Mukipara grafica effects 145, # MUKIPARA UNLOCKS 146, # MUKIPARA UNLOCKS 147, # MUKIPARA UNLOCKS 148, # MUKIPARA UNLOCKS 149, # MUKIPARA UNLOCKS 195, # Fictional Curator (foot pedal options) ] for evtid in event_ids: enable_event(evtid) if self.omnimix: for evtid in museca_plus_events: enable_event(evtid) #TODO: Add the campaign events to the webui so players can enable them in their profile. # List of known event IDs: # 56, # Generator grafica icon <print 1 in musicdb> # 83, # Paseli Light Start # 86, # Generator grafica icon <print 2 in musicdb> # 98, # Caption 2 notice (grs_grafica_caption_2.png) # 100, # DJ YOSHITAKA EXHIBITION 2016 # 103, # HATSUNE MIKU EXHIBITION 2016 - PART 1 # 104, # HATSUNE MIKU EXHIBITION 2016 - PART 2 # 105, # Makes the "Number of Layers" option visible in game settings # 106, # HATSUNE MIKU EXHIBITION 2016 - PART 3 # 117, # NEW GENERATION METEOR DIFFUSE FESTA 2016 / RYUSEI FESTA TRIGGER # 129, # COCONATSU EXHIBITION 2016 # 130, # Curator Rank # 97, # Agetta Moratta (vmlink_phase 1 in musicdb) # 114, # Agetta Moratta (vmlink_phase 2 in musicdb) # 140, # Agetta Moratta (vmlink_phase 3 in musicdb) # 141, # Coconatsu & Mukipara grafica effects # 143, # Matching # 144, # BEMANI ARCHAEOLOGICAL EXHIBITION # 163, # TUTORIAL SNOW # 169, # SHIORI FUJISAKI EXHIBITION 2017 - PART 1 # 174, # SHIORI FUJISAKI EXHIBITION 2017 - PART 2 # 182, # Mute illil's voice? # 192, # GREAT REPRINT FESTIVAL: MIKU + DJ YOSHITAKA # 194, # Continue # 195, # Fictional Curator (foot pedal options) # 211, #News 1 # 212, #News 2 # 213, #News 3 # 214, #News 4 # 217, #News 5 # 218, #News 6 # 219, #News 7 # 220, #News 8 # 221, # GRAFICA PRESENTATION CAMPAIGN “THE PRIMITIVE LIFE EXHIBITION” # 222, # GRAFICA PRESENTATION CAMPAIGN "NOISE" # 223, # GRAFICA PRESENTATION CAMPAIGN "PATISSERIE ROUGE" # 224, # GRAFICA PRESENTATION CAMPAIGN "GUNSLINGER" # 145, # MUKIPARA UNLOCKS # 146, # MUKIPARA UNLOCKS # 147, # MUKIPARA UNLOCKS # 148, # MUKIPARA UNLOCKS # 149, # MUKIPARA UNLOCKS # Makes special missions available on grafica that have them. extend = Node.void('extend') game.add_child(extend) info = Node.void('info') extend.add_child(info) info.add_child(Node.u32('extend_id', 1)) info.add_child(Node.u32('extend_type', 9)) info.add_child(Node.s32('param_num_1', 2)) info.add_child(Node.s32('param_num_2', 50)) info.add_child(Node.s32('param_num_3', 59)) info.add_child(Node.s32('param_num_4', 64)) info.add_child(Node.s32('param_num_5', 86)) info.add_child(Node.string('param_str_1', 'available_ex: 1')) info.add_child(Node.string('param_str_2', 'available_ex: 1')) info.add_child(Node.string('param_str_3', 'available_ex: 1')) info.add_child(Node.string('param_str_4', 'available_ex: 1')) info.add_child(Node.string('param_str_5', 'available_ex: 1')) if self.omnimix: info = Node.void('info') extend.add_child(info) info.add_child(Node.u32('extend_id', 2)) info.add_child(Node.u32('extend_type', 9)) info.add_child(Node.s32('param_num_1', 210)) info.add_child(Node.s32('param_num_2', 0)) info.add_child(Node.s32('param_num_3', 0)) info.add_child(Node.s32('param_num_4', 0)) info.add_child(Node.s32('param_num_5', 0)) info.add_child(Node.string('param_str_1', '')) info.add_child(Node.string('param_str_2', '')) info.add_child(Node.string('param_str_3', '')) info.add_child(Node.string('param_str_4', '')) info.add_child(Node.string('param_str_5', '')) return game
def handle_game_3_lounge_request(self, request: Node) -> Node: game = Node.void('game_3') # Refresh interval in seconds. game.add_child(Node.u32('interval', 10)) return game
def handle_game_load_m_request(self, request: Node) -> Node: extid = intish(request.attribute('code')) refid = request.attribute('refid') if extid is not None: # Rival score loading userid = self.data.remote.user.from_extid(self.game, self.version, extid) else: # Self score loading userid = self.data.remote.user.from_refid(self.game, self.version, refid) if userid is not None: scores = self.data.remote.music.get_scores(self.game, self.music_version, userid) old_scores = [ score for score in self.data.local.user.get_achievements(self.game, self.music_version, userid) if score.type == '2ndmix' ] else: scores = [] old_scores = [] sortedscores: Dict[int, Dict[int, Dict[str, Union[Score, Achievement]]]] = {} for score in scores: if score.id not in sortedscores: sortedscores[score.id] = {} if score.chart not in sortedscores[score.id]: sortedscores[score.id][score.chart] = {} sortedscores[score.id][score.chart]['score'] = score for oldscore in old_scores: songid = int(oldscore.id / 100) chart = int(oldscore.id % 100) if songid not in sortedscores: sortedscores[songid] = {} if chart not in sortedscores[songid]: sortedscores[songid][chart] = {} sortedscores[songid][chart]['oldscore'] = oldscore game = Node.void('game') for song in sortedscores: music = Node.void('music') game.add_child(music) music.set_attribute('reclink', str(song)) for chart in sortedscores[song]: try: gamechart = self.db_to_game_chart(chart) except KeyError: # Don't support this chart in this game continue scoredict = sortedscores[song][chart] if 'score' in scoredict: # We played the normal version of this song gamerank = self.db_to_game_rank(scoredict['score'].data.get_int('rank')) combo_type = self.db_to_game_halo(scoredict['score'].data.get_int('halo')) points = scoredict['score'].points # type: ignore plays = scoredict['score'].plays # type: ignore else: # We only played 2nd mix version of this song gamerank = 0 combo_type = self.GAME_HALO_NONE points = 0 plays = 0 if 'oldscore' in scoredict: # We played the 2nd mix version of this song oldpoints = scoredict['oldscore'].data.get_int('points') oldrank = scoredict['oldscore'].data.get_int('rank') oldplays = scoredict['oldscore'].data.get_int('plays') else: oldpoints = 0 oldrank = 0 oldplays = 0 typenode = Node.void('type') music.add_child(typenode) typenode.set_attribute('diff', str(gamechart)) typenode.add_child(Node.u32('score', points)) typenode.add_child(Node.u16('count', plays)) typenode.add_child(Node.u8('rank', gamerank)) typenode.add_child(Node.u8('combo_type', combo_type)) typenode.add_child(Node.u32('score_2nd', oldpoints)) typenode.add_child(Node.u8('rank_2nd', oldrank)) typenode.add_child(Node.u16('cnt_2nd', oldplays)) return game
def handle_game_entry_s_request(self, request: Node) -> Node: game = Node.void('game') # This should be created on the fly for a lobby that we're in. game.add_child(Node.u32('entry_id', 1)) return game
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', 'KFC-JA-B01')) game.add_child( Node.string('etc', 'TaxMode:1,BasicRate:100/1,FirstFree:0')) setting = Node.void('setting') game.add_child(setting) setting.add_child(Node.s32('coin_slot', 0)) setting.add_child(Node.s32('game_start', 1)) setting.add_child(Node.string('schedule', '0,0,0,0,0,0,0')) setting.add_child(Node.string('reference', '1,1,1')) setting.add_child(Node.string('basic_rate', '100,100,100')) setting.add_child(Node.s32('tax_rate', 1)) setting.add_child(Node.string('time_service', '0,0,0')) setting.add_child(Node.string('service_value', '10,10,10')) setting.add_child(Node.string('service_limit', '10,10,10')) setting.add_child( Node.string('service_time', '07:00-11:00,07:00-11:00,07:00-11:00')) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/game_3/nxt_time")
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', f'dialog#{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 handle_game_3_buy_request(self, request: Node) -> Node: refid = request.child_value('refid') if refid is not None: userid = self.data.remote.user.from_refid(self.game, self.version, refid) else: userid = None if userid is not None: profile = self.get_profile(userid) else: profile = None if userid is not None and profile is not None: # Look up packets and blocks packet = profile.get_int('packet') block = profile.get_int('block') # Add on any additional we earned this round packet = packet + (request.child_value('earned_gamecoin_packet') or 0) block = block + (request.child_value('earned_gamecoin_block') or 0) currency_type = request.child_value('currency_type') price = request.child_value('item/price') if isinstance(price, list): # Sometimes we end up buying more than one item at once price = sum(price) if currency_type == self.GAME_CURRENCY_PACKETS: # This is a valid purchase newpacket = packet - price if newpacket < 0: result = 1 else: packet = newpacket result = 0 elif currency_type == self.GAME_CURRENCY_BLOCKS: # This is a valid purchase newblock = block - price if newblock < 0: result = 1 else: block = newblock result = 0 else: # Bad currency type result = 1 if result == 0: # Transaction is valid, update the profile with new packets and blocks profile.replace_int('packet', packet) profile.replace_int('block', block) self.put_profile(userid, profile) # If this was a song unlock, we should mark it as unlocked item_type = request.child_value('item/item_type') item_id = request.child_value('item/item_id') param = request.child_value('item/param') if not isinstance(item_type, list): # Sometimes we buy multiple things at once. Make it easier by always assuming this. item_type = [item_type] item_id = [item_id] param = [param] for i in range(len(item_type)): self.data.local.user.put_achievement( self.game, self.version, userid, item_id[i], f'item_{item_type[i]}', { 'param': param[i], }, ) else: # Unclear what to do here, return a bad response packet = 0 block = 0 result = 1 game = Node.void('game_3') game.add_child(Node.u32('gamecoin_packet', packet)) game.add_child(Node.u32('gamecoin_block', block)) game.add_child(Node.s8('result', result)) return game
def handle_shopinfo_regist_request(self, request: Node) -> Node: # Update the name of this cab for admin purposes self.update_machine_name(request.child_value('shop/name')) shopinfo = Node.void('shopinfo') data = Node.void('data') shopinfo.add_child(data) data.add_child(Node.u32('cabid', 1)) data.add_child(Node.string('locationid', 'nowhere')) data.add_child(Node.u8('is_send', 1)) data.add_child( Node.s32_array( 'white_music_list', [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -16385, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ], )) data.add_child(Node.u8('tax_phase', 1)) lab = Node.void('lab') data.add_child(lab) lab.add_child(Node.bool('is_open', False)) vocaloid_event = Node.void('vocaloid_event') data.add_child(vocaloid_event) vocaloid_event.add_child(Node.u8('state', 0)) vocaloid_event.add_child(Node.s32('music_id', 0)) matching_off = Node.void('matching_off') data.add_child(matching_off) matching_off.add_child(Node.bool('is_open', True)) return shopinfo
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( 'Couldn\'t find raw offset for virtual offset 0x{:08x}'.format( offset)) 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('Invalid node type 0x{:02x}'.format(nodetype)) 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) else: raise Exception( 'Unimplemented node type 0x{:02x}'.format(nodetype)) # Append it root.add_child(node) return root
def handle_game_3_common_request(self, request: Node) -> Node: game = Node.void('game_3') limited = Node.void('music_limited') game.add_child(limited) # Song unlock config game_config = self.get_game_config() if game_config.get_bool('force_unlock_songs'): ids = set() songs = self.data.local.music.get_all_songs( self.game, self.music_version) for song in songs: if song.data.get_int('limited') in ( self.GAME_LIMITED_LOCKED, self.GAME_LIMITED_UNLOCKABLE): ids.add((song.id, song.chart)) for (songid, chart) in ids: info = Node.void('info') limited.add_child(info) info.add_child(Node.s32('music_id', songid)) info.add_child(Node.u8('music_type', chart)) info.add_child(Node.u8('limited', self.GAME_LIMITED_UNLOCKED)) # Event config event = Node.void('event') game.add_child(event) def enable_event(eid: int) -> None: evt = Node.void('info') event.add_child(evt) evt.add_child(Node.u32('event_id', eid)) if not game_config.get_bool('disable_matching'): enable_event(143) # Matching enabled # These events are meant specifically for Museca Plus museca_plus_events = [ 140, # Agetta Moratta (vmlink_phase 3 in musicdb) 211, # News 1 ] event_ids = [ 1, # Extended pedal options 56, # Generator grafica icon <print 1 in musicdb> 83, # Paseli Light Start 86, # Generator grafica icon <print 2 in musicdb> 98, # Caption 2 notice (grs_grafica_caption_2.png) 105, # Makes the "Number of Layers" option visible in game settings 130, # Curator Rank 141, # Coconatsu & Mukipara grafica effects 145, # MUKIPARA UNLOCKS 146, # MUKIPARA UNLOCKS 147, # MUKIPARA UNLOCKS 148, # MUKIPARA UNLOCKS 149, # MUKIPARA UNLOCKS 195, # Fictional Curator (foot pedal options) ] for evtid in event_ids: enable_event(evtid) if self.omnimix: for evtid in museca_plus_events: enable_event(evtid) # Makes special missions available on grafica that have them. extend = Node.void('extend') game.add_child(extend) info = Node.void('info') extend.add_child(info) info.add_child(Node.u32('extend_id', 12)) info.add_child(Node.u32('extend_type', 9)) info.add_child(Node.s32('param_num_1', 2)) info.add_child(Node.s32('param_num_2', 50)) info.add_child(Node.s32('param_num_3', 59)) info.add_child(Node.s32('param_num_4', 64)) info.add_child(Node.s32('param_num_5', 86)) info.add_child(Node.string('param_str_1', 'available_ex: 1')) info.add_child(Node.string('param_str_2', 'available_ex: 1')) info.add_child(Node.string('param_str_3', 'available_ex: 1')) info.add_child(Node.string('param_str_4', 'available_ex: 1')) info.add_child(Node.string('param_str_5', 'available_ex: 1')) return game
def verify_player_write(self, refid: str, extid: int, loc: str, records: List[Dict[str, int]], scores: List[Dict[str, int]]) -> int: call = self.call_node() player = Node.void('player') call.add_child(player) player.set_attribute('method', 'write') player.add_child(Node.string('rid', refid)) player.add_child(Node.string('lid', loc)) player.add_child(Node.u64('begin_time', Time.now() * 1000)) player.add_child(Node.u64('end_time', Time.now() * 1000)) pdata = Node.void('pdata') player.add_child(pdata) base = Node.void('base') pdata.add_child(base) base.add_child(Node.s32('uid', extid)) base.add_child(Node.string('name', self.NAME)) base.add_child(Node.s16('icon_id', 0)) base.add_child(Node.s16('lv', 1)) base.add_child(Node.s32('exp', 0)) base.add_child(Node.s16('mg', 0)) base.add_child(Node.s16('ap', 0)) base.add_child(Node.s32('pc', 0)) base.add_child(Node.s32('uattr', 0)) con = Node.void('con') pdata.add_child(con) con.add_child(Node.s32('day', 0)) con.add_child(Node.s32('cnt', 0)) con.add_child(Node.s32('total_cnt', 0)) con.add_child(Node.s32('last', 0)) con.add_child(Node.s32('now', 0)) custom = Node.void('custom') pdata.add_child(custom) custom.add_child(Node.u8('s_gls', 0)) custom.add_child(Node.u8('bgm_m', 0)) custom.add_child(Node.u8('st_f', 0)) custom.add_child(Node.u8('st_bg', 0)) custom.add_child(Node.u8('st_bg_b', 100)) custom.add_child(Node.u8('eff_e', 0)) custom.add_child(Node.u8('se_s', 0)) custom.add_child(Node.u8('se_s_v', 100)) custom.add_child(Node.s16('last_music_id', 85)) custom.add_child(Node.u8('last_note_grade', 0)) custom.add_child(Node.u8('sort_type', 0)) custom.add_child(Node.u8('narrowdown_type', 0)) custom.add_child(Node.bool('is_begginer', False)) custom.add_child(Node.bool('is_tut', False)) custom.add_child(Node.s16_array('symbol_chat_0', [0, 1, 2, 3, 4, 5])) custom.add_child(Node.s16_array('symbol_chat_1', [0, 1, 2, 3, 4, 5])) custom.add_child(Node.u8('gauge_style', 0)) custom.add_child(Node.u8('obj_shade', 0)) custom.add_child(Node.u8('obj_size', 0)) custom.add_child(Node.s16_array('byword', [0, 0])) custom.add_child(Node.bool_array('is_auto_byword', [True, True])) custom.add_child(Node.bool('is_tweet', False)) custom.add_child(Node.bool('is_link_twitter', False)) custom.add_child(Node.s16('mrec_type', 0)) custom.add_child(Node.s16('card_disp_type', 0)) custom.add_child(Node.s16('tab_sel', 0)) custom.add_child( Node.s32_array( 'hidden_param', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0])) pdata.add_child(Node.void('released')) pdata.add_child(Node.void('rival')) pdata.add_child(Node.void('glass')) pdata.add_child(Node.void('fav_music_slot')) lincle_link_4 = Node.void('lincle_link_4') pdata.add_child(lincle_link_4) lincle_link_4.add_child(Node.u32('qpro_add', 0)) lincle_link_4.add_child(Node.u32('glass_add', 0)) lincle_link_4.add_child(Node.bool('for_iidx_0_0', False)) lincle_link_4.add_child(Node.bool('for_iidx_0_1', False)) lincle_link_4.add_child(Node.bool('for_iidx_0_2', False)) lincle_link_4.add_child(Node.bool('for_iidx_0_3', False)) lincle_link_4.add_child(Node.bool('for_iidx_0_4', False)) lincle_link_4.add_child(Node.bool('for_iidx_0_5', False)) lincle_link_4.add_child(Node.bool('for_iidx_0_6', False)) lincle_link_4.add_child(Node.bool('for_iidx_0', False)) lincle_link_4.add_child(Node.bool('for_iidx_1', False)) lincle_link_4.add_child(Node.bool('for_iidx_2', False)) lincle_link_4.add_child(Node.bool('for_iidx_3', False)) lincle_link_4.add_child(Node.bool('for_iidx_4', False)) lincle_link_4.add_child(Node.bool('for_rb_0_0', False)) lincle_link_4.add_child(Node.bool('for_rb_0_1', False)) lincle_link_4.add_child(Node.bool('for_rb_0_2', False)) lincle_link_4.add_child(Node.bool('for_rb_0_3', False)) lincle_link_4.add_child(Node.bool('for_rb_0_4', False)) lincle_link_4.add_child(Node.bool('for_rb_0_5', False)) lincle_link_4.add_child(Node.bool('for_rb_0_6', False)) lincle_link_4.add_child(Node.bool('for_rb_0', False)) lincle_link_4.add_child(Node.bool('for_rb_1', False)) lincle_link_4.add_child(Node.bool('for_rb_2', False)) lincle_link_4.add_child(Node.bool('for_rb_3', False)) lincle_link_4.add_child(Node.bool('for_rb_4', False)) # First, filter down to only records that are also in the battle log def key(thing: Dict[str, int]) -> str: return f'{thing["id"]}-{thing["chart"]}' updates = [key(score) for score in scores] sortedrecords = { key(record): record for record in records if key(record) in updates } # Now, see what records need updating and update them for score in scores: if key(score) in sortedrecords: # Had a record, need to merge record = sortedrecords[key(score)] else: # First time playing record = { 'clear_type': 0, 'achievement_rate': 0, 'score': 0, 'combo': 0, 'miss_count': 999999999, } sortedrecords[key(score)] = { 'id': score['id'], 'chart': score['chart'], 'clear_type': max(record['clear_type'], score['clear_type']), 'achievement_rate': max(record['achievement_rate'], score['achievement_rate']), 'score': max(record['score'], score['score']), 'combo': max(record['combo'], score['combo']), 'miss_count': min(record['miss_count'], score['miss_count']), } # Finally, send the records and battle logs recordnode = Node.void('record') pdata.add_child(recordnode) blog = Node.void('blog') pdata.add_child(blog) for (_, record) in sortedrecords.items(): rec = Node.void('rec') recordnode.add_child(rec) rec.add_child(Node.u16('mid', record['id'])) rec.add_child(Node.u8('ng', record['chart'])) rec.add_child(Node.s32('point', 2)) rec.add_child(Node.s32('played_time', Time.now())) mrec_0 = Node.void('mrec_0') rec.add_child(mrec_0) mrec_0.add_child(Node.s32('win', 1)) mrec_0.add_child(Node.s32('lose', 0)) mrec_0.add_child(Node.s32('draw', 0)) mrec_0.add_child(Node.u8('ct', record['clear_type'])) mrec_0.add_child(Node.s16('ar', record['achievement_rate'])) mrec_0.add_child(Node.s32('bs', record['score'])) mrec_0.add_child(Node.s16('mc', record['combo'])) mrec_0.add_child(Node.s16('bmc', record['miss_count'])) mrec_1 = Node.void('mrec_1') rec.add_child(mrec_1) mrec_1.add_child(Node.s32('win', 0)) mrec_1.add_child(Node.s32('lose', 0)) mrec_1.add_child(Node.s32('draw', 0)) mrec_1.add_child(Node.u8('ct', 0)) mrec_1.add_child(Node.s16('ar', 0)) mrec_1.add_child(Node.s32('bs', 0)) mrec_1.add_child(Node.s16('mc', 0)) mrec_1.add_child(Node.s16('bmc', -1)) scoreid = 0 for score in scores: log = Node.void('log') blog.add_child(log) log.add_child(Node.u8('id', scoreid)) log.add_child(Node.u16('mid', score['id'])) log.add_child(Node.u8('ng', score['chart'])) log.add_child(Node.u8('mt', 0)) log.add_child(Node.u8('rt', 0)) log.add_child(Node.s32('ruid', 0)) myself = Node.void('myself') log.add_child(myself) myself.add_child(Node.s16('mg', 0)) myself.add_child(Node.s16('ap', 0)) myself.add_child(Node.u8('ct', score['clear_type'])) myself.add_child(Node.s32('s', score['score'])) myself.add_child(Node.s16('ar', score['achievement_rate'])) rival = Node.void('rival') log.add_child(rival) rival.add_child(Node.s16('mg', 0)) rival.add_child(Node.s16('ap', 0)) rival.add_child(Node.u8('ct', 2)) rival.add_child(Node.s32('s', 177)) rival.add_child(Node.s16('ar', 500)) log.add_child(Node.s32('time', Time.now())) scoreid = scoreid + 1 # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/player/uid") self.assert_path(resp, "response/player/time") return resp.child_value('player/uid')
def handle_playerdata_usergamedata_recv_request(self, request: Node) -> Node: playerdata = Node.void('playerdata') player = Node.void('player') playerdata.add_child(player) refid = request.child_value('data/refid') userid = self.data.remote.user.from_refid(self.game, self.version, refid) if userid is not None: profile = self.get_profile(userid) links = self.data.local.user.get_links(self.game, self.version, userid) records = 0 record = Node.void('record') player.add_child(record) def acehex(val: int) -> str: return hex(val)[2:] if profile is None: # Just return a default empty node record.add_child(Node.string('d', '<NODATA>')) records = 1 else: # Figure out what profiles are being requested profiletypes = request.child_value('data/recv_csv').split(',')[::2] usergamedata = profile.get_dict('usergamedata') for ptype in profiletypes: if ptype in usergamedata: records = records + 1 if ptype == "COMMON": # Return basic profile options common = usergamedata[ptype]['strdata'].split(b',') common[self.GAME_COMMON_NAME_OFFSET] = profile.get_str('name').encode('ascii') common[self.GAME_COMMON_AREA_OFFSET] = acehex(profile.get_int('area')).encode('ascii') common[self.GAME_COMMON_WEIGHT_DISPLAY_OFFSET] = b'1' if profile.get_bool('workout_mode') else b'0' common[self.GAME_COMMON_WEIGHT_OFFSET] = str(float(profile.get_int('weight')) / 10.0).encode('ascii') common[self.GAME_COMMON_CHARACTER_OFFSET] = acehex(profile.get_int('character')).encode('ascii') usergamedata[ptype]['strdata'] = b','.join(common) if ptype == "OPTION": # Return user settings for frontend option = usergamedata[ptype]['strdata'].split(b',') option[self.GAME_OPTION_FAST_SLOW_OFFSET] = acehex(profile.get_int('early_late')).encode('ascii') option[self.GAME_OPTION_COMBO_POSITION_OFFSET] = acehex(profile.get_int('combo')).encode('ascii') option[self.GAME_OPTION_ARROW_SKIN_OFFSET] = acehex(profile.get_int('arrowskin')).encode('ascii') option[self.GAME_OPTION_GUIDELINE_OFFSET] = acehex(profile.get_int('guidelines')).encode('ascii') option[self.GAME_OPTION_FILTER_OFFSET] = acehex(profile.get_int('filter')).encode('ascii') usergamedata[ptype]['strdata'] = b','.join(option) if ptype == "LAST": # Return the number of calories expended in the last day 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]) last = usergamedata[ptype]['strdata'].split(b',') last[self.GAME_LAST_CALORIES_OFFSET] = acehex(total).encode('ascii') usergamedata[ptype]['strdata'] = b','.join(last) if ptype == "RIVAL": # Fill in the DDR code and active status of the three active # rivals. rival = usergamedata[ptype]['strdata'].split(b',') lastdict = profile.get_dict('last') friends: Dict[int, Optional[ValidatedDict]] = {} for link in links: if link.type[:7] != 'friend_': continue pos = int(link.type[7:]) friends[pos] = self.get_profile(link.other_userid) for rivalno in [1, 2, 3]: activeslot = { 1: self.GAME_RIVAL_SLOT_1_ACTIVE_OFFSET, 2: self.GAME_RIVAL_SLOT_2_ACTIVE_OFFSET, 3: self.GAME_RIVAL_SLOT_3_ACTIVE_OFFSET, }[rivalno] whichfriend = lastdict.get_int('rival{}'.format(rivalno)) - 1 if whichfriend < 0: # This rival isn't active rival[activeslot] = b'0' continue friendprofile = friends.get(whichfriend) if friendprofile is None: # This rival doesn't exist rival[activeslot] = b'0' continue ddrcodeslot = { 1: self.GAME_RIVAL_SLOT_1_DDRCODE_OFFSET, 2: self.GAME_RIVAL_SLOT_2_DDRCODE_OFFSET, 3: self.GAME_RIVAL_SLOT_3_DDRCODE_OFFSET, }[rivalno] rival[activeslot] = acehex(rivalno).encode('ascii') rival[ddrcodeslot] = acehex(friendprofile.get_int('extid')).encode('ascii') usergamedata[ptype]['strdata'] = b','.join(rival) dnode = Node.string('d', base64.b64encode(usergamedata[ptype]['strdata']).decode('ascii')) dnode.add_child(Node.string('bin1', base64.b64encode(usergamedata[ptype]['bindata']).decode('ascii'))) record.add_child(dnode) player.add_child(Node.u32('record_num', records)) playerdata.add_child(Node.s32('result', 0)) return playerdata
def __handle_rivalload(self, userid: Optional[UserID], requestdata: Node, response: Node) -> None: data = Node.void('data') response.add_child(data) data.add_child(Node.s32('recordtype', requestdata.child_value('loadflag'))) thismachine = self.data.local.machine.get_machine(self.config['machine']['pcbid']) machines_by_id: Dict[int, Optional[Machine]] = {thismachine.id: thismachine} loadkind = requestdata.child_value('loadflag') profiles_by_userid: Dict[UserID, ValidatedDict] = {} def get_machine(lid: int) -> Optional[Machine]: if lid not in machines_by_id: pcbid = self.data.local.machine.from_machine_id(lid) if pcbid is None: machines_by_id[lid] = None return None machine = self.data.local.machine.get_machine(pcbid) if machine is None: machines_by_id[lid] = None return None machines_by_id[lid] = machine return machines_by_id[lid] if loadkind == self.GAME_RIVAL_TYPE_WORLD: # Just load all scores for this network scores = self.data.remote.music.get_all_records(self.game, self.music_version) elif loadkind == self.GAME_RIVAL_TYPE_AREA: if thismachine.arcade is not None: match_arcade = thismachine.arcade match_machine = None else: match_arcade = None match_machine = thismachine.id # Load up all scores by any user registered on a machine in the same arcade profiles = self.data.local.user.get_all_profiles(self.game, self.version) userids: List[UserID] = [] for userid, profiledata in profiles: profiles_by_userid[userid] = profiledata # If we have an arcade to match, see if this user's location matches the arcade. # If we don't, just match lid directly if match_arcade is not None: theirmachine = get_machine(profiledata.get_int('lid')) if theirmachine is not None and theirmachine.arcade == match_arcade: userids.append(userid) elif match_machine is not None: if profiledata.get_int('lid') == match_machine: userids.append(userid) # Load all scores for users in the area scores = self.data.local.music.get_all_records(self.game, self.music_version, userlist=userids) elif loadkind == self.GAME_RIVAL_TYPE_MACHINE: # Load up all scores and filter them by those earned at this location scores = self.data.local.music.get_all_records(self.game, self.music_version, locationlist=[thismachine.id]) elif loadkind in [ self.GAME_RIVAL_TYPE_RIVAL1, self.GAME_RIVAL_TYPE_RIVAL2, self.GAME_RIVAL_TYPE_RIVAL3, ]: # Load up this user's highscores, format the way the below code expects it extid = requestdata.child_value('ddrcode') otherid = self.data.remote.user.from_extid(self.game, self.version, extid) userscores = self.data.remote.music.get_scores(self.game, self.music_version, otherid) scores = [(otherid, score) for score in userscores] else: # Nothing here scores = [] missing_users = [userid for (userid, _) in scores if userid not in profiles_by_userid] for (userid, profile) in self.get_any_profiles(missing_users): profiles_by_userid[userid] = profile for userid, score in scores: if profiles_by_userid.get(userid) is None: raise Exception('Logic error, couldn\'t find any profile for {}'.format(userid)) profiledata = profiles_by_userid[userid] record = Node.void('record') data.add_child(record) record.add_child(Node.u32('mcode', score.id)) record.add_child(Node.u8('notetype', self.db_to_game_chart(score.chart))) record.add_child(Node.u8('rank', self.db_to_game_rank(score.data.get_int('rank')))) record.add_child(Node.u8('clearkind', self.db_to_game_halo(score.data.get_int('halo')))) record.add_child(Node.u8('flagdata', 0)) record.add_child(Node.string('name', profiledata.get_str('name'))) record.add_child(Node.s32('area', profiledata.get_int('area', 58))) record.add_child(Node.s32('code', profiledata.get_int('extid'))) record.add_child(Node.s32('score', score.points)) record.add_child(Node.s32('ghostid', score.key))
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'))) 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)) # Add rivals to profile. rivallist = Node.void('rivallist') player.add_child(rivallist) links = self.data.local.user.get_links(self.game, self.version, userid) rivalcount = 0 for link in links: if link.type != 'rival': continue rprofile = self.get_profile(link.other_userid) if rprofile is None: continue rival = Node.void('rival') rivallist.add_child(rival) rival.add_child(Node.s32('jid', rprofile.get_int('extid'))) rival.add_child(Node.string('name', rprofile.get_str('name'))) # Lazy way of keeping track of rivals, since we can only have 4 # or the game with throw up. At least, I think Fulfill can have # 4 instead of the 3 found in newer versions, given the size of # the array that it loads the values in. However, to keep things # simple, I only supported three here. rivalcount += 1 if rivalcount >= 3: break rivallist.set_attribute('count', str(rivalcount)) # Unclear what this is. Looks related to Jubeat lab. 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 verify_game_save_m(self, location: str, refid: str, score: Dict[str, int]) -> None: call = self.call_node() game = Node.void('game_3') call.add_child(game) game.set_attribute('ver', '0') game.set_attribute('method', 'save_m') game.add_child(Node.string('refid', refid)) game.add_child(Node.string('dataid', refid)) game.add_child(Node.u32('music_id', score['id'])) game.add_child(Node.u32('music_type', score['chart'])) game.add_child(Node.u32('score', score['score'])) game.add_child(Node.u32('clear_type', score['clear_type'])) game.add_child(Node.u32('score_grade', score['grade'])) game.add_child(Node.u32('max_chain', 0)) game.add_child(Node.u32('critical', 0)) game.add_child(Node.u32('near', 0)) game.add_child(Node.u32('error', 0)) game.add_child(Node.u32('effective_rate', 100)) game.add_child(Node.u32('btn_rate', 0)) game.add_child(Node.u32('long_rate', 0)) game.add_child(Node.u32('vol_rate', 0)) game.add_child(Node.u8('mode', 0)) game.add_child(Node.u8('gauge_type', 0)) game.add_child(Node.u16('online_num', 0)) game.add_child(Node.u16('local_num', 0)) game.add_child(Node.string('locid', location)) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/game_3")
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: root = Node.void('gametop') data = Node.void('data') root.add_child(data) player = Node.void('player') data.add_child(player) # Player info and statistics info = Node.void('info') player.add_child(info) info.add_child(Node.s16('jubility', profile.get_int('jubility'))) info.add_child(Node.s16('jubility_yday', profile.get_int('jubility_yday'))) info.add_child(Node.s32('tune_cnt', profile.get_int('tune_cnt'))) info.add_child(Node.s32('save_cnt', profile.get_int('save_cnt'))) info.add_child(Node.s32('saved_cnt', profile.get_int('saved_cnt'))) info.add_child(Node.s32('fc_cnt', profile.get_int('fc_cnt'))) info.add_child(Node.s32('ex_cnt', profile.get_int('ex_cnt'))) info.add_child(Node.s32('pf_cnt', profile.get_int('pf_cnt'))) info.add_child(Node.s32('clear_cnt', profile.get_int('clear_cnt'))) info.add_child(Node.s32('match_cnt', profile.get_int('match_cnt'))) info.add_child(Node.s32('beat_cnt', profile.get_int('beat_cnt'))) info.add_child(Node.s32('mynews_cnt', profile.get_int('mynews_cnt'))) info.add_child(Node.s32('extra_point', profile.get_int('extra_point'))) info.add_child(Node.bool('is_extra_played', profile.get_bool('is_extra_played'))) if 'total_best_score' in profile: info.add_child(Node.s32('total_best_score', profile.get_int('total_best_score'))) # Looks to be set to true when there's an old profile, stops tutorial from # happening on first load. info.add_child(Node.bool('inherit', profile.get_bool('has_old_version'))) # Not saved, but loaded info.add_child(Node.s32('mtg_entry_cnt', 123)) info.add_child(Node.s32('mtg_hold_cnt', 456)) info.add_child(Node.u8('mtg_result', 10)) # First play stuff we don't support free_first_play = Node.void('free_first_play') player.add_child(free_first_play) free_first_play.add_child(Node.bool('is_available', False)) free_first_play.add_child(Node.s32('point', 0)) free_first_play.add_child(Node.s32('point_used', 0)) # Secret unlocks item = Node.void('item') player.add_child(item) item.add_child(Node.s32_array( 'secret_list', profile.get_int_array( 'secret_list', 32, [-1] * 32, ), )) item.add_child(Node.s32_array( 'title_list', profile.get_int_array( 'title_list', 96, [-1] * 96, ), )) item.add_child(Node.s16('theme_list', profile.get_int('theme_list', -1))) item.add_child(Node.s32_array('marker_list', profile.get_int_array('marker_list', 2, [-1] * 2))) item.add_child(Node.s32_array('parts_list', profile.get_int_array('parts_list', 96, [-1] * 96))) new = Node.void('new') item.add_child(new) new.add_child(Node.s32_array( 'secret_list', profile.get_int_array( 'secret_list_new', 32, [-1] * 32, ), )) new.add_child(Node.s32_array( 'title_list', profile.get_int_array( 'title_list_new', 96, [-1] * 96, ), )) new.add_child(Node.s16('theme_list', profile.get_int('theme_list_new', -1))) new.add_child(Node.s32_array('marker_list', profile.get_int_array('marker_list_new', 2, [-1] * 2))) # Last played data, for showing cursor and such lastdict = profile.get_dict('last') last = Node.void('last') player.add_child(last) last.add_child(Node.s32('music_id', lastdict.get_int('music_id'))) last.add_child(Node.s8('marker', lastdict.get_int('marker'))) last.add_child(Node.s16('title', lastdict.get_int('title'))) last.add_child(Node.s8('theme', lastdict.get_int('theme'))) last.add_child(Node.s8('sort', lastdict.get_int('sort'))) last.add_child(Node.s8('rank_sort', lastdict.get_int('rank_sort'))) last.add_child(Node.s8('combo_disp', lastdict.get_int('combo_disp'))) last.add_child(Node.s8('seq_id', lastdict.get_int('seq_id'))) last.add_child(Node.s16('parts', lastdict.get_int('parts'))) last.add_child(Node.s8('category', lastdict.get_int('category'))) last.add_child(Node.s64('play_time', lastdict.get_int('play_time'))) last.add_child(Node.string('shopname', lastdict.get_str('shopname'))) last.add_child(Node.string('areaname', lastdict.get_str('areaname'))) last.add_child(Node.s8('expert_option', lastdict.get_int('expert_option'))) last.add_child(Node.s8('matching', lastdict.get_int('matching'))) last.add_child(Node.s8('hazard', lastdict.get_int('hazard'))) last.add_child(Node.s8('hard', lastdict.get_int('hard'))) # Miscelaneous crap player.add_child(Node.s32('session_id', 1)) player.add_child(Node.u64('event_flag', 0)) # Macchiato event macchiatodict = profile.get_dict('macchiato') macchiato = Node.void('macchiato') player.add_child(macchiato) macchiato.add_child(Node.s32('pack_id', macchiatodict.get_int('pack_id'))) macchiato.add_child(Node.u16('bean_num', macchiatodict.get_int('bean_num'))) macchiato.add_child(Node.s32('daily_milk_num', macchiatodict.get_int('daily_milk_num'))) macchiato.add_child(Node.bool('is_received_daily_milk', macchiatodict.get_bool('is_received_daily_milk'))) macchiato.add_child(Node.s32('today_tune_cnt', macchiatodict.get_int('today_tune_cnt'))) macchiato.add_child(Node.s32_array( 'daily_milk_bonus', macchiatodict.get_int_array('daily_milk_bonus', 9, [-1, -1, -1, -1, -1, -1, -1, -1, -1]), )) macchiato.add_child(Node.s32('daily_play_burst', macchiatodict.get_int('daily_play_burst'))) macchiato.add_child(Node.bool('sub_menu_is_completed', macchiatodict.get_bool('sub_menu_is_completed'))) macchiato.add_child(Node.s32('compensation_milk', macchiatodict.get_int('compensation_milk'))) macchiato.add_child(Node.s32('match_cnt', macchiatodict.get_int('match_cnt'))) # Probably never will support this macchiato_music_list = Node.void('macchiato_music_list') macchiato.add_child(macchiato_music_list) macchiato_music_list.set_attribute('count', '0') # Same with this comment macchiato.add_child(Node.s32('sub_pack_id', 0)) sub_macchiato_music_list = Node.void('sub_macchiato_music_list') macchiato.add_child(sub_macchiato_music_list) sub_macchiato_music_list.set_attribute('count', '0') # And this season_music_list = Node.void('season_music_list') macchiato.add_child(season_music_list) season_music_list.set_attribute('count', '0') # Weird, this is sent as a void with a bunch of subnodes, but returned as an int array. achievement_list = Node.void('achievement_list') macchiato.add_child(achievement_list) achievement_list.set_attribute('count', '0') # Also probably never supporting this either cow_list = Node.void('cow_list') macchiato.add_child(cow_list) cow_list.set_attribute('count', '0') # No news, ever. news = Node.void('news') player.add_child(news) news.add_child(Node.s16('checked', 0)) # No rival support, yet. rivallist = Node.void('rivallist') player.add_child(rivallist) rivallist.set_attribute('count', '0') # Full combo daily challenge. entry = self.data.local.game.get_time_sensitive_settings(self.game, self.version, 'fc_challenge') if entry is None: entry = ValidatedDict() # Figure out if we've played these songs start_time, end_time = self.data.local.network.get_schedule_duration('daily') today_attempts = self.data.local.music.get_all_attempts(self.game, self.version, userid, entry.get_int('today', -1), timelimit=start_time) whim_attempts = self.data.local.music.get_all_attempts(self.game, self.version, userid, entry.get_int('whim', -1), timelimit=start_time) challenge = Node.void('challenge') player.add_child(challenge) today = Node.void('today') challenge.add_child(today) today.add_child(Node.s32('music_id', entry.get_int('today', -1))) today.add_child(Node.u8('state', 0x40 if len(today_attempts) > 0 else 0x0)) whim = Node.void('whim') challenge.add_child(whim) whim.add_child(Node.s32('music_id', entry.get_int('whim', -1))) whim.add_child(Node.u8('state', 0x40 if len(whim_attempts) > 0 else 0x0)) # Sane defaults for unknown nodes only_now_music = Node.void('only_now_music') player.add_child(only_now_music) only_now_music.set_attribute('count', '0') lab_edit_seq = Node.void('lab_edit_seq') player.add_child(lab_edit_seq) lab_edit_seq.set_attribute('count', '0') kac_music = Node.void('kac_music') player.add_child(kac_music) kac_music.set_attribute('count', '0') history = Node.void('history') player.add_child(history) history.set_attribute('count', '0') share_music = Node.void('share_music') player.add_child(share_music) share_music.set_attribute('count', '0') bonus_music = Node.void('bonus_music') player.add_child(bonus_music) bonus_music.set_attribute('count', '0') bingo = Node.void('bingo') player.add_child(bingo) reward = Node.void('reward') bingo.add_child(reward) reward.add_child(Node.s32('total', 0)) reward.add_child(Node.s32('point', 0)) group = Node.void('group') player.add_child(group) group.add_child(Node.s32('group_id', 0)) # Basic profile info player.add_child(Node.string('name', profile.get_str('name', 'なし'))) player.add_child(Node.s32('jid', profile.get_int('extid'))) player.add_child(Node.string('refid', profile.get_str('refid'))) # Miscelaneous history stuff data.add_child(Node.u8('termver', 16)) data.add_child(Node.u32('season_etime', 0)) data.add_child(Node.s32_array( 'white_music_list', [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ], )) data.add_child(Node.s32_array( 'open_music_list', [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ], )) # Unsupported collaboration events with other games collabo_info = Node.void('collabo_info') data.add_child(collabo_info) # Unsupported policy break stuff policy_break = Node.void('policy_break') collabo_info.add_child(policy_break) policy_break.set_attribute('type', '1') policy_break.add_child(Node.u8('state', 1)) policy_break.add_child(Node.bool('is_report_end', False)) # Unsupported vocaloid stuff vocaloid_event = Node.void('vocaloid_event') collabo_info.add_child(vocaloid_event) vocaloid_event.set_attribute('type', '1') vocaloid_event.add_child(Node.u8('state', 0)) vocaloid_event.add_child(Node.s32('music_id', 0)) # Unsupported vocaloid stuff vocaloid_event2 = Node.void('vocaloid_event2') collabo_info.add_child(vocaloid_event2) vocaloid_event2.set_attribute('type', '1') vocaloid_event2.add_child(Node.u8('state', 0)) vocaloid_event2.add_child(Node.s32('music_id', 0)) # Maybe it is possible to turn off internet matching here? lab = Node.void('lab') data.add_child(lab) lab.add_child(Node.bool('is_open', False)) matching_off = Node.void('matching_off') data.add_child(matching_off) matching_off.add_child(Node.bool('is_open', True)) return root
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: 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 parse_psmap(data: bytes, offset: str, rootname: str) -> Node: pe = PEFile(data=data) root = Node.void(rootname) base = int(offset, 16) if pe.is_virtual(base): # Assume this is virtual base = pe.virtual_to_physical(base) def read_string(offset: int) -> str: # First, translate load offset in memory to disk offset offset = pe.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: if pe.is_64bit(): # 64 bit chunk = data[base:(base + 24)] base = base + 24 (nodetype, mandatory, outoffset, width, nameptr, defaultptr) = struct.unpack('<BBHIQQ', chunk) else: # 32 bit chunk = data[base:(base + 16)] base = base + 16 (nodetype, mandatory, outoffset, width, nameptr, defaultptr) = struct.unpack('<BBHIII', chunk) if nodetype == 0xFF or nodetype == 0x00: # if nodetype is 0 then we probably read garbage # 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 == 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 = pe.virtual_to_physical(defaultptr) else: saved_loc.append(None) root = node continue elif nodetype == 0x02 or nodetype == 0x43: if nodetype < 0x40: elements = int(width / 1) else: elements = width if elements > 1: node = Node.s8_array(name, [-1] * elements) else: node = Node.s8(name, -1) elif nodetype == 0x03 or nodetype == 0x44: if nodetype < 0x40: elements = int(width / 1) else: elements = width if elements > 1: node = Node.u8_array(name, [0] * elements) else: node = Node.u8(name, 0) elif nodetype == 0x04 or nodetype == 0x45: if nodetype < 0x40: elements = int(width / 2) else: elements = width if elements > 1: node = Node.s16_array(name, [-1] * elements) else: node = Node.s16(name, -1) elif nodetype == 0x05 or nodetype == 0x46: if nodetype < 0x40: elements = int(width / 2) else: elements = width if elements > 1: node = Node.u16_array(name, [0] * elements) else: node = Node.u16(name, 0) elif nodetype == 0x06 or nodetype == 0x47: if nodetype < 0x40: elements = int(width / 4) else: elements = width if elements > 1: node = Node.s32_array(name, [-1] * elements) else: node = Node.s32(name, -1) elif nodetype == 0x07 or nodetype == 0x48: if nodetype < 0x40: elements = int(width / 4) else: elements = width if elements > 1: node = Node.u32_array(name, [0] * elements) else: node = Node.u32(name, 0) elif nodetype == 0x08 or nodetype == 0x49: if nodetype < 0x40: elements = int(width / 8) else: elements = width if elements > 1: node = Node.s64_array(name, [-1] * elements) else: node = Node.s64(name, -1) elif nodetype == 0x09 or nodetype == 0x4A: if nodetype < 0x40: elements = int(width / 8) else: elements = width if elements > 1: node = Node.u64_array(name, [0] * elements) else: node = Node.u64(name, 0) elif nodetype == 0x0A: node = Node.string(name, '') elif nodetype == 0x0D: node = Node.float(name, 0.0) elif nodetype == 0x32 or nodetype == 0x6D: if nodetype < 0x40: elements = int(width / 1) else: elements = width if elements > 1: node = Node.bool_array(name, [False] * elements) else: node = Node.bool(name, False) elif nodetype == 0x2F: # Special case, this is an attribute if name[-1] != '@': raise Exception( f'Attribute name {name} expected to end with @') root.set_attribute(name[:-1], '') continue else: raise Exception(f'Unimplemented node type 0x{nodetype:02x}') # Append it root.add_child(node) return root
def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node: 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.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)) # 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 verify_playerdata_usergamedata_advanced_usersave( self, refid: str, extid: int, locid: str, score: Dict[str, Any], scorepos: int = 0) -> None: call = self.call_node() # Construct node playerdata = Node.void('playerdata') call.add_child(playerdata) playerdata.set_attribute('method', 'usergamedata_advanced') playerdata.add_child(Node.u32('retrycnt', 0)) info = Node.void('info') playerdata.add_child(info) info.add_child(Node.s32('version', 1)) data = Node.void('data') playerdata.add_child(data) data.add_child(Node.string('mode', 'usersave')) data.add_child(Node.string('name', self.NAME)) data.add_child(Node.s32('ddrcode', extid)) data.add_child(Node.s32('playside', 1)) data.add_child(Node.s32('playstyle', 0)) data.add_child(Node.s32('area', 58)) data.add_child(Node.s32('weight100', 0)) data.add_child(Node.string('shopname', 'gmw=')) data.add_child(Node.bool('ispremium', False)) data.add_child(Node.bool('iseapass', True)) data.add_child(Node.bool('istakeover', False)) data.add_child(Node.bool('isrepeater', False)) data.add_child(Node.bool('isgameover', scorepos < 0)) data.add_child(Node.string('locid', locid)) data.add_child(Node.string('shoparea', '.')) data.add_child(Node.s64('gamesession', 123456)) data.add_child(Node.string('refid', refid)) data.add_child(Node.string('dataid', refid)) data.add_child(Node.string('gamekind', 'MDX')) data.add_child(Node.string('pcbid', self.pcbid)) data.add_child(Node.void('record')) for i in range(5): if i == scorepos: # Fill in score here note = Node.void('note') data.add_child(note) note.add_child(Node.u8('stagenum', i + 1)) note.add_child(Node.u32('mcode', score['id'])) note.add_child(Node.u8('notetype', score['chart'])) note.add_child(Node.u8('rank', score['rank'])) note.add_child(Node.u8('clearkind', score['halo'])) note.add_child(Node.s32('score', score['score'])) note.add_child(Node.s32('exscore', 0)) note.add_child(Node.s32('maxcombo', 0)) note.add_child(Node.s32('life', 0)) note.add_child(Node.s32('fastcount', 0)) note.add_child(Node.s32('slowcount', 0)) note.add_child(Node.s32('judge_marvelous', 0)) note.add_child(Node.s32('judge_perfect', 0)) note.add_child(Node.s32('judge_great', 0)) note.add_child(Node.s32('judge_good', 0)) note.add_child(Node.s32('judge_boo', 0)) note.add_child(Node.s32('judge_miss', 0)) note.add_child(Node.s32('judge_ok', 0)) note.add_child(Node.s32('judge_ng', 0)) note.add_child(Node.s32('calorie', 0)) note.add_child(Node.s32('ghostsize', len(score['ghost']))) note.add_child(Node.string('ghost', score['ghost'])) note.add_child(Node.u8('opt_speed', 0)) note.add_child(Node.u8('opt_boost', 0)) note.add_child(Node.u8('opt_appearance', 0)) note.add_child(Node.u8('opt_turn', 0)) note.add_child(Node.u8('opt_dark', 0)) note.add_child(Node.u8('opt_scroll', 0)) note.add_child(Node.u8('opt_arrowcolor', 0)) note.add_child(Node.u8('opt_cut', 0)) note.add_child(Node.u8('opt_freeze', 0)) note.add_child(Node.u8('opt_jump', 0)) note.add_child(Node.u8('opt_arrowshape', 0)) note.add_child(Node.u8('opt_filter', 0)) note.add_child(Node.u8('opt_guideline', 0)) note.add_child(Node.u8('opt_gauge', 0)) note.add_child(Node.u8('opt_judgepriority', 0)) note.add_child(Node.u8('opt_timing', 0)) note.add_child(Node.string('basename', '')) note.add_child(Node.string('title_b64', '')) note.add_child(Node.string('artist_b64', '')) note.add_child(Node.u16('bpmMax', 0)) note.add_child(Node.u16('bpmMin', 0)) note.add_child(Node.u8('level', 0)) note.add_child(Node.u8('series', 0)) note.add_child(Node.u32('bemaniFlag', 0)) note.add_child(Node.u32('genreFlag', 0)) note.add_child(Node.u8('limited', 0)) note.add_child(Node.u8('region', 0)) note.add_child(Node.s32('gr_voltage', 0)) note.add_child(Node.s32('gr_stream', 0)) note.add_child(Node.s32('gr_chaos', 0)) note.add_child(Node.s32('gr_freeze', 0)) note.add_child(Node.s32('gr_air', 0)) note.add_child(Node.bool('share', False)) note.add_child(Node.u64('endtime', 0)) note.add_child(Node.s32('folder', 0)) else: note = Node.void('note') data.add_child(note) note.add_child(Node.u8('stagenum', 0)) note.add_child(Node.u32('mcode', 0)) note.add_child(Node.u8('notetype', 0)) note.add_child(Node.u8('rank', 0)) note.add_child(Node.u8('clearkind', 0)) note.add_child(Node.s32('score', 0)) note.add_child(Node.s32('exscore', 0)) note.add_child(Node.s32('maxcombo', 0)) note.add_child(Node.s32('life', 0)) note.add_child(Node.s32('fastcount', 0)) note.add_child(Node.s32('slowcount', 0)) note.add_child(Node.s32('judge_marvelous', 0)) note.add_child(Node.s32('judge_perfect', 0)) note.add_child(Node.s32('judge_great', 0)) note.add_child(Node.s32('judge_good', 0)) note.add_child(Node.s32('judge_boo', 0)) note.add_child(Node.s32('judge_miss', 0)) note.add_child(Node.s32('judge_ok', 0)) note.add_child(Node.s32('judge_ng', 0)) note.add_child(Node.s32('calorie', 0)) note.add_child(Node.s32('ghostsize', 0)) note.add_child(Node.string('ghost', '')) note.add_child(Node.u8('opt_speed', 0)) note.add_child(Node.u8('opt_boost', 0)) note.add_child(Node.u8('opt_appearance', 0)) note.add_child(Node.u8('opt_turn', 0)) note.add_child(Node.u8('opt_dark', 0)) note.add_child(Node.u8('opt_scroll', 0)) note.add_child(Node.u8('opt_arrowcolor', 0)) note.add_child(Node.u8('opt_cut', 0)) note.add_child(Node.u8('opt_freeze', 0)) note.add_child(Node.u8('opt_jump', 0)) note.add_child(Node.u8('opt_arrowshape', 0)) note.add_child(Node.u8('opt_filter', 0)) note.add_child(Node.u8('opt_guideline', 0)) note.add_child(Node.u8('opt_gauge', 0)) note.add_child(Node.u8('opt_judgepriority', 0)) note.add_child(Node.u8('opt_timing', 0)) note.add_child(Node.string('basename', '')) note.add_child(Node.string('title_b64', '')) note.add_child(Node.string('artist_b64', '')) note.add_child(Node.u16('bpmMax', 0)) note.add_child(Node.u16('bpmMin', 0)) note.add_child(Node.u8('level', 0)) note.add_child(Node.u8('series', 0)) note.add_child(Node.u32('bemaniFlag', 0)) note.add_child(Node.u32('genreFlag', 0)) note.add_child(Node.u8('limited', 0)) note.add_child(Node.u8('region', 0)) note.add_child(Node.s32('gr_voltage', 0)) note.add_child(Node.s32('gr_stream', 0)) note.add_child(Node.s32('gr_chaos', 0)) note.add_child(Node.s32('gr_freeze', 0)) note.add_child(Node.s32('gr_air', 0)) note.add_child(Node.bool('share', False)) note.add_child(Node.u64('endtime', 0)) note.add_child(Node.s32('folder', 0)) # Swap with server resp = self.exchange('', call) # Verify that response is correct self.assert_path(resp, "response/playerdata/result")
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('slot{}'.format(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('gr{}'.format(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('gr{}'.format(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('gr{}'.format(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('gr{}'.format(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('play_cnt{}'.format(i), str(areas[i])) return root
def verify_usergamedata_send(self, ref_id: str, ext_id: int, msg_type: str, send_only_common: bool = False) -> None: call = self.call_node() # Set up profile write profiledata = { 'COMMON': [ b'1', b'0', # shoparea spot, filled in below b'3c880f8', b'1', b'0', b'0', b'0', b'0', b'0', b'ffffffffffffffff', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'', # Name spot, filled in below ID.format_extid(ext_id).encode('ascii'), b'', b'', b'', b'', b'', b'', ], 'OPTION': [ b'0', b'3', b'0', b'0', b'0', b'0', b'0', b'3', b'0', b'0', b'0', b'0', b'1', b'2', b'0', b'0', b'0', b'10.000000', b'10.000000', b'10.000000', b'10.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'', b'', b'', b'', b'', b'', b'', b'', ], 'LAST': [ b'1', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'', b'', b'', b'', b'', b'', b'', b'', ], 'RIVAL': [ b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'0.000000', b'', b'', b'', b'', b'', b'', b'', b'', ] } if msg_type == 'new': # New profile gets blank name, because we save over it at the end of the round. profiledata['COMMON'][1] = b'0' profiledata['COMMON'][25] = b'' elif msg_type == 'existing': # Exiting profile gets our hardcoded name saved. profiledata['COMMON'][1] = b'3a' profiledata['COMMON'][25] = self.NAME.encode('shift-jis') else: raise Exception(f'Unknown message type {msg_type}!') if send_only_common: profiledata = {'COMMON': profiledata['COMMON']} # Construct node playerdata = Node.void('playerdata') call.add_child(playerdata) playerdata.set_attribute('method', 'usergamedata_send') playerdata.add_child(Node.u32('retrycnt', 0)) info = Node.void('info') playerdata.add_child(info) info.add_child(Node.s32('version', 1)) data = Node.void('data') playerdata.add_child(data) data.add_child(Node.string('refid', ref_id)) data.add_child(Node.string('dataid', ref_id)) data.add_child(Node.string('gamekind', 'MDX')) data.add_child(Node.u32('datanum', len(profiledata.keys()))) record = Node.void('record') data.add_child(record) for ptype in profiledata: profile = [b'ffffffff', ptype.encode('ascii')] + profiledata[ptype] d = Node.string( 'd', base64.b64encode(b','.join(profile)).decode('ascii')) record.add_child(d) d.add_child(Node.string('bin1', '')) # Swap with server resp = self.exchange('', call) self.assert_path(resp, "response/playerdata/result")
def handle_game_buy_request(self, request: Node) -> Node: refid = request.attribute('refid') if refid is not None: userid = self.data.remote.user.from_refid(self.game, self.version, refid) else: userid = None if userid is not None: profile = self.get_profile(userid) else: profile = None if userid is not None and profile is not None: # Look up packets and blocks packet = profile.get_int('packet') block = profile.get_int('block') # Add on any additional we earned this round packet = packet + (request.child_value('earned_gamecoin_packet') or 0) block = block + (request.child_value('earned_gamecoin_block') or 0) # Look up the item to get the actual price and currency used item = self.data.local.game.get_item( self.game, self.version, request.child_value('catalog_id'), 'song_unlock') if item is not None: currency_type = request.child_value('currency_type') if currency_type == self.GAME_CURRENCY_PACKETS: if 'packets' in item: # This is a valid purchase newpacket = packet - item.get_int('packets') if newpacket < 0: result = 1 else: packet = newpacket result = 0 else: # Bad transaction result = 1 elif currency_type == self.GAME_CURRENCY_BLOCKS: if 'blocks' in item: # This is a valid purchase newblock = block - item.get_int('blocks') if newblock < 0: result = 1 else: block = newblock result = 0 else: # Bad transaction result = 1 else: # Bad currency type result = 1 if result == 0: # Transaction is valid, update the profile with new packets and blocks profile.replace_int('packet', packet) profile.replace_int('block', block) self.put_profile(userid, profile) else: # Bad catalog ID result = 1 else: # Unclear what to do here, return a bad response packet = 0 block = 0 result = 1 game = Node.void('game') game.add_child(Node.u32('gamecoin_packet', packet)) game.add_child(Node.u32('gamecoin_block', block)) game.add_child(Node.s8('result', result)) return game
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.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)) # 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)) # 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 enable_event(eid: int) -> None: evt = Node.void('info') event.add_child(evt) evt.add_child(Node.u32('event_id', eid))
def handle_info22_request(self, request: Node) -> Optional[Node]: method = request.attribute('method') if method == 'common': # TODO: Hook these up to config so we can change this phases = { # Unknown event 0: 0, # Unknown event 1: 0, # Pop'n Aura, max 10 (remov all aura requirements) 2: 10, # Story 3: 1, # BEMANI ruins Discovery! 4: 0, # Unknown event 5: 0, # Unknown event 6: 0, # Unknown event 7: 0, # Unknown event 8: 0, # Unknown event 9: 0, # Unknown event 10: 0, # Unknown event 11: 0, # Unknown event 12: 0, # Unknown event 13: 0, # Unknown event 14: 0, # Unknown event 15: 0, # Unknown event 16: 0, # Unknown event 17: 0, # Unknown event 18: 0, # Unknown event 19: 0, } stories = list(range(173)) root = Node.void('info22') for phaseid in phases: phase = Node.void('phase') root.add_child(phase) phase.add_child(Node.s16('event_id', phaseid)) phase.add_child(Node.s16('phase', phases[phaseid])) for storyid in stories: story = Node.void('story') root.add_child(story) story.add_child(Node.u32('story_id', storyid)) story.add_child(Node.bool('is_limited', False)) story.add_child(Node.u64('limit_date', 0)) return root # Invalid method return None
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.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 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 handle_game_friend_request(self, request: Node) -> Node: extid = intish(request.attribute('code')) userid = None friend = None if extid is not None: # Rival score loading userid = self.data.remote.user.from_extid(self.game, self.version, extid) if userid is not None: friend = self.get_profile(userid) play_stats = self.get_play_statistics(userid) if friend is None: # Return an empty node to tell the game we don't have a player here game = Node.void('game') return game game = Node.void('game') game.set_attribute('data', '1') game.add_child(Node.u32('code', friend.get_int('extid'))) game.add_child(Node.string('name', friend.get_str('name'))) game.add_child(Node.u8('area', friend.get_int('area', 51))) game.add_child(Node.u32('exp', play_stats.get_int('exp'))) game.add_child(Node.u32('star', friend.get_int('star'))) # Drill rankings if 'title' in friend: title = Node.void('title') game.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') game.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') game.add_child(gr_s) index = 1 for entry in friend.get_int_array('gr_s', 5): gr_s.set_attribute('gr{}'.format(index), str(entry)) index = index + 1 gr_d = Node.void('gr_d') game.add_child(gr_d) index = 1 for entry in friend.get_int_array('gr_d', 5): gr_d.set_attribute('gr{}'.format(index), str(entry)) index = index + 1 return game