Exemple #1
0
    def handle_lobby_rb5_lobby_entry_request(self, request: Node) -> Node:
        root = Node.void('lobby')
        root.add_child(Node.s32('interval', 120))
        root.add_child(Node.s32('interval_p', 120))

        # Create a lobby entry for this user
        extid = request.child_value('e/uid')
        userid = self.data.remote.user.from_extid(self.game, self.version,
                                                  extid)
        if userid is not None:
            profile = self.get_profile(userid)
            info = self.data.local.lobby.get_play_session_info(
                self.game, self.version, userid)
            if profile is None or info is None:
                return root

            self.data.local.lobby.put_lobby(
                self.game, self.version, userid, {
                    'mid': request.child_value('e/mid'),
                    'ng': request.child_value('e/ng'),
                    'mopt': request.child_value('e/mopt'),
                    'lid': request.child_value('e/lid'),
                    'sn': request.child_value('e/sn'),
                    'pref': request.child_value('e/pref'),
                    'stg': request.child_value('e/stg'),
                    'pside': request.child_value('e/pside'),
                    'eatime': request.child_value('e/eatime'),
                    'ga': request.child_value('e/ga'),
                    'gp': request.child_value('e/gp'),
                    'la': request.child_value('e/la'),
                    'ver': request.child_value('e/ver'),
                })
            lobby = self.data.local.lobby.get_lobby(
                self.game,
                self.version,
                userid,
            )
            root.add_child(Node.s32('eid', lobby.get_int('id')))
            e = Node.void('e')
            root.add_child(e)
            e.add_child(Node.s32('eid', lobby.get_int('id')))
            e.add_child(Node.u16('mid', lobby.get_int('mid')))
            e.add_child(Node.u8('ng', lobby.get_int('ng')))
            e.add_child(Node.s32('uid', profile.get_int('extid')))
            e.add_child(Node.s32('uattr', profile.get_int('uattr')))
            e.add_child(Node.string('pn', profile.get_str('name')))
            e.add_child(Node.s32('plyid', info.get_int('id')))
            e.add_child(Node.s16('mg', profile.get_int('mg')))
            e.add_child(Node.s32('mopt', lobby.get_int('mopt')))
            e.add_child(Node.string('lid', lobby.get_str('lid')))
            e.add_child(Node.string('sn', lobby.get_str('sn')))
            e.add_child(Node.u8('pref', lobby.get_int('pref')))
            e.add_child(Node.s8('stg', lobby.get_int('stg')))
            e.add_child(Node.s8('pside', lobby.get_int('pside')))
            e.add_child(Node.s16('eatime', lobby.get_int('eatime')))
            e.add_child(Node.u8_array('ga', lobby.get_int_array('ga', 4)))
            e.add_child(Node.u16('gp', lobby.get_int('gp')))
            e.add_child(Node.u8_array('la', lobby.get_int_array('la', 4)))
            e.add_child(Node.u8('ver', lobby.get_int('ver')))

        return root
Exemple #2
0
    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')))

        # 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
Exemple #3
0
    def handle_player22_request(self, request: Node) -> Optional[Node]:
        method = request.attribute('method')

        if method == 'read':
            refid = request.child_value('ref_id')
            # Pop'n Music 22 doesn't send a modelstring to load old profiles,
            # it just expects us to know. So always look for old profiles in
            # Pop'n 22 land.
            root = self.get_profile_by_refid(refid,
                                             self.OLD_PROFILE_FALLTHROUGH)
            if root is None:
                root = Node.void('player22')
                root.set_attribute('status', str(Status.NO_PROFILE))
            return root

        elif method == 'new':
            refid = request.child_value('ref_id')
            name = request.child_value('name')
            root = self.new_profile_by_refid(refid, name)
            if root is None:
                root = Node.void('player22')
                root.set_attribute('status', str(Status.NO_PROFILE))
            return root

        elif method == 'start':
            return Node.void('player22')

        elif method == 'logout':
            return Node.void('player22')

        elif method == 'write':
            refid = request.child_value('ref_id')

            root = Node.void('player22')
            if refid is None:
                return root

            userid = self.data.remote.user.from_refid(self.game, self.version,
                                                      refid)
            if userid is None:
                return root

            oldprofile = self.get_profile(userid) or ValidatedDict()
            newprofile = self.unformat_profile(userid, request, oldprofile)

            if newprofile is not None:
                self.put_profile(userid, newprofile)

            return root

        elif method == 'friend':
            refid = request.attribute('ref_id')
            no = int(request.attribute('no', '-1'))

            root = Node.void('player22')
            if no < 0:
                root.add_child(Node.s8('result', 2))
                return root

            # Look up our own user ID based on the RefID provided.
            userid = self.data.remote.user.from_refid(self.game, self.version,
                                                      refid)
            if userid is None:
                root.add_child(Node.s8('result', 2))
                return root

            # Grab the links that we care about.
            links = self.data.local.user.get_links(self.game, self.version,
                                                   userid)
            profiles: Dict[UserID, ValidatedDict] = {}
            rivals: List[Link] = []
            for link in links:
                if link.type != 'rival':
                    continue

                other_profile = self.get_profile(link.other_userid)
                if other_profile is None:
                    continue
                profiles[link.other_userid] = other_profile
                rivals.append(link)

            # Somehow requested an invalid profile.
            if no >= len(rivals):
                root.add_child(Node.s8('result', 2))
                return root
            rivalid = links[no].other_userid
            rivalprofile = profiles[rivalid]
            scores = self.data.remote.music.get_scores(self.game, self.version,
                                                       rivalid)

            # First, output general profile info.
            friend = Node.void('friend')
            root.add_child(friend)
            friend.add_child(Node.s16('no', no))
            friend.add_child(
                Node.string('g_pm_id',
                            ID.format_extid(rivalprofile.get_int('extid'))))
            friend.add_child(
                Node.string('name', rivalprofile.get_str('name', 'なし')))
            friend.add_child(
                Node.s16('chara', rivalprofile.get_int('chara', -1)))
            # This might be for having non-active or non-confirmed friends, but setting to 0 makes the
            # ranking numbers disappear and the player icon show a questionmark.
            friend.add_child(Node.s8('is_open', 1))

            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')
                friend.add_child(music)
                music.set_attribute('music_num', str(score.id))
                music.set_attribute(
                    'sheet_num',
                    str({
                        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.set_attribute('score', str(points))
                music.set_attribute(
                    'clearmedal',
                    str({
                        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]))

            return root

        elif method == 'conversion':
            refid = request.child_value('ref_id')
            name = request.child_value('name')
            chara = request.child_value('chara')
            root = self.new_profile_by_refid(refid, name, chara)
            if root is None:
                root = Node.void('playerdata')
                root.set_attribute('status', str(Status.NO_PROFILE))
            return root

        elif method == 'write_music':
            refid = request.child_value('ref_id')

            root = Node.void('player22')
            if refid is None:
                return root

            userid = self.data.remote.user.from_refid(self.game, self.version,
                                                      refid)
            if userid is None:
                return root

            songid = request.child_value('music_num')
            chart = {
                self.GAME_CHART_TYPE_EASY: self.CHART_TYPE_EASY,
                self.GAME_CHART_TYPE_NORMAL: self.CHART_TYPE_NORMAL,
                self.GAME_CHART_TYPE_HYPER: self.CHART_TYPE_HYPER,
                self.GAME_CHART_TYPE_EX: self.CHART_TYPE_EX,
            }[request.child_value('sheet_num')]
            medal = request.child_value('clearmedal')
            points = request.child_value('score')
            combo = request.child_value('combo')
            stats = {
                'cool': request.child_value('cool'),
                'great': request.child_value('great'),
                'good': request.child_value('good'),
                'bad': request.child_value('bad')
            }
            medal = {
                self.GAME_PLAY_MEDAL_CIRCLE_FAILED:
                self.PLAY_MEDAL_CIRCLE_FAILED,
                self.GAME_PLAY_MEDAL_DIAMOND_FAILED:
                self.PLAY_MEDAL_DIAMOND_FAILED,
                self.GAME_PLAY_MEDAL_STAR_FAILED: self.PLAY_MEDAL_STAR_FAILED,
                self.GAME_PLAY_MEDAL_EASY_CLEAR: self.PLAY_MEDAL_EASY_CLEAR,
                self.GAME_PLAY_MEDAL_CIRCLE_CLEARED:
                self.PLAY_MEDAL_CIRCLE_CLEARED,
                self.GAME_PLAY_MEDAL_DIAMOND_CLEARED:
                self.PLAY_MEDAL_DIAMOND_CLEARED,
                self.GAME_PLAY_MEDAL_STAR_CLEARED:
                self.PLAY_MEDAL_STAR_CLEARED,
                self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO:
                self.PLAY_MEDAL_CIRCLE_FULL_COMBO,
                self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO:
                self.PLAY_MEDAL_DIAMOND_FULL_COMBO,
                self.GAME_PLAY_MEDAL_STAR_FULL_COMBO:
                self.PLAY_MEDAL_STAR_FULL_COMBO,
                self.GAME_PLAY_MEDAL_PERFECT: self.PLAY_MEDAL_PERFECT,
            }[medal]
            self.update_score(userid,
                              songid,
                              chart,
                              points,
                              medal,
                              combo=combo,
                              stats=stats)
            return root

        # Invalid method
        return None
Exemple #4
0
    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")
Exemple #5
0
    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')

            # Unsure if this should be last day, or total calories ever
            totalcalorie = Node.void('totalcalorie')
            root.add_child(totalcalorie)
            totalcalorie.set_attribute('total', str(total))

        # 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 2014
        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]))

        # 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)))
        option_ver = Node.void('option_ver')
        root.add_child(option_ver)
        option_ver.set_attribute('ver', str(profile.get_int('option_ver', 2)))
        if 'option_02' in profile:
            root.add_child(
                Node.s16_array('option_02',
                               profile.get_int_array('option_02', 24)))

        # Unlock flags
        root.add_child(
            Node.u8_array('flag',
                          profile.get_int_array('flag', 512, [1] * 512)[:256]))
        root.add_child(
            Node.u8_array('flag_ex',
                          profile.get_int_array('flag', 512, [1] * 512)))

        # 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

        # 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')))

        # 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
