fix(clew): log completed AST extractions
This commit is contained in:
@@ -8,11 +8,13 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from rpycdec.rpa import extract_rpa
|
from rpycdec.rpa import extract_rpa
|
||||||
from src.clew import extract_renpy_ast as dump_ast_script
|
from src.clew import extract_renpy_ast as extract_renpy_ast_script
|
||||||
|
|
||||||
|
EXTRACTOR = Path(extract_renpy_ast_script.__file__).resolve()
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
def parse_args(argv: list[str]) -> argparse.Namespace:
|
def parse_args(argv: list[str]) -> argparse.Namespace:
|
||||||
@@ -97,16 +99,19 @@ def find(root: Path, predicate: Callable[[Path], bool]) -> list[Path]:
|
|||||||
matches.append(file_path)
|
matches.append(file_path)
|
||||||
return sorted(matches)
|
return sorted(matches)
|
||||||
|
|
||||||
|
def is_game_root(path: Path) -> bool:
|
||||||
|
return (
|
||||||
|
(path / "game").is_dir()
|
||||||
|
and (path / "renpy").is_dir()
|
||||||
|
and (path / "lib" / "linux-x86_64" / "python").is_file())
|
||||||
|
|
||||||
def ast_dump_output_path(output_root: Path, extracted_root: Path, game_root: Path, script: Path) -> Path:
|
|
||||||
namespace = game_root.relative_to(extracted_root)
|
def is_renpy_script(path: Path) -> bool:
|
||||||
relative_script = script.relative_to(game_root)
|
return path.is_file() and path.suffix.lower() in {".rpyc", ".rpymc"}
|
||||||
return output_root / "ast" / namespace / relative_script.with_suffix(relative_script.suffix + ".json")
|
|
||||||
|
|
||||||
|
|
||||||
def run_extract_renpy_ast(
|
def run_extract_renpy_ast(
|
||||||
bundled_python: Path,
|
bundled_python: Path,
|
||||||
extractor: Path,
|
|
||||||
game_root: Path,
|
game_root: Path,
|
||||||
compiled_script: Path,
|
compiled_script: Path,
|
||||||
output: Path,
|
output: Path,
|
||||||
@@ -115,7 +120,7 @@ def run_extract_renpy_ast(
|
|||||||
command = [
|
command = [
|
||||||
"steam-run",
|
"steam-run",
|
||||||
str(bundled_python),
|
str(bundled_python),
|
||||||
str(extractor),
|
str(EXTRACTOR),
|
||||||
"--game-root",
|
"--game-root",
|
||||||
str(game_root),
|
str(game_root),
|
||||||
"--input",
|
"--input",
|
||||||
@@ -126,34 +131,42 @@ def run_extract_renpy_ast(
|
|||||||
subprocess.run(command, check=True)
|
subprocess.run(command, check=True)
|
||||||
|
|
||||||
|
|
||||||
def extract_game_ast(output_root: Path, extracted_root: Path, overwrite: bool) -> None:
|
def extract_game_ast(extracted_root: Path, ast_root: Path, overwrite: bool) -> None:
|
||||||
extractor = Path(dump_ast_script.__file__).resolve()
|
game_roots = find(extracted_root, is_game_root)
|
||||||
game_roots = find(
|
|
||||||
extracted_root,
|
|
||||||
lambda path: (
|
|
||||||
(path / "game").is_dir()
|
|
||||||
and (path / "renpy").is_dir()
|
|
||||||
and (path / "lib" / "linux-x86_64" / "python").is_file()
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if not game_roots:
|
if not game_roots:
|
||||||
LOG.warning("no Ren'Py game roots found under %s", extracted_root)
|
LOG.warning(f"no Ren'Py game roots found under {extracted_root}")
|
||||||
return
|
return
|
||||||
|
|
||||||
for game_root in game_roots:
|
tasks = [
|
||||||
bundled_python = game_root / "lib" / "linux-x86_64" / "python"
|
(game_root, script_path)
|
||||||
scripts = find(
|
for game_root in game_roots
|
||||||
game_root / "game",
|
for script_path in find(game_root / "game", is_renpy_script)
|
||||||
lambda path: path.is_file() and path.suffix.lower() in {".rpyc", ".rpymc"},
|
]
|
||||||
)
|
|
||||||
LOG.info("found %d compiled Ren'Py scripts under %s", len(scripts), game_root)
|
with ThreadPoolExecutor() as executor:
|
||||||
for script in scripts:
|
futures = {}
|
||||||
output = ast_dump_output_path(output_root, extracted_root, game_root, script)
|
for game_root, script_path in tasks:
|
||||||
if output.exists() and not overwrite:
|
output_path = (
|
||||||
LOG.info("skipping existing AST dump: %s", output)
|
ast_root
|
||||||
|
/ game_root.relative_to(extracted_root)
|
||||||
|
/ script_path.relative_to(game_root).with_suffix(script_path.suffix + ".json")
|
||||||
|
)
|
||||||
|
if output_path.exists() and not overwrite:
|
||||||
|
LOG.info(f"skipping existing AST extraction: {output_path}")
|
||||||
continue
|
continue
|
||||||
LOG.info("dumping AST: %s", script)
|
|
||||||
run_extract_renpy_ast(bundled_python, extractor, game_root, script, output)
|
future = executor.submit(
|
||||||
|
run_extract_renpy_ast,
|
||||||
|
game_root / "lib" / "linux-x86_64" / "python",
|
||||||
|
game_root,
|
||||||
|
script_path,
|
||||||
|
output_path,
|
||||||
|
)
|
||||||
|
futures[future] = script_path
|
||||||
|
|
||||||
|
for future in as_completed(futures):
|
||||||
|
future.result()
|
||||||
|
LOG.info(f"finished AST extraction: {futures[future]}")
|
||||||
|
|
||||||
|
|
||||||
def run(args: argparse.Namespace) -> None:
|
def run(args: argparse.Namespace) -> None:
|
||||||
@@ -168,7 +181,8 @@ def run(args: argparse.Namespace) -> None:
|
|||||||
extract_game_files(game_root, extracted)
|
extract_game_files(game_root, extracted)
|
||||||
|
|
||||||
if not args.no_extract_renpy_ast:
|
if not args.no_extract_renpy_ast:
|
||||||
extract_game_ast(output_root, extracted, args.overwrite)
|
ast = get_output_directory(output_root, "ast")
|
||||||
|
extract_game_ast(extracted, ast, args.overwrite)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user