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

        player = Node.void('player')
        player.set_attribute('method', 'rb5_player_start')
        player.add_child(Node.string('rid', refid))
        player.add_child(Node.u8_array('ga', [127, 0, 0, 1]))
        player.add_child(Node.u16('gp', 10573))
        player.add_child(Node.u8_array('la', [16, 0, 0, 0]))
        player.add_child(
            Node.u8_array(
                'pnid',
                [39, 16, 0, 0, 0, 23, 62, 60, 39, 127, 0, 0, 1, 23, 62, 60]))

        call.add_child(player)

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

        # Verify that response is correct
        self.assert_path(resp, "response/player/plyid")
        self.assert_path(resp, "response/player/start_time")
        self.assert_path(resp, "response/player/event_ctrl")
        self.assert_path(resp, "response/player/item_lock_ctrl")
        self.assert_path(resp, "response/player/mycourse_ctrl")
示例#2
0
    def verify_lobby_entry(self, location: str, extid: int) -> int:
        call = self.call_node()

        lobby = Node.void('lobby')
        lobby.set_attribute('method', 'entry')
        e = Node.void('e')
        lobby.add_child(e)
        e.add_child(Node.s32('eid', 0))
        e.add_child(Node.u16('mid', 79))
        e.add_child(Node.u8('ng', 0))
        e.add_child(Node.s32('uid', extid))
        e.add_child(Node.s32('uattr', 0))
        e.add_child(Node.string('pn', self.NAME))
        e.add_child(Node.s16('mg', 255))
        e.add_child(Node.s32('mopt', 0))
        e.add_child(Node.s32('tid', 0))
        e.add_child(Node.string('tn', ''))
        e.add_child(Node.s32('topt', 0))
        e.add_child(Node.string('lid', location))
        e.add_child(Node.string('sn', ''))
        e.add_child(Node.u8('pref', 51))
        e.add_child(Node.s8('stg', 4))
        e.add_child(Node.s8('pside', 0))
        e.add_child(Node.s16('eatime', 30))
        e.add_child(Node.u8_array('ga', [127, 0, 0, 1]))
        e.add_child(Node.u16('gp', 10007))
        e.add_child(Node.u8_array('la', [16, 0, 0, 0]))
        e.add_child(Node.u8('ver', 5))
        lobby.add_child(Node.s32_array('friend', []))
        call.add_child(lobby)

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

        # Verify that response is correct
        self.assert_path(resp, "response/lobby/interval")
        self.assert_path(resp, "response/lobby/interval_p")
        self.assert_path(resp, "response/lobby/eid")
        self.assert_path(resp, "response/lobby/e/eid")
        self.assert_path(resp, "response/lobby/e/mid")
        self.assert_path(resp, "response/lobby/e/ng")
        self.assert_path(resp, "response/lobby/e/uid")
        self.assert_path(resp, "response/lobby/e/uattr")
        self.assert_path(resp, "response/lobby/e/pn")
        self.assert_path(resp, "response/lobby/e/mg")
        self.assert_path(resp, "response/lobby/e/mopt")
        self.assert_path(resp, "response/lobby/e/tid")
        self.assert_path(resp, "response/lobby/e/tn")
        self.assert_path(resp, "response/lobby/e/topt")
        self.assert_path(resp, "response/lobby/e/lid")
        self.assert_path(resp, "response/lobby/e/sn")
        self.assert_path(resp, "response/lobby/e/pref")
        self.assert_path(resp, "response/lobby/e/stg")
        self.assert_path(resp, "response/lobby/e/pside")
        self.assert_path(resp, "response/lobby/e/eatime")
        self.assert_path(resp, "response/lobby/e/ga")
        self.assert_path(resp, "response/lobby/e/gp")
        self.assert_path(resp, "response/lobby/e/la")
        self.assert_path(resp, "response/lobby/e/ver")
        return resp.child_value('lobby/eid')
示例#3
0
    def handle_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)
            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'),
                    'tid': request.child_value('e/tid'),
                    'tn': request.child_value('e/tn'),
                    'topt': request.child_value('e/topt'),
                    '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'),
                })
            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.string('pn', profile.get_str('name')))
            e.add_child(Node.s32('uattr', profile.get_int('uattr')))
            e.add_child(Node.s32('mopt', lobby.get_int('mopt')))
            e.add_child(Node.s16('mg', profile.get_int('mg')))
            e.add_child(Node.s32('tid', lobby.get_int('tid')))
            e.add_child(Node.string('tn', lobby.get_str('tn')))
            e.add_child(Node.s32('topt', lobby.get_int('topt')))
            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)))

        return root
示例#4
0
    def handle_lobby_rb5_lobby_read_request(self, request: Node) -> Node:
        root = Node.void('lobby')
        root.add_child(Node.s32('interval', 120))
        root.add_child(Node.s32('interval_p', 120))

        # Look up all lobbies matching the criteria specified
        ver = request.child_value('var')
        mg = request.child_value('m_grade')  # noqa: F841
        extid = request.child_value('uid')
        limit = request.child_value('max')
        userid = self.data.remote.user.from_extid(self.game, self.version,
                                                  extid)
        if userid is not None:
            lobbies = self.data.local.lobby.get_all_lobbies(
                self.game, self.version)
            for (user, lobby) in lobbies:
                if limit <= 0:
                    break

                if user == userid:
                    # If we have our own lobby, don't return it
                    continue
                if ver != lobby.get_int('ver'):
                    # Don't return lobby data for different versions
                    continue

                profile = self.get_profile(user)
                info = self.data.local.lobby.get_play_session_info(
                    self.game, self.version, userid)
                if profile is None or info is None:
                    # No profile info, don't return this lobby
                    return root

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

                limit = limit - 1

        return root
示例#5
0
    def verify_game_save_m(self, ref_id: str, score: Dict[str, Any]) -> None:
        call = self.call_node()
        game = Node.void('game')
        call.add_child(game)
        game.set_attribute('refid', ref_id)
        game.set_attribute('ver', '1')
        game.set_attribute('mtype', str(score['chart']))
        game.set_attribute('mid', str(score['id']))
        game.set_attribute('method', 'save_m')
        data = Node.void('data')
        game.add_child(data)
        data.set_attribute('perf', '1' if score['halo'] >= 2 else '0')
        data.set_attribute('score', str(score['score']))
        data.set_attribute('rank', str(score['rank']))
        data.set_attribute('phase', '1')
        data.set_attribute('full', '1' if score['halo'] >= 1 else '0')
        data.set_attribute('combo', str(score['combo']))
        option = Node.void('option')
        game.add_child(option)
        option.set_attribute('opt0', '6')
        option.set_attribute('opt6', '1')
        game.add_child(Node.u8_array('trace', [0] * 512))
        game.add_child(Node.u32('size', 512))

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

        # Verify that response is correct
        self.assert_path(resp, "response/game")
示例#6
0
    def verify_game_save_c(self, ref_id: str, course: Dict[str, Any]) -> None:
        call = self.call_node()
        game = Node.void('game')
        call.add_child(game)
        game.set_attribute('ctype', str(course['chart']))
        game.set_attribute('cid', str(course['id']))
        game.set_attribute('method', 'save_c')
        game.set_attribute('ver', '2012092400')
        game.set_attribute('refid', ref_id)
        data = Node.void('data')
        game.add_child(data)
        data.set_attribute('combo_type', str(course['combo_type']))
        data.set_attribute('clear', '1')
        data.set_attribute('combo', str(course['combo']))
        data.set_attribute('opt', '32774')
        data.set_attribute('per', '995')
        data.set_attribute('score', str(course['score']))
        data.set_attribute('stage', str(course['stage']))
        data.set_attribute('rank', str(course['rank']))
        game.add_child(Node.u8_array('trace', [0] * 4096))
        game.add_child(Node.u32('size', 4096))

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

        # Verify that response is correct
        self.assert_path(resp, "response/game")
示例#7
0
    def handle_game_trace_request(self, request: Node) -> Node:
        # This is almost identical to 2013 and below, except it will never
        # even try to request course traces, so we fork from common functionality.
        extid = int(request.attribute('code'))
        chart = int(request.attribute('type'))
        mid = intish(request.attribute('mid'))

        # Base packet is just game, if we find something we add to it
        game = Node.void('game')

        # Rival trace loading
        userid = self.data.remote.user.from_extid(self.game, self.version,
                                                  extid)
        if userid is None:
            # Nothing to load
            return game

        # Load trace from song score
        songscore = self.data.remote.music.get_score(
            self.game,
            self.music_version,
            userid,
            mid,
            self.game_to_db_chart(chart),
        )
        if songscore is not None and 'trace' in songscore.data:
            game.add_child(Node.u32('size', len(songscore.data['trace'])))
            game.add_child(Node.u8_array('trace', songscore.data['trace']))

        return game
