diff --git a/config/steam-rom-manager/userConfigurations.json b/config/steam-rom-manager/userConfigurations.json index 8f3afbaf..eb0da29b 100644 --- a/config/steam-rom-manager/userConfigurations.json +++ b/config/steam-rom-manager/userConfigurations.json @@ -1,6 +1,6 @@ [ { - "parserType": "Glob", + "parserType": "Manual", "configTitle": "RetroDECK Steam Sync", "steamDirectory": "${steamdirglobal}", "romDirectory": "${romsdirglobal}", @@ -8,11 +8,9 @@ "RetroDECK" ], "executableArgs": "", - "executableModifier": "\"${exePath}\"", + "executableModifier": "", "startInDirectory": "", "titleModifier": "${fuzzyTitle}", - "fetchControllerTemplatesButton": null, - "removeControllersButton": null, "steamInputEnabled": "2", "imageProviders": [ "sgdb", @@ -29,7 +27,7 @@ ] }, "parserInputs": { - "glob": "${title}.sh" + "manualManifests": "${romsdirglobal}" }, "executable": { "path": "", @@ -108,7 +106,6 @@ ], "sizes": [], "sizesHero": [], - "sizesTall": null, "sizesIcon": [] } }, @@ -256,4 +253,4 @@ "parserId": "173908444383456337", "version": 25 } -] \ No newline at end of file +] diff --git a/functions/compression.sh b/functions/compression.sh index cf24c490..4482df47 100644 --- a/functions/compression.sh +++ b/functions/compression.sh @@ -2,7 +2,7 @@ compress_game() { # Function for compressing one or more files to .chd format - # USAGE: compress_game $format $full_path_to_input_file $system(optional) + # USAGE: compress_game $format $full_path_to_input_file $cleanup_choice $system(optional) local file="$2" local filename_no_path=$(basename "$file") local filename_no_extension="${filename_no_path%.*}" @@ -11,8 +11,9 @@ compress_game() { local dest_file=$(dirname "$(realpath "$file")")"/""$filename_no_extension" if [[ "$1" == "chd" ]]; then - case "$3" in # Check platform-specific compression options + case "$4" in # Check platform-specific compression options "psp" ) + log d "Compressing PSP game $source_file into $dest_file" /app/bin/chdman createdvd --hunksize 2048 -i "$source_file" -o "$dest_file".chd -c zstd ;; "ps2" ) @@ -32,7 +33,7 @@ compress_game() { dolphin-tool convert -f rvz -b 131072 -c zstd -l 5 -i "$source_file" -o "$dest_file.rvz" fi - if [[ $post_compression_cleanup == "true" ]]; then # Remove file(s) if requested + if [[ "$3" == "true" ]]; then # Remove file(s) if requested if [[ -f "${file%.*}.$1" ]]; then log i "Performing post-compression file cleanup" if [[ "$file" == *".cue" ]]; then @@ -149,13 +150,13 @@ find_compatible_games() { compression_format="$1" fi - local compressable_systems_list + local compressible_systems_list if [[ "$compression_format" == "all" ]]; then - compressable_systems_list=$(jq -r '.compression_targets | to_entries[] | .value[]' "$features") - log d "compressable_systems_list: $compressable_systems_list" + compressible_systems_list=$(jq -r '.compression_targets | to_entries[] | .value[]' "$features") + log d "compressible_systems_list: $compressible_systems_list" else - compressable_systems_list=$(jq -r '.compression_targets["'"$compression_format"'"][]' "$features") - log d "compressable_systems_list: $compressable_systems_list" + compressible_systems_list=$(jq -r '.compression_targets["'"$compression_format"'"][]' "$features") + log d "compressible_systems_list: $compressible_systems_list" fi log d "Finding compatible games for compression ($1)" @@ -163,59 +164,51 @@ find_compatible_games() { while IFS= read -r system; do log d "Checking system: $system" - local compression_candidates - compression_candidates=$(find "$roms_folder/$system" -type f -not -iname "*.txt") - if [[ -n "$compression_candidates" ]]; then - while IFS= read -r game; do - log d "Checking game: $game" - local compatible_compression_format - compatible_compression_format=$(find_compatible_compression_format "$game") - local file_ext="${game##*.}" - case "$compression_format" in - "chd") - if [[ "$compatible_compression_format" == "chd" ]]; then - if [[ "$file_ext" == "chd" ]]; then - log d "Skipping $game because it is already a CHD file." - elif [[ ! -f "${game%.*}.chd" ]]; then + if [[ -d "$roms_folder/$system" ]]; then + local compression_candidates + compression_candidates=$(find "$roms_folder/$system" -type f -not -iname "*.txt") + if [[ -n "$compression_candidates" ]]; then + while IFS= read -r game; do + log d "Checking game: $game" + local compatible_compression_format + compatible_compression_format=$(find_compatible_compression_format "$game") + if [[ -f "${game%.*}.$compatible_compression_format" ]]; then # If a compressed version of this game already exists + log d "Skipping $game because a $compatible_compression_format version already exists." + continue + fi + local file_ext="${game##*.}" + case "$compression_format" in + "chd") + if [[ "$compatible_compression_format" == "chd" ]]; then log d "Game $game is compatible with CHD compression" echo "${game}^chd" >> "$output_file" fi - fi - ;; - "zip") - if [[ "$compatible_compression_format" == "zip" ]]; then - if [[ "$file_ext" == "zip" ]]; then - log d "Skipping $game because it is already a ZIP file." - elif [[ ! -f "${game%.*}.zip" ]]; then + ;; + "zip") + if [[ "$compatible_compression_format" == "zip" ]]; then log d "Game $game is compatible with ZIP compression" echo "${game}^zip" >> "$output_file" fi - fi - ;; - "rvz") - if [[ "$compatible_compression_format" == "rvz" ]]; then - if [[ "$file_ext" == "rvz" ]]; then - log d "Skipping $game because it is already an RVZ file." - elif [[ ! -f "${game%.*}.rvz" ]]; then + ;; + "rvz") + if [[ "$compatible_compression_format" == "rvz" ]]; then log d "Game $game is compatible with RVZ compression" echo "${game}^rvz" >> "$output_file" fi - fi - ;; - "all") - if [[ "$compatible_compression_format" != "none" ]]; then - if [[ "$file_ext" == "$compatible_compression_format" ]]; then - log d "Skipping $game because it is already in $compatible_compression_format format." - else + ;; + "all") + if [[ "$compatible_compression_format" != "none" ]]; then log d "Game $game is compatible with $compatible_compression_format compression" echo "${game}^${compatible_compression_format}" >> "$output_file" fi - fi - ;; - esac - done < <(printf '%s\n' "$compression_candidates") + ;; + esac + done < <(printf '%s\n' "$compression_candidates") + fi + else + log d "Rom folder for $system is missing, skipping" fi - done < <(printf '%s\n' "$compressable_systems_list") + done < <(printf '%s\n' "$compressible_systems_list") log d "Compatible games have been written to $output_file" cat "$output_file" @@ -226,23 +219,32 @@ cli_compress_single_game() { # USAGE: cli_compress_single_game $full_file_path local file=$(realpath "$1") read -p "Do you want to have the original file removed after compression is complete? Please answer y/n and press Enter: " post_compression_cleanup - read -p "RetroDECK will now attempt to compress your selected game. Press Enter key to continue..." - if [[ ! -z "$file" ]]; then - if [[ -f "$file" ]]; then - local system=$(echo "$file" | grep -oE "$roms_folder/[^/]+" | grep -oE "[^/]+$") - local compatible_compression_format=$(find_compatible_compression_format "$file") - if [[ ! $compatible_compression_format == "none" ]]; then - log i "$(basename "$file") can be compressed to $compatible_compression_format" - compress_game "$compatible_compression_format" "$file" "$system" + if [[ "$post_compression_cleanup" == "y" || "$post_compression_cleanup" == "n" ]]; then + read -p "RetroDECK will now attempt to compress your selected game. Press Enter key to continue..." + if [[ ! -z "$file" ]]; then + if [[ -f "$file" ]]; then + local system=$(echo "$file" | grep -oE "$roms_folder/[^/]+" | grep -oE "[^/]+$") + local compatible_compression_format=$(find_compatible_compression_format "$file") + if [[ ! $compatible_compression_format == "none" ]]; then + log i "$(basename "$file") can be compressed to $compatible_compression_format" + if [[ "$post_compression_cleanup" == "y" ]]; then + post_compression_cleanup="true" + else + post_compression_cleanup="false" + fi + compress_game "$compatible_compression_format" "$file" "$post_compression_cleanup" "$system" + else + log w "$(basename "$file") does not have any compatible compression formats." + fi else - log w "$(basename "$file") does not have any compatible compression formats." + log w "File not found, please specify the full path to the file to be compressed." fi - else - log w "File not found, please specify the full path to the file to be compressed." - fi - else - log i "Please use this command format \"--compress-one \"" - fi + else + log i "Please use this command format \"--compress-one \"" + fi + else + log i "The response for post-compression file cleanup was not correct. Please try again." + fi } cli_compress_all_games() { @@ -257,31 +259,39 @@ cli_compress_all_games() { local compressable_game="" local all_compressable_games=() if [[ $compression_format == "all" ]]; then - local compressable_systems_list=$(jq -r '.compression_targets | to_entries[] | .value[]' $features) + local compressible_systems_list=$(jq -r '.compression_targets | to_entries[] | .value[]' $features) else - local compressable_systems_list=$(jq -r '.compression_targets["'"$compression_format"'"][]' $features) + local compressible_systems_list=$(jq -r '.compression_targets["'"$compression_format"'"][]' $features) fi read -p "Do you want to have the original files removed after compression is complete? Please answer y/n and press Enter: " post_compression_cleanup - read -p "RetroDECK will now attempt to compress all compatible games. Press Enter key to continue..." - - while IFS= read -r system # Find and validate all games that are able to be compressed with this compression type - do - local compression_candidates=$(find "$roms_folder/$system" -type f -not -iname "*.txt") - if [[ ! -z "$compression_candidates" ]]; then - log i "Checking files for $system" - while IFS= read -r file - do - local compatible_compression_format=$(find_compatible_compression_format "$file") - if [[ ! "$compatible_compression_format" == "none" ]]; then - log i "$(basename "$file") can be compressed to $compatible_compression_format" - compress_game "$compatible_compression_format" "$file" "$system" - else - log w "No compatible compression format found for $(basename "$file")" - fi - done < <(printf '%s\n' "$compression_candidates") + if [[ "$post_compression_cleanup" == "y" || "$post_compression_cleanup" == "n" ]]; then + read -p "RetroDECK will now attempt to compress all compatible games. Press Enter key to continue..." + if [[ "$post_compression_cleanup" == "y" ]]; then + post_compression_cleanup="true" else - log w "No compatible files found for compression in $system" + post_compression_cleanup="false" fi - done < <(printf '%s\n' "$compressable_systems_list") + while IFS= read -r system # Find and validate all games that are able to be compressed with this compression type + do + local compression_candidates=$(find "$roms_folder/$system" -type f -not -iname "*.txt") + if [[ ! -z "$compression_candidates" ]]; then + log i "Checking files for $system" + while IFS= read -r file + do + local compatible_compression_format=$(find_compatible_compression_format "$file") + if [[ ! "$compatible_compression_format" == "none" ]]; then + log i "$(basename "$file") can be compressed to $compatible_compression_format" + compress_game "$compatible_compression_format" "$file" "$post_compression_cleanup" "$system" + else + log w "No compatible compression format found for $(basename "$file")" + fi + done < <(printf '%s\n' "$compression_candidates") + else + log w "No compatible files found for compression in $system" + fi + done < <(printf '%s\n' "$compressible_systems_list") + else + log i "The response for post-compression file cleanup was not correct. Please try again." + fi } diff --git a/functions/configurator_functions.sh b/functions/configurator_functions.sh index acf7ff5a..78dd78bb 100644 --- a/functions/configurator_functions.sh +++ b/functions/configurator_functions.sh @@ -23,14 +23,14 @@ find_empty_rom_folders() { if [[ $count -eq 0 ]]; then # Directory is empty - empty_rom_folders_list=("${empty_rom_folders_list[@]}" "false" "$(realpath $dir)") - all_empty_folders=("${all_empty_folders[@]}" "$(realpath $dir)") - echo "$(realpath $dir)" >> "$godot_empty_roms_folders" # Godot data transfer temp file + empty_rom_folders_list=("${empty_rom_folders_list[@]}" "false" "$(realpath "$dir")") + all_empty_folders=("${all_empty_folders[@]}" "$(realpath "$dir")") + echo "$(realpath "$dir")" >> "$godot_empty_roms_folders" # Godot data transfer temp file elif [[ $count -eq 1 ]] && [[ "$(basename "${files[0]}")" == "systeminfo.txt" ]]; then # Directory contains only systeminfo.txt - empty_rom_folders_list=("${empty_rom_folders_list[@]}" "false" "$(realpath $dir)") - all_empty_folders=("${all_empty_folders[@]}" "$(realpath $dir)") - echo "$(realpath $dir)" >> "$godot_empty_roms_folders" # Godot data transfer temp file + empty_rom_folders_list=("${empty_rom_folders_list[@]}" "false" "$(realpath "$dir")") + all_empty_folders=("${all_empty_folders[@]}" "$(realpath "$dir")") + echo "$(realpath "$dir")" >> "$godot_empty_roms_folders" # Godot data transfer temp file elif [[ $count -eq 2 ]] && [[ "$files" =~ "systeminfo.txt" ]]; then contains_helper_file="false" for helper_file in "${all_helper_files[@]}" # Compare helper file list to dir file list @@ -42,9 +42,9 @@ find_empty_rom_folders() { done if [[ "$contains_helper_file" == "true" ]]; then # Directory contains only systeminfo.txt and a helper file - empty_rom_folders_list=("${empty_rom_folders_list[@]}" "false" "$(realpath $dir)") - all_empty_folders=("${all_empty_folders[@]}" "$(realpath $dir)") - echo "$(realpath $dir)" >> "$godot_empty_roms_folders" # Godot data transfer temp file + empty_rom_folders_list=("${empty_rom_folders_list[@]}" "false" "$(realpath "$dir")") + all_empty_folders=("${all_empty_folders[@]}" "$(realpath "$dir")") + echo "$(realpath "$dir")" >> "$godot_empty_roms_folders" # Godot data transfer temp file fi fi done @@ -61,5 +61,4 @@ configurator_check_multifile_game_structure() { else configurator_generic_dialog "RetroDECK Configurator - Verify Multi-file Structure" "No incorrect multi-file game folder structures found." fi - configurator_welcome_dialog } diff --git a/functions/dialogs.sh b/functions/dialogs.sh index 23ca4433..6e1a5670 100644 --- a/functions/dialogs.sh +++ b/functions/dialogs.sh @@ -92,11 +92,11 @@ configurator_move_folder_dialog() { if [[ ! "$rd_dir_name" == "rdhome" ]]; then # If a sub-folder is being moved, find it's path without the source_root. So /home/deck/retrodeck/roms becomes retrodeck/roms local rd_dir_path="$(echo "$dir_to_move" | sed "s/.*\(retrodeck\/.*\)/\1/; s/\/$//")" else # Otherwise just set the retrodeck root folder - local rd_dir_path="$(basename $dir_to_move)" + local rd_dir_path="$(basename "$dir_to_move")" fi if [[ -d "$dir_to_move" ]]; then # If the directory selected to move already exists at the expected location pulled from retrodeck.cfg - choice=$(configurator_destination_choice_dialog "RetroDECK Data" "Please choose a destination for the $(basename $dir_to_move) folder.") + choice=$(configurator_destination_choice_dialog "RetroDECK Data" "Please choose a destination for the $(basename "$dir_to_move") folder.") case $choice in "Internal Storage" | "SD Card" | "Custom Location" ) # If the user picks a location @@ -105,17 +105,17 @@ configurator_move_folder_dialog() { elif [[ "$choice" == "SD Card" ]]; then # If the user wants to move the folder to the predefined SD card location, set the target as sdcard from retrodeck.cfg local dest_root="$sdcard" else - configurator_generic_dialog "RetroDECK Configurator - Move Folder" "Select the parent folder you would like to store the $(basename $dir_to_move) folder in." + configurator_generic_dialog "RetroDECK Configurator - Move Folder" "Select the parent folder you would like to store the $(basename "$dir_to_move") folder in." local dest_root=$(directory_browse "RetroDECK directory location") # Set the destination root as the selected custom location fi if [[ (! -z "$dest_root") && ( -w "$dest_root") ]]; then # If user picked a destination and it is writable - if [[ (-d "$dest_root/$rd_dir_path") && (! -L "$dest_root/$rd_dir_path") && (! $rd_dir_name == "rdhome") ]] || [[ "$(realpath $dir_to_move)" == "$dest_root/$rd_dir_path" ]]; then # If the user is trying to move the folder to where it already is (excluding symlinks that will be unlinked) - configurator_generic_dialog "RetroDECK Configurator - Move Folder" "The $(basename $dir_to_move) folder is already at that location, please pick a new one." + if [[ (-d "$dest_root/$rd_dir_path") && (! -L "$dest_root/$rd_dir_path") && (! $rd_dir_name == "rdhome") ]] || [[ "$(realpath "$dir_to_move")" == "$dest_root/$rd_dir_path" ]]; then # If the user is trying to move the folder to where it already is (excluding symlinks that will be unlinked) + configurator_generic_dialog "RetroDECK Configurator - Move Folder" "The $(basename "$dir_to_move") folder is already at that location, please pick a new one." configurator_move_folder_dialog "$rd_dir_name" else if [[ $(verify_space "$(echo $dir_to_move | sed 's/\/$//')" "$dest_root") ]]; then # Make sure there is enough space at the destination - configurator_generic_dialog "RetroDECK Configurator - Move Folder" "Moving $(basename $dir_to_move) folder to $choice" + configurator_generic_dialog "RetroDECK Configurator - Move Folder" "Moving $(basename "$dir_to_move") folder to $choice" unlink "$dest_root/$rd_dir_path" # In case there is already a symlink at the picked destination move "$dir_to_move" "$dest_root/$rd_dir_path" if [[ -d "$dest_root/$rd_dir_path" ]]; then # If the move succeeded @@ -128,7 +128,7 @@ configurator_move_folder_dialog() { if [[ -z $(ls -1 "$source_root/retrodeck") ]]; then # Cleanup empty old_path/retrodeck folder if it was left behind rmdir "$source_root/retrodeck" fi - configurator_process_complete_dialog "moving the RetroDECK data directory to internal storage" + configurator_generic_dialog "RetroDECK Configurator - Move Folder" "moving the RetroDECK data directory to internal storage" else configurator_generic_dialog "RetroDECK Configurator - Move Folder" "The moving process was not completed, please try again." fi @@ -150,12 +150,12 @@ configurator_move_folder_dialog() { esac else # The folder to move was not found at the path pulled from retrodeck.cfg and it needs to be reconfigured manually. - configurator_generic_dialog "RetroDECK Configurator - Move Folder" "The $(basename $dir_to_move) folder was not found at the expected location.\n\nThis may have happened if the folder was moved manually.\n\nPlease select the current location of the folder." - dir_to_move=$(directory_browse "RetroDECK $(basename $dir_to_move) directory location") + configurator_generic_dialog "RetroDECK Configurator - Move Folder" "The $(basename "$dir_to_move") folder was not found at the expected location.\n\nThis may have happened if the folder was moved manually.\n\nPlease select the current location of the folder." + dir_to_move=$(directory_browse "RetroDECK $(basename "$dir_to_move") directory location") declare -g "$rd_dir_name=$dir_to_move" prepare_component "postmove" "all" conf_write - configurator_generic_dialog "RetroDECK Configurator - Move Folder" "RetroDECK $(basename $dir_to_move) folder now configured at\n$dir_to_move." + configurator_generic_dialog "RetroDECK Configurator - Move Folder" "RetroDECK $(basename "$dir_to_move") folder now configured at\n$dir_to_move." configurator_move_folder_dialog "$rd_dir_name" fi } diff --git a/functions/global.sh b/functions/global.sh index a1aee4b8..8948b352 100644 --- a/functions/global.sh +++ b/functions/global.sh @@ -8,6 +8,11 @@ : "${logging_level:=info}" # Initializing the log level variable if not already valued, this will be actually red later from the config file rd_logs_folder="/var/config/retrodeck/logs" # Static location to write all RetroDECK-related logs +if [ -h "$rd_logs_folder" ]; then # Check if internal logging folder is already a symlink + if [ ! -e "$rd_logs_folder" ]; then # Check if internal logging folder symlink is broken + unlink "$rd_logs_folder" # Remove broken symlink so the folder is recreated when sourcing logger.sh + fi +fi source /app/libexec/logger.sh rotate_logs @@ -47,6 +52,7 @@ source /app/libexec/prepare_component.sh source /app/libexec/presets.sh source /app/libexec/configurator_functions.sh source /app/libexec/run_game.sh +source /app/libexec/steam_sync.sh # Static variables rd_conf="/var/config/retrodeck/retrodeck.cfg" # RetroDECK config file path @@ -236,14 +242,13 @@ else # Verify rdhome is where it is supposed to be. if [[ ! -d "$rdhome" ]]; then - prev_home_path="$rdhome" configurator_generic_dialog "RetroDECK Setup" "The RetroDECK data folder was not found in the expected location.\nThis may happen when SteamOS is updated.\n\nPlease browse to the current location of the \"retrodeck\" folder." new_home_path=$(directory_browse "RetroDECK folder location") set_setting_value $rd_conf "rdhome" "$new_home_path" retrodeck "paths" conf_read #tmplog_merger # This function is tempry(?) removed - prepare_component "retrodeck" "postmove" - prepare_component "all" "postmove" + prepare_component "postmove" "retrodeck" + prepare_component "postmove" "all" conf_write fi @@ -252,9 +257,11 @@ else multi_user_data_folder="$rdhome/multi-user-data" # The default location of multi-user environment profiles fi -logs_folder="$rdhome/logs" # The path of the logs folder, here we collect all the logs -steamsync_folder="$rdhome/.sync" # Folder containing all the steam sync launchers for SRM -steamsync_folder_tmp="$rdhome/.sync-tmp" # Temp folder containing all the steam sync launchers for SRM -backups_folder="$rdhome/backups" # Folder containing all the RetroDECK backups +# Steam ROM Manager user files and paths + +steamsync_folder="$rdhome/.sync" # Folder containing favorites manifest for SRM +retrodeck_favorites_file="$steamsync_folder/retrodeck_favorites.json" # The current SRM manifest of all games that have been favorited in ES-DE +srm_log="$logs_folder/srm_log.log" # Log file for capturing the output of the most recent SRM run, for debugging purposes +retrodeck_removed_favorites="$steamsync_folder/retrodeck_removed_favorites.json" # Temporary manifest of any games that were unfavorited in ES-DE and should be removed from Steam export GLOBAL_SOURCED=true diff --git a/functions/other_functions.sh b/functions/other_functions.sh index d78f30ec..f1878e27 100644 --- a/functions/other_functions.sh +++ b/functions/other_functions.sh @@ -273,8 +273,8 @@ dir_prep() { # Call me with: # dir prep "real dir" "symlink location" - real="$(realpath -s $1)" - symlink="$(realpath -s $2)" + real="$(realpath -s "$1")" + symlink="$(realpath -s "$2")" log d "Preparing directory $symlink in $real" @@ -354,8 +354,288 @@ update_vita3k_firmware() { } backup_retrodeck_userdata() { + # This function can compress one or more RetroDECK userdata folders into a single zip file for backup. + # The function can do a "complete" backup of all userdata including ROMs and ES-DE media, so can end up being very large. + # The function can also do a "core" backup of all the very important userdata files (like saves, states and gamelists) or a "custom" backup of only specified paths + # The function can take both folder names as defined in retrodeck.cfg or full paths as arguments for folders to backup + # It will also validate that all the provided paths exist and that there is enough free space to perform the backup before actually proceeding. + # It will also rotate backups so that there are only 3 maximum of each type (standard or custom) + # USAGE: backup_retrodeck_userdata complete + # backup_retrodeck_userdata core + # backup_retrodeck_userdata custom saves_folder states_folder /some/other/path + create_dir "$backups_folder" - zip -rq9 "$backups_folder/$(date +"%0m%0d")_retrodeck_userdata.zip" "$saves_folder" "$states_folder" "$bios_folder" "$media_folder" "$themes_folder" "$rdhome/ES-DE/collections" "$rdhome/ES-DE/gamelists" "$logs_folder" "$screenshots_folder" "$mods_folder" "$texture_packs_folder" "$borders_folder" > $logs_folder/$(date +"%0m%0d")_backup_log.log + + backup_date=$(date +"%0m%0d_%H%M") + backup_log_file="$logs_folder/${backup_date}_${backup_type}_backup_log.log" + + # Check if first argument is the type + if [[ "$1" == "complete" || "$1" == "core" || "$1" == "custom" ]]; then + backup_type="$1" + shift # Remove the first argument + else + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "No valid backup option chosen. Valid options are and ." + fi + log e "No valid backup option chosen. Valid options are and ." + exit 1 + fi + + zip_file="$backups_folder/retrodeck_${backup_date}_${backup_type}.zip" + + # Initialize paths arrays + paths_to_backup=() + declare -A config_paths # Requires an associative (dictionary) array to work + + # Build array of folder names and real paths from retrodeck.cfg + while read -r config_line; do + local current_setting_name=$(get_setting_name "$config_line" "retrodeck") + if [[ ! $current_setting_name =~ (rdhome|sdcard|backups_folder) ]]; then # Ignore these locations + local current_setting_value=$(get_setting_value "$rd_conf" "$current_setting_name" "retrodeck" "paths") + config_paths["$current_setting_name"]="$current_setting_value" + fi + done < <(grep -v '^\s*$' $rd_conf | awk '/^\[paths\]/{f=1;next} /^\[/{f=0} f') + + # Determine which paths to backup + if [[ "$backup_type" == "complete" ]]; then + for folder_name in "${!config_paths[@]}"; do + path_value="${config_paths[$folder_name]}" + if [[ -e "$path_value" ]]; then + paths_to_backup+=("$path_value") + log i "Adding to backup: $folder_name = $path_value" + else + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "The $folder_name was not found at its expected location, $path_value\nSomething may be wrong with your RetroDECK installation." + fi + log i "Warning: Path does not exist: $folder_name = $path_value" + fi + done + + # Add static paths not defined in retrodeck.cfg + if [[ -e "$rdhome/ES-DE/collections" ]]; then + paths_to_backup+=("$rdhome/ES-DE/collections") + else + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "The ES-DE collections folder was not found at its expected location, $rdhome/ES-DE/collections\nSomething may be wrong with your RetroDECK installation." + fi + log i "Warning: Path does not exist: ES-DE/collections = $rdhome/ES-DE/collections" + fi + + if [[ -e "$rdhome/ES-DE/gamelists" ]]; then + paths_to_backup+=("$rdhome/ES-DE/gamelists") + else + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "The ES-DE gamelists folder was not found at its expected location, $rdhome/ES-DE/gamelists\nSomething may be wrong with your RetroDECK installation." + fi + log i "Warning: Path does not exist: ES-DE/gamelists = $rdhome/ES-DE/gamelists" + fi + + if [[ -e "$rdhome/ES-DE/custom_systems" ]]; then + paths_to_backup+=("$rdhome/ES-DE/custom_systems") + else + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "The ES-DE custom_systems folder was not found at its expected location, $rdhome/ES-DE/custom_systems\nSomething may be wrong with your RetroDECK installation." + fi + log i "Warning: Path does not exist: ES-DE/custom_systems = $rdhome/ES-DE/custom_systems" + fi + + # Check if we found any valid paths + if [[ ${#paths_to_backup[@]} -eq 0 ]]; then + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "No valid userdata folders were found.\nSomething may be wrong with your RetroDECK installation." + fi + log e "Error: No valid paths found in config file" + return 1 + fi + + elif [[ "$backup_type" == "core" ]]; then + for folder_name in "${!config_paths[@]}"; do + if [[ $folder_name =~ (saves_folder|states_folder|logs_folder) ]]; then # Only include these paths + path_value="${config_paths[$folder_name]}" + if [[ -e "$path_value" ]]; then + paths_to_backup+=("$path_value") + log i "Adding to backup: $folder_name = $path_value" + else + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "The $folder_name was not found at its expected location, $path_value\nSomething may be wrong with your RetroDECK installation." + fi + log i "Warning: Path does not exist: $folder_name = $path_value" + fi + fi + done + + # Add static paths not defined in retrodeck.cfg + if [[ -e "$rdhome/ES-DE/collections" ]]; then + paths_to_backup+=("$rdhome/ES-DE/collections") + else + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "The ES-DE collections folder was not found at its expected location, $rdhome/ES-DE/collections\nSomething may be wrong with your RetroDECK installation." + fi + log i "Warning: Path does not exist: ES-DE/collections = $rdhome/ES-DE/collections" + fi + + if [[ -e "$rdhome/ES-DE/gamelists" ]]; then + paths_to_backup+=("$rdhome/ES-DE/gamelists") + else + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "The ES-DE gamelists folder was not found at its expected location, $rdhome/ES-DE/gamelists\nSomething may be wrong with your RetroDECK installation." + fi + log i "Warning: Path does not exist: ES-DE/gamelists = $rdhome/ES-DE/gamelists" + fi + + if [[ -e "$rdhome/ES-DE/custom_systems" ]]; then + paths_to_backup+=("$rdhome/ES-DE/custom_systems") + else + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "The ES-DE custom_systems folder was not found at its expected location, $rdhome/ES-DE/custom_systems\nSomething may be wrong with your RetroDECK installation." + fi + log i "Warning: Path does not exist: ES-DE/custom_systems = $rdhome/ES-DE/custom_systems" + fi + + # Check if we found any valid paths + if [[ ${#paths_to_backup[@]} -eq 0 ]]; then + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "No valid userdata folders were found.\nSomething may be wrong with your RetroDECK installation." + fi + log e "Error: No valid paths found in config file" + return 1 + fi + + elif [[ "$backup_type" == "custom" ]]; then + if [[ "$#" -eq 0 ]]; then # Check if any paths were provided in the arguments + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "No valid backup locations were specified. Please try again." + fi + log e "Error: No paths specified for custom backup" + return 1 + fi + + # Process each argument - it could be a variable name or a direct path + for arg in "$@"; do + # Check if argument is a variable name in the config + if [[ -n "${config_paths[$arg]}" ]]; then + path_value="${config_paths[$arg]}" + if [[ -e "$path_value" ]]; then + paths_to_backup+=("$path_value") + log i "Added to backup: $arg = $path_value" + else + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "The $arg was not found at its expected location, $path_value.\nSomething may be wrong with your RetroDECK installation." + fi + log e "Error: Path from variable '$arg' does not exist: $path_value" + return 1 + fi + # Otherwise treat it as a direct path + elif [[ -e "$arg" ]]; then + paths_to_backup+=("$arg") + log i "Added to backup: $arg" + else + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "The path $arg was not found at its expected location.\nPlease check the path and try again." + fi + log e "Error: '$arg' is neither a valid variable name nor an existing path" + return 1 + fi + done + fi + + # Calculate total size of selected paths + log i "Calculating size of backup data..." + + total_size=0 + + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then # Show progress dialog if running Zenity Configurator + total_size_file=$(mktemp) # Create temp file for Zenity subshell data extraction + ( + for path in "${paths_to_backup[@]}"; do + if [[ -e "$path" ]]; then + log d "Checking size of path $path" + path_size=$(du -sk "$path" 2>/dev/null | cut -f1) # Get size in KB + path_size=$((path_size * 1024)) # Convert to bytes for calculation + total_size=$((total_size + path_size)) + echo "$total_size" > $total_size_file + fi + done + ) | + rd_zenity --icon-name=net.retrodeck.retrodeck --progress --no-cancel --pulsate --auto-close \ + --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ + --title "RetroDECK Configurator Utility - Userdata Backup" \ + --text="Verifying there is enough free space for the backup, please wait..." + total_size=$(cat "$total_size_file") + rm "$total_size_file" # Clean up temp file + else # If running in CLI + for path in "${paths_to_backup[@]}"; do + if [[ -e "$path" ]]; then + log d "Checking size of path $path" + path_size=$(du -sk "$path" 2>/dev/null | cut -f1) # Get size in KB + path_size=$((path_size * 1024)) # Convert to bytes for calculation + total_size=$((total_size + path_size)) + fi + done + fi + + # Get available space at destination + available_space=$(df -B1 "$backups_folder" | awk 'NR==2 {print $4}') + + # Log sizes for reference + log i "Total size of backup data: $(numfmt --to=iec-i --suffix=B $total_size)" + log i "Available space at destination: $(numfmt --to=iec-i --suffix=B $available_space)" + + # Check if we have enough space (using uncompressed size as a conservative estimate) + if [[ "$available_space" -lt "$total_size" ]]; then + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + configurator_generic_dialog "RetroDECK Userdata Backup" "There is not enough free space to perform this backup.\n\nYou need at least $(numfmt --to=iec-i --suffix=B $total_size),\nplease free up some space and try again." + fi + log e "Error: Not enough space to perform backup. Need at least $(numfmt --to=iec-i --suffix=B $total_size)" + return 1 + fi + + log i "Starting backup process..." + + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then # Show progress dialog if running Zenity Configurator + ( + # Create zip with selected paths + if zip -rq9 "$zip_file" "${paths_to_backup[@]}" >> "$backup_log_file" 2>&1; then + # Rotate backups for the specific type + cd "$backups_folder" || return 1 + ls -t *_${backup_type}.zip | tail -n +4 | xargs -r rm + + final_size=$(du -h "$zip_file" | cut -f1) + configurator_generic_dialog "RetroDECK Userdata Backup" "The backup to $zip_file was successful, final size is $final_size.\n\nThe backups have been rotated, keeping the last 3 of the $backup_type backup type." + log i "Backup completed successfully: $zip_file (Size: $final_size)" + log i "Older backups rotated, keeping latest 3 of type $backup_type" + + if [[ ! -s "$backup_log_file" ]]; then # If the backup log file is empty, meaning zip threw no errors + rm -f "$backup_log_file" + fi + else + configurator_generic_dialog "RetroDECK Userdata Backup" "Something went wrong with the backup process. Please check the log $backup_log_file for more information." + log i "Error: Backup failed" + return 1 + fi + ) | + rd_zenity --icon-name=net.retrodeck.retrodeck --progress --no-cancel --pulsate --auto-close \ + --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ + --title "RetroDECK Configurator Utility - Userdata Backup" \ + --text="Compressing files into backup, please wait..." + else + if zip -rq9 "$zip_file" "${paths_to_backup[@]}" >> "$backup_log_file" 2>&1; then + # Rotate backups for the specific type + cd "$backups_folder" || return 1 + ls -t *_${backup_type}.zip | tail -n +4 | xargs -r rm + + final_size=$(du -h "$zip_file" | cut -f1) + log i "Backup completed successfully: $zip_file (Size: $final_size)" + log i "Older backups rotated, keeping latest 3 of type $backup_type" + + if [[ ! -s "$backup_log_file" ]]; then # If the backup log file is empty, meaning zip threw no errors + rm -f "$backup_log_file" + fi + else + log i "Error: Backup failed" + return 1 + fi + fi } make_name_pretty() { @@ -962,22 +1242,8 @@ quit_retrodeck() { pkill -f "es-de" # if steam sync is on do the magic - if [[ $(get_setting_value "$rd_conf" "steam_sync" retrodeck "options") == "true" ]]; then - ( - source /app/libexec/steam_sync.sh - add_to_steam "$(ls "$rdhome/ES-DE/gamelists/")" - ) | - rd_zenity --progress \ - --title="Syncing with Steam" \ - --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ - --text="\t\t\t\tSyncing favorite games with Steam\n\nNOTE: This operation may take some time depending on the size of your library.\nFeel free to leave this in the background and switch to another application.\n\n" \ - --percentage=25 \ - --pulsate \ - --width=500 \ - --height=150 \ - --auto-close \ - --auto-kill \ - --no-cancel + if [[ $(get_setting_value "$rd_conf" "steam_sync" "retrodeck" "options") == "true" ]]; then + steam_sync fi log i "Shutting down RetroDECK's framework" pkill -f "retrodeck" @@ -1128,3 +1394,58 @@ add_retrodeck_to_steam(){ log i "RetroDECK has been added to Steam" } + +repair_paths() { + # This function will verify that all folders defined in the [paths] section of retrodeck.cfg exist + # If a folder doesn't exist and is defined outside of rdhome, it will check in rdhome first and have the user browse for them manually if it isn't there either + # USAGE: repair_paths + + invalid_path_found="false" + + log i "Checking that all RetroDECK paths are valid" + while read -r config_line; do + local current_setting_name=$(get_setting_name "$config_line" "retrodeck") + if [[ ! $current_setting_name =~ (rdhome|sdcard) ]]; then # Ignore these locations + local current_setting_value=$(get_setting_value "$rd_conf" "$current_setting_name" "retrodeck" "paths") + if [[ ! -d "$current_setting_value" ]]; then # If the folder doesn't exist as defined + log i "$current_setting_name does not exist as defined, config is incorrect" + if [[ ! -d "$rdhome/${current_setting_value#*retrodeck/}" ]]; then # If the folder doesn't exist within defined rdhome path + if [[ ! -d "$sdcard/${current_setting_value#*retrodeck/}" ]]; then # If the folder doesn't exist within defined sdcard path + log i "$current_setting_name cannot be found at any expected location, having user locate it manually" + configurator_generic_dialog "RetroDECK Path Repair" "The RetroDECK $current_setting_name was not found in the expected location.\nThis may happen when the folder is moved manually.\n\nPlease browse to the current location of the $current_setting_name." + new_path=$(directory_browse "RetroDECK $current_setting_name location") + set_setting_value "$rd_conf" "$current_setting_name" "$new_path" retrodeck "paths" + invalid_path_found="true" + else # Folder does exist within defined sdcard path, update accordingly + log i "$current_setting_name found in $sdcard/retrodeck, correcting path config" + new_path="$sdcard/retrodeck/${current_setting_value#*retrodeck/}" + set_setting_value "$rd_conf" "$current_setting_name" "$new_path" retrodeck "paths" + invalid_path_found="true" + fi + else # Folder does exist within defined rdhome path, update accordingly + log i "$current_setting_name found in $rdhome, correcting path config" + new_path="$rdhome/${current_setting_value#*retrodeck/}" + set_setting_value "$rd_conf" "$current_setting_name" "$new_path" retrodeck "paths" + invalid_path_found="true" + fi + fi + fi + done < <(grep -v '^\s*$' $rd_conf | awk '/^\[paths\]/{f=1;next} /^\[/{f=0} f') + + if [[ $invalid_path_found == "true" ]]; then + log i "One or more invalid paths repaired, fixing internal RetroDECK structures" + conf_read + dir_prep "$logs_folder" "$rd_logs_folder" + prepare_component "postmove" "all" + configurator_generic_dialog "RetroDECK Path Repair" "One or more incorrectly configured paths were repaired." + else + log i "All folders were found at their expected locations" + configurator_generic_dialog "RetroDECK Path Repair" "All RetroDECK folders were found at their expected locations." + fi +} + +# Function to sanitize strings for filenames +sanitize() { + # Replace sequences of underscores with a single space + echo "$1" | sed -e 's/_\{2,\}/ /g' -e 's/_/ /g' -e 's/:/ -/g' -e 's/&/and/g' -e 's%/%and%g' -e 's/ / /g' +} diff --git a/functions/post_update.sh b/functions/post_update.sh index 7d6f8a46..edd583c9 100644 --- a/functions/post_update.sh +++ b/functions/post_update.sh @@ -9,6 +9,58 @@ post_update() { update_rd_conf + export CONFIGURATOR_GUI="zenity" + + # Optional userdata backup prior to update + + choice=$(rd_zenity --title "RetroDECK Update - Backup Userdata" --info --no-wrap --ok-label="No Backup" --extra-button"Backup Core Userdata" --extra-button="Backup Some Userdata" --extra-button="Backup All Userdata" \ + --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" --text="Would you like to backup some or all of the RetroDECK userdata prior to update?\n\nIf you choose \"Backup Core Userdata\" only irreplaceable files (like saves, states and gamelists) will be backed up. If you choose \"Backup Some Userdata\" you will be given a choice of which folders to backup.\n\nIf you choose \"Backup All Userdata\" then ALL data (including ROMs and downloaded media) will be backed up.\nPLEASE NOTE: A full backup may take up a large amount of space, especially if you have a lot of scraped media.") + + local rc=$? + if [[ $rc == "0" ]] && [[ -z "$choice" ]]; then # User selected No Backup button + log i "User chose to not backup prior to update." + else + case $choice in + "Backup Core Userdata" ) + log i "User chose to backup core userdata prior to update." + backup_retrodeck_userdata "core" + ;; + "Backup Some Userdata" ) + log i "User chose to backup some userdata prior to update." + while read -r config_line; do + local current_setting_name=$(get_setting_name "$config_line" "retrodeck") + if [[ ! $current_setting_name =~ (rdhome|sdcard|backups_folder) ]]; then # Ignore these locations + log d "Adding $current_setting_name to compressible paths." + local current_setting_value=$(get_setting_value "$rd_conf" "$current_setting_name" "retrodeck" "paths") + compressible_paths=("${compressible_paths[@]}" "false" "$current_setting_name" "$current_setting_value") + fi + done < <(grep -v '^\s*$' $rd_conf | awk '/^\[paths\]/{f=1;next} /^\[/{f=0} f') + + choice=$(rd_zenity \ + --list --width=1200 --height=720 \ + --checklist \ + --separator="^" \ + --print-column=3 \ + --text="Please select folders to compress..." \ + --column "Backup?" \ + --column "Folder Name" \ + --column "Path" \ + "${compressible_paths[@]}") + + choices=() # Expand choice string into passable array + IFS='^' read -ra choices <<< "$choice" + + backup_retrodeck_userdata "custom" "${choices[@]}" # Expand array of choices into individual arguments + ;; + "Backup All Userdata" ) + log i "User chose to backup all userdata prior to update." + backup_retrodeck_userdata "complete" + ;; + esac + fi + + # Start of post_update actions + if [[ $(check_version_is_older_than "$version_being_updated" "0.5.0b") == "true" ]]; then # If updating from prior to save sorting change at 0.5.0b log d "Version is older than 0.5.0b, executing save migration" save_migration @@ -674,6 +726,29 @@ post_update() { fi # end of 0.9.1b + if [[ $(check_version_is_older_than "$version_being_updated" "0.9.2b") == "true" ]]; then + # In version 0.9.2b, the following changes were made that required config file updates/reset or other changes to the filesystem: + # Steam Sync completely rebuilt into new manifest system. Favorites will need to be nuked and, if steam_sync is enabled will be rebuilt. + + if [[ -d "$steamsync_folder" ]]; then # If Steam Sync has ever been run + steam-rom-manager nuke + steam_sync + if [[ "$(configurator_generic_question_dialog "RetroDECK 0.9.2b Steam Sync Reset" "In RetroDECK 0.9.2b we upgraded our Steam Sync feature and the shortcuts in Steam need to be rebuilt.\n\nAll of your ES-DE favorites are still unchanged.\nAny games you have favorited now will be recreated.\n\nIf you have added RetroDECK to Steam through our Configurator it will also be removed through this process.\nWould you like to add the RetroDECK shortcut again?")" == "true" ]]; then + ( + # Add RetroDECK launcher to Steam + steam-rom-manager enable --names "RetroDECK Launcher" >> "$srm_log" 2>&1 + steam-rom-manager add >> "$srm_log" 2>&1 + ) | + rd_zenity --progress \ + --title="RetroDECK Configurator: Add RetroDECK to Steam" \ + --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ + --text="Adding RetroDECK launcher to Steam, please wait..." \ + --pulsate --width=500 --height=150 --auto-close --no-cancel + fi + fi + + fi # end of 0.9.2b + # The following commands are run every time. if [[ -d "/var/data/dolphin-emu/Load/DynamicInputTextures" ]]; then # Refresh installed textures if they have been enabled @@ -710,5 +785,7 @@ post_update() { changelog_dialog "$version" fi + unset CONFIGURATOR_GUI + log i "Upgrade process completed successfully." } diff --git a/functions/prepare_component.sh b/functions/prepare_component.sh index 52b043b2..08ad8357 100644 --- a/functions/prepare_component.sh +++ b/functions/prepare_component.sh @@ -138,8 +138,6 @@ prepare_component() { jq '.environmentVariables.romsDirectory = "'$rdhome'/.sync"' "$srm_userdata/userSettings.json" > "$srm_userdata/tmp.json" && mv -f "$srm_userdata/tmp.json" "$srm_userdata/userSettings.json" get_steam_user - populate_steamuser_srm - fi if [[ "$component" =~ ^(retroarch|all)$ ]]; then @@ -561,8 +559,10 @@ prepare_component() { if [[ "$component" =~ ^(pico8|pico-8|all)$ ]]; then component_found="true" if [[ ("$action" == "reset") || ("$action" == "postmove") ]]; then + if [[ -d "$roms_folder/pico8" ]]; then + dir_prep "$roms_folder/pico8" "$bios_folder/pico-8/carts" # Symlink default game location to RD roms for cleanliness (this location is overridden anyway by the --root_path launch argument anyway) + fi dir_prep "$bios_folder/pico-8" "$HOME/.lexaloffle/pico-8" # Store binary and config files together. The .lexaloffle directory is a hard-coded location for the PICO-8 config file, cannot be changed - dir_prep "$roms_folder/pico8" "$bios_folder/pico-8/carts" # Symlink default game location to RD roms for cleanliness (this location is overridden anyway by the --root_path launch argument anyway) dir_prep "$saves_folder/pico-8" "$bios_folder/pico-8/cdata" # PICO-8 saves folder cp -fv "$config/pico-8/config.txt" "$bios_folder/pico-8/config.txt" cp -fv "$config/pico-8/sdl_controllers.txt" "$bios_folder/pico-8/sdl_controllers.txt" diff --git a/functions/steam_sync.sh b/functions/steam_sync.sh index c12c289f..5a34e290 100644 --- a/functions/steam_sync.sh +++ b/functions/steam_sync.sh @@ -1,121 +1,167 @@ #!/bin/bash -# Function to sanitize strings for filenames -sanitize() { - # Replace sequences of underscores with a single space - echo "$1" | sed -e 's/_\{2,\}/ /g' -e 's/_/ /g' -e 's/:/ -/g' -e 's/&/and/g' -e 's%/%and%g' -e 's/ / /g' -} +steam_sync() { -add_to_steam() { + # This function looks for favorited games in all ES-DE gamelists and builds a manifest of any found. + # It then compares the new manifest to the existing one (if it exists) and runs an SRM sync if there are differences + # If all favorites were removed from ES-DE, it will remove all existing entries from Steam and then remove the favorites manifest entirely + # If there is no existing manifest, this is a first time sync and games are synced automatically + # USAGE: steam_sync log "i" "Starting Steam Sync" - create_dir $steamsync_folder - create_dir $steamsync_folder_tmp - local srm_path="/var/config/steam-rom-manager/userData/userConfigurations.json" if [ ! -f "$srm_path" ]; then log "e" "Steam ROM Manager configuration not initialized! Initializing now." prepare_component "reset" "steam-rom-manager" fi - # Iterate through all gamelist.xml files in the folder structure + # Prepare fresh log file + echo > "$srm_log" + + # Prepare new favorites manifest + echo "[]" > "${retrodeck_favorites_file}.new" # Initialize favorites JSON file + + # Static definitions for all JSON objects + target="flatpak" + launch_command="run net.retrodeck.retrodeck" + startIn="" + for system_path in "$rdhome/ES-DE/gamelists/"*/; do # Skip the CLEANUP folder if [[ "$system_path" == *"/CLEANUP/"* ]]; then continue fi + # Skip folders with no gamelists + if [[ ! -f "${system_path}gamelist.xml" ]]; then + continue + fi system=$(basename "$system_path") # Extract the folder name as the system name gamelist="${system_path}gamelist.xml" - - log d "Reading favorites for $system" - - # Ensure gamelist.xml exists in the current folder - if [ -f "$gamelist" ]; then - while IFS= read -r line; do - # Detect the start of a block - if [[ "$line" =~ \ ]]; then - to_be_added=false # Reset the flag for a new block - path="" - name="" + system_favorites=$(xml sel -t -m "//game[favorite='true']" -v "path" -n "$gamelist") + while read -r game; do + if [[ -n "$game" ]]; then # Avoid empty lines created by xmlstarlet + local game="${game#./}" # Remove leading ./ + if [[ -f "$roms_folder/$system/$game" ]]; then # Validate file exists and isn't a stale ES-DE entry for a removed file + # Construct launch options with the rom path in quotes, to handle spaces + local launchOptions="$launch_command -s $system \"$roms_folder/$system/$game\"" + jq --arg title "${game%.*}" --arg target "$target" --arg launchOptions "$launchOptions" \ + '. += [{"title": $title, "target": $target, "launchOptions": $launchOptions}]' "${retrodeck_favorites_file}.new" > "${retrodeck_favorites_file}.tmp" \ + && mv "${retrodeck_favorites_file}.tmp" "${retrodeck_favorites_file}.new" fi - - # Check for true - if [[ "$line" =~ \true\<\/favorite\> ]]; then - to_be_added=true - fi - - # Extract the and remove leading "./" if present - if [[ "$line" =~ \(.*)\<\/path\> ]]; then - path="${BASH_REMATCH[1]#./}" - fi - - # Extract and sanitize - if [[ "$line" =~ \(.*)\<\/name\> ]]; then - name=$(sanitize "${BASH_REMATCH[1]}") - fi - - # Detect the end of a block - if [[ "$line" =~ \<\/game\> ]]; then - # If the block is meaningful (marked as favorite), generate the launcher - if [ "$to_be_added" = true ] && [ -n "$path" ] && [ -n "$name" ]; then - local launcher="$steamsync_folder/${name}.sh" - local launcher_tmp="$steamsync_folder_tmp/${name}.sh" - - # Create the launcher file - # Check if the launcher file does not already exist - if [ ! -e "$launcher_tmp" ]; then - log d "Creating launcher file: $launcher" - command="flatpak run net.retrodeck.retrodeck -s $system '$roms_folder/$system/$path'" - echo '#!/bin/bash' > "$launcher_tmp" - echo "$command" >> "$launcher_tmp" - chmod +x "$launcher_tmp" - else - log d "$(basename $launcher) desktop file already exists" - fi - fi - - # Clean up variables for safety - to_be_added=false - path="" - name="" - fi - done < "$gamelist" - else - log "e" "Gamelist file not found for system: $system" - fi + fi + done <<< "$system_favorites" done - # Remove the old Steam sync folder - rm -rf "$steamsync_folder" - - # Move the temporary Steam sync folder to the final location - log d "Moving the temporary Steam sync folder to the final location" - mv "$steamsync_folder_tmp" "$steamsync_folder" && log d "\"$steamsync_folder_tmp\" -> \"$steamsync_folder\"" + if [[ -f "$retrodeck_favorites_file" && -f "${retrodeck_favorites_file}.new" ]]; then + # Look for favorites removed between steam_sync runs, if any + removed_items=$(jq -n \ + --slurpfile source "$retrodeck_favorites_file" \ + --slurpfile target "${retrodeck_favorites_file}.new" \ + '[$source[0][] | select(. as $item | ($target[0] | map(. == $item) | any | not))]') + fi - # Check if the Steam sync folder is empty - if [ -z "$(ls -A $steamsync_folder)" ]; then - # if empty, add the remove_from_steam function - log d "No games found, cleaning shortcut" - remove_from_steam - else - log d "Updating game list" - steam-rom-manager enable --names "RetroDECK Steam Sync" - steam-rom-manager add + # Check if there are any missing objects + if [[ "$(echo "$removed_items" | jq 'length')" -gt 0 ]]; then + log d "Some favorites were removed between sync, writing to $retrodeck_removed_favorites" + echo "$removed_items" > "$retrodeck_removed_favorites" + fi + + # Decide if sync needs to happen + if [[ -f "$retrodeck_favorites_file" ]]; then # If an existing favorites manifest exists + if [[ ! "$(cat "${retrodeck_favorites_file}.new" | jq 'length')" -gt 0 ]]; then # If all favorites were removed from all gamelists, meaning new manifest is empty + log i "No favorites were found in current ES-DE gamelists, removing old entries" + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + ( + # Remove old entries + steam-rom-manager disable --names "RetroDECK Launcher" >> "$srm_log" 2>&1 + steam-rom-manager enable --names "RetroDECK Steam Sync" >> "$srm_log" 2>&1 + steam-rom-manager remove >> "$srm_log" 2>&1 + ) | + rd_zenity --progress \ + --title="Syncing with Steam" \ + --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ + --text="\t\t\t\tRemoving unfavorited games from Steam\n\nNOTE: This operation may take some time depending on the size of your library.\nFeel free to leave this in the background and switch to another application.\n\n" \ + --pulsate --width=500 --height=150 --auto-close --no-cancel + else + # Remove old entries + steam-rom-manager disable --names "RetroDECK Launcher" >> "$srm_log" 2>&1 + steam-rom-manager enable --names "RetroDECK Steam Sync" >> "$srm_log" 2>&1 + steam-rom-manager remove >> "$srm_log" 2>&1 + fi + # Old manifest cleanup + rm "$retrodeck_favorites_file" + rm "${retrodeck_favorites_file}.new" + else # The new favorites manifest is not empty + if cmp -s "$retrodeck_favorites_file" "${retrodeck_favorites_file}.new"; then # See if the favorites manifests are the same, meaning there were no changes + log i "ES-DE favorites have not changed, no need to sync again" + rm "${retrodeck_favorites_file}.new" + else + log d "New and old manifests are different, running sync" + if [[ -f "$retrodeck_removed_favorites" ]]; then # If some favorites were removed between syncs + log d "Some favorites removed between syncs, removing unfavorited games" + # Load removed favorites as manifest and run SRM remove + mv "$retrodeck_removed_favorites" "$retrodeck_favorites_file" + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + ( + steam-rom-manager disable --names "RetroDECK Launcher" >> "$srm_log" 2>&1 + steam-rom-manager enable --names "RetroDECK Steam Sync" >> "$srm_log" 2>&1 + steam-rom-manager remove >> "$srm_log" 2>&1 + ) | + rd_zenity --progress \ + --title="Syncing with Steam" \ + --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ + --text="\t\t\t\tRemoving unfavorited games from Steam\n\nNOTE: This operation may take some time depending on the size of your library.\nFeel free to leave this in the background and switch to another application.\n\n" \ + --pulsate --width=500 --height=150 --auto-close --no-cancel + else + steam-rom-manager disable --names "RetroDECK Launcher" >> "$srm_log" 2>&1 + steam-rom-manager enable --names "RetroDECK Steam Sync" >> "$srm_log" 2>&1 + steam-rom-manager remove >> "$srm_log" 2>&1 + fi + fi + + # Load new favorites manifest as games to add during sync + mv "${retrodeck_favorites_file}.new" "$retrodeck_favorites_file" + + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + ( + # Add new favorites manifest + steam-rom-manager disable --names "RetroDECK Launcher" >> "$srm_log" 2>&1 + steam-rom-manager enable --names "RetroDECK Steam Sync" >> "$srm_log" 2>&1 + steam-rom-manager add >> "$srm_log" 2>&1 + ) | + rd_zenity --progress \ + --title="Syncing with Steam" \ + --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ + --text="\t\t\t\tSyncing favorite games with Steam\n\nNOTE: This operation may take some time depending on the size of your library.\nFeel free to leave this in the background and switch to another application.\n\n" \ + --pulsate --width=500 --height=150 --auto-close --no-cancel + else + steam-rom-manager disable --names "RetroDECK Launcher" >> "$srm_log" 2>&1 + steam-rom-manager enable --names "RetroDECK Steam Sync" >> "$srm_log" 2>&1 + steam-rom-manager add >> "$srm_log" 2>&1 + fi + fi + fi + elif [[ "$(cat "${retrodeck_favorites_file}.new" | jq 'length')" -gt 0 ]]; then # No existing favorites manifest was found, so check if new manifest has entries + log d "First time building favorites manifest, running sync" + mv "${retrodeck_favorites_file}.new" "$retrodeck_favorites_file" + if [[ "$CONFIGURATOR_GUI" == "zenity" ]]; then + ( + # Add new favorites manifest + steam-rom-manager disable --names "RetroDECK Launcher" >> "$srm_log" 2>&1 + steam-rom-manager enable --names "RetroDECK Steam Sync" >> "$srm_log" 2>&1 + steam-rom-manager add >> "$srm_log" 2>&1 + ) | + rd_zenity --progress \ + --title="Syncing with Steam" \ + --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ + --text="\t\t\t\tSyncing favorite games with Steam\n\nNOTE: This operation may take some time depending on the size of your library.\nFeel free to leave this in the background and switch to another application.\n\n" \ + --pulsate --width=500 --height=150 --auto-close --no-cancel + else + # Add new favorites manifest + steam-rom-manager disable --names "RetroDECK Launcher" >> "$srm_log" 2>&1 + steam-rom-manager enable --names "RetroDECK Steam Sync" >> "$srm_log" 2>&1 + steam-rom-manager add >> "$srm_log" 2>&1 + fi fi } - -# Function to remove the games from Steam, this is a workaround to make SRM remove the games as it cannot remove the games based on a empty folder -# So a dummy file must be in place to make SRM remove the other games -remove_from_steam() { - log d "Creating dummy game" - cat "" > "$steamsync_folder/CUL0.sh" - log d "Cleaning the shortcut" - steam-rom-manager enable --names "RetroDECK Steam Sync" - steam-rom-manager disable --names "RetroDECK Launcher" - steam-rom-manager remove - log d "Removing dummy game" - rm "$steamsync_folder/CUL0.sh" - steam-rom-manager enable --names "RetroDECK Launcher" - steam-rom-manager disable --names "RetroDECK Steam Sync" -} diff --git a/net.retrodeck.retrodeck.yml b/net.retrodeck.retrodeck.yml index fe4a25ac..13d0f819 100644 --- a/net.retrodeck.retrodeck.yml +++ b/net.retrodeck.retrodeck.yml @@ -86,7 +86,7 @@ modules: # VERSION INITIALIZATION # on main please update this with the version variable, eg: VERSION=0.8.0b # on cooker will be VERSION=cooker-0.9.0b for example - VERSION=cooker-0.9.1b + VERSION=cooker-0.9.2b git checkout ${GITHUB_REF_NAME} mkdir -p ${FLATPAK_DEST}/retrodeck/ diff --git a/res/Affinity Files/icon.afphoto b/res/Affinity Files/icon.afphoto index b88cb5ff..14c8ea8a 100644 Binary files a/res/Affinity Files/icon.afphoto and b/res/Affinity Files/icon.afphoto differ diff --git a/res/icon-configurator.svg b/res/icon-configurator.svg index 5df3161b..d6f515ea 100644 --- a/res/icon-configurator.svg +++ b/res/icon-configurator.svg @@ -1,20 +1,18 @@ - - - - - - - - - - - - + + + + + + + + + + - + diff --git a/res/icon-engine.svg b/res/icon-engine.svg index 30db9cf8..bb776009 100644 --- a/res/icon-engine.svg +++ b/res/icon-engine.svg @@ -1,14 +1,18 @@ - - - - - - - + + + + + + + + + + + - + diff --git a/res/icon-framework.svg b/res/icon-framework.svg index 95fbb2c6..95ced5b1 100644 --- a/res/icon-framework.svg +++ b/res/icon-framework.svg @@ -1,18 +1,18 @@ - - - - - - - + + + + - - + + + + + - + diff --git a/res/icon-ponzu.svg b/res/icon-ponzu.svg new file mode 100644 index 00000000..483a36d0 --- /dev/null +++ b/res/icon-ponzu.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/retrodeck.sh b/retrodeck.sh index 8b24fe3c..75a60005 100644 --- a/retrodeck.sh +++ b/retrodeck.sh @@ -109,6 +109,10 @@ while [[ $# -gt 0 ]]; do cli_compress_all_games "$2" shift 2 ;; + --repair-paths) + repair_paths + exit 0 + ;; --configurator) sh /app/tools/configurator.sh exit 0 diff --git a/tools/configurator.sh b/tools/configurator.sh index a8f8dca7..0b694561 100755 --- a/tools/configurator.sh +++ b/tools/configurator.sh @@ -76,6 +76,7 @@ rd_zenity --progress --no-cancel --pulsate --auto-close \ # - Update Notification # - Add RetroDECK to Steam # - M3U Multi-File Validator +# - Repair RetroDECK paths # - Ponzu: Remove Yuzu # - Ponzu: Remove Citra # - Steam Sync @@ -108,7 +109,6 @@ rd_zenity --progress --no-cancel --pulsate --auto-close \ configurator_welcome_dialog() { log i "Configurator: opening welcome dialog" - export CONFIGURATOR_GUI="zenity" welcome_menu_options=( "Settings" "Customize your RetroDECK experience with various presets and tweaks." "Open Component" "Manually launch and configure settings for each emulator or component (for advanced users)." @@ -443,6 +443,7 @@ configurator_tools_dialog() { "Update Notification" "Enable / Disable: Notifications for new RetroDECK versions." "Add RetroDECK to Steam" "Add RetroDECK shortcut to Steam. Steam restart required." "M3U Multi-File Validator" "Verify the proper structure of multi-file or multi-disc games." + "Repair RetroDECK Paths" "Repair RetroDECK folder path configs for unexpectedly missing folders." ) if [[ $(get_setting_value "$rd_conf" "kiroi_ponzu" "retrodeck" "options") == "true" ]]; then @@ -460,26 +461,66 @@ configurator_tools_dialog() { case $choice in "Backup Userdata" ) + log i "Configurator: opening \"$choice\" menu" - configurator_generic_dialog "RetroDECK Configurator - Backup Userdata" "This tool will compress important RetroDECK userdata (basically everything except the ROMs folder) into a zip file.\n\nThis process can take several minutes, and the resulting zip file can be found in the ~/retrodeck/backups folder." - ( - backup_retrodeck_userdata - ) | - rd_zenity --icon-name=net.retrodeck.retrodeck --progress --no-cancel --pulsate --auto-close \ - --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ - --title "RetroDECK Configurator Utility - Backup in Progress" \ - --text="Backing up RetroDECK userdata, please wait..." - if [[ -f "$backups_folder/$(date +"%0m%0d")_retrodeck_userdata.zip" ]]; then - configurator_generic_dialog "RetroDECK Configurator - Backup Userdata" "The backup process is now complete." + configurator_generic_dialog "RetroDECK Configurator - Backup Userdata" "This tool will compress one or more RetroDECK userdata folders into a single zip file.\n\nThis process can take several minutes, and the resulting zip file can be found in the ~/retrodeck/backups folder." + + choice=$(rd_zenity --title "RetroDECK Configurator Utility - Backup Userdata" --info --no-wrap --ok-label="Cancel" --extra-button"Backup Core Userdata" --extra-button="Backup Some Userdata" --extra-button="Backup All Userdata" \ + --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" --text="Would you like to compress all RetroDECK userdata folders, or only some of them?\nThe \"Core Userdata\" includes irreplaceable files such as saves, states and gamelists.\n\n A complete \"All Userdata\" backup can take up A LOT of space if you have a large library.") + + local rc=$? + if [[ $rc == "0" ]] && [[ -z "$choice" ]]; then # User selected Cancel button + configurator_tools_dialog else - configurator_generic_dialog "RetroDECK Configurator - Backup Userdata" "The backup process could not be completed,\nplease check the logs folder for more information." + case $choice in + "Backup Core Userdata" ) + log i "User chose to backup core userdata prior to update." + export CONFIGURATOR_GUI="zenity" + backup_retrodeck_userdata "core" + ;; + "Backup Some Userdata" ) + log i "User chose to backup custom userdata prior to update." + while read -r config_line; do + local current_setting_name=$(get_setting_name "$config_line" "retrodeck") + if [[ ! $current_setting_name =~ (rdhome|sdcard|backups_folder) ]]; then # Ignore these locations + log d "Adding $current_setting_name to compressible paths." + local current_setting_value=$(get_setting_value "$rd_conf" "$current_setting_name" "retrodeck" "paths") + compressible_paths=("${compressible_paths[@]}" "false" "$current_setting_name" "$current_setting_value") + fi + done < <(grep -v '^\s*$' $rd_conf | awk '/^\[paths\]/{f=1;next} /^\[/{f=0} f') + + choice=$(rd_zenity \ + --list --width=1200 --height=720 \ + --checklist \ + --separator="^" \ + --print-column=3 \ + --text="Please select folders to compress..." \ + --column "Backup?" \ + --column "Folder Name" \ + --column "Path" \ + "${compressible_paths[@]}") + + choices=() # Expand choice string into passable array + IFS='^' read -ra choices <<< "$choice" + + export CONFIGURATOR_GUI="zenity" + backup_retrodeck_userdata "custom" "${choices[@]}" # Expand array of choices into individual arguments + ;; + "Backup All Userdata" ) + log i "User chose to backup all userdata prior to update." + export CONFIGURATOR_GUI="zenity" + backup_retrodeck_userdata "complete" + ;; + esac + + configurator_tools_dialog fi - configurator_welcome_dialog ;; "BIOS Checker" ) log i "Configurator: opening \"$choice\" menu" configurator_bios_checker + configurator_tools_dialog ;; "Games Compressor" ) @@ -542,12 +583,29 @@ configurator_tools_dialog() { ;; "Add RetroDECK to Steam" ) - add_retrodeck_to_steam + ( + # Add RetroDECK launcher to Steam + steam-rom-manager enable --names "RetroDECK Launcher" >> "$srm_log" 2>&1 + steam-rom-manager add >> "$srm_log" 2>&1 + ) | + rd_zenity --progress \ + --title="RetroDECK Configurator: Add RetroDECK to Steam" \ + --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ + --text="Adding RetroDECK launcher to Steam, please wait..." \ + --pulsate --width=500 --height=150 --auto-close --no-cancel + configurator_tools_dialog ;; "M3U Multi-File Validator" ) log i "Configurator: opening \"$choice\" menu" configurator_check_multifile_game_structure + configurator_tools_dialog + ;; + + "Repair RetroDECK Paths" ) + log i "Configurator: opening \"$choice\" menu" + repair_paths + configurator_tools_dialog ;; "Ponzu: Remove Yuzu" ) @@ -658,7 +716,7 @@ configurator_data_management_dialog() { --list --width=1200 --height=720 --title "RetroDECK Configurator - RetroDECK: Clean Empty ROM Folders" \ --checklist --hide-column=3 --ok-label="Remove Selected" --extra-button="Remove All" \ --separator="," --print-column=2 \ - --text="Choose which ROM folders to remove:" \ + --text="Choose which empty ROM folders to remove:" \ --column "Remove?" \ --column "System" \ "${empty_rom_folders_list[@]}") @@ -715,26 +773,31 @@ configurator_compression_tool_dialog() { "Compress Multiple Games: CHD" ) log i "Configurator: opening \"$choice\" menu" configurator_compress_multiple_games_dialog "chd" + configurator_compression_tool_dialog ;; "Compress Multiple Games: ZIP" ) log i "Configurator: opening \"$choice\" menu" configurator_compress_multiple_games_dialog "zip" + configurator_compression_tool_dialog ;; "Compress Multiple Games: RVZ" ) log i "Configurator: opening \"$choice\" menu" configurator_compress_multiple_games_dialog "rvz" + configurator_compression_tool_dialog ;; "Compress Multiple Games: All Formats" ) log i "Configurator: opening \"$choice\" menu" configurator_compress_multiple_games_dialog "all" + configurator_compression_tool_dialog ;; "Compress All Games" ) log i "Configurator: opening \"$choice\" menu" configurator_compress_multiple_games_dialog "everything" + configurator_compression_tool_dialog ;; "" ) # No selection made or Back button clicked @@ -755,7 +818,7 @@ configurator_compress_single_game_dialog() { ( echo "# Compressing $(basename "$file") to $compatible_compression_format format" # This updates the Zenity dialog log i "Compressing $(basename "$file") to $compatible_compression_format format" - compress_game "$compatible_compression_format" "$file" "$system" + compress_game "$compatible_compression_format" "$file" "$post_compression_cleanup" "$system" ) | rd_zenity --icon-name=net.retrodeck.retrodeck --progress --no-cancel --pulsate --auto-close \ --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ @@ -775,120 +838,86 @@ configurator_compress_single_game_dialog() { configurator_compress_multiple_games_dialog() { log d "Starting to compress \"$1\"" - local output_file="${godot_compression_compatible_games}" - [ -f "$output_file" ] && rm -f "$output_file" - touch "$output_file" + local compressible_games_list_file="${godot_compression_compatible_games}" + [ -f "$compressible_games_list_file" ] && rm -f "$compressible_games_list_file" + touch "$compressible_games_list_file" - ## --- SEARCH PHASE WITH LOADING SCREEN --- - local progress_pipe - progress_pipe=$(mktemp -u) - mkfifo "$progress_pipe" - - # Launch find_compatible_games in the background (its output goes to the file) - find_compatible_games "$1" > "$output_file" & - local finder_pid=$! - - # Launch a background process that writes loading messages until the search completes. ( - while kill -0 "$finder_pid" 2>/dev/null; do - echo "# Loading: Searching for compatible games..." - sleep 1 - done - echo "100" - ) > "$progress_pipe" & - local progress_writer_pid=$! + find_compatible_games "$1" > "$compressible_games_list_file" + ) | + rd_zenity --icon-name=net.retrodeck.retrodeck --progress --no-cancel --auto-close \ + --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ + --title "RetroDECK Configurator - RetroDECK: Compression Tool" --text "RetroDECK is searching for compress1ble games, please wait..." - rd_zenity --progress --pulsate --auto-close \ - --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ - --title="RetroDECK Configurator Utility - Searching for Compressable Games" \ - --text="Searching for compressable games, please wait..." < "$progress_pipe" - - wait "$finder_pid" - wait "$progress_writer_pid" - rm "$progress_pipe" - - if [[ -s "$output_file" ]]; then - mapfile -t all_compressable_games < "$output_file" - log d "Found the following games to compress: ${all_compressable_games[*]}" + if [[ -s "$compressible_games_list_file" ]]; then + mapfile -t all_compressible_games < "$compressible_games_list_file" + log d "Found the following games to compress: ${all_compressible_games[*]}" else - configurator_generic_dialog "RetroDECK Configurator - Compression Tool" "No compressable files were found." - return + configurator_generic_dialog "RetroDECK Configurator - Compression Tool" "No compressible files were found." + configurator_compression_tool_dialog fi local games_to_compress=() if [[ "$1" != "everything" ]]; then local checklist_entries=() - for line in "${all_compressable_games[@]}"; do + for line in "${all_compressible_games[@]}"; do IFS="^" read -r game comp <<< "$line" local short_game="${game#$roms_folder}" checklist_entries+=( "TRUE" "$short_game" "$line" ) done - local choice - choice=$(rd_zenity \ + local choice=$(rd_zenity \ --list --width=1200 --height=720 --title "RetroDECK Configurator - Compression Tool" \ --checklist --hide-column=3 --ok-label="Compress Selected" --extra-button="Compress All" \ - --separator="," --print-column=3 \ + --separator=$'\0' --print-column=3 \ --text="Choose which games to compress:" \ --column "Compress?" \ --column "Game" \ - --column "Game Full Path" \ + --column "Game Full Path and Compression Format" \ "${checklist_entries[@]}") local rc=$? log d "User choice: $choice" if [[ $rc == 0 && -n "$choice" ]]; then - IFS="," read -ra games_to_compress <<< "$choice" + while IFS="^" read -r game comp; do # Split Zenity choice string into compatible pairs (game^format) + games_to_compress+=("$game"^"$comp") + done <<< "$choice" elif [[ -n "$choice" ]]; then - games_to_compress=("${all_compressable_games[@]}") + games_to_compress=("${all_compressible_games[@]}") else - return + configurator_compression_tool_dialog fi else - games_to_compress=("${all_compressable_games[@]}") + games_to_compress=("${all_compressible_games[@]}") fi + local post_compression_cleanup=$(configurator_compression_cleanup_dialog) + local total_games=${#games_to_compress[@]} local games_left=$total_games - ## --- COMPRESSION PHASE WITH PROGRESS SCREEN --- - local comp_pipe - comp_pipe=$(mktemp -u) - mkfifo "$comp_pipe" - ( - for game_line in "${games_to_compress[@]}"; do - IFS="^" read -r game compression_format <<< "$game_line" - local system - system=$(echo "$game" | grep -oE "$roms_folder/[^/]+" | grep -oE "[^/]+$") - log i "Compressing $(basename "$game") into $compression_format format" + for game_line in "${games_to_compress[@]}"; do + IFS="^" read -r game compression_format <<< "$game_line" - # Launch the compression in the background. - compress_game "$compression_format" "$game" "$system" & - local comp_pid=$! + local system + system=$(echo "$game" | grep -oE "$roms_folder/[^/]+" | grep -oE "[^/]+$") + log i "Compressing $(basename "$game") into $compression_format format" - # While the compression is in progress, write a status message every second. - while kill -0 "$comp_pid" 2>/dev/null; do - echo "# Compressing $(basename "$game") into $compression_format format" - sleep 1 - done - - # When finished, update the progress percentage. - local progress=$(( 100 - (( 100 / total_games ) * games_left) )) - echo "$progress" - games_left=$(( games_left - 1 )) - done - echo "100" - ) > "$comp_pipe" & - local comp_pid_group=$! + echo "#Compressing $(basename "$game") into $compression_format format.\n\n$games_left games left to compress." # Update Zenity dialog text + compress_game "$compression_format" "$game" "$post_compression_cleanup" "$system" + games_left=$(( games_left - 1 )) + local progress=$(( 99 - (( 99 / total_games ) * games_left) )) + log d "progress: $progress" + echo "$progress" # Update Zenity dialog progress bar + done + echo "100" # Close Zenity progress dialog when finished + ) | rd_zenity --icon-name=net.retrodeck.retrodeck --progress --no-cancel --auto-close \ --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck/retrodeck.svg" \ --width="800" \ - --title "RetroDECK Configurator Utility - Compression in Progress" < "$comp_pipe" - - wait "$comp_pid_group" - rm "$comp_pipe" + --title "RetroDECK Configurator Utility - Compression in Progress" configurator_generic_dialog "RetroDECK Configurator - Compression Tool" "The compression process is complete!" configurator_compression_tool_dialog @@ -1066,8 +1095,6 @@ configurator_bios_checker() { --title "RetroDECK Configurator Utility - BIOS Check in Progress" \ --text="This check will look for BIOS files that RetroDECK has identified as working.\n\nNot all BIOS files are required for games to work, please check the BIOS description for more information on its purpose.\n\nBIOS files not known to this tool could still function.\n\nSome more advanced emulators such as Ryujinx will have additional methods to verify that the BIOS files are in working order.\n\nRetroDECK is now checking your BIOS files, please wait...\n\n" \ --width=400 --height=100 - - configurator_welcome_dialog } configurator_reset_dialog() { @@ -1199,7 +1226,7 @@ configurator_steam_sync() { if [ $? == 0 ] # User clicked "Yes" then - disable_steam_sync + configurator_disable_steam_sync else # User clicked "Cancel" configurator_welcome_dialog fi @@ -1211,15 +1238,17 @@ configurator_steam_sync() { if [ $? == 0 ] then - enable_steam_sync + configurator_enable_steam_sync else configurator_welcome_dialog fi fi } -enable_steam_sync() { +configurator_enable_steam_sync() { set_setting_value "$rd_conf" "steam_sync" "true" retrodeck "options" + export CONFIGURATOR_GUI="zenity" + steam_sync zenity --icon-name=net.retrodeck.retrodeck --info --no-wrap --ok-label="OK" \ --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ --title "RetroDECK Configurator - RetroDECK Steam Syncronization" \ @@ -1227,10 +1256,22 @@ enable_steam_sync() { configurator_welcome_dialog } -disable_steam_sync() { +configurator_disable_steam_sync() { set_setting_value "$rd_conf" "steam_sync" "false" retrodeck "options" - source /app/libexec/steam_sync.sh - remove_from_steam + # Remove only synced favorites, leave RetroDECK shortcut if it exists + ( + steam-rom-manager enable --names "RetroDECK Steam Sync" >> "$srm_log" 2>&1 + steam-rom-manager disable --names "RetroDECK Launcher" >> "$srm_log" 2>&1 + steam-rom-manager remove >> "$srm_log" 2>&1 + ) | + rd_zenity --progress \ + --title="Removing RetroDECK Sync from Steam" \ + --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ + --text="Removing synced entries from Steam, please wait..." \ + --pulsate --width=500 --height=150 --auto-close --no-cancel + if [[ -f "$retrodeck_favorites_file" ]]; then + rm -f "$retrodeck_favorites_file" + fi zenity --icon-name=net.retrodeck.retrodeck --info --no-wrap --ok-label="OK" \ --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" \ --title "RetroDECK Configurator - RetroDECK Steam Syncronization" \ @@ -1382,7 +1423,6 @@ configurator_usb_import_dialog() { "Prepare USB device" ) log i "Configurator: opening \"$choice\" menu" - configurator_generic_dialog "RetroDeck Configurator - USB Import" "If you have an SD card installed that is not currently configured in RetroDECK it may show up in this list, but not be suitable for USB import.\n\nPlease select your desired drive carefully." external_devices=() @@ -1392,6 +1432,7 @@ configurator_usb_import_dialog() { done < <(df --output=size,target -h | grep "/run/media/" | grep -v "$sdcard" | awk '{$1=$1;print}') if [[ "${#external_devices[@]}" -gt 0 ]]; then + configurator_generic_dialog "RetroDeck Configurator - USB Import" "If you have an SD card installed that is not currently configured in RetroDECK it may show up in this list, but not be suitable for USB import.\n\nPlease select your desired drive carefully." choice=$(rd_zenity --list --title="RetroDECK Configurator Utility - USB Migration Tool" --cancel-label="Back" \ --window-icon="/app/share/icons/hicolor/scalable/apps/net.retrodeck.retrodeck.svg" --width=1200 --height=720 \ --hide-column=3 --print-column=3 \ @@ -1401,9 +1442,10 @@ configurator_usb_import_dialog() { "${external_devices[@]}") if [[ ! -z "$choice" ]]; then + create_dir "$choice/RetroDECK Import" es-de --home "$choice/RetroDECK Import" --create-system-dirs rm -rf "$choice/RetroDECK Import/ES-DE" # Cleanup unnecessary folder - create_dir "$choice/RetroDECK Import/BIOS" + # Prepare default BIOS folder subfolders create_dir "$choice/RetroDECK Import/BIOS/np2kai"