supported() {
	echo "the following games are supported:"
	echo
	printf "\t    name\tdescription\n"
	printf "\t    ----\t-----------\n"

	find $SUPPORTED -type f | grep -v '\.svn' | grep -v 'swp$' | sort |
	while read file; do
		. $file
		printf "\t%8s\t%s\n" "$SHORTNAME" "$LONGNAME"
	done
}
options() {
	echo "game-data-packager arguments:"
	echo "     -i               install the generated package"
	echo "     -n               do not install the generated package (requires -d, default)"
	echo "     -d OUTDIR        write the generated .deb(s) to OUTDIR"
	echo "     -z | --compress  compress generated .deb (default unless -i is used)"
	echo "     --no-compress    do not compress generated .deb (default with -i)"
}

usage() {
	echo "usage:"
	printf "\tgame-data-packager [game-data-packager-args] game [game-args]\n"
	echo
	options
	echo
	supported
	echo
	echo "run game-data-packager [game] to see game-specific arguments."
	echo
}

set +u
if [ -n "$DEBUG" ]; then
debug() {
		echo "DEBUG: $*" >&2
}
else
debug() { :; }
fi
set -u

warn() {
	echo "WARNING: $*" >&2
}

## die(string,retcode)
##      end the program, complaining with string on stderr
##      and returning retcode if supplied, 2 if not.
die() { 
    if [ $# -lt 2 ]; then
        RET=2
    else
        RET=$2
    fi
	echo $0: $1 >&2
	exit $RET
}

## verify_md5sum(file,sum)
##      calculates the md5sum of file and compares it to sum.
##      if the sum doesn't match, complains on stderr and causes the program
##      to exit.
verify_md5sum() {
	FILE=$1
	GOODSUM=$2
	SUM=`md5sum $FILE|cut -d' ' -f1`
	[ "$SUM" = "$GOODSUM" ] || die "$FILE's md5 checksum is unknown"
}

## verify_md5sum_alternatives(file,sums,warn)
##      calculates the md5sum of file and compares it to sums (comma-separated).
##      if the sum doesn't match any of them, complains on stderr and causes
##      the program to exit.
##	If warn is supplied, warn the user if the first sum doesn't match.
verify_md5sum_alternatives() {
	FILE=$1
	GOODSUMS=$2
	SUM=`md5sum $FILE|cut -d' ' -f1`
	case ",$GOODSUMS," in
		(,$SUM,*)
			;;
		(*,$SUM,*)
			if [ $# -gt 2 ]; then
				warn "$3"
			fi
			;;
		(*)
			die "$FILE's md5 checksum $SUM is unknown"
			;;
	esac
}

## verify_directory(dir)
##      ensures dir is a directory, or complains on stderr
##      and causes the program to exit.
verify_directory() {
	DIR=$1
	[ -d "$DIR" ] || die "$DIR is not a directory" 
}

## verify_file(file)
##      ensures file is a file, or complains on stderr
##      and causes the program to exit.
verify_file() {
	FILE=$1
	[ -f "$FILE" ] || die "$FILE is not a file"
	[ -r "$FILE" ] || die "$FILE is not readable"
}

## slipstream(deb,relpath,file1,file2...)
##      insert file1,file2... into the deb, under the
##      path 'relpath', relative to the package root, e.g.:
##          slipstream(deb, 'usr/share/doc', 'README', 'copyright')
##              => /usr/share/doc/README
##              => /usr/share/doc/copyright
##      prerequisites:
##        * $WORKDIR must be defined to a directory within which
##          slipstream can do it's work (somewhere writeable)
# TODO: this assumes every file is going to go in the same RELPATH. hmm.
slipstream() {
	DEB="$1"     # the .deb file we are going to mangle
	RELPATH="$2" # relative path in the unpacked .deb
	shift 2

	OLDWD=`pwd`
	cd "$WORKDIR"

	slipstream_permcheck "$DEB"
	slipstream_unpack "$DEB"

	if [ ! -d "$RELPATH" ]; then
		mkdir -p "./slipstream.unpacked/$RELPATH"
	fi

	while [ "$#" -gt 0 ]; do
		file="$1"
		destpath="${RELPATH}/${file##*/}"
		slipstream_file "$file" "$destpath"
		shift
	done

	slipstream_instsize
	slipstream_repack "$DEB"
	slipstream_cleanup

	cd "$OLDWD"
}

## slipstream_dir(deb,relpath,directory)
## insert a directory with game data instead of single files 
slipstream_dir() { 
        DEB="$1"     # the .deb file we are going to mangle 
        RELPATH="$2" # relative path in the unpacked .deb 
        DIR="$3"     # directory with game data 
         
        find $DIR -type f | (
 
        OLDWD=`pwd` 
        cd "$WORKDIR" 
 
        slipstream_permcheck "$DEB" 
        slipstream_unpack "$DEB" 
        while read f; do
                destpath="$RELPATH$f" 
                targetdir=`dirname "$destpath"` 
                mkdir -p "./slipstream.unpacked/$targetdir" 
                cp -p "$f" "./slipstream.unpacked/$targetdir" 
                chmod 644 "./slipstream.unpacked/$destpath" 
 
                # add a line to md5sums 
                cd slipstream.unpacked 
                md5sum "$destpath" >> "../DEBIAN/md5sums" 
                cd .. 
        done
 
        slipstream_instsize 
        slipstream_repack "$DEB" 
        slipstream_cleanup 
 
        cd "$OLDWD" 
        )
}


## slipstream_permcheck(deb)
##      ensures that the file deb can be written to and
##      that the current working directory is writeable
slipstream_permcheck() {
	DEB="$1"

	# ensure we can write to $DEB
	if [ ! -w "$DEB" ]; then
		ls -l "$DEB"
		die "wrong permissions on $DEB (I can't write to it)"
	fi

	# ensure we can write to the workdir
	if [ ! -w . ]; then
		die "cannot write to $PWD"
	fi
}

## slipstream_unpack(deb)
##      unpacks the deb file into "./slipstream_unpacked"
##      and the control data into "./DEBIAN"
slipstream_unpack() {
	DEB="$1"
	dpkg-deb -e "$DEB" "./DEBIAN"
	dpkg-deb -x "$DEB" "./slipstream.unpacked"
}

## slipstream_file(file,destpath)
##      copies the file into "./slipstream_unpacked/$destpath",
##      calculates the files md5sum and adds it to the md5sums
##      file in the control area.
slipstream_file() {
	file="$1"
	destpath="$2"

	cp -p "$file" "./slipstream.unpacked/$destpath"
	chmod 644 "./slipstream.unpacked/$destpath"

	# add a line to md5sums
	cd slipstream.unpacked
	md5sum "$destpath" >> "../DEBIAN/md5sums"
	cd ..
}

##  slipstream_instsize
##      calculates the installed size of the deb, (based on
##      the contents of the ./slipstream_unpacked directory)
##      and writes the result to the control file in the
##      control area.
slipstream_instsize() {
	# figure out the new installed-size
	INSTSIZE=`du -sk ./slipstream.unpacked | cut -f1`
	sed -i  "s/^Installed-Size.*/Installed-Size: $INSTSIZE/" \
		"./DEBIAN/control"
}

## slipstream_repack(deb)
##      writes a new debian package over deb, packing
##      the files from ./slipstream_unpacked inside,
##      using the control area in ./DEBIAN
slipstream_repack() {
	DEB="$1"     # the .deb file we are going to mangle

	# repack
	mv DEBIAN slipstream.unpacked
	# XXX: store output in a temporary file, then cat the file if
	# dpkg-deb fails for some reason. (this is all to hide a non-
	# suppressable "building package foo in ..." message)
	if [ "$COMPRESS" = "no" ]; then
		fakeroot dpkg-deb -Znone -b slipstream.unpacked "$DEB" >/dev/null
	else
		fakeroot dpkg-deb -b slipstream.unpacked "$DEB" >/dev/null
	fi

}

## slipstream_cleanup()
##      removes the ./slipstream_unpacked directory.
slipstream_cleanup() {
	rm -rf ./slipstream.unpacked
}

# stuff relating to installing the generated packages ########################

## install_deb(deb)
##      uses sudo and dpkg to install the supplied .deb file
# TODO: configurable priviledge escalation method (not hardcoded sudo)
# TODO: configurable package installation method (not hardcoded dpkg)
install_deb() {
	DEB="$1"
    echo "using su(1) to obtain root privileges and install the package"
	su -c "dpkg -i \"$DEB\""
}

## unravel(path)
##      convert 'path' from relative to absolute
##      if it does not begin with a slash.
unravel() {
	FILE="$1"
	if echo "$FILE" | grep ^/ >/dev/null; then
		:
	else
		# assume a relative path
		FILE="$PWD/$FILE"
	fi

	echo $FILE
}

if [ -f ./debian/changelog -a -x ./game-data-packager ]; then
    GAME_PACKAGE_VERSION=`dpkg-parsechangelog -ldebian/changelog --format \
        rfc822 | grep Version | cut -d' ' -f2`
else
    GAME_PACKAGE_VERSION=`dpkg-query --showformat='${Version}\n' \
        --show game-data-packager`
fi

## gdp_unzip_into (zipfile, extraction directory, [files...])
##      Unpack files (or everything if not given) from zipfile, with hierarchy
##      preserved.
##
##      e.g. gdp_unzip_into q2-3.20-x86-full-ctf.exe ~/tmp baseq2/pak1.pak
##      will extract the zip file member "baseq2/pak1.pak" to
##      ~/tmp/baseq2/pak1.pak
gdp_unzip_paths () {
    zipfile="$1"
    shift
    target="$1"
    shift
    if which 7za >/dev/null; then
        debug "using 7za"
        7za x -o"$target" -y "$zipfile" "$@" >/dev/null
    elif which 7z >/dev/null; then
        debug "using 7z"
        7z x -o"$target" -y "$zipfile" "$@" >/dev/null
    else
        debug "using unzip"
        unzip -d "$target" -qqo "$zipfile" "$@"
    fi
}

## Deprecated version of gdp_unzip_paths with ambiguous behaviour.
## If the zip file does not contain subdirectories,
## "gdp_unzip x.zip y" is equivalent to "gdp_unzip_paths x.zip . y".
## If the zip file contains a/b/c, it might unpack to either ./a/b/c
## or ./c, depending on the tool used.
gdp_unzip() {
    zipfile="$1"
    shift
    if which 7za >/dev/null; then
        debug "using 7za"
        7za e -y "$zipfile" "$@" >/dev/null
    elif which 7z >/dev/null; then
        debug "using 7z"
        7z e -y "$zipfile" "$@" >/dev/null
    else
        debug "using unzip"
        unzip -qqo "$zipfile" "$@"
    fi
}

## ifind(base,file)
##      case-insensitive search for file in base
ifind() {
  base="$1"
  file="$2"
  [ -d "$base" ] && find "$base" -iwholename "$base/$file"
}

package_installed() {
  dpkg-query -W "$1" >/dev/null 2>&1
}

## require_program(program,package,..)
## make sure 'program' exists and can be executed, if not, instruct
## the user to install package (or packages) and quit gdp.
require_program() {
  program="$1"
  shift

  which "$program" >/dev/null || {
    diestr="'$program' is required for this target.\n"
    if [ $# -gt 1 ]; then
      diestr="${diestr}Please install the following packages:\n"
      while [ $# -gt 0 ]; do
        package_installed "$1" || diestr="${diestr} • $1\n"
        shift
      done
      die "${diestr}and try again."
    fi
    die "${diestr}Please install the package '$1' and try again."
  }
}

package_installed() {
  dpkg-query -W "$1" >/dev/null 2>&1
}

## require_program(program,package,..)
## make sure 'program' exists and can be executed, if not, instruct
## the user to install package (or packages) and quit gdp.
require_program() {
  program="$1"
  shift

  which "$program" >/dev/null || {
    diestr="'$program' is required for this target.\n"
    if [ $# -gt 1 ]; then
      diestr="${diestr}Please install the following packages:\n"
      while [ $# -gt 0 ]; do
        package_installed "$1" || diestr="${diestr} • $1\n"
        shift
      done
      die "${diestr}and try again."
    fi
    die "${diestr}Please install the package '$1' and try again."
  }
}
