示例#1
0
    def verify_game_shop(self, location: str) -> None:
        call = self.call_node()

        game = Node.void('game_3')
        call.add_child(game)
        game.set_attribute('method', 'shop')
        game.set_attribute('ver', '0')
        game.add_child(Node.string('locid', location))
        game.add_child(Node.string('regcode', '.'))
        game.add_child(Node.string('locname', ''))
        game.add_child(Node.u8('loctype', 0))
        game.add_child(Node.string('cstcode', ''))
        game.add_child(Node.string('cpycode', ''))
        game.add_child(Node.s32('latde', 0))
        game.add_child(Node.s32('londe', 0))
        game.add_child(Node.u8('accu', 0))
        game.add_child(Node.string('linid', '.'))
        game.add_child(Node.u8('linclass', 0))
        game.add_child(Node.ipv4('ipaddr', '0.0.0.0'))
        game.add_child(Node.string('hadid', '00010203040506070809'))
        game.add_child(Node.string('licid', '00010203040506070809'))
        game.add_child(Node.string('actid', self.pcbid))
        game.add_child(Node.s8('appstate', 0))
        game.add_child(Node.s8('c_need', 1))
        game.add_child(Node.s8('c_credit', 2))
        game.add_child(Node.s8('s_credit', 2))
        game.add_child(Node.bool('free_p', True))
        game.add_child(Node.bool('close', False))
        game.add_child(Node.s32('close_t', 1380))
        game.add_child(Node.u32('playc', 0))
        game.add_child(Node.u32('playn', 0))
        game.add_child(Node.u32('playe', 0))
        game.add_child(Node.u32('test_m', 0))
        game.add_child(Node.u32('service', 0))
        game.add_child(Node.bool('paseli', True))
        game.add_child(Node.u32('update', 0))
        game.add_child(Node.string('shopname', ''))
        game.add_child(Node.bool('newpc', False))
        game.add_child(Node.s32('s_paseli', 206))
        game.add_child(Node.s32('monitor', 1))
        game.add_child(Node.string('romnumber', '-'))
        game.add_child(
            Node.string('etc', 'TaxMode:1,BasicRate:100/1,FirstFree:0'))

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

        # Verify that response is correct
        self.assert_path(resp, "response/game_3/nxt_time")
示例#2
0
    def handle_info_rb5_info_read_shop_ranking_request(self, request: Node) -> Node:
        start_music_id = request.child_value('min')
        end_music_id = request.child_value('max')

        root = Node.void('info')
        shop_score = Node.void('shop_score')
        root.add_child(shop_score)
        shop_score.add_child(Node.s32('time', Time.now()))

        profiles: Dict[UserID, ValidatedDict] = {}
        for songid in range(start_music_id, end_music_id + 1):
            allscores = self.data.local.music.get_all_scores(
                self.game,
                self.version,
                songid=songid,
            )

            for ng in [
                self.CHART_TYPE_BASIC,
                self.CHART_TYPE_MEDIUM,
                self.CHART_TYPE_HARD,
                self.CHART_TYPE_SPECIAL,
            ]:
                scores = sorted(
                    [score for score in allscores if score[1].chart == ng],
                    key=lambda score: score[1].points,
                    reverse=True,
                )

                for i in range(len(scores)):
                    userid, score = scores[i]
                    if userid not in profiles:
                        profiles[userid] = self.get_any_profile(userid)
                    profile = profiles[userid]

                    data = Node.void('data')
                    shop_score.add_child(data)
                    data.add_child(Node.s32('rank', i + 1))
                    data.add_child(Node.s16('music_id', songid))
                    data.add_child(Node.s8('note_grade', score.chart))
                    data.add_child(Node.s8('clear_type', self._db_to_game_clear_type(score.data.get_int('clear_type'))))
                    data.add_child(Node.s32('user_id', profile.get_int('extid')))
                    data.add_child(Node.s16('icon_id', profile.get_dict('config').get_int('icon_id')))
                    data.add_child(Node.s32('score', score.points))
                    data.add_child(Node.s32('time', score.timestamp))
                    data.add_child(Node.string('name', profile.get_str('name')))

        return root
示例#3
0
    def verify_playerdata_new(self, ref_id: str) -> None:
        call = self.call_node()

        # Construct node
        playerdata = Node.void('playerdata')
        call.add_child(playerdata)
        playerdata.set_attribute('method', 'new')

        playerdata.add_child(Node.string('ref_id', ref_id))
        playerdata.add_child(Node.string('name', self.NAME))
        playerdata.add_child(Node.string('shop_name', ''))
        playerdata.add_child(Node.s8('pref', 51))

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

        # Verify nodes that cause crashes if they don't exist
        self.assert_path(resp, "response/playerdata/base/g_pm_id")
        self.assert_path(resp, "response/playerdata/base/name")
        self.assert_path(resp, "response/playerdata/base/my_best")
        self.assert_path(resp, "response/playerdata/base/latest_music")
        self.assert_path(resp, "response/playerdata/base/clear_medal")
        self.assert_path(resp, "response/playerdata/base/clear_medal_sub")
        self.assert_path(resp, "response/playerdata/player_card")
        self.assert_path(resp, "response/playerdata/player_card_ex")
        self.assert_path(resp, "response/playerdata/hiscore")
        self.assert_path(resp, "response/playerdata/netvs")
        self.assert_path(resp, "response/playerdata/sp_data")
        self.assert_path(resp, "response/playerdata/reflec_data")
        self.assert_path(resp, "response/playerdata/navigate")
示例#4
0
 def handle_player23_read_request(self, request: Node) -> Node:
     refid = request.child_value('ref_id')
     root = self.get_profile_by_refid(refid, self.OLD_PROFILE_FALLTHROUGH)
     if root is None:
         root = Node.void('player23')
         root.add_child(Node.s8('result', 2))
     return root
示例#5
0
    def verify_game_play_e(self, location: str, refid: str) -> None:
        call = self.call_node()

        game = Node.void('game_3')
        call.add_child(game)
        game.set_attribute('ver', '0')
        game.set_attribute('method', 'play_e')
        game.add_child(Node.string('dataid', refid))
        game.add_child(Node.s8('mode', 2))
        game.add_child(Node.s16('track_num', 3))
        game.add_child(Node.s32('s_coin', 0))
        game.add_child(Node.s32('s_paseli', 0))
        game.add_child(Node.s16('blaster_count', 0))
        game.add_child(Node.s16('blaster_cartridge', 0))
        game.add_child(Node.string('locid', location))
        game.add_child(Node.u16('drop_frame', 396))
        game.add_child(Node.u16('drop_frame_max', 396))
        game.add_child(Node.u16('drop_count', 1))
        game.add_child(Node.string('etc', 'StoryID:0,StoryPrg:0,PrgPrm:0'))

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

        # Verify that response is correct
        self.assert_path(resp, "response/game_3")
