2023-10-11 00:14:17 +08:00

168 lines
5.1 KiB
Python

import argparse
import logging
import re
import sys
import zipfile
from pathlib import Path, PurePath
from tqdm import tqdm
from common import ArcaeaApkParser, ExtractTask, extract
ROOT_OUTPUT_PATH = Path(sys.argv[0]).parent.absolute()
logger = logging.getLogger(__name__)
parser = argparse.ArgumentParser(
prog="arcaea-apk-extract for Andreal",
description="Literal meaning.",
epilog="This program is licensed under the WTFPL. Feel free to fuck anything.",
)
parser.add_argument("apk_file")
parser.add_argument(
"-op",
"--output-path",
action="store",
help="output path, defaults to Path(sys.argv[0]).parent",
)
parser.add_argument(
"-v",
"--verbose",
action="store_true",
help="set logging level to logging.DEBUG",
)
class SongParser(ArcaeaApkParser):
@property
def OUTPUT_PATH(self):
return ROOT_OUTPUT_PATH / "Song"
def parse(self):
songs_dir_zf_path = zipfile.Path(self.zf) / "assets" / "songs"
songs_zf_path = [zfp for zfp in songs_dir_zf_path.iterdir() if zfp.is_dir()]
tasks = []
for song_zf_path in songs_zf_path:
for file_zf_path in song_zf_path.iterdir():
if not file_zf_path.is_file():
continue
file_pure_path = PurePath(str(file_zf_path))
if file_pure_path.suffix not in [".jpg", ".png"]:
continue
song_id = re.sub(r"^dl_", "", song_zf_path.name)
if file_pure_path.stem in ["base", "1080_base"]:
new_file_name_stem = song_id
elif file_pure_path.stem in ["1080_base_night", "base_night"]:
new_file_name_stem = f"{song_id}_night"
elif file_pure_path.stem in [
"0",
"1",
"2",
"3",
"1080_0",
"1080_1",
"1080_2",
"1080_3",
]:
new_file_name_stem = (
f"{song_id}_{file_pure_path.stem.replace('1080_', '')}"
)
else:
continue
new_file_name = f"{new_file_name_stem}{file_pure_path.suffix}"
new_file_path = self.OUTPUT_PATH / new_file_name
tasks.append(ExtractTask(file_zf_path, new_file_path))
return tasks
class CharParser(ArcaeaApkParser):
CHAR_RE = r"^\d+u?\.png$"
CHAR_ICON_RE = r"^\d+u?_icon\.png$"
@property
def OUTPUT_PATH_CHAR(self):
return ROOT_OUTPUT_PATH / "Char"
@property
def OUTPUT_PATH_ICON(self):
return ROOT_OUTPUT_PATH / "Icon"
def parse(self):
if (zipfile.Path(self.zf) / "assets" / "char" / "1080").exists():
char_dir_zf_path = zipfile.Path(self.zf) / "assets" / "char" / "1080"
else:
char_dir_zf_path = zipfile.Path(self.zf) / "assets" / "char"
char_zf_path_list = [zfp for zfp in char_dir_zf_path.iterdir() if zfp.is_file()]
char_icon_dir_zf_path = zipfile.Path(self.zf) / "assets" / "char"
char_icon_zf_path_list = [
zfp for zfp in char_icon_dir_zf_path.iterdir() if zfp.is_file()
]
tasks = []
for char_zf_path in char_zf_path_list:
char_pure_path = PurePath(str(char_zf_path))
if not re.match(self.CHAR_RE, char_pure_path.name):
continue
new_file_name = char_pure_path.name
new_file_path = self.OUTPUT_PATH_CHAR / new_file_name
tasks.append(ExtractTask(char_zf_path, new_file_path))
for char_icon_zf_path in char_icon_zf_path_list:
char_icon_pure_path = PurePath(str(char_icon_zf_path))
if not re.match(self.CHAR_ICON_RE, char_icon_pure_path.name):
continue
new_file_name = char_icon_pure_path.name.replace("_icon", "")
new_file_path = self.OUTPUT_PATH_ICON / new_file_name
tasks.append(ExtractTask(char_icon_zf_path, new_file_path))
return tasks
if __name__ == "__main__":
args = parser.parse_args(sys.argv[1:])
if args.output_path:
output_path = Path(args.output_path)
assert output_path.exists()
ROOT_OUTPUT_PATH = output_path
logging_level = logging.DEBUG if args.verbose else logging.INFO
logging.basicConfig(
level=logging_level,
style="{",
format="[{levelname}]{name}: {msg}",
stream=sys.stdout,
encoding="utf-8",
)
apk_path = Path(args.apk_file)
assert apk_path.exists()
apk_zf = zipfile.ZipFile(str(apk_path))
sp = SongParser(apk_zf)
cp = CharParser(apk_zf)
sp.OUTPUT_PATH.mkdir(parents=True, exist_ok=True)
cp.OUTPUT_PATH_CHAR.mkdir(parents=True, exist_ok=True)
cp.OUTPUT_PATH_ICON.mkdir(parents=True, exist_ok=True)
sp_tasks = sp.parse()
logger.info(f"{len(sp_tasks)} files from songs")
cp_tasks = cp.parse()
logger.info(f"{len(cp_tasks)} files from char")
tasks = sp_tasks + cp_tasks
logger.info("Extracting, please wait...")
for task in tqdm(tasks):
extract(task)