From 4de3e8dca071ba396d3fcf29e62e6dbbf6825af6 Mon Sep 17 00:00:00 2001 From: alexion Date: Mon, 7 Sep 2026 14:01:50 -0400 Subject: [PATCH] fix(clew): extract full archives by default --- src/clew/README.md | 3 ++- src/clew/clew.py | 41 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/clew/README.md b/src/clew/README.md index 86414ad..aaa5a3e 100644 --- a/src/clew/README.md +++ b/src/clew/README.md @@ -12,7 +12,8 @@ bazel run //src/clew -- \ ``` The first version copies loose `.rpy` files and collects compiled script files. -It extracts `.rpy`, `.rpyc`, and `.rpymc` files from `.rpa` archives. +It extracts all files from `.rpa` archives. +Use `--suffix` to limit archive extraction to selected file suffixes. It writes output under `source/`, `work/`, and `logs/`. Use `--force` to replace those Clew-managed directories. diff --git a/src/clew/clew.py b/src/clew/clew.py index bc6fde3..47126e4 100644 --- a/src/clew/clew.py +++ b/src/clew/clew.py @@ -11,7 +11,6 @@ from rpycdec.rpa import extract_rpa LOGGER = logging.getLogger("clew") MANAGED_DIRS = ("source", "work", "logs") -SCRIPT_SUFFIXES = (".rpy", ".rpyc", ".rpymc") def parse_args(argv: list[str]) -> argparse.Namespace: @@ -36,6 +35,11 @@ def parse_args(argv: list[str]) -> argparse.Namespace: action="store_true", help="Replace Clew-managed output directories.", ) + parser.add_argument( + "--suffix", + action="append", + help="Extract only archive paths with this suffix.", + ) return parser.parse_args(argv) @@ -131,19 +135,45 @@ def archive_destination_prefix(archive_relative: Path) -> Path: return parent -def extract_archive(archive: Path, game_root: Path, source_dir: Path, work_dir: Path) -> None: +def normalize_suffixes(suffixes: list[str] | None) -> list[str] | None: + if suffixes is None: + return None + + normalized = [] + for suffix in suffixes: + if not suffix: + fail("suffix must not be empty") + if not suffix.startswith("."): + suffix = f".{suffix}" + normalized.append(suffix) + return normalized + + +def extract_archive( + archive: Path, + game_root: Path, + source_dir: Path, + work_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.mkdir(parents=True, exist_ok=True) LOGGER.info("extracting archive: %s", archive_relative) with archive.open("rb") as handle: - extract_rpa(handle, output_dir=str(output_dir), suffixes=list(SCRIPT_SUFFIXES)) + 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) + copy_source_file( + source_file, + output_dir, + source_dir, + "archived", + destination_prefix, + ) def configure_logging(logs_dir: Path) -> None: @@ -166,6 +196,7 @@ def run(args: argparse.Namespace) -> None: if not game_root.is_dir(): 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) configure_logging(logs_dir) @@ -180,7 +211,7 @@ def run(args: argparse.Namespace) -> None: LOGGER.info("scanning archives") for archive in sorted(game_root.rglob("*.rpa")): - extract_archive(archive, game_root, source_dir, work_dir) + extract_archive(archive, game_root, source_dir, work_dir, suffixes) LOGGER.info("done")