first commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/backups/
|
||||||
152
README.md
Normal file
152
README.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# ROCKNIX Backup & Restore
|
||||||
|
|
||||||
|
Backup and restore script for ROCKNIX emulator configurations. Allows syncing optimal settings between multiple consoles via SSH.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Specific backup**: Full backup including all device/user-specific settings
|
||||||
|
- **Generic backup**: Portable backup with device-specific settings filtered out
|
||||||
|
- **Selective restore**: Choose which components to restore via interactive dialog
|
||||||
|
- **Safety backup**: Automatically saves current settings before restoring
|
||||||
|
- **Device detection**: Automatically identifies the target ROCKNIX device
|
||||||
|
- **SSH support**: Password and key-based authentication
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Fedora/RHEL
|
||||||
|
sudo dnf install tar dialog rsync sshpass
|
||||||
|
|
||||||
|
# Debian/Ubuntu
|
||||||
|
sudo apt install tar dialog rsync sshpass
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Backup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Specific backup (includes all settings)
|
||||||
|
./backup-rocknix.sh
|
||||||
|
|
||||||
|
# Generic backup (portable between devices)
|
||||||
|
./backup-rocknix.sh --type generic
|
||||||
|
|
||||||
|
# Custom host and output directory
|
||||||
|
./backup-rocknix.sh --host myconsole.local --output /path/to/backups
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Interactive restore (lists available backups)
|
||||||
|
./restore-rocknix.sh
|
||||||
|
|
||||||
|
# Restore specific backup file
|
||||||
|
./restore-rocknix.sh --backup backups/rocknix-config-specific-rg-cubexx-20260714-113010.tar.gz
|
||||||
|
|
||||||
|
# Restore all components without prompting
|
||||||
|
./restore-rocknix.sh --backup backup.tar.gz --all
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### backup-rocknix.sh
|
||||||
|
|
||||||
|
| Option | Description | Default |
|
||||||
|
|-------------------|-------------------------|------------------|
|
||||||
|
| `--host HOST` | Console hostname/IP | `rgcubexx.local` |
|
||||||
|
| `--user USER` | SSH username | `root` |
|
||||||
|
| `--password PASS` | SSH password | (prompts) |
|
||||||
|
| `--output DIR` | Backup output directory | `backups/` |
|
||||||
|
| `--type TYPE` | `specific` or `generic` | `specific` |
|
||||||
|
| `--help` | Show help | |
|
||||||
|
|
||||||
|
### restore-rocknix.sh
|
||||||
|
|
||||||
|
| Option | Description | Default |
|
||||||
|
|-------------------|----------------------------|------------------|
|
||||||
|
| `--host HOST` | Target console hostname/IP | `rgcubexx.local` |
|
||||||
|
| `--user USER` | SSH username | `root` |
|
||||||
|
| `--password PASS` | SSH password | (prompts) |
|
||||||
|
| `--backup FILE` | Backup file to restore | (interactive) |
|
||||||
|
| `--all` | Restore all components | `false` |
|
||||||
|
| `--help` | Show help | |
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
The following components can be backed up and restored:
|
||||||
|
|
||||||
|
| Component | Description |
|
||||||
|
|--------------------------|--------------------------------------------|
|
||||||
|
| `retroarch-core-options` | RetroArch global core options |
|
||||||
|
| `retroarch-opt` | Per-core `.opt` files |
|
||||||
|
| `retroarch-rmp` | Button remaps `.rmp` files |
|
||||||
|
| `retroarch-config` | Per-core overrides (`config/`) |
|
||||||
|
| `shaders` | Shader packs and presets |
|
||||||
|
| `ppsspp` | PPSSPP (PSP) settings |
|
||||||
|
| `drastic` | DraStic (NDS) settings |
|
||||||
|
| `other-emu` | Flycast, DuckStation, Yabasanshiro configs |
|
||||||
|
|
||||||
|
## Backup Types
|
||||||
|
|
||||||
|
### Specific Backup
|
||||||
|
|
||||||
|
Includes all settings, including device-specific values like:
|
||||||
|
- Screen resolution and scaling
|
||||||
|
- Video/audio drivers
|
||||||
|
- Input mappings
|
||||||
|
- Account credentials (RetroAchievements, cloud sync)
|
||||||
|
|
||||||
|
Best for: Backing up the same device or identical hardware.
|
||||||
|
|
||||||
|
### Generic Backup
|
||||||
|
|
||||||
|
Filters out device-specific settings for portability:
|
||||||
|
- Screen dimensions (`video_fullscreen_x/y`)
|
||||||
|
- Scale factors (`menu_scale_factor`)
|
||||||
|
- Drivers (`video_driver`, `audio_driver`)
|
||||||
|
- Credentials and tokens
|
||||||
|
|
||||||
|
Best for: Sharing configs between different devices or users.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Edit `config.sh` to customize:
|
||||||
|
|
||||||
|
- `ROCKNIX_HOST`: Default console hostname
|
||||||
|
- `ROCKNIX_STORAGE`: Storage path on device (default: `/storage`)
|
||||||
|
- `BACKUP_DIR`: Local backup directory
|
||||||
|
- `SPECIFIC_FILES`: List of files to back up
|
||||||
|
- `GENERIC_EXCLUDES`: Files excluded from generic backups
|
||||||
|
- `GENERIC_SETTINGS_FILTER`: Settings patterns removed from generic backups
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# First-time setup: create a specific backup
|
||||||
|
./backup-rocknix.sh --host rgcubexx.local
|
||||||
|
|
||||||
|
# Clone settings to another device of the same model
|
||||||
|
./backup-rocknix.sh --type generic
|
||||||
|
./restore-rocknix.sh --host rgcubexx2.local --backup backups/rocknix-config-generic-*.tar.gz
|
||||||
|
|
||||||
|
# List available backups
|
||||||
|
./restore-rocknix.sh # Shows interactive menu
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
rocknix-backup/
|
||||||
|
├── backup-rocknix.sh # Backup script
|
||||||
|
├── restore-rocknix.sh # Restore script
|
||||||
|
├── lib-common.sh # Shared functions (SSH, rsync, logging)
|
||||||
|
├── config.sh # Default configuration
|
||||||
|
├── backups/ # Backup files (*.tar.gz)
|
||||||
|
└── PRE-ANALISIS-ROCKNIX.md # Technical analysis document
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
203
backup-rocknix.sh
Executable file
203
backup-rocknix.sh
Executable file
@@ -0,0 +1,203 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# backup-rocknix.sh - Backup script for ROCKNIX configurations
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./backup-rocknix.sh [OPTIONS]
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# --host HOST Console host (default: rgeubexx.local)
|
||||||
|
# --user USER SSH user (default: root)
|
||||||
|
# --output DIR Output directory for backups
|
||||||
|
# --type TYPE Backup type: specific or generic (default: specific)
|
||||||
|
# --help Show help
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib-common.sh"
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
BACKUP_TYPE="specific"
|
||||||
|
|
||||||
|
# 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
|
||||||
|
;;
|
||||||
|
--output)
|
||||||
|
BACKUP_DIR="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--type)
|
||||||
|
BACKUP_TYPE="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--help)
|
||||||
|
echo "Usage: $0 [OPTIONS]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " --host HOST Console host (default: $ROCKNIX_HOST)"
|
||||||
|
echo " --user USER SSH user (default: $ROCKNIX_USER)"
|
||||||
|
echo " --password PASS SSH password"
|
||||||
|
echo " --output DIR Output directory"
|
||||||
|
echo " --type TYPE Backup type: specific, generic (default: specific)"
|
||||||
|
echo " --help Show this help"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log ERROR "Unknown option: $1"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Verify valid type
|
||||||
|
if [[ "$BACKUP_TYPE" != "specific" && "$BACKUP_TYPE" != "generic" ]]; then
|
||||||
|
log ERROR "Invalid backup type: $BACKUP_TYPE (use: specific, generic)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check dependencies
|
||||||
|
check_deps || exit 1
|
||||||
|
|
||||||
|
# Ask for password if not configured
|
||||||
|
ask_password || exit 1
|
||||||
|
|
||||||
|
# Verify SSH connection
|
||||||
|
ssh_check "$ROCKNIX_HOST" "$ROCKNIX_USER" || exit 1
|
||||||
|
|
||||||
|
# Detect device
|
||||||
|
DEVICE=$(detect_device "$ROCKNIX_HOST" "$ROCKNIX_USER" 2>/dev/null || echo "unknown")
|
||||||
|
DEVICE_SLUG=$(echo "$DEVICE" | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
|
# Create backups directory
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
# Create temporary directory
|
||||||
|
TMP_DIR=$(create_tmp_dir "rocknix-backup")
|
||||||
|
trap 'cleanup_tmp "$TMP_DIR"' EXIT
|
||||||
|
|
||||||
|
log INFO "Starting ${BACKUP_TYPE} backup..."
|
||||||
|
log INFO "Device: ${DEVICE}"
|
||||||
|
log INFO "Host: ${ROCKNIX_USER}@${ROCKNIX_HOST}"
|
||||||
|
|
||||||
|
# Select file list based on type
|
||||||
|
if [[ "$BACKUP_TYPE" == "specific" ]]; then
|
||||||
|
FILES_TO_BACKUP=("${SPECIFIC_FILES[@]}")
|
||||||
|
else
|
||||||
|
# For generic, exclude device-specific files
|
||||||
|
FILES_TO_BACKUP=()
|
||||||
|
for file in "${SPECIFIC_FILES[@]}"; do
|
||||||
|
skip=false
|
||||||
|
for exclude in "${GENERIC_EXCLUDES[@]}"; do
|
||||||
|
if [[ "$file" == "$exclude" ]]; then
|
||||||
|
skip=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ "$skip" == "false" ]]; then
|
||||||
|
FILES_TO_BACKUP+=("$file")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Download files
|
||||||
|
log INFO "Downloading files..."
|
||||||
|
DOWNLOADED=0
|
||||||
|
FAILED=0
|
||||||
|
|
||||||
|
for file in "${FILES_TO_BACKUP[@]}"; do
|
||||||
|
local_path="${TMP_DIR}/${file}"
|
||||||
|
remote_path="${file}"
|
||||||
|
|
||||||
|
# Create local directory
|
||||||
|
mkdir -p "$(dirname "$local_path")"
|
||||||
|
|
||||||
|
# Download
|
||||||
|
if [[ -d "${local_path}" ]] || [[ "$file" == */ ]]; then
|
||||||
|
# It's a directory
|
||||||
|
mkdir -p "$local_path"
|
||||||
|
if download_dir "$remote_path" "$local_path" "$ROCKNIX_HOST" "$ROCKNIX_USER" &>/dev/null; then
|
||||||
|
DOWNLOADED=$((DOWNLOADED + 1))
|
||||||
|
log DEBUG "Downloaded: ${file}"
|
||||||
|
else
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
log WARN "Could not download: ${file}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# It's a file
|
||||||
|
if download_file "$remote_path" "$local_path" "$ROCKNIX_HOST" "$ROCKNIX_USER" &>/dev/null; then
|
||||||
|
DOWNLOADED=$((DOWNLOADED + 1))
|
||||||
|
log DEBUG "Downloaded: ${file}"
|
||||||
|
else
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
log WARN "Could not download: ${file}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log INFO "Downloaded: ${DOWNLOADED} files"
|
||||||
|
|
||||||
|
# For generic backup, filter device/user-specific settings
|
||||||
|
if [[ "$BACKUP_TYPE" == "generic" ]]; then
|
||||||
|
log INFO "Filtering device/user-specific settings..."
|
||||||
|
|
||||||
|
# Filter retroarch-core-options.cfg if it exists
|
||||||
|
if [[ -f "${TMP_DIR}/.config/retroarch/retroarch-core-options.cfg" ]]; then
|
||||||
|
filter_specific_settings \
|
||||||
|
"${TMP_DIR}/.config/retroarch/retroarch-core-options.cfg" \
|
||||||
|
"${TMP_DIR}/.config/retroarch/retroarch-core-options.cfg.filtered"
|
||||||
|
mv "${TMP_DIR}/.config/retroarch/retroarch-core-options.cfg.filtered" \
|
||||||
|
"${TMP_DIR}/.config/retroarch/retroarch-core-options.cfg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Filter overrides (.cfg files)
|
||||||
|
find "${TMP_DIR}/.config/retroarch/config/" -name "*.cfg" -type f 2>/dev/null | while read -r cfg_file; do
|
||||||
|
filter_specific_settings "$cfg_file" "${cfg_file}.filtered"
|
||||||
|
mv "${cfg_file}.filtered" "$cfg_file"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create backup filename
|
||||||
|
TIMESTAMP=$(date '+%Y%m%d-%H%M%S')
|
||||||
|
BACKUP_NAME="rocknix-config-${BACKUP_TYPE}-${DEVICE_SLUG}-${TIMESTAMP}.tar.gz"
|
||||||
|
BACKUP_FILE="${BACKUP_DIR}/${BACKUP_NAME}"
|
||||||
|
|
||||||
|
# Compress
|
||||||
|
log INFO "Compressing backup..."
|
||||||
|
tar -czf "$BACKUP_FILE" -C "$TMP_DIR" .
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
if [[ -f "$BACKUP_FILE" ]]; then
|
||||||
|
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||||
|
BACKUP_FILES=$(tar -tzf "$BACKUP_FILE" | wc -l)
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}========================================${NC}"
|
||||||
|
echo -e "${GREEN} Backup completed successfully${NC}"
|
||||||
|
echo -e "${GREEN}========================================${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " Type: ${CYAN}${BACKUP_TYPE}${NC}"
|
||||||
|
echo -e " Device: ${CYAN}${DEVICE}${NC}"
|
||||||
|
echo -e " File: ${CYAN}${BACKUP_NAME}${NC}"
|
||||||
|
echo -e " Location: ${CYAN}${BACKUP_DIR}${NC}"
|
||||||
|
echo -e " Size: ${CYAN}${BACKUP_SIZE}${NC}"
|
||||||
|
echo -e " Files: ${CYAN}${BACKUP_FILES}${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${YELLOW}To restore, run:${NC}"
|
||||||
|
echo -e " ${CYAN}./restore-rocknix.sh --backup ${BACKUP_FILE}${NC}"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
log ERROR "Could not create backup file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
86
config.sh
Executable file
86
config.sh
Executable file
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# config.sh - Default configuration for ROCKNIX backup/restore
|
||||||
|
|
||||||
|
# ROCKNIX console host
|
||||||
|
ROCKNIX_HOST="${ROCKNIX_HOST:-rgcubexx.local}"
|
||||||
|
ROCKNIX_USER="${ROCKNIX_USER:-root}"
|
||||||
|
ROCKNIX_PASSWORD="${ROCKNIX_PASSWORD:-}"
|
||||||
|
ROCKNIX_STORAGE="${ROCKNIX_STORAGE:-/storage}"
|
||||||
|
|
||||||
|
# Local backup directory (resolved in lib-common.sh)
|
||||||
|
BACKUP_DIR="${BACKUP_DIR:-backups}"
|
||||||
|
|
||||||
|
# Files to back up (relative to /storage)
|
||||||
|
# Specific backup (includes everything)
|
||||||
|
SPECIFIC_FILES=(
|
||||||
|
# RetroArch global
|
||||||
|
".config/retroarch/retroarch.cfg"
|
||||||
|
".config/retroarch/retroarch-core-options.cfg"
|
||||||
|
".config/retroarch/retroarch32bit-append.cfg"
|
||||||
|
".config/retroarch/retroarch64bit-append.cfg"
|
||||||
|
|
||||||
|
# RetroArch .opt (per-core options at root level)
|
||||||
|
".config/retroarch/Flycast.opt"
|
||||||
|
".config/retroarch/Supafaust.opt"
|
||||||
|
".config/retroarch/ParaLLEl N64.opt"
|
||||||
|
|
||||||
|
# RetroArch .rmp (remaps at root level)
|
||||||
|
".config/retroarch/TATE-MAME 2003-Plus.rmp"
|
||||||
|
|
||||||
|
# RetroArch overrides (complete directory)
|
||||||
|
".config/retroarch/config/"
|
||||||
|
|
||||||
|
# Shaders
|
||||||
|
"shaders/"
|
||||||
|
|
||||||
|
# PPSSPP
|
||||||
|
".config/ppsspp/PSP/SYSTEM/ppsspp.ini"
|
||||||
|
".config/ppsspp/PSP/SYSTEM/controls.ini"
|
||||||
|
|
||||||
|
# DraStic
|
||||||
|
".config/drastic/config/"
|
||||||
|
|
||||||
|
# Other emulators
|
||||||
|
".config/game/configs/"
|
||||||
|
".config/flycast/emu.cfg"
|
||||||
|
".config/flycast/flycast.gptk"
|
||||||
|
".config/flycast/mappings/"
|
||||||
|
".config/duckstation/settings.ini"
|
||||||
|
".config/yabasanshiro/.config"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Generic backup: exclude files with device/user-specific settings
|
||||||
|
GENERIC_EXCLUDES=(
|
||||||
|
".config/retroarch/retroarch.cfg"
|
||||||
|
".config/retroarch/retroarch32bit-append.cfg"
|
||||||
|
".config/retroarch/retroarch64bit-append.cfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Settings patterns to filter from overrides in generic backup
|
||||||
|
GENERIC_SETTINGS_FILTER=(
|
||||||
|
"menu_scale_factor"
|
||||||
|
"menu_widget_scale_factor"
|
||||||
|
"video_fullscreen_x"
|
||||||
|
"video_fullscreen_y"
|
||||||
|
"video_windowed_position_width"
|
||||||
|
"video_windowed_position_height"
|
||||||
|
"video_refresh_rate"
|
||||||
|
"custom_viewport_width"
|
||||||
|
"custom_viewport_height"
|
||||||
|
"video_driver"
|
||||||
|
"audio_driver"
|
||||||
|
"input_driver"
|
||||||
|
"input_joypad_driver"
|
||||||
|
"cheevos_username"
|
||||||
|
"cheevos_password"
|
||||||
|
"cheevos_token"
|
||||||
|
"cheevos_custom_host"
|
||||||
|
"google_drive_refresh_token"
|
||||||
|
"webdav_password"
|
||||||
|
"webdav_username"
|
||||||
|
"kiosk_mode_password"
|
||||||
|
"content_show_settings_password"
|
||||||
|
"netplay_ip_address"
|
||||||
|
"netplay_ip_port"
|
||||||
|
"netplay_password"
|
||||||
|
)
|
||||||
343
lib-common.sh
Executable file
343
lib-common.sh
Executable file
@@ -0,0 +1,343 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# lib-common.sh - Shared functions for ROCKNIX backup/restore
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Script directory
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# Load configuration
|
||||||
|
source "$SCRIPT_DIR/config.sh"
|
||||||
|
|
||||||
|
# Resolve BACKUP_DIR relative to script
|
||||||
|
case "$BACKUP_DIR" in
|
||||||
|
/*) ;; # Absolute path, do nothing
|
||||||
|
*) BACKUP_DIR="${SCRIPT_DIR}/${BACKUP_DIR}" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Colores
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
log() {
|
||||||
|
local level="$1"
|
||||||
|
shift
|
||||||
|
local msg="$*"
|
||||||
|
local timestamp
|
||||||
|
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
case "$level" in
|
||||||
|
INFO) echo -e "${GREEN}[${timestamp}] INFO:${NC} $msg" ;;
|
||||||
|
WARN) echo -e "${YELLOW}[${timestamp}] WARN:${NC} $msg" ;;
|
||||||
|
ERROR) echo -e "${RED}[${timestamp}] ERROR:${NC} $msg" >&2 ;;
|
||||||
|
DEBUG) echo -e "${BLUE}[${timestamp}] DEBUG:${NC} $msg" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check dependencies
|
||||||
|
check_deps() {
|
||||||
|
local missing=()
|
||||||
|
for dep in tar dialog rsync sshpass; do
|
||||||
|
if ! command -v "$dep" &>/dev/null; then
|
||||||
|
missing+=("$dep")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||||
|
log ERROR "Missing dependencies: ${missing[*]}"
|
||||||
|
log INFO "Install with: sudo dnf install ${missing[*]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ask for password if not configured
|
||||||
|
ask_password() {
|
||||||
|
if [[ -z "$ROCKNIX_PASSWORD" ]]; then
|
||||||
|
read -s -p "Password for ${ROCKNIX_USER}@${ROCKNIX_HOST}: " ROCKNIX_PASSWORD
|
||||||
|
echo ""
|
||||||
|
if [[ -z "$ROCKNIX_PASSWORD" ]]; then
|
||||||
|
log ERROR "Empty password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
export ROCKNIX_PASSWORD
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build SSH connection string
|
||||||
|
ssh_target() {
|
||||||
|
local host="${1:-$ROCKNIX_HOST}"
|
||||||
|
local user="${2:-$ROCKNIX_USER}"
|
||||||
|
echo "${user}@${host}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify SSH connection
|
||||||
|
ssh_check() {
|
||||||
|
local host="${1:-$ROCKNIX_HOST}"
|
||||||
|
local user="${2:-$ROCKNIX_USER}"
|
||||||
|
local target
|
||||||
|
target=$(ssh_target "$host" "$user")
|
||||||
|
|
||||||
|
log INFO "Checking connection to ${target}..."
|
||||||
|
|
||||||
|
if [[ -n "$ROCKNIX_PASSWORD" ]]; then
|
||||||
|
if sshpass -p "$ROCKNIX_PASSWORD" ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no "$target" "echo ok" &>/dev/null; then
|
||||||
|
log INFO "Connection successful"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if ssh -o ConnectTimeout=5 -o BatchMode=yes "$target" "echo ok" &>/dev/null; then
|
||||||
|
log INFO "Connection successful"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log ERROR "Could not connect to ${target}"
|
||||||
|
log INFO "Make sure SSH is enabled on the ROCKNIX console"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Execute remote command
|
||||||
|
remote_exec() {
|
||||||
|
local cmd="$1"
|
||||||
|
local host="${2:-$ROCKNIX_HOST}"
|
||||||
|
local user="${3:-$ROCKNIX_USER}"
|
||||||
|
local target
|
||||||
|
target=$(ssh_target "$host" "$user")
|
||||||
|
|
||||||
|
if [[ -n "$ROCKNIX_PASSWORD" ]]; then
|
||||||
|
sshpass -p "$ROCKNIX_PASSWORD" ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$target" "$cmd" 2>&1
|
||||||
|
else
|
||||||
|
ssh -o ConnectTimeout=10 "$target" "$cmd" 2>&1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# List remote files
|
||||||
|
remote_list() {
|
||||||
|
local remote_path="$1"
|
||||||
|
local host="${2:-$ROCKNIX_HOST}"
|
||||||
|
local user="${3:-$ROCKNIX_USER}"
|
||||||
|
|
||||||
|
remote_exec "ls -la ${ROCKNIX_STORAGE}/${remote_path}/ 2>/dev/null" "$host" "$user"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Download entire directory via rsync
|
||||||
|
download_dir() {
|
||||||
|
local remote_path="$1"
|
||||||
|
local local_path="$2"
|
||||||
|
local host="${3:-$ROCKNIX_HOST}"
|
||||||
|
local user="${4:-$ROCKNIX_USER}"
|
||||||
|
local target
|
||||||
|
target=$(ssh_target "$host" "$user")
|
||||||
|
|
||||||
|
mkdir -p "$local_path"
|
||||||
|
|
||||||
|
local rsync_opts=(-avz --timeout=30 -e "ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10")
|
||||||
|
|
||||||
|
if [[ -n "$ROCKNIX_PASSWORD" ]]; then
|
||||||
|
sshpass -p "$ROCKNIX_PASSWORD" rsync "${rsync_opts[@]}" \
|
||||||
|
"${target}:${ROCKNIX_STORAGE}/${remote_path}/" \
|
||||||
|
"${local_path}/" 2>&1
|
||||||
|
else
|
||||||
|
rsync "${rsync_opts[@]}" \
|
||||||
|
"${target}:${ROCKNIX_STORAGE}/${remote_path}/" \
|
||||||
|
"${local_path}/" 2>&1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Upload entire directory via rsync
|
||||||
|
upload_dir() {
|
||||||
|
local local_path="$1"
|
||||||
|
local remote_path="$2"
|
||||||
|
local host="${3:-$ROCKNIX_HOST}"
|
||||||
|
local user="${4:-$ROCKNIX_USER}"
|
||||||
|
local target
|
||||||
|
target=$(ssh_target "$host" "$user")
|
||||||
|
|
||||||
|
local rsync_opts=(-avz --timeout=30 -e "ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10")
|
||||||
|
|
||||||
|
if [[ -n "$ROCKNIX_PASSWORD" ]]; then
|
||||||
|
sshpass -p "$ROCKNIX_PASSWORD" rsync "${rsync_opts[@]}" \
|
||||||
|
"${local_path}/" \
|
||||||
|
"${target}:${ROCKNIX_STORAGE}/${remote_path}/" 2>&1
|
||||||
|
else
|
||||||
|
rsync "${rsync_opts[@]}" \
|
||||||
|
"${local_path}/" \
|
||||||
|
"${target}:${ROCKNIX_STORAGE}/${remote_path}/" 2>&1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Download a single file
|
||||||
|
download_file() {
|
||||||
|
local remote_file="$1"
|
||||||
|
local local_file="$2"
|
||||||
|
local host="${3:-$ROCKNIX_HOST}"
|
||||||
|
local user="${4:-$ROCKNIX_USER}"
|
||||||
|
local target
|
||||||
|
target=$(ssh_target "$host" "$user")
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$local_file")"
|
||||||
|
|
||||||
|
local rsync_opts=(-avz --timeout=30 -e "ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10")
|
||||||
|
|
||||||
|
if [[ -n "$ROCKNIX_PASSWORD" ]]; then
|
||||||
|
sshpass -p "$ROCKNIX_PASSWORD" rsync "${rsync_opts[@]}" \
|
||||||
|
"${target}:${ROCKNIX_STORAGE}/${remote_file}" \
|
||||||
|
"${local_file}" 2>&1
|
||||||
|
else
|
||||||
|
rsync "${rsync_opts[@]}" \
|
||||||
|
"${target}:${ROCKNIX_STORAGE}/${remote_file}" \
|
||||||
|
"${local_file}" 2>&1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Upload a single file
|
||||||
|
upload_file() {
|
||||||
|
local local_file="$1"
|
||||||
|
local remote_file="$2"
|
||||||
|
local host="${3:-$ROCKNIX_HOST}"
|
||||||
|
local user="${4:-$ROCKNIX_USER}"
|
||||||
|
local target
|
||||||
|
target=$(ssh_target "$host" "$user")
|
||||||
|
|
||||||
|
local rsync_opts=(-avz --timeout=30 -e "ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10")
|
||||||
|
|
||||||
|
if [[ -n "$ROCKNIX_PASSWORD" ]]; then
|
||||||
|
sshpass -p "$ROCKNIX_PASSWORD" rsync "${rsync_opts[@]}" \
|
||||||
|
"${local_file}" \
|
||||||
|
"${target}:${ROCKNIX_STORAGE}/${remote_file}" 2>&1
|
||||||
|
else
|
||||||
|
rsync "${rsync_opts[@]}" \
|
||||||
|
"${local_file}" \
|
||||||
|
"${target}:${ROCKNIX_STORAGE}/${remote_file}" 2>&1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Direct backup via tar over SSH (no temporary files)
|
||||||
|
backup_via_ssh() {
|
||||||
|
local files=("$@")
|
||||||
|
local host="${ROCKNIX_HOST}"
|
||||||
|
local user="${ROCKNIX_USER}"
|
||||||
|
local target
|
||||||
|
target=$(ssh_target "$host" "$user")
|
||||||
|
|
||||||
|
# Build remote tar command
|
||||||
|
local tar_args=""
|
||||||
|
for file in "${files[@]}"; do
|
||||||
|
tar_args="${tar_args} ${ROCKNIX_STORAGE}/${file}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Execute remote tar and compress locally
|
||||||
|
if [[ -n "$ROCKNIX_PASSWORD" ]]; then
|
||||||
|
sshpass -p "$ROCKNIX_PASSWORD" ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$target" \
|
||||||
|
"tar czf - -C ${ROCKNIX_STORAGE} ${tar_args}" 2>/dev/null
|
||||||
|
else
|
||||||
|
ssh -o ConnectTimeout=10 "$target" \
|
||||||
|
"tar czf - -C ${ROCKNIX_STORAGE} ${tar_args}" 2>/dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Detect ROCKNIX device
|
||||||
|
detect_device() {
|
||||||
|
local host="${1:-$ROCKNIX_HOST}"
|
||||||
|
local user="${2:-$ROCKNIX_USER}"
|
||||||
|
local target
|
||||||
|
target=$(ssh_target "$host" "$user")
|
||||||
|
|
||||||
|
log INFO "Detecting device..." >&2
|
||||||
|
|
||||||
|
local device_info
|
||||||
|
if [[ -n "$ROCKNIX_PASSWORD" ]]; then
|
||||||
|
device_info=$(sshpass -p "$ROCKNIX_PASSWORD" ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no "$target" \
|
||||||
|
"cat ${ROCKNIX_STORAGE}/device_info_ROCKNIX_*.txt 2>/dev/null" 2>/dev/null || true)
|
||||||
|
else
|
||||||
|
device_info=$(ssh -o ConnectTimeout=5 "$target" \
|
||||||
|
"cat ${ROCKNIX_STORAGE}/device_info_ROCKNIX_*.txt 2>/dev/null" 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$device_info" ]]; then
|
||||||
|
local device_name
|
||||||
|
device_name=$(echo "$device_info" | grep "^DEVICE_NAME=" | cut -d'=' -f2 | tr -d '"')
|
||||||
|
local cfw_version
|
||||||
|
cfw_version=$(echo "$device_info" | grep "^CFW_VERSION=" | cut -d'=' -f2 | tr -d '"')
|
||||||
|
|
||||||
|
if [[ -n "$device_name" ]]; then
|
||||||
|
log INFO "Device: ${device_name} (ROCKNIX ${cfw_version})" >&2
|
||||||
|
echo "${device_name}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log WARN "Could not detect device" >&2
|
||||||
|
echo "Unknown"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create temporary directory
|
||||||
|
create_tmp_dir() {
|
||||||
|
local prefix="${1:-rocknix-backup}"
|
||||||
|
local tmp_dir
|
||||||
|
tmp_dir=$(mktemp -d "/tmp/${prefix}-XXXXXX")
|
||||||
|
echo "$tmp_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up temporary directory
|
||||||
|
cleanup_tmp() {
|
||||||
|
local tmp_dir="$1"
|
||||||
|
if [[ -d "$tmp_dir" ]]; then
|
||||||
|
rm -rf "$tmp_dir"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get human-readable size
|
||||||
|
human_size() {
|
||||||
|
local bytes="$1"
|
||||||
|
if [[ $bytes -ge 1073741824 ]]; then
|
||||||
|
echo "$(echo "scale=1; $bytes/1073741824" | bc)G"
|
||||||
|
elif [[ $bytes -ge 1048576 ]]; then
|
||||||
|
echo "$(echo "scale=1; $bytes/1048576" | bc)M"
|
||||||
|
elif [[ $bytes -ge 1024 ]]; then
|
||||||
|
echo "$(echo "scale=1; $bytes/1024" | bc)K"
|
||||||
|
else
|
||||||
|
echo "${bytes}B"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Filter device-specific settings from file
|
||||||
|
filter_specific_settings() {
|
||||||
|
local input_file="$1"
|
||||||
|
local output_file="$2"
|
||||||
|
|
||||||
|
cp "$input_file" "$output_file"
|
||||||
|
|
||||||
|
for setting in "${GENERIC_SETTINGS_FILTER[@]}"; do
|
||||||
|
sed -i "/^${setting} = /d" "$output_file"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show contents of a backup file
|
||||||
|
show_backup_contents() {
|
||||||
|
local backup_file="$1"
|
||||||
|
|
||||||
|
if [[ ! -f "$backup_file" ]]; then
|
||||||
|
log ERROR "File not found: $backup_file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
log INFO "Backup contents:"
|
||||||
|
tar -tzf "$backup_file" | head -50
|
||||||
|
|
||||||
|
local total_files
|
||||||
|
total_files=$(tar -tzf "$backup_file" | wc -l)
|
||||||
|
local total_size
|
||||||
|
total_size=$(du -h "$backup_file" | cut -f1)
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}Total: ${total_files} archivos, ${total_size}${NC}"
|
||||||
|
}
|
||||||
393
restore-rocknix.sh
Executable file
393
restore-rocknix.sh
Executable file
@@ -0,0 +1,393 @@
|
|||||||
|
#!/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 ""
|
||||||
Reference in New Issue
Block a user