示例#8
0
    def verify_gameend_regist(
        self,
        ref_id: str,
        jid: int,
        scores: List[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))
        pcbinfo = Node.void('pcbinfo')
        gameend.add_child(pcbinfo)
        pcbinfo.set_attribute('client_data_version', '0')
        data = Node.void('data')
        gameend.add_child(data)
        player = Node.void('player')
        data.add_child(player)
        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.add_child(Node.s32('music', score['id']))
            tune.add_child(Node.s64('timestamp', Time.now() * 1000))
            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]))

        # Swap with server
        resp = self.exchange('', call)
        self.assert_path(resp, "response/gameend/data/player/session_id")
示例#9
0
    def handle_lobby_read_request(self, request: Node) -> Node:
        root = Node.void('lobby')

        # Look up all lobbies matching the criteria specified
        mg = request.child_value('m_grade')  # noqa: F841
        extid = request.child_value('uid')
        limit = request.child_value('max')
        userid = self.data.remote.user.from_extid(self.game, self.version,
                                                  extid)
        if userid is not None:
            lobbies = self.data.local.lobby.get_all_lobbies(
                self.game, self.version)
            for (user, lobby) in lobbies:
                if limit <= 0:
                    break

                if user == userid:
                    # If we have our own lobby, don't return it
                    continue

                profile = self.get_profile(user)
                if profile is None:
                    # No profile info, don't return this lobby
                    continue

                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.string('pn', profile.get_str('name')))
                e.add_child(Node.s32('exp', profile.get_int('exp')))
                e.add_child(Node.u8('mg', profile.get_int('mg')))
                e.add_child(Node.s32('tid', lobby.get_int('tid')))
                e.add_child(Node.string('tn', lobby.get_str('tn')))
                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.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)))

                limit = limit - 1

        return root
示例#10
0
    def handle_game_trace_request(self, request: Node) -> Node:
        extid = int(request.attribute('code'))
        chart = int(request.attribute('type'))
        cid = intish(request.attribute('cid'))
        mid = intish(request.attribute('mid'))

        # Base packet is just game, if we find something we add to it
        game = Node.void('game')

        # Rival trace loading
        userid = self.data.remote.user.from_extid(self.game, self.version, extid)
        if userid is None:
            # Nothing to load
            return game

        if mid is not None:
            # Load trace from song score
            songscore = self.data.remote.music.get_score(
                self.game,
                self.music_version,
                userid,
                mid,
                self.game_to_db_chart(chart),
            )
            if songscore is not None and 'trace' in songscore.data:
                game.add_child(Node.u32('size', len(songscore.data['trace'])))
                game.add_child(Node.u8_array('trace', songscore.data['trace']))

        elif cid is not None:
            # Load trace from achievement
            coursescore = self.data.local.user.get_achievement(
                self.game,
                self.version,
                userid,
                (cid * 4) + chart,
                'course',
            )
            if coursescore is not None and 'trace' in coursescore:
                game.add_child(Node.u32('size', len(coursescore['trace'])))
                game.add_child(Node.u8_array('trace', coursescore['trace']))

        # Nothing found, return empty
        return game
示例#11
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)
示例#12
0
    def verify_player_start(self, refid: str) -> None:
        call = self.call_node()

        player = Node.void('player')
        player.set_attribute('method', 'start')
        player.add_child(Node.string('rid', refid))
        player.add_child(Node.u8_array('ga', [127, 0, 0, 1]))
        player.add_child(Node.u16('gp', 10573))
        player.add_child(Node.u8_array('la', [16, 0, 0, 0]))
        call.add_child(player)

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

        # Verify that response is correct
        self.assert_path(resp, "response/player/plyid")
        self.assert_path(resp, "response/player/start_time")
        self.assert_path(resp, "response/player/event_ctrl")
        self.assert_path(resp, "response/player/item_lock_ctrl")
        self.assert_path(resp, "response/player/lincle_link_4")
        self.assert_path(resp, "response/player/jbrbcollabo")
        self.assert_path(resp, "response/player/tricolettepark")
示例#13
0
    def verify_game_save_m(self, ref_id: str, ext_id: str,
                           score: Dict[str, Any]) -> None:
        call = self.call_node()
        game = Node.void('game')
        call.add_child(game)
        game.set_attribute('method', 'save_m')
        game.set_attribute('diff', '12345')
        game.set_attribute('mtype', str(score['chart']))
        game.set_attribute('mid', str(score['id']))
        game.set_attribute('refid', ref_id)
        game.set_attribute('ver', '2014102700')
        data = Node.void('data')
        game.add_child(data)
        data.set_attribute('score', str(score['score']))
        data.set_attribute('rank', str(score['rank']))
        data.set_attribute('shop_area', '0')
        data.set_attribute('playmode', '1')
        data.set_attribute('combo', str(score['combo']))
        data.set_attribute('phase', '1')
        data.set_attribute('style', '0')
        data.set_attribute('full', '1' if score['halo'] >= 1 else '0')
        data.set_attribute('great_fc', '1' if score['halo'] == 1 else '0')
        data.set_attribute('good_fc', '1' if score['halo'] == 4 else '0')
        data.set_attribute('perf_fc', '1' if score['halo'] == 2 else '0')
        gauge = Node.void('gauge')
        game.add_child(gauge)
        gauge.set_attribute('life8', '0')
        gauge.set_attribute('assist', '0')
        gauge.set_attribute('risky', '0')
        gauge.set_attribute('life4', '0')
        gauge.set_attribute('hard', '0')
        player = Node.void('player')
        game.add_child(player)
        player.set_attribute('playcnt', '123')
        player.set_attribute('code', ext_id)
        option = Node.void('option_02')
        game.add_child(option)
        option.set_attribute('opt02_0', '6')
        option.set_attribute('opt02_6', '1')
        option.set_attribute('opt02_13', '2')
        game.add_child(Node.u8_array('trace', [0] * 512))
        game.add_child(Node.u32('size', 512))

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

        # Verify that response is correct
        self.assert_path(resp, "response/game")
示例#14
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
示例#15
0
    def format_scores(self, userid: UserID, profile: ValidatedDict,
                      scores: List[Score]) -> Node:

        root = Node.void('gametop')
        datanode = Node.void('data')
        root.add_child(datanode)
        player = Node.void('player')
        datanode.add_child(player)
        player.add_child(Node.s32('jid', profile.get_int('extid')))
        playdata = Node.void('playdata')
        player.add_child(playdata)
        playdata.set_attribute('count', str(len(scores)))

        music = ValidatedDict()
        for score in scores:
            chart = self.db_to_game_chart(score.chart)
            data = music.get_dict(str(score.id))
            play_cnt = data.get_int_array('play_cnt', 3)
            clear_cnt = data.get_int_array('clear_cnt', 3)
            clear_flags = data.get_int_array('clear_flags', 3)
            fc_cnt = data.get_int_array('fc_cnt', 3)
            ex_cnt = data.get_int_array('ex_cnt', 3)
            points = data.get_int_array('points', 3)

            # This means that we already assigned a value and it was greater than current
            # This is possible because we iterate through both hard mode and normal mode scores
            # and treat them equally.
            # TODO: generalize score merging code into a library since this does not account for
            # having a full combo in hard mode but not in normal.
            if points[chart] >= score.points:
                continue
            # Replace data for this chart type
            play_cnt[chart] = score.plays
            clear_cnt[chart] = score.data.get_int('clear_count')
            fc_cnt[chart] = score.data.get_int('full_combo_count')
            ex_cnt[chart] = score.data.get_int('excellent_count')
            points[chart] = score.points

            # Format the clear flags
            clear_flags[chart] = self.GAME_FLAG_BIT_PLAYED
            if score.data.get_int('clear_count') > 0:
                clear_flags[chart] |= self.GAME_FLAG_BIT_CLEARED
            if score.data.get_int('full_combo_count') > 0:
                clear_flags[chart] |= self.GAME_FLAG_BIT_FULL_COMBO
            if score.data.get_int('excellent_count') > 0:
                clear_flags[chart] |= self.GAME_FLAG_BIT_EXCELLENT

            # Save chart data back
            data.replace_int_array('play_cnt', 3, play_cnt)
            data.replace_int_array('clear_cnt', 3, clear_cnt)
            data.replace_int_array('clear_flags', 3, clear_flags)
            data.replace_int_array('fc_cnt', 3, fc_cnt)
            data.replace_int_array('ex_cnt', 3, ex_cnt)
            data.replace_int_array('points', 3, points)

            # Update the ghost (untyped)
            ghost = data.get('ghost', [None, None, None])
            ghost[chart] = score.data.get('ghost')
            data['ghost'] = ghost

            # Save it back
            music.replace_dict(str(score.id), data)

        for scoreid in music:
            scoredata = music[scoreid]
            musicdata = Node.void('musicdata')
            playdata.add_child(musicdata)

            musicdata.set_attribute('music_id', scoreid)
            musicdata.add_child(
                Node.s32_array('play_cnt',
                               scoredata.get_int_array('play_cnt', 3)))
            musicdata.add_child(
                Node.s32_array('clear_cnt',
                               scoredata.get_int_array('clear_cnt', 3)))
            musicdata.add_child(
                Node.s32_array('fc_cnt', scoredata.get_int_array('fc_cnt', 3)))
            musicdata.add_child(
                Node.s32_array('ex_cnt', scoredata.get_int_array('ex_cnt', 3)))
            musicdata.add_child(
                Node.s32_array('score', scoredata.get_int_array('points', 3)))
            musicdata.add_child(
                Node.s8_array('clear',
                              scoredata.get_int_array('clear_flags', 3)))

            ghosts = scoredata.get('ghost', [None, None, None])
            for i in range(len(ghosts)):
                ghost = ghosts[i]
                if ghost is None:
                    continue

                bar = Node.u8_array('bar', ghost)
                musicdata.add_child(bar)
                bar.set_attribute('seq', str(i))

        return root
示例#16
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('exp', play_stats.get_int('exp')))
        root.add_child(Node.u32('exp_o', profile.get_int('exp_o')))
        root.add_child(Node.u32('star', profile.get_int('star')))
        root.add_child(Node.u32('star_c', profile.get_int('star_c')))
        root.add_child(Node.u8('combo', profile.get_int('combo', 0)))
        root.add_child(Node.u8('timing_diff', profile.get_int('early_late', 0)))

        # Character stuff
        chara = Node.void('chara')
        root.add_child(chara)
        if 'chara' in profile:
            chara.set_attribute('my', str(profile.get_int('chara')))

        root.add_child(Node.u8_array('chara_opt', profile.get_int_array('chara_opt', 96)))

        # Drill rankings
        if 'title' in profile:
            title = Node.void('title')
            root.add_child(title)
            titledict = profile.get_dict('title')
            if 't' in titledict:
                title.set_attribute('t', str(titledict.get_int('t')))
            if 's' in titledict:
                title.set_attribute('s', str(titledict.get_int('s')))
            if 'd' in titledict:
                title.set_attribute('d', str(titledict.get_int('d')))

        if 'title_gr' in profile:
            title_gr = Node.void('title_gr')
            root.add_child(title_gr)
            title_grdict = profile.get_dict('title_gr')
            if 't' in title_grdict:
                title_gr.set_attribute('t', str(title_grdict.get_int('t')))
            if 's' in title_grdict:
                title_gr.set_attribute('s', str(title_grdict.get_int('s')))
            if 'd' in title_grdict:
                title_gr.set_attribute('d', str(title_grdict.get_int('d')))

        # Event progrses
        if 'event' in profile:
            event = Node.void('event')
            root.add_child(event)
            event_dict = profile.get_dict('event')
            if 'diff_sum' in event_dict:
                event.set_attribute('diff_sum', str(event_dict.get_int('diff_sum')))
            if 'welcome' in event_dict:
                event.set_attribute('welcome', str(event_dict.get_int('welcome')))
            if 'e_flags' in event_dict:
                event.set_attribute('e_flags', str(event_dict.get_int('e_flags')))

        if 'e_panel' in profile:
            e_panel = Node.void('e_panel')
            root.add_child(e_panel)
            e_panel_dict = profile.get_dict('e_panel')
            if 'play_id' in e_panel_dict:
                e_panel.set_attribute('play_id', str(e_panel_dict.get_int('play_id')))
            e_panel.add_child(Node.u8_array('cell', e_panel_dict.get_int_array('cell', 24)))
            e_panel.add_child(Node.u8_array('panel_state', e_panel_dict.get_int_array('panel_state', 6)))

        if 'e_pix' in profile:
            e_pix = Node.void('e_pix')
            root.add_child(e_pix)
            e_pix_dict = profile.get_dict('e_pix')
            if 'max_distance' in e_pix_dict:
                e_pix.set_attribute('max_distance', str(e_pix_dict.get_int('max_distance')))
            if 'max_planet' in e_pix_dict:
                e_pix.set_attribute('max_planet', str(e_pix_dict.get_int('max_planet')))
            if 'total_distance' in e_pix_dict:
                e_pix.set_attribute('total_distance', str(e_pix_dict.get_int('total_distance')))
            if 'total_planet' in e_pix_dict:
                e_pix.set_attribute('total_planet', str(e_pix_dict.get_int('total_planet')))
            if 'border_character' in e_pix_dict:
                e_pix.set_attribute('border_character', str(e_pix_dict.get_int('border_character')))
            if 'border_balloon' in e_pix_dict:
                e_pix.set_attribute('border_balloon', str(e_pix_dict.get_int('border_balloon')))
            if 'border_music_aftr' in e_pix_dict:
                e_pix.set_attribute('border_music_aftr', str(e_pix_dict.get_int('border_music_aftr')))
            if 'border_music_meii' in e_pix_dict:
                e_pix.set_attribute('border_music_meii', str(e_pix_dict.get_int('border_music_meii')))
            if 'border_music_dirt' in e_pix_dict:
                e_pix.set_attribute('border_music_dirt', str(e_pix_dict.get_int('border_music_dirt')))
            if 'flags' in e_pix_dict:
                e_pix.set_attribute('flags', str(e_pix_dict.get_int('flags')))

        # Calorie mode
        if 'weight' in profile:
            workouts = self.data.local.user.get_time_based_achievements(
                self.game,
                self.version,
                userid,
                achievementtype='workout',
                since=Time.now() - Time.SECONDS_IN_DAY,
            )
            total = sum([w.data.get_int('calories') for w in workouts])
            workout = Node.void('workout')
            root.add_child(workout)
            workout.set_attribute('weight', str(profile.get_int('weight')))
            workout.set_attribute('day', str(total))
            workout.set_attribute('disp', '1')

        # Last cursor settings
        last = Node.void('last')
        root.add_child(last)
        lastdict = profile.get_dict('last')
        last.set_attribute('fri', str(lastdict.get_int('fri')))
        last.set_attribute('style', str(lastdict.get_int('style')))
        last.set_attribute('mode', str(lastdict.get_int('mode')))
        last.set_attribute('cate', str(lastdict.get_int('cate')))
        last.set_attribute('sort', str(lastdict.get_int('sort')))
        last.set_attribute('mid', str(lastdict.get_int('mid')))
        last.set_attribute('mtype', str(lastdict.get_int('mtype')))
        last.set_attribute('cid', str(lastdict.get_int('cid')))
        last.set_attribute('ctype', str(lastdict.get_int('ctype')))
        last.set_attribute('sid', str(lastdict.get_int('sid')))

        # Groove gauge level-ups
        gr_s = Node.void('gr_s')
        root.add_child(gr_s)
        index = 1
        for entry in profile.get_int_array('gr_s', 5):
            gr_s.set_attribute(f'gr{index}', str(entry))
            index = index + 1

        gr_d = Node.void('gr_d')
        root.add_child(gr_d)
        index = 1
        for entry in profile.get_int_array('gr_d', 5):
            gr_d.set_attribute(f'gr{index}', str(entry))
            index = index + 1

        # Options in menus
        root.add_child(Node.s16_array('opt', profile.get_int_array('opt', 16)))
        root.add_child(Node.s16_array('opt_ex', profile.get_int_array('opt_ex', 16)))

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

        # Ranking display?
        root.add_child(Node.u16_array('rank', profile.get_int_array('rank', 100)))

        # Rivals
        links = self.data.local.user.get_links(self.game, self.version, userid)
        for link in links:
            if link.type[:7] != 'friend_':
                continue

            pos = int(link.type[7:])
            friend = self.get_profile(link.other_userid)
            play_stats = self.get_play_statistics(link.other_userid)
            if friend is not None:
                friendnode = Node.void('friend')
                root.add_child(friendnode)
                friendnode.set_attribute('pos', str(pos))
                friendnode.set_attribute('vs', '0')
                friendnode.set_attribute('up', '0')
                friendnode.add_child(Node.u32('code', friend.get_int('extid')))
                friendnode.add_child(Node.string('name', friend.get_str('name')))
                friendnode.add_child(Node.u8('area', friend.get_int('area', 51)))
                friendnode.add_child(Node.u32('exp', play_stats.get_int('exp')))
                friendnode.add_child(Node.u32('star', friend.get_int('star')))

                # Drill rankings
                if 'title' in friend:
                    title = Node.void('title')
                    friendnode.add_child(title)
                    titledict = friend.get_dict('title')
                    if 't' in titledict:
                        title.set_attribute('t', str(titledict.get_int('t')))
                    if 's' in titledict:
                        title.set_attribute('s', str(titledict.get_int('s')))
                    if 'd' in titledict:
                        title.set_attribute('d', str(titledict.get_int('d')))

                if 'title_gr' in friend:
                    title_gr = Node.void('title_gr')
                    friendnode.add_child(title_gr)
                    title_grdict = friend.get_dict('title_gr')
                    if 't' in title_grdict:
                        title_gr.set_attribute('t', str(title_grdict.get_int('t')))
                    if 's' in title_grdict:
                        title_gr.set_attribute('s', str(title_grdict.get_int('s')))
                    if 'd' in title_grdict:
                        title_gr.set_attribute('d', str(title_grdict.get_int('d')))

                # Groove gauge level-ups
                gr_s = Node.void('gr_s')
                friendnode.add_child(gr_s)
                index = 1
                for entry in friend.get_int_array('gr_s', 5):
                    gr_s.set_attribute(f'gr{index}', str(entry))
                    index = index + 1

                gr_d = Node.void('gr_d')
                friendnode.add_child(gr_d)
                index = 1
                for entry in friend.get_int_array('gr_d', 5):
                    gr_d.set_attribute(f'gr{index}', str(entry))
                    index = index + 1

        return root
示例#17
0
    def format_scores(self, userid: UserID, profile: ValidatedDict, scores: List[Score]) -> Node:
        scores = self.data.remote.music.get_scores(self.game, self.version, userid)

        root = Node.void('gametop')
        datanode = Node.void('data')
        root.add_child(datanode)
        player = Node.void('player')
        datanode.add_child(player)
        playdata = Node.void('playdata')
        player.add_child(playdata)
        playdata.set_attribute('count', str(len(scores)))

        music = ValidatedDict()
        for score in scores:
            data = music.get_dict(str(score.id))
            play_cnt = data.get_int_array('play_cnt', 3)
            clear_cnt = data.get_int_array('clear_cnt', 3)
            clear_flags = data.get_int_array('clear_flags', 3)
            fc_cnt = data.get_int_array('fc_cnt', 3)
            ex_cnt = data.get_int_array('ex_cnt', 3)
            points = data.get_int_array('points', 3)

            # Replace data for this chart type
            play_cnt[score.chart] = score.plays
            clear_cnt[score.chart] = score.data.get_int('clear_count')
            fc_cnt[score.chart] = score.data.get_int('full_combo_count')
            ex_cnt[score.chart] = score.data.get_int('excellent_count')
            points[score.chart] = score.points

            # Format the clear flags
            clear_flags[score.chart] = self.GAME_FLAG_BIT_PLAYED
            if score.data.get_int('clear_count') > 0:
                clear_flags[score.chart] |= self.GAME_FLAG_BIT_CLEARED
            if score.data.get_int('full_combo_count') > 0:
                clear_flags[score.chart] |= self.GAME_FLAG_BIT_FULL_COMBO
            if score.data.get_int('excellent_count') > 0:
                clear_flags[score.chart] |= self.GAME_FLAG_BIT_EXCELLENT

            # Save chart data back
            data.replace_int_array('play_cnt', 3, play_cnt)
            data.replace_int_array('clear_cnt', 3, clear_cnt)
            data.replace_int_array('clear_flags', 3, clear_flags)
            data.replace_int_array('fc_cnt', 3, fc_cnt)
            data.replace_int_array('ex_cnt', 3, ex_cnt)
            data.replace_int_array('points', 3, points)

            # Update the ghost (untyped)
            ghost = data.get('ghost', [None, None, None])
            ghost[score.chart] = score.data.get('ghost')
            data['ghost'] = ghost

            # Save it back
            music.replace_dict(str(score.id), data)

        for scoreid in music:
            scoredata = music[scoreid]
            musicdata = Node.void('musicdata')
            playdata.add_child(musicdata)

            musicdata.set_attribute('music_id', scoreid)
            musicdata.add_child(Node.s32_array('play_cnt', scoredata.get_int_array('play_cnt', 3)))
            musicdata.add_child(Node.s32_array('clear_cnt', scoredata.get_int_array('clear_cnt', 3)))
            musicdata.add_child(Node.s32_array('fc_cnt', scoredata.get_int_array('fc_cnt', 3)))
            musicdata.add_child(Node.s32_array('ex_cnt', scoredata.get_int_array('ex_cnt', 3)))
            musicdata.add_child(Node.s32_array('score', scoredata.get_int_array('points', 3)))
            musicdata.add_child(Node.s8_array('clear', scoredata.get_int_array('clear_flags', 3)))

            ghosts = scoredata.get('ghost', [None, None, None])
            for i in range(len(ghosts)):
                ghost = ghosts[i]
                if ghost is None:
                    continue

                bar = Node.u8_array('bar', ghost)
                musicdata.add_child(bar)
                bar.set_attribute('seq', str(i))

        return root
示例#18
0
    def verify_gameend_regist(
        self,
        ref_id: str,
        jid: int,
        scores: List[Dict[str, Any]],
        course: Dict[str, Any],
        league: Optional[Tuple[int, Tuple[int, int, int]]],
    ) -> 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.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')
            player.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

        if league is not None:
            leaguenode = Node.void('league')
            player.add_child(leaguenode)
            leaguenode.add_child(Node.s32('league_id', league[0]))
            leaguenode.add_child(Node.bool('is_first_play', False))
            leaguenode.add_child(Node.bool('is_checked', True))

            index = 0
            for leaguescore in league[1]:
                musicnode = Node.void('music')
                leaguenode.add_child(musicnode)
                musicnode.set_attribute('index', str(index))
                index = index + 1

                scorenode = Node.s32('score', leaguescore)
                musicnode.add_child(scorenode)
                scorenode.set_attribute('clear', '3')

        # Swap with server
        resp = self.exchange('', call)
        self.assert_path(resp, "response/gameend/data/player/session_id")
