Skip to content

Instantly share code, notes, and snippets.

@i30817
Last active February 23, 2026 04:51
Show Gist options
  • Select an option

  • Save i30817/afe1e0a4e05cce53a13d8ef65c4de384 to your computer and use it in GitHub Desktop.

Select an option

Save i30817/afe1e0a4e05cce53a13d8ef65c4de384 to your computer and use it in GitHub Desktop.
create rxdelta files from as hack dir to a nointro update dir and move the hacks there (overwriting the old matched rom) to make rhdndat-rn recognize some older softpatch romhacks as the newer nointro roms in new dats
#!/bin/bash
display_help() {
cat << EOF
$(basename "$0") - NES Game Patch Processor & Merger
_
Usage: $(basename "$0") <patch_dir> <clean_zip_dir> <destination_dir>
A utility to identify differences between NES files and compressed
clean ROMs, generating delta patches for modified versions.
It is useful when no-intro updates because the generated rxdelta
makes my other tool rhdndat-rn consider the rom file the same as
the clean rom, because it can be restored with the patch.
I recommend rom hacks be in a different base directory as the clean
roms to make updating easier, so this tool does not support that.
ARGUMENTS:
patch_dir Directory containing subfolders with .nes and .bps/.ips files.
clean_zip_dir Directory containing clean ROMs in .zip format (named matching subfolders).
destination_dir Target directory where successfully processed game folders will be moved.
WHAT THIS SCRIPT DOES:
1. Validates that all three paths are unique, accessible, and exist.
2. Scans <patch_dir> for game subfolders with bps or ips files.
3. Locates a matching ZIP file in <clean_zip_dir> for each subfolder.
4. Compares the existing .nes file against the clean ZIP content.
5. If they differ, it uses 'xdelta3' to create a new .rxdelta patch.
6. Moves matched folders (from 3.) to the <destination_dir>.
7. Prints a summary of processed games and the total number of patches created.
OPTIONS:
-h, --help Display this help message and exit.
EOF
exit 0
}
# Check for help flags before running the main logic
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
display_help
fi
process_and_merge_games() {
local patch_root=$(realpath -m "$1")
local clean_root=$(realpath -m "$2")
local dest_root=$(realpath -m "$3")
if [[ ! -d "$patch_root" || ! -d "$clean_root" || ! -d "$dest_root" ]]; then
echo "Error: One or more directories do not exist."
return 1
fi
if [[ ! -r "$patch_root" || ! -r "$clean_root" || ! -w "$dest_root" ]]; then
echo "Error: Insufficient permissions (Read required for sources, Write for destination)."
return 1
fi
if [[ "$patch_root" == "$clean_root" ]] || [[ "$patch_root" == "$dest_root" ]] || [[ "$clean_root" == "$dest_root" ]]; then
echo "Error: Source and destination directories must be unique."
return 1
fi
local processed_dirs=()
local patch_count=0
for sub_path in "$patch_root"/*; do
[[ -d "$sub_path" ]] || continue
local dir_name=$(basename "$sub_path")
local patch_subdir="$patch_root/$dir_name"
local clean_subzip="$clean_root/$dir_name".zip
# Check if the same named zip exists
if [[ -f "$clean_subzip" ]]; then
local patch_exists=false
# Identify target games via patch files (.bps or .ips)
for patch in "$patch_subdir"/*.bps "$patch_subdir"/*.ips; do
[[ -e "$patch" ]] || continue
patch_exists=true
local base_name=$(basename "${patch%.*}")
local nes_file="$patch_subdir/$base_name.nes"
# If both files exist, we process them
if [[ -f "$nes_file" ]]; then
if ! cmp -s "$nes_file" <(zcat "$clean_subzip"); then
# Files differ: Create the .rxdelta patch
xdelta3 -f -e -s "$nes_file" <(zcat "$clean_subzip") "$patch_subdir/$base_name.rxdelta" ((patch_count++))
fi
fi
done if [ "$patch_exists" = "true" ]; then processed_dirs+=("$patch_subdir")
if [ "$patch_exists" = "true" ]; then
processed_dirs+=("$patch_subdir")
fi
fi
done
if [[ ${#processed_dirs[@]} -gt 0 ]]; then
mv -t "$dest_root" "${processed_dirs[@]}"
fi
echo -e "\n--- Process Summary ---"
if [[ ${#processed_dirs[@]} -gt 0 ]]; then
echo -e "Processed List:"
for d in "${processed_dirs[@]}"; do echo " - $(basename "$d")"; done
fi
echo "Total .rxdelta patches generated: $patch_count"
}
process_and_merge_games "$1" "$2" "$3"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment