diff --git a/titles/mai2/read.py b/titles/mai2/read.py index 1c86518..d9ffbca 100644 --- a/titles/mai2/read.py +++ b/titles/mai2/read.py @@ -53,6 +53,10 @@ class Mai2Reader(BaseReader): self.logger.error(f"tables directory not found in {self.bin_dir}") return + if not os.path.exists(f"{self.opt_dir}/tables"): + self.logger.warning(f"tables directory not found in {self.opt_dir}, not using") + self.opt_dir = None + if self.version >= Mai2Constants.VER_MAIMAI_MILK: if self.extra is None: self.logger.error("Milk - Finale requre an AES key via a hex string send as the --extra flag") @@ -63,41 +67,54 @@ class Mai2Reader(BaseReader): else: key = None - evt_table = self.load_table_raw(f"{self.bin_dir}/tables", "mmEvent.bin", key) - txt_table = self.load_table_raw(f"{self.bin_dir}/tables", "mmtextout_jp.bin", key) - score_table = self.load_table_raw(f"{self.bin_dir}/tables", "mmScore.bin", key) + evt_table = self.load_table_raw(f"{self.bin_dir}/tables", "mmEvent.bin", key, f"{self.opt_dir}/tables" if self.opt_dir else None) + jp_table = self.load_table_raw(f"{self.bin_dir}/tables", "mmtextout_jp.bin", key, f"{self.opt_dir}/tables" if self.opt_dir else None) + #en_table = self.load_table_raw(f"{self.bin_dir}/tables", "mmtextout_ex.bin", key, f"{self.opt_dir}/tables" if self.opt_dir else None) + en_table = [] + score_table = self.load_table_raw(f"{self.bin_dir}/tables", "mmScore.bin", key, f"{self.opt_dir}/tables" if self.opt_dir else None) + music_table = self.load_table_raw(f"{self.bin_dir}/tables", "mmMusic.bin", key, f"{self.opt_dir}/tables" if self.opt_dir else None) + genre_table = self.load_table_raw(f"{self.bin_dir}/tables", "mmGenre.bin", key, f"{self.opt_dir}/tables" if self.opt_dir else None) + + txt_table = {} # Build our translation table + for entry in jp_table: + vals = list(entry.values()) + txt_table[vals[0]] = ", ".join(vals[1:]) + for entry in en_table: + vals = list(entry.values()) + txt_table[vals[0]] = ", ".join(vals[1:]) + genre_lookup = {} + for entry in genre_table: + genre_lookup[entry['ID']] = txt_table[entry['名前テキスト']] + + self.logger.info(f"Insert {len(evt_table)} events") await self.read_old_events(evt_table) - await self.read_old_music(score_table, txt_table) - - if self.opt_dir is not None: - evt_table = self.load_table_raw(f"{self.opt_dir}/tables", "mmEvent.bin", key) - txt_table = self.load_table_raw(f"{self.opt_dir}/tables", "mmtextout_jp.bin", key) - score_table = self.load_table_raw(f"{self.opt_dir}/tables", "mmScore.bin", key) - - await self.read_old_events(evt_table) - await self.read_old_music(score_table, txt_table) - - return + self.logger.info(f"Insert {len(score_table)} charts") + await self.read_old_music(music_table, score_table, txt_table, genre_lookup) - def load_table_raw(self, dir: str, file: str, key: Optional[bytes]) -> Optional[List[Dict[str, str]]]: - if not os.path.exists(f"{dir}/{file}"): + def load_table_raw(self, dir: str, file: str, key: Optional[bytes], opt_dir: Optional[str] = None) -> Optional[List[Dict[str, str]]]: + if opt_dir is not None and os.path.exists(f"{opt_dir}/{file}"): + fpath = f"{opt_dir}/{file}" + else: + fpath = f"{dir}/{file}" + + if not os.path.exists(fpath): self.logger.warning(f"file {file} does not exist in directory {dir}, skipping") return - self.logger.info(f"Load table {file} from {dir}") + self.logger.info(f"Load table {fpath}") if key is not None: cipher = AES.new(key, AES.MODE_CBC) - with open(f"{dir}/{file}", "rb") as f: + with open(fpath, "rb") as f: f_encrypted = f.read() f_data = cipher.decrypt(f_encrypted)[0x10:] else: - with open(f"{dir}/{file}", "rb") as f: + with open(fpath, "rb") as f: f_data = f.read()[0x10:] if f_data is None or not f_data: - self.logger.warning(f"file {dir} could not be read, skipping") + self.logger.warning(f"file {fpath} could not be read, skipping") return f_data_deflate = zlib.decompress(f_data, wbits = zlib.MAX_WBITS | 16)[0x12:] # lop off the junk at the beginning @@ -325,20 +342,37 @@ class Mai2Reader(BaseReader): for event in events: evt_id = int(event.get('イベントID', '0')) - evt_expire_time = float(event.get('オフ時強制時期', '0.0')) - is_exp = bool(int(event.get('海外許可', '0'))) - is_aou = bool(int(event.get('AOU許可', '0'))) name = event.get('comment', f'evt_{evt_id}') await self.data.static.put_game_event(self.version, 0, evt_id, name) - - if not (is_exp or is_aou): - await self.data.static.toggle_game_event(self.version, evt_id, False) - async def read_old_music(self, scores: Optional[List[Dict[str, str]]], text: Optional[List[Dict[str, str]]]) -> None: - if scores is None or text is None: + async def read_old_music(self, music: Optional[List[Dict[str, str]]], scores: Optional[List[Dict[str, str]]], text: Optional[Dict[str, str]], genre: Dict[str, str]) -> None: + if music is None or scores is None or text is None: return - # TODO + + last_music = music[0] + for score in scores: + mid = score['ID'][:-2] + cid = score['ID'][-2:] + + if last_music['ID'] != mid: + for x in range(len(music)): + if music[x]['ID'] == mid: + last_music = music[x] + break + + await self.data.static.put_game_music( + self.version, + int(mid), + int(cid), + text[last_music['タイトル']], + text[last_music['アーティスト']], + genre[last_music['GenreID']], + last_music['BPM'], + last_music['Ver'], + float(score['LV']), + text[f"RST_SCORECREATOR_{int(score['譜面作者ID']):04d}"] + ) async def read_opt_info(self, directory: str) -> Optional[int]: datacfg_file = os.path.join(directory, "DataConfig.xml")