diff --git a/titles/ongeki/read.py b/titles/ongeki/read.py index ed3043f..594689e 100644 --- a/titles/ongeki/read.py +++ b/titles/ongeki/read.py @@ -58,12 +58,13 @@ class OngekiReader(BaseReader): data_dirs += self.get_data_directories(self.opt_dir) for dir in data_dirs: - await self.read_events(f"{dir}/event") - await self.read_music(f"{dir}/music") - await self.read_card(f"{dir}/card") - await self.read_reward(f"{dir}/reward") + this_opt_id = await self.read_opt_info(dir) + await self.read_events(f"{dir}/event", this_opt_id) + await self.read_music(f"{dir}/music", this_opt_id) + await self.read_card(f"{dir}/card", this_opt_id) + await self.read_reward(f"{dir}/reward", this_opt_id) - async def read_card(self, base_dir: str) -> None: + async def read_card(self, base_dir: str, opt_id: int = None) -> None: self.logger.info(f"Reading cards from {base_dir}...") for root, dirs, files in os.walk(base_dir): @@ -75,6 +76,7 @@ class OngekiReader(BaseReader): card_id = int(troot.find("Name").find("id").text) # skip already existing cards + # Hay1tsme 2025/04/08: What is this for, and why does it only check for BM cards? if ( await self.data.static.get_card( OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY, card_id @@ -108,6 +110,7 @@ class OngekiReader(BaseReader): await self.data.static.put_card( self.parse_version(troot), card_id, + opt_id, name=name, charaId=chara_id, nickName=nick_name, @@ -122,7 +125,7 @@ class OngekiReader(BaseReader): ) self.logger.info(f"Added card {card_id}") - async def read_events(self, base_dir: str) -> None: + async def read_events(self, base_dir: str, opt_id: int = None) -> None: self.logger.info(f"Reading events from {base_dir}...") for root, dirs, files in os.walk(base_dir): @@ -140,10 +143,10 @@ class OngekiReader(BaseReader): if troot.find("EventType").text == "MissionEvent": name = (troot.find("Event").find("MissionName").find("str").text) - await self.data.static.put_event(self.version, id, event_type, name) + await self.data.static.put_event(self.version, id, event_type, name, opt_id) self.logger.info(f"Added event {id}") - async def read_music(self, base_dir: str) -> None: + async def read_music(self, base_dir: str, opt_id: int = None) -> None: self.logger.info(f"Reading music from {base_dir}...") for root, dirs, files in os.walk(base_dir): @@ -178,11 +181,11 @@ class OngekiReader(BaseReader): ) await self.data.static.put_chart( - version, song_id, chart_id, title, artist, genre, level + version, song_id, chart_id, title, artist, genre, level, opt_id ) self.logger.info(f"Added song {song_id} chart {chart_id}") - async def read_reward(self, base_dir: str) -> None: + async def read_reward(self, base_dir: str, opt_id: int = None) -> None: self.logger.info(f"Reading rewards from {base_dir}...") for root, dirs, files in os.walk(base_dir): @@ -204,5 +207,53 @@ class OngekiReader(BaseReader): itemKind = OngekiConstants.REWARD_TYPES[troot.find("ItemType").text].value itemId = troot.find("RewardItem").find("ItemName").find("id").text - await self.data.static.put_reward(self.version, rewardId, rewardname, itemKind, itemId) + await self.data.static.put_reward(self.version, rewardId, rewardname, itemKind, itemId, opt_id) self.logger.info(f"Added reward {rewardId}") + + async def read_opt_info(self, directory: str) -> Optional[int]: + datacfg_file = os.path.join(directory, "DataConfig.xml") + if not os.path.exists(datacfg_file): + self.logger.warning(f"{datacfg_file} does not contain DataConfig.xml, opt info will not be read") + return None + + with open(datacfg_file, encoding="utf-8") as f: + troot = ET.fromstring(f.read()) + + if troot.find("DataConfig/version") is None: + self.logger.warning(f"{directory}/DataConfig.xml contains no Version section, opt info will not be read") + return None + + ver_maj = troot.find("DataConfig/version/major") + ver_min = troot.find("DataConfig/version/minor") + ver_rel = troot.find("DataConfig/version/release") + cm_maj = troot.find("DataConfig/cardMakerVersion/major") + cm_min = troot.find("DataConfig/cardMakerVersion/minor") + cm_rel = troot.find("DataConfig/cardMakerVersion/release") + + if ver_maj is None: # Probably not worth checking that the other sections exist + self.logger.warning(f"{datacfg_file} contains no major item in the Version section, opt info will not be read") + return None + + if ver_min is None: # Probably not worth checking that the other sections exist + self.logger.warning(f"{datacfg_file} contains no minor item in the Version section, opt info will not be read") + return None + + if ver_rel is None: # Probably not worth checking that the other sections exist + self.logger.warning(f"{datacfg_file} contains no release item in the Version section, opt info will not be read") + return None + + opt_folder = os.path.basename(os.path.normpath(directory)) + opt_id = await self.data.static.get_opt_by_version_folder(self.version, opt_folder) + + if not opt_id: + opt_id = await self.data.static.put_opt(self.version, opt_folder, int(ver_rel.text), int(cm_rel.text) if cm_rel else None) + if not opt_id: + self.logger.error(f"Failed to put opt folder info for {opt_folder}") + return None + else: + opt_id = opt_id['id'] + + self.logger.info( + f"Opt folder {opt_folder} (Database ID {opt_id}) contains v{ver_maj.text}.{ver_min.text}.{ver_rel.text} (cm v{cm_maj.text if cm_maj else 'None'}.{cm_min.text if cm_min else 'None'}.{cm_rel.text if cm_rel else 'None'})" + ) + return opt_id diff --git a/titles/ongeki/schema/static.py b/titles/ongeki/schema/static.py index 8609f5c..a784f67 100644 --- a/titles/ongeki/schema/static.py +++ b/titles/ongeki/schema/static.py @@ -5,6 +5,7 @@ from sqlalchemy.schema import ForeignKey from sqlalchemy.sql import func, select from sqlalchemy.engine import Row from sqlalchemy.dialects.mysql import insert +from sqlalchemy.sql.functions import coalesce from core.data.schema import BaseData, metadata from core.data.schema.arcade import machine @@ -212,10 +213,10 @@ game_point = Table( ) class OngekiStaticData(BaseData): - async def put_card(self, version: int, card_id: int, **card_data) -> Optional[int]: - sql = insert(cards).values(version=version, cardId=card_id, **card_data) + async def put_card(self, version: int, card_id: int, opt_id: int = None, **card_data) -> Optional[int]: + sql = insert(cards).values(version=version, cardId=card_id, opt=coalesce(cards.c.opt, opt_id), **card_data) - conflict = sql.on_duplicate_key_update(**card_data) + conflict = sql.on_duplicate_key_update(opt=coalesce(cards.c.opt, opt_id), **card_data) result = await self.execute(conflict) if result is None: @@ -342,7 +343,7 @@ class OngekiStaticData(BaseData): return result.fetchall() async def put_event( - self, version: int, event_id: int, event_type: int, event_name: str + self, version: int, event_id: int, event_type: int, event_name: str, opt_id: int = None ) -> Optional[int]: sql = insert(events).values( version=version, @@ -350,10 +351,11 @@ class OngekiStaticData(BaseData): type=event_type, name=event_name, endDate=f"2038-01-01 00:00:00", + opt=coalesce(events.c.opt, opt_id) ) conflict = sql.on_duplicate_key_update( - name=event_name, + name=event_name, opt=coalesce(events.c.opt, opt_id) ) result = await self.execute(conflict) @@ -399,6 +401,7 @@ class OngekiStaticData(BaseData): artist: str, genre: str, level: float, + opt_id: int = None ) -> Optional[int]: sql = insert(music).values( version=version, @@ -408,6 +411,7 @@ class OngekiStaticData(BaseData): artist=artist, genre=genre, level=level, + opt=coalesce(music.c.opt, opt_id) ) conflict = sql.on_duplicate_key_update( @@ -415,6 +419,7 @@ class OngekiStaticData(BaseData): artist=artist, genre=genre, level=level, + opt=coalesce(music.c.opt, opt_id) ) result = await self.execute(conflict) @@ -449,17 +454,21 @@ class OngekiStaticData(BaseData): return None return result.fetchone() - async def put_reward(self, version: int, rewardId: int, rewardname: str, itemKind: int, itemId: int) -> Optional[int]: + async def put_reward(self, version: int, rewardId: int, rewardname: str, itemKind: int, itemId: int, opt_id: int = None) -> Optional[int]: sql = insert(rewards).values( - version=version, - rewardId=rewardId, - rewardname=rewardname, - itemKind=itemKind, - itemId=itemId, - ) + version=version, + rewardId=rewardId, + rewardname=rewardname, + itemKind=itemKind, + itemId=itemId, + opt=coalesce(rewards.c.opt, opt_id) + ) + conflict = sql.on_duplicate_key_update( - rewardname=rewardname, - ) + rewardname=rewardname, + opt=coalesce(rewards.c.opt, opt_id) + ) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert reward! reward_id: {rewardId}")