示例#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)
        rprofiles: Dict[UserID, ValidatedDict] = {}
        root = Node.void('player')
        pdata = Node.void('pdata')
        root.add_child(pdata)

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

        # Previous account info
        previous_version = self.previous_version()
        if previous_version:
            succeeded = previous_version.has_profile(userid)
        else:
            succeeded = False

        # Account info
        account = Node.void('account')
        pdata.add_child(account)
        account.add_child(Node.s32('usrid', profile.get_int('extid')))
        account.add_child(Node.s32('tpc', statistics.get_int('total_plays',
                                                             0)))
        account.add_child(Node.s32('dpc', today_count))
        account.add_child(Node.s32('crd', 1))
        account.add_child(Node.s32('brd', 1))
        account.add_child(Node.s32('tdc', statistics.get_int('total_days', 0)))
        account.add_child(Node.s32('intrvld', 0))
        account.add_child(Node.s16('ver', 0))
        account.add_child(Node.bool('succeed', succeeded))
        account.add_child(Node.u64('pst', 0))
        account.add_child(Node.u64('st', Time.now() * 1000))
        account.add_child(Node.s32('opc', 0))
        account.add_child(Node.s32('lpc', 0))
        account.add_child(Node.s32('cpc', 0))
        account.add_child(Node.s32('mpc', 0))

        # Base profile info
        base = Node.void('base')
        pdata.add_child(base)
        base.add_child(Node.string('name', profile.get_str('name')))
        base.add_child(Node.s32('mg', profile.get_int('mg')))
        base.add_child(Node.s32('ap', profile.get_int('ap')))
        base.add_child(Node.string('cmnt', ''))
        base.add_child(Node.s32('uattr', profile.get_int('uattr')))
        base.add_child(Node.s32('money', profile.get_int('money')))
        base.add_child(Node.s32('tbs_5', -1))
        base.add_child(Node.s32_array('tbgs_5', [-1, -1, -1, -1]))
        base.add_child(
            Node.s16_array('mlog', [
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1
            ]))
        base.add_child(Node.s32('class', profile.get_int('class')))
        base.add_child(Node.s32('class_ar', profile.get_int('class_ar')))
        base.add_child(Node.s32('skill_point', profile.get_int('skill_point')))
        base.add_child(Node.bool('meteor_flg', False))

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

            if link.other_userid not in rprofiles:
                rprofile = self.get_profile(link.other_userid)
                if rprofile is None:
                    continue
                rprofiles[link.other_userid] = rprofile
            else:
                rprofile = rprofiles[link.other_userid]
            lobbyinfo = self.data.local.lobby.get_play_session_info(
                self.game, self.version, link.other_userid)
            if lobbyinfo is None:
                lobbyinfo = ValidatedDict()

            r = Node.void('r')
            rival.add_child(r)
            r.add_child(Node.s32('slot_id', slotid))
            r.add_child(Node.s32('id', rprofile.get_int('extid')))
            r.add_child(Node.string('name', rprofile.get_str('name')))
            r.add_child(
                Node.s32('icon',
                         rprofile.get_dict('config').get_int('icon_id')))
            r.add_child(Node.s32('class', rprofile.get_int('class')))
            r.add_child(Node.s32('class_ar', rprofile.get_int('class_ar')))
            r.add_child(Node.bool('friend', True))
            r.add_child(Node.bool('target', False))
            r.add_child(Node.u32('time', lobbyinfo.get_int('time')))
            r.add_child(Node.u8_array('ga', lobbyinfo.get_int_array('ga', 4)))
            r.add_child(Node.u16('gp', lobbyinfo.get_int('gp')))
            r.add_child(Node.u8_array('ipn', lobbyinfo.get_int_array('la', 4)))
            r.add_child(
                Node.u8_array('pnid', lobbyinfo.get_int_array('pnid', 16)))
            slotid = slotid + 1

        # Configuration
        configdict = profile.get_dict('config')
        config = Node.void('config')
        pdata.add_child(config)
        config.add_child(Node.u8('msel_bgm', configdict.get_int('msel_bgm')))
        config.add_child(
            Node.u8('narrowdown_type', configdict.get_int('narrowdown_type')))
        config.add_child(Node.s16('icon_id', configdict.get_int('icon_id')))
        config.add_child(Node.s16('byword_0', configdict.get_int('byword_0')))
        config.add_child(Node.s16('byword_1', configdict.get_int('byword_1')))
        config.add_child(
            Node.bool('is_auto_byword_0',
                      configdict.get_bool('is_auto_byword_0')))
        config.add_child(
            Node.bool('is_auto_byword_1',
                      configdict.get_bool('is_auto_byword_1')))
        config.add_child(Node.u8('mrec_type', configdict.get_int('mrec_type')))
        config.add_child(Node.u8('tab_sel', configdict.get_int('tab_sel')))
        config.add_child(Node.u8('card_disp', configdict.get_int('card_disp')))
        config.add_child(
            Node.u8('score_tab_disp', configdict.get_int('score_tab_disp')))
        config.add_child(
            Node.s16('last_music_id', configdict.get_int('last_music_id', -1)))
        config.add_child(
            Node.u8('last_note_grade', configdict.get_int('last_note_grade')))
        config.add_child(Node.u8('sort_type', configdict.get_int('sort_type')))
        config.add_child(
            Node.u8('rival_panel_type',
                    configdict.get_int('rival_panel_type')))
        config.add_child(
            Node.u64('random_entry_work',
                     configdict.get_int('random_entry_work')))
        config.add_child(
            Node.u64('custom_folder_work',
                     configdict.get_int('custom_folder_work')))
        config.add_child(
            Node.u8('folder_type', configdict.get_int('folder_type')))
        config.add_child(
            Node.u8('folder_lamp_type',
                    configdict.get_int('folder_lamp_type')))
        config.add_child(Node.bool('is_tweet',
                                   configdict.get_bool('is_tweet')))
        config.add_child(
            Node.bool('is_link_twitter',
                      configdict.get_bool('is_link_twitter')))

        # Customizations
        customdict = profile.get_dict('custom')
        custom = Node.void('custom')
        pdata.add_child(custom)
        custom.add_child(Node.u8('st_shot', customdict.get_int('st_shot')))
        custom.add_child(Node.u8('st_frame', customdict.get_int('st_frame')))
        custom.add_child(Node.u8('st_expl', customdict.get_int('st_expl')))
        custom.add_child(Node.u8('st_bg', customdict.get_int('st_bg')))
        custom.add_child(
            Node.u8('st_shot_vol', customdict.get_int('st_shot_vol')))
        custom.add_child(Node.u8('st_bg_bri', customdict.get_int('st_bg_bri')))
        custom.add_child(
            Node.u8('st_obj_size', customdict.get_int('st_obj_size')))
        custom.add_child(
            Node.u8('st_jr_gauge', customdict.get_int('st_jr_gauge')))
        custom.add_child(
            Node.u8('st_clr_gauge', customdict.get_int('st_clr_gauge')))
        custom.add_child(Node.u8('st_rnd', customdict.get_int('st_rnd')))
        custom.add_child(
            Node.u8('st_gr_gauge_type',
                    customdict.get_int('st_gr_gauge_type')))
        custom.add_child(
            Node.s16('voice_message_set',
                     customdict.get_int('voice_message_set', -1)))
        custom.add_child(
            Node.u8('same_time_note_disp',
                    customdict.get_int('same_time_note_disp')))
        custom.add_child(
            Node.u8('st_score_disp_type',
                    customdict.get_int('st_score_disp_type')))
        custom.add_child(
            Node.u8('st_bonus_type', customdict.get_int('st_bonus_type')))
        custom.add_child(
            Node.u8('st_rivalnote_type',
                    customdict.get_int('st_rivalnote_type')))
        custom.add_child(
            Node.u8('st_topassist_type',
                    customdict.get_int('st_topassist_type')))
        custom.add_child(
            Node.u8('high_speed', customdict.get_int('high_speed')))
        custom.add_child(Node.u8('st_hazard', customdict.get_int('st_hazard')))
        custom.add_child(
            Node.u8('st_clr_cond', customdict.get_int('st_clr_cond')))
        custom.add_child(
            Node.u8('voice_message_volume',
                    customdict.get_int('voice_message_volume')))

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

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

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

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

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

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

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

        # Announcements
        announce = Node.void('announce')
        pdata.add_child(announce)

        for announcement in achievements:
            if announcement.type[:13] != 'announcement_':
                continue
            announcementtype = int(announcement.type[13:])

            info = Node.void('info')
            announce.add_child(info)
            info.add_child(Node.u8('type', announcementtype))
            info.add_child(Node.u16('id', announcement.id))
            info.add_child(
                Node.u16('param', announcement.data.get_int('param')))
            info.add_child(
                Node.bool('bneedannounce', announcement.data.get_bool('need')))

        # Dojo ranking return
        dojo = Node.void('dojo')
        pdata.add_child(dojo)

        for entry in achievements:
            if entry.type != 'dojo':
                continue

            rec = Node.void('rec')
            dojo.add_child(rec)
            rec.add_child(Node.s32('class', entry.id))
            rec.add_child(
                Node.s32('clear_type', entry.data.get_int('clear_type')))
            rec.add_child(Node.s32('total_ar', entry.data.get_int('ar')))
            rec.add_child(Node.s32('total_score', entry.data.get_int('score')))
            rec.add_child(Node.s32('play_count', entry.data.get_int('plays')))
            rec.add_child(
                Node.s32('last_play_time',
                         entry.data.get_int('play_timestamp')))
            rec.add_child(
                Node.s32('record_update_time',
                         entry.data.get_int('record_timestamp')))
            rec.add_child(Node.s32('rank', 0))

        # Player Parameters
        player_param = Node.void('player_param')
        pdata.add_child(player_param)

        for param in achievements:
            if param.type[:13] != 'player_param_':
                continue
            itemtype = int(param.type[13:])

            itemnode = Node.void('item')
            player_param.add_child(itemnode)
            itemnode.add_child(Node.s32('type', itemtype))
            itemnode.add_child(Node.s32('bank', param.id))
            itemnode.add_child(
                Node.s32_array('data', param.data.get_int_array('data', 256)))

        # Shop score for players
        self._add_shop_score(pdata)

        # My List data
        mylist = Node.void('mylist')
        pdata.add_child(mylist)
        listdata = Node.void('list')
        mylist.add_child(listdata)
        listdata.add_child(Node.s16('idx', 0))
        listdata.add_child(
            Node.s16_array('mlst',
                           profile.get_int_array('favorites', 30, [-1] * 30)))

        # Minigame settings
        minigame = Node.void('minigame')
        pdata.add_child(minigame)
        minigame.add_child(Node.s8('mgid', profile.get_int('mgid')))
        minigame.add_child(Node.s32('sc', profile.get_int('mgsc')))

        # Derby settings
        derby = Node.void('derby')
        pdata.add_child(derby)
        derby.add_child(Node.bool('is_open', False))

        # Music rank points
        music_rank_point = Node.void('music_rank_point')
        pdata.add_child(music_rank_point)

        # yurukome list stuff
        yurukome_list = Node.void('yurukome_list')
        pdata.add_child(yurukome_list)

        for entry in achievements:
            if entry.type != 'yurukome':
                continue

            yurukome = Node.void('yurukome')
            yurukome_list.add_child(yurukome)
            yurukome.add_child(Node.s32('yurukome_id', entry.id))

        # My course mode
        mycourse = Node.void('mycourse')
        mycoursedict = profile.get_dict('mycourse')
        pdata.add_child(mycourse)
        mycourse.add_child(Node.s16('mycourse_id', 1))
        mycourse.add_child(
            Node.s32('music_id_1', mycoursedict.get_int('music_id_1', -1)))
        mycourse.add_child(
            Node.s16('note_grade_1', mycoursedict.get_int('note_grade_1', -1)))
        mycourse.add_child(
            Node.s32('score_1', mycoursedict.get_int('score_1', -1)))
        mycourse.add_child(
            Node.s32('music_id_2', mycoursedict.get_int('music_id_2', -1)))
        mycourse.add_child(
            Node.s16('note_grade_2', mycoursedict.get_int('note_grade_2', -1)))
        mycourse.add_child(
            Node.s32('score_2', mycoursedict.get_int('score_2', -1)))
        mycourse.add_child(
            Node.s32('music_id_3', mycoursedict.get_int('music_id_3', -1)))
        mycourse.add_child(
            Node.s16('note_grade_3', mycoursedict.get_int('note_grade_3', -1)))
        mycourse.add_child(
            Node.s32('score_3', mycoursedict.get_int('score_3', -1)))
        mycourse.add_child(
            Node.s32('music_id_4', mycoursedict.get_int('music_id_4', -1)))
        mycourse.add_child(
            Node.s16('note_grade_4', mycoursedict.get_int('note_grade_4', -1)))
        mycourse.add_child(
            Node.s32('score_4', mycoursedict.get_int('score_4', -1)))
        mycourse.add_child(
            Node.s32('insert_time', mycoursedict.get_int('insert_time', -1)))
        mycourse.add_child(Node.s32('def_music_id_1', -1))
        mycourse.add_child(Node.s16('def_note_grade_1', -1))
        mycourse.add_child(Node.s32('def_music_id_2', -1))
        mycourse.add_child(Node.s16('def_note_grade_2', -1))
        mycourse.add_child(Node.s32('def_music_id_3', -1))
        mycourse.add_child(Node.s16('def_note_grade_3', -1))
        mycourse.add_child(Node.s32('def_music_id_4', -1))
        mycourse.add_child(Node.s16('def_note_grade_4', -1))

        # Friend course scores
        mycourse_f = Node.void('mycourse_f')
        pdata.add_child(mycourse_f)

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

            if link.other_userid not in rprofiles:
                rprofile = self.get_profile(link.other_userid)
                if rprofile is None:
                    continue
                rprofiles[link.other_userid] = rprofile
            else:
                rprofile = rprofiles[link.other_userid]
            mycoursedict = rprofile.get_dict('mycourse')

            rec = Node.void('rec')
            mycourse_f.add_child(rec)
            rec.add_child(Node.s32('rival_id', rprofile.get_int('extid')))
            rec.add_child(Node.s16('mycourse_id', 1))
            rec.add_child(
                Node.s32('music_id_1', mycoursedict.get_int('music_id_1', -1)))
            rec.add_child(
                Node.s16('note_grade_1',
                         mycoursedict.get_int('note_grade_1', -1)))
            rec.add_child(
                Node.s32('score_1', mycoursedict.get_int('score_1', -1)))
            rec.add_child(
                Node.s32('music_id_2', mycoursedict.get_int('music_id_2', -1)))
            rec.add_child(
                Node.s16('note_grade_2',
                         mycoursedict.get_int('note_grade_2', -1)))
            rec.add_child(
                Node.s32('score_2', mycoursedict.get_int('score_2', -1)))
            rec.add_child(
                Node.s32('music_id_3', mycoursedict.get_int('music_id_3', -1)))
            rec.add_child(
                Node.s16('note_grade_3',
                         mycoursedict.get_int('note_grade_3', -1)))
            rec.add_child(
                Node.s32('score_3', mycoursedict.get_int('score_3', -1)))
            rec.add_child(
                Node.s32('music_id_4', mycoursedict.get_int('music_id_4', -1)))
            rec.add_child(
                Node.s16('note_grade_4',
                         mycoursedict.get_int('note_grade_4', -1)))
            rec.add_child(
                Node.s32('score_4', mycoursedict.get_int('score_4', -1)))
            rec.add_child(
                Node.s32('insert_time',
                         mycoursedict.get_int('insert_time', -1)))

        return root
