[BACK]Return to fnaify CVS log [TXT][DIR] Up to [local] / projects / fnaify

File: [local] / projects / fnaify / fnaify (download)

Revision 1.114, Sun Dec 8 15:45:49 2019 UTC (22 months, 2 weeks ago) by thfr
Branch: MAIN
CVS Tags: release-2_1
Changes since 1.113: +1 -5 lines

update description and TODO in fnaify

#!/bin/sh

#########################################################################
# fnaify 2.1								#
# ===============							#
#									#
# Run FNA/XNA games on OpenBSD						#
#									#
# created 2017-12-27 by Thomas Frohwein (thfr)				#
# portability fixes by Mariusz Zaborski (oshogbo)			#
#									#
# FNA is a reimplementation of the Microsoft XNA Game Studio 4.0	#
# Refresh libraries.							#
# Thanks to the great work by Ethan Lee (flibitijibibo) games using FNA	#
# are highly portable and can even run on OpenBSD. Please refer to	#
# https://fna-xna.github.io/ for more information about FNA.		#
#									#
# License: ISC								#
#########################################################################

# TODO:
# - fix FIXMEs
# - check_nlog now only looks at major version number; is there a better way?
#   (it reports only major in 'monodis --assembly': 3.0.0.0, 4.0.0.0)
# - fix whitespace issue seen with 'Battlepaths V1.8.vshost.exe.config', and Super
#   Daryl Deluxe
# - figure out how to do selection between AJ1.exe and AJ2.exe for Apple Jack

USAGE="\nUsage: `basename $0` [-i|-y] [-hv] [-c <configfile>] [-d <depdir>] [-m <monopath>] [<gamedir>]\n\
\n\
-c: specify config file for dllmap (optional)\n\
-d: replace directories for finding dependencies\n\
    NOTE: separate multiple entries with ':' (default:
    /usr/local/share/fnaify-libs:/usr/local/lib:/usr/X11R6/lib)\n\
-h: print usage information\n\
-i: interactive mode\n\
-m: add directories to MONO_PATH\n\
-y: non-interactive mode; automatically replies 'yes' to all recommended choices\n\
-v: verbose output\n\
\n\
<gamedir> is optional and defaults to the current directory if not specified.\n\
"

depdir="/usr/local/lib:/usr/X11R6/lib"	# /usr/X11R6/lib is location of libfreetype.so on OpenBSD
gamedir="$PWD"
interaction=n	# y|n|i: yes to all, no to all, interactive
libatomstb_commit=f8291c591d04ac304c4f5324163bc83287de77b7
licenses=
monoconfig=
monopath="${MONO_PATH}:"
netstub_commit=e7d890e0ede0caa9e76ef37af6070344e3ab0abf
newline='
'
scriptfile=

if [ -d "/usr/local/lib/steamworks-nosteam" ] ; then
	monopath="${monopath}/usr/local/lib/steamworks-nosteam:"
	depdir="/usr/local/lib/steamworks-nosteam:$depdir"
fi
if [ -d "/usr/local/share/steamstubs" ]; then
	monopath="${monopath}/usr/local/share/steamstubs:"
fi
if [ -d "/usr/local/share/fnaify-libs" ]; then
	depdir="/usr/local/share/fnaify-libs:$depdir"
fi

while [ $# -gt 0 ]; do
	case "$1" in
		-c) monoconfig="$2"; shift;;
		-d) depdir="$2"; shift;;
		-h) echo "$USAGE"; exit 0;;
		-i) interaction=i;;
		-m) monopath="$2"; shift;;
		-v) FNAIFY_DEBUG=1;;
		-y) interaction=y;;
		-*) echo "$USAGE"; exit 1;;
		*) gamedir=$(readlink -f "$1");;
	esac
	shift
done
cd "$gamedir"

debug_echo()
{
	if [ -z "$FNAIFY_DEBUG" ]; then
		return
	fi
	if [ "${1}" = '-n' ]; then
		printf "$2"
	else
		printf "$1\n"
	fi
}

if [ -z "$monoconfig" ] ; then
	if [ -f "$HOME/.config/fnaify/fnaify.dllmap.config" ] ; then
		monoconfig="$HOME/.config/fnaify/fnaify.dllmap.config"
		# check for outdated config file
		if [ "x$(cat "$monoconfig" | grep -m 1 "LoadLibrary")" = "x" ] ; then
			echo "WARNING: $monoconfig appears to be out of date. It is strongly recommended to remove it and re-run `basename $0`."
		fi
	else
		if [ -f "/usr/local/share/fnaify/fnaify.dllmap.config" ] ; then
			monoconfig="/usr/local/share/fnaify/fnaify.dllmap.config"
		else	# in this case create ~/.config/fnaify/fnaify.dllmap.config
			monoconfig="$HOME/.config/fnaify/fnaify.dllmap.config"
			debug_echo "creating $monoconfig"
			mkdir -p ~/.config/fnaify
			cat <<EOF > "$monoconfig"
