#!/bin/bash
#The MIT License (MIT)
#
#Copyright (c) 2016
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
hash zenity 2>/dev/null || { echo >&2 "Program requires zenity but it's not installed. Aborting."; exit 1; }
hash unionfs-fuse 2>/dev/null || { echo >&2 "Program requires unionfs-fuse but it's not installed. Aborting."; exit 1; }
hash wine 2>/dev/null || { echo >&2 "Program requires wine but it's not installed. Aborting."; exit 1; }
hash fusermount 2>/dev/null || { echo >&2 "Program requires fusermount but it's not installed. Aborting."; exit 1; }
MOD_DIR="./mods"
GAME_DIR="./NWN"
MOUNT_DIR="./game_mount"
PREFIXES="$HOME/.local/share/wineprefixes"
mkdir -p "$PREFIXES"
export WINEARCH=win32
export WINEPREFIX="$PREFIXES/Neverwinter Nights"
#cd to the script dir if executed from outside
GAME_PATH=$(dirname "$(readlink -f "$0")")
cd "$GAME_PATH"
containsElement () {
local e
for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
return 1
}
#most arrays will use \0 as seperator (illegal filename)
IFS=$'\0'
#saved history (SEL array variable)
source "$MOD_DIR/selection_dat"
PMODS=()
while read -r -d $'\0'; do
x=$(basename "$REPLY")
if containsElement "$x" "${SEL[@]}" ; then
PMODS+=("TRUE")
else
PMODS+=("FALSE")
fi
PMODS+=("$x")
#find all directories in the mod dir and put them in a array \0 seperated (-print0 adds one at the end so offbyone)
done < <(find "$MOD_DIR" -mindepth 1 -maxdepth 1 -type d -print0 )
if [ ${#PMODS[@]} -eq 0 ]; then
zenity --error --text "No mods detected, you need to extract them to seperate dirs in the \'$MOD_DIR\' dir"
if [ ! -d "$MOD_DIR" ]; then
mkdir "$MOD_DIR"
fi
exit 1
fi
if [ ! -d "$MOUNT_DIR" ]; then
mkdir "$MOUNT_DIR"
fi
#Zenity has no concept of arrays. Bash splits the array into individual arguments before passing ('\0' cant be the separator)
selection=$(zenity --height=500 --width=400 --list --checklist --column "On" --column "Name" --title "Select mods before starting" --text="Select active mods.\nDependencies or conflicts are not checked here." "${PMODS[@]}" --separator='/' )
#user didn't cancel (pressed ok)
if [ $? -eq 0 ]; then
#therefore we need to transform the string back to array
SEL=()
while read -r -d $'/'; do
SEL+=("$REPLY")
done <<<"$selection/"
#save selection for next run
typeset -p SEL > "$MOD_DIR/selection_dat"
#mount everything read only except the game dir (things are saved there as usual)
mount_points="$GAME_DIR=RW"
for mod in "${SEL[@]}"; do
#mods dirs and files don't need to write
mount_points="$MOD_DIR/$mod=RO:$mount_points"
done
#create userspace unionfs (requires unionfs-fuse and fusermount)
fusermount -q -u "$MOUNT_DIR"
#copy on write is needed due to a bug (it's on the manpage known issues)
#"there is no support for read-only branches when copy-on-write is disabled"
#we could just as well had made another RW layer above to save modifications
#there instead of the game folder
unionfs-fuse -ocow "$mount_points" "$MOUNT_DIR"
cd "$MOUNT_DIR"
#start game, wait for it to end and unmount
wine nwmain.exe
cd ..
#game ended back to normal
fusermount -u "$MOUNT_DIR"
fi
unset IFS