mirror of
https://github.com/DragonMinded/bemaniutils.git
synced 2026-08-28 05:14:11 -05:00
Combine module into single file to follow project pattern
This commit is contained in:
@@ -2,14 +2,15 @@
|
||||
import base64
|
||||
import hashlib
|
||||
import html
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
import logging
|
||||
import math
|
||||
|
||||
from enum import IntEnum
|
||||
from typing import Dict, Optional, Tuple
|
||||
from typing_extensions import Final
|
||||
from ctypes import *
|
||||
|
||||
from bemani.backend.ddr.base import DDRBase
|
||||
from bemani.backend.ddr.ddrsn2.eventinfo import EventInfo
|
||||
from bemani.backend.ddr.ddrsn2.playerinfo import PlayerInfo
|
||||
from bemani.backend.ddr.ddrsn2.scoreinfo import ScoreInfo
|
||||
from bemani.backend.ddr.stubs import DDRSuperNova
|
||||
from bemani.backend.ddr.common import (
|
||||
DDRGameFriendHandler,
|
||||
@@ -27,10 +28,352 @@ from bemani.backend.ddr.common import (
|
||||
DDRGameShopHandler,
|
||||
DDRGameTraceHandler,
|
||||
)
|
||||
from bemani.common import Time, VersionConstants, Profile, intish
|
||||
from bemani.common import VersionConstants, Profile, PlayStatistics, DBConstants, intish
|
||||
from bemani.data import Score, UserID
|
||||
from bemani.protocol import Node
|
||||
from bemani.backend.base import Status
|
||||
|
||||
|
||||
GAME_RANK_AAA = 0
|
||||
GAME_RANK_AA = 4
|
||||
GAME_RANK_A = 8
|
||||
GAME_RANK_B = 13
|
||||
GAME_RANK_C = 16
|
||||
GAME_RANK_D = 20
|
||||
GAME_RANK_E = 24
|
||||
GAME_RANK_NONE = 0xFF
|
||||
|
||||
|
||||
class ScoreRankRecordStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("lo_score", c_int8),
|
||||
("score", c_int16),
|
||||
("rank", c_int8, 5),
|
||||
("unk1", c_int8, 1),
|
||||
("yfc", c_int8, 1),
|
||||
("ofc", c_int8, 1),
|
||||
]
|
||||
|
||||
|
||||
class ScoreRecordStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("beginner", ScoreRankRecordStruct),
|
||||
("basic", ScoreRankRecordStruct),
|
||||
("difficult", ScoreRankRecordStruct),
|
||||
("expert", ScoreRankRecordStruct),
|
||||
("challenge", ScoreRankRecordStruct),
|
||||
("double_basic", ScoreRankRecordStruct),
|
||||
("double_difficult", ScoreRankRecordStruct),
|
||||
("double_expert", ScoreRankRecordStruct),
|
||||
("double_challenge", ScoreRankRecordStruct),
|
||||
]
|
||||
|
||||
|
||||
class ScoreRecord:
|
||||
@staticmethod
|
||||
def blank() -> ScoreRecordStruct:
|
||||
record = ScoreRecordStruct()
|
||||
|
||||
record.beginner.rank = GAME_RANK_NONE
|
||||
record.basic.rank = GAME_RANK_NONE
|
||||
record.difficult.rank = GAME_RANK_NONE
|
||||
record.expert.rank = GAME_RANK_NONE
|
||||
record.challenge.rank = GAME_RANK_NONE
|
||||
|
||||
record.double_basic.rank = GAME_RANK_NONE
|
||||
record.double_difficult.rank = GAME_RANK_NONE
|
||||
record.double_expert.rank = GAME_RANK_NONE
|
||||
record.double_challenge.rank = GAME_RANK_NONE
|
||||
|
||||
return record
|
||||
|
||||
@staticmethod
|
||||
def create(scores: Optional[dict[int, Score]]) -> ScoreRecordStruct:
|
||||
record = ScoreRecord.blank()
|
||||
|
||||
if scores is not None:
|
||||
for chart in scores:
|
||||
if chart == 0:
|
||||
chart_rec = record.beginner
|
||||
elif chart == 1:
|
||||
chart_rec = record.basic
|
||||
elif chart == 2:
|
||||
chart_rec = record.difficult
|
||||
elif chart == 3:
|
||||
chart_rec = record.expert
|
||||
else:
|
||||
chart_rec = record.challenge
|
||||
|
||||
score = scores[chart]
|
||||
|
||||
formatted_score = math.floor(score.points / 10)
|
||||
hi_score = formatted_score >> 8
|
||||
|
||||
lo_score = formatted_score ^ (hi_score << 8)
|
||||
|
||||
chart_rec.lo_score = lo_score
|
||||
chart_rec.score = hi_score
|
||||
|
||||
rank = score.data.get_int("rank")
|
||||
if rank == DBConstants.DDR_RANK_AAA:
|
||||
chart_rec.rank = GAME_RANK_AAA
|
||||
elif rank == DBConstants.DDR_RANK_AA:
|
||||
chart_rec.rank = GAME_RANK_AA
|
||||
elif rank == DBConstants.DDR_RANK_A:
|
||||
chart_rec.rank = GAME_RANK_A
|
||||
elif rank == DBConstants.DDR_RANK_B:
|
||||
chart_rec.rank = GAME_RANK_B
|
||||
elif rank == DBConstants.DDR_RANK_C:
|
||||
chart_rec.rank = GAME_RANK_C
|
||||
elif rank == DBConstants.DDR_RANK_D:
|
||||
chart_rec.rank = GAME_RANK_D
|
||||
else:
|
||||
chart_rec.rank = GAME_RANK_E
|
||||
|
||||
halo = score.data.get_int("halo")
|
||||
if halo == DBConstants.DDR_HALO_PERFECT_FULL_COMBO:
|
||||
chart_rec.yfc = 1
|
||||
chart_rec.ofc = 1
|
||||
elif halo == DBConstants.DDR_HALO_GREAT_FULL_COMBO:
|
||||
chart_rec.yfc = 1
|
||||
|
||||
return record
|
||||
|
||||
|
||||
class BattleRecordStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("name", c_char * 14),
|
||||
("wins", c_uint16),
|
||||
("loses", c_uint16),
|
||||
("draws", c_uint16),
|
||||
]
|
||||
|
||||
|
||||
class BattleRecord:
|
||||
@staticmethod
|
||||
def create(name: str, wins: int, losses: int, draws: int) -> BattleRecordStruct:
|
||||
this_name = name
|
||||
if len(this_name) > 8:
|
||||
this_name = this_name[:8]
|
||||
logging.warning("name {} too long, truncating to {}", name, this_name)
|
||||
elif len(this_name) < 8:
|
||||
logging.warning("name too short, padding with spaces")
|
||||
this_name = this_name.ljust(8, " ")
|
||||
|
||||
return BattleRecordStruct(this_name.encode("ascii"), wins, losses, draws)
|
||||
|
||||
|
||||
class PlayerInfoStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("count", c_uint16), # 0x00
|
||||
("area", c_uint16), # 0x02
|
||||
("title", c_uint16), # 0x04
|
||||
("flag", c_uint16), # 0x06
|
||||
("id", c_uint32), # 0x08
|
||||
("exp", c_uint32), # 0x0c
|
||||
("weight", c_uint16), # 0x10
|
||||
("last_cate", c_uint8), # 0x12
|
||||
("last_mode", c_uint8), # 0x13
|
||||
("last_type", c_uint8), # 0x14
|
||||
("last_sort", c_uint8), # 0x15
|
||||
("last_music", c_uint8), # 0x16
|
||||
("_unused1", c_char * 6), # 0x17 - 0x1b
|
||||
("team", c_uint8), # 0x1c
|
||||
("_unused2", c_char * 8), # 0x1d - 0x24
|
||||
("takeover", c_uint8), # 0x25
|
||||
("count_b", c_uint8), # 0x26
|
||||
("_unused3", c_char * 2), # 0x27 - 0x29
|
||||
("groove_radar", c_uint16 * 5), # 0x2a
|
||||
("options", c_uint8 * 32), # 0x34
|
||||
("name", c_char * 14), # 0x54
|
||||
("_unused4", c_char * 2), # 0x62 - 0x63
|
||||
# Bit is set to 1 = already displayed, 0 = not yet displayed
|
||||
("unlock_prompt_bits", c_uint8 * 12), # 0x64 - 0x70
|
||||
("_unused5", c_char * 20), # 0x70 - 0x83
|
||||
("course", c_uint32 * 3), # 0x84
|
||||
("_unused6", c_char * (0x1344 - 0x90)),
|
||||
("battle_records", BattleRecordStruct * 5), # 0x1344
|
||||
]
|
||||
|
||||
|
||||
class PlayerInfo:
|
||||
@staticmethod
|
||||
def create(play_stats: PlayStatistics, profile: Profile, machine_region: int) -> PlayerInfoStruct:
|
||||
player = PlayerInfoStruct()
|
||||
|
||||
player.count = play_stats.get_int("single_plays")
|
||||
player.area = profile.get_int("area", machine_region)
|
||||
player.title = profile.get_int("title")
|
||||
player.flag = profile.get_int("flag")
|
||||
player.id = profile.extid
|
||||
player.exp = play_stats.get_int("exp")
|
||||
player.weight = profile.get_int("weight")
|
||||
|
||||
lastdict = profile.get_dict("last")
|
||||
player.last_cate = lastdict.get_int("cate")
|
||||
player.last_mode = lastdict.get_int("mode")
|
||||
player.last_type = lastdict.get_int("type")
|
||||
player.last_sort = lastdict.get_int("sort")
|
||||
player.last_music = lastdict.get_int("music")
|
||||
|
||||
player.team = profile.get_int("team")
|
||||
|
||||
player.takeover = profile.get_int("takeover")
|
||||
player.count_b = play_stats.get_int("battle_plays")
|
||||
|
||||
idx = 0
|
||||
for entry in profile.get_int_array("gr_s", 5):
|
||||
player.groove_radar[idx] = entry
|
||||
idx += 1
|
||||
|
||||
idx = 0
|
||||
|
||||
if "opt" not in profile:
|
||||
profile.replace_int_array("opt", 16, [2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 2, 0, 0, 0, 0])
|
||||
|
||||
for entry in profile.get_int_array("opt", 16):
|
||||
player.options[idx] = entry
|
||||
idx += 1
|
||||
|
||||
player.name = (profile.get_str("name").ljust(8, " ")[:8]).encode("ascii")
|
||||
|
||||
# Default, unlock everything?
|
||||
for i in range(len(player.unlock_prompt_bits)):
|
||||
player.unlock_prompt_bits[i] = 0xFF
|
||||
|
||||
idx = 0
|
||||
for entry in profile.get_int_array("course", 3):
|
||||
player.course[idx] = entry
|
||||
idx += 1
|
||||
|
||||
return player
|
||||
|
||||
|
||||
class ScoreInfoStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("records", ScoreRecordStruct * 200),
|
||||
]
|
||||
|
||||
|
||||
class ScoreInfo:
|
||||
@staticmethod
|
||||
def create(scores: list[Score], part: int) -> ScoreInfoStruct:
|
||||
info = ScoreInfoStruct()
|
||||
|
||||
scores_by_song_id: dict[int, dict[int, Score]] = {}
|
||||
|
||||
for score in scores:
|
||||
if score.id not in scores_by_song_id:
|
||||
scores_by_song_id[score.id] = {}
|
||||
|
||||
scores_by_song_id[score.id][score.chart] = score
|
||||
|
||||
for i in range(len(info.records)):
|
||||
if part == 2:
|
||||
song_id = i + 200
|
||||
else:
|
||||
song_id = i
|
||||
|
||||
if song_id in scores_by_song_id:
|
||||
score_record = ScoreRecord.create(scores_by_song_id[song_id])
|
||||
else:
|
||||
score_record = ScoreRecord.create(None)
|
||||
|
||||
info.records[i] = score_record
|
||||
|
||||
return info
|
||||
|
||||
|
||||
class GlobalRankingStateEntryStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("id", c_int8),
|
||||
("state", c_int8), # 0 = disabled, 1 = English ("Global Ranking"), 2 = Japanese ("Gachinko Dance Matsuri")
|
||||
("_unused", c_int8 * 2),
|
||||
]
|
||||
|
||||
|
||||
class GlobalRankingStateEntry:
|
||||
@staticmethod
|
||||
def create(id: int, state: int) -> GlobalRankingStateEntryStruct:
|
||||
return GlobalRankingStateEntryStruct(id, state)
|
||||
|
||||
|
||||
class ZukinTeams(IntEnum):
|
||||
GREEN = 1
|
||||
RED = 2
|
||||
YELLOW = 3
|
||||
|
||||
|
||||
class UnlockEntryStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("id", c_uint8),
|
||||
("enabled", c_uint8),
|
||||
# Set to 1 to enable. This is only read if the other flags check out. Set all other flags to 0 to force unlock.
|
||||
("flag1", c_uint8), # Must be less than some value passed to check function (always 0x3d6???)
|
||||
("flag2", c_uint8), # Some kind of "unlock type" flag? If 0 then other flags aren't checked.
|
||||
("flag3", c_uint8), # Unused??
|
||||
("flag4", c_uint8),
|
||||
# If 0, flag2 (if flag2 is non-0) must match the type of unlock looked for by the checker function. If non-0, flag4 * 10 must be <= param4 of checker function (when is this used?)
|
||||
]
|
||||
|
||||
|
||||
class UnlockEntry:
|
||||
@staticmethod
|
||||
def create(id: int, enabled: int, flag1: int, flag2: int, flag3: int, flag4: int) -> UnlockEntryStruct:
|
||||
return UnlockEntryStruct(id, enabled, flag1, flag2, flag3, flag4)
|
||||
|
||||
|
||||
class EventInfoStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("unlocks", UnlockEntryStruct * 256),
|
||||
("global_ranking_state", GlobalRankingStateEntryStruct * 8),
|
||||
("_unused", c_uint8 * 4), # Unused?
|
||||
("event_ver", c_uint8), # 0x624
|
||||
("event_end", c_uint8), # 0x625
|
||||
("event_sel_team", c_uint8), # 0x626
|
||||
("event_rankings", c_uint8 * 3), # 0x627
|
||||
("event_avg", c_uint16), # 0x62a
|
||||
("event_score_green", c_uint32), # 0x62c
|
||||
("event_score_red", c_uint32), # 0x62c
|
||||
("event_score_yellow", c_uint32), # 0x62c
|
||||
("event_border", c_uint16), # 0x638
|
||||
]
|
||||
|
||||
|
||||
class EventInfo:
|
||||
@staticmethod
|
||||
def create() -> EventInfoStruct:
|
||||
p = EventInfoStruct()
|
||||
p.event_ver = 5 # Event episode
|
||||
p.event_end = 1 # Event has ended
|
||||
p.event_sel_team = ZukinTeams.RED # Winning team
|
||||
p.event_rankings[0] = ZukinTeams.RED
|
||||
p.event_rankings[1] = ZukinTeams.GREEN
|
||||
p.event_rankings[2] = ZukinTeams.YELLOW
|
||||
p.event_avg = 123 # ?
|
||||
p.event_score_green = 57312
|
||||
p.event_score_red = 3516541
|
||||
p.event_score_yellow = 5631
|
||||
p.event_border = 123
|
||||
|
||||
for i in range(len(p.unlocks)):
|
||||
p.unlocks[i].id = i & 0xFF
|
||||
p.unlocks[i].enabled = 1
|
||||
|
||||
for i in range(0, len(p.global_ranking_state)):
|
||||
p.global_ranking_state[i].id = i & 0xFF
|
||||
p.global_ranking_state[i].state = 2
|
||||
|
||||
p.unlocks[0].enabled = 7 # Phase unlock max
|
||||
|
||||
return p
|
||||
|
||||
|
||||
class DDRSuperNova2(
|
||||
@@ -1,3 +0,0 @@
|
||||
from bemani.backend.ddr.ddrsn2.ddrsn2 import DDRSuperNova2
|
||||
|
||||
__all__ = ["DDRSuperNova2"]
|
||||
@@ -1,26 +0,0 @@
|
||||
import logging
|
||||
from ctypes import *
|
||||
|
||||
|
||||
class BattleRecordStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("name", c_char * 14),
|
||||
("wins", c_uint16),
|
||||
("loses", c_uint16),
|
||||
("draws", c_uint16),
|
||||
]
|
||||
|
||||
|
||||
class BattleRecord:
|
||||
@staticmethod
|
||||
def create(name: str, wins: int, losses: int, draws: int) -> BattleRecordStruct:
|
||||
this_name = name
|
||||
if len(this_name) > 8:
|
||||
this_name = this_name[:8]
|
||||
logging.warning("name {} too long, truncating to {}", name, this_name)
|
||||
elif len(this_name) < 8:
|
||||
logging.warning("name too short, padding with spaces")
|
||||
this_name = this_name.ljust(8, " ")
|
||||
|
||||
return BattleRecordStruct(this_name.encode("ascii"), wins, losses, draws)
|
||||
@@ -1,52 +0,0 @@
|
||||
from ctypes import *
|
||||
|
||||
from bemani.backend.ddr.ddrsn2.globalrankingstateentry import GlobalRankingStateEntryStruct
|
||||
from bemani.backend.ddr.ddrsn2.unlockentry import UnlockEntryStruct
|
||||
from bemani.backend.ddr.ddrsn2.zukinteams import ZukinTeams
|
||||
|
||||
|
||||
class EventInfoStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("unlocks", UnlockEntryStruct * 256),
|
||||
("global_ranking_state", GlobalRankingStateEntryStruct * 8),
|
||||
("_unused", c_uint8 * 4), # Unused?
|
||||
("event_ver", c_uint8), # 0x624
|
||||
("event_end", c_uint8), # 0x625
|
||||
("event_sel_team", c_uint8), # 0x626
|
||||
("event_rankings", c_uint8 * 3), # 0x627
|
||||
("event_avg", c_uint16), # 0x62a
|
||||
("event_score_green", c_uint32), # 0x62c
|
||||
("event_score_red", c_uint32), # 0x62c
|
||||
("event_score_yellow", c_uint32), # 0x62c
|
||||
("event_border", c_uint16), # 0x638
|
||||
]
|
||||
|
||||
|
||||
class EventInfo:
|
||||
@staticmethod
|
||||
def create() -> EventInfoStruct:
|
||||
p = EventInfoStruct()
|
||||
p.event_ver = 5 # Event episode
|
||||
p.event_end = 1 # Event has ended
|
||||
p.event_sel_team = ZukinTeams.RED # Winning team
|
||||
p.event_rankings[0] = ZukinTeams.RED
|
||||
p.event_rankings[1] = ZukinTeams.GREEN
|
||||
p.event_rankings[2] = ZukinTeams.YELLOW
|
||||
p.event_avg = 123 # ?
|
||||
p.event_score_green = 57312
|
||||
p.event_score_red = 3516541
|
||||
p.event_score_yellow = 5631
|
||||
p.event_border = 123
|
||||
|
||||
for i in range(len(p.unlocks)):
|
||||
p.unlocks[i].id = i & 0xFF
|
||||
p.unlocks[i].enabled = 1
|
||||
|
||||
for i in range(0, len(p.global_ranking_state)):
|
||||
p.global_ranking_state[i].id = i & 0xFF
|
||||
p.global_ranking_state[i].state = 2
|
||||
|
||||
p.unlocks[0].enabled = 7 # Phase unlock max
|
||||
|
||||
return p
|
||||
@@ -1,16 +0,0 @@
|
||||
from ctypes import *
|
||||
|
||||
|
||||
class GlobalRankingStateEntryStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("id", c_int8),
|
||||
("state", c_int8), # 0 = disabled, 1 = English ("Global Ranking"), 2 = Japanese ("Gachinko Dance Matsuri")
|
||||
("_unused", c_int8 * 2),
|
||||
]
|
||||
|
||||
|
||||
class GlobalRankingStateEntry:
|
||||
@staticmethod
|
||||
def create(id: int, state: int) -> GlobalRankingStateEntryStruct:
|
||||
return GlobalRankingStateEntryStruct(id, state)
|
||||
@@ -1,91 +0,0 @@
|
||||
from ctypes import *
|
||||
|
||||
from bemani.backend.ddr.ddrsn2.battlerecord import BattleRecordStruct
|
||||
from bemani.common import Profile, PlayStatistics
|
||||
|
||||
|
||||
class PlayerInfoStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("count", c_uint16), # 0x00
|
||||
("area", c_uint16), # 0x02
|
||||
("title", c_uint16), # 0x04
|
||||
("flag", c_uint16), # 0x06
|
||||
("id", c_uint32), # 0x08
|
||||
("exp", c_uint32), # 0x0c
|
||||
("weight", c_uint16), # 0x10
|
||||
("last_cate", c_uint8), # 0x12
|
||||
("last_mode", c_uint8), # 0x13
|
||||
("last_type", c_uint8), # 0x14
|
||||
("last_sort", c_uint8), # 0x15
|
||||
("last_music", c_uint8), # 0x16
|
||||
("_unused1", c_char * 6), # 0x17 - 0x1b
|
||||
("team", c_uint8), # 0x1c
|
||||
("_unused2", c_char * 8), # 0x1d - 0x24
|
||||
("takeover", c_uint8), # 0x25
|
||||
("count_b", c_uint8), # 0x26
|
||||
("_unused3", c_char * 2), # 0x27 - 0x29
|
||||
("groove_radar", c_uint16 * 5), # 0x2a
|
||||
("options", c_uint8 * 32), # 0x34
|
||||
("name", c_char * 14), # 0x54
|
||||
("_unused4", c_char * 2), # 0x62 - 0x63
|
||||
# Bit is set to 1 = already displayed, 0 = not yet displayed
|
||||
("unlock_prompt_bits", c_uint8 * 12), # 0x64 - 0x70
|
||||
("_unused5", c_char * 20), # 0x70 - 0x83
|
||||
("course", c_uint32 * 3), # 0x84
|
||||
("_unused6", c_char * (0x1344 - 0x90)),
|
||||
("battle_records", BattleRecordStruct * 5), # 0x1344
|
||||
]
|
||||
|
||||
|
||||
class PlayerInfo:
|
||||
@staticmethod
|
||||
def create(play_stats: PlayStatistics, profile: Profile, machine_region: int) -> PlayerInfoStruct:
|
||||
player = PlayerInfoStruct()
|
||||
|
||||
player.count = play_stats.get_int("single_plays")
|
||||
player.area = profile.get_int("area", machine_region)
|
||||
player.title = profile.get_int("title")
|
||||
player.flag = profile.get_int("flag")
|
||||
player.id = profile.extid
|
||||
player.exp = play_stats.get_int("exp")
|
||||
player.weight = profile.get_int("weight")
|
||||
|
||||
lastdict = profile.get_dict("last")
|
||||
player.last_cate = lastdict.get_int("cate")
|
||||
player.last_mode = lastdict.get_int("mode")
|
||||
player.last_type = lastdict.get_int("type")
|
||||
player.last_sort = lastdict.get_int("sort")
|
||||
player.last_music = lastdict.get_int("music")
|
||||
|
||||
player.team = profile.get_int("team")
|
||||
|
||||
player.takeover = profile.get_int("takeover")
|
||||
player.count_b = play_stats.get_int("battle_plays")
|
||||
|
||||
idx = 0
|
||||
for entry in profile.get_int_array("gr_s", 5):
|
||||
player.groove_radar[idx] = entry
|
||||
idx += 1
|
||||
|
||||
idx = 0
|
||||
|
||||
if "opt" not in profile:
|
||||
profile.replace_int_array("opt", 16, [2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 2, 0, 0, 0, 0])
|
||||
|
||||
for entry in profile.get_int_array("opt", 16):
|
||||
player.options[idx] = entry
|
||||
idx += 1
|
||||
|
||||
player.name = (profile.get_str("name").ljust(8, " ")[:8]).encode("ascii")
|
||||
|
||||
# Default, unlock everything?
|
||||
for i in range(len(player.unlock_prompt_bits)):
|
||||
player.unlock_prompt_bits[i] = 0xFF
|
||||
|
||||
idx = 0
|
||||
for entry in profile.get_int_array("course", 3):
|
||||
player.course[idx] = entry
|
||||
idx += 1
|
||||
|
||||
return player
|
||||
@@ -1,40 +0,0 @@
|
||||
from ctypes import *
|
||||
|
||||
from bemani.backend.ddr.ddrsn2.scorerecord import ScoreRecordStruct, ScoreRecord
|
||||
from bemani.data import Score
|
||||
|
||||
|
||||
class ScoreInfoStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("records", ScoreRecordStruct * 200),
|
||||
]
|
||||
|
||||
|
||||
class ScoreInfo:
|
||||
@staticmethod
|
||||
def create(scores: list[Score], part: int) -> ScoreInfoStruct:
|
||||
info = ScoreInfoStruct()
|
||||
|
||||
scores_by_song_id: dict[int, dict[int, Score]] = {}
|
||||
|
||||
for score in scores:
|
||||
if score.id not in scores_by_song_id:
|
||||
scores_by_song_id[score.id] = {}
|
||||
|
||||
scores_by_song_id[score.id][score.chart] = score
|
||||
|
||||
for i in range(len(info.records)):
|
||||
if part == 2:
|
||||
song_id = i + 200
|
||||
else:
|
||||
song_id = i
|
||||
|
||||
if song_id in scores_by_song_id:
|
||||
score_record = ScoreRecord.create(scores_by_song_id[song_id])
|
||||
else:
|
||||
score_record = ScoreRecord.create(None)
|
||||
|
||||
info.records[i] = score_record
|
||||
|
||||
return info
|
||||
@@ -1,115 +0,0 @@
|
||||
import math
|
||||
|
||||
from ctypes import *
|
||||
from typing import Optional
|
||||
|
||||
from bemani.common import DBConstants
|
||||
from bemani.data import Score
|
||||
|
||||
|
||||
class ScoreRankRecordStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("lo_score", c_int8),
|
||||
("score", c_int16),
|
||||
("rank", c_int8, 5),
|
||||
("unk1", c_int8, 1),
|
||||
("yfc", c_int8, 1),
|
||||
("ofc", c_int8, 1),
|
||||
]
|
||||
|
||||
|
||||
class ScoreRecordStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("beginner", ScoreRankRecordStruct),
|
||||
("basic", ScoreRankRecordStruct),
|
||||
("difficult", ScoreRankRecordStruct),
|
||||
("expert", ScoreRankRecordStruct),
|
||||
("challenge", ScoreRankRecordStruct),
|
||||
("double_basic", ScoreRankRecordStruct),
|
||||
("double_difficult", ScoreRankRecordStruct),
|
||||
("double_expert", ScoreRankRecordStruct),
|
||||
("double_challenge", ScoreRankRecordStruct),
|
||||
]
|
||||
|
||||
|
||||
GAME_RANK_AAA = 0
|
||||
GAME_RANK_AA = 4
|
||||
GAME_RANK_A = 8
|
||||
GAME_RANK_B = 13
|
||||
GAME_RANK_C = 16
|
||||
GAME_RANK_D = 20
|
||||
GAME_RANK_E = 24
|
||||
GAME_RANK_NONE = 0xFF
|
||||
|
||||
|
||||
class ScoreRecord:
|
||||
@staticmethod
|
||||
def blank() -> ScoreRecordStruct:
|
||||
record = ScoreRecordStruct()
|
||||
|
||||
record.beginner.rank = GAME_RANK_NONE
|
||||
record.basic.rank = GAME_RANK_NONE
|
||||
record.difficult.rank = GAME_RANK_NONE
|
||||
record.expert.rank = GAME_RANK_NONE
|
||||
record.challenge.rank = GAME_RANK_NONE
|
||||
|
||||
record.double_basic.rank = GAME_RANK_NONE
|
||||
record.double_difficult.rank = GAME_RANK_NONE
|
||||
record.double_expert.rank = GAME_RANK_NONE
|
||||
record.double_challenge.rank = GAME_RANK_NONE
|
||||
|
||||
return record
|
||||
|
||||
@staticmethod
|
||||
def create(scores: Optional[dict[int, Score]]) -> ScoreRecordStruct:
|
||||
record = ScoreRecord.blank()
|
||||
|
||||
if scores is not None:
|
||||
for chart in scores:
|
||||
if chart == 0:
|
||||
chart_rec = record.beginner
|
||||
elif chart == 1:
|
||||
chart_rec = record.basic
|
||||
elif chart == 2:
|
||||
chart_rec = record.difficult
|
||||
elif chart == 3:
|
||||
chart_rec = record.expert
|
||||
else:
|
||||
chart_rec = record.challenge
|
||||
|
||||
score = scores[chart]
|
||||
|
||||
formatted_score = math.floor(score.points / 10)
|
||||
hi_score = formatted_score >> 8
|
||||
|
||||
lo_score = formatted_score ^ (hi_score << 8)
|
||||
|
||||
chart_rec.lo_score = lo_score
|
||||
chart_rec.score = hi_score
|
||||
|
||||
rank = score.data.get_int("rank")
|
||||
if rank == DBConstants.DDR_RANK_AAA:
|
||||
chart_rec.rank = GAME_RANK_AAA
|
||||
elif rank == DBConstants.DDR_RANK_AA:
|
||||
chart_rec.rank = GAME_RANK_AA
|
||||
elif rank == DBConstants.DDR_RANK_A:
|
||||
chart_rec.rank = GAME_RANK_A
|
||||
elif rank == DBConstants.DDR_RANK_B:
|
||||
chart_rec.rank = GAME_RANK_B
|
||||
elif rank == DBConstants.DDR_RANK_C:
|
||||
chart_rec.rank = GAME_RANK_C
|
||||
elif rank == DBConstants.DDR_RANK_D:
|
||||
chart_rec.rank = GAME_RANK_D
|
||||
else:
|
||||
chart_rec.rank = GAME_RANK_E
|
||||
|
||||
halo = score.data.get_int("halo")
|
||||
if halo == DBConstants.DDR_HALO_PERFECT_FULL_COMBO:
|
||||
chart_rec.yfc = 1
|
||||
chart_rec.ofc = 1
|
||||
elif halo == DBConstants.DDR_HALO_GREAT_FULL_COMBO:
|
||||
chart_rec.yfc = 1
|
||||
|
||||
return record
|
||||
@@ -1,21 +0,0 @@
|
||||
from ctypes import *
|
||||
|
||||
|
||||
class UnlockEntryStruct(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("id", c_uint8),
|
||||
("enabled", c_uint8),
|
||||
# Set to 1 to enable. This is only read if the other flags check out. Set all other flags to 0 to force unlock.
|
||||
("flag1", c_uint8), # Must be less than some value passed to check function (always 0x3d6???)
|
||||
("flag2", c_uint8), # Some kind of "unlock type" flag? If 0 then other flags aren't checked.
|
||||
("flag3", c_uint8), # Unused??
|
||||
("flag4", c_uint8),
|
||||
# If 0, flag2 (if flag2 is non-0) must match the type of unlock looked for by the checker function. If non-0, flag4 * 10 must be <= param4 of checker function (when is this used?)
|
||||
]
|
||||
|
||||
|
||||
class UnlockEntry:
|
||||
@staticmethod
|
||||
def create(id: int, enabled: int, flag1: int, flag2: int, flag3: int, flag4: int) -> UnlockEntryStruct:
|
||||
return UnlockEntryStruct(id, enabled, flag1, flag2, flag3, flag4)
|
||||
@@ -1,7 +0,0 @@
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class ZukinTeams(IntEnum):
|
||||
GREEN = 1
|
||||
RED = 2
|
||||
YELLOW = 3
|
||||
Reference in New Issue
Block a user