示例#6
0
    def verify_game_get(self) -> None:
        call = self.call_node()

        # Construct node
        game = Node.void('game')
        call.add_child(game)
        game.set_attribute('location_id', 'JP-1')
        game.set_attribute('method', 'get')
        game.add_child(Node.s8('event', 0))

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

        # Verify that response is correct
        self.assert_path(resp, "response/game")

        for name in [
                'ir_phase',
                'music_open_phase',
                'collabo_phase',
                'personal_event_phase',
                'shop_event_phase',
                'netvs_phase',
                'card_phase',
                'other_phase',
                'local_matching_enable',
                'n_matching_sec',
                'l_matching_sec',
                'is_check_cpu',
                'week_no',
        ]:
            node = resp.child('game').child(name)

            if node is None:
                raise Exception(
                    'Missing node \'{}\' in response!'.format(name))
            if node.data_type != 's32':
                raise Exception(
                    'Node \'{}\' has wrong data type!'.format(name))

        sel_ranking = resp.child('game').child('sel_ranking')
        up_ranking = resp.child('game').child('up_ranking')

        for nodepair in [('sel_ranking', sel_ranking),
                         ('up_ranking', up_ranking)]:
            name = nodepair[0]
            node = nodepair[1]

            if node is None:
                raise Exception(
                    'Missing node \'{}\' in response!'.format(name))
            if node.data_type != 's16':
                raise Exception(
                    'Node \'{}\' has wrong data type!'.format(name))
            if not node.is_array:
                raise Exception('Node \'{}\' is not array!'.format(name))
            if len(node.value) != 5:
                raise Exception(
                    'Node \'{}\' is wrong array length!'.format(name))
示例#7
0
    def verify_game_active(self) -> None:
        call = self.call_node()

        # Construct node
        game = Node.void('game')
        call.add_child(game)
        game.set_attribute('method', 'active')

        # Add what Pop'n 20 would add after full unlock
        game.set_attribute('method', 'active')
        game.add_child(Node.s8('event', 0))
        game.add_child(Node.s8('card_use', 0))
        game.add_child(Node.string('name', ''))
        game.add_child(Node.string('location_id', 'JP-1'))
        game.add_child(Node.string('shop_name_facility', '.'))
        game.add_child(Node.s8('pref', 50))
        game.add_child(Node.string('shop_addr', '127.0.0.1  10000'))
        game.add_child(Node.string('shop_name', ''))
        game.add_child(Node.string('eacoin_price', '200,260,200,200'))
        game.add_child(Node.s8('eacoin_available', 1))
        game.add_child(Node.s8('dipswitch', 0))
        game.add_child(Node.u8('max_stage', 1))
        game.add_child(Node.u8('difficult', 4))
        game.add_child(Node.s8('free_play', 1))
        game.add_child(Node.s8('event_mode', 0))
        game.add_child(Node.s8('popn_card', 0))
        game.add_child(Node.s16('close_time', -1))
        game.add_child(Node.s32('game_phase', 2))
        game.add_child(Node.s32('net_phase', 0))
        game.add_child(Node.s32('event_phase', 0))
        game.add_child(Node.s32('card_phase', 3))
        game.add_child(Node.s32('ir_phase', 0))
        game.add_child(Node.u8('coin_to_credit', 1))
        game.add_child(Node.u8('credit_to_start', 2))
        game.add_child(Node.s32('sound_attract', 100))
        game.add_child(Node.s8('bookkeeping', 0))
        game.add_child(Node.s8('set_clock', 0))
        game.add_child(Node.s32('local_clock_sec', 80011))
        game.add_child(Node.s32('revision_sec', 0))
        game.add_child(Node.string('crash_log', ''))

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

        # Verify that response is correct
        self.assert_path(resp, "response/game/@status")
示例#8
0
 def handle_player23_new_request(self, request: Node) -> Node:
     refid = request.child_value('ref_id')
     name = request.child_value('name')
     root = self.new_profile_by_refid(refid, name)
     if root is None:
         root = Node.void('player23')
         root.add_child(Node.s8('result', 2))
     return root
示例#9
0
    def format_conversion(self, userid: UserID, profile: ValidatedDict) -> Node:
        # Circular import, ugh
        from bemani.backend.popn.lapistoria import PopnMusicLapistoria

        root = Node.void('playerdata')

        root.add_child(Node.string('name', profile.get_str('name', 'なし')))
        root.add_child(Node.s16('chara', profile.get_int('chara', -1)))
        root.add_child(Node.s32('option', profile.get_int('option', 0)))
        root.add_child(Node.s8('result', 1))

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

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

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

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

        return root
示例#10
0
    def test_packet1(self) -> Node:
        root = Node.void('test')
        root.set_attribute('test', 'test string value')

        # Regular nodes
        root.add_child(Node.void('void_node'))
        root.add_child(Node.s8('s8_node', -1))
        root.add_child(Node.u8('u8_node', 245))
        root.add_child(Node.s16('s16_node', -8000))
        root.add_child(Node.u16('u16_node', 65000))
        root.add_child(Node.s32('s32_node', -2000000000))
        root.add_child(Node.u32('u32_node', 4000000000))
        root.add_child(Node.s64('s64_node', -1234567890000))
        root.add_child(Node.u64('u64_node', 1234567890000))
        root.add_child(Node.binary('bin_node', b'DEADBEEF'))
        root.add_child(Node.string('str_node', 'this is a string!'))
        root.add_child(Node.ipv4('ip4_node', '192.168.1.24'))
        root.add_child(Node.time('time_node', 1234567890))
        root.add_child(Node.float('float_node', 2.5))
        root.add_child(Node.fouru8('4u8_node', [0x20, 0x21, 0x22, 0x23]))
        root.add_child(Node.bool('bool_true_node', True))
        root.add_child(Node.bool('bool_false_node', False))

        # Array nodes
        root.add_child(Node.s8_array('s8_array_node', [-1, -2, 3, 4, -5]))
        root.add_child(Node.u8_array('u8_array_node', [245, 2, 0, 255, 1]))
        root.add_child(Node.s16_array('s16_array_node', [-8000, 8000]))
        root.add_child(Node.u16_array('u16_array_node', [65000, 1, 2, 65535]))
        root.add_child(Node.s32_array('s32_array_node', [-2000000000, -1]))
        root.add_child(Node.u32_array('u32_array_node', [4000000000, 0, 1, 2]))
        root.add_child(
            Node.s64_array('s64_array_node', [-1234567890000, -1, 1, 1337]))
        root.add_child(
            Node.u64_array('u64_array_node', [1234567890000, 123, 456, 7890]))
        root.add_child(
            Node.time_array('time_array_node', [1234567890, 98765432]))
        root.add_child(
            Node.float_array('float_array_node', [2.5, 0.0, 5.0, 20.5]))
        root.add_child(
            Node.bool_array('bool_array_node', [False, True, True, False]))

        # XML escaping
        escape = Node.string(
            'escape_test',
            '\r\n<testing> & \'thing\' "thing" \r\nthing on new line\r\n    ')
        escape.set_attribute('test',
                             '<testing> & \'thing\' "thing" \r\n thing')
        root.add_child(escape)

        # Unicode
        unicode_node = Node.string('unicode', '今日は')
        unicode_node.set_attribute('unicode_attr', 'わたし')
        root.add_child(unicode_node)

        self.assertLoopback(root)