<?xml version="1.0" encoding="utf-8"?>
<!-- mono config file for fnaify dllmaps -->
<configuration>
	<dllmap dll="FAudio" target="libFAudio.so"/>
	<dllmap dll="MojoShader.dll" target="libmojoshader.so"/>
	<dllmap dll="SDL2.dll" target="libSDL2.so"/>
	<!-- MidBoss needs libSDL2_image_compact.so -->
	<dllmap dll="SDL2_image.dll" target="libSDL2_image.so"/>
	<dllmap dll="SDL2_mixer.dll" target="libSDL2_mixer.so"/>
	<dllmap dll="SDL2_ttf.dll" target="libSDL2_ttf.so"/>
	<dllmap dll="SteamworksNative.dll" target="libSteamworksNative.so"/>
	<dllmap dll="freetype6" target="libfreetype.so" />
	<dllmap dll="freetype6.dll" target="libfreetype.so" />
	<dllmap dll="libtheorafile.dll" target="libtheorafile.so"/>
	<dllmap dll="libtheoraplay.dll" target="libtheoraplay.so"/>
	<dllmap dll="libvorbisfile.dll" target="libvorbisfile.so"/>
	<dllmap dll="openal32.dll" target="libopenal.so"/>
	<dllmap dll="soft_oal.dll" target="libopenal.so"/>

	<!-- Atom Zombie Smasher -->
	<dllmap dll="msvcr100.dll" target="libc.so"/>

	<!-- Terraria, Solaroids -->
	<dllmap dll="gdiplus.dll" target="libgdiplus.so"/>

	<!-- MidBoss -->
	<dllmap dll="i:cygwin1.dll" target="libc.so"/>
	<dllmap dll="libc" target="libc.so"/>
	<dllmap dll="intl" target="libc.so"/>
	<dllmap dll="libintl" target="libc.so"/>
	<dllmap dll="i:libxslt.dll" target="libxslt.so"/>
	<dllmap dll="i:odbc32.dll" target="libodbc.so"/>
	<dllmap dll="oci" target="libclntsh.so"/>
	<dllmap dll="db2cli" target="libdb2_36.so"/>
	<dllmap dll="MonoPosixHelper" target="libMonoPosixHelper.so"/>
	<dllmap dll="i:msvcrt" target="libc.so"/>
	<dllmap dll="i:msvcrt.dll" target="libc.so"/>
	<dllmap dll="sqlite" target="libsqlite.so"/>
	<dllmap dll="sqlite3" target="libsqlite3.so"/>
	<dllmap dll="libX11" target="libX11.so"/>
	<dllmap dll="libcairo-2.dll" target="libcairo.so"/>
	<dllmap dll="libcups" target="libcups.so"/>

	<!-- Nuclex.Input (Dead Pixels, Amazing Princess Sarah, Akane the Kunoichi -->
	<dllmap dll="kernel32">
		<dllentry dll="ld.so" name="LoadLibrary" target="dlopen"/>
	</dllmap>
</configuration>
EOF
		fi
	fi
fi

# Variables and Functions

SAVEIFS=$IFS
fna_warning=0
nlog_warning=0

# array of lib names to ignore for the configuration checking
ignoredarray="
FarseerPhysics.Portable.xml
libCommunityExpressSW.so
libCSteamworks.so
libGalaxy64.so
libGalaxyCSharpGlue.so
libGalaxy.so
libParisSteam.so
libSkiaSharp.so
libSteamWrapper.so
libXNAFileDialog.so
libXNAWebRenderer.so
libsteam_api.so
libcef.so
libfmod.so
libfmodstudio.so
libtiny_jpeg.so
steamwrapper.so
steam_appid.txt
"

# array of mono files that need to be removed from the game folder
monofilearray="
I18N.CJK.dll
I18N.MidEast.dll
I18N.Other.dll
I18N.Rare.dll
I18N.West.dll
I18N.dll
Microsoft.CSharp.dll
Mono.CSharp.dll
Mono.Posix.dll
Mono.Security.dll
System.Configuration.dll
System.Core.dll
System.Data.dll
System.Drawing.dll
System.Runtime.Serialization.dll
System.Security.dll
System.ServiceModel.dll
System.Xml.Linq.dll
System.Xml.dll
System.dll
WindowsBase.dll
monoconfig
monomachineconfig
mscorlib.dll
"

needlibarray="" # array that will hold the names of needed libraries

#list of exefiles and their number
exefile=
nexefile=0
my_exe=""	# the .exe selected for the launch script

exe_flags=""
exe_env=""

printdash()
{
	if [ -z "$*" ]; then
		return
	fi
	printf "$*\n"
	c=1
	dashes=
	while [ $c -lt $(echo "$*" | wc -c) ]
	do
		dashes="${dashes}-"
		# $((...)) for arithmetic substitution
		c=$((c+1))
	done
	echo "$dashes"
}

inarray() # check if $1 is in array $2
{
	firstarg="$1"
	shift 1
	echo "$*" | grep -q "$firstarg"
}

validlib() # returns 1 if $1 is in ignoredarray, otherwise 0
{
	libnam="$(trunklibnam $1)"
	if [ -n "$(echo \"$ignoredarray\" | grep $libnam)" ]; then
		return 1
	elif [ -n "$(echo \"$libnam\" | grep '\.dll[ \t]*')" ]; then
		return 1
	else
		return 0
	fi
}

trunklibnam() # truncate library name
{
	libnam="$1"
	libnam="$(echo "$libnam" | sed -n -E "s/[\.0-9]*$//p")"
	libnam="$(echo "$libnam" | sed -E "s/(libSDL2[^-]*)-2\.0(\.so.*)/\1\2/")"
	echo "$libnam"
}

libraryname()
{
        version="$1"
	if [ ! -e "$gamedir/$version" ]; then
		debug_echo "\nCouldn't find library directory $gamedir/$version"
	else
		debug_echo "\nEntering library directory $gamedir/$version"
		for file in $(ls "$gamedir/$version"); do
			# sort out libs that need to be ignored
			validlib $file
			if [ $? -eq 1 ]; then
				debug_echo "\tignored file: $file"
				continue
			fi
			debug_echo -n "\tfound library file: $file"
			file=$(trunklibnam "$file")
			debug_echo -n " -> $file"
			inarray $file ${needlibarray}
			if [ $? -eq 0 ]; then
				debug_echo " - already in array"
			elif [ $? -eq 1 ]; then
				needlibarray="$needlibarray$file "
				debug_echo ""
			else
				echo "\n\t - ERROR: inarray returned with unexpected error\n"
				exit 1
			fi
		done
		debug_echo "Done with library directory $gamedir/$version"
	fi
}

selectexe()
{
	# find correct .exe file for the wrapper script
	if [ $nexefile -gt 1 ]; then
		if [ "x$interaction" = "xi" ] ; then
			i=1
			while [ $i -le $nexefile ]; do
				echo "$i: $(echo $exefile | cut -f $i -d ':')"
				i=$((i + 1))
			done
			input_exe=0
			while [ \( $(echo "$input_exe" | wc -c) -gt 2 \) -o \( -z "$(echo "$input_exe" | grep [1-9])" \) -o \( $input_exe -gt $((i - 1)) \) ]; do
				echo -n "Enter number of .exe file to choose for wrapper script: "
				read input_exe
			done
			# NOTE: this only works for up to 9 candidate files
			my_exe="$(echo $exefile | cut -f $input_exe -d ':')"
		else
			# FIXME: for now just pick the first .exe. Come up with
			#        better heuristic (match directory or launch
			#        script name; pick .exe with shortest name ... ?
			my_exe="$(echo $exefile | cut -f 1 -d ':')"
		fi
	elif [ $nexefile -eq 1 ]; then
		my_exe="$exefile"
	else
		echo "ERROR: no .exe file found\n"
		exit 1
	fi
	my_exe="$(echo "$my_exe" | sed -E "s/[ \t]$//")" # trim trailing whitespace
}

# check if Steamworks.NET.dll is present, move it away if stub is in $monopath
check_remove_steamworks()
{
	# FIXME: likely not whitespace-safe
	if [ -e "$gamedir/Steamworks.NET.dll" \
		-a -n "$monopath" -a -n "$(ls $(echo "$monopath" | tr -s ':' ' ') | grep -m 1 "Steamworks\.NET\.dll")" ]
	then
		if $(ls $(echo "$monopath" | tr -s ':' ' ') | grep -q "Steamworks\.NET\.dll"); then
			debug_echo "\nFound Steamworks.NET.dll in gamedir and in monopath, moving it out of gamedir"
			mkdir -p "$gamedir/fnaify-backup"
			mv "$gamedir/Steamworks.NET.dll" "$gamedir/fnaify-backup/"
		fi
	fi
}

create_wrapper()
{
	cat <<EOF > "$fullscriptpath"
#!/bin/sh

# fnaify wrapper script for launching FNA games on BSD

cd "\`dirname "\$0"\`" # Move to the game's location in case it was invoked from elsewhere

LD_LIBRARY_PATH="$(echo "$depdir" | sed -E 's/:$//')" MONO_PATH="$(echo "$monopath" | sed -E 's/:$//' | sed -E 's/^://')" MONO_CONFIG="$monoconfig" $exe_env exec "$FNAIFY_MONO" "$(echo "$my_exe" | tr -d '\:')" $exe_flags \$*
EOF

	chmod +x "$fullscriptpath"
}

finish_fnaify()
{
	#echo ""
	#printdash $(echo "SETUP COMPLETE")
	#echo ""
	if [ -n "${licenses}" ]; then
		echo "Installed modules are under the following license(s):\n"
		echo "${licenses}"
	fi
	echo "To run the game in $gamedir, use\n"
	echo "./${scriptfile}\n"
	if [ $fna_warning -eq 1 ]; then
		echo "\nWARNING: version of FNA.dll potentially incompatible!"
		echo "If the game doesn't run, try running 'fnaify -i' (or 'fnaify -y').\n"
	fi
	if [ $nlog_warning -eq 1 ]; then
		echo "\nWARNING: version of NLog.dll potentially incompatible!"
		echo "If the game doesn't run, try running 'fnaify -i' (or 'fnaify -y').\n"
	fi
	exit 0
}

# install FNA into gamedir; specify version (e.g. 17.12) as $1
# if XNA bridge should be installed, add "xna_bridge" as $2
install_fna()
{
	if [ $# -lt 1 ]; then
		echo "ERROR: install_fna called with insufficient arguments"
		exit 1
	fi
	fna_version=
	if [ "${1}" = 'latest' ]; then
		# get version number of latest release from GitHub API
		fna_version=$(ftp -Vo - https://api.github.com/repos/FNA-XNA/FNA/releases/latest \
			| grep -Eo "\"tag_name\"\:\"[0-9\.]*\"" \
			| cut -d\" -f4)
	else
		fna_version="$1"
	fi
	echo "\nInstalling FNA ..."
	licenses="${licenses}FNA:\t\tMs-PL (https://github.com/FNA-XNA/FNA/blob/master/licenses/LICENSE)${newline}"
	lastdir="$PWD"
	cd /tmp
	ftp -V https://github.com/FNA-XNA/FNA/releases/download/${fna_version}/FNA-$(echo $fna_version | tr -d '.').zip
	debug_echo "extracting FNA ${fna_version}"
	unzip FNA-$(echo $fna_version | tr -d '.').zip > /dev/null
	if [ \( $# -gt 1 \) -a \( x$2 = 'xxna_bridge' \) ]; then
		echo "\nInstalling FNA.NetStub for XNA ..."
		licenses="${licenses}FNA.NetStub:\tMs-PL (https://github.com/FNA-XNA/FNA.NetStub/blob/master/LICENSE)${newline}"
		ftp -V https://github.com/FNA-XNA/FNA.NetStub/archive/$netstub_commit.tar.gz
		tar zxf $netstub_commit.tar.gz
		mv FNA.NetStub-$netstub_commit FNA.NetStub
		echo -n "compiling XNA ABI/bridge files. This may take a moment... "
		xbuild FNA/abi/Microsoft.Xna.Framework.sln >> /tmp/fnaify-xbuild.log
		echo "done."
		cp /tmp/FNA/abi/bin/Debug/* "$gamedir/"
	else
		echo -n "compiling FNA ${fna_version} with xbuild... "
		xbuild FNA/FNA.sln >> /tmp/fnaify-xbuild.log
		debug_echo "done."
		echo "Moving FNA.dll to fnaify-backup subdirectory"
		mkdir -p "$gamedir/fnaify-backup"
		mv "$gamedir/FNA.dll" "$gamedir/fnaify-backup/"
		cp /tmp/FNA/bin/Debug/FNA.dll "$gamedir"
	fi
	debug_echo "removing the temporary build directory for FNA and returning to previous directory"
	rm -rf /tmp/FNA /tmp/FNA.NetStub
	cd "$lastdir"
}

check_nlog()
{
	if [ -f "$gamedir/NLog.dll" ] ; then
		nlogversion=`monodis --assembly "$gamedir/NLog.dll" | grep "Version" | tr -d [:alpha:] | tr -d " " | tr -d \:`
		nlogmajor=`echo "$nlogversion" | sed -n -E "s/\..*//p"`
		nlogminor=`echo "$nlogversion" | sed -n -E "s/[0-9]+\.([0-9]+)\.[0-9]+\.[0-9]+/\1/p"`
		debug_echo -n "\tNLog.dll version $nlogversion, "
		debug_echo -n "major: $nlogmajor, "
		debug_echo "minor: $nlogminor"
		if [ \( $nlogmajor -lt 4 \) -a \( "x$interaction" = "xi" \) ] ; then
			echo -n "\nInstall NLog 4.6.5 from www.nuget.org? [y/n] "
			response=
			until [ \( "x$response" = "xy" \) -o \( "x$response" = "xn" \) ]
			do
				read response
			done
		elif [ \( $nlogmajor -lt 4 \) -a \( "x$interaction" = "xy" \) ] ; then
			response="y"
		else
			response="n"
		fi
		if [ "x$response" = "xy" ] ; then
			debug_echo "Moving old NLog.dll out of the way"
			mkdir -p "$gamedir/fnaify-backup"
			mv "$gamedir/NLog.dll" "$gamedir/fnaify-backup/"
			lastdir="$PWD"
			mkdir /tmp/nlog
			cd /tmp/nlog
			echo "\nInstalling NLog 4.6.5 ..."
			licenses="${licenses}NLog:\tBSD 3-clause (https://github.com/NLog/NLog/blob/master/LICENSE.txt)${newline}"
			ftp -Vo nlog.4.6.5.nupkg https://www.nuget.org/api/v2/package/NLog/4.6.5
			unzip nlog.4.6.5.nupkg > /dev/null
			cp lib/net45/NLog.dll "$gamedir/"
			cd "$lastdir"
			rm -rf /tmp/nlog
		elif [ $nlogmajor -lt 4 ] ; then
			nlog_warning=1
		fi
	fi
}
# check if this is an XNA game, otherwise abort
xnasetup()
{
	# reset exefile and nexefile to pick only XNA files
	exefile=
	nexefile=0
	IFS='
'
	for assembly in $(cd "${gamedir}" && find . -maxdepth 1 -type f | grep -E ".*\.exe$" | cut -f 2 -d "/" | sort); do
		debug_echo "Checking $assembly for XNA"
		monodis --assemblyref "${gamedir}/$assembly" 2>&1 \
			| grep -iqm 1 "Microsoft.Xna.Framework"
		if [ $? -eq 0 ]; then
			exefile="$exefile$assembly:"
			nexefile=$((nexefile + 1))
		fi
	done
	IFS=$SAVEIFS
	if [ $nexefile -lt 1 ]; then
		echo "No FNA or XNA game found. Quitting."
		exit 1
	fi
	if [ -d "/usr/local/share/fnaify-libs/XNA-bridge" ];then
		monopath="${monopath}/usr/local/share/fnaify-libs/XNA-bridge:"
	else
		if [ "x$interaction" = "xi" ] ; then
			echo -n "\nInstall FNA files from GitHub to for this XNA application? [y/n] "
			response=
			until [ \( "x$response" = "xy" \) -o \( "x$response" = "xn" \) ]
			do
				read response
			done
		elif [ "x$interaction" = "xy" ] ; then
			response="y"
		else
			response="n"
		fi

		if [ "$response" = "y" ]; then
			install_fna latest xna_bridge
		else
			echo "Failed to install FNA/XNA files. Try running with -i or -y flag\n"
			exit 1
		fi
	fi
	
	# convert .wma to .ogg and .wmv to .ogv
	# https://gist.github.com/flibitijibibo/c97bc14aab04b1277d8ef5e97fc9aeff
	IFS="
"
	# FIXME: check for errors on return of these commands
	echo -n "checking for WMA/WMV files and converting. This may take several minutes..."
	for afile in $(find "$gamedir" -name "*.wma"); do
		ffmpeg -loglevel fatal -i "$afile" -c:a libvorbis -q:a 10 \
			"$(echo "$afile" | rev | cut -d. -f2-$(($(echo "$afile" \
			| tr -dc '.' | wc -c) + 1)) | rev).ogg"
	done
	for vfile in $(find . -name "*.wmv"); do
		ffmpeg -loglevel fatal -i "$vfile" -c:v libtheora -q:v 10 -c:a libvorbis \
			-q:a 10 "$(echo "$vfile" | rev | cut -d. -f2-$(($(echo "$vfile" \
			| tr -dc '.' | wc -c) + 1)) | rev).ogv"
	done
	IFS=$SAVEIFS
	echo " done."

	selectexe
	check_remove_steamworks
	debug_echo "creating launch script"
	exe_env="${exe_env}MONO_IOMAP=all "
	scriptfile="run_game.sh"
	fullscriptpath="$gamedir/$scriptfile"
	create_wrapper
	finish_fnaify
}

# End of Variables and Functions

debug_echo "Dependency directory string: $depdir"

# Identify mono and FNA/MonoGame framework files first, warn/break if errors

if [ -z "$FNAIFY_MONO" ]; then
	FNAIFY_MONO="mono"
fi

debug_echo "Checking mono, monodis, framework library (FNA/MonoGame)..."
"$FNAIFY_MONO" --version 2>/dev/null >/dev/null
if [ "$?" -gt 0 ]; then
	echo "error calling mono - aborting... Please make sure that mono is in path or set it in FNAIFY_MONO environment variable!"
	exit 1
fi
# check if monodis can be called
monodis 2>/dev/null
if [ "$?" -eq 127 ]; then
	echo "error calling monodis - aborting... Please make sure that monodis is in the PATH!"
	exit 1
fi
# does any of the framework files exist in $gamedir?
if [ -e "$gamedir/FNA.dll" ]; then
	debug_echo "\tFound FNA.dll"
elif [ -e "$gamedir/MonoGame.Framework.dll" ]; then
	debug_echo "\tFound MonoGame.Framework.dll"
else
	# Atom Zombie Smasher is the only compatible game without framework files
	ls "$gamedir" | grep -iqm 1 "atomzombiesmasher"
	if [ $? -eq 0 ]; then
		debug_echo "Atom Zombie Smasher requires libatomstb.so"
		# FIXME: check for "libatomstb.so*" in depdirs
		#        only proceed if not found
		if [ "x$interaction" = "xi" ] ; then
			echo -n "Compile and install libatomstb.so in game directory? [y/n]"
			response=
			until [ \( "x$response" = "xy" \) -o \( "x$response" = "xn" \) ]
			do
				read response
			done
		elif [ "x$interaction" = "xy" ] ; then
			response="y"
		else
			response="n"
		fi

		if [ "x$response" = "xy" ] ; then
			lastdir="$PWD"
			cd /tmp/
			echo "\nInstalling libatomstb.so ..."
			licenses="${licenses}libatomstb:\thttps://github.com/flibitijibibo/AZSNotSFML/blob/master/NotSFML.cs${newline}"
			ftp -V https://github.com/flibitijibibo/AZSNotSFML/archive/$libatomstb_commit.tar.gz
			tar zxf $libatomstb_commit.tar.gz
			mv AZSNotSFML-$libatomstb_commit AZSNotSFML
			echo -n "compiling libatomstb.so. This may take a moment... "
			cc -O2 -pipe -c -fpic -I/usr/include/sys \
				-o AZSNotSFML/atomstb/atomstb.o AZSNotSFML/atomstb/atomstb.c
			cc -shared -o AZSNotSFML/atomstb/libatomstb.so \
				AZSNotSFML/atomstb/atomstb.o
			echo "done."
			cp /tmp/AZSNotSFML/atomstb/libatomstb.so "$gamedir/"
			cd "$lastdir"
			rm -rf /tmp/AZSNotSFML
		fi
	else
		check_nlog
		xnasetup	# check if this is an XNA game and set up in that case
	fi
fi

# The following code assumes that only one of FNA.dll and MonoGame.Framework.dll is present
if [ -e "$gamedir/FNA.dll" ]; then
	fnaversion=`monodis --assembly "$gamedir/FNA.dll" | grep "Version" | tr -d [:alpha:] | tr -d " " | tr -d \:`
	fnamajor=`echo "$fnaversion" | sed -n -E "s/\..*//p"`
	fnaminor=`echo "$fnaversion" | sed -n -E "s/[0-9]+\.([0-9]+)\.[0-9]+\.[0-9]+/\1/p"`
	debug_echo -n "\tFNA.dll version $fnaversion, "
	debug_echo -n "major: $fnamajor, "
	debug_echo "minor: $fnaminor"
	if [ $fnamajor -lt 16 ] || ( [ $fnamajor -eq 16 ] && [ $fnaminor -lt 5 ] )
	then
		fna_warning=1
	fi
elif [ -e "$gamedir/MonoGame.Framework.dll" ]; then
	mgversion=`monodis --assembly "$gamedir/MonoGame.Framework.dll" | grep "Version" | tr -d [:alpha:] | tr -d " " | tr -d \:`
	debug_echo "\tMonoGame.Framework.dll version $mgversion"
fi

check_nlog

debug_echo "\nTrying to identify the launch script file automatically..."
scount=0
scriptfilearray="" # files that are possible launchscripts
sfile=""
IFS="
"
for sfile in $(cd "$gamedir" && find . -maxdepth 1 -type f | grep -Ev "\.[^/]|mono.*config$" | cut -f 2 -d "/" | sort); do
	debug_echo "\tfound candidate for launch script file: $sfile"
	if [ -z "${scriptfilearray}" ]; then
		scriptfilearray="$sfile"
	else
		scriptfilearray="$scriptfilearray $sfile"
	fi
	scount=$((scount + 1))
done
IFS=$SAVEIFS

if [ \( $scount -gt 1 \) -a \( "x$interaction" = "xi" \) ]; then
	i=1
	for fsc in $scriptfilearray; do
		echo "$i: ${fsc}"
		i=$((i + 1))
	done
	input_script=0
	# Parentheses for grouping must be escaped with '\'
	# check for range of numbers, number of characters, and presence of non-digit characters
	while [ \( $(echo "$input_script" | wc -c) -gt 2 \) -o \( -z "$(echo "$input_script" | grep [1-9])" \) -o \( $input_script -gt $((i - 1)) \) ]; do
		echo -n "Enter number of the file to use as the launch script: "
		read input_script
	done
	# http://www.etalabs.net/sh_tricks.html - Working with arrays
	set -- ${scriptfilearray}
	input_script="\$$input_script"
	# hmm, eval... simplest way to pick an array element it seems.
	# http://mywiki.wooledge.org/BashFAQ/048
	# NOTE: this only works for up to 9 candidate files
	scriptfile=$(eval echo "$input_script")
elif [ $scount -eq 1 ]; then
	scriptfile="${scriptfilearray}"
else
	scriptfile="run_game.sh"
fi

debug_echo "Identified the following file as the launch script: ${scriptfile}\n"

# path and file variable definitions
fullscriptpath="$gamedir/$scriptfile"

configfilesarray="" # files in gamedir ending in '.config'
nconfigfilesarray=0
debug_echo "Identifying config files..."
IFS="
"
for cfile in $(ls "$gamedir" | grep "\.config$"); do
	debug_echo "\tfound config file: $cfile"
	configfilesarray="$configfilesarray$cfile "
	nconfigfilesarray=$((nconfigfilesarray + 1))
done
IFS=$SAVEIFS
debug_echo "Done identifying config files.\n"

# identify required libraries in .config files and lib{,64} directories
# FIXME: filenames not whitespace-safe (but should not be used in such files anyway)
printdash $(debug_echo "Identifying libraries required by the game...")

#for MidBoss, add SDL2_image_compact to needlib
MidBoss=0
ls "$gamedir" | grep -iqm 1 "midboss"
if [ $? -eq 0 ]; then
	MidBoss=1
	needlibarray="${needlibarray}libSDL2_image_compact.so "
	# FIXME: add option to compile https://github.com/FNA-XNA/SDL_image_compact ??
fi

libraryname "lib64"
libraryname "lib"

debug_echo "Obtaining library names from the following config files"
if [ $nconfigfilesarray -lt 1 ]; then
	debug_echo "No config files found."
else
	cfile=""
	for cfile in $configfilesarray; do
		debug_echo "\t$cfile"
		linuxlines=$(grep "os\=\"linux" "$gamedir/$cfile")
		for libstring in $(echo "$linuxlines" | sed -n -E "s/.*target=\"([^\"]+).*/\1/p"); do
			# Fix where library name includes directory information
			libstring=$(echo "$libstring" | sed -E 's/^.\///')
			# remove directories at the start of lib name
			libstring=$(echo "$libstring" | sed -E 's/^.*\///')
			debug_echo -n "\t\tFound library string: $libstring"
			# sort out libs that need to be ignored
			validlib $libstring
			if [ $? -eq 1 ]; then
				debug_echo " - ignored"
				continue
			fi
			libstring=$(trunklibnam "$libstring")
			debug_echo -n " -> $libstring"
			# add to libstring to needlibarray if not in there yet
			inarray $libstring $needlibarray
			if [ $? -eq 0 ]; then
				debug_echo " - already in array"
			elif [ $? -eq 1 ]; then
				needlibarray="$needlibarray$libstring "
				debug_echo " - added to array"
			else
				echo "\n\t - ERROR: inarray returned with unexpected error\n"
				exit 1
			fi
		done
	done
	debug_echo "Done with identifying libraries in config files"
fi
debug_echo "Done with identification of needed libraries."

debug_echo -n "Fixing libpng filenames if present..."
needlibarray=$(echo "${needlibarray}" | sed -E "s/(libpng)..(\.so.*)/\1\2/")
debug_echo " done.\n"

# Check if the libraries are available on the system (/usr/local/lib).
# If not, break and inform user which libraries need to be installed.
echo -n "Checking installed libraries... "
debug_echo ""

# missinglibs[*] accumulates missing library names to inform user
missinglibs=""
for needlib in ${needlibarray}; do
	if $(ls $(echo "$depdir" | tr -s ':' ' ') | grep -q "$needlib")
	then
		IFS=$SAVEIFS
		debug_echo "\tfound library for: $needlib"
	else
		IFS=$SAVEIFS
		debug_echo "\tNot found: $needlib"
		missinglibs="$missinglibs$needlib "
	fi
done
echo "done.\n"

if [ -n "${missinglibs}" ]; then
	#printdash $(echo "Result of configuration testing: FAILED")
	echo "\nCould not find the following libraries:\n
	${missinglibs}\n"
	exit 1
#else
	#printdash $(echo "Result of configuration testing: SUCCESS")
fi

# identify all .config files that do dllmap and move out of the way
debug_echo 'moving .config files with dllmap out of the way'
if [ ! -d "$gamedir/fnaify-backup" ] ; then	# FIXME: is this check needed?
	mkdir -p "$gamedir/fnaify-backup"
fi
for config_file in $(ls "$gamedir" | grep "\.config$"); do
	if $(grep -q "dllmap" "$config_file"); then
		mv "$config_file" "$gamedir/fnaify-backup/"
	fi
done

# FIXME: leave this out if not importing libSDL2_image_compact??
# if MidBoss, add a FNA.dll.config to overwrite libSDL2_image_compact
#if [ $MidBoss -eq 1 ]; then
	#debug_echo 'creating FNA.dll.config for MidBoss'
	#cat <<EOF > "$gamedir/FNA.dll.config"
#<?xml version="1.0" encoding="utf-8"?>
#<configuration>
	#<dllmap dll="SDL2_image.dll" os="openbsd,freebsd,netbsd" target="libSDL2_image_compact.so"/>
#</configuration>
#EOF
#fi

debug_echo "Moving some bundled dll files into fnaify-backup subfolder... "
for file in $monofilearray; do
	if [ -e "$gamedir/$file" ]; then
		debug_echo "\tFound bundled mono file: $file"
		mkdir -p "$gamedir/fnaify-backup"
		mv "$gamedir/$file" "$gamedir/fnaify-backup/"
	fi
done
debug_echo " done."

# if necessary, replace FNA.dll
fna_replace=""
lastdir=""
if [ $fna_warning -eq 1 ]; then
	if [ "x$interaction" = "xi" ] ; then
		echo "\nWARNING: version of FNA.dll potentially incompatble!"
		echo "Fetch FNA 17.12 from GitHub, compile, and replace bundled FNA.dll?"
		echo "FNA is distributed under Microsoft Public License. The license"
		echo "can be viewed here:"
		echo "https://github.com/FNA-XNA/FNA/blob/master/licenses/LICENSE"
		echo -n "Proceed with replacing FNA with version 17.12 (recommended)? [y/n] "
		until [ "$fna_replace" = "y" -o "$fna_replace" = "n" ]; do
			read fna_replace
		done
	elif [ "x$interaction" = "xy" ] ; then
		fna_replace="y"
	else
		fna_replace="n"
	fi

	if [ "$fna_replace" = "y" ]; then
		install_fna 17.12
		fna_warning=0
	fi
fi

check_remove_steamworks

IFS="
"
for xfile in $(cd "$gamedir" && find . -maxdepth 1 -type f -iname "*.exe" | cut -f 2 -d "/" | sort); do
	exefile="$exefile$xfile:"
	nexefile=$((nexefile + 1))
done
IFS=$SAVEIFS
selectexe

echo "Replacing launch script with BSD version..."
if [ \( -e "$fullscriptpath" \) -a \( ! -e "$fullscriptpath.linux" \) ]; then
	debug_echo -n "\t(creating copy of original launcher script with suffix '.linux')... "
	cp -p "$fullscriptpath" "$fullscriptpath.linux"
	debug_echo "done."
fi

# Quirks for the runtime invocation of some games

# Hacknet
if $(ls "$gamedir" | grep -iqm 1 hacknet); then
	debug_echo "Setting flag '-disableweb' for HackNet"
	exe_flags="-disableweb"
fi

# WizOrb
if $(ls "$gamedir" | grep -iqm 1 wizorb); then
	debug_echo "Setting 'MONO_IOMAP=all' for WizOrb"
	exe_env="${exe_env}MONO_IOMAP=all "
fi

# Terraria
if $(ls "$gamedir" | grep -iqm 1 terraria); then
	debug_echo "Setting 'MONO_FORCE_COMPAT' for Terraria"
	exe_env="${exe_env}MONO_FORCE_COMPAT=1 "
fi

# Sword of the Stars: The Pit
if $(ls "$gamedir" | grep -qm 1 ThePit); then
	debug_echo "Setting 'MONO_FORCE_COMPAT' for Sword of the Stars: The Pit"
	exe_env="${exe_env}MONO_FORCE_COMPAT=1 "
fi

# Blueberry Garden
if $(ls "$gamedir" | grep -qm 1 Blueberry.exe); then
	debug_echo "Setting 'MONO_FORCE_COMPAT' for Blueberry Garden"
	exe_env="${exe_env}MONO_FORCE_COMPAT=1 "
fi

create_wrapper
debug_echo "Launcher script replaced.\n"
finish_fnaify