#!/bin/sh # Script to automatically and reverseably apply patches to ut2k4 on linux # Note that "reverseably" means the backups that were made during install will be restored # Version 0.8.3, by Hethrep (at linux-gamers.net), GPL'ed set -e # exit on first error utdir=/usr/local/games/ut2004 # when autodetecting via ~/.loki/installed/ut2004.xml fails self="`basename $0`" [ "$DEBUG" ] && { unset DEBUG; VERBOSE=1; set -x; } [ "$VERBOSE" ] && { unset VERBOSE; v="v"; } || v="" # !!! Nomenklatura: !!! # backups of the files are named $FILENAME.pre-.bak # if a new file is introduced an empty file will be created: $FILENAME.pre-.bak0 # the list of files is in "$utdir"/pre-patch..list # find utdir based on the .manifest symlink placed in ~, else fallback to given path if [ -L ~/.loki/installed/ut2004.xml -a -x "`which readlink`" ]; then i="`readlink -f ~/.loki/installed/ut2004.xml`" utdir="${i%/.manifest*}" unset i else utdir="$utdir" fi makereadme() { [ -w "$utdir" -a ! -e "$utdir"/pre-patch.README.list ] && cat < "$utdir"/pre-patch.README.list DO NOT DELETE the pre-patch..list files! They contain the files that have to be restored if a patch is to be reversed, the backups are usually at \$FILENAME.pre-.bak, pre-.bak0 means the file itself should be deleted. If you removed the file and want to reverse patch manually: cd ; for i in \`find -name '*.pre-.bak'\` do mv "\$i" "\${i%.pre-.bak}" done for i in \`find -name '*.pre-.bak0'\` do rm "\${i%.pre-.bak0}" "\$i" done EOF true } explainquit() { echo echo "To install a patch run:" echo " $self [Version]" echo " If Version is not given autoguessing from filename will be tried" echo "To install an already extracted patch run:" echo " $self " echo " (Version is required, ExtractedDir is most likely UT2004-Patch)" echo echo "To list Patches that can be reversed:" echo " $self -L" echo "To reverse a Patch run:" echo " $self -R XYZ" echo echo "Installing and Reversing will ask for a confirmation" echo exit 1 } [ -e "`tty`" ] || { echo "Error, can not run without a controlling terminal"; exit 1; } pgrep 'ut2004-bin|ucc-bin' > /dev/null && { echo "Avoiding \"Text file busy\" Error, shutdown ut2004 first!"; exit 1; } listavail() { echo "Patch(es) that can be reversed:" { ls "$utdir"/pre-patch.[!R]*[!E].list 2> /dev/null || echo " None"; exit 0; } | sed 's/.*\/pre-patch.\(.*\).list/ \1/' exit 0 } reverseto() { [ "$1" ] || { echo "Error, no patch given. Try $self -L or $self -h"; exit 1; } list="$utdir/pre-patch.$1.list" echo "Checking if reversing to state before $1 can be done..." [ -e "$list" ] || { echo "Error, no listing to reverse \"$1\" (i.e. \"$list\") found"; exit 1; } unset foo cd "$utdir" [ -w "$list" ] || { echo "Error, permissions do not allow deleting \"$list\" (are you root?)"; exit 1; } # first check if all said files are present while read -r i do [ -w "$i" ] || { echo "Error, permissions do not allow overwriting \"$i\" (are you root?)"; exit 1; } [ -f "$i" ] || { echo "Error, file \"$i\" not found"; foo=narf; } [ -f "$i.pre-$1.bak" -o -e "$i.pre-$1.bak0" ] || { echo "Error, backup for file \"$i\" not found"; foo=narf; } done < "$list" [ "$foo" = "narf" ] && { echo "(Used \"$list\" for listing)"; exit 1; } read -p "Yes, to continue, Ctrl-C to cancel: " # all clear, reverse and rewind echo "Reversing..." while read -r i do [ -f "$i.pre-$1.bak0" ] && rm "$i" "$i.pre-$1.bak0" [ -f "$i.pre-$1.bak" ] && mv "$i.pre-$1.bak" "$i" done < "$list" rm -f "$list" echo "Patch $1 successfully reversed" exit 0 } exitpatchdone() { echo "Warning, $1 is present already. Do not apply a patch twice, this would overwrite backups!"; exit; } patchut() { # tmpdir="`dirname "$utdir"`/.tmp.$$.$RANDOM.utpatch/" # just usefull if we mv'ed and not copied the stuff tmpdir="/tmp/.tmp.$$.$RANDOM.utpatch/" mkdir "$tmpdir" # maybe make cleanup 1 remove listfile as well? cleanup() { echo -e "\rCleaning up... "; rm -rf "$tmpdir"; trap "" EXIT; exit $1; } # cleanup and exit with and error for now trap 'cleanup 1' EXIT TERM INT QUIT HUP ABRT pwdnow="`pwd`" if [ -f "$1" ]; then [ "$2" ] && version="$2" || { version="${1%.tar*}"; version="${version#*patch}"; } [ "$version" ] || { echo "Error, autoguessing version from filename failed, specify on commandline."; exit 1; } # bla, assuming gnu tar echo "Extracting..." tar -x${v}jf "$1" -C "$tmpdir" if [ -d "$tmpdir/UT2004-Patch" ]; then cd "$tmpdir/UT2004-Patch" else #leap of faith cd "$tmpdir/"* 2> /dev/null || { echo "Error, unexpected Patchstructure"; exit 1; } fi patchdir="`pwd`" elif [ -d "$1" ]; then [ "$2" ] && version="$2" || { echo "Error, must specify Version! See $self -h"; exit; } #get absolute dir cd "$1" && patchdir="`pwd`" else echo "Error, $1 not a file or dir" exit fi # dont overwrite! [ -e "$utdir/pre-patch.$version.list" ] && exitpatchdone "$utdir/pre-patch.$version.list" #check if this looks like an ut patch, error if none of these are found # all are: Animations/ Help/ KarmaData/ Maps/ Music/ Sounds/ System/ StaticMeshes/ Textures/ Web/ # i deem only Help, Web and Sounds containing patches unlikely j=0; k=0 for i in Animations/ KarmaData/ Maps/ Music/ System/ StaticMeshes/ Textures/ do ((k++)) if [ -d "$i" ]; then [ -w "$utdir/$i" ] || { echo "Error, permissions do not allow patching \"$utdir\" (are you root?)"; exit 1; } else ((j++)) fi done [ $j -eq $k ] && { echo "Error, none of the characteristic ut2004 dirs found"; exit; } cd "$pwdnow" echo "Patching ut2004 install at \"$utdir\" with" [ -f "$1" ] && echo -n "file `basename "$1"`, patchname " || echo -n "directory \"$patchdir\", patchname " [ $2 ] && echo "\"$version\"." || echo "guessed to be \"$version\"." cd "$patchdir" # maybe check for n|N y|Y as well? read -p " to continue, Ctrl-C to cancel: " #make the list: find . -type f > "$utdir/pre-patch.$version.list" # check and then make the backup of the corresponding files cd "$utdir" while read -r i do [ -e "$i.pre-$version.bak" ] && exitpatchdone "$i.pre-$version.bak" [ -e "$i.pre-$version.bak0" ] && exitpatchdone "$i.pre-$version.bak0" done < "$utdir/pre-patch.$version.list" # mkdirs, just in case cd "$patchdir" find . -type d -exec mkdir -p${v} "$utdir/"{} \; cd "$utdir" # if Ctrl-C here i *should* delete backups again echo "Making backups..." while read -r i do [ -f "$i" ] && cp -a${v} "$i" "$i.pre-$version.bak" || touch "$i.pre-$version.bak0" done < "$utdir/pre-patch.$version.list" # then patch it # if Ctrl-C here i *should* copy the packups back into place :) echo "Applying patch..." # assuming no hidden files, else add "$patchdir/"[!.].* cp -R${v} "$patchdir/"* "$utdir" makereadme echo echo "Patch \"$version\" successfully applied" echo # seems all went smooth, exit 0 after cleanup trap 'cleanup 0' EXIT exit 0 } # easier than getops :) [ $# -gt 2 ] && { echo "Too many arguments"; explainquit; } [ $# -lt 1 ] && { explainquit; } [ "x$1" = "x-l" -o "x$1" = "x-L" ] && listavail [ "x$1" = "x-r" -o "x$1" = "x-R" ] && reverseto "$2" [ "x$1" = "x-h" -o "x$1" = "x--help" ] && explainquit [ "x${1:0:1}" = "x-" ] && { echo "Unrecognized option $1"; explainquit; } [ -e "$1" ] && patchut "$1" "$2" echo "Not a file or directory: $1" explainquit # MAYBE # is $tmpdir generation racecondition safe? # make errors go to stderr # allow http:// and download via wget? # check diskspace available? # check it the patch is bzipped or gzipped? # also check if patches add new dirs and remove these too?