示例#11
0
    def verify_player24_start(self, ref_id: str, loc: str) -> None:
        call = self.call_node()

        # Construct node
        player24 = Node.void('player24')
        call.add_child(player24)
        player24.set_attribute('loc_id', loc)
        player24.set_attribute('ref_id', ref_id)
        player24.set_attribute('method', 'start')
        player24.set_attribute('start_type', '0')
        pcb_card = Node.void('pcb_card')
        player24.add_child(pcb_card)
        pcb_card.add_child(Node.s8('card_enable', 1))
        pcb_card.add_child(Node.s8('card_soldout', 0))

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

        # Verify that response is correct
        self.__verify_common('player24', resp)
示例#12
0
    def verify_game_save(self, location: str, refid: str, packet: int,
                         block: int, blaster_energy: int) -> None:
        call = self.call_node()

        game = Node.void('game_3')
        call.add_child(game)
        game.set_attribute('method', 'save')
        game.set_attribute('ver', '0')
        game.add_child(Node.string('refid', refid))
        game.add_child(Node.string('locid', location))
        game.add_child(Node.u8('headphone', 0))
        game.add_child(Node.u16('appeal_id', 1001))
        game.add_child(Node.u16('comment_id', 0))
        game.add_child(Node.s32('music_id', 29))
        game.add_child(Node.u8('music_type', 1))
        game.add_child(Node.u8('sort_type', 1))
        game.add_child(Node.u8('narrow_down', 0))
        game.add_child(Node.u8('gauge_option', 0))
        game.add_child(Node.u32('earned_gamecoin_packet', packet))
        game.add_child(Node.u32('earned_gamecoin_block', block))
        item = Node.void('item')
        game.add_child(item)
        info = Node.void('info')
        item.add_child(info)
        info.add_child(Node.u32('id', 1))
        info.add_child(Node.u32('type', 5))
        info.add_child(Node.u32('param', 333333376))
        info = Node.void('info')
        item.add_child(info)
        info.add_child(Node.u32('id', 0))
        info.add_child(Node.u32('type', 5))
        info.add_child(Node.u32('param', 600))
        game.add_child(
            Node.s32_array(
                'hidden_param',
                [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]))
        game.add_child(Node.s16('skill_name_id', -1))
        game.add_child(Node.s32('earned_blaster_energy', blaster_energy))
        game.add_child(Node.u32('blaster_count', 0))
        printn = Node.void('print')
        game.add_child(printn)
        printn.add_child(Node.s32('count', 0))
        ea_shop = Node.void('ea_shop')
        game.add_child(ea_shop)
        ea_shop.add_child(Node.s32('used_packet_booster', 0))
        ea_shop.add_child(Node.s32('used_block_booster', 0))
        game.add_child(Node.s8('start_option', 0))

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

        # Verify that response is correct
        self.assert_path(resp, "response/game_3")
示例#13
0
    def verify_rb4pzlcmt_write(self, loc: str, extid: int) -> None:
        call = self.call_node()

        info = Node.void('info')
        info.set_attribute('method', 'rb4pzlcmt_write')
        info.add_child(Node.s32('uid', extid))
        info.add_child(Node.string('name', self.NAME))
        info.add_child(Node.s16('icon', 0))
        info.add_child(Node.s8('bln', 0))
        info.add_child(Node.string('lid', loc))
        info.add_child(Node.s8('pref', 51))
        info.add_child(Node.s32('time', int(time.time())))
        info.add_child(Node.string('comment', 'アメ〜〜!'))
        info.add_child(Node.bool('is_tweet', False))
        call.add_child(info)

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

        # Verify that response is correct
        self.assert_path(resp, "response/info/@status")
示例#14
0
    def handle_player_rb5_player_read_score_request(self, request: Node) -> Node:
        refid = request.child_value('rid')
        userid = self.data.remote.user.from_refid(self.game, self.version, refid)
        if userid is None:
            scores: List[Score] = []
        else:
            scores = self.data.remote.music.get_scores(self.game, self.version, userid)

        root = Node.void('player')
        pdata = Node.void('pdata')
        root.add_child(pdata)

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

        for score in scores:
            rec = Node.void('rec')
            record.add_child(rec)
            rec.add_child(Node.s16('mid', score.id))
            rec.add_child(Node.s8('ntgrd', score.chart))
            rec.add_child(Node.s32('pc', score.plays))
            rec.add_child(Node.s8('ct', self._db_to_game_clear_type(score.data.get_int('clear_type'))))
            rec.add_child(Node.s16('ar', score.data.get_int('achievement_rate')))
            rec.add_child(Node.s16('scr', score.points))
            rec.add_child(Node.s16('ms', score.data.get_int('miss_count')))
            rec.add_child(Node.s16(
                'param',
                self._db_to_game_combo_type(score.data.get_int('combo_type')) + score.data.get_int('param'),
            ))
            rec.add_child(Node.s32('bscrt', score.timestamp))
            rec.add_child(Node.s32('bart', score.data.get_int('best_achievement_rate_time')))
            rec.add_child(Node.s32('bctt', score.data.get_int('best_clear_type_time')))
            rec.add_child(Node.s32('bmst', score.data.get_int('best_miss_count_time')))
            rec.add_child(Node.s32('time', score.data.get_int('last_played_time')))
            rec.add_child(Node.s32('k_flag', score.data.get_int('kflag')))

        return root
示例#15
0
    def verify_player24_read_score(
            self, ref_id: str) -> Dict[str, Dict[int, Dict[int, int]]]:
        call = self.call_node()

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

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

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

        # Verify defaults
        self.assert_path(resp, "response/player24/@status")

        # Grab scores
        scores: Dict[int, Dict[int, int]] = {}
        medals: Dict[int, Dict[int, int]] = {}
        ranks: Dict[int, Dict[int, int]] = {}
        for child in resp.child('player24').children:
            if child.name != 'music':
                continue

            musicid = child.child_value('music_num')
            chart = child.child_value('sheet_num')
            score = child.child_value('score')
            medal = child.child_value('clear_type')
            rank = child.child_value('clear_rank')

            if musicid not in scores:
                scores[musicid] = {}
            if musicid not in medals:
                medals[musicid] = {}
            if musicid not in ranks:
                ranks[musicid] = {}

            scores[musicid][chart] = score
            medals[musicid][chart] = medal
            ranks[musicid][chart] = rank

        return {
            'scores': scores,
            'medals': medals,
            'ranks': ranks,
        }
示例#16
0
    def verify_gametop_get_mdata(self,
                                 jid: int) -> Dict[str, List[Dict[str, Any]]]:
        call = self.call_node()

        # Construct node
        gametop = Node.void('gametop')
        call.add_child(gametop)
        gametop.set_attribute('method', 'get_mdata')
        retry = Node.s32('retry', 0)
        gametop.add_child(retry)
        data = Node.void('data')
        gametop.add_child(data)
        player = Node.void('player')
        data.add_child(player)
        player.add_child(Node.s32('jid', jid))
        # Technically the game sends this same packet 3 times, one with
        # each value 1, 2, 3 here. Unclear why, but we won't emulate it.
        player.add_child(Node.s8('mdata_ver', 1))
        player.add_child(Node.bool('rival', False))

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

        # Parse out scores
        self.assert_path(resp, "response/gametop/data/player/mdata_list")

        ret = {}
        for musicdata in resp.child('gametop/data/player/mdata_list').children:
            if musicdata.name != 'musicdata':
                raise Exception('Unexpected node in playdata!')

            music_id = musicdata.attribute('music_id')
            scores_by_chart: List[Dict[str, int]] = [{}, {}, {}]

            def extract_cnts(name: str, val: List[int]) -> None:
                scores_by_chart[0][name] = val[0]
                scores_by_chart[1][name] = val[1]
                scores_by_chart[2][name] = val[2]

            extract_cnts('plays', musicdata.child_value('play_cnt'))
            extract_cnts('clears', musicdata.child_value('clear_cnt'))
            extract_cnts('full_combos', musicdata.child_value('fc_cnt'))
            extract_cnts('excellents', musicdata.child_value('ex_cnt'))
            extract_cnts('score', musicdata.child_value('score'))
            extract_cnts('medal', musicdata.child_value('clear'))
            ret[music_id] = scores_by_chart

        return ret
示例#17
0
    def verify_game_active(self) -> None:
        call = self.call_node()

        # Construct node
        game = Node.void('game')
        call.add_child(game)
        game.set_attribute('method', 'active')

        # Add minimum amount of stuff so server accepts
        game.add_child(Node.s8('event', 0))

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

        # Verify that response is correct
        self.assert_path(resp, "response/game/@status")
示例#18
0
    def verify_game_save(self, location: str, refid: str, packet: int,
                         block: int, blaster_energy: int) -> None:
        call = self.call_node()

        game = Node.void('game')
        call.add_child(game)
        game.set_attribute('method', 'sv4_save')
        game.set_attribute('ver', '0')
        game.add_child(Node.string('refid', refid))
        game.add_child(Node.string('locid', location))
        game.add_child(Node.u8('headphone', 0))
        game.add_child(Node.u16('appeal_id', 1001))
        game.add_child(Node.u16('comment_id', 0))
        game.add_child(Node.s32('music_id', 29))
        game.add_child(Node.u8('music_type', 1))
        game.add_child(Node.u8('sort_type', 1))
        game.add_child(Node.u8('narrow_down', 0))
        game.add_child(Node.u8('gauge_option', 0))
        game.add_child(Node.u8('ars_option', 0))
        game.add_child(Node.u8('notes_option', 0))
        game.add_child(Node.u8('early_late_disp', 0))
        game.add_child(Node.s32('draw_adjust', 0))
        game.add_child(Node.u8('eff_c_left', 0))
        game.add_child(Node.u8('eff_c_right', 1))
        game.add_child(Node.u32('earned_gamecoin_packet', packet))
        game.add_child(Node.u32('earned_gamecoin_block', block))
        item = Node.void('item')
        game.add_child(item)
        game.add_child(Node.s16('skill_name_id', 0))
        game.add_child(Node.s16('skill_base_id', 0))
        game.add_child(Node.s16('skill_name', 0))
        game.add_child(Node.s32('earned_blaster_energy', blaster_energy))
        game.add_child(Node.u32('blaster_count', 0))
        printn = Node.void('print')
        game.add_child(printn)
        printn.add_child(Node.s32('count', 0))
        ea_shop = Node.void('ea_shop')
        game.add_child(ea_shop)
        ea_shop.add_child(Node.s32('used_packet_booster', 0))
        ea_shop.add_child(Node.s32('used_block_booster', 0))
        game.add_child(Node.s8('start_option', 1))

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

        # Verify that response is correct
        self.assert_path(resp, "response/game")
示例#19
0
    def verify_player24_new(self, ref_id: str) -> None:
        call = self.call_node()

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

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

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

        # Verify nodes
        self.__verify_profile(resp)
示例#20
0
    def format_conversion(self, userid: UserID, profile: ValidatedDict) -> Node:
        root = Node.void('player23')
        root.add_child(Node.string('name', profile.get_str('name', 'なし')))
        root.add_child(Node.s16('chara', profile.get_int('chara', -1)))
        root.add_child(Node.s8('result', 1))

        scores = self.data.remote.music.get_scores(self.game, self.version, userid)
        for score in scores:
            # Skip any scores for chart types we don't support
            if score.chart not in [
                self.CHART_TYPE_EASY,
                self.CHART_TYPE_NORMAL,
                self.CHART_TYPE_HYPER,
                self.CHART_TYPE_EX,
            ]:
                continue

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

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

        return root
示例#21
0
    def verify_player22_new(self, ref_id: str) -> None:
        call = self.call_node()

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

        player22.add_child(Node.string('ref_id', ref_id))
        player22.add_child(Node.string('name', self.NAME))
        player22.add_child(Node.string('shop_name', ''))
        player22.add_child(Node.s8('pref', 51))

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

        # Verify nodes that cause crashes if they don't exist
        self.assert_path(resp, "response/player22/account")
示例#22
0
    def verify_player_rb5_player_read_rank_5(self) -> None:
        call = self.call_node()

        player = Node.void('player')
        player.set_attribute('method', 'rb5_player_read_rank_5')
        player.add_child(Node.s32('uid', 0))
        player.add_child(Node.s32_array('sc', [897, 897, 0, 0, 0]))
        player.add_child(Node.s8('mg_id', 0))
        player.add_child(Node.s32('mg_sc', 220))
        call.add_child(player)

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

        # Verify that response is correct
        self.assert_path(resp, "response/player/tbs/new_rank")
        self.assert_path(resp, "response/player/tbs/old_rank")
        self.assert_path(resp, "response/player/mng/new_rank")
        self.assert_path(resp, "response/player/mng/old_rank")
示例#23
0
    def verify_player_rb5_player_read_rival_score_5(self, extid: int) -> None:
        call = self.call_node()

        player = Node.void('player')
        player.set_attribute('method', 'rb5_player_read_rival_score_5')
        player.add_child(Node.s32('uid', extid))
        player.add_child(Node.s32('music_id', 6))
        player.add_child(Node.s8('note_grade', 0))
        call.add_child(player)

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

        # Verify that response is correct
        self.assert_path(resp, "response/player/@status")

        # Verify that we got a score if the extid is nonzero
        if extid != 0:
            self.assert_path(resp,
                             "response/player/player_select_score/user_id")
            self.assert_path(resp, "response/player/player_select_score/name")
            self.assert_path(resp,
                             "response/player/player_select_score/m_score")
            self.assert_path(
                resp, "response/player/player_select_score/m_scoreTime")
            self.assert_path(resp,
                             "response/player/player_select_score/m_iconID")

            if resp.child_value(
                    'player/player_select_score/name') != self.NAME:
                raise Exception(
                    f'Invalid name {resp.child_value("player/player_select_score/name")} returned on score read!'
                )
            if resp.child_value('player/player_select_score/user_id') != extid:
                raise Exception(
                    f'Invalid name {resp.child_value("player/player_select_score/user_id")} returned on score read!'
                )