示例#20
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', 'dialog#{}'.format(dialog)))

        sp_data = Node.void('sp_data')
        root.add_child(sp_data)
        sp_data.add_child(Node.s32('sp', profile.get_int('sp', 0)))

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

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

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

        # Daily play counts
        last_play_date = play_stats.get_int_array('last_play_date', 3)
        today_play_date = Time.todays_date()
        if (
            last_play_date[0] == today_play_date[0] and
            last_play_date[1] == today_play_date[1] and
            last_play_date[2] == today_play_date[2]
        ):
            today_count = play_stats.get_int('today_plays', 0)
        else:
            today_count = 0
        daycount = Node.void('daycount')
        root.add_child(daycount)
        daycount.set_attribute('playcount', str(today_count))

        # Daily combo stuff, unknown how this works
        dailycombo = Node.void('dailycombo')
        root.add_child(dailycombo)
        dailycombo.set_attribute('daily_combo', str(0))
        dailycombo.set_attribute('daily_combo_lv', str(0))

        # Last cursor settings
        last = Node.void('last')
        root.add_child(last)
        lastdict = profile.get_dict('last')
        last.set_attribute('rival1', str(lastdict.get_int('rival1', -1)))
        last.set_attribute('rival2', str(lastdict.get_int('rival2', -1)))
        last.set_attribute('rival3', str(lastdict.get_int('rival3', -1)))
        last.set_attribute('fri', str(lastdict.get_int('rival1', -1)))  # This literally goes to the same memory in X3
        last.set_attribute('style', str(lastdict.get_int('style')))
        last.set_attribute('mode', str(lastdict.get_int('mode')))
        last.set_attribute('cate', str(lastdict.get_int('cate')))
        last.set_attribute('sort', str(lastdict.get_int('sort')))
        last.set_attribute('mid', str(lastdict.get_int('mid')))
        last.set_attribute('mtype', str(lastdict.get_int('mtype')))
        last.set_attribute('cid', str(lastdict.get_int('cid')))
        last.set_attribute('ctype', str(lastdict.get_int('ctype')))
        last.set_attribute('sid', str(lastdict.get_int('sid')))

        # Result stars
        result_star = Node.void('result_star')
        root.add_child(result_star)
        result_stars = profile.get_int_array('result_stars', 9)
        for i in range(9):
            result_star.set_attribute(f'slot{i + 1}', str(result_stars[i]))

        # Target stuff
        target = Node.void('target')
        root.add_child(target)
        target.set_attribute('flag', str(profile.get_int('target_flag')))
        target.set_attribute('setnum', str(profile.get_int('target_setnum')))

        # Groove gauge level-ups
        gr_s = Node.void('gr_s')
        root.add_child(gr_s)
        index = 1
        for entry in profile.get_int_array('gr_s', 5):
            gr_s.set_attribute(f'gr{index}', str(entry))
            index = index + 1

        gr_d = Node.void('gr_d')
        root.add_child(gr_d)
        index = 1
        for entry in profile.get_int_array('gr_d', 5):
            gr_d.set_attribute(f'gr{index}', str(entry))
            index = index + 1

        # Options in menus
        root.add_child(Node.s16_array('opt', profile.get_int_array('opt', 16)))
        root.add_child(Node.s16_array('opt_ex', profile.get_int_array('opt_ex', 16)))

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

        # Ranking display?
        root.add_child(Node.u16_array('rank', profile.get_int_array('rank', 100)))

        # Rivals
        links = self.data.local.user.get_links(self.game, self.version, userid)
        for link in links:
            if link.type[:7] != 'friend_':
                continue

            pos = int(link.type[7:])
            friend = self.get_profile(link.other_userid)
            play_stats = self.get_play_statistics(link.other_userid)
            if friend is not None:
                friendnode = Node.void('friend')
                root.add_child(friendnode)
                friendnode.set_attribute('pos', str(pos))
                friendnode.set_attribute('vs', '0')
                friendnode.set_attribute('up', '0')
                friendnode.add_child(Node.u32('code', friend.get_int('extid')))
                friendnode.add_child(Node.string('name', friend.get_str('name')))
                friendnode.add_child(Node.u8('area', friend.get_int('area', 51)))
                friendnode.add_child(Node.u32('exp', play_stats.get_int('exp')))
                friendnode.add_child(Node.u32('star', friend.get_int('star')))

                # Drill rankings
                if 'title' in friend:
                    title = Node.void('title')
                    friendnode.add_child(title)
                    titledict = friend.get_dict('title')
                    if 't' in titledict:
                        title.set_attribute('t', str(titledict.get_int('t')))
                    if 's' in titledict:
                        title.set_attribute('s', str(titledict.get_int('s')))
                    if 'd' in titledict:
                        title.set_attribute('d', str(titledict.get_int('d')))

                if 'title_gr' in friend:
                    title_gr = Node.void('title_gr')
                    friendnode.add_child(title_gr)
                    title_grdict = friend.get_dict('title_gr')
                    if 't' in title_grdict:
                        title_gr.set_attribute('t', str(title_grdict.get_int('t')))
                    if 's' in title_grdict:
                        title_gr.set_attribute('s', str(title_grdict.get_int('s')))
                    if 'd' in title_grdict:
                        title_gr.set_attribute('d', str(title_grdict.get_int('d')))

                # Groove gauge level-ups
                gr_s = Node.void('gr_s')
                friendnode.add_child(gr_s)
                index = 1
                for entry in friend.get_int_array('gr_s', 5):
                    gr_s.set_attribute(f'gr{index}', str(entry))
                    index = index + 1

                gr_d = Node.void('gr_d')
                friendnode.add_child(gr_d)
                index = 1
                for entry in friend.get_int_array('gr_d', 5):
                    gr_d.set_attribute(f'gr{index}', str(entry))
                    index = index + 1

        # Play area
        areas = profile.get_int_array('play_area', 55)
        play_area = Node.void('play_area')
        root.add_child(play_area)
        for i in range(len(areas)):
            play_area.set_attribute(f'play_cnt{i}', str(areas[i]))

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

        elif method == 'friend':
            refid = request.attribute('ref_id')
            root = Node.void('playerdata')

            # 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.set_attribute('status', str(Status.NO_PROFILE))
                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)

            for rival in links[:2]:
                rivalid = rival.other_userid
                rivalprofile = profiles[rivalid]
                scores = self.data.remote.music.get_scores(
                    self.game, self.music_version, rivalid)

                # First, output general profile info.
                friend = Node.void('friend')
                root.add_child(friend)

                # 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('open', 1))

                # Set up some sane defaults.
                friend.add_child(
                    Node.string('name', rivalprofile.get_str('name', 'なし')))
                friend.add_child(
                    Node.string('g_pm_id',
                                ID.format_extid(
                                    rivalprofile.get_int('extid'))))
                friend.add_child(
                    Node.s16('chara', rivalprofile.get_int('chara', -1)))

                # Set up player avatar.
                avatar_dict = rivalprofile.get_dict('avatar')
                friend.add_child(
                    Node.u8('hair', avatar_dict.get_int('hair', 0)))
                friend.add_child(
                    Node.u8('face', avatar_dict.get_int('face', 0)))
                friend.add_child(
                    Node.u8('body', avatar_dict.get_int('body', 0)))
                friend.add_child(
                    Node.u8('effect', avatar_dict.get_int('effect', 0)))
                friend.add_child(
                    Node.u8('object', avatar_dict.get_int('object', 0)))
                friend.add_child(
                    Node.u8_array('comment',
                                  avatar_dict.get_int_array('comment', 2)))

                # Perform hiscore/medal conversion.
                hiscore_array = [0] * int(
                    (((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8)
                clear_medal = [0] * self.GAME_MAX_MUSIC_ID
                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 + rivalprofile.get_int('extid') % 100
                    medal = {
                        self.PLAY_MEDAL_CIRCLE_FAILED:
                        self.GAME_PLAY_MEDAL_CIRCLE_FAILED,
                        self.PLAY_MEDAL_DIAMOND_FAILED:
                        self.GAME_PLAY_MEDAL_DIAMOND_FAILED,
                        self.PLAY_MEDAL_STAR_FAILED:
                        self.GAME_PLAY_MEDAL_STAR_FAILED,
                        self.PLAY_MEDAL_EASY_CLEAR: self.
                        GAME_PLAY_MEDAL_CIRCLE_CLEARED,  # Map approximately
                        self.PLAY_MEDAL_CIRCLE_CLEARED:
                        self.GAME_PLAY_MEDAL_CIRCLE_CLEARED,
                        self.PLAY_MEDAL_DIAMOND_CLEARED:
                        self.GAME_PLAY_MEDAL_DIAMOND_CLEARED,
                        self.PLAY_MEDAL_STAR_CLEARED:
                        self.GAME_PLAY_MEDAL_STAR_CLEARED,
                        self.PLAY_MEDAL_CIRCLE_FULL_COMBO:
                        self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO,
                        self.PLAY_MEDAL_DIAMOND_FULL_COMBO:
                        self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO,
                        self.PLAY_MEDAL_STAR_FULL_COMBO:
                        self.GAME_PLAY_MEDAL_STAR_FULL_COMBO,
                        self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT,
                    }[score.data.get_int('medal')]
                    medal_pos = {
                        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]
                    clear_medal[score.id] = clear_medal[score.id] | (
                        medal << (medal_pos * 4))

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

                hiscore = bytes(hiscore_array)
                friend.add_child(Node.u16_array('clear_medal', clear_medal))
                friend.add_child(Node.binary('hiscore', hiscore))

            return root

        # Invalid method
        return None
示例#23
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', 255))
        base.add_child(Node.u8('sheet', profile.get_int('sheet', 0)))
        base.add_child(Node.s8('tutorial', profile.get_int('tutorial', 0)))
        base.add_child(
            Node.s8('music_open_pt', profile.get_int('music_open_pt', 0)))
        base.add_child(Node.s8('is_conv', -1))
        base.add_child(Node.s32('option', profile.get_int('option', 0)))
        base.add_child(Node.s16('music', profile.get_int('music', -1)))
        base.add_child(Node.u16('ep', profile.get_int('ep', 0)))
        base.add_child(
            Node.s32_array('sp_color_flg',
                           profile.get_int_array('sp_color_flg', 2)))
        base.add_child(Node.s32('read_news', profile.get_int('read_news', 0)))
        base.add_child(
            Node.s16('consecutive_days_coupon',
                     profile.get_int('consecutive_days_coupon', 0)))
        base.add_child(Node.s8('staff', 0))
        # These are probably from an old event, but if they aren't present and defaulted,
        # then different songs show up in the Zoo event.
        base.add_child(
            Node.u16_array(
                'gitadora_point',
                profile.get_int_array('gitadora_point', 3,
                                      [2000, 2000, 2000])))
        base.add_child(
            Node.u8('gitadora_select', profile.get_int('gitadora_select', 2)))

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

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

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

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

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

            points = score.points
            medal = {
                self.PLAY_MEDAL_CIRCLE_FAILED:
                self.GAME_PLAY_MEDAL_CIRCLE_FAILED,
                self.PLAY_MEDAL_DIAMOND_FAILED:
                self.GAME_PLAY_MEDAL_DIAMOND_FAILED,
                self.PLAY_MEDAL_STAR_FAILED: self.GAME_PLAY_MEDAL_STAR_FAILED,
                self.PLAY_MEDAL_EASY_CLEAR:
                self.GAME_PLAY_MEDAL_CIRCLE_CLEARED,  # Map approximately
                self.PLAY_MEDAL_CIRCLE_CLEARED:
                self.GAME_PLAY_MEDAL_CIRCLE_CLEARED,
                self.PLAY_MEDAL_DIAMOND_CLEARED:
                self.GAME_PLAY_MEDAL_DIAMOND_CLEARED,
                self.PLAY_MEDAL_STAR_CLEARED:
                self.GAME_PLAY_MEDAL_STAR_CLEARED,
                self.PLAY_MEDAL_CIRCLE_FULL_COMBO:
                self.GAME_PLAY_MEDAL_CIRCLE_FULL_COMBO,
                self.PLAY_MEDAL_DIAMOND_FULL_COMBO:
                self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO,
                self.PLAY_MEDAL_STAR_FULL_COMBO:
                self.GAME_PLAY_MEDAL_STAR_FULL_COMBO,
                self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT,
            }[score.data.get_int('medal')]
            clear_medal[score.id] = clear_medal[score.id] | (medal <<
                                                             (score.chart * 4))

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

        hiscore = bytes(hiscore_array)

        base.add_child(Node.s16_array('my_best', most_played))
        base.add_child(Node.s16_array('latest_music', last_played))
        base.add_child(Node.u16_array('clear_medal', clear_medal))
        base.add_child(Node.u8_array('clear_medal_sub', clear_medal_sub))

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

        # Avatar section
        avatar_dict = profile.get_dict('avatar')
        avatar = Node.void('avatar')
        root.add_child(avatar)
        avatar.add_child(Node.u8('hair', avatar_dict.get_int('hair', 0)))
        avatar.add_child(Node.u8('face', avatar_dict.get_int('face', 0)))
        avatar.add_child(Node.u8('body', avatar_dict.get_int('body', 0)))
        avatar.add_child(Node.u8('effect', avatar_dict.get_int('effect', 0)))
        avatar.add_child(Node.u8('object', avatar_dict.get_int('object', 0)))
        avatar.add_child(
            Node.u8_array('comment', avatar_dict.get_int_array('comment', 2)))
        avatar.add_child(
            Node.s32_array('get_hair',
                           avatar_dict.get_int_array('get_hair', 2)))
        avatar.add_child(
            Node.s32_array('get_face',
                           avatar_dict.get_int_array('get_face', 2)))
        avatar.add_child(
            Node.s32_array('get_body',
                           avatar_dict.get_int_array('get_body', 2)))
        avatar.add_child(
            Node.s32_array('get_effect',
                           avatar_dict.get_int_array('get_effect', 2)))
        avatar.add_child(
            Node.s32_array('get_object',
                           avatar_dict.get_int_array('get_object', 2)))
        avatar.add_child(
            Node.s32_array('get_comment_over',
                           avatar_dict.get_int_array('get_comment_over', 3)))
        avatar.add_child(
            Node.s32_array('get_comment_under',
                           avatar_dict.get_int_array('get_comment_under', 3)))

        # Avatar add section
        avatar_add_dict = profile.get_dict('avatar_add')
        avatar_add = Node.void('avatar_add')
        root.add_child(avatar_add)
        avatar_add.add_child(
            Node.s32_array('get_hair',
                           avatar_add_dict.get_int_array('get_hair', 2)))
        avatar_add.add_child(
            Node.s32_array('get_face',
                           avatar_add_dict.get_int_array('get_face', 2)))
        avatar_add.add_child(
            Node.s32_array('get_body',
                           avatar_add_dict.get_int_array('get_body', 2)))
        avatar_add.add_child(
            Node.s32_array('get_effect',
                           avatar_add_dict.get_int_array('get_effect', 2)))
        avatar_add.add_child(
            Node.s32_array('get_object',
                           avatar_add_dict.get_int_array('get_object', 2)))
        avatar_add.add_child(
            Node.s32_array(
                'get_comment_over',
                avatar_add_dict.get_int_array('get_comment_over', 2)))
        avatar_add.add_child(
            Node.s32_array(
                'get_comment_under',
                avatar_add_dict.get_int_array('get_comment_under', 2)))
        avatar_add.add_child(
            Node.s32_array('new_hair',
                           avatar_add_dict.get_int_array('new_hair', 2)))
        avatar_add.add_child(
            Node.s32_array('new_face',
                           avatar_add_dict.get_int_array('new_face', 2)))
        avatar_add.add_child(
            Node.s32_array('new_body',
                           avatar_add_dict.get_int_array('new_body', 2)))
        avatar_add.add_child(
            Node.s32_array('new_effect',
                           avatar_add_dict.get_int_array('new_effect', 2)))
        avatar_add.add_child(
            Node.s32_array('new_object',
                           avatar_add_dict.get_int_array('new_object', 2)))
        avatar_add.add_child(
            Node.s32_array(
                'new_comment_over',
                avatar_add_dict.get_int_array('new_comment_over', 2)))
        avatar_add.add_child(
            Node.s32_array(
                'new_comment_under',
                avatar_add_dict.get_int_array('new_comment_under', 2)))

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

        sp_data = Node.void('sp_data')
        root.add_child(sp_data)
        sp_data.add_child(Node.s32('sp', profile.get_int('sp', 0)))

        gakuen = Node.void('gakuen_data')
        root.add_child(gakuen)
        gakuen.add_child(Node.s32('music_list', -1))

        saucer = Node.void('flying_saucer')
        root.add_child(saucer)
        saucer.add_child(Node.s32('music_list', -1))
        saucer.add_child(Node.s32('tune_count', -1))
        saucer.add_child(Node.u32('clear_norma', 0))
        saucer.add_child(Node.u32('clear_norma_add', 0))

        zoo_dict = profile.get_dict('zoo')
        zoo = Node.void('zoo')
        root.add_child(zoo)
        zoo.add_child(
            Node.u16_array('point', zoo_dict.get_int_array('point', 5)))
        zoo.add_child(
            Node.s32_array('music_list',
                           zoo_dict.get_int_array('music_list', 2)))
        zoo.add_child(
            Node.s8_array('today_play_flag',
                          zoo_dict.get_int_array('today_play_flag', 4)))

        triple = Node.void('triple_journey')
        root.add_child(triple)
        triple.add_child(Node.s32('music_list', -1))
        triple.add_child(
            Node.s32_array('boss_damage', [65534, 65534, 65534, 65534]))
        triple.add_child(Node.s32_array('boss_stun', [0, 0, 0, 0]))
        triple.add_child(Node.s32('magic_gauge', 0))
        triple.add_child(Node.s32('today_party', 0))
        triple.add_child(Node.bool('union_magic', False))
        triple.add_child(Node.float('base_attack_rate', 1.0))
        triple.add_child(Node.s32('iidx_play_num', 0))
        triple.add_child(Node.s32('reflec_play_num', 0))
        triple.add_child(Node.s32('voltex_play_num', 0))
        triple.add_child(Node.bool('iidx_play_flg', True))
        triple.add_child(Node.bool('reflec_play_flg', True))
        triple.add_child(Node.bool('voltex_play_flg', True))

        ios = Node.void('ios')
        root.add_child(ios)
        ios.add_child(Node.s32('continueRightAnswer', 30))
        ios.add_child(Node.s32('totalRightAnswer', 30))

        kac2013 = Node.void('kac2013')
        root.add_child(kac2013)
        kac2013.add_child(Node.s8('music_num', 0))
        kac2013.add_child(Node.s16('music', 0))
        kac2013.add_child(Node.u8('sheet', 0))

        baseball = Node.void('baseball_data')
        root.add_child(baseball)
        baseball.add_child(Node.s64('music_list', -1))

        for id in [3, 5, 7]:
            node = Node.void('floor_infection')
            root.add_child(node)
            node.add_child(Node.s32('infection_id', id))
            node.add_child(Node.s32('music_list', -1))

        return root