#!/bin/bash set -e source .config workdir=${WORKDIR:-/tmp/wacca} version=${VERSION:-3.07.01} airgap=${AIRGAP:-false} check_prereqs () { tools=("vhdimount" "umu-run" "fsdecrypt" "aria2c" "udisksctl") echo "Checking for required tools..." echo "==============================" all_installed=true for tool in "${tools[@]}"; do if command -v "$tool" &> /dev/null; then echo -e "\033[0;32m✓\033[0m $tool is installed" else echo -e "\033[0;31m✗\033[0m $tool is NOT installed" all_installed=false fi done echo "==============================" if [ "$all_installed" != true ]; then echo "Some tools are missing :(" if [ "$ignore_prereqs" = true ]; then echo "Ignoring prerequisites" else exit 1 fi fi } # GET NEEDED APPS FROM $VERSION get_required_app () { apps="" while IFS=',' read -r version url; do apps+="$url," if [[ "$version" == "$VERSION" ]]; then break fi done < <(grep "$(echo "^$VERSION" | cut -c1-4)" imagestore/index.csv) } import_from_ins () { result=$(blkid | grep SEGA_INS | head -n 1 | cut -d: -f1 ) if [ -n "${result:-}" ]; then udisksctl mount --block-device $result mount_point=$(findmnt -n -o TARGET $result) echo "Checking SEGA_INS for SDFE .app files..." for file in $mount_point/SDFE*.app; do basename=$(basename $file) if [ -f "imagestore/app/$basename" ]; then echo "$basename: File already present on target, skipping." else echo "$basename: Adding file." pv $file > imagestore/app/$basename fi done echo "Checking SEGA_INS for .icf files..." for file in $mount_point/*.icf; do basename=$(basename $file) echo "$basename: Adding file." pv $file > imagestore/misc/ICF1 done echo "Checking SEGA_INS for segatools.zip..." for file in $mount_point/segatools.zip; do basename=$(basename $file) echo "$basename: Adding file." pv $file > imagestore/misc/segatools.zip done udisksctl unmount --block-device $result else echo "SEGA_INS not found, skipping." fi } extract_apps () { missingapps=() mkdir -p imagestore/vhd for url in ${apps//,/ }; do local file=$(basename "$url") local vhd=$(basename "$url" | sed -E "s/.app/.vhd/") if [ -f imagestore/app/$file ]; then echo "Found $file" if [ -f imagestore/vhd/$vhd ]; then echo "Found $vhd" else echo "Decrypting $file" fsdecrypt imagestore/app/$file mv imagestore/app/$vhd imagestore/vhd/ fi else if [ ! -f imagestore/vhd/$vhd ]; then if [ "$airgap" = false ]; then echo "Downloading $file" aria2c -x 8 -j 8 -o imagestore/app/$file $url echo "Decrypting $file" fsdecrypt imagestore/app/$file mv imagestore/app/$vhd imagestore/vhd/ else echo $file missingapps+=("$file") fi fi fi done if [ ${#missingapps[@]} -gt 0 ]; then echo "ERROR: Missing following .app files for specified version:" for app in ${missingapps[@]}; do echo "- "$app done exit 2 fi } patchbin () { # local targetvhd="$1" # local tmp=$(mktemp -u) # dd if=$targetvhd of=$tmp bs=1 count=576 # dd if=TEST.vhd of=$tmp bs=1 skip=575 seek=576 count=80 # dd if=$tmp of=$targetvhd bs=1 conv=notrunc # echo -n $(basename $1) | iconv -f UTF-8 -t UTF-16BE | dd of=test for url in ${apps//,/ }; do local file=$(basename $url) IFS='_' read gameid version date vhdpos parentversion <<< "${file%.app}" if [[ "$vhdpos" != "0" ]]; then local vhd=$(basename "$url" | sed -E "s/.app/.vhd/") local input=$(vhdiinfo imagestore/vhd/$vhd) local identifier=$(echo "$input" | grep -i "Identifier" | head -1 | awk '{print $NF}') local parent_identifier=$(echo "$input" | grep -i "Parent identifier" | awk '{print $NF}') local parent_filename=$(echo "$input" | grep -i "Parent filename" | awk -F': ' '{print $2}') echo "Checking $vhd parent" if [[ ! "$parent_filename" =~ ^SDFE ]]; then echo "$vhd Parent filename incorrect" local new_parent_filename=$(grep "^$parentversion" imagestore/index.csv | cut -d',' -f2 | xargs basename | sed -E "s/.app/.vhd/") local len=${#new_parent_filename} local result=$(((len * 2))) echo "Writing parent filename $new_parent_filename to $vhd" echo -n $new_parent_filename | iconv -f UTF-8 -t UTF-16BE | dd of=imagestore/vhd/$vhd seek=576 bs=1 conv=notrunc echo -n $new_parent_filename | iconv -f UTF-8 -t UTF-16BE | dd of=imagestore/vhd/$vhd seek=4096 bs=1 conv=notrunc if [ $result -lt 80 ]; then local remainder=$((80 - result)) dd if=/dev/zero of=imagestore/vhd/$vhd seek=$((576 + result)) bs=1 count=$remainder conv=notrunc dd if=/dev/zero of=imagestore/vhd/$vhd seek=$((4096 + result)) bs=1 count=$remainder conv=notrunc fi fi fi done } mount () { if [ "$MOUNT_AS_ROOT" = false ]; then sudo="" else sudo="sudo" fi local vhdpath=$(grep "^$version" imagestore/index.csv | cut -d',' -f2 | xargs basename | sed -E "s/.app/.vhd/") mkdir -p $workdir/$version/{lower,upper,game,work} || true if [ -z "$($sudo ls -A "$workdir/$version/dev")" ]; then mkdir -p $workdir/$version/dev || true $sudo vhdimount imagestore/vhd/$vhdpath $workdir/$version/dev fi if ! df -h | grep -q "$workdir/$version/lower"; then $sudo mount -t ntfs -o offset=$((2048*512)),uid=$(id -u),gid=$(id -g) $($sudo find $workdir/$version/dev -maxdepth 1 -type f | sort -V | tail -1) $workdir/$version/$profile/lower fi if ! df -h | grep -q "$workdir/$version/game"; then $sudo mount overlay -t overlay -o lowerdir=$workdir/$version/lower,upperdir=$workdir/$version/upper,workdir=$workdir/$version/work $workdir/$version/game fi } apply_patches () { # Temp shit icf="https://archive.org/download/WACCA/EXP/3.00.00%20USB/SDFE_ACA.icf" dir=$workdir/$version cd ./patches for script in $(ls -1v 2>/dev/null); do [ -f "$script" ] && [ -r "$script" ] || continue if ! grep -qxF "$script" "$dir/patches"; then echo -e "\033[0;34m=== Applying: $script ===\033[0m" # Create a named pipe for real-time output local pipe=$(mktemp -u) mkfifo "$pipe" # Execute the script with real-time output if (source "$script" 2>&1 | while IFS= read -r line; do echo "[$script] $line" done); then echo -e "\033[0;32m=== Successfully applied: $script ===\033[0m" echo "$script" >> $dir/patches else exit_code=$? echo -e "\033[0;31m=== Failed: $script (exit code: $exit_code) ===\033[0m" fi else echo -e "\033[0;33m=== Skipping: $script ===\033[0m" fi # Clean up the pipe rm -f "$pipe" echo done } check_prereqs get_required_app import_from_ins extract_apps patchbin mount apply_patches