示例#24
0
    def verify_player_rb5_player_read_rival_ranking_data_5(self,
                                                           extid: int) -> None:
        call = self.call_node()

        player = Node.void('player')
        player.set_attribute('method', 'rb5_player_read_rival_ranking_data_5')
        player.add_child(Node.s32('uid', extid))
        player.add_child(Node.s8('mgid', -1))
        call.add_child(player)

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

        # Verify that response is correct
        self.assert_path(resp, "response/player/rival_data/rl/uid")
        self.assert_path(resp, "response/player/rival_data/rl/nm")
        self.assert_path(resp, "response/player/rival_data/rl/ic")
        self.assert_path(resp, "response/player/rival_data/rl/sl/mid")
        self.assert_path(resp, "response/player/rival_data/rl/sl/m")
        self.assert_path(resp, "response/player/rival_data/rl/sl/t")
        if resp.child_value('player/rival_data/rl/nm') != self.NAME:
            raise Exception(
                f'Invalid name {resp.child_value("player/rival_data/rl/nm")} returned on rival ranking read!'
            )
示例#25
0
    def verify_event_w_add_comment(self, loc: str, extid: int) -> None:
        call = self.call_node()

        event_w = Node.void('event_w')
        call.add_child(event_w)
        event_w.set_attribute('method', 'add_comment')
        event_w.add_child(Node.s32('uid', extid))
        event_w.add_child(Node.string('p_name', self.NAME))
        event_w.add_child(Node.s32('exp', 0))
        event_w.add_child(Node.s32('customize', 0))
        event_w.add_child(Node.s32('tid', 0))
        event_w.add_child(Node.string('t_name', ''))
        event_w.add_child(Node.string('lid', loc))
        event_w.add_child(Node.string('s_name', ''))
        event_w.add_child(Node.s8('pref', 51))
        event_w.add_child(Node.s32('time', Time.now()))
        event_w.add_child(Node.string('comment', 'アメ〜〜!'))
        event_w.add_child(Node.bool('is_tweet', False))

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

        # Verify that response is correct
        self.assert_path(resp, "response/event_w/@status")
示例#26
0
    def verify_playerdata_new(self, ref_id: str) -> None:
        call = self.call_node()

        # Construct node
        playerdata = Node.void('playerdata')
        call.add_child(playerdata)
        playerdata.set_attribute('method', 'new')

        playerdata.add_child(Node.string('ref_id', ref_id))
        playerdata.add_child(Node.string('name', self.NAME))
        playerdata.add_child(Node.string('shop_name', ''))
        playerdata.add_child(Node.s8('pref', 51))
        playerdata.add_child(Node.s8('gakuen', 2))
        playerdata.add_child(Node.s8('zoo', 1))
        playerdata.add_child(Node.s8('floor_infection', 1))
        playerdata.add_child(Node.s8('triple_journey', 1))
        playerdata.add_child(Node.s8('baseball', 1))

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

        # Verify nodes that cause crashes if they don't exist
        self.assert_path(resp, "response/playerdata/base")
示例#27
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(
            f'Couldn\'t find raw offset for virtual offset 0x{offset:08x}')

    if base >= pe.OPTIONAL_HEADER.ImageBase:
        # Assume this is virtual
        base = virtual_to_physical(base)

    def read_string(offset: int) -> str:
        # First, translate load offset in memory to disk offset
        offset = virtual_to_physical(offset)

        # Now, grab bytes until we're null-terminated
        bytestring = []
        while data[offset] != 0:
            bytestring.append(data[offset])
            offset = offset + 1

        # Its shift-jis encoded, so decode it now
        return bytes(bytestring).decode('shift_jisx0213')

    # For recursing into nodes
    saved_root: List[Node] = []
    saved_loc: List[int] = []

    while True:
        chunk = data[base:(base + 16)]
        base = base + 16

        (nodetype, mandatory, outoffset, width, nameptr,
         defaultptr) = struct.unpack('<BBHIII', chunk)

        if nodetype == 0xFF:
            # End of nodes, see if we should exit
            if len(saved_root) == 0:
                break
            else:
                root = saved_root.pop()
                oldbase = saved_loc.pop()
                if oldbase is not None:
                    base = oldbase
                continue

        # Grab name, get rid of parse numbers
        name = read_string(nameptr)
        try:
            if name.index('#') >= 0:
                name = name[:name.index('#')]
        except ValueError:
            pass

        if nodetype == 0x00:
            raise Exception(f'Invalid node type 0x{nodetype:02x}')
        elif nodetype == 0x01:
            # This is a void node, so we should handle by recursing
            node = Node.void(name)
            root.add_child(node)

            # Recurse here
            saved_root.append(root)

            if defaultptr != 0:
                saved_loc.append(base)
                base = virtual_to_physical(defaultptr)
            else:
                saved_loc.append(None)

            root = node
            continue
        elif nodetype == 0x02 or nodetype == 0x43:
            if nodetype < 0x40:
                elements = int(width / 1)
            else:
                elements = width
            if elements > 1:
                node = Node.s8_array(name, [-1] * elements)
            else:
                node = Node.s8(name, -1)
        elif nodetype == 0x03 or nodetype == 0x44:
            if nodetype < 0x40:
                elements = int(width / 1)
            else:
                elements = width
            if elements > 1:
                node = Node.u8_array(name, [0] * elements)
            else:
                node = Node.u8(name, 0)
        elif nodetype == 0x04 or nodetype == 0x45:
            if nodetype < 0x40:
                elements = int(width / 2)
            else:
                elements = width
            if elements > 1:
                node = Node.s16_array(name, [-1] * elements)
            else:
                node = Node.s16(name, -1)
        elif nodetype == 0x05 or nodetype == 0x46:
            if nodetype < 0x40:
                elements = int(width / 2)
            else:
                elements = width
            if elements > 1:
                node = Node.u16_array(name, [0] * elements)
            else:
                node = Node.u16(name, 0)
        elif nodetype == 0x06 or nodetype == 0x47:
            if nodetype < 0x40:
                elements = int(width / 4)
            else:
                elements = width
            if elements > 1:
                node = Node.s32_array(name, [-1] * elements)
            else:
                node = Node.s32(name, -1)
        elif nodetype == 0x07 or nodetype == 0x48:
            if nodetype < 0x40:
                elements = int(width / 4)
            else:
                elements = width
            if elements > 1:
                node = Node.u32_array(name, [0] * elements)
            else:
                node = Node.u32(name, 0)
        elif nodetype == 0x08 or nodetype == 0x49:
            if nodetype < 0x40:
                elements = int(width / 8)
            else:
                elements = width
            if elements > 1:
                node = Node.s64_array(name, [-1] * elements)
            else:
                node = Node.s64(name, -1)
        elif nodetype == 0x09 or nodetype == 0x4A:
            if nodetype < 0x40:
                elements = int(width / 8)
            else:
                elements = width
            if elements > 1:
                node = Node.u64_array(name, [0] * elements)
            else:
                node = Node.u64(name, 0)
        elif nodetype == 0x0A:
            node = Node.string(name, '')
        elif nodetype == 0x0D:
            node = Node.float(name, 0.0)
        elif nodetype == 0x32 or nodetype == 0x6D:
            if nodetype < 0x40:
                elements = int(width / 1)
            else:
                elements = width
            if elements > 1:
                node = Node.bool_array(name, [False] * elements)
            else:
                node = Node.bool(name, False)
        elif nodetype == 0x2F:
            # Special case, this is an attribute
            if name[-1] != '@':
                raise Exception(
                    f'Attribute name {name} expected to end with @')
            root.set_attribute(name[:-1], '')
            continue
        else:
            raise Exception(f'Unimplemented node type 0x{nodetype:02x}')

        # Append it
        root.add_child(node)

    return root
示例#28
0
    def verify_player_rb5_player_write_5(
        self,
        refid: str,
        loc: str,
        scores: List[Dict[str, int]] = [],
        rivals: List[int] = [],
        mycourse: Optional[List[Dict[str, int]]] = None,
    ) -> int:
        call = self.call_node()

        player = Node.void('player')
        call.add_child(player)
        player.set_attribute('method', 'rb5_player_write_5')
        pdata = Node.void('pdata')
        player.add_child(pdata)
        account = Node.void('account')
        pdata.add_child(account)
        account.add_child(Node.s32('usrid', 0))
        account.add_child(Node.s32('plyid', 0))
        account.add_child(Node.s32('tpc', 1))
        account.add_child(Node.s32('dpc', 1))
        account.add_child(Node.s32('crd', 1))
        account.add_child(Node.s32('brd', 1))
        account.add_child(Node.s32('tdc', 1))
        account.add_child(Node.string('rid', refid))
        account.add_child(Node.string('lid', loc))
        account.add_child(Node.u8('wmode', 0))
        account.add_child(Node.u8('gmode', 0))
        account.add_child(Node.s16('ver', 0))
        account.add_child(Node.bool('pp', False))
        account.add_child(Node.bool('ps', False))
        account.add_child(Node.bool('continue', False))
        account.add_child(Node.bool('firstfree', False))
        account.add_child(Node.s16('pay', 0))
        account.add_child(Node.s16('pay_pc', 0))
        account.add_child(Node.u64('st', int(time.time() * 1000)))
        base = Node.void('base')
        pdata.add_child(base)
        base.add_child(Node.string('name', self.NAME))
        base.add_child(Node.s32('mg', 0))
        base.add_child(Node.s32('ap', 0))
        base.add_child(Node.s32('uattr', 0))
        base.add_child(Node.s32('money', 0))
        base.add_child(Node.bool('is_tut', False))
        base.add_child(Node.s32('class', -1))
        base.add_child(Node.s32('class_ar', 0))
        base.add_child(Node.s32('skill_point', 0))
        stglog = Node.void('stglog')
        pdata.add_child(stglog)

        index = 0
        for score in scores:
            log = Node.void('log')
            stglog.add_child(log)
            log.add_child(Node.s8('stg', index))
            log.add_child(Node.s16('mid', score['id']))
            log.add_child(Node.s8('ng', score['chart']))
            log.add_child(Node.s8('col', 1))
            log.add_child(Node.s8('mt', 0))
            log.add_child(Node.s8('rt', 0))
            log.add_child(Node.s8('ct', score['clear_type']))
            log.add_child(Node.s16('param', score['combo_type']))
            log.add_child(Node.s16('grd', 0))
            log.add_child(Node.s16('ar', score['achievement_rate']))
            log.add_child(Node.s16('sc', score['score']))
            log.add_child(Node.s16('jt_jst', 0))
            log.add_child(Node.s16('jt_grt', 0))
            log.add_child(Node.s16('jt_gd', 0))
            log.add_child(Node.s16('jt_ms', score['miss_count']))
            log.add_child(Node.s16('jt_jr', 0))
            log.add_child(Node.s32('r_uid', 0))
            log.add_child(Node.s32('r_plyid', 0))
            log.add_child(Node.s8('r_stg', 0))
            log.add_child(Node.s8('r_ct', -1))
            log.add_child(Node.s16('r_sc', 0))
            log.add_child(Node.s16('r_grd', 0))
            log.add_child(Node.s16('r_ar', 0))
            log.add_child(Node.s8('r_cpuid', -1))
            log.add_child(Node.s32('time', int(time.time())))
            log.add_child(Node.s8('decide', 0))
            log.add_child(Node.s16('g_gauge', 5000))
            log.add_child(Node.s32('k_flag', 0))
            index = index + 1

        rivalnode = Node.void('rival')
        pdata.add_child(rivalnode)
        for rival in rivals:
            r = Node.void('r')
            rivalnode.add_child(r)
            r.add_child(Node.s32('id', rival))
            r.add_child(Node.s8('regist_type', 3))

        if mycourse is not None:
            mycoursenode = Node.void('mycourse')
            pdata.add_child(mycoursenode)
            mycoursenode.add_child(Node.s16('mycourse_id', 1))
            mycoursenode.add_child(
                Node.s32('music_id_1', mycourse[0]['music_id']))
            mycoursenode.add_child(
                Node.s16('note_grade_1', mycourse[0]['note_grade']))
            mycoursenode.add_child(Node.s32('score_1', mycourse[0]['score']))
            mycoursenode.add_child(
                Node.s32('music_id_2', mycourse[1]['music_id']))
            mycoursenode.add_child(
                Node.s16('note_grade_2', mycourse[1]['note_grade']))
            mycoursenode.add_child(Node.s32('score_2', mycourse[1]['score']))
            mycoursenode.add_child(
                Node.s32('music_id_3', mycourse[2]['music_id']))
            mycoursenode.add_child(
                Node.s16('note_grade_3', mycourse[2]['note_grade']))
            mycoursenode.add_child(Node.s32('score_3', mycourse[2]['score']))
            mycoursenode.add_child(
                Node.s32('music_id_4', mycourse[3]['music_id']))
            mycoursenode.add_child(
                Node.s16('note_grade_4', mycourse[3]['note_grade']))
            mycoursenode.add_child(Node.s32('score_4', mycourse[3]['score']))
            mycoursenode.add_child(Node.s32('insert_time', -1))
            mycoursenode.add_child(Node.s32('def_music_id_1', -1))
            mycoursenode.add_child(Node.s16('def_note_grade_1', -1))
            mycoursenode.add_child(Node.s32('def_music_id_2', -1))
            mycoursenode.add_child(Node.s16('def_note_grade_2', -1))
            mycoursenode.add_child(Node.s32('def_music_id_3', -1))
            mycoursenode.add_child(Node.s16('def_note_grade_3', -1))
            mycoursenode.add_child(Node.s32('def_music_id_4', -1))
            mycoursenode.add_child(Node.s16('def_note_grade_4', -1))

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

        # Verify that response is correct
        self.assert_path(resp, "response/player/uid")
        return resp.child_value('player/uid')
