from pathlib import Path from itertools import chain from PyCriCodecs import AWB, ACB, HCA from .extracter import Extracter, add_extracter class Chuni(Extracter): def __init__(self, *, data_dir, opt_dir, **kwargs): super().__init__(**kwargs) self.data_dir = Path(data_dir) self.opt_dir = Path(opt_dir) self.out_dir /= 'chuni' def process_image(self, file: Path, out: Path, media_type): out.parent.mkdir(parents=True, exist_ok=True) self.ffmpeg(['-i', file], media_type, out) print(file) def process_music(self, file: Path, out: Path): out.parent.mkdir(parents=True, exist_ok=True) awb = AWB(str(file)) # omnimix hca's cannot be decoded by PyCriCodecs self.ffmpeg(['-i', self.vgmstream(next(awb.getfiles()))], 'music', out) print(file) def process_audio(self, hca: bytes, out: Path): out.parent.mkdir(parents=True, exist_ok=True) self.ffmpeg(['-i', self.vgmstream(hca)], 'audio', out) print(out) def extract_images(self): for folder_name, output_folder in ( ('avatarAccessory', 'avatar'), ('ddsImage', 'character'), ('mapIcon', 'map-icon'), ('namePlate', 'name-plate'), ('systemVoice', 'system-voice-icon') ): for file in chain(self.data_dir.glob(f'A000/{folder_name}/*/*.dds'), self.opt_dir.glob(f'*/{folder_name}/*/*.dds')): yield self.process_image, file, self.out_dir / output_folder / file.name, 'images' texture = self.data_dir / 'surfboard' / 'texture' yield self.process_image, texture / 'CHU_UI_Common_Avatar_body_00.dds', self.out_dir / 'avatar' / 'CHU_UI_Common_Avatar_body_00.dds', 'images' yield self.process_image, texture / 'CHU_UI_Common_Avatar_face_00.dds', self.out_dir / 'avatar' / 'CHU_UI_Common_Avatar_face_00.dds', 'images' yield self.process_image, texture / 'CHU_UI_title_rank_00_v10.dds', self.out_dir / 'trophy' / 'CHU_UI_title_rank_00_v10.dds', 'images' def extract_jacket(self): for file in chain(self.data_dir.glob('A000/music/*/*.dds'), self.opt_dir.glob('*/music/*/*.dds')): yield self.process_image, file, self.out_dir / 'jacket' / file.name, 'jackets' def extract_music(self): for file in chain(self.data_dir.glob('A000/cueFile/*/music*.awb'), self.opt_dir.glob('*/cueFile/*/music*.awb')): yield self.process_music, file, self.out_dir / 'music' / file.name def extract_audio(self): for file in chain(self.data_dir.glob('A000/cueFile/*/systemvoice*.acb'), self.opt_dir.glob('*/cueFile/*/systemvoice*.acb')): acb = ACB(str(file)) names = self.acb_filenames(acb) for i, data in enumerate(acb.awb.getfiles()): yield self.process_audio, data, self.out_dir / 'system-voice' / f'{file.stem}_{names[0][i][1]}' @staticmethod def register(parser): parser.add_argument('--data-dir', help='data directory (containing A000)', required=True) parser.add_argument('--opt-dir', help='opt directory (containing A001, etc.)', required=True) add_extracter(Chuni)