Exemple #6
0
    def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node:
        root = Node.void('playerdata')

        # Set up the base profile
        base = Node.void('base')
        root.add_child(base)
        base.add_child(Node.string('name', profile.get_str('name', 'なし')))
        base.add_child(
            Node.string('g_pm_id', ID.format_extid(profile.get_int('extid'))))
        base.add_child(Node.u8('mode', profile.get_int('mode', 0)))
        base.add_child(Node.s8('button', profile.get_int('button', 0)))
        base.add_child(
            Node.s8('last_play_flag', profile.get_int('last_play_flag', -1)))
        base.add_child(
            Node.u8('medal_and_friend', profile.get_int('medal_and_friend',
                                                        0)))
        base.add_child(Node.s8('category', profile.get_int('category', -1)))
        base.add_child(
            Node.s8('sub_category', profile.get_int('sub_category', -1)))
        base.add_child(Node.s16('chara', profile.get_int('chara', -1)))
        base.add_child(
            Node.s8('chara_category', profile.get_int('chara_category', -1)))
        base.add_child(Node.u8('collabo', profile.get_int('collabo', 255)))
        base.add_child(Node.u8('sheet', profile.get_int('sheet', 0)))
        base.add_child(Node.s8('tutorial', profile.get_int('tutorial', 0)))
        base.add_child(
            Node.s32('music_open_pt', profile.get_int('music_open_pt', 0)))
        base.add_child(Node.s8('is_conv', -1))
        base.add_child(Node.s32('option', profile.get_int('option', 0)))
        base.add_child(Node.s16('music', profile.get_int('music', -1)))
        base.add_child(Node.u16('ep', profile.get_int('ep', 0)))
        base.add_child(
            Node.s32_array('sp_color_flg',
                           profile.get_int_array('sp_color_flg', 2)))
        base.add_child(Node.s32('read_news', profile.get_int('read_news', 0)))
        base.add_child(
            Node.s16('consecutive_days_coupon',
                     profile.get_int('consecutive_days_coupon', 0)))
        base.add_child(Node.s8('staff', 0))

        # Player card section
        player_card_dict = profile.get_dict('player_card')
        player_card = Node.void('player_card')
        root.add_child(player_card)
        player_card.add_child(
            Node.u8_array('title',
                          player_card_dict.get_int_array('title', 2, [0, 1])))
        player_card.add_child(
            Node.u8('frame', player_card_dict.get_int('frame')))
        player_card.add_child(Node.u8('base',
                                      player_card_dict.get_int('base')))
        player_card.add_child(
            Node.u8_array('seal', player_card_dict.get_int_array('seal', 2)))
        player_card.add_child(
            Node.s32_array('get_title',
                           player_card_dict.get_int_array('get_title', 4)))
        player_card.add_child(
            Node.s32('get_frame', player_card_dict.get_int('get_frame')))
        player_card.add_child(
            Node.s32('get_base', player_card_dict.get_int('get_base')))
        player_card.add_child(
            Node.s32_array('get_seal',
                           player_card_dict.get_int_array('get_seal', 2)))

        # Player card EX section
        player_card_ex = Node.void('player_card_ex')
        root.add_child(player_card_ex)
        player_card_ex.add_child(
            Node.s32('get_title_ex', player_card_dict.get_int('get_title_ex')))
        player_card_ex.add_child(
            Node.s32('get_frame_ex', player_card_dict.get_int('get_frame_ex')))
        player_card_ex.add_child(
            Node.s32('get_base_ex', player_card_dict.get_int('get_base_ex')))
        player_card_ex.add_child(
            Node.s32('get_seal_ex', player_card_dict.get_int('get_seal_ex')))

        # Statistics section and scores section
        statistics = self.get_play_statistics(userid)
        last_play_date = statistics.get_int_array('last_play_date', 3)
        today_play_date = Time.todays_date()
        if (last_play_date[0] == today_play_date[0]
                and last_play_date[1] == today_play_date[1]
                and last_play_date[2] == today_play_date[2]):
            today_count = statistics.get_int('today_plays', 0)
        else:
            today_count = 0
        base.add_child(Node.u8('active_fr_num',
                               0))  # TODO: Hook up rivals code?
        base.add_child(
            Node.s32('total_play_cnt', statistics.get_int('total_plays', 0)))
        base.add_child(Node.s16('today_play_cnt', today_count))
        base.add_child(
            Node.s16('consecutive_days',
                     statistics.get_int('consecutive_days', 0)))

        last_played = [
            x[0] for x in self.data.local.music.get_last_played(
                self.game, self.version, userid, 3)
        ]
        most_played = [
            x[0] for x in self.data.local.music.get_most_played(
                self.game, self.version, userid, 20)
        ]
        while len(last_played) < 3:
            last_played.append(-1)
        while len(most_played) < 20:
            most_played.append(-1)

        hiscore_array = [0] * int(
            (((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8)
        clear_medal = [0] * self.GAME_MAX_MUSIC_ID
        clear_medal_sub = [0] * self.GAME_MAX_MUSIC_ID

        scores = self.data.remote.music.get_scores(self.game, self.version,
                                                   userid)
        for score in scores:
            if score.id > self.GAME_MAX_MUSIC_ID:
                continue

            # Skip any scores for chart types we don't support
            if score.chart not in [
                    self.CHART_TYPE_EASY,
                    self.CHART_TYPE_NORMAL,
                    self.CHART_TYPE_HYPER,
                    self.CHART_TYPE_EX,
            ]:
                continue

            points = score.points
            clear_medal[score.id] = clear_medal[
                score.id] | self.__format_medal_for_score(score)

            hiscore_index = (score.id * 4) + {
                self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
                self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION,
                self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER_POSITION,
                self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX_POSITION,
            }[score.chart]
            hiscore_byte_pos = int((hiscore_index * 17) / 8)
            hiscore_bit_pos = int((hiscore_index * 17) % 8)
            hiscore_value = points << hiscore_bit_pos
            hiscore_array[hiscore_byte_pos] = hiscore_array[
                hiscore_byte_pos] | (hiscore_value & 0xFF)
            hiscore_array[hiscore_byte_pos +
                          1] = hiscore_array[hiscore_byte_pos + 1] | (
                              (hiscore_value >> 8) & 0xFF)
            hiscore_array[hiscore_byte_pos +
                          2] = hiscore_array[hiscore_byte_pos + 2] | (
                              (hiscore_value >> 16) & 0xFF)

        hiscore = bytes(hiscore_array)

        player_card.add_child(Node.s16_array('best_music', most_played[0:3]))
        base.add_child(Node.s16_array('my_best', most_played))
        base.add_child(Node.s16_array('latest_music', last_played))
        base.add_child(Node.u16_array('clear_medal', clear_medal))
        base.add_child(Node.u8_array('clear_medal_sub', clear_medal_sub))

        # Goes outside of base for some reason
        root.add_child(Node.binary('hiscore', hiscore))

        # Net VS section
        netvs = Node.void('netvs')
        root.add_child(netvs)
        netvs.add_child(Node.s32_array('get_ojama', [0, 0]))
        netvs.add_child(Node.s32('rank_point', 0))
        netvs.add_child(Node.s32('play_point', 0))
        netvs.add_child(Node.s16_array('record', [0, 0, 0, 0, 0, 0]))
        netvs.add_child(Node.u8('rank', 0))
        netvs.add_child(Node.s8_array('ojama_condition', [0] * 74))
        netvs.add_child(Node.s8_array('set_ojama', [0, 0, 0]))
        netvs.add_child(Node.s8_array('set_recommend', [0, 0, 0]))
        netvs.add_child(Node.s8_array('jewelry', [0] * 15))
        for dialog in [0, 1, 2, 3, 4, 5]:
            # TODO: Configure this, maybe?
            netvs.add_child(Node.string('dialog', 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)))

        reflec_data = Node.void('reflec_data')
        root.add_child(reflec_data)
        reflec_data.add_child(
            Node.s8_array('reflec', profile.get_int_array('reflec', 2)))

        # Navigate section
        navigate_dict = profile.get_dict('navigate')
        navigate = Node.void('navigate')
        root.add_child(navigate)
        navigate.add_child(Node.s8('genre', navigate_dict.get_int('genre')))
        navigate.add_child(Node.s8('image', navigate_dict.get_int('image')))
        navigate.add_child(Node.s8('level', navigate_dict.get_int('level')))
        navigate.add_child(Node.s8('ojama', navigate_dict.get_int('ojama')))
        navigate.add_child(
            Node.s16('limit_num', navigate_dict.get_int('limit_num')))
        navigate.add_child(Node.s8('button', navigate_dict.get_int('button')))
        navigate.add_child(Node.s8('life', navigate_dict.get_int('life')))
        navigate.add_child(
            Node.s16('progress', navigate_dict.get_int('progress')))

        return root
Exemple #7
0
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
Exemple #8
0
    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
Exemple #9
0
    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.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(1)  # Matching enabled
        enable_event(2)  # Floor Infection
        enable_event(3)  # Policy Break
        enable_event(60)  # BEMANI Summer Diary

        for eventid in self._get_extra_events():
            enable_event(eventid)

        # Skill Analyzer config
        skill_course = Node.void('skill_course')
        game.add_child(skill_course)

        seasons = self._get_skill_analyzer_seasons()
        skillnames = self.__get_skill_analyzer_skill_levels()
        courses = self._get_skill_analyzer_courses()
        max_level: Dict[int, int] = {}
        for course in courses:
            max_level[course['level']] = max(course['season_id'], max_level.get(course['level'], -1))
        for course in courses:
            info = Node.void('info')
            skill_course.add_child(info)
            info.add_child(Node.s16('course_id', course.get('id', course['level'])))
            info.add_child(Node.s16('level', course['level']))
            info.add_child(Node.s32('season_id', course['season_id']))
            info.add_child(Node.string('season_name', seasons[course['season_id']]))
            info.add_child(Node.bool('season_new_flg', max_level[course['level']] == course['season_id']))
            info.add_child(Node.string('course_name', course.get('skill_name', skillnames.get(course['level'], ''))))
            info.add_child(Node.s16('course_type', 0))
            info.add_child(Node.s16('skill_name_id', course.get('skill_name_id', course['level'])))
            info.add_child(Node.bool('matching_assist', course['level'] >= 0 and course['level'] <= 6))
            info.add_child(Node.s16('gauge_type', self.GAME_GAUGE_TYPE_SKILL))
            info.add_child(Node.s16('paseli_type', 0))

            for trackno, trackdata in enumerate(course['tracks']):
                track = Node.void('track')
                info.add_child(track)
                track.add_child(Node.s16('track_no', trackno))
                track.add_child(Node.s32('music_id', trackdata['id']))
                track.add_child(Node.s8('music_type', trackdata['type']))

        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-M01'))
        game.add_child(
            Node.string('etc', 'TaxMode:1,BasicRate:100/1,FirstFree:0'))

        # Swap with server
        resp = self.exchange('', call)

        # Verify that response is correct
        self.assert_path(resp, "response/game_3/nxt_time")
Exemple #11
0
    def verify_gameend_regist(self, ref_id: str, jid: int, mode: int,
                              scores: List[Dict[str, Any]],
                              course: Dict[str, Any]) -> None:
        call = self.call_node()

        # Construct node
        gameend = Node.void('gameend')
        call.add_child(gameend)
        gameend.set_attribute('method', 'regist')
        gameend.add_child(Node.s32('retry', 0))
        data = Node.void('data')
        gameend.add_child(data)
        player = Node.void('player')
        data.add_child(player)
        player.add_child(Node.s8('mode', mode))
        player.add_child(Node.string('refid', ref_id))
        player.add_child(Node.s32('jid', jid))
        player.add_child(Node.string('name', self.NAME))
        result = Node.void('result')
        data.add_child(result)
        result.set_attribute('count', str(len(scores)))

        # Send scores
        scoreid = 0
        for score in scores:
            # Always played
            bits = 0x1
            if score['clear']:
                bits |= 0x2
            if score['fc']:
                bits |= 0x4
            if score['ex']:
                bits |= 0x8

            # Intentionally starting at 1 because that's what the game does
            scoreid = scoreid + 1
            tune = Node.void('tune')
            result.add_child(tune)
            tune.set_attribute('id', str(scoreid))
            tune.set_attribute('count', '0')
            tune.add_child(Node.s32('music', score['id']))
            player_1 = Node.void('player')
            tune.add_child(player_1)
            player_1.set_attribute('rank', '1')
            scorenode = Node.s32('score', score['score'])
            player_1.add_child(scorenode)
            scorenode.set_attribute('seq', str(score['chart']))
            scorenode.set_attribute('clear', str(bits))
            scorenode.set_attribute('combo', '69')
            player_1.add_child(
                Node.u8_array('mbar', [
                    239, 175, 170, 170, 190, 234, 187, 158, 153, 230, 170, 90,
                    102, 170, 85, 150, 150, 102, 85, 234, 171, 169, 157, 150,
                    170, 101, 230, 90, 214, 255
                ]))

        if len(course) > 0:
            coursenode = Node.void('course')
            data.add_child(coursenode)
            coursenode.add_child(Node.s32('course_id', course['course_id']))
            coursenode.add_child(Node.u8('rating', course['rating']))
            index = 0
            for coursescore in course['scores']:
                music = Node.void('music')
                coursenode.add_child(music)
                music.set_attribute('index', str(index))
                music.add_child(Node.s32('score', coursescore))
                index = index + 1

        # Swap with server
        resp = self.exchange('', call)
        self.assert_path(resp, "response/gameend/data/player/session_id")
    def verify_game_load(self, cardid: str, refid: str,
                         msg_type: str) -> Dict[str, Any]:
        call = self.call_node()

        game = Node.void('game_3')
        call.add_child(game)
        game.set_attribute('method', 'load')
        game.set_attribute('ver', '0')
        game.add_child(Node.string('dataid', refid))
        game.add_child(Node.string('cardid', cardid))
        game.add_child(Node.string('refid', refid))

        # Swap with server
        resp = self.exchange('', call)

        # Verify that response is correct
        if msg_type == "new":
            self.assert_path(resp, "response/game_3/result")
            if resp.child_value('game_3/result') != 1:
                raise Exception("Invalid result for new profile!")
            return None

        if msg_type == "existing":
            self.assert_path(resp, "response/game_3/name")
            self.assert_path(resp, "response/game_3/code")
            self.assert_path(resp, "response/game_3/gamecoin_packet")
            self.assert_path(resp, "response/game_3/gamecoin_block")
            self.assert_path(resp, "response/game_3/skill_name_id")
            self.assert_path(resp, "response/game_3/hidden_param")
            self.assert_path(resp, "response/game_3/blaster_energy")
            self.assert_path(resp, "response/game_3/blaster_count")
            self.assert_path(resp, "response/game_3/play_count")
            self.assert_path(resp, "response/game_3/daily_count")
            self.assert_path(resp, "response/game_3/play_chain")
            self.assert_path(resp, "response/game_3/item")
            self.assert_path(resp, "response/game_3/skill/course_all")
            self.assert_path(resp, "response/game_3/story")

            items: Dict[int, Dict[int, int]] = {}
            for child in resp.child('game_3/item').children:
                if child.name != 'info':
                    continue

                itype = child.child_value('type')
                iid = child.child_value('id')
                param = child.child_value('param')

                if itype not in items:
                    items[itype] = {}
                items[itype][iid] = param

            courses: Dict[int, Dict[int, Dict[str, int]]] = {}
            for child in resp.child('game_3/skill/course_all').children:
                if child.name != 'd':
                    continue

                crsid = child.child_value('crsid')
                season = child.child_value('ssnid')
                achievement_rate = child.child_value('ar')
                clear_type = child.child_value('ct')

                if season not in courses:
                    courses[season] = {}
                courses[season][crsid] = {
                    'achievement_rate': achievement_rate,
                    'clear_type': clear_type,
                }

            return {
                'name': resp.child_value('game_3/name'),
                'packet': resp.child_value('game_3/gamecoin_packet'),
                'block': resp.child_value('game_3/gamecoin_block'),
                'blaster_energy': resp.child_value('game_3/blaster_energy'),
                'items': items,
                'courses': courses,
            }
        else:
            raise Exception("Invalid game load type {}".format(msg_type))
Exemple #13
0
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
Exemple #14
0
    def _add_shop_score(self, root: Node) -> None:
        shop_score = Node.void('shop_score')
        root.add_child(shop_score)
        today = Node.void('today')
        shop_score.add_child(today)
        yesterday = Node.void('yesterday')
        shop_score.add_child(yesterday)

        all_profiles = self.data.local.user.get_all_profiles(
            self.game, self.version)
        all_attempts = self.data.local.music.get_all_attempts(
            self.game,
            self.music_version,
            timelimit=(Time.beginning_of_today() - Time.SECONDS_IN_DAY))
        machine = self.data.local.machine.get_machine(
            self.config['machine']['pcbid'])
        if machine.arcade is not None:
            lids = [
                machine.id for machine in
                self.data.local.machine.get_all_machines(machine.arcade)
            ]
        else:
            lids = [machine.id]

        relevant_profiles = [
            profile for profile in all_profiles
            if profile[1].get_int('lid', -1) in lids
        ]

        for (rootnode, timeoffset) in [
            (today, 0),
            (yesterday, Time.SECONDS_IN_DAY),
        ]:
            # Grab all attempts made in the relevant day
            relevant_attempts = [
                attempt for attempt in all_attempts
                if (attempt[1].timestamp >= (Time.beginning_of_today() -
                                             timeoffset) and
                    attempt[1].timestamp <= (Time.end_of_today() - timeoffset))
            ]

            # Calculate scores based on attempt
            scores_by_user: Dict[UserID, Dict[int, Dict[int, Attempt]]] = {}
            for (userid, attempt) in relevant_attempts:
                if userid not in scores_by_user:
                    scores_by_user[userid] = {}
                if attempt.id not in scores_by_user[userid]:
                    scores_by_user[userid][attempt.id] = {}
                if attempt.chart not in scores_by_user[userid][attempt.id]:
                    # No high score for this yet, just use this attempt
                    scores_by_user[userid][attempt.id][attempt.chart] = attempt
                else:
                    # If this attempt is better than the stored one, replace it
                    if scores_by_user[userid][attempt.id][
                            attempt.chart].points < attempt.points:
                        scores_by_user[userid][attempt.id][
                            attempt.chart] = attempt

            # Calculate points earned by user in the day
            points_by_user: Dict[UserID, int] = {}
            for userid in scores_by_user:
                points_by_user[userid] = 0
                for mid in scores_by_user[userid]:
                    for chart in scores_by_user[userid][mid]:
                        points_by_user[userid] = points_by_user[
                            userid] + scores_by_user[userid][mid][chart].points

            # Output that day's earned points
            for (userid, profile) in relevant_profiles:
                data = Node.void('data')
                rootnode.add_child(data)
                data.add_child(
                    Node.s16(
                        'day_id',
                        int((Time.now() - timeoffset) / Time.SECONDS_IN_DAY)))
                data.add_child(Node.s32('user_id', profile.get_int('extid')))
                data.add_child(
                    Node.s16('icon_id',
                             profile.get_dict('config').get_int('icon_id')))
                data.add_child(
                    Node.s16('point', min(points_by_user.get(userid, 0),
                                          32767)))
                data.add_child(Node.s32('update_time', Time.now()))
                data.add_child(Node.string('name', profile.get_str('name')))

            rootnode.add_child(
                Node.s32('time',
                         Time.beginning_of_today() - timeoffset))
Exemple #15
0
    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))
        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('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('flag', 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('last', 0))
        con.add_child(Node.s32('now', 0))
        custom = Node.void('custom')
        pdata.add_child(custom)
        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))
        pdata.add_child(Node.void('released'))

        # First, filter down to only records that are also in the battle log
        def key(thing: Dict[str, int]) -> str:
            return '{}-{}'.format(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('win', 1))
            rec.add_child(Node.s32('lose', 0))
            rec.add_child(Node.s32('draw', 0))
            rec.add_child(Node.u8('ct', record['clear_type']))
            rec.add_child(Node.s16('ar', record['achievement_rate']))
            rec.add_child(Node.s16('bs', record['score']))
            rec.add_child(Node.s16('mc', record['combo']))
            rec.add_child(Node.s16('bmc', record['miss_count']))

        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.s16('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.s16('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')
Exemple #16
0
    def verify_player23_read(
            self, ref_id: str,
            msg_type: str) -> Dict[str, Dict[int, Dict[str, int]]]:
        call = self.call_node()

        # Construct node
        player23 = Node.void('player23')
        call.add_child(player23)
        player23.set_attribute('method', 'read')

        player23.add_child(Node.string('ref_id', ref_id))
        player23.add_child(Node.s8('pref', 51))

        # Swap with server
        resp = self.exchange('', call)

        if msg_type == 'new':
            # Verify that response is correct
            self.assert_path(resp, "response/player23/result")
            status = resp.child_value('player23/result')
            if status != 2:
                raise Exception(
                    'Reference ID \'{}\' returned invalid status \'{}\''.
                    format(ref_id, status))

            return {
                'medals': {},
                'items': {},
                'characters': {},
                'lumina': {},
            }
        elif msg_type == 'query':
            # Verify that the response is correct
            self.__verify_profile(resp)

            self.assert_path(resp, "response/player23/result")
            status = resp.child_value('player23/result')
            if status != 0:
                raise Exception(
                    'Reference ID \'{}\' returned invalid status \'{}\''.
                    format(ref_id, status))
            name = resp.child_value('player23/account/name')
            if name != self.NAME:
                raise Exception(
                    'Invalid name \'{}\' returned for Ref ID \'{}\''.format(
                        name, ref_id))

            # Medals and items
            items: Dict[int, Dict[str, int]] = {}
            medals: Dict[int, Dict[str, int]] = {}
            charas: Dict[int, Dict[str, int]] = {}
            for obj in resp.child('player23').children:
                if obj.name == 'medal':
                    medals[obj.child_value('medal_id')] = {
                        'level': obj.child_value('level'),
                        'exp': obj.child_value('exp'),
                    }
                elif obj.name == 'item':
                    items[obj.child_value('id')] = {
                        'type': obj.child_value('type'),
                        'param': obj.child_value('param'),
                    }
                elif obj.name == 'chara_param':
                    charas[obj.child_value('chara_id')] = {
                        'friendship': obj.child_value('friendship'),
                    }

            return {
                'medals': medals,
                'items': items,
                'characters': charas,
                'lumina': {
                    0: {
                        'lumina': resp.child_value('player23/account/lumina')
                    }
                },
            }
        else:
            raise Exception(
                'Unrecognized message type \'{}\''.format(msg_type))
Exemple #17
0
    def handle_playerdata_request(self, request: Node) -> Optional[Node]:
        method = request.attribute('method')

        if method == 'expire':
            return Node.void('playerdata')

        elif method == 'logout':
            return Node.void('playerdata')

        elif method == 'get':
            modelstring = request.attribute('model')
            refid = request.child_value('ref_id')
            root = self.get_profile_by_refid(
                refid,
                self.NEW_PROFILE_ONLY
                if modelstring is None else self.OLD_PROFILE_ONLY,
            )
            if root is None:
                root = Node.void('playerdata')
                root.set_attribute('status', str(Status.NO_PROFILE))
            return root

        elif method == 'conversion':
            refid = request.child_value('ref_id')
            name = request.child_value('name')
            chara = request.child_value('chara')
            root = self.new_profile_by_refid(refid, name, chara)
            if root is None:
                root = Node.void('playerdata')
                root.set_attribute('status', str(Status.NO_PROFILE))
            return root

        elif method == 'new':
            refid = request.child_value('ref_id')
            name = request.child_value('name')
            root = self.new_profile_by_refid(refid, name)
            if root is None:
                root = Node.void('playerdata')
                root.set_attribute('status', str(Status.NO_PROFILE))
            return root

        elif method == 'set':
            refid = request.attribute('ref_id')

            root = Node.void('playerdata')
            root.add_child(Node.s8('pref', -1))
            if refid is None:
                return root

            userid = self.data.remote.user.from_refid(self.game, self.version,
                                                      refid)
            if userid is None:
                return root

            oldprofile = self.get_profile(userid) or ValidatedDict()
            newprofile = self.unformat_profile(userid, request, oldprofile)

            if newprofile is not None:
                self.put_profile(userid, newprofile)
                root.add_child(Node.string('name', newprofile['name']))

            return root

        # Invalid method
        return None
Exemple #18
0
    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
Exemple #19
0
    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)
        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.u64('pst', 0))
        account.add_child(Node.u64('st', Time.now() * 1000))
        account.add_child(Node.bool('succeed', succeeded))
        account.add_child(Node.s32('opc', 0))
        account.add_child(Node.s32('lpc', 0))
        account.add_child(Node.s32('cpc', 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', -1))
        base.add_child(Node.s32_array('tbgs', [-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')))

        # Rivals
        rival = Node.void('rival')
        pdata.add_child(rival)
        slotid = 0
        for link in links:
            if link.type != 'rival':
                continue

            rprofile = self.get_profile(link.other_userid)
            if rprofile is None:
                continue
            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_jdg_disp', customdict.get_int('st_jdg_disp')))
        custom.add_child(Node.u8('st_rnd', customdict.get_int('st_rnd')))
        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('same_time_note_disp', customdict.get_int('same_time_note_disp')))
        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('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')))

        if game_config.get_bool('force_unlock_songs'):
            ids: Dict[int, int] = {}
            songs = self.data.local.music.get_all_songs(self.game, self.version)
            for song in songs:
                if song.id not in ids:
                    ids[song.id] = 0

                if song.data.get_int('difficulty') > 0:
                    ids[song.id] = ids[song.id] | (1 << song.chart)

            for songid in ids:
                if ids[songid] == 0:
                    continue

                info = Node.void('info')
                released.add_child(info)
                info.add_child(Node.u8('type', 0))
                info.add_child(Node.u16('id', songid))
                info.add_child(Node.u16('param', ids[songid]))

        # 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))

        return root
Exemple #20
0
    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
Exemple #21
0
    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")
Exemple #22
0
    def handle_event_r_get_all_request(self, request: Node) -> Node:
        limit = request.child_value('limit')

        comments = [
            achievement for achievement in self.data.local.user.
            get_all_time_based_achievements(self.game, self.version)
            if achievement[1].type == 'puzzle_comment'
        ]
        comments.sort(key=lambda x: x[1].timestamp, reverse=True)
        statuses = self.data.local.lobby.get_all_play_session_infos(
            self.game, self.version)
        statuses.sort(key=lambda x: x[1]['time'], reverse=True)

        # Cap all comment blocks to the limit
        if limit >= 0:
            comments = comments[:limit]
            statuses = statuses[:limit]

        # Mapping of profiles to userIDs
        uid_mapping = {
            uid: prof
            for (uid, prof) in self.get_any_profiles([c[0] for c in comments] +
                                                     [s[0] for s in statuses])
        }

        # Mapping of location ID to machine name
        lid_mapping: Dict[int, str] = {}

        root = Node.void('event_r')
        root.add_child(Node.s32('time', Time.now()))
        statusnode = Node.void('status')
        root.add_child(statusnode)
        commentnode = Node.void('comment')
        root.add_child(commentnode)

        for (uid, comment) in comments:
            lid = ID.parse_machine_id(comment.data.get_str('lid'))

            # Look up external data for the request
            if lid not in lid_mapping:
                machine = self.get_machine_by_id(lid)
                if machine is not None:
                    lid_mapping[lid] = machine.name
                else:
                    lid_mapping[lid] = ''

            c = Node.void('c')
            commentnode.add_child(c)
            c.add_child(Node.s32('uid', uid_mapping[uid].get_int('extid')))
            c.add_child(Node.string('p_name',
                                    uid_mapping[uid].get_str('name')))
            c.add_child(Node.s32('exp', uid_mapping[uid].get_int('exp')))
            c.add_child(
                Node.s32('customize', comment.data.get_int('customize')))
            c.add_child(Node.s32('tid', comment.data.get_int('teamid')))
            c.add_child(Node.string('t_name',
                                    comment.data.get_str('teamname')))
            c.add_child(Node.string('lid', comment.data.get_str('lid')))
            c.add_child(Node.string('s_name', lid_mapping[lid]))
            c.add_child(Node.s8('pref', comment.data.get_int('prefecture')))
            c.add_child(Node.s32('time', comment.timestamp))
            c.add_child(Node.string('comment',
                                    comment.data.get_str('comment')))
            c.add_child(Node.bool('is_tweet', comment.data.get_bool('tweet')))

        for (uid, status) in statuses:
            lid = ID.parse_machine_id(status.get_str('lid'))

            # Look up external data for the request
            if lid not in lid_mapping:
                machine = self.get_machine_by_id(lid)
                if machine is not None:
                    lid_mapping[lid] = machine.name
                else:
                    lid_mapping[lid] = ''

            s = Node.void('s')
            statusnode.add_child(s)
            s.add_child(Node.s32('uid', uid_mapping[uid].get_int('extid')))
            s.add_child(Node.string('p_name',
                                    uid_mapping[uid].get_str('name')))
            s.add_child(Node.s32('exp', uid_mapping[uid].get_int('exp')))
            s.add_child(Node.s32('customize', status.get_int('customize')))
            s.add_child(
                Node.s32('tid', uid_mapping[uid].get_int('team_id', -1)))
            s.add_child(
                Node.string('t_name',
                            uid_mapping[uid].get_str('team_name', '')))
            s.add_child(Node.string('lid', status.get_str('lid')))
            s.add_child(Node.string('s_name', lid_mapping[lid]))
            s.add_child(Node.s8('pref', status.get_int('prefecture')))
            s.add_child(Node.s32('time', status.get_int('time')))
            s.add_child(Node.s8('status', status.get_int('status')))
            s.add_child(Node.s8('stage', status.get_int('stage')))
            s.add_child(Node.s32('mid', status.get_int('mid')))
            s.add_child(Node.s8('ng', status.get_int('ng')))

        return root
Exemple #23
0
    def verify_player22_read(self, ref_id: str,
                             msg_type: str) -> Optional[Dict[str, Any]]:
        call = self.call_node()

        # Construct node
        player22 = Node.void('player22')
        call.add_child(player22)
        player22.set_attribute('method', 'read')

        player22.add_child(Node.string('ref_id', value=ref_id))
        player22.add_child(Node.string('shop_name', ''))
        player22.add_child(Node.s8('pref', 51))

        # Swap with server
        resp = self.exchange('', call)

        if msg_type == 'new':
            # Verify that response is correct
            self.assert_path(resp, "response/player22/@status")

            status = int(resp.child('player22').attribute('status'))
            if status != 109:
                raise Exception(
                    'Reference ID \'{}\' returned invalid status \'{}\''.
                    format(ref_id, status))

            # No score data
            return None
        elif msg_type == 'query':
            # Verify that the response is correct
            self.assert_path(resp, "response/player22/account/name")
            self.assert_path(resp, "response/player22/account/g_pm_id")
            self.assert_path(resp, "response/player22/account/my_best")
            self.assert_path(resp, "response/player22/account/latest_music")
            self.assert_path(resp, "response/player22/netvs")
            self.assert_path(resp, "response/player22/config")
            self.assert_path(resp, "response/player22/option")
            self.assert_path(resp, "response/player22/info")
            self.assert_path(resp, "response/player22/custom_cate")
            self.assert_path(resp, "response/player22/customize")

            name = resp.child('player22').child('account').child('name').value
            if name != self.NAME:
                raise Exception(
                    'Invalid name \'{}\' returned for Ref ID \'{}\''.format(
                        name, ref_id))

            # Extract and return score data
            medals: Dict[int, List[int]] = {}
            scores: Dict[int, List[int]] = {}
            for child in resp.child('player22').children:
                if child.name == 'music':
                    songid = child.child_value('music_num')
                    chart = child.child_value('sheet_num')
                    medal = child.child_value('clear_type')
                    points = child.child_value('score')

                    if songid not in medals:
                        medals[songid] = [0, 0, 0, 0]
                    medals[songid][chart] = medal
                    if songid not in scores:
                        scores[songid] = [0, 0, 0, 0]
                    scores[songid][chart] = points

            return {'medals': medals, 'scores': scores}

        else:
            raise Exception(
                'Unrecognized message type \'{}\''.format(msg_type))
Exemple #24
0
    def format_profile(self, userid: UserID, profile: ValidatedDict) -> Node:
        statistics = self.get_play_statistics(userid)
        game_config = self.get_game_config()
        achievements = self.data.local.user.get_achievements(
            self.game, self.version, userid)
        scores = self.data.remote.music.get_scores(self.game, self.version,
                                                   userid)
        links = self.data.local.user.get_links(self.game, self.version, userid)
        root = Node.void('player')
        pdata = Node.void('pdata')
        root.add_child(pdata)

        base = Node.void('base')
        pdata.add_child(base)
        base.add_child(Node.s32('uid', profile.get_int('extid')))
        base.add_child(Node.string('name', profile.get_str('name')))
        base.add_child(Node.s16('icon_id', profile.get_int('icon')))
        base.add_child(Node.s16('lv', profile.get_int('lvl')))
        base.add_child(Node.s32('exp', profile.get_int('exp')))
        base.add_child(Node.s16('mg', profile.get_int('mg')))
        base.add_child(Node.s16('ap', profile.get_int('ap')))
        base.add_child(Node.s32('pc', profile.get_int('pc')))
        base.add_child(Node.s32('uattr', profile.get_int('uattr')))

        last_play_date = statistics.get_int_array('last_play_date', 3)
        today_play_date = Time.todays_date()
        if (last_play_date[0] == today_play_date[0]
                and last_play_date[1] == today_play_date[1]
                and last_play_date[2] == today_play_date[2]):
            today_count = statistics.get_int('today_plays', 0)
        else:
            today_count = 0

        con = Node.void('con')
        pdata.add_child(con)
        con.add_child(Node.s32('day', today_count))
        con.add_child(Node.s32('cnt', statistics.get_int('total_plays')))
        con.add_child(Node.s32('total_cnt', statistics.get_int('total_plays')))
        con.add_child(
            Node.s32('last', statistics.get_int('last_play_timestamp')))
        con.add_child(Node.s32('now', Time.now()))

        team = Node.void('team')
        pdata.add_child(team)
        team.add_child(Node.s32('id', profile.get_int('team_id', -1)))
        team.add_child(Node.string('name', profile.get_str('team_name', '')))

        custom = Node.void('custom')
        customdict = profile.get_dict('custom')
        pdata.add_child(custom)
        custom.add_child(Node.u8('s_gls', customdict.get_int('s_gls')))
        custom.add_child(Node.u8('bgm_m', customdict.get_int('bgm_m')))
        custom.add_child(Node.u8('st_f', customdict.get_int('st_f')))
        custom.add_child(Node.u8('st_bg', customdict.get_int('st_bg')))
        custom.add_child(Node.u8('st_bg_b', customdict.get_int('st_bg_b')))
        custom.add_child(Node.u8('eff_e', customdict.get_int('eff_e')))
        custom.add_child(Node.u8('se_s', customdict.get_int('se_s')))
        custom.add_child(Node.u8('se_s_v', customdict.get_int('se_s_v')))
        custom.add_child(
            Node.s16('last_music_id', customdict.get_int('last_music_id')))
        custom.add_child(
            Node.u8('last_note_grade', customdict.get_int('last_note_grade')))
        custom.add_child(Node.u8('sort_type', customdict.get_int('sort_type')))
        custom.add_child(
            Node.u8('narrowdown_type', customdict.get_int('narrowdown_type')))
        custom.add_child(
            Node.bool('is_begginer', customdict.get_bool(
                'is_begginer')))  # Yes, this is spelled right
        custom.add_child(Node.bool('is_tut', customdict.get_bool('is_tut')))
        custom.add_child(
            Node.s16_array('symbol_chat_0',
                           customdict.get_int_array('symbol_chat_0', 6)))
        custom.add_child(
            Node.s16_array('symbol_chat_1',
                           customdict.get_int_array('symbol_chat_1', 6)))
        custom.add_child(
            Node.u8('gauge_style', customdict.get_int('gauge_style')))
        custom.add_child(Node.u8('obj_shade', customdict.get_int('obj_shade')))
        custom.add_child(Node.u8('obj_size', customdict.get_int('obj_size')))
        custom.add_child(
            Node.s16_array('byword', customdict.get_int_array('byword', 2)))
        custom.add_child(
            Node.bool_array('is_auto_byword',
                            customdict.get_bool_array('is_auto_byword', 2)))
        custom.add_child(Node.bool('is_tweet',
                                   customdict.get_bool('is_tweet')))
        custom.add_child(
            Node.bool('is_link_twitter',
                      customdict.get_bool('is_link_twitter')))
        custom.add_child(Node.s16('mrec_type',
                                  customdict.get_int('mrec_type')))
        custom.add_child(
            Node.s16('card_disp_type', customdict.get_int('card_disp_type')))
        custom.add_child(Node.s16('tab_sel', customdict.get_int('tab_sel')))
        custom.add_child(
            Node.s32_array('hidden_param',
                           customdict.get_int_array('hidden_param', 20)))

        released = Node.void('released')
        pdata.add_child(released)

        for item in achievements:
            if item.type[:5] != 'item_':
                continue
            itemtype = int(item.type[5:])
            if game_config.get_bool('force_unlock_songs') and itemtype == 0:
                # Don't echo unlocks when we're force unlocking, we'll do it later
                continue

            info = Node.void('info')
            released.add_child(info)
            info.add_child(Node.u8('type', itemtype))
            info.add_child(Node.u16('id', item.id))
            info.add_child(Node.u16('param', item.data.get_int('param')))

        if game_config.get_bool('force_unlock_songs'):
            ids: Dict[int, int] = {}
            songs = self.data.local.music.get_all_songs(
                self.game, self.version)
            for song in songs:
                if song.id not in ids:
                    ids[song.id] = 0

                if song.data.get_int('difficulty') > 0:
                    ids[song.id] = ids[song.id] | (1 << song.chart)

            for songid in ids:
                if ids[songid] == 0:
                    continue

                info = Node.void('info')
                released.add_child(info)
                info.add_child(Node.u8('type', 0))
                info.add_child(Node.u16('id', songid))
                info.add_child(Node.u16('param', ids[songid]))

        # Scores
        record = Node.void('record')
        pdata.add_child(record)

        for score in scores:
            rec = Node.void('rec')
            record.add_child(rec)
            rec.add_child(Node.u16('mid', score.id))
            rec.add_child(Node.u8('ng', score.chart))
            rec.add_child(
                Node.s32(
                    'point',
                    score.data.get_dict('stats').get_int('earned_points')))
            rec.add_child(Node.s32('played_time', score.timestamp))

            mrec_0 = Node.void('mrec_0')
            rec.add_child(mrec_0)
            mrec_0.add_child(
                Node.s32('win',
                         score.data.get_dict('stats').get_int('win')))
            mrec_0.add_child(
                Node.s32('lose',
                         score.data.get_dict('stats').get_int('lose')))
            mrec_0.add_child(
                Node.s32('draw',
                         score.data.get_dict('stats').get_int('draw')))
            mrec_0.add_child(
                Node.u8(
                    'ct',
                    self.__db_to_game_clear_type(
                        score.data.get_int('clear_type'),
                        score.data.get_int('combo_type'))))
            mrec_0.add_child(
                Node.s16('ar',
                         int(score.data.get_int('achievement_rate') / 10)))
            mrec_0.add_child(Node.s32('bs', score.points))
            mrec_0.add_child(Node.s16('mc', score.data.get_int('combo')))
            mrec_0.add_child(Node.s16('bmc', score.data.get_int('miss_count')))

            mrec_1 = Node.void('mrec_1')
            rec.add_child(mrec_1)
            mrec_1.add_child(Node.s32('win', 0))
            mrec_1.add_child(Node.s32('lose', 0))
            mrec_1.add_child(Node.s32('draw', 0))
            mrec_1.add_child(Node.u8('ct', 0))
            mrec_1.add_child(Node.s16('ar', 0))
            mrec_1.add_child(Node.s32('bs', 0))
            mrec_1.add_child(Node.s16('mc', 0))
            mrec_1.add_child(Node.s16('bmc', -1))

        # Comment (seems unused?)
        pdata.add_child(Node.string('cmnt', ''))

        # Rivals
        rival = Node.void('rival')
        pdata.add_child(rival)

        slotid = 0
        for link in links:
            if link.type != 'rival':
                continue

            rprofile = self.get_profile(link.other_userid)
            if rprofile is None:
                continue

            r = Node.void('r')
            rival.add_child(r)
            r.add_child(Node.u8('slot_id', slotid))
            r.add_child(Node.string('name', rprofile.get_str('name')))
            r.add_child(Node.s32('id', rprofile.get_int('extid')))
            r.add_child(Node.bool('friend', True))
            r.add_child(Node.bool('locked', False))
            r.add_child(Node.s32('rc', 0))
            slotid = slotid + 1

        # Glass points
        glass = Node.void('glass')
        pdata.add_child(glass)

        for item in achievements:
            if item.type != 'glass':
                continue

            g = Node.void('g')
            glass.add_child(g)
            g.add_child(Node.s32('id', item.id))
            g.add_child(Node.s32('exp', item.data.get_int('exp')))

        # Favorite music
        fav_music_slot = Node.void('fav_music_slot')
        pdata.add_child(fav_music_slot)

        for item in achievements:
            if item.type != 'music':
                continue

            slot = Node.void('slot')
            fav_music_slot.add_child(slot)
            slot.add_child(Node.u8('slot_id', item.id))
            slot.add_child(Node.s16('music_id', item.data.get_int('music_id')))

        narrow_down = Node.void('narrow_down')
        pdata.add_child(narrow_down)
        narrow_down.add_child(
            Node.s32_array('adv_param', [
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1
            ]))

        return root
Exemple #25
0
    def verify_playerdata_get(self, ref_id: str,
                              msg_type: str) -> Optional[Dict[str, Any]]:
        call = self.call_node()

        # Construct node
        playerdata = Node.void('playerdata')
        call.add_child(playerdata)
        playerdata.set_attribute('method', 'get')
        if msg_type == 'new':
            playerdata.set_attribute(
                'model', self.config['old_profile_model'].split(':')[0])

        playerdata.add_child(Node.string('ref_id', ref_id))
        playerdata.add_child(Node.string('shop_name', ''))
        playerdata.add_child(Node.s8('pref', 50))
        playerdata.add_child(Node.s32('navigate', 1))

        # Swap with server
        resp = self.exchange('pnm20/playerdata', call)

        if msg_type == 'new':
            # Verify that response is correct
            self.assert_path(resp, "response/playerdata/@status")

            status = int(resp.child('playerdata').attribute('status'))
            if status != 109:
                raise Exception(
                    f'Reference ID \'{ref_id}\' returned invalid status \'{status}\''
                )

            # No score data
            return None
        elif msg_type == 'query':
            # Verify that the response is correct
            self.assert_path(resp, "response/playerdata/base/g_pm_id")
            self.assert_path(resp, "response/playerdata/base/name")
            self.assert_path(resp, "response/playerdata/base/my_best")
            self.assert_path(resp, "response/playerdata/base/latest_music")
            self.assert_path(resp, "response/playerdata/base/clear_medal")
            self.assert_path(resp, "response/playerdata/base/clear_medal_sub")
            self.assert_path(resp, "response/playerdata/player_card")
            self.assert_path(resp, "response/playerdata/player_card_ex")
            self.assert_path(resp, "response/playerdata/hiscore")
            self.assert_path(resp, "response/playerdata/netvs")
            self.assert_path(resp, "response/playerdata/sp_data")
            self.assert_path(resp, "response/playerdata/reflec_data")
            self.assert_path(resp, "response/playerdata/navigate")

            name = resp.child('playerdata').child('base').child('name').value
            if name != self.NAME:
                raise Exception(
                    f'Invalid name \'{name}\' returned for Ref ID \'{ref_id}\''
                )

            # Extract and return score data
            self.assert_path(resp, "response/playerdata/base/clear_medal")

            def transform_medals(medal: int) -> Tuple[int, int, int, int]:
                return (
                    (medal >> 0) & 0xF,
                    (medal >> 4) & 0xF,
                    (medal >> 8) & 0xF,
                    (medal >> 12) & 0xF,
                )

            medals = [
                transform_medals(medal) for medal in resp.child(
                    'playerdata').child('base').child('clear_medal').value
            ]

            hiscore = resp.child('playerdata').child('hiscore').value
            hiscores = []
            for i in range(0, len(hiscore) * 8, 17):
                byte_offset = int(i / 8)
                bit_offset = int(i % 8)

                value = hiscore[byte_offset]
                value = value + (hiscore[byte_offset + 1] << 8)
                value = value + (hiscore[byte_offset + 2] << 16)

                value = value >> bit_offset
                hiscores.append(value & 0x1FFFF)

            scores = [(hiscores[x], hiscores[x + 1], hiscores[x + 2],
                       hiscores[x + 3]) for x in range(0, len(hiscores), 4)]

            return {'medals': medals, 'scores': scores}

        else:
            raise Exception(f'Unrecognized message type \'{msg_type}\'')
Exemple #26
0
    def handle_gametop_get_course_request(self, request: Node) -> Node:
        data = request.child('data')
        player = data.child('player')
        extid = player.child_value('jid')

        gametop = Node.void('gametop')
        data = Node.void('data')
        gametop.add_child(data)

        # Course list available
        course_list = Node.void('course_list')
        data.add_child(course_list)

        validcourses: List[int] = []
        for course in self.get_all_courses():
            coursenode = Node.void('course')
            course_list.add_child(coursenode)

            # Basic course info
            if course['id'] in validcourses:
                raise Exception('Cannot have same course ID specified twice!')
            validcourses.append(course['id'])
            coursenode.add_child(Node.s32('id', course['id']))
            coursenode.add_child(Node.string('name', course['name']))
            coursenode.add_child(Node.u8('level', course['level']))

            # Translate internal to game
            def translate_req(internal_req: int) -> int:
                return {
                    self.COURSE_REQUIREMENT_SCORE: self.GAME_COURSE_REQUIREMENT_SCORE,
                    self.COURSE_REQUIREMENT_FULL_COMBO: self.GAME_COURSE_REQUIREMENT_FULL_COMBO,
                    self.COURSE_REQUIREMENT_PERFECT_PERCENT: self.GAME_COURSE_REQUIREMENT_PERFECT_PERCENT,
                }.get(internal_req, 0)

            # Course bronze/silver/gold rules
            ids = [0] * 3
            bronze_values = [0] * 3
            silver_values = [0] * 3
            gold_values = [0] * 3
            slot = 0
            for req in course['requirements']:
                req_values = course['requirements'][req]

                ids[slot] = translate_req(req)
                bronze_values[slot] = req_values[0]
                silver_values[slot] = req_values[1]
                gold_values[slot] = req_values[2]
                slot = slot + 1

            norma = Node.void('norma')
            coursenode.add_child(norma)
            norma.add_child(Node.s32_array('norma_id', ids))
            norma.add_child(Node.s32_array('bronze_value', bronze_values))
            norma.add_child(Node.s32_array('silver_value', silver_values))
            norma.add_child(Node.s32_array('gold_value', gold_values))

            # Music list for course
            music_index = 0
            music_list = Node.void('music_list')
            coursenode.add_child(music_list)

            for entry in course['music']:
                music = Node.void('music')
                music.set_attribute('index', str(music_index))
                music_list.add_child(music)
                music.add_child(Node.s32('music_id', entry[0]))
                music.add_child(Node.u8('seq', entry[1]))
                music_index = music_index + 1

        # Look up profile so we can load the last course played
        userid = self.data.remote.user.from_extid(self.game, self.version, extid)
        profile = self.get_profile(userid)
        if profile is None:
            profile = ValidatedDict()

        # Player scores for courses
        player_list = Node.void('player_list')
        data.add_child(player_list)
        player = Node.void('player')
        player_list.add_child(player)
        player.add_child(Node.s32('jid', extid))

        result_list = Node.void('result_list')
        player.add_child(result_list)
        playercourses = self.get_courses(userid)
        for courseid in playercourses:
            if courseid not in validcourses:
                continue

            rating = {
                self.COURSE_RATING_FAILED: self.GAME_COURSE_RATING_FAILED,
                self.COURSE_RATING_BRONZE: self.GAME_COURSE_RATING_BRONZE,
                self.COURSE_RATING_SILVER: self.GAME_COURSE_RATING_SILVER,
                self.COURSE_RATING_GOLD: self.GAME_COURSE_RATING_GOLD,
            }[playercourses[courseid]['rating']]
            scores = playercourses[courseid]['scores']

            result = Node.void('result')
            result_list.add_child(result)
            result.add_child(Node.s32('id', courseid))
            result.add_child(Node.u8('rating', rating))
            result.add_child(Node.s32_array('score', scores))

        # Last course ID
        last_course_id = Node.s32('last_course_id', profile.get_dict('last').get_int('last_course_id', -1))
        data.add_child(last_course_id)

        return gametop
Exemple #27
0
    def format_conversion(self, userid: UserID,
                          profile: ValidatedDict) -> Node:
        # Circular import, ugh
        from bemani.backend.popn.eclale import PopnMusicEclale

        root = Node.void('player23')
        root.add_child(Node.string('name', profile.get_str('name', 'なし')))
        root.add_child(Node.s16('chara', profile.get_int('chara', -1)))
        root.add_child(Node.s8('result', 1))

        scores = self.data.remote.music.get_scores(self.game,
                                                   self.music_version, userid)
        for score in scores:
            if score.id > self.GAME_MAX_MUSIC_ID:
                continue

            # Skip any scores for chart types we don't support
            if score.chart not in [
                    self.CHART_TYPE_EASY,
                    self.CHART_TYPE_NORMAL,
                    self.CHART_TYPE_HYPER,
                    self.CHART_TYPE_EX,
            ]:
                continue

            points = score.points
            medal = score.data.get_int('medal')

            music = Node.void('music')
            root.add_child(music)
            music.add_child(Node.s16('music_num', score.id))
            music.add_child(
                Node.u8(
                    'sheet_num', {
                        self.CHART_TYPE_EASY:
                        PopnMusicEclale.GAME_CHART_TYPE_EASY,
                        self.CHART_TYPE_NORMAL:
                        PopnMusicEclale.GAME_CHART_TYPE_NORMAL,
                        self.CHART_TYPE_HYPER:
                        PopnMusicEclale.GAME_CHART_TYPE_HYPER,
                        self.CHART_TYPE_EX: PopnMusicEclale.GAME_CHART_TYPE_EX,
                    }[score.chart]))
            music.add_child(Node.s32('score', points))
            music.add_child(
                Node.u8(
                    'clear_type', {
                        self.PLAY_MEDAL_CIRCLE_FAILED:
                        PopnMusicEclale.GAME_PLAY_MEDAL_CIRCLE_FAILED,
                        self.PLAY_MEDAL_DIAMOND_FAILED:
                        PopnMusicEclale.GAME_PLAY_MEDAL_DIAMOND_FAILED,
                        self.PLAY_MEDAL_STAR_FAILED:
                        PopnMusicEclale.GAME_PLAY_MEDAL_STAR_FAILED,
                        self.PLAY_MEDAL_EASY_CLEAR:
                        PopnMusicEclale.GAME_PLAY_MEDAL_EASY_CLEAR,
                        self.PLAY_MEDAL_CIRCLE_CLEARED:
                        PopnMusicEclale.GAME_PLAY_MEDAL_CIRCLE_CLEARED,
                        self.PLAY_MEDAL_DIAMOND_CLEARED:
                        PopnMusicEclale.GAME_PLAY_MEDAL_DIAMOND_CLEARED,
                        self.PLAY_MEDAL_STAR_CLEARED:
                        PopnMusicEclale.GAME_PLAY_MEDAL_STAR_CLEARED,
                        self.PLAY_MEDAL_CIRCLE_FULL_COMBO:
                        PopnMusicEclale.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO,
                        self.PLAY_MEDAL_DIAMOND_FULL_COMBO:
                        PopnMusicEclale.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO,
                        self.PLAY_MEDAL_STAR_FULL_COMBO:
                        PopnMusicEclale.GAME_PLAY_MEDAL_STAR_FULL_COMBO,
                        self.PLAY_MEDAL_PERFECT:
                        PopnMusicEclale.GAME_PLAY_MEDAL_PERFECT,
                    }[medal]))
            music.add_child(Node.s16('cnt', score.plays))

        return root
Exemple #28
0
    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
Exemple #29
0
    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
Exemple #30
0
    def verify_player24_read(
            self, ref_id: str,
            msg_type: str) -> Dict[str, Dict[int, Dict[str, int]]]:
        call = self.call_node()

        # Construct node
        player24 = Node.void('player24')
        call.add_child(player24)
        player24.set_attribute('method', 'read')

        player24.add_child(Node.string('ref_id', ref_id))
        player24.add_child(Node.s8('pref', 51))

        # Swap with server
        resp = self.exchange('', call)

        if msg_type == 'new':
            # Verify that response is correct
            self.assert_path(resp, "response/player24/result")
            status = resp.child_value('player24/result')
            if status != 2:
                raise Exception(
                    f'Reference ID \'{ref_id}\' returned invalid status \'{status}\''
                )

            return {
                'items': {},
                'characters': {},
                'points': {},
            }
        elif msg_type == 'query':
            # Verify that the response is correct
            self.__verify_profile(resp)

            self.assert_path(resp, "response/player24/result")
            status = resp.child_value('player24/result')
            if status != 0:
                raise Exception(
                    f'Reference ID \'{ref_id}\' returned invalid status \'{status}\''
                )
            name = resp.child_value('player24/account/name')
            if name != self.NAME:
                raise Exception(
                    f'Invalid name \'{name}\' returned for Ref ID \'{ref_id}\''
                )

            # Medals and items
            items: Dict[int, Dict[str, int]] = {}
            charas: Dict[int, Dict[str, int]] = {}
            courses: Dict[int, Dict[str, int]] = {}
            for obj in resp.child('player24').children:
                if obj.name == 'item':
                    items[obj.child_value('id')] = {
                        'type': obj.child_value('type'),
                        'param': obj.child_value('param'),
                    }
                elif obj.name == 'chara_param':
                    charas[obj.child_value('chara_id')] = {
                        'friendship': obj.child_value('friendship'),
                    }
                elif obj.name == 'course_data':
                    courses[obj.child_value('course_id')] = {
                        'clear_type': obj.child_value('clear_type'),
                        'clear_rank': obj.child_value('clear_rank'),
                        'total_score': obj.child_value('total_score'),
                        'count': obj.child_value('update_count'),
                        'sheet_num': obj.child_value('sheet_num'),
                    }

            return {
                'items': items,
                'characters': charas,
                'courses': courses,
                'points': {
                    0: {
                        'points':
                        resp.child_value('player24/account/player_point')
                    }
                },
            }
        else:
            raise Exception(f'Unrecognized message type \'{msg_type}\'')