示例#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.u8('active_fr_num',
                                  0))  # TODO: Hook up rivals code?
        account.add_child(
            Node.s16('total_play_cnt', statistics.get_int('total_plays', 0)))
        account.add_child(Node.s16('today_play_cnt', today_count))
        account.add_child(
            Node.s16('consecutive_days',
                     statistics.get_int('consecutive_days', 0)))
        account.add_child(
            Node.s16('total_days', statistics.get_int('total_days', 0)))
        account.add_child(Node.s16('interval_day', 0))

        # Add scores section
        last_played = [
            x[0] for x in self.data.local.music.get_last_played(
                self.game, self.version, userid, 5)
        ]
        most_played = [
            x[0] for x in self.data.local.music.get_most_played(
                self.game, self.version, userid, 10)
        ]
        while len(last_played) < 5:
            last_played.append(-1)
        while len(most_played) < 10:
            most_played.append(-1)

        account.add_child(Node.s16_array('my_best', most_played))
        account.add_child(Node.s16_array('latest_music', last_played))

        scores = self.data.remote.music.get_scores(self.game, self.version,
                                                   userid)
        for score in scores:
            # Skip any scores for chart types we don't support
            if score.chart not in [
                    self.CHART_TYPE_EASY,
                    self.CHART_TYPE_NORMAL,
                    self.CHART_TYPE_HYPER,
                    self.CHART_TYPE_EX,
            ]:
                continue

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

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

        # Net VS section
        netvs = Node.void('netvs')
        root.add_child(netvs)
        netvs.add_child(Node.s32('rank_point', 0))
        netvs.add_child(Node.s16_array('record', [0, 0, 0, 0, 0, 0]))
        netvs.add_child(Node.u8('rank', 0))
        netvs.add_child(Node.s8('vs_rank_old', 0))
        netvs.add_child(Node.s8_array('ojama_condition', [0] * 74))
        netvs.add_child(Node.s8_array('set_ojama', [0, 0, 0]))
        netvs.add_child(Node.s8_array('set_recommend', [0, 0, 0]))
        netvs.add_child(Node.u32('netvs_play_cnt', 0))
        for dialog in [0, 1, 2, 3, 4, 5]:
            # TODO: Configure this, maybe?
            netvs.add_child(Node.string('dialog', f'dialog#{dialog}'))

        # Set up config
        config = Node.void('config')
        root.add_child(config)
        config.add_child(Node.u8('mode', profile.get_int('mode', 0)))
        config.add_child(Node.s16('chara', profile.get_int('chara', -1)))
        config.add_child(Node.s16('music', profile.get_int('music', -1)))
        config.add_child(Node.u8('sheet', profile.get_int('sheet', 0)))
        config.add_child(Node.s8('category', profile.get_int('category', 1)))
        config.add_child(
            Node.s8('sub_category', profile.get_int('sub_category', -1)))
        config.add_child(
            Node.s8('chara_category', profile.get_int('chara_category', -1)))
        config.add_child(Node.s16('story_id', profile.get_int('story_id', -1)))
        config.add_child(
            Node.s16('course_id', profile.get_int('course_id', -1)))
        config.add_child(
            Node.s8('course_folder', profile.get_int('course_folder', -1)))
        config.add_child(
            Node.s8('story_folder', profile.get_int('story_folder', -1)))
        config.add_child(
            Node.s8('ms_banner_disp', profile.get_int('ms_banner_disp')))
        config.add_child(
            Node.s8('ms_down_info', profile.get_int('ms_down_info')))
        config.add_child(
            Node.s8('ms_side_info', profile.get_int('ms_side_info')))
        config.add_child(
            Node.s8('ms_raise_type', profile.get_int('ms_raise_type')))
        config.add_child(Node.s8('ms_rnd_type',
                                 profile.get_int('ms_rnd_type')))

        # Set up option
        option_dict = profile.get_dict('option')
        option = Node.void('option')
        root.add_child(option)
        option.add_child(
            Node.s16('hispeed', option_dict.get_int('hispeed', 10)))
        option.add_child(Node.u8('popkun', option_dict.get_int('popkun', 0)))
        option.add_child(
            Node.bool('hidden', option_dict.get_bool('hidden', False)))
        option.add_child(
            Node.s16('hidden_rate', option_dict.get_int('hidden_rate', -1)))
        option.add_child(
            Node.bool('sudden', option_dict.get_bool('sudden', False)))
        option.add_child(
            Node.s16('sudden_rate', option_dict.get_int('sudden_rate', -1)))
        option.add_child(Node.s8('randmir', option_dict.get_int('randmir', 0)))
        option.add_child(
            Node.s8('gauge_type', option_dict.get_int('gauge_type', 0)))
        option.add_child(Node.u8('ojama_0', option_dict.get_int('ojama_0', 0)))
        option.add_child(Node.u8('ojama_1', option_dict.get_int('ojama_1', 0)))
        option.add_child(
            Node.bool('forever_0', option_dict.get_bool('forever_0', False)))
        option.add_child(
            Node.bool('forever_1', option_dict.get_bool('forever_1', False)))
        option.add_child(
            Node.bool('full_setting',
                      option_dict.get_bool('full_setting', False)))

        # Set up info
        info = Node.void('info')
        root.add_child(info)
        info.add_child(Node.u16('ep', profile.get_int('ep', 0)))
        info.add_child(Node.u16('ap', profile.get_int('ap', 0)))

        # Set up custom_cate
        custom_cate = Node.void('custom_cate')
        root.add_child(custom_cate)
        custom_cate.add_child(Node.s8('valid', 0))
        custom_cate.add_child(Node.s8('lv_min', -1))
        custom_cate.add_child(Node.s8('lv_max', -1))
        custom_cate.add_child(Node.s8('medal_min', -1))
        custom_cate.add_child(Node.s8('medal_max', -1))
        custom_cate.add_child(Node.s8('friend_no', -1))
        custom_cate.add_child(Node.s8('score_flg', -1))

        # Set up customize
        customize_dict = profile.get_dict('customize')
        customize = Node.void('customize')
        root.add_child(customize)
        customize.add_child(
            Node.u16('effect', customize_dict.get_int('effect')))
        customize.add_child(
            Node.u16('hukidashi', customize_dict.get_int('hukidashi')))
        customize.add_child(Node.u16('font', customize_dict.get_int('font')))
        customize.add_child(
            Node.u16('comment_1', customize_dict.get_int('comment_1')))
        customize.add_child(
            Node.u16('comment_2', customize_dict.get_int('comment_2')))

        # Set up achievements
        achievements = self.data.local.user.get_achievements(
            self.game, self.version, userid)
        for achievement in achievements:
            if achievement.type == 'item':
                itemtype = achievement.data.get_int('type')
                param = achievement.data.get_int('param')

                item = Node.void('item')
                root.add_child(item)
                item.add_child(Node.u8('type', itemtype))
                item.add_child(Node.u16('id', achievement.id))
                item.add_child(Node.u16('param', param))
                item.add_child(Node.bool('is_new', False))

            elif achievement.type == 'achievement':
                count = achievement.data.get_int('count')

                ach_node = Node.void('achievement')
                root.add_child(ach_node)
                ach_node.add_child(Node.u8('type', achievement.id))
                ach_node.add_child(Node.u32('count', count))

            elif achievement.type == 'chara':
                friendship = achievement.data.get_int('friendship')

                chara = Node.void('chara_param')
                root.add_child(chara)
                chara.add_child(Node.u16('chara_id', achievement.id))
                chara.add_child(Node.u16('friendship', friendship))

            elif achievement.type == 'story':
                chapter = achievement.data.get_int('chapter')
                gauge = achievement.data.get_int('gauge')
                cleared = achievement.data.get_bool('cleared')
                clear_chapter = achievement.data.get_int('clear_chapter')

                story = Node.void('story')
                root.add_child(story)
                story.add_child(Node.u32('story_id', achievement.id))
                story.add_child(Node.u32('chapter_id', chapter))
                story.add_child(Node.u16('gauge_point', gauge))
                story.add_child(Node.bool('is_cleared', cleared))
                story.add_child(Node.u32('clear_chapter', clear_chapter))

        return root
