summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorsmls2015-07-09 17:08:58 +0200
committersmls2015-07-09 17:08:58 +0200
commitabbecc5426f28596c09d871fca91f5a64ec9facb (patch)
tree7e981103e713c6c711f255ff2f898334c2502369
parentf7d22f66ff0e1f2b9d3b47474799d3fca3d76bc4 (diff)
downloadaur-abbecc5426f28596c09d871fca91f5a64ec9facb.tar.gz
Upgrade to new install script; use gog:// source instead of custom logic
-rw-r--r--.SRCINFO8
-rw-r--r--PKGBUILD36
-rwxr-xr-xarx-install-data3278
-rwxr-xr-xinstall-gog96
-rw-r--r--install-verify257
5 files changed, 3291 insertions, 384 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 7af46d43f575..3c8b4c233f5d 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -11,10 +11,10 @@ pkgbase = arx-fatalis-data-gog
provides = arx-fatalis-data
conflicts = arx-fatalis-data-copy
conflicts = arx-fatalis-data-demo
- source = install-verify
- source = install-gog
- md5sums = e9c245ac1bb48cb8ef667933e0b725d2
- md5sums = fc86639646ba5dba6d737560fb846f4a
+ source = gog://setup_arx_fatalis_2.0.0.7.exe
+ source = arx-install-data
+ md5sums = 5be0898e71632e46ca430d7a32d0179a
+ md5sums = fc5456e4c213af243b65862db8d5db0a
pkgname = arx-fatalis-data-gog
diff --git a/PKGBUILD b/PKGBUILD
index a032ac18f8b8..2d490c63dff0 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -10,40 +10,22 @@ license=('custom:commercial')
makedepends=('innoextract')
provides=('arx-fatalis-data')
conflicts=('arx-fatalis-data-copy' 'arx-fatalis-data-demo')
-source=("install-verify" "install-gog")
-md5sums=('e9c245ac1bb48cb8ef667933e0b725d2'
- 'fc86639646ba5dba6d737560fb846f4a')
install='arx-fatalis-data.install'
PKGEXT='.pkg.tar'
+DLAGENTS+=('hib::/usr/bin/echo "Could not find %u. Manually download it to \"$(pwd)\", or set up a gog:// DLAGENT in /etc/makepkg.conf."; exit 1')
-_gamepkg="setup_arx_fatalis.exe"
+_gamepkg="setup_arx_fatalis_2.0.0.7.exe"
+source=("gog://$_gamepkg"
+ "arx-install-data") # from http://arx.vg/arx-install-data
+md5sums=('5be0898e71632e46ca430d7a32d0179a'
+ 'fc5456e4c213af243b65862db8d5db0a')
package() {
cd $srcdir
- _get_local_source "$_gamepkg" || {
- error "Unable to find the game archive. Please download it from your GOG.com
- account, and copy or symlink it into one of the above directories."
- exit 1; }
- msg "Starting setup..."
- chmod +x install-gog
- ./install-gog --no-progress "$_gamepkg" "$pkgdir/usr/share/arx"
- mkdir "$pkgdir/usr/share/games" && ln -s "/usr/share/arx/" "$pkgdir/usr/share/games/arx"
-}
+ chmod +x arx-install-data
+ ./arx-install-data --batch "$_gamepkg" "$pkgdir/usr/share/arx"
-
-# Locate a file or folder provided by the user, and symlink it into $srcdir
-_get_local_source() {
- msg "Looking for '$1'..."
- declare -A _search=(['build dir']="$startdir"
- ['$LOCAL_PACKAGE_SOURCES']="$LOCAL_PACKAGE_SOURCES")
- for _key in "${!_search[@]}"; do local _dir="${_search["$_key"]}"
- if [ -z "$_dir" ]; then _dir="<undefined>"; fi
- echo -n " - in $_key ['$_dir'] ... ";
- if [ -e "$_dir/$1" ]; then
- echo "FOUND"; ln -sfT "$(readlink -f "$_dir/$1")" "$srcdir/$1"; break; fi
- echo "NOT FOUND"
- done
- if [ ! -e "$srcdir/$1" ]; then return 1; fi
+ mkdir "$pkgdir/usr/share/games" && ln -s "/usr/share/arx/" "$pkgdir/usr/share/games/arx"
}
diff --git a/arx-install-data b/arx-install-data
new file mode 100755
index 000000000000..04e4dfce1e22
--- /dev/null
+++ b/arx-install-data
@@ -0,0 +1,3278 @@
+#!/bin/sh
+
+# Copyright 2013 Arx Libertatis Team (see the AUTHORS file)
+#
+# This file is part of Arx Libertatis.
+#
+# Arx Libertatis 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.
+#
+# Arx Libertatis 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 Arx Libertatis. If not, see <http://www.gnu.org/licenses/>.
+
+##########################################################################################
+# Install script for Arx Fatalis data files to be used with Arx Libertatis
+# Usage: just run the damned script, maybe check --help
+
+# This scripts targets Linux and FreeBSD, but may also work on other UNIX-like systems.
+
+# Is this a multi-thousand-line bas^H^H^HPOSIX shell script?
+# Sure looks like it.
+# Am I mad?
+# Most likely.
+
+# If you want to edit the required files and checksums, scroll to the end.
+
+
+##########################################################################################
+# Colors
+
+disable_color() {
+ red='' ; green='' ; yellow='' ; blue='' ; pink='' ; cyan='' ; white=''
+ dim_red='' ; dim_green='' ; dim_yellow='' ; dim_blue='' ; dim_pink=''
+ dim_cyan='' ; dim_white='' ; reset=''
+}
+disable_color
+if [ -t 1 ] && [ "$(tput colors 2> /dev/null)" != -1 ] ; then
+
+ red="$(printf '\033[1;31m')"
+ green="$(printf '\033[1;32m')"
+ yellow="$(printf '\033[1;33m')"
+ blue="$(printf '\033[1;34m')"
+ pink="$(printf '\033[1;35m')"
+ cyan="$(printf '\033[1;36m')"
+ white="$(printf '\033[1;37m')"
+
+ dim_red="$(printf '\033[0;31m')"
+ dim_green="$(printf '\033[0;32m')"
+ dim_yellow="$(printf '\033[0;33m')"
+ dim_blue="$(printf '\033[0;34m')"
+ dim_pink="$(printf '\033[0;35m')"
+ dim_cyan="$(printf '\033[0;36m')"
+ dim_white="$(printf '\033[0;37m')"
+
+ reset="$(printf '\033[m')"
+fi
+
+
+##########################################################################################
+# Constants
+
+# Name and download locations for the 1.21 patch
+patch_ver='1.21'
+patch_name="ArxFatalis_${patch_ver}_MULTILANG.exe"
+patch_name_localized="ArxFatalis_${patch_ver}_%s.exe"
+patch_url_path="arxfatalis/patches/${patch_ver}/${patch_name}"
+patch_url_master="http://cdn.bethsoft.com/${patch_url_path}"
+patch_urls="http://arx.vg/${patch_name} ${patch_url_master}"
+patch_urls="$patch_urls http://download.zenimax.com/${patch_url_path}"
+patch_urls="$patch_urls http://web.archive.org/web/${patch_url_master}"
+
+# Name and download locations for the Japanese 1.02j patch
+patch_jp_ver='1.02j'
+patch_jp_name="arx_jpn_patch_${patch_jp_ver}.exe"
+patch_jp_url_master="http://www.capcom.co.jp/pc/arx/patch/${patch_jp_name}"
+patch_jp_urls="http://arx.vg/${patch_jp_name}" # master URL is no longer available
+patch_jp_urls="$patch_jp_urls http://web.archive.org/web/${patch_jp_url_master}"
+
+# Name and store page for the GOG.com download
+gog_names='setup_arx_fatalis.exe setup_arx_fatalis_2.0.0.7.exe'
+gog_url='http://www.gog.com/gamecard/arx_fatalis'
+
+# Store page for the Steam download
+steam_appid='1700'
+steam_url="http://store.steampowered.com/app/$steam_appid/"
+steam_install_dir="Arx Fatalis"
+
+# Name and wiki page for the demo download
+demo_names="arx_demo_english.zip arxdemoenglish.zip arx_demo_english.exe"
+demo_names="$demo_names arx_fatalis_demo_fr.zip arx_demo_german.exe arx_jpn_demo.exe"
+demo_url='http://arx.vg/Getting_the_game_data#Demo'
+
+bug_tracker_url='http://bugs.arx-libertatis.org/'
+
+cabextract_url='http://www.cabextract.org.uk/'
+innoextract_url='http://constexpr.org/innoextract/'
+
+
+##########################################################################################
+# Standard directories
+
+user_pwd="$PWD"
+user_pwd="${user_pwd%/}"
+platform="$(uname)"
+command="$(basename "$0")"
+scommand="$(printf '%s' "$command" | tr - _)"
+if [ "$platform" = 'Darwin' ] ; then
+ # Mac OS X
+ data_dirs='/Applications'
+ data_home="$HOME/Library/Application Support"
+ config_home="$HOME/Library/Application Support"
+ data_dir_suffixes='ArxLibertatis'
+ user_dir_suffixes='ArxLibertatis'
+ config_dir_suffixes='ArxLibertatis'
+ downloads_dir="$HOME/Downloads"
+else
+ # Linux, FreeBSD, ...
+ data_dirs="${XDG_DATA_DIRS:-"/usr/local/share/:/usr/share/"}:/opt"
+ data_home="${XDG_DATA_HOME:-"$HOME/.local/share"}"
+ config_home="${XDG_CONFIG_HOME:-"$HOME/.config"}"
+ data_dir_suffixes='games/arx:arx'
+ user_dir_suffixes='arx'
+ config_dir_suffixes='arx'
+ [ -f "${config_home}/user-dirs.dirs" ] && . "${config_home}/user-dirs.dirs"
+ downloads_dir="${XDG_DOWNLOAD_DIR:-"$HOME/Downloads"}"
+fi
+downloads_dir="${downloads_dir%/}"
+tempdir="${TMPDIR:-"/tmp"}"
+tempdir="${tempdir%/}"
+[ -d "$tempdir" ] || tempdir="$PWD"
+eval "data_path=\"\$${scommand}_PATH\""
+[ -z "$data_path" ] && data_path="$arx_PATH"
+
+
+##########################################################################################
+# Helper functions
+
+exec 4>&2 # fd to the original stderr (we redirect output to a log file in some cases)
+logfile='' # log file receiving sdout and stderr
+
+true=0 # Return value / exit status that evaluates to true
+false=1 # Return value / exit status that evaluates to false
+
+# 1 if the script is being run as root, false otherwise
+if [ "$(id -u)" = 0 ] ; then is_root=1 ; else is_root=0 ; fi
+
+# Print one line of text, without escape codes or other shell-specific shenanigans.
+# Seriously, shells, you can't even agree on a consistent implementation of echo?
+# Usage: print <text>
+print() {
+ printf '%s\n' "$1"
+}
+
+puts() {
+ printf '%s' "$1"
+}
+
+disabled_commands=' ' # List of commands that should not be used, even if they exist
+
+# Make `have` return false for a comand
+# Usage: disable_command <command>
+disable_command() {
+ disabled_commands="$disabled_commands$1 "
+}
+
+# Check if a command is available.
+# Usage: have <command>
+# Return: $true if the command is available, $false otherwise
+have() {
+ case "$disabled_commands" in *" $1 "*) return $false ; esac
+ command -v "$1" > /dev/null 2>&1
+}
+
+# Make a path absolute no matter if it is relative or not
+# Usage: abspath <path>
+# Too bad we can't just use readlink -m
+abspath() {
+ case "$1" in
+ /*) print "$1" ;;
+ *) print "$PWD/$1" ;;
+ esac
+}
+
+# Get the canonical representation of an existing path
+# Usage: canonicalize <path>
+# Too bad we can't just use readlink -f
+if have realpath ; then
+ canonicalize() { realpath "$1" ; }
+elif have grealpath ; then
+ canonicalize() { grealpath "$1" ; }
+elif have greadlink ; then
+ canonicalize() { greadlink -f "$1" ; }
+else
+ canonicalize() {
+ _canonicalize_old_pwd="$PWD"
+ _canonicalize_file="$1"
+ while true ; do
+ cd "$(dirname "$_canonicalize_file")"
+ _canonicalize_file="$(basename "$_canonicalize_file")"
+ [ -L "$_canonicalize_file" ] || break;
+ _canonicalize_file="$(readlink "$_canonicalize_file")"
+ done
+ echo "$(pwd -P)/$_canonicalize_file"
+ cd "$_canonicalize_old_pwd"
+ }
+fi
+
+cleanup_functions='' # List of functions to be run on exit
+
+# Add a function to ron on exit.
+# Functions are un in the order they are added.
+# Usage: on_exit <code>
+# Cleanup functions will receive one argument: the exit message if any or an empty string.
+on_exit() {
+ [ -z "$cleanup_functions" ] || cleanup_functions=" $cleanup_functions"
+ cleanup_functions="$1$cleanup_functions"
+}
+
+# Run exit runctions.
+cleanup() {
+ _cleanup_functions="$cleanup_functions" ; cleanup_functions=''
+ [ -z "$_cleanup_functions" ] && return
+ eval "for _cleanup_func in $_cleanup_functions ; do \"\$_cleanup_func\" \"\$@\" ; done"
+}
+
+# Register our cleanup handler.
+trap "cleanup" EXIT
+# Some shells don't have their own (non-libc) SIGINT handler, but the EXIT trap
+# won't trigger if there is none!
+trap 'print >&4 ; quit 1' INT
+
+# Run cleanup functions with a possible message and then exit.
+# Usage: quit <status> [<message>]
+quit() {
+ cleanup "$2"
+ exit $1
+}
+
+# Exit with a non-zero status and optionally print a message.
+# Usage: die [<message>...]
+die() {
+ _die_message=''
+ if [ $# -gt 0 ] ; then
+ _die_message="$1" ; shift
+ for _die_arg ; do _die_message="$_die_message $_die_arg" ; done
+ _die_message="$_die_message
+
+If you think this is a bug in the install script
+please report the complete output at
+ $bug_tracker_url"
+ if [ ! -z "$logfile" ] && [ -f "$logfile" ] ; then
+ _die_message="$_die_message
+
+Also attach the contents of
+ $logfile"
+ logfile='' # so that we don't remove it on exit
+ printf "${red}%s${reset}\\n" "$_die_message" >&4 # also print to priginal stdout
+ printf '\n%s\n' 'Preserving log file.' >&4
+ fi
+
+ printf "${red}%s${reset}\\n" "$_die_message"
+ fi
+ quit 1 "$_die_message"
+}
+
+# Escape a string from stdin for use in a whitespace-seperated list.
+# Usage: print <string> | escape_pipe
+escape_pipe() {
+ sed "s:[^a-zA-Z0-9/_.$1]:\\\\&:g"
+}
+
+# Escape a string for use in a whitespace-seperated list.
+# Usage: escape <string>
+escape() {
+ print "$1" | escape_pipe "$2"
+}
+
+# Convert a colon-seperated list into an escaped whitespace-seperated list.
+# Usage: to_list <colon-list>
+to_list() {
+ escape "$1" | sed 's/\\:/ /g'
+}
+
+# Line-based output into a list
+# Usage: ls | lines_to_list
+lines_to_list() {
+ escape_pipe | tr '\n' ' '
+}
+
+# Check if a whitespace separated list contains a string.
+# Usage: list_contains <list-var> <needle>
+list_contains() {
+ eval "_list_contents=\"\$$1\""
+ [ -z "$_list_contents" ] && return $false
+ eval "for _list_contains_entry in $_list_contents ; do" \
+ " [ \"\$_list_contains_entry\" = \"\$2\" ] && return \$true ; done"
+ return $false
+}
+
+# Append a string to a whitespace separated list.
+# Usage: list_append <list-var> <string> [comment]
+# Whitespace seperated lists can be loaded into the argument list using:
+# eval "set -- $var"
+list_append() {
+ _list_entry="$(escape "$2")"
+ eval "_list_contents=\"\$$1\""
+ if [ -z "$_list_contents" ]
+ then eval "$1=\"\$_list_entry\""
+ else eval "$1=\"\$_list_contents \$_list_entry\""
+ fi
+ eval "[ -z \"\$$1__list_count\" ] && $1__list_count=0"
+ eval "_list_count=\$$1__list_count"
+ eval "$1__list_comment_$_list_count=\"\$3\""
+ eval "$1__list_count=\$(($1__list_count + 1))"
+}
+
+# Append one list to another, preserving comments.
+# Usage: list_merge <list-var> <append-list-var>
+list_merge() {
+ eval "_list_append=\"\$$2\""
+ [ -z "$_list_append" ] && return
+ eval "
+ _list_merge_i=0
+ for _list_merge_entry in $_list_append ; do
+ list_append $1 \"\$_list_merge_entry\" \"\$(list_comment $2 \$_list_merge_i)\"
+ _list_merge_i=\$((_list_merge_i + 1))
+ done
+ "
+}
+
+# Get a comment associated with alist entry
+# Usage: list_comment <list-var> <index>
+list_comment() {
+ eval "print \"\$$1__list_comment_$2\""
+}
+
+# Set a comment associated with alist entry
+# Usage: list_comment <list-var> <index> <comment>
+set_list_comment() {
+ eval "$1__list_comment_$2=\"\$3\""
+}
+
+# Append a string to a whitespace separated list if it isn't already in the list.
+# Usage: set_append <list-var> <string> [comment]
+set_append() {
+ if ! list_contains "$1" "$2" ; then
+ list_append "$1" "$2" "$3"
+ fi
+}
+
+# Check if a directory contains a file while ignoring case differences.
+# Usage: icontains <dir> <filename>
+icontains() {
+ [ ! -z "$(find "$1" -mindepth 1 -maxdepth 1 -iname "$2")" ]
+}
+
+# Check if a directory or file is writable or can be created.
+# Usage: is_writable <path>
+is_writable() {
+ [ -w "$1" ] && return $true
+ [ ! -e "$1" ] && is_writable "$(dirname "$1")"
+}
+
+# Create a directory and die with a message on error.
+# Usage: create_dir <path> <type>
+create_dir() {
+ mkdir -p "$1" || die "Could not create $2 directory: $1"
+}
+
+probe_file_dirs=''
+set_append probe_file_dirs "$user_pwd"
+set_append probe_file_dirs "$downloads_dir"
+set_append probe_file_dirs "$HOME"
+set_append probe_file_dirs "$tempdir"
+
+# Find a file in standard directories.
+# Usage: probe_file <command> <filename> [comment]
+# Will call `command <file>` for each file found.
+probe_file() {
+ eval "for _probe_file_d in $probe_file_dirs ; do [ -f \"\$_probe_file_d/\$2\" ] && \$1 \"\$_probe_file_d/\$2\" \"\$3\" && return \$true ; done"
+}
+
+# Find files in standard directories.
+# Usage: probe_file <command> <list> [comment]
+# Will call `command <file>` for each file found.
+probe_files() {
+ [ -z "$2" ] && return $false
+ eval "for _probe_files_file in $2 ; do probe_file \"\$1\" \"\$_probe_files_file\" \"\$3\" && return \$true ; done"
+ return $false
+}
+
+
+##########################################################################################
+# Parse command-line arguments
+
+extract_zip_reqs=''
+list_append extract_zip_reqs 'bsdtar' 'libarchive'
+list_append extract_zip_reqs 'unzip'
+list_append extract_zip_reqs '7za'
+list_append extract_zip_reqs '7z' 'p7zip'
+extract_ms_cab_reqs=''
+list_append extract_ms_cab_reqs 'bsdtar' 'with libarchive 3.1+'
+list_append extract_ms_cab_reqs 'cabextract' "$cabextract_url"
+list_append extract_ms_cab_reqs '7za'
+list_append extract_ms_cab_reqs '7z' 'p7zip'
+extract_installshield_reqs=''
+list_append extract_installshield_reqs 'unshield'
+extract_rar_reqs=''
+list_append extract_rar_reqs 'unrar'
+extract_ace_reqs=''
+list_append extract_ace_reqs 'unace'
+extract_innosetup_reqs=''
+list_append extract_innosetup_reqs 'innoextract' "$innoextract_url"
+mount_cdrom_reqs=''
+list_append mount_cdrom_reqs 'fuseiso'
+extract_iso_reqs=''
+list_append extract_iso_reqs 'isoinfo'
+list_append extract_iso_reqs 'bsdtar' 'libarchive'
+list_append extract_iso_reqs '7z' 'p7zip'
+extract_cdrom_reqs=''
+list_merge extract_cdrom_reqs mount_cdrom_reqs
+list_merge extract_cdrom_reqs extract_iso_reqs
+download_reqs=''
+list_append download_reqs 'wget'
+list_append download_reqs 'curl'
+list_append download_reqs 'fetch' 'FreeBSD'
+
+printf '%s %s\n' "${white}Welome to the ${green}Arx Fatalis${white} ${patch_ver} data" \
+ "install script for UNIX-like systems!${reset}"
+
+patchfile='' # Main patch file
+patchfile_jp='' # Japanese patch file
+sourcefile='' # Source file or directory
+datadir='' # Output data directory
+batch=0 # Never wait for user input
+gui=0 # Display a graphical user interface (command-line interface otherwise)
+install=1 # Install new non-patch files
+selected_stuff=0 # Has the user made a selection
+installed_stuff=0 # Have we already installed anything?
+patch=1 # Install patch files if needed
+probe_patch=1 # Look for patch files in standard locations and download if needed
+redirect_log=1 # Redirect standard output/error output to a log file in GUI mode
+
+# Enable compatiblity with old install-* scripts.
+# Usage: enable_compat_mode <help-flag> <sourcefile> <patchfile> <datadir>
+enable_compat_mode() {
+ print \
+ "${yellow}Enabling compatibility mode for ${pink}$command${yellow}.${reset}
+
+${dim_yellow}The individual ${dim_pink}install-*${dim_yellow} scripts have been merged.
+Rename this script to something else (like ${dim_pink}arx-install-data${dim_yellow}) to unlock its full power!${reset}
+" >&2
+ batch=1
+ probe_patch=0
+ if [ -z "$1" ] || [ "$1" = '--help' ] || [ "$1" = '-h' ] ; then
+ printf '%s\n\n%s\n' "$5" \
+ "${yellow}More options are available in the non-compatiblity mode.${reset}"
+ exit $false
+ fi
+ if [ -z "$2" ] ; then install=0 ; else sourcefile="$2" ; fi
+ if [ -z "$3" ] ; then patch=0 ; else patchfile="$3" ; fi
+ if [ -z "$4" ] ; then datadir="$user_pwd" ; else datadir="$4" ; fi
+}
+
+case "$command" in
+
+install-cd)
+[ "$1" = "--no-progress" ] && shift # ignore - not supported
+enable_compat_mode "$1" "$1" "$2" "$3" "\
+Usage: $command path/to/mount/point/ path/to/ArxFatalis_1.21_MULTILANG.exe [output_dir]
+or $command path/to/cd.iso path/to/ArxFatalis_1.21_MULTILANG.exe [output_dir]" ;;
+
+install-copy)
+enable_compat_mode "$1" "$1" '' "$2" "\
+Usage: $command path/to/ArxFatalis/ [output_dir]" ;;
+
+install-demo)
+enable_compat_mode "$1" "$1" '' "$2" "\
+Usage: $command path/to/arx_demo_english.zip [output_dir]" ;;
+
+install-gog)
+[ "$1" = "--no-progress" ] && shift # ignore - not supported
+enable_compat_mode "$1" "$1" '' "$2" "\
+Usage: $command path/to/setup_arx_fatalis.exe [output_dir]" ;;
+
+install-verify)
+enable_compat_mode "$1" '' '' "$1" "\
+Usage: $command [directory]" ;;
+
+*) # non-compatibility mode
+
+# Print elements in a list, joined by ' or '
+# Usage: print_help_or <list-var> [color]
+print_help_or() {
+ _print_help_or_var=$1
+ eval "_print_help_or_list=\"\$$1\""
+ _print_help_or_color="$2"
+ [ -z "$1" ] && return
+ eval "
+ _print_help_or_i=0
+ for _print_help_or_entry in $_print_help_or_list ; do
+ [ \$_print_help_or_i = 0 ] || puts ' or '
+ printf '%s%s' \"\$_print_help_or_color\" \"\$_print_help_or_entry\"
+ [ -z \"\$_print_help_or_color\" ] || puts \"\$reset\"
+ _print_help_or_comment=\"\$(list_comment \$_print_help_or_var \$_print_help_or_i)\"
+ [ -z \"\$_print_help_or_comment\" ] || printf ' (%s)' \"\$_print_help_or_comment\"
+ _print_help_or_i=\$((_print_help_or_i + 1))
+ done
+ "
+}
+
+# Print elements in a list, one per line.
+# Usage: print_help_list <prefix-format> <list-var>
+# prefi-format will receive one argument: the list index starting at 1
+print_help_list() {
+ _print_help_list_prefix="$1"
+ _print_help_list_var=$2
+ eval "_print_help_list_list=\"\$$2\""
+ [ -z "$1" ] && return
+ eval "
+ _print_help_list_i=0
+ for _print_help_list_entry in $_print_help_list_list ; do
+ case \"\$_print_help_list_prefix\" in
+ *%*) printf \"\$_print_help_list_prefix\" \$((_print_help_list_i + 1)) ;;
+ *) puts \"\$_print_help_list_prefix\"
+ esac
+ printf \"%s\${reset}\" \"\$_print_help_list_entry\"
+ _print_help_list_comment=\"\$(list_comment \$_print_help_list_var \$_print_help_list_i)\"
+ [ -z \"\$_print_help_list_comment\" ] || printf ' (%s)' \"\$_print_help_list_comment\"
+ printf '\n'
+ _print_help_list_i=\$((_print_help_list_i + 1))
+ done
+ "
+}
+
+# Print help output.
+# Usage: print_help [<error-message>]
+print_help() {
+ [ -z "${1-}" ] || ( printf '%s\n\n' "${red}$1${reset}" )
+ print "
+${white}Start the script without any arguments to select paths interactively:
+ \$ $command${reset}
+
+Usage: $command [--source] source [--patch patchfile] [[--data-dir] datadir]
+ $command [--patch patchfile] [--data-dir datadir]
+ $command --verify [[--data-dir] datadir]
+
+ ${green}-s, --source PATH${reset} Path to the source file or directory
+ ${cyan}-d, --data-dir DIR${reset} Where to install the data
+ ${blue}-p, --patch FILE${reset} Path to the ${patch_ver} patch file
+ --patch-jp FILE Path to the ${patch_jp_ver} Japanese patch file
+ -v, --verify Only verify the files in the data-dir, don't install new ones,
+ except for patch files.
+ -n, --no-patch Don't use a patch file unless explicitly specified.
+ -h, --help Print this message and maybe more
+ -b, --batch Never ask the user questions
+ -g, --gui Show a GUI asking the user what to do
+ Requires ${dim_pink}KDialog${reset}, ${dim_pink}Zenity${reset}, or ${dim_pink}Xdialog${reset}.
+ If none of them are available the script is re-launched
+ in a terminal emulator.
+ -c, --cli Interactively ask the user to select files/directories (no GUI)
+ --no-redirect-log Don't redirect output to a log file when in GUI mode
+ --disable-COMMAND Don't use the given tool, even if it exists.
+ Valid values are 7z, 7za, aterm, bsdtar, cabextract, curl,
+ dcop, fetch, fuseiso, fusermount, gnome-terminal, greadlink,
+ grealpath, gtkterm, gxmessage, innoextract, isoinfo, kdialog,
+ konsole, md5, md5sum, mount, qdbus, realpath, rxvt, umount,
+ unace, unrar, unshield, unzip, urxvt, wget, Xdialog,
+ xmessage, xterm, x-terminal-emulator, zenity.
+
+--gui is enabled by default if there are no arguments *and* stdin, stdout or stderr is not a terminal
+"
+ [ ! -z "${1-}" ] && exit $false
+ help_innosetup="$(print_help_or extract_innosetup_reqs "$dim_pink")"
+ help_cdrom="$(print_help_or extract_cdrom_reqs "$dim_pink") or root access"
+ help_cab="$(print_help_or extract_ms_cab_reqs "$dim_pink")"
+ help_zip="$(print_help_or extract_zip_reqs "$dim_pink")"
+ help_unshield="$(print_help_or extract_installshield_reqs "$dim_pink")"
+ help_rar="$(print_help_or extract_rar_reqs "$dim_pink")"
+ help_ace="$(print_help_or extract_ace_reqs "$dim_pink")"
+ help_download="$(print_help_or download_reqs "$dim_pink")"
+ help_optpatch="may use the 1.21 patch file and require ${help_innosetup} if not already patched"
+ help_probe_file_dirs="
+ a) the current working directory (\$PWD): $user_pwd
+ b) the user's downloads directory (\$XDG_DOWNLOAD_DIR): $downloads_dir
+ c) the user's home directory (\$HOME): $HOME
+ d) the temp directory (\$TMPDIR): $tempdir"
+ help_probe_file_dirs_patch="${help_probe_file_dirs}
+ e) the directory containing the source file"
+ help_probed_files="$gog_names $demo_names"
+ print "
+The ${pink}dependencies${reset} required by the ${command} script depend on the source files.
+However, you always need either ${dim_pink}md5sum${reset} or ${dim_pink}md5${reset}.
+
+
+The ${green}source${reset} can be one of many things:
+
+ * ${white}Mounted Arx Fatalis ${green}cdrom${reset}
+ requires:
+ - ${help_cab}
+ - ${help_innosetup}
+ needs the 1.21 patch file
+
+ * ${white}Arx Fatalis cdrom ${green}ISO${white} image / device file${reset}
+ requires:
+ - ${help_cdrom}
+ - ${help_cab}
+ - ${help_innosetup}
+ needs the 1.21 patch file
+
+ * ${white}Arx Fatalis installer from ${green}GOG.com${white}${reset} ($(print_help_or gog_names))
+ requires:
+ - ${help_innosetup}
+ never uses the 1.21 patch file
+ get it from ${dim_green}${gog_url}${reset}
+
+ * ${green}Installed${white} copy of Arx Fatalis${reset} (for example from ${green}Steam${reset})
+ ${help_optpatch}
+ get it from ${dim_green}${steam_url}${reset}
+
+ * ${white}Arx Fatalis ${green}demo${reset} (one of the following)
+$(print_help_list " - " demo_names)
+ requires:
+ - ${help_zip} [english/french .zip]
+ - ${help_cab}
+ - ${help_rar} [english .exe]
+ - ${help_ace} [german .exe]
+ - ${help_unshield} [japanese .exe]
+ never uses the 1.21 patch file
+ get it from ${dim_green}${demo_url}${reset}
+
+ * ${white}Extracted Arx Fatalis demo installer${reset}
+ requires:
+ - ${help_cab}
+ never uses the 1.21 patch file
+
+ * ${white}Installed copy of the Arx Fatalis demo${reset}
+ never uses the 1.21 patch file
+
+If no source is specified, these files will be probed:
+1. The following files in${help_probe_file_dirs}
+$(print_help_list " 1.%d ${green}" help_probed_files)
+2. The user's ${green}Steam${reset} library, if available
+3. If \$WINEPREFIX is set, any installation in there
+4. Any installation in the default WINEPREFIX (${green}~/.wine${reset})
+5. Any mounted ${green}cdrom${reset} or ISO file
+
+
+If no ${blue}patch${reset} file is specified, but is needed and
+the --no-patch option wasn't specified:
+1. Try to find the following files in${help_probe_file_dirs_patch}
+ 1.1. ${blue}${patch_name}${reset}
+ 1.2. $(printf "$patch_name_localized" '<LANG>')
+ Where <LANG> is one of EN, ES, FR, GE, IT, RU,
+ depending on the language of the data files.
+2. Downloaded from:
+$(print_help_list " - ${dim_blue}" patch_urls)
+Downloading the patch file requires ${help_download}.
+Extracting the ${patch_ver} patch file requires ${help_innosetup}.
+
+For the Japanese version, if no ${blue}patch-jp${reset} file is specified,
+but is needed and the --no-patch option wasn't specified specified:
+1. Try to find ${blue}${patch_jp_name}${reset} in${help_probe_file_dirs_patch}
+2. Downloaded from:
+$(print_help_list " - ${dim_blue}" patch_jp_urls)
+Downloading the patch file requires ${help_download}.
+Extracting the Japanese patch file requires:
+ - ${help_unshield}
+ - ${help_cab}.
+
+
+If no ${cyan}data-dir${reset} to install into is specified,
+one is automatically selected similarly to how Arx Libertatis would:
+If --verify and --no-patch are give, use the first existing directory
+of the following, otherwise, use the first existing writable directory
+or, if none exists, the first directory that can be created:
+1. Any path in \$${scommand}_PATH or \$arx_PATH (for use in wrapper scripts)
+2. \"\${XDG_DATA_DIRS:-\"/usr/local/share/:/usr/share/\"}:/opt\" / \"$data_dir_suffixes\":"
+ i=1
+ eval "set -- $(to_list "$data_dirs")"
+ for prefix in "$@" ; do
+ eval "set -- $(to_list "$data_dir_suffixes")"
+ for suffix ; do
+ printf " 2.%d. ${dim_cyan}%s${reset}\\n" $i "$prefix/$suffix"
+ i=$((i + 1))
+ done
+ done
+print "3. \"\${XDG_DATA_HOME:-\"\$HOME/.local/share\"}\" / \"$user_dir_suffixes\""
+ i=1
+ eval "set -- $(to_list "$user_dir_suffixes")"
+ for suffix ; do
+ printf " 3.%d. ${dim_cyan}%s${reset}\\n" $i "$data_home/$suffix"
+ i=$((i + 1))
+ done
+ print
+ exit $true
+}
+
+user_is_sane=1
+if [ ! -t 0 ] || [ ! -t 1 ] || [ ! -t 2 ] ; then
+ [ $# = 0 ] && gui=1
+fi
+while [ $# -gt 0 ] ; do
+ case "$1" in
+ --source=*) sourcefile="${1#--source=}" ; install=1 ;;
+ -s|--source) shift ; sourcefile="$1" ; install=1 ;;
+ --data-dir=*) datadir="${1#--data-dir=}" ;;
+ -d|--data-dir) shift ; datadir="$1" ;;
+ --patch=*) patchfile="${1#--patch=}" ; patch=1 ;;
+ -p|--patch) shift ; patchfile="$1" ; patch=1 ;;
+ --patch-jp=*) patchfile_jp="${1#--patch-jp=}" ; patch=1 ;;
+ --patch-jp) shift ; patchfile_jp="$1" ; patch=1 ;;
+ -v|--verify) install=0 patch=0 ;;
+ -n|--no-patch) [ -z "$patchfile" ] && [ -z "$patchfile_jp" ] && patch=0
+ probe_patch=0 ;;
+ -b|--batch) batch=1 ;;
+ -g|--gui) gui=1 ;;
+ -c|--cui|--cli) batch=0 ; gui=0 ;;
+ --no-redirect-log) redirect_log=0 ;;
+ --i-am-insane) user_is_sane=0 ;;
+ --disable-*) disable_command "${1#--disable-}" ;;
+ --disable) shift ; disable_command "${1#--disable-}" ;;
+ -h|--help) print_help ;;
+ -*) print_help "Uknown option: $1" ;;
+ *)
+ if [ -z "${sourcefile-}" ] && [ $install = 1 ] ; then sourcefile="$1"
+ elif [ -z "${datadir-}" ] ; then datadir="$1"
+ else print_help "Too many options: $1" ; fi
+ esac
+ [ -z "${1-}" ] && print_help "Expected more options"
+ shift;
+done
+
+print "See \`${dim_pink}$command --help${reset}\` for available options."
+
+esac
+
+# Make user-provided paths absolute
+[ ! -z "$sourcefile" ] && sourcefile="$(abspath "$sourcefile")"
+[ ! -z "$datadir" ] && datadir="$(abspath "$datadir")"
+[ ! -z "$patchfile" ] && patchfile="$(abspath "$patchfile")"
+
+# Sanity check
+[ $install = 1 ] && [ $batch = 1 ] && [ -z "$sourcefile" ] && [ $user_is_sane = 1 ] \
+ && die "You have used --batch without providing a source file!
+This would just pick the first source file found, which is a bad idea™.
+If you really want this, add the --i-am-insane option."
+
+
+##########################################################################################
+# User interface abstraction
+
+_dialog_title="Arx Fatalis ${patch_ver} data installer"
+
+# Handle magic environment variable to tell the script that it has been launched
+# in its own terminal and should not try to create a GUI.
+if [ $batch = 0 ] && [ "$_arx_install_data_force_cli" = 1 ] ; then
+ trap '_arx_install_data_force_cli=0 ; quit 1' INT
+ printf "\n${yellow}%s${reset}\n\n" \
+ 'Note: Install KDialog, Zenity or Xdialog for a better GUI'
+ wait_exit() {
+ [ "$_arx_install_data_force_cli" = 1 ] && print 'Press enter to exit...' && read f
+ exit $false
+ }
+ on_exit wait_exit
+ gui=0
+ for var in batch install patch probe_patch sourcefile datadir \
+ patchfile patchfile_jp disabled_commands; do
+ eval "$var=\"\$_arx_install_data_force_$var\""
+ done
+fi
+
+# Select the dialog backend to use
+if [ $gui = 1 ] ; then
+
+ # Detect if we are running in a KDE session
+ is_kde=0
+ case "$DESKTOP_SESSION" in *kde*|*KDE*) is_kde=1 ; esac
+ [ -z "$KDE_FULL_SESSION" ] || is_kde=1
+ [ -z "$KDE_SESSION_UID" ] || is_kde=1
+ [ -z "$KDE_SESSION_VERSION" ] || is_kde=1
+
+ # Select the GUI backend, prefer kdialog for KDE sessions, zenity otherwise
+ if [ $is_kde = 1 ] ; then preferred=kdialog ; else preferred=zenity ; fi
+ for backend in $preferred zenity kdialog Xdialog ; do
+ have $backend && gui=$backend && break
+ done
+
+ if [ $gui = 1 ] ; then
+
+ # No dialog backend available
+ # Try opening a graphical terminal and launching the script in there.
+ print 'No GUI dialog backend is available - trying to launch a terminal emulator'
+ term_cmd="$(abspath "$(command -v "$0" 2> /dev/null)")"
+ # Not all terminals accept command arguments in the same way.
+ # Instead of hacking terminal-specific code, use a magic environment
+ # variable to tell the sub-process how to behave.
+ _arx_install_data_force_cli=1
+ export _arx_install_data_force_cli
+ for var in batch install patch probe_patch sourcefile datadir \
+ patchfile patchfile_jp disabled_commands ; do
+ eval "_arx_install_data_force_$var=\"\$$var\""
+ eval "export _arx_install_data_force_$var"
+ done
+ if [ $is_kde = 1 ] ; then preferred=konsole ; else preferred=x-terminal-emulator ; fi
+ for backend in x-terminal-emulator $preferred \
+ aterm urxvt rxvt konsole xterm gnome-terminal
+ do
+ if have $backend ; then
+ $backend -e "$term_cmd" || continue
+ exit $true
+ fi
+ done
+
+ # Hm, that didn't work either - bail
+ message="No GUI dialog backend is available"
+ message="$message - install KDialog, Zenity or Xdialog, or use the --cli option."
+ # Final attempt to let the user know what happened
+ for backend in gxmessage xmessage ; do
+ if have $backend ; then
+ $backend -center -buttons OK "$_dialog_title
+
+$message"
+ break
+ fi
+ done
+ die "$message"
+
+ fi
+
+ # We don't need colors for the UI, but they may cause problems - get rid of them
+ disable_color
+
+ if [ $redirect_log = 1 ] ; then
+ # Redirect all further output into a log file
+ logfile="$(abspath "$(mktemp "$tempdir/arx-install-data.log.XXXXX")")"
+ clean_logfile() {
+ [ -z "$logfile" ] || rm -f "$logfile"
+ }
+ on_exit clean_logfile
+ print "Enabling GUI mode, standard output/error saved to $logfile"
+ print "Use the --cli option for an interactive command-line interface."
+ exec > "$logfile" 2>&1
+ else
+ print "Enabling GUI mode..."
+ print "Use the --cli option for an interactive command-line interface."
+ fi
+
+elif [ ! "$_arx_install_data_force_cli" = 1 ] ; then
+
+ print "Enabling CLI mode, use the --gui option for a graphical interface."
+
+fi
+
+#----------------------------------------------------------------------------------------#
+# Functions for controlling an asynchronous process via stdin
+
+pipe_file=''
+pipe_pid=0
+
+# Run a command in the background and open a pipe to pass commmands to it
+# Usage: pipe_create <command> [<args>...]
+pipe_create() {
+
+ pipe_destroy
+
+ # Pipe commands via a FIFO or, if that fails, via a regular file
+ pipe_file="$(mktemp -u "$tempdir/arx-install-data.pipe.XXXXX")"
+ if mkfifo -m 600 "$pipe_file" 2> /dev/null ; then
+ # Fast communication via a FIFO
+ "$@" < "$pipe_file" &
+ pipe_pid="$!"
+ else
+ # Fallback via regular file, may use polling
+ pipe_file="$(mktemp "$tempdir/arx-install-data.pipe.XXXXX")"
+ [ -z "$pipe_file" ] && return $false
+ tail -f "$pipe_file" | "$@" &
+ pipe_pid="$!"
+ fi
+
+ # Open fd 3 for writing into the pipe
+ exec 3> "$pipe_file"
+
+ return $true
+}
+
+# Kill the program created via pipe_create and cleanup files
+# Usage: pipe_destroy
+pipe_destroy() {
+
+ # Close fd pointing to the pipe
+ exec 3<&-
+
+ # Remove the FIFO or temp file
+ [ -z "$pipe_file" ] || rm -f "$pipe_file" > /dev/null 2>&1
+ pipe_file=''
+
+ # Terminate the remote process
+ [ "$pipe_pid" = 0 ] || kill "$pipe_pid" > /dev/null 2>&1
+ pipe_pid=0
+
+ return $true
+}
+
+# Check if the remote process is running
+# Usage: pipe_exists || print 'oh noes'
+pipe_exists() {
+ [ -z "$pipe_file" ] && return $false
+ [ "$pipe_pid" = 0 ] && return $false
+ kill -s 0 "$pipe_pid" > /dev/null 2>&1
+}
+
+# Send a message to the remote process
+# Usage: pipe_write <command>
+pipe_write() {
+ [ -z "$pipe_file" ] || print "$1" >&3
+}
+
+#----------------------------------------------------------------------------------------#
+# Code for the different GUI/CLI implementations
+# Each implementation exposes dialog_* primitives that are used by generic functions.
+
+case $gui in
+
+#----------------------------------------------------------------------------------------#
+zenity)
+
+# Helper functions
+
+# Run Zenity
+# Usage: zenity_run <title-prefix> <dialog-type> [<args>...]
+zenity_run() {
+ _zenity_run_t="$1" ; shift
+ [ -z "$_zenity_run_t" ] || _zenity_run_t="$_zenity_run_t - "
+ zenity --title "$_zenity_run_t$_dialog_title" "$@"
+}
+
+# Dialog abstraction
+
+# Create the main progress window.
+# Usage: dialog_create
+dialog_create() {
+ pipe_create zenity --title "$_dialog_title" --width 450 --progress
+}
+
+# Destroy the main progress window.
+# Usage: dialog_destroy
+dialog_destroy() {
+ pipe_destroy
+}
+
+# Show an error dialog.
+# Usage: dialog_error <message>
+dialog_error() {
+ zenity_run 'Error' --error --no-wrap --text="$1"
+}
+
+# Show a message box.
+# Usage: dialog_message <message>
+dialog_message() {
+ zenity_run 'Status' --info --no-wrap --text="$1"
+}
+
+# Ask a yes/no question.
+# Usage: dialog_ask <question>
+dialog_ask() {
+ zenity_run 'Confirm' --question --no-wrap --ok-label=Yes --cancel-label=No --text="$1"
+}
+
+# Has the user quested to cancel the operation?
+# Usage: dialog_cancelled && print "cancelled"
+dialog_cancelled() {
+ ! pipe_exists
+}
+
+# Set the status text.
+# Usage: dialog_set_text <text>
+dialog_set_text() {
+ pipe_write "#$1"
+}
+
+# Set if the progress bar should continously animate instead of showing the value.
+# Usage: dialog_set_pulsate <enable>
+dialog_set_pulsate() {
+ if [ $1 = 1 ]
+ then pipe_write "pulsate:true"
+ else pipe_write "pulsate:false"
+ fi
+}
+
+# Set the current progress value.
+# Usage: dialog_set_value <percentage>
+dialog_set_value() {
+ pipe_write "$1"
+}
+
+# Select an entry in a list.
+# Usage dialog_select_entry <var> <label> <tag1> <item1> [ <tag2> < item2> ... ]
+dialog_select_entry() {
+ _zenity_select_entry_v="$1" ; shift
+ _zenity_select_entry_t="$1" ; shift
+ _zenity_select_entry_r="$(
+ zenity_run 'Select path' --width 550 --height 300 \
+ --list --text="$_zenity_select_entry_t" \
+ --column '#' --column 'Path' --hide-column=1 "$@" --hide-header
+ )"
+ [ -z "$_zenity_select_entry_r" ] && return $false
+ eval "$_zenity_select_entry_v=\"\$_zenity_select_entry_r\""
+ return $true
+}
+
+# dialog_select_path does not support the --any flag
+dialog_select_path_any=0
+
+# Let the user select a path.
+# Usage: dialog_select_path (--file|--dir|--any) <result-var> <label>
+# Any is only supported if $dialog_select_path_any is 1.
+dialog_select_path() {
+ case "$1" in
+ --any) die 'not implemented' ;;
+ --file) _zenity_select_path_f='--file-selection' ;;
+ --dir) _zenity_select_path_f='--file-selection --directory' ;;
+ esac
+ _zenity_select_path="$(
+ eval "zenity_run \"\$3\" $_zenity_select_path_f" 2> /dev/null
+ )"
+ [ -z "$_zenity_select_path" ] && return $false
+ eval "$2=\"\$_zenity_select_path\""
+ return $true
+}
+
+dialog_retry() {
+ zenity_run 'Error' --question --no-wrap --text="$1" \
+ --ok-label='Retry' --cancel-label='Ignore'
+ case $? in
+ 0) dialog_retry_choice='retry' ;;
+ 1) dialog_retry_choice='ignore' ;;
+ *) dialog_retry_choice='abort' ;;
+ esac
+}
+
+;;
+
+#----------------------------------------------------------------------------------------#
+kdialog)
+
+# Helper functions
+
+kdialog_handle='' # dbus/dcop handle for the main progress window
+
+# Send a message to the main KDialog instance non-_q variants hide all output
+kdialog_qdbus_q() { have qdbus && eval "qdbus $kdialog_handle \"\$@\"" 2> /dev/null ; }
+kdialog_qdbus() { kdialog_qdbus_q "$@" > /dev/null ; }
+kdialog_dcop_q() { have dcop && eval "dcop $kdialog_handle \"\$@\"" 2> /dev/null ; }
+kdialog_dcop() { kdialog_dcop_q "$@" > /dev/null ; }
+kdialog_cmd_q() { kdialog_qdbus_q "$@" || kdialog_dcop_q "$@" ; }
+kdialog_cmd() { kdialog_cmd_q "$@" > /dev/null ; }
+
+# Run KDialog
+# Usage: kdialog_run <title-prefix> <dialog-type> [<args>...]
+kdialog_run() {
+ _kdialog_run_t="$1" ; shift
+ [ -z "$_kdialog_run_t" ] || _kdialog_run_t="$_kdialog_run_t - "
+ kdialog --icon arx-libertatis --title "$_kdialog_run_t$_dialog_title" "$@"
+}
+
+# Dialog abstraction
+
+# Create the main progress window.
+# Usage: dialog_create
+dialog_create() {
+ dialog_destroy
+ _kdialog_force_width='WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW'
+ kdialog_handle="$(kdialog_run '' --progressbar "$_kdialog_force_width" 0)"
+ [ -z "$kdialog_handle" ] && return $false
+ kdialog_cmd showCancelButton true
+ return $true
+}
+
+# Destroy the main progress window.
+# Usage: dialog_destroy
+dialog_destroy() {
+ [ -z "$kdialog_handle" ] && return $true
+ kdialog_cmd close
+ kdialog_handle=''
+}
+
+# Show an error dialog.
+# Usage: dialog_error <message>
+dialog_error() {
+ kdialog_run 'Error' --error "$1" > /dev/null 2> /dev/null
+}
+
+# Show a message box.
+# Usage: dialog_message <message>
+dialog_message() {
+ kdialog_run 'Status' --msgbox "$1" > /dev/null 2> /dev/null
+}
+
+# Ask a yes/no question.
+# Usage: dialog_ask <question>
+dialog_ask() {
+ kdialog_run 'Confirm' --warningyesno "$1" > /dev/null 2> /dev/null
+}
+
+# Has the user quested to cancel the operation?
+# Usage: dialog_cancelled && print "cancelled"
+dialog_cancelled() {
+ [ "$(kdialog_cmd_q wasCancelled || print true)" = true ]
+}
+
+# Set the status text.
+# Usage: dialog_set_text <text>
+dialog_set_text() {
+ kdialog_qdbus setLabelText "$1" || kdialog_dcop setLabel "$1"
+}
+
+# Set if the progress bar should continously animate instead of showing the value.
+# Usage: dialog_set_pulsate <enable>
+dialog_set_pulsate() {
+ _kdialog_max=100
+ [ $1 = 1 ] && _kdialog_max=0
+ kdialog_qdbus Set "" maximum $_kdialog_max || kdialog_dcop setMaximum $_kdialog_max
+}
+
+# Set the current progress value.
+# Usage: dialog_set_value <percentage>
+dialog_set_value() {
+ kdialog_qdbus Set "" value "$1" || kdialog_dcop setProgress "$1"
+ [ $1 = 100 ] && kdialog_cmd showCancelButton true
+}
+
+# Select an entry in a list.
+# Usage dialog_select_entry <var> <label> <tag1> <item1> [ <tag2> < item2> ... ]
+dialog_select_entry() {
+ _kdialog_select_entry_v="$1" ; shift
+ _kdialog_select_entry_t="$1" ; shift
+ _kdialog_select_entry_w=" "
+ _kdialog_select_entry_w="$_kdialog_select_entry_w$_kdialog_select_entry_w"
+ _kdialog_select_entry_r="$(
+ kdialog_run 'Select path' \
+ --menu "$_kdialog_select_entry_t$_kdialog_select_entry_w" "$@" 2> /dev/null
+ )"
+ [ -z "$_kdialog_select_entry_r" ] && return $false
+ eval "$_kdialog_select_entry_v=\"\$_kdialog_select_entry_r\""
+ return $true
+}
+
+# dialog_select_path does not support the --any flag
+dialog_select_path_any=0
+
+# Let the user select a path.
+# Usage: dialog_select_path (--file|--dir|--any) <result-var> <label>
+# Any is only supported if $dialog_select_path_any is 1.
+dialog_select_path() {
+ case "$1" in
+ --any) die 'not implemented' ;;
+ --file) _kdialog_select_path_f=--getopenfilename ;;
+ --dir) _kdialog_select_path_f=--getexistingdirectory ;;
+ esac
+ _kdialog_select_path="$(
+ kdialog_run "$3" $_kdialog_select_path_f "$HOME" 2> /dev/null
+ )"
+ [ -z "$_kdialog_select_path" ] && return $false
+ eval "$2=\"\$_kdialog_select_path\""
+ return $true
+}
+
+dialog_retry() {
+ kdialog_run 'Error' \
+ --yes-label 'Retry' --no-label 'Ignore' --cancel-label 'Abort' \
+ --warningyesnocancel "$1" 2>&1
+ case $? in
+ 0) dialog_retry_choice='retry' ;;
+ 1) dialog_retry_choice='ignore' ;;
+ *) dialog_retry_choice='abort' ;;
+ esac
+}
+
+;;
+
+#----------------------------------------------------------------------------------------#
+Xdialog)
+
+# Helper functions
+
+# Run Xdialog
+# Usage: Xdialog_run <title-prefix> <dialog-type> [<args>...]
+Xdialog_run() {
+ _Xdialog_run_t="$1" ; shift
+ [ -z "$_Xdialog_run_t" ] || _Xdialog_run_t="$_Xdialog_run_t - "
+ Xdialog --left --title "$_Xdialog_run_t$_dialog_title" "$@"
+}
+
+# Dialog abstraction
+
+# Create the main progress window.
+# Usage: dialog_create
+dialog_create() {
+ _Xdialog_width='WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW'
+ pipe_create Xdialog --left --title "$_dialog_title" --gauge "$_Xdialog_width" 0 0
+}
+
+# Destroy the main progress window.
+# Usage: dialog_destroy
+dialog_destroy() {
+ pipe_destroy
+}
+
+# Show an error dialog.
+# Usage: dialog_error <message>
+dialog_error() {
+ dialog_message "$1" # no dedicated error box for Xdialog
+}
+
+# Show a message box.
+# Usage: dialog_message <message>
+dialog_message() {
+ Xdialog_run 'Status' --msgbox "$1" 0 0
+}
+
+# Ask a yes/no question.
+# Usage: dialog_ask <question>
+dialog_ask() {
+ Xdialog_run 'Confirm' --yesno "$1" 0 0
+}
+
+# Has the user quested to cancel the operation?
+# Usage: dialog_cancelled && print "cancelled"
+dialog_cancelled() {
+ ! pipe_exists
+}
+
+# Set the status text.
+# Usage: dialog_set_text <text>
+dialog_set_text() {
+ pipe_write 'XXX'
+ pipe_write "$1"
+ pipe_write 'XXX'
+}
+
+# Set if the progress bar should continously animate instead of showing the value.
+# Usage: dialog_set_pulsate <enable>
+dialog_set_pulsate() {
+ true # Pulsate is not supported by Xdialog
+}
+
+# Set the current progress value.
+# Usage: dialog_set_value <percentage>
+dialog_set_value() {
+ pipe_write "$1"
+}
+
+# Select an entry in a list.
+# Usage dialog_select_entry <var> <label> <tag1> <item1> [ <tag2> < item2> ... ]
+dialog_select_entry() {
+ _Xdialog_select_entry_v="$1" ; shift
+ _Xdialog_select_entry_t="$1" ; shift
+ _Xdialog_select_entry_r="$(
+ Xdialog_run 'Select path' \
+ --menubox "$_Xdialog_select_entry_t" 20 80 10 "$@" 2>&1
+ )"
+ [ -z "$_Xdialog_select_entry_r" ] && return $false
+ eval "$_Xdialog_select_entry_v=\"\$_Xdialog_select_entry_r\""
+ return $true
+}
+
+# dialog_select_path does not support the --any flag
+dialog_select_path_any=0
+
+# Let the user select a path.
+# Usage: dialog_select_path (--file|--dir|--any) <result-var> <label>
+# Any is only supported if $dialog_select_path_any is 1.
+dialog_select_path() {
+ case "$1" in
+ --any) die 'not implemented' ;;
+ --file) _Xdialog_select_path_f=--fselect ;;
+ --dir) _Xdialog_select_path_f=--dselect ;;
+ esac
+ _Xdialog_select_path="$(
+ Xdialog_run "$3" $_Xdialog_select_path_f "$HOME" 0 0 2>&1
+ )"
+ [ -z "$_Xdialog_select_path" ] && return $false
+ eval "$2=\"\$_Xdialog_select_path\""
+ return $true
+}
+
+dialog_retry() {
+ Xdialog_run 'Error' --ok-label='Retry' --cancel-label='Ignore' --yesno "$1" 0 0
+ case $? in
+ 0) dialog_retry_choice='retry' ;;
+ 1) dialog_retry_choice='ignore' ;;
+ *) dialog_retry_choice='abort' ;;
+ esac
+}
+
+;;
+
+#----------------------------------------------------------------------------------------#
+0) # command-line
+
+# Dialog abstraction
+
+# Create the main progress window.
+# Usage: dialog_create
+dialog_create() {
+ true
+}
+
+# Destroy the main progress window.
+# Usage: dialog_destroy
+dialog_destroy() {
+ true
+}
+
+# Show an error dialog.
+# Usage: dialog_error <message>
+dialog_error() {
+ true # error messages are always printed to stdout
+}
+
+# Show a message box.
+# Usage: dialog_message <message>
+dialog_message() {
+ true
+}
+
+# Ask a yes/no question.
+# Usage: dialog_ask <question>
+dialog_ask() {
+ die 'unimplemented'
+}
+
+# Has the user quested to cancel the operation?
+# Usage: dialog_cancelled && print "cancelled"
+dialog_cancelled() {
+ false # never cancelled SIGINT is not trapped
+}
+
+# Set the status text.
+# Usage: dialog_set_text <text>
+dialog_set_text() {
+ true
+}
+
+# Set if the progress bar should continously animate instead of showing the value.
+# Usage: dialog_set_pulsate <enable>
+dialog_set_pulsate() {
+ true
+}
+
+# Set the current progress value.
+# Usage: dialog_set_value <percentage>
+dialog_set_value() {
+ true
+}
+
+# Select an entry in a list.
+# Usage dialog_select_entry <var> <label> <tag1> <item1> [ <tag2> < item2> ... ]
+dialog_select_entry() {
+ _cli_select_entry_var="$1" ; shift
+
+ # Print a list for the user to select from
+ print "$1:" ; shift
+ _cli_select_entry_min=$1
+ _cli_select_entry_max=$1
+ _cli_select_entry_f=' [default]'
+ while [ $# -gt 0 ] ; do
+ _cli_select_entry_i=$1 ; shift
+ _cli_select_entry_t="$1" ; shift
+ if [ $_cli_select_entry_i -lt $_cli_select_entry_min ] ; then
+ _cli_select_entry_min=$_cli_select_entry_i
+ fi
+ if [ $_cli_select_entry_i -gt $_cli_select_entry_max ] ; then
+ _cli_select_entry_max=$_cli_select_entry_i
+ fi
+ printf ' %d) %s%s\n' $_cli_select_entry_i \
+ "$_cli_select_entry_t" "$_cli_select_entry_f"
+ _cli_select_entry_f=''
+ done
+
+ # Read a number (or empty string for the first entry)
+ while true ; do
+
+ puts '> #'
+ read -r _cli_select_entry_r
+
+ [ -z "$_cli_select_entry_r" ] && _cli_select_entry_r=1
+
+ case "$_cli_select_entry_r" in
+ 'quit') ;; 'q') ;; 'exit') ;; 'abort') ;;
+ *)
+ if [ ! "$_cli_select_entry_r" -lt $_cli_select_entry_min ] 2> /dev/null \
+ && [ ! "$_cli_select_entry_r" -gt $_cli_select_entry_max ] 2> /dev/null
+ then
+ eval "$_cli_select_entry_var=\"\$_cli_select_entry_r\""
+ return $true
+ else
+ printf "Please enter a number between %d and %d.\n" \
+ $_cli_select_entry_min $_cli_select_entry_max
+ continue
+ fi
+ esac
+ die
+
+ done
+
+ return $true
+}
+
+# dialog_select_path supports the --any flag
+dialog_select_path_any=1
+
+# Let the user select a path.
+# Usage: dialog_select_path (--file|--dir|--any) <result-var> <label>
+# Any is only supported if $dialog_select_path_any is 1.
+dialog_select_path() {
+ _cli_select_path_var="$2"
+ print "$3:" ; shift
+ puts '> '
+ read -r _cli_select_path_r
+ [ -z "$_cli_select_path_r" ] && return $false
+ eval "$_cli_select_path_var=\"\$_cli_select_path_r\""
+ return $true
+}
+
+dialog_retry() {
+ printf '\n%s\n' "${red}Error:${reset} $1"
+ while true ; do
+ print "Abort / [Retry] / Ignore"
+ puts '> '
+ read -r _cli_dialog_retry_r
+ case "$_cli_dialog_retry_r" in
+ a|A|abort|Abort|ABORT) dialog_retry_choice='abort' ; return ;;
+ ''|r|R|retry|Retry|RETRY) dialog_retry_choice='retry' ; return ;;
+ i|I|ignore|Ignore|IGNORE) dialog_retry_choice='ignore' ; return ;;
+ esac
+ done
+}
+
+esac
+
+
+##########################################################################################
+# Common user interface implementation
+
+# Ask the user if the setup should really be cancelled.
+handle_cancel() {
+ _handle_cancel_message="Are you sure you want to exit the Arx Fatalis data installer?"
+ if [ $installed_stuff = 1 ] ; then
+ _handle_cancel_message="$_handle_cancel_message
+
+Already installed files will not be removed!"
+ fi
+ if [ $installed_stuff = 1 ] || [ $selected_stuff = 1 ] ; then
+ dialog_ask "$_handle_cancel_message" || return $true
+ fi
+ print 'Aborted by user' && die
+}
+
+# Update the status.
+# Usage: status (<percent>|--temp) [<message>]
+_status_text="Initializing..."
+_status_cur=''
+_status_pulsate=default
+status() {
+
+ if [ "$1" = '--temp' ] ; then
+ _status_value=0
+ _status_temp=1
+ else
+ _status_value=$1
+ _status_temp=0
+ fi
+ _status_new="${2:-$_status_text}"
+
+ # Handle the cancel and close buttons
+ if dialog_cancelled ; then
+ handle_cancel
+ dialog_create || die "Could not re-create progress window."
+ _status_cur=''
+ _status_pulsate=default
+ fi
+
+ # Update the progress text if one was provided
+ if [ ! "$_status_cur" = "$_status_new" ] ; then
+ _status_cur="$_status_new"
+ print "$_status_new"
+ dialog_set_text "$_status_new"
+ fi
+ [ $_status_temp = 0 ] && _status_text="$_status_new"
+
+ # Set the maximum progress value
+ if [ $_status_value = 0 ] ; then _new_status_pulsate=1 ; else _new_status_pulsate=0 ; fi
+ if [ ! "$_status_pulsate" = $_new_status_pulsate ] ; then
+ _status_pulsate="$_new_status_pulsate"
+ dialog_set_pulsate $_status_pulsate
+ fi
+
+ # Update the progress value
+ [ $_status_pulsate = 0 ] && dialog_set_value $_status_value
+
+}
+
+# Print an error message and show an error dialog if we have a GUI/
+# Usage: error <message>
+error() {
+ print "${dim_red}$1${reset}"
+ dialog_error "$1"
+}
+
+# Let the user select an item from a list or enter a custom one.
+# In batch mode, select the first one if --first is given, die otherwise.
+# Usage: user_select_entry (--existing|--writable) (--any|--file|--dir) \
+# <list> <result-var> <desc> <desc-color> <list-color> <verb>
+user_select_entry() {
+
+ _user_select_entry_access="$1"
+ _user_select_entry_t="$2"
+ _user_select_entry_lname="$3"
+ eval "_user_select_entry_list=\"\$$_user_select_entry_lname\""
+ _user_select_entry_var="$4"
+ _user_select_entry_desc="$5"
+ _user_select_entry_color1="$6"
+ _user_select_entry_color2="$7"
+ _user_select_entry_verb="$8"
+
+ # Select the first element if in batch mode
+ eval "_user_select_entry_current=\"\$$_user_select_entry_var\""
+ if [ $batch = 1 ] || [ ! -z "$_user_select_entry_current" ] ; then
+ _user_select_entry_=''
+ eval "set -- $_user_select_entry_list"
+ for _user_select_entry ; do
+ _user_select_entry_="$_user_select_entry"
+ break
+ done
+ [ -z "$_user_select_entry_" ] && die "Missing $_user_select_entry_desc!"
+ eval "$_user_select_entry_var=\"\$_user_select_entry_\""
+ return $true
+ fi
+
+ if [ $gui = 0 ]
+ then print
+ else status --temp "Select a ${_user_select_entry_desc}"
+ fi
+
+ _user_select_entry_i=1
+ _user_select_entry_nolist=0
+ if [ -z "$_user_select_entry_list" ] \
+ && [ ! $_user_select_entry_t = --any ] \
+ && [ ! $dialog_select_path_any = 1 ] ; then
+
+ # No entries detected - direclty promt the user
+ _user_select_entry_num=1
+ _user_select_entry_nolist=1
+
+ else
+
+ _user_select_entry_tlist=''
+
+ # Format the list entries in a user friendly way
+ eval "set -- $_user_select_entry_list"
+ for _user_select_entry ; do
+
+ # Use 'command (file)' if comment available or 'file' otherwise
+ _user_select_entry_comment="$(
+ list_comment "$_user_select_entry_lname" "$((_user_select_entry_i - 1))"
+ )"
+ if [ -z "$_user_select_entry_comment" ] ; then
+ _user_select_entry_label="$_user_select_entry_color2$_user_select_entry$reset"
+ else
+ _user_select_entry_label="$_user_select_entry_color2$_user_select_entry_comment$reset"
+ _user_select_entry_label="$_user_select_entry_label: $_user_select_entry"
+ fi
+
+ # Add the tag and label to the arguments
+ list_append _user_select_entry_tlist $_user_select_entry_i
+ list_append _user_select_entry_tlist "$_user_select_entry_label"
+
+ # Remeber the file for the tag
+ eval "_user_select_entry_$_user_select_entry_i=\"\$_user_select_entry\""
+
+ _user_select_entry_i=$((_user_select_entry_i + 1))
+ done
+
+ # Add entries for custom files/diectories
+ if [ $_user_select_entry_t = --any ] && [ $dialog_select_path_any = 1 ] ; then
+ list_append _user_select_entry_tlist $_user_select_entry_i
+ list_append _user_select_entry_tlist "Select file or directory to $_user_select_entry_verb.."
+ else
+ _user_select_entry_ii=$_user_select_entry_i
+ if [ $_user_select_entry_t = --any ] || [ $_user_select_entry_t = --file ] ; then
+ list_append _user_select_entry_tlist $_user_select_entry_ii
+ list_append _user_select_entry_tlist "Select file to $_user_select_entry_verb..."
+ _user_select_entry_ii=$((_user_select_entry_ii + 1))
+ fi
+ if [ $_user_select_entry_t = --any ] || [ $_user_select_entry_t = --dir ] ; then
+ list_append _user_select_entry_tlist $_user_select_entry_ii
+ list_append _user_select_entry_tlist "Select directory to $_user_select_entry_verb..."
+ _user_select_entry_ii=$((_user_select_entry_ii + 1))
+ fi
+ fi
+
+ fi
+
+ # Loop until we have selected a value
+ while true ; do
+
+ if [ $_user_select_entry_nolist = 0 ] ; then
+
+ # Ask the user to select an entry
+ eval "set -- $_user_select_entry_tlist"
+ while true ; do
+ if dialog_select_entry _user_select_entry_num \
+ "${_user_select_entry_color1}Select a ${_user_select_entry_desc}${reset}" "$@"
+ then
+ break
+ else
+ handle_cancel
+ fi
+ done
+
+ fi
+
+ if [ $_user_select_entry_num -lt $_user_select_entry_i ] ; then
+
+ # User selected an entry -- return that
+ eval "$_user_select_entry_var=\"\$_user_select_entry_$_user_select_entry_num\""
+ selected_stuff=1
+ return $true
+
+ fi
+
+ # Loop until we have selected a path of the correct type
+ while true ; do
+
+ # Adjust type based on user selection
+ if [ $_user_select_entry_t = --any ] && [ ! $dialog_select_path_any = 1 ] ; then
+ if [ $_user_select_entry_num = $_user_select_entry_i ]
+ then _user_select_entry_type=--file
+ else _user_select_entry_type=--dir
+ fi
+ else
+ _user_select_entry_type=$_user_select_entry_t
+ fi
+
+ _user_select_entry_c="Select a custom source "
+
+ if ! dialog_select_path "$_user_select_entry_type" _user_select_entry_path \
+ "${_user_select_entry_color1}Choose a custom ${_user_select_entry_desc}${reset}" \
+ "$@" ; then
+
+ # User cancelled the dialog - return to the main selection, unless there is none
+ if [ $_user_select_entry_nolist = 1 ] ; then
+ handle_cancel
+ continue # let the user try again
+ fi
+ break # return to list selection
+
+ fi
+
+ _user_select_entry_path="$(abspath "$_user_select_entry_path")"
+
+ # The user selected a path, now check if it meets our criteria.
+ case "$_user_select_entry_access" in
+ --existing)
+ if [ ! -e "$_user_select_entry_path" ] ; then
+ error "$_user_select_entry_path does not exist!"
+ continue # let the user try again
+ fi ;;
+ --writable)
+ if ! is_writable "$_user_select_entry_path" ; then
+ error "$_user_select_entry_path is not writable!"
+ continue # let the user try again
+ fi ;;
+ esac
+ case "$_user_select_entry_t" in
+ --any) ;; # anything goes
+ --file)
+ if [ -e "$_user_select_entry_path" ] && [ -d "$_user_select_entry_path" ] ; then
+ error "$_user_select_entry_path is is a directory, but we need a file!"
+ continue
+ fi ;;
+ --dir)
+ if [ -e "$_user_select_entry_path" ] && [ ! -d "$_user_select_entry_path" ] ; then
+ error "$_user_select_entry_path is is a file, but we need a directory!"
+ continue
+ fi ;;
+ esac
+
+ # Everything went better than expected - save the result
+ eval "$_user_select_entry_var=\"\$_user_select_entry_path\""
+ selected_stuff=1
+ return $true
+
+ done
+
+ done
+
+}
+
+# Show error message and close windows on exit
+ui_cleanup() {
+ [ -z "$1" ] || dialog_error "$1"
+ dialog_destroy
+}
+on_exit ui_cleanup
+
+# Initilize the UI
+dialog_create || die "
+Could not create main window.
+
+You could try the --cli option.
+"
+[ $gui = 0 ] || status --temp "$_status_text"
+
+
+##########################################################################################
+# Autodetect source file/dir
+
+sourcefiles='' # List of source file/directory candidates
+
+# Add a source file or directory to the list of candidates.
+# Usage: found_source_file <file> [comment]
+# Return: $true if more source files should be probed, $false otherwise.
+found_source_file() {
+ set_append sourcefiles "$(canonicalize "$1")" "$2"
+ if [ $batch = 1 ] ; then return $true ; else return $false ; fi
+}
+
+# Get a subsection in a .vdf file
+# Usage: cat config | steam_get_section <section>
+steam_get_section() {
+ # Abuse indentation to find the section end
+ grep -Pzoi "\\n(\\s*)\"$1\"\\n\\s+\\{(\\n.*)*?\\n\\1}"
+}
+
+# Get a subsection in a .vdf file - and everything after it
+# Usage: cat config | steam_get_section_tail <section>
+steam_get_section_tail() {
+ # Abuse indentation to find the section end
+ grep -Pzoi "\\n\\s*\"$1\"\\n\\s+\\{(\\n.*)*"
+}
+
+# Remove backtick escapes
+# Usage: echo <escaped_string> | unescape
+steam_unescape() {
+ sed 's/\\\(.\)/\1/g'
+}
+
+# Get an entry in a .vdf file, or the first one if there are multiple
+# Usage: cat config | steam_get_entry <key>
+steam_get_entry() {
+ grep -Pi "^\\s*\"$1\"\s*\".*\"\\s*$" \
+ | head -1 \
+ | sed 's/^[^"]*"[^"]*"[^"]*"\(.*\)"[^"]*$/\1/' \
+ | steam_unescape
+}
+
+# Get the app information section for an appid
+# Usage: steam_get_appinfo <config_file> <appid>
+steam_get_appinfo() {
+ _steam_config="$1"
+ _steam_appid="$2"
+
+ _steam_appinfo="$(
+ steam_get_section 'Software' < "$_steam_config" \
+ | steam_get_section 'Valve' \
+ | steam_get_section 'Steam' \
+ | steam_get_section 'apps' \
+ | steam_get_section "$_steam_appid"
+ )"
+
+ # Fallback if the tree structure changed
+ [ -z "$_steam_appinfo" ] && _steam_appinfo="$(
+ steam_get_section "$_steam_appid" < "$_steam_config"
+ )"
+
+ # Fallback if the indentation changed
+ [ -z "$_steam_appinfo" ] && _steam_appinfo="$(
+ steam_get_section_tail "$_steam_appid" < "$_steam_config"
+ )"
+
+ print "$_steam_appinfo"
+}
+
+# Get a config entry for an appid
+# Usage: steam_get_app_config <config_file> <appid>
+steam_get_installdir() {
+ steam_get_appinfo "$1" "$2" | steam_get_entry 'installdir'
+}
+
+# Find possible source directories from a Steam library
+# Usage: probe_steam_library <steamlibrary>
+# Return: $true if more source files should be probed, $false otherwise.
+probe_steam_library() {
+ _steam_library="$1"
+ for _steam_appdir in "$_steam_library/SteamApps" "$_steam_library/steamapps" ; do
+ _steam_installdir="$_steam_appdir/common/$steam_install_dir"
+ if [ -d "$_steam_installdir" ] ; then
+ found_source_file "$_steam_installdir" "Steam" && return $true
+ fi
+ done
+ return $false
+}
+
+# Find possible source directories from a Steam installation
+# Usage: probe_steam_root <steamroot>
+# Return: $true if more source files should be probed, $false otherwise.
+probe_steam_root() {
+ _steam_root="$1"
+
+ _steam_config="$_steam_root/config/config.vdf"
+
+ if [ -f "$_steam_config" ] ; then
+
+ # Strategy 1: Read install directory from Steam config file
+ _steam_installdir="$(steam_get_installdir "$_steam_config" "$steam_appid")"
+ if [ -d "$_steam_installdir" ] ; then
+ found_source_file "$_steam_installdir" "Steam" && return $true
+ fi
+
+ # Strategy 2: Look for known install directory in steam libraries
+ _steam_libraries="$(
+ sed -n '/BaseInstallFolder/s/^[^\"]*\"[^\"]*\"[^\"]*\"\([^\"]*\)\".*/\1/p' \
+ < "$_steam_config" \
+ | sed 's:[^a-zA-Z0-9/_.\n]:\\&:g'
+ )"
+ _steam_library_call='probe_steam_library "$_steam_library" && return $true'
+ eval "for _steam_library in $_steam_libraries ; do $_steam_library_call ; done"
+
+ fi
+
+ # Strategy 3: Look for known install directory in the default library
+ probe_steam_library "$_steam_root" && return $true
+
+ return $false
+}
+
+# Find possible source directories from Steam
+# Usage: probe_steam
+# Return: $true if more source files should be probed, $false otherwise.
+probe_steam() {
+ _steam_roots=''
+ for _steam_root in "$HOME/.steam/root" "$HOME/.steam/steam" "$data_home/Steam" ; do
+ [ -d "$_steam_root" ] || continue
+ set_append _steam_roots "$(canonicalize "$_steam_root")"
+ done
+ _steam_root_call='probe_steam_root "$_steam_root" && return $true'
+ [ -z "$_steam_roots" ] && return $false
+ eval "for _steam_root in $_steam_roots ; do $_steam_root_call ; done"
+ return $false
+}
+
+# Find a match for a case insensitive path containing arx.exe
+# Usage: probe_wine_path <baseprefix> <ipath>
+probe_wine_path() {
+ [ -d "$1" ] || return $false
+ if [ -z "$2" ] ; then
+ if icontains "$1" 'arx.exe' ; then
+ found_source_file "$1" "Wine"
+ fi
+ return $false
+ fi
+ _wine_path_dir="${2%%\\*}"
+ if [ "$_wine_path_dir" = "$2" ]
+ then _wine_path=''
+ else _wine_path="${2#*\\}"
+ fi
+ _wine_paths="$(
+ find "$1/" -mindepth 1 -maxdepth 1 -iname "$(escape "$_wine_path_dir")" -print \
+ | lines_to_list
+ )"
+ [ -z "$_wine_paths" ] && return $false
+ _wine_path_call='probe_wine_path "$_wine_path_prefix" "$_wine_path" && return $true'
+ eval "for _wine_path_prefix in $_wine_paths ; do $_wine_path_call ; done"
+ return $false
+}
+
+# Find possible source directories from a registry key.
+# Usage: probe_wine_registry <key> <variable>
+# Return: $true if more source files should be probed, $false otherwise.
+probe_wine_registry() {
+
+ # This is intentionally implemented without calling wine as we don't want to
+ # modify the source.
+
+ # Get candidate paths from the registry file
+ _wine_pattern='Software\\\\(Wow6432Node\\\\)?'
+ _wine_pattern="$_wine_pattern$(print "$1" | sed 's/\\/\\\\/g' | escape_pipe '()|')"
+ _wine_paths="$(
+ # The --after is just an arbitrary limit
+ # We verify the paths by checking for arx.exe, but for effiency we still want
+ # to avoid false positives.
+ cat "$_wine_prefix"/*.reg \
+ | grep -iPA 20 "^\\[$_wine_pattern\\]" 2> /dev/null \
+ | grep -iP "^\"?$2\"?=" 2> /dev/null \
+ | sed 's/^[^=]*="\([^"]*\)".*$/\1/;s/\\\(.\)/\1/g' \
+ | lines_to_list
+ )"
+
+ # For each candidate, find a case-insensitive match and check if it contains arx.exe
+ eval "set -- $_wine_paths"
+ for _wine_path ; do
+ probe_wine_path "$_wine_prefix/dosdevices" "$_wine_path" && return $true
+ done
+
+ return $false
+}
+
+# Find possible source directories from uninstall registry entries.
+# Usage: probe_wine_uninstall_info <id>
+# Return: $true if more source files should be probed, $false otherwise.
+probe_wine_uninstall_info() {
+ probe_wine_registry "Microsoft\\Windows\\CurrentVersion\\Uninstall\\$1" \
+ 'InstallLocation'
+}
+
+# Find possible source directories in a WINEPREFIX.
+# Usage: probe_wineprefix <wineprefix>
+# Return: $true if more source files should be probed, $false otherwise.
+probe_wineprefix() {
+
+ [ -d "$1" ] || return $false
+ [ -e "$1/system.reg" ] || [ -e "$1/user.reg" ] || return $false
+ _wine_prefix="$1"
+
+ # Normal install
+ probe_wine_registry 'Arkane Studios\Installed Apps\Arx Fatalis' 'Folder' && return $true
+
+ # GOG version
+ probe_wine_registry 'GOG.com\GOGARXFATALIS' 'PATH' && return $true
+
+ # Probe uninstall entries - combined for effiency
+ _wine_uninstall='Microsoft\Windows\CurrentVersion\Uninstall\'
+ # Steam
+ _wine_steam="Steam App $steam_appid"
+ # Original game
+ _wine_orig='{96443F45-13E2-11D6-AC87-00D0B7A9E540}'
+ # 1.21 patch
+ _wine_patch='{171251E0-4EED-4EA1-A46D-3213A226F2B3}_is1'
+ probe_wine_registry "$_wine_uninstall($_wine_steam|$_wine_orig|$_wine_patch)" \
+ 'InstallLocation' && return $true
+
+}
+
+# Check a mounte point if it's an Arx Fatalis cd
+# Usage: probe_cd <mountpoint> <fstype>
+# Return: $true if more source files should be probed, $false otherwise
+probe_cd() {
+ node="$1"
+ mountpoint="$2"
+ fstype="$3"
+
+ case "$fstype" in
+ cd9660) ;; iso9660) ;; udf) ;; *fuseiso) ;;
+ *) return $true
+ esac
+
+ if [ -d "$mountpoint/bin" ] && icontains "$mountpoint/bin" 'arx.ttf' 2> /dev/null ; then
+ found_source_file "$mountpoint" "CDROM at $node" && return $true
+ fi
+
+}
+
+# Find mounted Arx Fatalis CDs
+# Usage: probe_cdrom
+# Return: $true if more source files should be probed, $false otherwise
+probe_cdrom() {
+
+ _probe_cdrom_mountpoints="$(
+ [ -f /proc/mounts ] && lines_to_list < /proc/mounts # Linux - use /proc/mounts
+ mount -p 2> /dev/null | lines_to_list # Hope that mount has a -p option
+ )"
+
+ eval "set -- $_probe_cdrom_mountpoints"
+ for _probe_cdrom_line ; do
+ eval "probe_cd $(escape "$_probe_cdrom_line" ' ' | sed 's/\\\\\040/\\ /g')" # space-separated
+ eval "probe_cd $(print "$_probe_cdrom_line" | tr '\t' '\n' | lines_to_list)" # tab-separated
+ done
+
+}
+
+# Find possible source files/directories
+# Usage: probe_source_files
+# If sourcefile is already set, uses that.
+probe_source_files() {
+
+ if [ ! -z "$sourcefile" ] ; then
+
+ # Trust the user
+ found_source_file "$sourcefile" && return $true
+
+ # But also allow to refile the dir in non-batch mode if it is a wineprefix
+ [ -d "$sourcefile" ] && probe_wineprefix "$sourcefile"
+
+ [ "$sourcefiles" = "$sourcefile" ] || sourcefile=''
+
+ return $true
+ fi
+
+ status 5 "Searching for source files..."
+
+ # Find by filename
+ probe_files found_source_file "$gog_names" 'GOG.com setup'
+ probe_files found_source_file "$demo_names" 'Demo'
+
+ probe_steam && return $true
+
+ # Find an Arx Fatalis installation in $WINEPREFIX
+ [ ! -z "${WINEPREFIX-}" ] && probe_wineprefix "$WINEPREFIX" && return $true
+
+ # Find an Arx Fatalis installation in ~/.wine
+ [ ! "${WINEPREFIX-}" = "$HOME/.wine" ] && probe_wineprefix "$HOME/.wine" && return $true
+
+ # Find an Arx Fatalis cdrom
+ probe_cdrom && return $true
+
+ # Just in case it will ever be installable under non-Windows systems
+ steam_source_path="$HOME/.steam/root/SteamApps/common/Arx Fatalis"
+ [ -d "$steam_source_path" ] && found_source_file "$steam_source_path" 'Steam'
+
+}
+
+
+##########################################################################################
+# Autodetect destination dir
+
+datadirs='' # List of destination data directory candidate
+
+# Add a destination data directory to the list of candidates.
+# Usage: found_data_dir <dir> [comment]
+# Return: $true if more data directories should be probed, $false otherwise.
+found_data_dir() {
+ set_append datadirs "$(abspath "$1")" "$2"
+ if [ $batch = 1 ] ; then return $true ; else return $false ; fi
+}
+
+# Add a destination data directory to the list of candidates if it is writable.
+# In verify mode, also existing read-only directories are added.
+# Usage: probe_data_dir <must-exists> <dir> [comment]
+# Return: $true if more data directories should be probed, $false otherwise.
+probe_data_dir() {
+ [ "$1" = 1 ] && [ ! -e "$2" ] && return $false
+ [ $install = 0 ] && [ ! -e "$2" ] && return $false
+ if [ $install = 1 ] || [ $patch = 1 ] ; then is_writable "$2" || return $false ; fi
+ found_data_dir "$2" "$3"
+}
+
+# Find possible destination data directories
+# Usage: probe_data_dirs
+# If datadir is already set, uses that.
+probe_data_dirs() {
+
+ if [ ! -z "$datadir" ] ; then
+ found_data_dir "$datadir"
+ return $true
+ fi
+
+ status 10 "Searching for destination directories..."
+
+ for _probe_data_dirs_existing in 1 0 ; do
+
+ # Try paths supplied by wrapper scripts
+ if [ ! -z "$data_path" ] ; then
+ eval "set -- $(to_list "$data_path")"
+ for _probe_data_dirs_path ; do
+ if [ -f "$_probe_data_dirs_path/data" ] ; then
+ eval "set -- $(lines_to_list < "$_probe_data_dirs_path/data")"
+ for _probe_data_dirs_line ; do
+ case "$_probe_data_dirs_line" in
+ /*) ;; *) _probe_data_dirs_line="$(
+ canonicalize "$_probe_data_dirs_path/$_probe_data_dirs_line"
+ )"
+ esac
+ probe_data_dir $_probe_data_dirs_existing "$_probe_data_dirs_line" \
+ "portable" && return $true
+ done
+ elif [ -d "$_probe_data_dirs_path/data" ] ; then
+ probe_data_dir $_probe_data_dirs_existing "$_probe_data_dirs_path/data" \
+ "portable" && return $true
+ else
+ probe_data_dir $_probe_data_dirs_existing "$_probe_data_dirs_path" "portable" \
+ && return $true
+ fi
+ done
+ fi
+
+ # Try system paths
+ eval "set -- $(to_list "$data_dirs")"
+ for _probe_data_dirs_prefix in "$@" ; do
+ eval "set -- $(to_list "$data_dir_suffixes")"
+ for _probe_data_dirs_suffix ; do
+ _probe_data_dirs_force=$_probe_data_dirs_existing
+ [ -e "$_probe_data_dirs_prefix/$_probe_data_dirs_suffix" ] && \
+ _probe_data_dirs_force=0
+ probe_data_dir $_probe_data_dirs_force \
+ "$_probe_data_dirs_prefix/$_probe_data_dirs_suffix/data" "system" \
+ && return $true
+ probe_data_dir $_probe_data_dirs_existing \
+ "$_probe_data_dirs_prefix/$_probe_data_dirs_suffix" "system" \
+ && return $true
+ done
+ done
+
+ # Try user paths
+ if [ $is_root = 0 ] ; then
+ eval "set -- $(to_list "$user_dir_suffixes")"
+ for _probe_data_dirs_suffix ; do
+ _probe_data_dirs_force=$_probe_data_dirs_existing
+ [ -e "$data_home/$_probe_data_dirs_suffix" ] && \
+ _probe_data_dirs_force=0
+ probe_data_dir $_probe_data_dirs_force \
+ "$data_home/$_probe_data_dirs_suffix/data" "user" \
+ && return $true
+ [ $install = 0 ] && probe_data_dir $_probe_data_dirs_existing \
+ "$data_home/$_probe_data_dirs_suffix" "user" \
+ && return $true
+ done
+ fi
+
+ done
+
+}
+
+
+##########################################################################################
+# Extract helpers and other utility abstractions
+
+# Calculate the MD5 checksum of a file.
+# Usage: checksum <result-var> <file>
+checksum() {
+
+ if have md5sum ; then
+ _checksum_result="$(md5sum -b "$2" | sed 's/ .*//g')"
+ eval "$1=\"\$_checksum_result\""
+ return $true
+ fi
+
+ if have md5 ; then
+ _checksum_result="$(md5 -q "$2")"
+ eval "$1=\"\$_checksum_result\""
+ return $true
+ fi
+
+ die "You need either md5sum or md5."
+}
+
+have_run() {
+ eval "_have_run_p=\"\$${1}_reqs\""
+ eval "for _have_run_program in $_have_run_p ; do have \$_have_run_program && return $true ; done"
+ return $false
+}
+
+# Extract an archive file to the current directory.
+# Usage: extract <file> <types>...
+have_extract() {
+ have_run "extract_$1"
+}
+extract() {
+ _extract_file="$1" ; shift
+
+ while true ; do
+
+ _extract_missing=''
+ for _extract_type ; do
+
+ if "have_extract_${_extract_type}" ; then
+ "extract_${_extract_type}" "$_extract_file" && return "$true"
+ else
+ list_merge _extract_missing extract_${_extract_type}_reqs
+ fi
+
+ done
+
+ _extract_msg="${white}Could not extract $(basename "$_extract_file")${reset}"
+ [ -z "$_extract_missing" ] \
+ || _extract_msg="$_extract_msg
+
+Please install one or more of the following:
+$(print_help_list " - $dim_pink" _extract_missing)"
+
+ [ $batch = 1 ] && die "Error: $_extract_msg"
+
+ dialog_retry "$_extract_msg"
+ case $dialog_retry_choice in
+ abort) die "Error extracting files" ;;
+ retry) continue ;;
+ ignore) return $true ;;
+ esac
+
+ done
+
+}
+
+# Extract a .zip file to the current directory.
+# Usage: extract_zip <zipfile>
+have_extract_zip() { have_extract zip ; }
+extract_zip() {
+
+ if have bsdtar ; then
+ printf 'Extracting %s using bsdtar\n' "$1"
+ bsdtar xvf "$1"
+ return $?
+ fi
+
+ if have unzip ; then
+ puts 'unzip: '
+ unzip "$1"
+ return $?
+ fi
+
+ for _extract_zip_sz in 7za 7z ; do
+ if have $_extract_zip_sz ; then
+ $_extract_zip_sz x "$1"
+ return $?
+ fi
+ done
+
+ die "no program to extract $1"
+}
+
+# Mount a .iso file or CDROM using fuseiso if available or normal mount if root.
+# Usage: mount_cdrom <cdromfile> <mountpoint>
+have_mount_cdrom() {
+ have fuseiso && have fusermount && return $true
+ have mount && have umount && [ $is_root = 1 ] && return $true
+ return $false
+}
+mount_cdrom() {
+
+ if have fuseiso && have fusermount && fuseiso "$1" "$2" ; then
+ printf 'Mounted %s at %s using fuseiso\n' "$1" "$2"
+ return $true
+ fi
+
+ if have mount && have umount && [ $is_root = 1 ] && mount -o loop,ro "$1" "$2" ; then
+ printf 'Mounted %s at %s\n' "$1" "$2"
+ return $true
+ fi
+
+ die "no program to extract $1"
+}
+
+# Unmount a CDROM that was mounted using mount_cdrom.
+# Usage: unmount_cdrom <mountpoint>
+unmount_cdrom() {
+ have fusermount && fusermount -u "$1" > /dev/null 2>&1
+ have umount && [ $is_root = 1 ] && umount "$1" > /dev/null 2>&1
+}
+
+# isoinfo wrapper to extract all files to the current directory
+extract_isoinfo() {
+ _extract_isoinfo_file="$1"
+
+ # Get a list of all files in the ISO image
+ _extract_isoinfo_files="$(
+ isoinfo -i "$_extract_isoinfo_file" -J -f | grep ';1$' | lines_to_list
+ )"
+ [ -z "$_extract_isoinfo_files" ] && return $false
+
+ eval "set -- $_extract_isoinfo_files"
+ for _extract_isoinfo_e ; do
+
+ # Remove leading / and trailing ;1 from filenames
+ _extract_isoinfo_f="$(print "$_extract_isoinfo_e" | sed 's:^/*::;s:;1$::')"
+ [ -z "$_extract_isoinfo_f" ] && continue
+ printf ' - %s\n' "$_extract_isoinfo_f"
+
+ # Create subdirectories as needed
+ _extract_isoinfo_d="$(dirname "$_extract_isoinfo_f")"
+ [ -z "$_extract_isoinfo_d" ] || mkdir -p "$_extract_isoinfo_d" || return $false
+
+ # Extract the file
+ isoinfo -i "$_extract_isoinfo_file" -J -x "$_extract_isoinfo_e" \
+ > "$_extract_isoinfo_f" || return $false
+
+ # Don't rely on isoinfo setting a non-zero return code, check that we got something
+ [ -s "$_extract_isoinfo_f" ] || return $false
+
+ done
+
+ return $true
+}
+
+# Extract a .iso file or CDROM to the current directory.
+# Usage: extract_iso <cdromfile>
+have_extract_iso() { have_extract iso ; }
+extract_iso() {
+
+ if have isoinfo ; then
+ printf 'Extracting %s using isoinfo\n' "$1"
+ extract_isoinfo "$1"
+ return $?
+ fi
+
+ ret=$false
+
+ if have bsdtar ; then
+ printf 'Extracting %s using bsdtar\n' "$1"
+ bsdtar xvf "$1"
+ ret="$?"
+
+ # Older versions of bsdtar don't always get the names right - fix them
+ _extrac_cdrom_f="$(find "$PWD" -depth -iname '*;1' | lines_to_list)"
+ if [ ! -z "$_extrac_cdrom_f" ] ; then
+ eval "for _extract_iso_f in $_extrac_cdrom_f ; do mv -f \"\$_extract_iso_f\" \"\$(print \"\$_extract_iso_f\" | sed 's:;1$::')\" ; done"
+ fi
+
+ elif have 7z ; then
+ 7z x -tiso "$1"
+ ret="$?"
+ fi
+
+ # For some iso files bsdtar just does nothing - at least let the user know
+ if [ ! -d "$PWD/bin" ] || ! icontains "$PWD/bin" 'arx.ttf' ; then
+ _extract_iso_err="${yellow}It looks like bsdtar/p7zip didn't do what it was supposed to - this will likely fail!${reset}
+
+You might have better luck with ${dim_pink}isoinfo${reset} or ${dim_pink}fuseiso${reset}, or by manually mounting the CD/ISO."
+ printf '%s\n' "$_extract_iso_err"
+ [ $batch = 0 ] && dialog_message "$_extract_iso_err"
+ fi
+
+ return $ret
+}
+
+# Extract a microsoft .cab or .exe file to the current directory.
+# Usage: extract_ms_cab <cabfile>
+extract_cab_check_bsdtar() {
+ if have bsdtar ; then
+ case "$(bsdtar --version)" in
+ # These versions have bugs that cause corrupted files
+ '') ;;
+ *'libarchive 1.'*) ;;
+ *'libarchive 2.'*) ;;
+ *'libarchive 3.0') ;;
+ *'libarchive 3.0.'*) ;;
+ # Newer versions should work fine
+ *)
+ return $true
+ esac
+ fi
+ return $false
+}
+have_extract_ms_cab() {
+ extract_cab_check_bsdtar && return $true
+ have cabextract || have 7za || have 7z
+}
+extract_ms_cab() {
+
+ if extract_cab_check_bsdtar ; then
+ printf 'Extracting %s using bsdtar\n' "$1"
+ bsdtar xvf "$1"
+ return $?
+ fi
+
+ if have cabextract ; then
+ puts 'cabextract: '
+ cabextract "$1"
+ return $?
+ fi
+
+ for _extract_ms_cab_sz in 7za 7z ; do
+ if have $_extract_ms_cab_sz ; then
+ $_extract_ms_cab_sz x "$1"
+ return $?
+ fi
+ done
+
+ die "no program to extract $1"
+}
+
+# Extract an InstallShield .cab or .exe file to the current directory.
+# Usage: extract_installshield <cabfile>
+have_extract_installshield() { have_extract installshield ; }
+extract_installshield() {
+
+ if have unshield ; then
+ puts 'unshield: '
+ unshield x "$1"
+ return $?
+ fi
+
+ die "no program to extract $1"
+}
+
+# Extract a RAR .rar or .exe file to the current directory.
+# Usage: extract_rar <rarfile>
+have_extract_rar() { have_extract rar ; }
+extract_rar() {
+
+ if have unrar ; then
+ puts 'unrar: '
+ unrar x -y -o "$1"
+ return $?
+ fi
+
+ die "no program to extract $1"
+}
+
+# Extract an ACE .ace or .exe file to the current directory.
+# Usage: extract_ace <acefile>
+have_extract_ace() { have_extract ace ; }
+extract_ace() {
+
+ if have unace ; then
+ puts 'unace '
+ unace x -y -o "$1"
+ return $?
+ fi
+
+ die "no program to extract $1"
+}
+
+# Extract an Inno Setup .exe file to the current directory.
+# Usage: extract_innosetup <exefile>
+have_extract_innosetup() { have innoextract ; }
+innosetup_language=''
+extract_innosetup() {
+
+ if have innoextract ; then
+ puts 'innoextract: '
+ if [ -z "$innosetup_language" ]
+ then innoextract --color=off "$1" ; return $?
+ else innoextract --color=off --language="$innosetup_language" "$1" ; return $?
+ fi
+ fi
+
+ die "no program to extract $1"
+}
+
+# Extract all .cab files in a directory.
+# Usage: extract_cab_files <sourcedir> <destdir>
+extract_cab_files() {
+
+ _extract_cab_files_i=0
+ _extract_cab_files_files="$(
+ find "$1/" -mindepth 1 -type f -iname '*.cab' -print \
+ | lines_to_list
+ )"
+ eval "set -- $_extract_cab_files_files"
+ for _extract_cab_files_cabfile ; do
+ [ -z "$_extract_cab_files_cabfile" ] && continue
+
+ while true ; do
+ _extract_cab_files_cabdir="$sourcedir/cab.$_extract_cab_files_i"
+ if [ -e "$_extract_cab_files_cabdir" ] ; then
+ _extract_cab_files_i=$((_extract_cab_files_i + 1))
+ continue
+ fi
+ create_dir "$_extract_cab_files_cabdir" "cab #$i work"
+ break
+ done
+
+ cd "$_extract_cab_files_cabdir"
+ case "$(basename "$_extract_cab_files_cabfile")" in
+ data*) extract "$_extract_cab_files_cabfile" installshield ms_cab ;;
+ *) extract "$_extract_cab_files_cabfile" ms_cab installshield ;;
+ esac
+
+ _extract_cab_files_i=$((_extract_cab_files_i + 1))
+ done
+
+}
+
+# Download a file.
+# Usage: download_file <url> <destination>
+have_download() { have_run download ; }
+download_impl() {
+
+ if have wget ; then
+ wget -O "$2" "$1"
+ return $?
+ fi
+
+ if have curl ; then
+ curl --location --fail -o "$2" "$1"
+ return $?
+ fi
+
+ if have fetch ; then
+ fetch -o "$2" "$1"
+ return $?
+ fi
+
+ die "no program to download $1"
+}
+download_file() {
+
+ download_impl "$1" "$2" || return $false
+
+ # Check that we got something useful
+ case "$(file --dereference --brief --mime-type "$2" 2> /dev/null)" in
+ */html*) ;;
+ */xml*) ;;
+ *) return $true
+ esac
+
+ rm -f "$2"
+ return $false;
+}
+
+# Download a file from a list of mirrors.
+# Usage: download <callback> <name> <names> <urls> <destdir>
+download() {
+ _download_callback="$1"
+ _download_name="$2"
+ _download_names="$3"
+ _download_urls="$4"
+ _download_dest="$5"
+
+ while true ; do
+
+ probe_files "$_download_callback" "$_download_names" && return $true
+
+ _download_missing=''
+ if have_download ; then
+ eval "for _download_url in $_download_urls ; do download_file \"\$_download_url\" \"\$_download_dest\" && \$_download_callback \"\$_download_dest\" && return $true ; done"
+ else
+ list_merge _download_missing download_reqs
+ fi
+
+ _download_msg="${white}Could not download ${_download_name}${reset}"
+ [ -z "$_download_missing" ] \
+ || _download_msg="$_download_msg
+
+Please install one of the following:
+$(print_help_list " - $dim_pink" _download_missing)"
+
+ _download_msg="$_download_msg
+
+You can download the file manually from
+$(print_help_list " - $dim_blue" _download_urls)
+
+and put it in one of these locations:
+$(print_help_list " - $dim_white" probe_file_dirs)"
+
+ [ $batch = 1 ] && die "Error: $_download_msg"
+
+ dialog_retry "$_download_msg"
+ case $dialog_retry_choice in
+ abort) die "Error downloading files" ;;
+ retry) continue ;;
+ ignore) return $true ;;
+ esac
+
+ done
+
+}
+
+
+##########################################################################################
+# Unpack source
+
+workdir=''
+cleanup_workdir() {
+ [ ! -z "$workdir" ] && [ -e "$workdir" ] && rm -rf "$workdir"
+}
+# Create the work directory if it doesn't exist.
+# Also regiter a cleanup function to remove the work directory on exit.
+# Usage: create_workdir
+create_workdir() {
+ [ -z "$workdir" ] || return $true
+ selected_stuff=1
+ workdir="$datadir/$command-temp"
+ cleanup_workdir
+ on_exit cleanup_workdir
+ create_dir "$workdir" 'work'
+}
+
+# Extract setup*.cab files from a source directory into $sourcedir/cab.*.
+# Usage: extract_source_dir <sourcedir>
+extract_source_dir() {
+ extract_cab_files "$1" "$sourcedir"
+}
+
+# Extract a source executable into $sourcedir/exe.
+# Usage: extract_source_exe <sourcefile>
+extract_source_exe() {
+
+ sourcedir_exe="$sourcedir/exe"
+ create_dir "$sourcedir_exe" 'exe work'
+
+ cd "$sourcedir_exe" || die
+ case "$(basename "$1")" in
+ arx_demo_english.exe)
+ extract "$1" rar ace ms_cab installshield innosetup ;; # English demo
+ arx_demo_german.exe)
+ extract "$1" ace rar ms_cab installshield innosetup ;; # German demo
+ arx_jpn_*.exe)
+ extract "$1" ms_cab installshield innosetup rar ace ;; # Japanese demo
+ *)
+ extract "$1" innosetup ms_cab installshield rar ace ;; # GOG.com setup
+ esac
+
+ extract_source_dir "$sourcedir_exe"
+
+}
+
+# Wrap mount_cdrom et al so we can use them with extract()
+extract_mount_cdrom_reqs=''
+list_merge extract_mount_cdrom_reqs mount_cdrom_reqs
+have_extract_mount_cdrom() { have_mount_cdrom ; }
+extract_mount_cdrom() {
+ # Ignore the current working directory, always mount to $cdromdir
+ if mount_cdrom "$1" "$cdromdir" ; then
+ # Pretent the mountpoint is the original source
+ sourcefile="$cdromdir"
+ sourcedir_cdrom="$cdromdir"
+ return $true
+ else
+ return $false
+ fi
+}
+
+cdromdir=''
+cleanup_cdrom() {
+ [ ! -z "$cdromdir" ] && [ -e "$cdromdir" ] && unmount_cdrom "$cdromdir"
+}
+# Mount a source CDROM/ISO and adjust $sourcefile or extract it into $sourcedir/cdrom.
+# Usage: extract_source_cdrom <sourcefile>
+extract_source_cdrom() {
+
+ # Try to mount the cdrom to avoid unneeded copies
+ # Keep the mount point out of $sourcedir so we don't try to mv files from it
+ cdromdir="$workdir/cdrom"
+ cleanup_cdrom
+ on_exit cleanup_cdrom
+ create_dir "$cdromdir" 'mount work'
+
+ # Otherwise, extract the files from the CDROM if we have the required tools
+ sourcedir_cdrom="$sourcedir/cdrom"
+ create_dir "$sourcedir_cdrom" 'cdrom work'
+ cd "$sourcedir_cdrom" || die
+
+ extract "$1" mount_cdrom iso
+
+ # Extract any cab files on the CDROM
+ extract_source_dir "$sourcedir_cdrom"
+}
+
+# Extract a source executable into $sourcedir/zip.
+# Also extracts contained setup*.cab files into $sourcedir/cab.*.
+# Usage: extract_source_zip <sourcefile>
+extract_source_zip() {
+
+ sourcedir_zip="$sourcedir/zip"
+ create_dir "$sourcedir_zip" 'zip work'
+
+ cd "$sourcedir_zip" || die
+ extract "$1" zip
+
+ extract_source_dir "$sourcedir_zip"
+}
+
+sourcedir=''
+# Extract the source file or directory if it hansn't been extracted already.
+# Usage: extract_source
+extract_source() {
+ [ -z "$sourcedir" ] || return $true
+
+ status --temp "${white}Extracting source...${reset}"
+
+ create_workdir
+ sourcedir="$workdir/source"
+ create_dir "$sourcedir" 'source work'
+
+ if [ -d "$sourcefile" ] ; then
+ extract_source_dir "$sourcefile"
+ elif [ -f "$sourcefile" ] ; then
+ case "$sourcefile" in
+ *.zip) extract_source_zip "$sourcefile" ;;
+ *.exe) extract_source_exe "$sourcefile" ;;
+ *.iso) extract_source_cdrom "$sourcefile" ;;
+ *)
+ case "$(file --dereference --brief --mime-type "$sourcefile" 2> /dev/null)" in
+ application/zip) extract_source_zip "$sourcefile" ;;
+ application/x-dosexec) extract_source_exe "$sourcefile" ;;
+ application/x-iso9660-image) extract_source_cdrom "$sourcefile" ;;
+ *) die "Unknown source file type: $sourcefile"
+ esac
+ esac
+ else
+ extract_source_cdrom "$sourcefile"
+ fi
+
+ print
+}
+
+
+##########################################################################################
+# Detect data language
+
+find_file_impl() {
+ _patchable="$1"
+
+ # Search for both file.ext and file_default.ext
+ _file="$(escape "$(basename "$2")")"
+ _file_d="$(print "$_file" | sed 's/^\(.*\)\(\.[^.]*\)$/\1_default\2/')"
+
+ # Prefer files from the patch - if availbale, they are most likely the correct ones
+ set -- -iname "$_file" -print -o -iname "$_file_d" -print
+ [ "$_patchable" = 1 ] && [ ! -z "$patchdir" ] && find "$patchdir" "$@"
+
+ # Find the file in the source
+ [ ! -z "$sourcedir" ] && find "$sourcedir" "$@"
+ [ -d "$sourcefile" ] && find "$sourcefile" "$@"
+
+ # Also find the file if it is already in the data directory, but don't ignore case
+ find "$datadir" -path '*-temp' -prune -o \
+ -name "$_file" -print -o -name "$_file_d" -print
+
+}
+# Find a file in patch, source and data directories.
+# Usage: find_file <is-patchable> <return-list-var> <filename/path>
+find_file() {
+ eval "$2=\"\$(find_file_impl \"\$1\" \"\$3\" | lines_to_list)\""
+}
+
+# Copy/move a file to the data diectory.
+# Usage: use_file <file> <data-path>
+# Action taken depends on the source directory.
+use_file() {
+ _in="$1"
+ _out="$datadir/$2"
+
+ # Don't change anything on verify-only mode
+ [ $install = 0 ] && [ $patch = 0 ] && return $true
+
+ # Don't try to copy/move a file onto itself
+ [ "$_in" = "$_out" ] && return $true
+
+ # Create directories as needed
+ _outdir="$(dirname "$_out")"
+ create_dir "$_outdir" 'output'
+
+ # Copy or move the file
+ if [ "${_in#"$sourcefile"}" = "$_in" ]
+ then mv -f "$_in" "$_out" || die "Could not move $_in to $_out!"
+ else cp -f "$_in" "$_out" || die "Could not copy $_in to $_out!"
+ fi
+ installed_stuff=1
+
+ # Fix permissions
+ chmod --reference="$_outdir" "$_out" > /dev/null 2>&1
+ chmod -x "$_out" > /dev/null 2>&1
+}
+
+data_lang='' # Data language/tipe ID
+data_lang_desc='' # Friendly data language/type label
+
+# Detect the data language and type
+# Usage: detect_data_langauge <callback>
+# <callback> receives a speech.pak checksum and sets data_lang and data_lang_desc
+detect_data_langauge() {
+ callback="$1"
+
+ if [ $install = 1 ]
+ then _detect_data_langauge_status=40
+ else _detect_data_langauge_status=10
+ fi
+ [ $gui = 0 ] || status $_detect_data_langauge_status 'Detecting data language...'
+ puts "${white}Detecting data language..."
+
+ _speech_checksums=''
+
+ find_file 0 _speech_files 'speech.pak'
+ eval "set -- $_speech_files"
+ for _speech_file ; do
+
+ checksum _speech_checksum "$_speech_file"
+
+ data_lang=''
+ "$callback" "$_speech_checksum"
+
+ if [ -z "$data_lang" ] ; then
+ list_append _speech_checksums "$_speech_checksum"
+ else
+ use_file "$_speech_file" 'speech.pak'
+ break
+ fi
+
+ done
+
+ if [ -z "$data_lang" ] ; then
+ printf '\n'
+ case "$_speech_checksums" in
+ '') die "speech*.pak not found" ;;
+ *) die "Unsupported data language - speech*.pak checksum: $_speech_checksums" ;;
+ esac
+ fi
+
+ printf " ${green}%s${reset}\n\n" "${data_lang_desc}"
+
+}
+
+
+##########################################################################################
+# Get the patch file
+
+# Warn if the user-supplied patch file has a suspicious name.
+# Usage: patch_check_file_name <file> <expected-name-list>
+patch_check_file_name() {
+ _user_patch_name="$(basename "$1")"
+ _patch_check_file_names="$2"
+ if ! list_contains _patch_check_file_names "$_user_patch_name" ; then
+ printf "${yellow}Warning: unexpected patch file name: %s\n" "$_user_patch_name" >&2
+ printf "Expected %s${reset}\n" "$(print_help_or _patch_check_file_names)" >&2
+ fi
+}
+
+# Find or download a patch file.
+# Usage: probe_patch_file_impl <callback> <name> <user-supplied-file> \
+# <expected-name-list> <url-list> \
+# <callback> will be called with the patch file
+probe_patch_file_impl() {
+
+ _patch_found="$1"
+ _patch_name="$2"
+ _patch_file="$3"
+ _patch_names="$4"
+ _patch_urls="$5"
+
+ if [ ! -z "$_patch_file" ] ; then
+
+ # Check the filename of user-supplied patchse
+ patch_check_file_name "$_patch_file" "$_patch_names"
+
+ "$_patch_found" "$_patch_file"
+ return $true
+ fi
+
+ [ $probe_patch = 0 ] && return $true
+
+ # Probe local files now so we don't lie abut downloading it
+ probe_files "$_patch_found" "$_patch_names" && return $true
+
+ status --temp "${white}Downloading patch ${blue}${_patch_name}${reset}..."
+
+ create_workdir
+ download "$_patch_found" "$_patch_name" \
+ "$_patch_names" "$_patch_urls" "${workdir}/${_patch_name}" \
+ && return $true
+
+}
+
+# Callback for the japanese patch.
+patch_jp_found() {
+ patchfile_jp="$(abspath "$1")"
+ printf "Using Japanese %s patch: ${blue}%s${reset}\n" \
+ "$patch_jp_ver" "$patchfile_jp"
+ return $true
+}
+
+# Callback for the main patch.
+patch_found() {
+ patchfile="$(abspath "$1")"
+ printf "Using %s patch: ${blue}%s${reset}\n" "$patch_ver" "$patchfile"
+ return $true
+}
+
+# Find the patch file(s) for a specific language.
+# Usage: probe_patch_file <language>
+# If patchfile is already set, uses that.
+probe_patch_file() {
+
+ _patch_file_lang=''
+ case "$1" in
+ 'german') _patch_file_lang='GE' ;;
+ 'english') _patch_file_lang='EN' ;;
+ 'spanish') _patch_file_lang='ES' ;;
+ 'french') _patch_file_lang='FR' ;;
+ 'italian') _patch_file_lang='IT' ;;
+ 'russian') _patch_file_lang='RU' ;;
+ 'japanese')
+ _patch_jp_names=''
+ list_append _patch_jp_names "$patch_jp_name"
+ probe_patch_file_impl patch_jp_found "$patch_jp_name" "$patchfile_jp" \
+ "$_patch_jp_names" "$patch_jp_urls"
+ ;;
+ esac
+ _patch_names=''
+ list_append _patch_names "$patch_name"
+ if [ ! -z "$_patch_file_lang" ] ; then
+ list_append _patch_names "$(printf "$patch_name_localized" "$_patch_file_lang")"
+ fi
+
+ probe_patch_file_impl patch_found "$patch_name" "$patchfile" \
+ "$_patch_names" "$patch_urls"
+
+}
+
+patchdir='' # Directory where the patch file(s) are extracted
+
+# Extract all patch files for a specific language.
+# Usage: extract_patch <language>
+# Does nothing if the patch files are already extracted.
+extract_patch() {
+ [ -z "$patchdir" ] || return $true
+ _extract_patch_lang="$1"
+
+ print
+
+ # Search for and download the patch files if needed
+ probe_patch_file "$_extract_patch_lang"
+
+ status --temp "${white}Extracting patch...${reset}"
+
+ create_workdir
+ patchdir="$workdir/patch"
+ create_dir "$patchdir" 'patch work'
+
+ # Extract the main patch file
+ if [ ! -z "$patchfile" ] ; then
+ _patchdir_main="$patchdir/main"
+ create_dir "$_patchdir_main" 'main patch work'
+ cd "$_patchdir_main"
+ innosetup_language="$_extract_patch_lang"
+ extract "$patchfile" innosetup
+ innosetup_language=''
+ fi
+
+ if [ ! -z "$patchfile_jp" ] ; then
+
+ # Extract the Japanese patch file
+ _patchdir_jp="$patchdir/main"
+ create_dir "$_patchdir_jp" 'jp patch work'
+ cd "$_patchdir_jp"
+ extract "$patchfile_jp" ms_cab
+
+ # Also extract contained files
+ extract_cab_files "$_patchdir_jp" "$patchdir"
+
+ fi
+
+ print
+ status --temp
+}
+
+
+##########################################################################################
+# Copy and verify files
+
+checksum_failed=0 # Was there any mismatched checksum or missing file so far?
+
+# Handle a required file: find, compare checksum and copy/move if needed.
+# Usage: required_file <is-patchable> <filepath> <checksums>
+required_file() {
+
+ _patchable="$1"
+ _name="$2"
+ _valid="$3"
+
+ find_file 1 _files "$_name"
+ eval "set -- $_files"
+ _checksums=''
+ for _file ; do
+ checksum _checksum "$_file"
+
+ if list_contains _valid "$_checksum" ; then
+ # We found a match - use it
+ printf ' - %s\n' "$_name"
+ use_file "$_file" "$_name"
+ return $true
+ fi
+
+ # Remember mismatched checksums so we can output debug info if none matched
+ list_append _checksums "$_checksum"
+ continue
+
+ done
+
+ # No matching file found!
+
+ # If we didn't use the patch yet, fetch it and try again!
+ if [ $patch = 1 ] && [ $_patchable = 1 ] && [ -z "$patchdir" ] ; then
+ extract_patch "$data_lang"
+ if [ ! -z "$patchdir" ] ; then
+ required_file "$_patchable" "$_name" "$_valid"
+ return $?
+ fi
+ fi
+
+ # Let the user know that something is wrong!
+ if [ -z "$_checksums" ] ; then
+ printf "${red}Missing ${dim_red}%s${red}!${reset}\n" "$_name" >&2
+ else
+ printf "${red}Checksum failed for ${dim_red}%s${reset}:\n" "$_name" >&2
+ printf " expected: ${dim_red}%s${reset}\n" "$(print_help_or _valid)" >&2
+ printf " actual: ${dim_red}%s${reset}\n" "$(print_help_or _checksums)" >&2
+ fi
+
+ # Be optimistic, copy the first result even if the checksum doesn't match!
+ # We will display an error at the end (end exit with $false), but it may still work.
+ eval "set -- $_files"
+ for _file ; do
+ use_file "$_file" "$_name"
+ break
+ done
+
+ checksum_failed=1
+ return $false
+}
+
+# Handle an optional file: find and copy/move if it exists.
+# Usage: optional_file <filepath>
+optional_file() {
+ _name="$1"
+ find_file 1 _files "$_name"
+ eval "set -- $_files"
+ for _file ; do
+ # There is no checksum, just copy the first file
+ printf ' - %s\n' "$_name"
+ use_file "$_file" "$_name"
+ break
+ done
+}
+
+
+##########################################################################################
+# Setup
+
+# Select source file / directory
+if [ $install = 1 ] ; then
+ probe_source_files
+ if [ $batch = 0 ] ; then
+ list_append sourcefiles 'Patch existing install' ''
+ list_append sourcefiles 'Verify existing install only' ''
+ fi
+ user_select_entry --existing --any sourcefiles sourcefile \
+ "source file or directory to install from" "$green" "$dim_green" 'install from'
+ case "$sourcefile" in
+ 'Patch existing install') install=0 ; patch=1 ;;
+ 'Verify existing install only') install=0 ; patch=0 ;;
+ *) [ -e "$sourcefile" ] || die "Missing source file: $sourcefile"
+ esac
+ set_append probe_file_dirs "$(dirname "$sourcefile")"
+fi
+
+# Select destination data directory
+probe_data_dirs
+if [ $install = 1 ] ; then
+ verb='install to' ; access=--writable
+elif [ $patch = 1 ] ; then
+ verb='patch' ; access=--existing
+else
+ verb='verify' ; access=--existing
+fi
+user_select_entry $access --dir datadirs datadir \
+ "data directory to $verb" "$cyan" "$dim_cyan" "$verb"
+[ -z "$datadir" ] && die "Missing data dir."
+if [ $install = 1 ] ; then
+ create_dir "$datadir" 'data'
+else
+ [ -d "$datadir" ] || die "Missing data dir: $datadir"
+fi
+
+# Extract source files
+if [ $install = 1 ] ; then
+ printf "\nInstalling Arx Fatalis data files \nfrom %s\nto %s\n\n" \
+ "${green}$sourcefile${reset}" "${cyan}$datadir${reset}"
+ extract_source
+else
+ printf "\nVerifying Arx Fatalis data files \nin %s\n\n" "${cyan}$datadir${reset}"
+fi
+
+
+##########################################################################################
+# Required files
+
+# Detect language
+determine_language() {
+ speech_checksum="$1" # speech.pak
+
+ case "$speech_checksum" in
+ '4e8f962d8204bcfd79ce6f3226d6d6de') data_lang='english' ;;
+ '4c3fdb1f702700255924afde49081b6e') data_lang='german' ;;
+ 'ab8a93161688d793a7c78fbefd7d133e') data_lang='german' ;;
+ '2f88c67ae1537919e69386d27583125b') data_lang='spanish' ;;
+ '4edf9f8c799190590b4cd52cfa5f91b1') data_lang='french' ;;
+ '81f05dea47c52d43f01c9b44dd8fe962') data_lang='italian' ;;
+ '677163bc319cd1e9aa1b53b5fb3e9402') data_lang='russian' ;;
+ '235b86700fc80b3eb86731d748013a38') data_lang='japanese' ;;
+ '62ca7b1751c0615ee131a94f0856b389') data_lang='english-demo' ;;
+ '09038e43508232c44537c162f9e3ecde') data_lang='french-demo' ;;
+ 'a424fcfc46dd4f11b04030efac15a668') data_lang='german-demo' ;;
+ 'eeacbd9a845ecc00054934e82e9d7dd3') data_lang='japanese-demo' ;;
+ esac
+
+ case "$data_lang" in
+ 'english') data_lang_desc='English' ;;
+ 'german') data_lang_desc='German' ;;
+ 'spanish') data_lang_desc='Spanish' ;;
+ 'french') data_lang_desc='French' ;;
+ 'italian') data_lang_desc='Italian' ;;
+ 'russian') data_lang_desc='Russian' ;;
+ 'japanese') data_lang_desc='Japanese' ;;
+ 'english-demo') data_lang_desc='English (demo)' ;;
+ 'french-demo') data_lang_desc='French (demo)' ;;
+ 'german-demo') data_lang_desc='German (demo)' ;;
+ 'japanese-demo') data_lang_desc='Japanese (demo)' ;;
+ esac
+
+}
+detect_data_langauge determine_language
+
+if [ $install = 1 ] ; then
+ progress=50
+ status $progress "${white}Copying and verifying files...${reset}"
+ case "$data_lang" in *-demo) increment=8 ;; *) increment=1 ;; esac
+else
+ progress=15
+ status $progress "${white}Verifying files...${reset}"
+ case "$data_lang" in *-demo) increment=14 ;; *) increment=2 ;; esac
+fi
+print " - speech.pak"
+
+# Usage: f <is-patchable> <file> <checksums>...
+f() {
+
+ # Update progress bar
+ progress=$((progress + increment))
+ status $progress
+
+ # Verify & copy file
+ required_file "$@"
+}
+
+# speech.pak - already copied in detect_data_langauge
+
+# loc.pak contains the localized text, so it's different for each language!
+case "$data_lang" in
+ german) loc_checksum='31bc35bca48e430e108db1b8bcc2621d' ;;
+ english) loc_checksum='a47b192493afb5794e2161a62d35b69f' ;;
+ spanish) loc_checksum='121f99608814a2c9c5857cfadb665553' ;;
+ french) loc_checksum='f8fc448fea12469ed94f417c313fe5ea' ;;
+ italian) loc_checksum='a9e162f2916f5737a95bd8c5bd8a979e' ;;
+ russian) loc_checksum='a131bf2398ee70a9c22a2bbffd9d0d99' ;;
+ japanese) loc_checksum='9dcb0f5d7a517be4f1d9190419900892' ;;
+ english-demo) loc_checksum='2ae16d3925c597dca70f960f175def3a' ;;
+ french-demo) loc_checksum='4a8ac68341d4758a32d9cd04955b115e' ;;
+ german-demo) loc_checksum='87accec0658aa109a3efa8b41aab61df' ;;
+ japanese-demo) loc_checksum='9d84cede805b13fdf7fce856ecc15b19' ;;
+ *) loc_checksum=''
+esac
+if [ ! -z "$loc_checksum" ] ; then
+ f 1 'loc.pak' "$loc_checksum"
+fi
+
+# misc/arx.ttf is the same for everything except japanese
+# there are also separate misc/arx_russian.ttf and misc/arx_taiwanese.ttf handled later
+case "$data_lang" in
+ japanese*) font_checksum='58eab00842d8adea8d553ae1f66b0c9b' ;;
+ *) font_checksum='9a95ff96795c034524ba1c2e94ea12c7' ;;
+esac
+if [ ! -z "$font_checksum" ] ; then
+ f 1 'misc/arx.ttf' "$font_checksum"
+fi
+
+case "$data_lang" in
+
+ english-demo)
+ f 0 'data2.pak' 958b78f8f370b06d769843137138c461
+ f 0 'data.pak' 5d7ba6e6c79ebf7fbb232eaced9e8ad9
+ f 0 'misc/logo.bmp' aa3dfbd4bc9c863d10a0c5345ae5a4c9
+ f 0 'sfx.pak' ea1b3e6d6f4906905d4a34f07e9a59ac
+ ;;
+
+ french-demo)
+ f 0 'data2.pak' 8dc1d1b3e85d4a41ae320aa3fa9c649a
+ f 0 'data.pak' 5d7ba6e6c79ebf7fbb232eaced9e8ad9
+ f 0 'misc/logo.bmp' aa3dfbd4bc9c863d10a0c5345ae5a4c9
+ f 0 'sfx.pak' ea1b3e6d6f4906905d4a34f07e9a59ac
+ ;;
+
+ german-demo)
+ f 0 'data2.pak' 143ba491a357263a2dfad9936a66eeb6
+ f 0 'data.pak' 5d7ba6e6c79ebf7fbb232eaced9e8ad9
+ f 0 'misc/logo.bmp' aa3dfbd4bc9c863d10a0c5345ae5a4c9
+ f 0 'sfx.pak' ea1b3e6d6f4906905d4a34f07e9a59ac
+ ;;
+
+ japanese-demo)
+ f 0 'data2.pak' 958b78f8f370b06d769843137138c461
+ f 0 'data.pak' 903dfe1878a0cedff3b941fd3aa22ba9
+ f 0 'misc/logo.bmp' aa3dfbd4bc9c863d10a0c5345ae5a4c9
+ f 0 'sfx.pak' ea1b3e6d6f4906905d4a34f07e9a59ac
+ ;;
+
+ *) # full game
+
+ f 1 'graph/interface/misc/arkane.bmp' afff1099c01ffeb03b9a351f7b5966b6
+ f 1 'graph/interface/misc/quit1.bmp' 41445d3792a1f8818d950aca47254488
+ f 1 'graph/obj3d/textures/fixinter_barrel.jpg' 8419274acbff7346c3661b18d6aad6dc
+ f 1 'graph/obj3d/textures/fixinter_bell.bmp' 5743b9047c9ad65540c318dfcc98123a
+ f 1 'graph/obj3d/textures/fixinter_metal_door.jpg' f246eff6b19c9c710313b4a4dce96a69
+ f 1 'graph/obj3d/textures/fixinter_public_notice.bmp' f81394abbb9006ce0950843b7909db33
+ f 1 'graph/obj3d/textures/item_bread.bmp' 544448f8eedc912aa231a6a04fffb7c5
+ f 1 'graph/obj3d/textures/item_club.jpg' 7e26c4199ddaca494c8b369294306b0b
+ f 1 'graph/obj3d/textures/item_long_sword.jpg' 3a6196fe9b7666c7d80d82be06f6de86
+ f 1 'graph/obj3d/textures/item_mauld_sabre.jpg' 18492c25ebac02f83e2f0ebda61ecb00
+ f 1 'graph/obj3d/textures/item_mauldsword.jpg' 503a5c2f23668040c675aefdde6dbbe5
+ f 1 'graph/obj3d/textures/item_mirror.jpg' c0a22b4f7a7a6461da68206e94928637
+ f 1 'graph/obj3d/textures/item_ring_casting.bmp' 348f9add709bacee08556d1f8cf10f3f
+ f 1 'graph/obj3d/textures/item_rope.bmp' ff05de281c8b380ee98f6e123d3d51cb
+ f 1 'graph/obj3d/textures/item_spell_sheet.jpg' 024ccbb520020f92fba5a5a4f0270cea
+ f 1 'graph/obj3d/textures/item_torch2.jpg' 027951899b4829599ca611010ea3484f
+ f 1 'graph/obj3d/textures/item_torch.jpg' 9ada166f23ddcb775ac20836e752187e
+ f 1 'graph/obj3d/textures/item_zohark.bmp' cd206a4027f86c6e57b7710c94049efa
+ f 1 'graph/obj3d/textures/l7_dwarf_[wood]_board08.jpg' 79ccc81adb7c37b98f40b478ef1fccd4
+ f 1 'graph/obj3d/textures/l7_dwarf_[wood]_board80.jpg' 691611087b13d38ef02bb9dfd6a2518e
+ f 1 'graph/obj3d/textures/npc_dog.bmp' 116bd374c14ae8c387a4da1899e1dca7
+ f 1 'graph/obj3d/textures/npc_pig.bmp' b7a4d0d3d230b2d1470176909004e38b
+ f 1 'graph/obj3d/textures/npc_pig_dirty.bmp' 76034d8d74056c8a982479d36321c228
+ f 1 'graph/obj3d/textures/npc_rat_base.bmp' 00c585ec9ebe8006d7ca72993de7b51b
+ f 1 'graph/obj3d/textures/npc_rat_base_cm.bmp' cae38facbf77db742180b9e58d0eb42f
+ f 1 'graph/obj3d/textures/npc_worm_body_part1.jpg' 0b220bffaedc89fa663f08d12630c342
+ f 1 'graph/obj3d/textures/npc_worm_body_part2.bmp' 20797cb78f6393a0fb5405969ba9f805
+ f 1 'graph/obj3d/textures/[wood]_light_door.jpg' 00d0b018e995e7d013d6e52e92126901
+ f 1 'misc/arx_russian.ttf' 921561e83786efcd25f92147b60a13db
+ f 1 'misc/arx_taiwanese.ttf' da59198061cef0761c6b2fca113f76f6
+ f 1 'misc/logo.avi' 63ed31a4eb3d226c23e58cfaa974d484
+ f 1 'misc/logo.bmp' afff1099c01ffeb03b9a351f7b5966b6
+ f 1 'data2.pak' f7e0ce700bf963429ac535ca86f8a7b4
+
+ f 0 'sfx.pak' 2efc9a74c517fd1ee9919900cf4091d2
+
+ # data.pak is censored in some versions (presumably has less gore)
+ # At least the original german and italian CDs have the censored version.
+ # The censored version has different level files and a different
+ # human_female_villager model.
+ # There are also minor differences in the scripts, but those are
+ # overwritten by data2.pak from the 1.21 patch.
+ # A third data.pak variant can be found on the original French Arx Fatalis CD:
+ # It is almost identical to the preceding censored version, but has different level
+ # files for level 1 (.llf only) and 3 (.llf and .fts).
+ data_checksum_original='a91a0b39a046233debbb10b4850e13eb'
+ data_checksum_censored='a88d239dc7919ab113ff45483cb4ad46'
+ data_checksum_frenchcd='7ae3632eef92700cd6c5e143aa0fe67b'
+ f 0 'data.pak' "$data_checksum_original $data_checksum_censored $data_checksum_frenchcd"
+
+esac
+
+# Optional files - we don't need them, but copy them anyway if available
+optional_file 'manual.pdf'
+optional_file 'map.pdf'
+optional_file 'arx_handbuch.pdf'
+
+print
+
+
+##########################################################################################
+# Print a summary
+
+if [ $install = 1 ] ; then verb='Installed' ; else verb='Verified' ; fi
+printf "${white}%s Arx Fatalis %s data: ${green}%s${reset}\n" "$verb" \
+ "$patch_ver" "$data_lang_desc"
+
+if [ $checksum_failed = 1 ] ; then
+ [ $gui = 0 ] || status 100 "Error!"
+ die "There are wrong or missing files!${reset}
+
+The game may run fine, or it may fail - good luck!" >&2
+fi
+
+status 100 "${dim_green}All good!${reset}"
+if [ $install = 1 ] ; then verb='Installation' ; else verb='Verification' ; fi
+dialog_message "$verb complete: $data_lang_desc
+
+Have fun playing Arx Fatalis!"
+
+quit $true
diff --git a/install-gog b/install-gog
deleted file mode 100755
index 98f09ca83492..000000000000
--- a/install-gog
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/bin/sh
-
-die() {
- echo "$@" >&2
- exit 1
-}
-
-# Handle arguments
-
-innoextract_opts=''
-if [ "$1" = "--no-progress" ] ; then
- innoextract_opts='--progress=off'
- shift
-fi
-
-[ "$1" = "--help" ] || [ "$1" = "-h" ] || setupfile="$(readlink -m "$1")"
-
-[ "$setupfile" != "" ] || die "\
-Usage: install-gog path/to/setup_arx_fatalis.exe [output_dir]
-
-Optional option (must be the first argument):
- --no-progress Disable the innoextract progress bar
-
-This script can be used to install the Arx Fatalis data from a GOG.com setup file.
-Files will be verified and renamed as needed by Arx Libertatis.
-
-setup_arx_fatalis.exe can be downloaded from your GOG.com account after buying Arx Fatalis"
-
-if [ "$2" = "" ]
- then destdir="$(pwd)"
- else destdir="$(readlink -m "$2")"
-fi
-
-cd "$(dirname "$0")"
-here="$(pwd)"
-
-echo "Installing Arx Fatalis GOG.com data from \"$setupfile\" to \"$destdir\".
-"
-
-[ -f "$here/install-verify" ] || die "Missing install-verify script."
-install_verify_sourced=1
-. "$here/install-verify"
-
-# Check for required commands
-
-innoextract=`which innoextract 2> /dev/null`
-
-[ -f "$innoextract" ] \
- || die "Please install innoextract (http://constexpr.org/innoextract/)"
-
-# Verify input file
-
-checksum="$("$md5sum" -b "$setupfile" | sed 's/ .*//')"
-expected='0dd8ec13c10146db1a741258b624040a'
-if [ "$checksum" = "$expected" ]
- then echo "Checksum matched."
- else echo "Got checksum $checksum, expected $expected."
-fi
-
-# Prepare output and temp dirs
-
-mkdir -p "$destdir" || exit 1
-
-tempdir="$destdir/arx-install-gog-temp"
-
-rm -rf "$tempdir" 2> /dev/null
-mkdir "$tempdir" || exit 1
-cd "$tempdir" || exit 1
-
-# Extract files
-
-"$innoextract" $innoextract_opts --lowercase "$setupfile"
-
-# Install required files
-
-for f in "$@" ; do
-
- dir="$(dirname "$f")"
- mkdir -pv "$destdir/$dir"
-
- mv -fv "app/$f" "$destdir/$f"
-
- chmod "--reference=$destdir" "$destdir/$f" > /dev/null 2>&1
- chmod -x "$destdir/$f" > /dev/null 2>&1
-
-done
-
-# Cleanup temporary files
-
-rm -rf "$tempdir"
-
-# Verify installed files
-
-cd "$destdir"
-detect_language
-verify_checksums
diff --git a/install-verify b/install-verify
deleted file mode 100644
index d52bddac27ec..000000000000
--- a/install-verify
+++ /dev/null
@@ -1,257 +0,0 @@
-#!/bin/sh
-
-# Verify arx fatalis non-demo checksums.
-# This script is meant to be run on it's own or sourced by the other
-# install scripts.
-
-# List of required files
-
-if [ "$install_verify_sourced" != "1" ] ; then
- die() {
- echo "$@" >&2
- exit 1
- }
- ( [ "$1" = "--help" ] || [ "$1" = "-h" ] ) && die "\
-Usage: install-verify [directory]"
- if [ "$1" = "" ]
- then checkdir="$(pwd)"
- else checkdir="$(readlink -m "$1")"
- fi
- [ -d "$checkdir" ] || die "directory $checkdir does not exist"
-fi
-
-set -- \
- "data2.pak" \
- "graph/interface/misc/arkane.bmp" \
- "graph/interface/misc/quit1.bmp" \
- "graph/obj3d/textures/fixinter_barrel.jpg" \
- "graph/obj3d/textures/fixinter_bell.bmp" \
- "graph/obj3d/textures/fixinter_metal_door.jpg" \
- "graph/obj3d/textures/fixinter_public_notice.bmp" \
- "graph/obj3d/textures/item_bread.bmp" \
- "graph/obj3d/textures/item_club.jpg" \
- "graph/obj3d/textures/item_long_sword.jpg" \
- "graph/obj3d/textures/item_mauld_sabre.jpg" \
- "graph/obj3d/textures/item_mauldsword.jpg" \
- "graph/obj3d/textures/item_mirror.jpg" \
- "graph/obj3d/textures/item_ring_casting.bmp" \
- "graph/obj3d/textures/item_rope.bmp" \
- "graph/obj3d/textures/item_spell_sheet.jpg" \
- "graph/obj3d/textures/item_torch2.jpg" \
- "graph/obj3d/textures/item_torch.jpg" \
- "graph/obj3d/textures/item_zohark.bmp" \
- "graph/obj3d/textures/l7_dwarf_[wood]_board08.jpg" \
- "graph/obj3d/textures/l7_dwarf_[wood]_board80.jpg" \
- "graph/obj3d/textures/npc_dog.bmp" \
- "graph/obj3d/textures/npc_pig.bmp" \
- "graph/obj3d/textures/npc_pig_dirty.bmp" \
- "graph/obj3d/textures/npc_rat_base.bmp" \
- "graph/obj3d/textures/npc_rat_base_cm.bmp" \
- "graph/obj3d/textures/npc_worm_body_part1.jpg" \
- "graph/obj3d/textures/npc_worm_body_part2.bmp" \
- "graph/obj3d/textures/[wood]_light_door.jpg" \
- "manual.pdf" \
- "map.pdf" \
- "misc/arx_default.ttf" \
- "misc/arx_russian.ttf" \
- "misc/arx_taiwanese.ttf" \
- "misc/logo.avi" \
- "misc/logo.bmp" \
- "sfx.pak" \
- "data.pak" \
- "loc.pak" \
- "speech.pak"
-
-# Common functions
-
-md5sum=`which md5sum 2> /dev/null`
-[ -f "$md5sum" ] \
- || die "Please install md5sum (http://www.gnu.org/software/coreutils/)"
-
-data_lang='english'
-detect_language() {
-
- speech_checksum=`find '.' -iname "speech.pak" -exec "$md5sum" -b {} \; \
- | sed "s/ .*//g"`
- if [ "$speech_checksum" = '' ] ; then
- speech_checksum=`find '.' -iname "speech_default.pak" \
- -exec "$md5sum" -b {} \; | sed "s/ .*//g"`
- fi
-
- # check if the checksum is of a known localisation and set data_lang to
- # the language string to be used with the 1.21 patch installer
- case "$speech_checksum" in
-
- '4c3fdb1f702700255924afde49081b6e') data_lang='german'
- loc_checksum='31bc35bca48e430e108db1b8bcc2621d' ;;
-
- # Bundled version of AF included with NVIDIA card
- 'ab8a93161688d793a7c78fbefd7d133e') data_lang='german'
- loc_checksum='31bc35bca48e430e108db1b8bcc2621d' ;;
-
- '4e8f962d8204bcfd79ce6f3226d6d6de') data_lang='english'
- loc_checksum='a47b192493afb5794e2161a62d35b69f' ;;
-
- '2f88c67ae1537919e69386d27583125b') data_lang='spanish'
- loc_checksum='121f99608814a2c9c5857cfadb665553' ;;
-
- '4edf9f8c799190590b4cd52cfa5f91b1') data_lang='french'
- loc_checksum='f8fc448fea12469ed94f417c313fe5ea' ;;
-
- '81f05dea47c52d43f01c9b44dd8fe962') data_lang='italian'
- loc_checksum='a9e162f2916f5737a95bd8c5bd8a979e' ;;
-
- '677163bc319cd1e9aa1b53b5fb3e9402') data_lang='russian'
- loc_checksum='a131bf2398ee70a9c22a2bbffd9d0d99' ;;
-
- '') die "speech*.pak not found in $(pwd)" ;;
- *) die "unsupported data language - speech*.pak checksum:" \
- "$speech_checksum" ;;
- esac
-
- echo "
-Data language: $data_lang
-"
-}
-
-record_checksum_failure() {
- if [ $checksum_failed = 0 ] ; then
- echo "
-Checksum failed:"
- checksum_failed=1
- fi
-}
-
-verify_checksum() {
-
- file="$1" ; shift
-
- if [ ! -f "$file" ] ; then
- record_checksum_failure
- echo "- missing $file"
- return
- fi
-
- checksum=`"$md5sum" -b "$file" | sed "s/ .*//g"`
-
- checksum_matched=0
- for valid_checksum in "$@" ; do
- [ "$checksum" = "$valid_checksum" ] && checksum_matched=1
- done
-
- if [ $checksum_matched = 0 ] ; then
-
- record_checksum_failure
-
- errorstr="- $file: got '$checksum', expected '$1'" ; shift
- for alternate_checksum in "$@" ; do
- errorstr="$errorstr or '$alternate_checksum'"
- done
- echo "$errorstr"
- fi
-}
-
-verify_checksums() {
-
- echo "
-Done:"
-
- checksum_failed=0
-
- # Common files
- verify_checksum 'data2.pak' \
- 'f7e0ce700bf963429ac535ca86f8a7b4'
- verify_checksum 'graph/interface/misc/arkane.bmp' \
- 'afff1099c01ffeb03b9a351f7b5966b6'
- verify_checksum 'graph/interface/misc/quit1.bmp' \
- '41445d3792a1f8818d950aca47254488'
- verify_checksum 'graph/obj3d/textures/fixinter_barrel.jpg' \
- '8419274acbff7346c3661b18d6aad6dc'
- verify_checksum 'graph/obj3d/textures/fixinter_bell.bmp' \
- '5743b9047c9ad65540c318dfcc98123a'
- verify_checksum 'graph/obj3d/textures/fixinter_metal_door.jpg' \
- 'f246eff6b19c9c710313b4a4dce96a69'
- verify_checksum 'graph/obj3d/textures/fixinter_public_notice.bmp' \
- 'f81394abbb9006ce0950843b7909db33'
- verify_checksum 'graph/obj3d/textures/item_bread.bmp' \
- '544448f8eedc912aa231a6a04fffb7c5'
- verify_checksum 'graph/obj3d/textures/item_club.jpg' \
- '7e26c4199ddaca494c8b369294306b0b'
- verify_checksum 'graph/obj3d/textures/item_long_sword.jpg' \
- '3a6196fe9b7666c7d80d82be06f6de86'
- verify_checksum 'graph/obj3d/textures/item_mauld_sabre.jpg' \
- '18492c25ebac02f83e2f0ebda61ecb00'
- verify_checksum 'graph/obj3d/textures/item_mauldsword.jpg' \
- '503a5c2f23668040c675aefdde6dbbe5'
- verify_checksum 'graph/obj3d/textures/item_mirror.jpg' \
- 'c0a22b4f7a7a6461da68206e94928637'
- verify_checksum 'graph/obj3d/textures/item_ring_casting.bmp' \
- '348f9add709bacee08556d1f8cf10f3f'
- verify_checksum 'graph/obj3d/textures/item_rope.bmp' \
- 'ff05de281c8b380ee98f6e123d3d51cb'
- verify_checksum 'graph/obj3d/textures/item_spell_sheet.jpg' \
- '024ccbb520020f92fba5a5a4f0270cea'
- verify_checksum 'graph/obj3d/textures/item_torch2.jpg' \
- '027951899b4829599ca611010ea3484f'
- verify_checksum 'graph/obj3d/textures/item_torch.jpg' \
- '9ada166f23ddcb775ac20836e752187e'
- verify_checksum 'graph/obj3d/textures/item_zohark.bmp' \
- 'cd206a4027f86c6e57b7710c94049efa'
- verify_checksum 'graph/obj3d/textures/l7_dwarf_[wood]_board08.jpg' \
- '79ccc81adb7c37b98f40b478ef1fccd4'
- verify_checksum 'graph/obj3d/textures/l7_dwarf_[wood]_board80.jpg' \
- '691611087b13d38ef02bb9dfd6a2518e'
- verify_checksum 'graph/obj3d/textures/npc_dog.bmp' \
- '116bd374c14ae8c387a4da1899e1dca7'
- verify_checksum 'graph/obj3d/textures/npc_pig.bmp' \
- 'b7a4d0d3d230b2d1470176909004e38b'
- verify_checksum 'graph/obj3d/textures/npc_pig_dirty.bmp' \
- '76034d8d74056c8a982479d36321c228'
- verify_checksum 'graph/obj3d/textures/npc_rat_base.bmp' \
- '00c585ec9ebe8006d7ca72993de7b51b'
- verify_checksum 'graph/obj3d/textures/npc_rat_base_cm.bmp' \
- 'cae38facbf77db742180b9e58d0eb42f'
- verify_checksum 'graph/obj3d/textures/npc_worm_body_part1.jpg' \
- '0b220bffaedc89fa663f08d12630c342'
- verify_checksum 'graph/obj3d/textures/npc_worm_body_part2.bmp' \
- '20797cb78f6393a0fb5405969ba9f805'
- verify_checksum 'graph/obj3d/textures/[wood]_light_door.jpg' \
- '00d0b018e995e7d013d6e52e92126901'
- verify_checksum 'misc/arx_default.ttf' \
- '9a95ff96795c034524ba1c2e94ea12c7'
- verify_checksum 'misc/arx_russian.ttf' \
- '921561e83786efcd25f92147b60a13db'
- verify_checksum 'misc/arx_taiwanese.ttf' \
- 'da59198061cef0761c6b2fca113f76f6'
- verify_checksum 'misc/logo.avi' \
- '63ed31a4eb3d226c23e58cfaa974d484'
- verify_checksum 'misc/logo.bmp' \
- 'afff1099c01ffeb03b9a351f7b5966b6'
- verify_checksum 'sfx.pak' \
- '2efc9a74c517fd1ee9919900cf4091d2'
-
- # data.pak is censored in some versions (presumably has less gore)
- # At least the original german and italian CDs have the censored version.
- # The censored version has different level files and a different
- # human_female_villager model.
- # There are also minor differences in the scripts, but those are
- # overwritten by data2.pak from the 1.21 patch.
- data_checksum_original='a91a0b39a046233debbb10b4850e13eb'
- data_checksum_censored='a88d239dc7919ab113ff45483cb4ad46'
- verify_checksum 'data.pak' "$data_checksum_original" "$data_checksum_censored"
-
- # Language-specific files
- verify_checksum 'loc.pak' "$loc_checksum"
- # There is no need to check speech.pak here as we already used it to
- # detect $loc_checksum
-
- [ $checksum_failed = 0 ] || die "
-ERROR: Checksum mismatch."
- echo "Checksum match."
-}
-
-if [ "$install_verify_sourced" != "1" ] ; then
- cd "$checkdir"
- detect_language
- verify_checksums
-fi