From 16ca280959758fa7ca47051505f5dd226f855deb Mon Sep 17 00:00:00 2001 From: alexion Date: Mon, 7 Sep 2026 14:26:38 -0400 Subject: [PATCH] refactor(clew): recreate extracted game tree --- src/clew/clew.py | 103 +++++++++++------------------------------------ 1 file changed, 24 insertions(+), 79 deletions(-) diff --git a/src/clew/clew.py b/src/clew/clew.py index 9a7d4cd..5833a32 100644 --- a/src/clew/clew.py +++ b/src/clew/clew.py @@ -10,7 +10,7 @@ from pathlib import Path from rpycdec.rpa import extract_rpa LOGGER = logging.getLogger("clew") -MANAGED_DIRS = ("source", "work", "logs") +MANAGED_DIRS = ("extracted", "logs") def parse_args(argv: list[str]) -> argparse.Namespace: @@ -28,7 +28,7 @@ def parse_args(argv: list[str]) -> argparse.Namespace: "--output-root", type=Path, default=Path("output"), - help="Preparation output root. Defaults to repository output/.", + help="Output root. Defaults to repository output/.", ) parser.add_argument( "--force", @@ -58,76 +58,28 @@ def resolve_output_root(output_root: Path) -> Path: return output_root.resolve() -def prepare_output(output_root: Path, force: bool) -> tuple[Path, Path, Path]: +def prepare_output(output_root: Path, force: bool) -> tuple[Path, Path]: for name in MANAGED_DIRS: path = output_root / name if path.exists() and not force: - fail(f"refusing to overwrite existing preparation path: {path}") + fail(f"refusing to overwrite existing output path: {path}") if force: for name in MANAGED_DIRS: shutil.rmtree(output_root / name, ignore_errors=True) - source_dir = output_root / "source" - work_dir = output_root / "work" + extracted_dir = output_root / "extracted" logs_dir = output_root / "logs" - for path in (source_dir, work_dir, logs_dir): + for path in (extracted_dir, logs_dir): path.mkdir(parents=True, exist_ok=True) - return source_dir, work_dir, logs_dir + return extracted_dir, logs_dir def relative_to(root: Path, path: Path) -> Path: return path.resolve().relative_to(root.resolve()) -def is_renpy_runtime_path(relative: Path) -> bool: - parts = relative.parts - return bool(parts) and (parts[0] == "renpy" or "renpy" in parts[:-1]) - - -def copy_source_file( - source_file: Path, - input_root: Path, - source_dir: Path, - label: str, - destination_prefix: Path = Path(), -) -> None: - relative = relative_to(input_root, source_file) - output_relative = destination_prefix / relative - if is_renpy_runtime_path(relative): - LOGGER.info("skipped %s Ren'Py runtime source: %s", label, output_relative) - return - - destination = source_dir / output_relative - destination.parent.mkdir(parents=True, exist_ok=True) - shutil.copy2(source_file, destination) - LOGGER.info("copied %s source: %s", label, output_relative) - - -def copy_compiled_file(compiled_file: Path, input_root: Path, work_dir: Path) -> None: - relative = relative_to(input_root, compiled_file) - if is_renpy_runtime_path(relative): - LOGGER.info("skipped Ren'Py runtime compiled script: %s", relative) - return - - destination = work_dir / "compiled" / relative - destination.parent.mkdir(parents=True, exist_ok=True) - shutil.copy2(compiled_file, destination) - LOGGER.info("copied compiled script: %s", relative) - - -def archive_work_path(work_dir: Path, archive_relative: Path) -> Path: - return work_dir / "archives" / archive_relative.with_suffix("") - - -def archive_destination_prefix(archive_relative: Path) -> Path: - parent = archive_relative.parent - if str(parent) == ".": - return Path() - return parent - - def normalize_suffixes(suffixes: list[str] | None) -> list[str] | None: if suffixes is None: return None @@ -142,15 +94,22 @@ def normalize_suffixes(suffixes: list[str] | None) -> list[str] | None: return normalized +def copy_loose_file(file: Path, game_root: Path, extracted_dir: Path) -> None: + relative = relative_to(game_root, file) + destination = extracted_dir / relative + destination.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(file, destination) + LOGGER.info("copied loose file: %s", relative) + + def extract_archive( archive: Path, game_root: Path, - source_dir: Path, - work_dir: Path, + extracted_dir: Path, suffixes: list[str] | None, ) -> None: archive_relative = relative_to(game_root, archive) - output_dir = archive_work_path(work_dir, archive_relative) + output_dir = extracted_dir / archive_relative.with_suffix("") output_dir.mkdir(parents=True, exist_ok=True) LOGGER.info("extracting archive: %s", archive_relative) @@ -158,16 +117,6 @@ def extract_archive( extract_rpa(handle, output_dir=str(output_dir), suffixes=suffixes) LOGGER.info("extracted archive: %s", archive_relative) - destination_prefix = archive_destination_prefix(archive_relative) - for source_file in sorted(output_dir.rglob("*.rpy")): - copy_source_file( - source_file, - output_dir, - source_dir, - "archived", - destination_prefix, - ) - def configure_logging(logs_dir: Path) -> None: log_file = logs_dir / "clew.log" @@ -190,21 +139,17 @@ def run(args: argparse.Namespace) -> None: fail(f"game root is not a directory: {game_root}") suffixes = normalize_suffixes(args.suffix) - source_dir, work_dir, logs_dir = prepare_output(output_root, args.force) + extracted_dir, logs_dir = prepare_output(output_root, args.force) configure_logging(logs_dir) - LOGGER.info("scanning loose source files") - for source_file in sorted(game_root.rglob("*.rpy")): - copy_source_file(source_file, game_root, source_dir, "loose") + LOGGER.info("copying loose files") + for file in sorted(game_root.rglob("*")): + if file.is_file() and file.suffix.lower() != ".rpa": + copy_loose_file(file, game_root, extracted_dir) - LOGGER.info("scanning loose compiled scripts") - for suffix in ("*.rpyc", "*.rpymc"): - for compiled_file in sorted(game_root.rglob(suffix)): - copy_compiled_file(compiled_file, game_root, work_dir) - - LOGGER.info("scanning archives") + LOGGER.info("extracting archives") for archive in sorted(game_root.rglob("*.rpa")): - extract_archive(archive, game_root, source_dir, work_dir, suffixes) + extract_archive(archive, game_root, extracted_dir, suffixes) LOGGER.info("done")