#!/usr/bin/env bash # restore-rocknix.sh - Restore script for ROCKNIX configurations # # Usage: # ./restore-rocknix.sh [OPTIONS] # # Options: # --host HOST Target console host # --user USER SSH user (default: root) # --backup FILE .tar.gz backup file to restore # --all Restore all components without prompting # --help Show help set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/lib-common.sh" # Default values BACKUP_FILE="" RESTORE_ALL=false # Parse arguments while [[ $# -gt 0 ]]; do case "$1" in --host) ROCKNIX_HOST="$2" shift 2 ;; --user) ROCKNIX_USER="$2" shift 2 ;; --password) ROCKNIX_PASSWORD="$2" shift 2 ;; --backup) BACKUP_FILE="$2" shift 2 ;; --all) RESTORE_ALL=true shift ;; --help) echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " --host HOST Target console host" echo " --user USER SSH user (default: $ROCKNIX_USER)" echo " --password PASS SSH password" echo " --backup FILE .tar.gz backup file to restore" echo " --all Restore all components" echo " --help Show this help" exit 0 ;; *) log ERROR "Unknown option: $1" exit 1 ;; esac done # Check dependencies check_deps || exit 1 # Ask for password if not configured ask_password || exit 1 # If no backup specified, list available ones if [[ -z "$BACKUP_FILE" ]]; then log INFO "Searching for backups in ${BACKUP_DIR}..." if [[ ! -d "$BACKUP_DIR" ]]; then log ERROR "Backup directory not found: $BACKUP_DIR" log INFO "Run first: ./backup-rocknix.sh" exit 1 fi BACKUPS=($(find "$BACKUP_DIR" -name "rocknix-config-*.tar.gz" -type f | sort -r)) if [[ ${#BACKUPS[@]} -eq 0 ]]; then log ERROR "No backups found in ${BACKUP_DIR}" log INFO "Run first: ./backup-rocknix.sh" exit 1 fi echo "" echo -e "${CYAN}Available backups:${NC}" echo "" for i in "${!BACKUPS[@]}"; do local backup="${BACKUPS[$i]}" local name=$(basename "$backup") local size=$(du -h "$backup" | cut -f1) local date=$(stat -c %y "$backup" 2>/dev/null | cut -d'.' -f1) echo -e " ${GREEN}$((i+1))${NC}) ${name}" echo -e " Size: ${size} | Date: ${date}" done echo "" read -p "Select backup (1-${#BACKUPS[@]}): " choice if [[ "$choice" -lt 1 || "$choice" -gt ${#BACKUPS[@]} ]]; then log ERROR "Invalid selection" exit 1 fi BACKUP_FILE="${BACKUPS[$((choice-1))]}" fi # Verify backup file exists if [[ ! -f "$BACKUP_FILE" ]]; then log ERROR "Backup file not found: $BACKUP_FILE" exit 1 fi # Show backup contents show_backup_contents "$BACKUP_FILE" # Verify connection with target console echo "" ssh_check "$ROCKNIX_HOST" "$ROCKNIX_USER" || exit 1 # Detect target device DEVICE=$(detect_device "$ROCKNIX_HOST" "$ROCKNIX_USER" 2>/dev/null || echo "unknown") log INFO "Target device: ${DEVICE}" # Check backup type BACKUP_NAME=$(basename "$BACKUP_FILE") if [[ "$BACKUP_NAME" == *"generic"* ]]; then log WARN "This is a GENERIC backup (without device-specific settings)" log WARN "Make sure the target device is compatible" echo "" read -p "Continue? (y/N): " confirm if [[ "$confirm" != "s" && "$confirm" != "S" ]]; then log INFO "Restore cancelled" exit 0 fi fi # Create temporary directory TMP_DIR=$(create_tmp_dir "rocknix-restore") trap 'cleanup_tmp "$TMP_DIR"' EXIT # Extract backup log INFO "Extracting backup..." tar -xzf "$BACKUP_FILE" -C "$TMP_DIR" # Select components to restore if [[ "$RESTORE_ALL" == "false" ]]; then echo "" echo -e "${CYAN}Which components do you want to restore?${NC}" echo "" COMPONENTS=( "retroarch-core-options:RetroArch core-options" "retroarch-opt:RetroArch .opt (core options)" "retroarch-rmp:RetroArch .rmp (remaps)" "retroarch-config:RetroArch overrides (config/)" "shaders:Shaders" "ppsspp:PPSSPP" "drastic:DraStic" "other-emu:Other emulators" ) SELECTION=() for i in "${!COMPONENTS[@]}"; do IFS=':' read -r key label <<< "${COMPONENTS[$i]}" # Determine if there are files of this type case "$key" in retroarch-core-options) [[ -f "${TMP_DIR}/.config/retroarch/retroarch-core-options.cfg" ]] && available=true || available=false ;; retroarch-opt) find "${TMP_DIR}/.config/retroarch/" -maxdepth 1 -name "*.opt" -type f 2>/dev/null | grep -q . && available=true || available=false ;; retroarch-rmp) find "${TMP_DIR}/.config/retroarch/" -maxdepth 1 -name "*.rmp" -type f 2>/dev/null | grep -q . && available=true || available=false ;; retroarch-config) [[ -d "${TMP_DIR}/.config/retroarch/config/" ]] && available=true || available=false ;; shaders) [[ -d "${TMP_DIR}/shaders/" ]] && available=true || available=false ;; ppsspp) [[ -f "${TMP_DIR}/.config/ppsspp/PSP/SYSTEM/ppsspp.ini" ]] && available=true || available=false ;; drastic) [[ -d "${TMP_DIR}/.config/drastic/config/" ]] && available=true || available=false ;; other-emu) [[ -d "${TMP_DIR}/.config/game/" ]] || \ [[ -d "${TMP_DIR}/.config/flycast/" ]] || \ [[ -f "${TMP_DIR}/.config/duckstation/settings.ini" ]] || \ [[ -f "${TMP_DIR}/.config/yabasanshiro/.config" ]] && available=true || available=false ;; esac if [[ "$available" == "true" ]]; then SELECTION+=("$key" "$label" "on") else SELECTION+=("$key" "$label" "off") fi done CHOICES=$(dialog --checklist "Components to restore:" \ 20 60 12 \ "${SELECTION[@]}" \ 3>&1 1>&2 2>&3) # If user cancels if [[ $? -ne 0 ]]; then log INFO "Restore cancelled" exit 0 fi else # Restore all CHOICES="retroarch-core-options retroarch-opt retroarch-rmp retroarch-config shaders ppsspp drastic other-emu" fi # Confirm restore echo "" echo -e "${YELLOW}Restore the following components on ${ROCKNIX_HOST}?${NC}" for choice in $CHOICES; do case "$choice" in retroarch-core-options) echo " - RetroArch core-options" ;; retroarch-opt) echo " - RetroArch .opt" ;; retroarch-rmp) echo " - RetroArch .rmp" ;; retroarch-config) echo " - RetroArch overrides" ;; shaders) echo " - Shaders" ;; ppsspp) echo " - PPSSPP" ;; drastic) echo " - DraStic" ;; other-emu) echo " - Other emulators" ;; esac done echo "" read -p "Confirm restore? (y/N): " confirm if [[ "$confirm" != "s" && "$confirm" != "S" ]]; then log INFO "Restore cancelled" exit 0 fi # Create safety backup on target console log INFO "Creating safety backup on console..." SAFETY_DIR="${TMP_DIR}/safety" mkdir -p "$SAFETY_DIR" # Download current files before overwriting for choice in $CHOICES; do case "$choice" in retroarch-core-options) download_file ".config/retroarch/retroarch-core-options.cfg" \ "${SAFETY_DIR}/retroarch-core-options.cfg" \ "$ROCKNIX_HOST" "$ROCKNIX_USER" 2>/dev/null || true ;; # Add others if needed esac done log INFO "Safety backup created" # Restore files log INFO "Restoring files..." RESTORED=0 FAILED=0 for choice in $CHOICES; do log INFO "Restoring: ${choice}..." case "$choice" in retroarch-core-options) if [[ -f "${TMP_DIR}/.config/retroarch/retroarch-core-options.cfg" ]]; then if upload_file "${TMP_DIR}/.config/retroarch/retroarch-core-options.cfg" \ ".config/retroarch/retroarch-core-options.cfg" \ "$ROCKNIX_HOST" "$ROCKNIX_USER" &>/dev/null; then RESTORED=$((RESTORED + 1)) else FAILED=$((FAILED + 1)) log WARN "Error restoring retroarch-core-options.cfg" fi fi ;; retroarch-opt) for opt_file in "${TMP_DIR}/.config/retroarch/"*.opt; do if [[ -f "$opt_file" ]]; then filename=$(basename "$opt_file") if upload_file "$opt_file" \ ".config/retroarch/${filename}" \ "$ROCKNIX_HOST" "$ROCKNIX_USER" &>/dev/null; then RESTORED=$((RESTORED + 1)) else FAILED=$((FAILED + 1)) fi fi done ;; retroarch-rmp) for rmp_file in "${TMP_DIR}/.config/retroarch/"*.rmp; do if [[ -f "$rmp_file" ]]; then filename=$(basename "$rmp_file") if upload_file "$rmp_file" \ ".config/retroarch/${filename}" \ "$ROCKNIX_HOST" "$ROCKNIX_USER" &>/dev/null; then RESTORED=$((RESTORED + 1)) else FAILED=$((FAILED + 1)) fi fi done ;; retroarch-config) if [[ -d "${TMP_DIR}/.config/retroarch/config/" ]]; then if upload_dir "${TMP_DIR}/.config/retroarch/config/" \ ".config/retroarch/config/" \ "$ROCKNIX_HOST" "$ROCKNIX_USER" &>/dev/null; then RESTORED=$((RESTORED + 1)) else FAILED=$((FAILED + 1)) log WARN "Error restoring config/" fi fi ;; shaders) if [[ -d "${TMP_DIR}/shaders/" ]]; then if upload_dir "${TMP_DIR}/shaders/" \ "shaders/" \ "$ROCKNIX_HOST" "$ROCKNIX_USER" &>/dev/null; then RESTORED=$((RESTORED + 1)) else FAILED=$((FAILED + 1)) log WARN "Error restoring shaders/" fi fi ;; ppsspp) if [[ -f "${TMP_DIR}/.config/ppsspp/PSP/SYSTEM/ppsspp.ini" ]]; then upload_file "${TMP_DIR}/.config/ppsspp/PSP/SYSTEM/ppsspp.ini" \ ".config/ppsspp/PSP/SYSTEM/ppsspp.ini" \ "$ROCKNIX_HOST" "$ROCKNIX_USER" 2>/dev/null && RESTORED=$((RESTORED + 1)) || FAILED=$((FAILED + 1)) fi if [[ -f "${TMP_DIR}/.config/ppsspp/PSP/SYSTEM/controls.ini" ]]; then upload_file "${TMP_DIR}/.config/ppsspp/PSP/SYSTEM/controls.ini" \ ".config/ppsspp/PSP/SYSTEM/controls.ini" \ "$ROCKNIX_HOST" "$ROCKNIX_USER" 2>/dev/null && RESTORED=$((RESTORED + 1)) || FAILED=$((FAILED + 1)) fi ;; drastic) if [[ -d "${TMP_DIR}/.config/drastic/config/" ]]; then if upload_dir "${TMP_DIR}/.config/drastic/config/" \ ".config/drastic/config/" \ "$ROCKNIX_HOST" "$ROCKNIX_USER" &>/dev/null; then RESTORED=$((RESTORED + 1)) else FAILED=$((FAILED + 1)) log WARN "Error restoring drastic/config/" fi fi ;; other-emu) for emu_path in ".config/game/configs" ".config/flycast" ".config/duckstation" ".config/yabasanshiro"; do if [[ -e "${TMP_DIR}/${emu_path}" ]]; then upload_dir "${TMP_DIR}/${emu_path}/" "${emu_path}/" \ "$ROCKNIX_HOST" "$ROCKNIX_USER" 2>/dev/null && RESTORED=$((RESTORED + 1)) || FAILED=$((FAILED + 1)) fi done ;; esac done # Clean up temporary safety backup cleanup_tmp "${SAFETY_DIR}" 2>/dev/null || true # Summary echo "" echo -e "${GREEN}========================================${NC}" echo -e "${GREEN} Restore completed${NC}" echo -e "${GREEN}========================================${NC}" echo "" echo -e " Device: ${CYAN}${DEVICE}${NC}" echo -e " Restored: ${CYAN}${RESTORED}${NC}" if [[ $FAILED -gt 0 ]]; then echo -e " Failed: ${RED}${FAILED}${NC}" fi echo "" echo -e " ${YELLOW}Note: Restart the console to apply changes${NC}" echo ""