diff --git a/AGENTS.md b/AGENTS.md index 80e7e25..bd8749d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -96,3 +96,5 @@ - In this monorepo, "dotfiles" refers to the dotfiles/NixOS configuration under `src/dotfiles/` and `configs/nixos/`. - The flake does not currently define `formatter.x86_64-linux`. Do not use `nix fmt` as a validation command until a formatter is added. +- Ariadne extraction must preserve the game-root season prefix for archived source. + Different game installs can contain compiled scripts with the same internal path. diff --git a/src/ariadne/README.md b/src/ariadne/README.md index 1d25d73..bbadaaa 100644 --- a/src/ariadne/README.md +++ b/src/ariadne/README.md @@ -18,6 +18,8 @@ The script defaults to `/src/ariadne/output`. This path stays stable when you run the command from inside `src/ariadne`. Use the game installation directory as the game root. One run processes all installed season directories. +Extracted archive source keeps the archive path relative to the game root, so files with the same +internal path from different season installs do not overwrite each other. If `rpycdec` is not available, the script uses `extract-tools.nix` to enter a Nix shell. Keep the script as a thin orchestration layer. diff --git a/src/ariadne/extract-and-decompile.sh b/src/ariadne/extract-and-decompile.sh index 0249baa..8aca368 100755 --- a/src/ariadne/extract-and-decompile.sh +++ b/src/ariadne/extract-and-decompile.sh @@ -68,7 +68,12 @@ say() { } game_root= -default_output_root=${BUILD_WORKSPACE_DIRECTORY:-$(pwd)}/src/ariadne/output +script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +if [[ -n "${BUILD_WORKSPACE_DIRECTORY:-}" ]]; then + default_output_root=$BUILD_WORKSPACE_DIRECTORY/src/ariadne/output +else + default_output_root=$script_dir/output +fi output_root=$default_output_root force=false original_args=("$@") @@ -149,20 +154,42 @@ is_renpy_runtime_path() { [[ "$relative" == renpy/* || "$relative" == */renpy/* ]] } +source_output_path() { + local relative=$1 + local destination_prefix=${2:-} + if [[ -n "$destination_prefix" ]]; then + printf '%s/%s/%s\n' "$source_dir" "$destination_prefix" "$relative" + else + printf '%s/%s\n' "$source_dir" "$relative" + fi +} + +source_log_relative() { + local relative=$1 + local destination_prefix=${2:-} + if [[ -n "$destination_prefix" ]]; then + printf '%s/%s\n' "$destination_prefix" "$relative" + else + printf '%s\n' "$relative" + fi +} + copy_source_file() { local input=$1 local input_root=$2 local label=$3 - local relative output + local destination_prefix=${4:-} + local relative output log_relative relative=$(realpath --relative-to="$input_root" "$input") + log_relative=$(source_log_relative "$relative" "$destination_prefix") if is_renpy_runtime_path "$relative"; then - log "skipped $label Ren'Py runtime source: $relative" + log "skipped $label Ren'Py runtime source: $log_relative" return fi - output=$source_dir/$relative + output=$(source_output_path "$relative" "$destination_prefix") mkdir -p -- "$(dirname -- "$output")" cp -- "$input" "$output" - record "copied $label source: $relative" + record "copied $label source: $log_relative" } copy_loose_source() { @@ -172,37 +199,43 @@ copy_loose_source() { decompile_compiled_script() { local input=$1 local input_root=$2 - local relative output_dir log_file + local destination_prefix=${3:-} + local relative output_dir log_file log_relative relative=$(realpath --relative-to="$input_root" "$input") + log_relative=$(source_log_relative "$relative" "$destination_prefix") if is_renpy_runtime_path "$relative"; then - log "skipped Ren'Py runtime compiled script: $relative" + log "skipped Ren'Py runtime compiled script: $log_relative" return fi - output_dir=$source_dir/$(dirname -- "$relative") - log_file=$logs_dir/decompile-${relative//\//__}.log + output_dir=$(source_output_path "$(dirname -- "$relative")" "$destination_prefix") + log_file=$logs_dir/decompile-${log_relative//\//__}.log mkdir -p -- "$output_dir" - say "decompiling script: $relative" + say "decompiling script: $log_relative" RPYCDEC_NO_WARNING=1 rpycdec decompile --output "$output_dir" "$input" > "$log_file" 2>&1 - log "decompiled script: $relative" + log "decompiled script: $log_relative" } extract_archive() { local archive=$1 - local relative archive_output log_file + local relative archive_output log_file destination_prefix relative=$(relative_to_game_root "$archive") archive_output=$work_dir/archives/${relative%.rpa} log_file=$logs_dir/unrpa-${relative//\//__}.log + destination_prefix=$(dirname -- "$relative") + if [[ "$destination_prefix" == "." ]]; then + destination_prefix= + fi mkdir -p -- "$archive_output" say "extracting archive: $relative" RPYCDEC_NO_WARNING=1 rpycdec unrpa --output "$archive_output" "$archive" > "$log_file" 2>&1 log "extracted archive: $relative" while IFS= read -r -d '' archived_source; do - copy_source_file "$archived_source" "$archive_output" archived + copy_source_file "$archived_source" "$archive_output" archived "$destination_prefix" done < <(find "$archive_output" -type f -name '*.rpy' -print0 | sort -z) while IFS= read -r -d '' compiled; do - decompile_compiled_script "$compiled" "$archive_output" + decompile_compiled_script "$compiled" "$archive_output" "$destination_prefix" done < <(find "$archive_output" -type f \( -name '*.rpyc' -o -name '*.rpymc' \) -print0 | sort -z) } diff --git a/src/ariadne/extract-tools.nix b/src/ariadne/extract-tools.nix index 70bd6b9..2f20db7 100644 --- a/src/ariadne/extract-tools.nix +++ b/src/ariadne/extract-tools.nix @@ -23,6 +23,7 @@ pkgs.buildEnv { pkgs.bash pkgs.coreutils pkgs.findutils + pkgs.python3 rpycdec ]; }