diff options
Diffstat (limited to 'sgs.sh')
| -rw-r--r-- | sgs.sh | 293 |
1 files changed, 293 insertions, 0 deletions
@@ -0,0 +1,293 @@ +#!/bin/bash + + +# Copyright 2024 Daniel Almeida Martins +# License GPL-3.0-or-later +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see <https://www.gnu.org/licenses/>. + + +# NOTE +# This script performs two main operations, backup and restore. +# - On backup, it archives and compresses the savegame files. These archives are +# stored in a directory defined below. +# - On restore, it restores the savegame files based on a backup archive. For +# added cautiousness, right before the restore operation, a backup of the +# current savegame files is performed to a temporary directory. Check the +# restore_savegame function for these implementation details. +# Backup archives are identified by their TAG, which is simply the date and time +# when they were created (using the ISO 8601 format YYYYMMDDThhmm). + +# NOTE +# The following variables control the core behaviour of the script: +# backup_folder : The folder where the backup archives are stored. +# backup_extension : File extension that determines the compression used. + +declare version=2.1 +declare backup_folder="${HOME}/Documents/savegame" +declare backup_extension="tzst" + +# NOTE +# Below is the list of supported games. Each game needs three entries: +# game_GAME : The display name of the game. +# include_GAME : The directory to be backed up. +# exclude_GAME : A list of files to be excluded from the backup. +# Each game must use a different GAME which should be an acronym since it will +# also be used from the command line when you want to reference a specific game. + +declare game_ash="A Short Hike" +declare include_ash="${HOME}/.config/unity3d/adamgryu/A Short Hike" +declare exclude_ash=() + +declare game_aermoo="AER Memories of Old" +declare include_aermoo="${HOME}/.local/share/Daedalic Entertainment GmbH/Aer" +declare exclude_aermoo=("./CLog.log" "config.ini") + +declare game_aor="Art of Rally" +declare include_aor="${HOME}/.config/unity3d/Funselektor Labs/art of rally/Save" +declare exclude_aor=() + +declare game_darwin="Darwinia" +declare include_darwin="${HOME}/.darwinia/full/users" +declare exclude_darwin=() + +declare game_emud="Dolphin Emulator" +declare include_emud="${HOME}/.local/share/dolphin-emu/GC" +declare exclude_emud=() + +declare game_epistory="Epistory" +declare include_epistory="${HOME}/.config/unity3d/Fishing Cactus/Epistory" +declare exclude_epistory=() + +declare game_exap="EXAPUNKS" +declare include_exap="${HOME}/.local/share/EXAPUNKS" +declare exclude_exap=("*/config.cfg" "*/log.dat") + +declare game_lcbbs="Last Call BBS" +declare include_lcbbs="${HOME}/.local/share/Last Call BBS" +declare exclude_lcbbs=("*/config.cfg" "*/log.dat") + +declare game_oxen="OXENFREE" +declare include_oxen="${HOME}/.config/unity3d/Night School Studio/Oxenfree/save" +declare exclude_oxen=() + +declare game_pa="Prison Architect" +declare include_pa="${HOME}/.Prison Architect/saves" +declare exclude_pa=() + +declare game_quake="Quake" +declare include_quake="${HOME}/.quakespasm" +declare exclude_quake=() + +declare game_q3a="Quake 3 Arena" +declare include_q3a="${HOME}/.q3a" +declare exclude_q3a=("*/*.pk3") + +declare game_rtmi="Return to Monkey Island" +declare include_rtmi="${HOME}/.local/share/Terrible Toybox/Return to Monkey Island" +declare exclude_rtmi=() + +declare game_sh="Super Hexagon" +declare include_sh="${HOME}/.local/share/SuperHexagon" +declare exclude_sh=() + +declare game_wfto="War for the Overworld" +declare include_wfto="${HOME}/Games/War for the Overworld/game/WFTOGame_Data/GameData" +declare exclude_wfto=("./Options.txt") + +declare game_yuzu="YUZU" +declare include_yuzu="${HOME}/Games/yuzu/files/data/.local/yuzu/nand" +declare exclude_yuzu=("*/Contents") + + +print_game_info() { + declare game=$1 + declare game_def="game_${game}" + declare -n include="include_${game}" + declare installed_status=$([ -d "${include}" ] && echo "[installed]" || echo "") + printf "%-24s\t%-8s\t%s\n" "${!game_def}" "${game}" "${installed_status}" + return +} + +list_savegames() { + declare game=$1 + declare savegames=(`ls "${backup_folder}" | grep "^${game}_" | sort`) + declare it + for it in ${savegames[@]}; do + declare basename=$(basename ${it}) + declare filename=${basename%%.*} + declare parts=(${filename//_/ }) + declare game=${parts[0]} + declare date=${parts[1]} + declare date_iso="${date:(0):4}-${date:(4):2}-${date:(6):2} ${date:(9):2}:${date:(11):2}:${date:(13):2}" + printf "%-24s\t%-8s\t%s\n" "${date_iso}" "${game}" "${date}" + done + return +} + +backup_savegame() { + declare game=$1 + declare tag=$2 + declare -n include="include_${game}" + declare -n exclude="exclude_${game}" + + # Check if game is installed. + if ! [ -d "${include}" ]; then + printf "${script}: backup not performed because '$game' is not installed.\n" + exit 1 + fi + + printf "Backing up '${game}' '${tag}'..." + tar --auto-compress --create --file "${backup_folder}/${game}_${tag}.${backup_extension}" "${exclude[@]/#/--exclude=}" --directory "${include}" . + printf " done.\n" + return +} + +restore_savegame() { + declare game=$1 + declare tag=$2 + declare -n include="include_${game}" + declare -n exclude="exclude_${game}" + declare date=`date +"%Y%m%dT%H%M%S"` + + # Check if game is installed. + if ! [ -d "${include}" ]; then + printf "${script}: restore not performed because '$game' is not installed.\n" + exit 1 + fi + + printf "Restoring '${game}' '${tag}'..." + + # Backup savegame directory before restoring. + declare savegame_backup=$(mktemp --tmpdir=/tmp "savegame.backup.${game}_${date}.XXXXXXXX.${backup_extension}") + tar --auto-compress --create --file "${savegame_backup}" "${exclude[@]/#/--exclude=}" --directory "${include}" . + + # Extract selected savegame. + tar --auto-compress --extract --file "${backup_folder}/${game}_${tag}.${backup_extension}" --directory "${include}" + printf " done.\n" + return +} + + +declare script=$(basename $0) +declare action=$1 +declare -a supported_games=(`compgen -v game_`) +supported_games=("${supported_games[@]#game_}") +mkdir -p "$backup_folder" +case "${action}" in + + # List available games or backups of game defined in argument. + "--list" | "-l") + declare game=$2 + if [ -z "$game" ]; then + for it in ${supported_games[@]}; do + print_game_info $it + done + elif ! [ -v "game_${game}" ]; then # Check if game is undefined. + echo "${script}: game '${game}' not found." + exit 22 + else + list_savegames $game + fi + ;; + + + # List backups for all games. + "--list-all" | "-L") +# declare -a supported_games=(`compgen -v game_`) + declare last_item="${supported_games[-1]#game_}" + + for it in ${supported_games[@]}; do + declare game="${it#game_}" + print_game_info $it + list_savegames $game + if [ "$game" != "$last_item" ]; then + printf "\n" + fi + done + ;; + + + # Backup save game. + "--backup" | "-b") + declare game=$2 + if ! [ -v "game_${game}" ]; then # Check if game is undefined. + echo "${script}: game '${game}' not found." + exit 22 + fi + declare tag=`date +"%Y%m%dT%H%M%S"` + backup_savegame $game $tag + ;; + + + # Restore save game. + "--restore" | "-r") + declare game=$2 + declare tag=$3 + if ! [ -v "game_${game}" ]; then # Check if game is undefined. + echo "${script}: game '${game}' not found." + exit 22 + fi + if [ -z "$tag" ]; then # Check if tag is undefined. + # Search for the latest backup. + declare savegames=(`ls "${backup_folder}" | grep "^${game}_" | sort`) + if [ ${#savegames[@]} -eq 0 ]; then + echo "${script}: no backups found." + exit 61 + fi + declare selected="${savegames[-1]}" + tag="${selected#${game}_}" # Substring removal: game name from front. + tag="${tag%%.*}" # Substring removal: file extension from back. + fi + restore_savegame $game $tag + ;; + + + # Show help. + "--help" | "-h") + printf -- "Usage: ${script} OPTION [GAME] [TAG]\n" + declare options=( + "-l, --list [GAME] " "List supported games. Provide GAME to list backup TAGs." + "-L, --list-all " "List all backup TAGs." + "-b, --backup GAME " "Backup GAME savegame data. Displays new backup TAG." + "-r, --restore GAME [TAG] " "Restore GAME savegame data. Omit TAG to use last backup." + "-h, --help " "Display this help and exit." + "-v, --version " "Output version information and exit." + ) + for ((idx=0; idx<${#options[@]}; idx+=2)); do + printf -- " %s\t%s\n" "${options[$idx]}" "${options[$idx+1]}" + done + printf "\nNotes\n" + printf -- "- Backup folder '%s'.\n" ${backup_folder} + printf -- "- Backup extension '%s.'\n" ${backup_extension} + ;; + + + # Show version. + "--version" | "-v") + echo "SaveGame Saver version ${version}" + echo "Copyright 2024 Daniel Martins" + echo "License GPL-3.0-or-later" + ;; + + + # Unknown option. + *) + echo "${script}: invalid option '${action}'." + echo "Try '${script} --help' for more information." + exit 22 + ;; + +esac + +exit 0 |