示例#30
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))

        # No rival support, yet.
        rivallist = Node.void('rivallist')
        player.add_child(rivallist)
        rivallist.set_attribute('count', '0')
        mylist = Node.void('mylist')
        player.add_child(mylist)
        mylist.set_attribute('count', '0')

        # No collaboration support yet.
        collabo = Node.void('collabo')
        player.add_child(collabo)
        collabo.add_child(Node.bool('success', False))
        collabo.add_child(Node.bool('completed', False))

        # Daily FC challenge.
        entry = self.data.local.game.get_time_sensitive_settings(self.game, self.version, 'fc_challenge')
        if entry is None:
            entry = ValidatedDict()

        # Figure out if we've played these songs
        start_time, end_time = self.data.local.network.get_schedule_duration('daily')
        today_attempts = self.data.local.music.get_all_attempts(self.game, self.version, userid, entry.get_int('today', -1), timelimit=start_time)

        challenge = Node.void('challenge')
        player.add_child(challenge)
        today = Node.void('today')
        challenge.add_child(today)
        today.add_child(Node.s32('music_id', entry.get_int('today', -1)))
        today.add_child(Node.u8('state', 0x40 if len(today_attempts) > 0 else 0x0))
        onlynow = Node.void('onlynow')
        challenge.add_child(onlynow)
        onlynow.add_child(Node.s32('magic_no', 0))
        onlynow.add_child(Node.s16('cycle', 0))

        # Bistro event
        bistro = Node.void('bistro')
        player.add_child(bistro)

        # Presumably these can affect the speed of the event
        info_1 = Node.void('info')
        bistro.add_child(info_1)
        info_1.add_child(Node.float('delicious_rate', 1.0))
        info_1.add_child(Node.float('favorite_rate', 1.0))
        bistro.add_child(Node.s32('carry_over', profile.get_int('bistro_carry_over')))

        # Your chef dude, I guess?
        chefdict = profile.get_dict('chef')
        chef = Node.void('chef')
        bistro.add_child(chef)
        chef.add_child(Node.s32('id', chefdict.get_int('id', 1)))
        chef.add_child(Node.u8('ability', chefdict.get_int('ability', 2)))
        chef.add_child(Node.u8('remain', chefdict.get_int('remain', 30)))
        chef.add_child(Node.u8('rate', chefdict.get_int('rate', 1)))

        # Routes, similar to story mode in Pop'n I guess?
        routes = [
            {
                'id': 50000284,
                'price': 20,
                'satisfaction': 10,
                'favorite': True,
            },
            {
                'id': 50000283,
                'price': 20,
                'satisfaction': 20,
                'favorite': False,
            },
            {
                'id': 50000282,
                'price': 30,
                'satisfaction': 10,
                'favorite': False,
            },
            {
                'id': 50000275,
                'price': 10,
                'satisfaction': 55,
                'favorite': False,
            },
            {
                'id': 50000274,
                'price': 40,
                'satisfaction': 40,
                'favorite': False,
            },
            {
                'id': 50000273,
                'price': 80,
                'satisfaction': 60,
                'favorite': False,
            },
            {
                'id': 50000272,
                'price': 70,
                'satisfaction': 60,
                'favorite': False,
            },
            {
                'id': 50000271,
                'price': 90,
                'satisfaction': 80,
                'favorite': False,
            },
            {
                'id': 50000270,
                'price': 90,
                'satisfaction': 20,
                'favorite': False,
            },
        ]
        for route_no in range(len(routes)):
            routedata = routes[route_no]
            route = Node.void('route')
            bistro.add_child(route)
            route.set_attribute('no', str(route_no))

            music = Node.void('music')
            route.add_child(music)
            music.add_child(Node.s32('id', routedata['id']))
            music.add_child(Node.u16('price', routedata['price']))
            music.add_child(Node.s32('price_s32', routedata['price']))

            # Look up any updated satisfaction stored by the game
            routesaved = self.data.local.user.get_achievement(self.game, self.version, userid, route_no + 1, 'route')
            if routesaved is None:
                routesaved = ValidatedDict()
            satisfaction = routesaved.get_int('satisfaction', routedata['satisfaction'])

            gourmates = Node.void('gourmates')
            route.add_child(gourmates)
            gourmates.add_child(Node.s32('id', route_no + 1))
            gourmates.add_child(Node.u8('favorite', 1 if routedata['favorite'] else 0))
            gourmates.add_child(Node.u16('satisfaction', satisfaction))
            gourmates.add_child(Node.s32('satisfaction_s32', satisfaction))

        # Sane defaults for unknown nodes
        only_now_music = Node.void('only_now_music')
        player.add_child(only_now_music)
        only_now_music.set_attribute('count', '0')
        requested_music = Node.void('requested_music')
        player.add_child(requested_music)
        requested_music.set_attribute('count', '0')
        kac_music = Node.void('kac_music')
        player.add_child(kac_music)
        kac_music.set_attribute('count', '0')
        history = Node.void('history')
        player.add_child(history)
        history.set_attribute('count', '0')

        # Basic profile info
        player.add_child(Node.string('name', profile.get_str('name', 'なし')))
        player.add_child(Node.s32('jid', profile.get_int('extid')))
        player.add_child(Node.string('refid', profile.get_str('refid')))

        # Miscelaneous history stuff
        data.add_child(Node.u8('termver', 16))
        data.add_child(Node.u32('season_etime', 0))
        data.add_child(Node.s32('bistro_last_music_id', 0))
        data.add_child(Node.s32_array(
            'white_music_list',
            [
                -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1,
            ],
        ))
        data.add_child(Node.s32_array(
            'old_music_list',
            [
                -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1,
            ],
        ))
        data.add_child(Node.s32_array(
            'open_music_list',
            [
                -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1,
            ],
        ))

        # Unsupported collaboration events with other games
        collabo_info = Node.void('collabo_info')
        data.add_child(collabo_info)

        # Unsupported marathon stuff
        run_run_marathon = Node.void('run_run_marathon')
        collabo_info.add_child(run_run_marathon)
        run_run_marathon.set_attribute('type', '1')
        run_run_marathon.add_child(Node.u8('state', 1))
        run_run_marathon.add_child(Node.bool('is_report_end', True))

        # Unsupported policy break stuff
        policy_break = Node.void('policy_break')
        collabo_info.add_child(policy_break)
        policy_break.set_attribute('type', '1')
        policy_break.add_child(Node.u8('state', 1))
        policy_break.add_child(Node.bool('is_report_end', False))

        # Unsupported vocaloid stuff
        vocaloid_event = Node.void('vocaloid_event')
        collabo_info.add_child(vocaloid_event)
        vocaloid_event.set_attribute('type', '1')
        vocaloid_event.add_child(Node.u8('state', 0))
        vocaloid_event.add_child(Node.s32('music_id', 0))

        # No obnoxious 30 second wait to play.
        matching_off = Node.void('matching_off')
        data.add_child(matching_off)
        matching_off.add_child(Node.bool('is_open', True))

        return root