#!/usr/bin/env ksh set -o errexit set -o pipefail FNAIFY_VERSION=3.0 ######################################################################### # fnaify # # ====== # # # # Run FNA/XNA games on OpenBSD # # # # Copyright (c) 2017-2020 Thomas Frohwein # # 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 TODOs # - fails to detect XNA in Streets of Fury EX because of whitespace; resolved after renaming .exe # - update the creator for dllmap file; based on fnaify.dllmap.config # - mention package fnaify-extralibs in documentation # - add option to add runtime exe_flags to run mode # - AVNT: find a way to have Steam deactivated for this one in libSteamworksNative.so # - add flag to create log file (for fnaify, as well as the output from the game) # - document '--' to end getopts parsing; can make it clear when to pass remaining args to game # - add check for ffmpeg if needed USAGE=" Usage: $(basename "$0") [-i|-y] [-hsv] [-c configfile] [-D depdir] [-d gamedir] [-f frameworkfile | -F frameworkversion] [-m monopath] [userflags] -c: specify config file for dllmap -D: add directory to native library loader -d: game directory -F: framework version -f: framework file -h: print usage information -i: interactive mode -m: add directories to MONO_PATH -n: skip the library check -s: force (re-)running setup -y: non-interactive mode; automatically replies 'yes' to all recommended choices -V: display version -v: verbose output are optional. These arguments are passed to the game runtime. " depdir="/usr/local/lib:/usr/X11R6/lib" # /usr/X11R6/lib is location of libfreetype.so on OpenBSD fnadir="/usr/local/share/FNA" gamedir="$PWD" interaction=n # y|n|i: yes to all, no to all, interactive debug= licenses= gameconfig= force_setup=0 nolibcheck=0 frameworkfile= setup_frameworkfile= frameworkversion= setup_frameworkversion= frameworkmajor= frameworkminor= userflags= monopath="${MONO_PATH}" netstub_commit=e7d890e0ede0caa9e76ef37af6070344e3ab0abf newline=' ' SAVEIFS=$IFS exefile= nexefile=0 # TODO: is nexefile really needed? my_exe="" exe_flags="" exe_env="" fna_warning=0 nlog_warning=0 set -A needlibarray # array that will hold the names of needed libraries FNAIFY_DEBUG=false 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 "$fnadir" ] ; then monopath="${monopath}:${fnadir}" 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 # 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 libfmodex.so libfmodstudio.so liblua53.so libtiny_jpeg.so steamwrapper.so steam_appid.txt " # array of mono files that need to be removed from the game folder monofilearray=" CSteamworks.dll 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 Steam.dll System.Configuration.dll System.Configuration.Install.dll System.Core.dll System.Data.dll System.Design.dll System.Drawing.dll System.IO.Compression.FileSystem.dll System.IO.Compression.dll System.Management.dll System.Net.dll System.Numerics.dll System.Runtime.Serialization.dll System.Security.dll System.ServiceModel.dll System.Transactions.dll System.Web.Extensions.dll System.Web.Http.dll System.Web.Services.dll System.Web.dll System.Windows.Forms.dll System.Xml.Linq.dll System.Xml.dll System.dll WindowsBase.dll libMonoPosixHelper.so libMonoPosixHelper.so.x86 libMonoPosixHelper.so.x86_64 libSteamworksNative.so monoconfig monomachineconfig mscorlib.dll " configbasearray=" BlitNet.dll CDGEngine.dll CommunityExpress.dll FMOD.dll FNA.dll GameClasses.dll MonoGame.Framework.dll Nuclex.Input.dll OpenAL-CS.dll SDL2#.dll SDL2-CS.dll " debug_echo() { [ "$FNAIFY_DEBUG" = "false" ] && return if [ "${1}" = '-n' ]; then printf '%b' "$2" else printf '%b\n' "$1" fi } printdash() { string= string="$*" if [ -z "$string" ]; then return fi printf '%b\n' "$string" c=1 dashes= while [ $c -lt ${#string} ] do dashes="${dashes}-" # $((...)) for arithmetic substitution c=$((c+1)) done echo "$dashes" } inarray() # check if $1 is in array $2 { firstarg="$1" shift 1 if echo "$*" | grep -q "$firstarg" then echo "true" else echo "false" fi } # function to find the latest present library. Return -1 if none found. # $1 is the basename of the library without the version suffix (e.g. /usr/lib/libc.so) # it will return the filename of the latest library version (e.g. /usr/lib/libc.so.96.0) latest_syslib() { if [ -z "$1" ] ; then return 1 fi find "$(dirname "$1")" -maxdepth 1 -name "$(basename "$1")*" | tail -1 } trunklibnam() # truncate library name { libnam="$1" libnam="$(echo "$libnam" | sed -n -E "s/[\.0-9]*$//p")" echo "$libnam" | sed -E "s/(libSDL2[^-]*)-2\.0(\.so.*)/\1\2/" } validlib() # returns "false" if $1 is in ignoredarray, otherwise "true" { libnam="$(trunklibnam "$1")" if echo \""$ignoredarray"\" | grep -q "$libnam"; then echo "false" elif echo \""$libnam"\" | grep -q '\.dll[ \t]*'; then echo "false" else echo "true" fi } symlink_mg_libs() { set -A mg_subdirs x64 x86 debug_echo "\nChecking for MonoGame x64/x86 dir to create symlinks" if [ ! -e "$gamedir/x64" ] && [ ! -e "$gamedir/x86" ] ; then debug_echo "...Couldn't find directories x64 or x86 in $gamedir\n" else for d in "${mg_subdirs[@]}" ; do debug_echo "\nProcessing library directory $gamedir/$d" for file in $gamedir/$d; do # sort out libs that need to be ignored if [ "$(validlib "$file")" = "true" ] then debug_echo "\tignored file: $file" continue fi debug_echo "\tfound library file: $file. Replacing with symlink..." trunk=$(trunklibnam "$file") rm "$gamedir/$d/$file" debug_echo -n "\t\tchecking for location of $trunk... " if [ -n "$(latest_syslib "/usr/local/lib/$trunk")" ] ; then debug_echo "found in /usr/local/lib!" trunkpath="/usr/local/lib/$trunk" elif [ -n "$(latest_syslib "/usr/X11R6/lib/$trunk")" ] ; then debug_echo "found in /usr/X11R6/lib!" trunkpath="/usr/X11R6/lib/$trunk" elif [ -n "$(latest_syslib "/usr/lib/$trunk")" ] ; then debug_echo "found in /usr/lib!" trunkpath="/usr/lib/$trunk" else debug_echo "NOT FOUND!!" trunkpath= printf "\n\t - ERROR: couldn't find system lib for %s\n" "$file" fi ln -s "$(latest_syslib "$trunkpath")" "$gamedir/$d/$file" done debug_echo "Done with library directory $gamedir/$d" done debug_echo "\nCreating libdl.so.2 symlink\n" [ ! -e "$gamedir/libdl.so.2" ] && \ ln -s "$(latest_syslib /usr/lib/libc.so)" "$gamedir/libdl.so.2" fi } selectexe() { debug_echo "\nexecutable assembly selection routine" IFS=" " for xfile in $(cd "$gamedir" && find . -maxdepth 1 -type f -iname "*.exe" -exec basename {} \; | sort) do if echo "$xfile" | ! grep -q vshost && \ echo "$xfile" | ! grep -iq config && \ echo "$xfile" | ! grep -q dotNetFx4 && \ echo "$xfile" | ! grep -q Updater then exefile="$exefile$xfile:" nexefile=$((nexefile + 1)) fi done IFS=$SAVEIFS if [ $nexefile -gt 1 ]; then if [ "$interaction" = "y" ] ; then # TODO: for now just pick the first .exe. Come up with # better heuristic my_exe="$(echo "$exefile" | cut -f 1 -d ':')" else i=1 while [ $i -le $nexefile ]; do echo "$i: $(echo "$exefile" | cut -f $i -d ':')" i=$((i + 1)) done input_exe=0 while [ ${#input_exe} -gt 2 ] \ || ! echo "$input_exe" | grep -q '[1-9]' \ || [ $input_exe -gt $((i - 1)) ] do echo -n "Enter number of .exe file to run: " read -r input_exe done # NOTE: this only works for up to 9 candidate files my_exe="$(echo "$exefile" | cut -f "$input_exe" -d ':')" fi elif [ $nexefile -eq 1 ]; then my_exe="$exefile" else # TODO: there appear to be games without .exe, like Streets of Rage 4 printf "ERROR: no .exe file found\n\n" exit 1 fi my_exe="$(echo "$my_exe" | sed -E "s/[ \t]$//")" # trim trailing whitespace } check_gameconfig() { if [ -z "$gameconfig" ] ; then if [ -f "$HOME/.config/fnaify/fnaify.dllmap.config" ] ; then gameconfig="$HOME/.config/fnaify/fnaify.dllmap.config" # check for outdated config file if [ "$(grep -m 1 "fnaify version $FNAIFY_VERSION" "$gameconfig")" = "" ] ; then echo "WARNING: $gameconfig appears to be out of date. It is strongly recommended to remove it and re-run $(basename "$0")." fi else if [ -f "$fnadir/FNA.dll.config" ] ; then gameconfig="$fnadir/FNA.dll.config" elif [ -f "/usr/local/share/fnaify/fnaify.dllmap.config" ] ; then gameconfig="/usr/local/share/fnaify/fnaify.dllmap.config" else # in this case create ~/.config/fnaify/fnaify.dllmap.config gameconfig="$HOME/.config/fnaify/fnaify.dllmap.config" debug_echo "creating $gameconfig" mkdir -p ~/.config/fnaify cat < "$gameconfig" EOF fi fi fi } libraryname() { version="$1" if [ ! -e "$gamedir/$version" ]; then debug_echo "Couldn't find library directory $gamedir/$version" else debug_echo "\nEntering library directory $gamedir/$version" for file in "$gamedir"/"$version"/*; do file="$(basename "$file")" # sort out libs that need to be ignored if [ "$(validlib "$file")" = "true" ] then debug_echo "\tignored file: $file" continue fi debug_echo -n " found library file: $file" file=$(trunklibnam "$file") debug_echo -n " -> $file" case "$(inarray "$file" "${needlibarray}")" in "true") debug_echo " - already in array" ;; "false") needlibarray[$((${#needlibarray[*]} + 1))]="$file" debug_echo "" ;; *) printf "\n\tERROR: inarray returned with unexpected error\n\n" exit 1 ;; esac done debug_echo "Done with library directory $gamedir/$version" fi } # 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 printf '\nInstalling FNA %s ...' "${fna_version}\n" licenses="${licenses}FNA:\t\tMs-PL (https://github.com/FNA-XNA/FNA/blob/master/licenses/LICENSE)${newline}" lastdir="$PWD" cd /tmp || exit 1 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 ] \ && [ "$2" = "xna_bridge" ] then printf '\nInstalling FNA.NetStub for XNA ...\n' 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 /p:Configuration=Release FNA/abi/Microsoft.Xna.Framework.sln >> /tmp/fnaify-xbuild.log echo "done." cp /tmp/FNA/abi/bin/Release/* "$gamedir/" else # Download Vorbisfile-CS to be included in the build ftp https://raw.githubusercontent.com/flibitijibibo/Vorbisfile-CS/master/Vorbisfile.cs # add in FNA.Settings.props cat < FNA/FNA.Settings.props EOF echo -n "compiling FNA ${fna_version} with xbuild... " xbuild /p:Configuration=Release FNA/FNA.sln >> /tmp/fnaify-xbuild.log 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/Release/FNA.dll "$gamedir" fi debug_echo "removing the temporary build directories and returning to previous directory" rm -rf /tmp/FNA /tmp/FNA.NetStub /tmp/Vorbisfile.cs cd "$lastdir" || exit 1 } 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 ] \ && [ "$interaction" = "i" ] then printf '\nInstall NLog 4.6.5 from www.nuget.org? [y/n] ' response= until [ "$response" = "y" ] || [ "$response" = "n" ] do read -r response done elif [ "$nlogmajor" -lt 4 ] && [ "$interaction" = "y" ] ; then response="y" else response="n" fi if [ "$response" = "y" ] ; 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 || exit 1 printf '\nInstalling NLog 4.6.5 ...\n' 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" || exit 1 rm -rf /tmp/nlog elif [ "$nlogmajor" -lt 4 ] ; then nlog_warning=1 fi fi } process_options() { while getopts c:Dd:F:f:hil:m:nsVvy o; do case "$o" in c) gameconfig="$OPTARG" ;; D) debug="--debug" ;; d) gamedir=$(readlink -f "$OPTARG") ;; f) frameworkfile="$OPTARG" ;; F) frameworkversion="$OPTARG" ;; h) echo "$USAGE"; exit 0;; i) interaction=i ;; l) depdir="$OPTARG" ;; m) monopath="$OPTARG" ;; n) nolibcheck=1 ;; s) force_setup=1 ;; V) echo "$FNAIFY_VERSION"; exit 0;; v) FNAIFY_DEBUG=true ;; y) interaction=y ;; --) break ;; *) ;; esac done debug_echo "options: $*" debug_echo "OPTIND: $OPTIND" shift $((OPTIND-1)) debug_echo "options after shift: $*\n" userflags=$* } check_mono_binaries() { debug_echo "Checking mono and monodis" if [ -z "$FNAIFY_MONO" ]; then FNAIFY_MONO="$(which mono 2>/dev/null)" fi [ -z "$FNAIFY_MONO" ] && { \ echo "error finding mono" exit 1 } if ! "$FNAIFY_MONO" --version 2>/dev/null >/dev/null 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 if monodis 2>/dev/null then echo "error calling monodis - aborting... Please make sure that monodis is in the PATH!" exit 1 fi } iomap() { debug_echo "iomap: checking if symlinks for filename cases need to be created" for f in $(find "$gamedir" -maxdepth 1 -name "*.exe" -exec basename {} \;); do case $f in "Dead Pixels.exe" ) ln -sf Sprites Content/sprites ln -sf Effects Content/Sprites/effects ln -sf Splash Content/Sprites/splash ln -sf Items Content/Sprites/InGame/items ln -sf Grenades Content/Sprites/InGame/grenades ln -sf Hud Content/Sprites/InGame/hud ln -sf insideBuildings Content/Sprites/InGame/InsideBuildings ln -sf Character Content/Sprites/InGame/character ln -sf City Content/Sprites/InGame/city ln -sf Traders Content/Sprites/InGame/traders ln -sf Zombies Content/Sprites/InGame/zombies ln -sf Objects Content/Sprites/InGame/objects ln -sf Other Content/Sprites/InGame/other ln -sf GunShots Content/Sprites/InGame/gunShots ln -sf Buttons Content/Sprites/buttons ln -sf Menu Content/Sprites/menu ln -sf Cursor Content/Sprites/cursor ln -sf Achievements Content/Sprites/achievements ln -sf Credits Content/Sprites/credits ln -sf Font Content/Sprites/font ln -sf preview Content/Sprites/Preview ln -sf preview Content/Sprites/Menu/Preview ln -sf PsxButtons Content/Sprites/buttons/psxButtons ln -sf Cutscene Content/Sprites/CutScene ln -sf buildings Content/Sprites/InGame/City/Buildings ln -sf mainbackground.xnb Content/ConfigSprites/mainBackground.xnb ln -sf largeCuts.xnb Content/Sprites/effects/largecuts.xnb ln -sf smallCuts.xnb Content/Sprites/effects/smallcuts.xnb ln -sf effects Content/Sprites/Effects ln -sf cuemark.xnb Content/Sprites/effects/cueMark.xnb ln -sf Static.xnb Content/Sprites/Menu/Preview/static.xnb ;; AJ1.exe ) ln -sf j_rip.xnb Content/AJ1/j_Rip.xnb ln -sf j_rip.xnb Content/AJ2/j_Rip.xnb ln -sf Owlturnneo2.xnb Content/AJ1/owlturnneo2.xnb ln -sf Owlturnneo2.xnb Content/AJ2/owlturnneo2.xnb ;; CSTW.exe ) ln -sf paws_Happy.xnb Content/Portrait/Paws/Paws_Happy.xnb ;; ThePit.exe ) ln -sf UI Content/ui ;; "Grand Class Melee.exe" ) ln -sf water.xnb Content/Sounds/Water.xnb ln -sf grass.xnb Content/Sounds/Grass.xnb ln -sf move.xnb Content/Sounds/Move.xnb ln -sf select.xnb Content/Sounds/Select.xnb ln -sf back.xnb Content/Sounds/Back.xnb ln -sf squire_base.xnb Content/Textures/Players/Squire_base.xnb ln -sf squire_greyscale.xnb Content/Textures/Players/Squire_greyscale.xnb ln -sf militia_base.xnb Content/Textures/Players/Militia_base.xnb ln -sf militia_greyscale.xnb Content/Textures/Players/Militia_greyscale.xnb ln -sf apprentice_base.xnb Content/Textures/Players/Apprentice_base.xnb ln -sf apprentice_greyscale.xnb Content/Textures/Players/Apprentice_greyscale.xnb ln -sf savant_base.xnb Content/Textures/Players/Savant_base.xnb ln -sf savant_greyscale.xnb Content/Textures/Players/Savant_greyscale.xnb ln -sf sword.xnb Content/Textures/Weapons/Sword.xnb ln -sf arrow.xnb Content/Sounds/Arrow.xnb ln -sf scorch.xnb Content/Sounds/Scorch.xnb ln -sf bigspeed.xnb Content/Sounds/Bigspeed.xnb ln -sf frame_ingame_left_ruin.xnb Content/Textures/Menu/frame_ingame_left_Ruin.xnb ln -sf frame_ingame_left_mire.xnb Content/Textures/Menu/frame_ingame_left_Mire.xnb ln -sf frame_ingame_right_ruin.xnb Content/Textures/Menu/frame_ingame_right_Ruin.xnb ln -sf frame_ingame_left_wood.xnb Content/Textures/Menu/frame_ingame_left_Wood.xnb ln -sf ruin_leaf.xnb Content/Textures/Terrain/Ruin_leaf.xnb ln -sf frame_ingame_right_wood.xnb Content/Textures/Menu/frame_ingame_right_Wood.xnb ln -sf bigblow1.xnb Content/Sounds/Bigblow1.xnb ln -sf ruin_grassmove.xnb Content/Textures/Terrain/Ruin_grassmove.xnb ln -sf wood_leaf.xnb Content/Textures/Terrain/Wood_leaf.xnb ln -sf ruin_watermove.xnb Content/Textures/Terrain/Ruin_watermove.xnb ln -sf castshort.xnb Content/Sounds/Castshort.xnb ln -sf frame_ingame_right_mire.xnb Content/Textures/Menu/frame_ingame_right_Mire.xnb ln -sf frame_ingame_left_dune.xnb Content/Textures/Menu/frame_ingame_left_Dune.xnb ln -sf sword1.xnb Content/Sounds/Sword1.xnb ln -sf frame_ingame_right_dune.xnb Content/Textures/Menu/frame_ingame_right_Dune.xnb ln -sf staff1.xnb Content/Sounds/Staff1.xnb ;; "LaserCat.exe" ) ln -sf audio Content/Audio ;; "MountYourFriends.exe" ) ln -sf menuBg.xnb Content/images/menubg.xnb ln -sf humanClean.xnb Content/images/humanclean.xnb ln -sf humanCleanNorm.xnb Content/images/humancleannorm.xnb ln -sf menuMarker.xnb Content/images/menumarker.xnb ln -sf stegersaurusLogo.xnb Content/images/backdrops/stegersauruslogo.xnb ln -sf UIComponents.xnb Content/images/uicomponents.xnb ln -sf restrictedArea.xnb Content/images/restrictedarea.xnb ln -sf goatSheet.xnb Content/images/goatsheet.xnb ln -sf BP3_SSTRIP_64.xnb Content/images/bp3_sstrip_64.xnb ln -sf BP3_SSTRIP_32.xnb Content/images/bp3_sstrip_32.xnb ln -sf keySheet.xnb Content/images/keysheet.xnb ;; "One Finger Death Punch.exe" ) ln -sf font2.xnb Content/Font2.xnb ln -sf font5.xnb Content/Font5.xnb ln -sf font6.xnb Content/Font6.xnb ;; "PhoenixForce.exe" ) ln -sf LIfeBar.xnb Content/1.4/Boss/lifeBar.xnb ln -sf firewavexml.xml Content/1.4/Player/fireWavexml.xml ln -sf firewave.xnb Content/1.4/Player/fireWave.xnb ;; "Streets of Fury EX.exe" ) ln -sf ShockWave.xnb Content/Texture2D/Shockwave.xnb ;; "Aces Wild.exe" ) ln -sf HitSparks Content/Sprites/Hitsparks ln -sf preFabs.awx Content/Data/prefabs.awx ;; "CameraObscura.exe" ) ln -sf enemies Content/Enemies ln -sf buttons Content/Buttons ln -sf ui Content/UI ln -sf particle Content/Particle ;; "DLC.exe" ) ln -sf ../../campaigns/dlcquest/texture/awardmentSpriteSheet.xnb Content/base/texture/awardmentSpriteSheet.xnb ln -sf ../../campaigns/dlcquest/texture/dlcSpriteSheet.xnb Content/base/texture/dlcSpriteSheet.xnb ln -sf ../../campaigns/dlcquest/data/map Content/base/data/map ln -sf ../../campaigns/dlcquest/texture/tiles_16x16.xnb Content/base/texture/tiles_16x16.xnb ln -sf ../../campaigns/dlcquest/texture/skyNightSpriteSheet.xnb Content/base/texture/skyNightSpriteSheet.xnb ln -sf ../../campaigns/dlcquest/texture/backgroundSpriteSheet.xnb Content/base/texture/backgroundSpriteSheet.xnb ln -sf ../../campaigns/dlcquest/texture/background2SpriteSheet.xnb Content/base/texture/background2SpriteSheet.xnb ln -sf ../../../campaigns/dlcquest/data/npc/shopkeep.xnb Content/base/data/npc/shopkeep.xnb ln -sf ../../../campaigns/dlcquest/data/npc/shopkeep2.xnb Content/base/data/npc/shopkeep2.xnb ln -sf ../../../campaigns/dlcquest/data/npc/shepherd.xnb Content/base/data/npc/shepherd.xnb ln -sf ../../../campaigns/dlcquest/data/npc/random.xnb Content/base/data/npc/random.xnb ln -sf ../../../campaigns/dlcquest/data/npc/filler.xnb Content/base/data/npc/filler.xnb ln -sf ../../../campaigns/dlcquest/data/npc/blacksmith.xnb Content/base/data/npc/blacksmith.xnb ln -sf ../../../campaigns/dlcquest/data/npc/sidequest.xnb Content/base/data/npc/sidequest.xnb ln -sf ../../../campaigns/dlcquest/data/npc/troll.xnb Content/base/data/npc/troll.xnb ln -sf ../../../campaigns/dlcquest/data/npc/gunsmith.xnb Content/base/data/npc/gunsmith.xnb ln -sf ../../../campaigns/dlcquest/data/npc/princess.xnb Content/base/data/npc/princess.xnb ln -sf ../../../campaigns/dlcquest/data/npc/horse.xnb Content/base/data/npc/horse.xnb ;; "EvilQuest.exe" ) ln -sf Weapons Content/weapons ln -sf DialogWindow.xnb Content/HUD/dialogWindow.xnb ln -sf SpellSprites Content/spellSprites ln -sf PromptMessageWindow.xnb Content/HUD/promptMessageWindow.xnb ln -sf PromptWindow.xnb Content/HUD/promptWindow.xnb ln -sf Menu Content/menu ln -sf smallCursor.xnb Content/Menu/smallcursor.xnb ln -sf ItemIcons.xnb Content/Menu/itemIcons.xnb ln -sf SpellIcons.xnb Content/Menu/spellIcons.xnb ln -sf SplashSymbol.xnb Content/Menu/splashSymbol.xnb ln -sf SplashChaosoftLogo.xnb Content/Menu/splashChaosoftLogo.xnb ln -sf SplashGamesText.xnb Content/Menu/splashGamesText.xnb ln -sf PrisonGalvis_NoShadow.xnb Content/prisonGalvis_NoShadow.xnb ln -sf EnemySprites Content/enemysprites ln -sf Galvis_NoShadow.xnb Content/EnemySprites/galvis_noShadow.xnb ln -sf DemonGalvis.xnb Content/demonGalvis.xnb ln -sf StunnedEffect.xnb Content/SpellSprites/stunnedEffect.xnb ln -sf Colorize.xnb Content/colorize.xnb ln -sf bossDialogMusic.xnb Content/BossDialogMusic.xnb ln -sf Equip.xnb Content/Menu/equip.xnb ln -sf MessageWindow.xnb Content/Menu/messageWindow.xnb ln -sf Shop.xnb Content/shop.xnb ln -sf Title.xnb Content/Menu/title.xnb ln -sf TitleNewGame.xnb Content/Menu/titleNewGame.xnb ln -sf TitleNewGameActive.xnb Content/Menu/titleNewGameActive.xnb ln -sf GalvisTheme.xnb Content/galvisTheme.xnb ln -sf FlamesBG.xnb Content/Intro/Screen1/flamesBG.xnb ln -sf FlamesFG.xnb Content/Intro/Screen1/flamesFG.xnb ln -sf ForegroundMask.xnb Content/Intro/Screen1/foregroundMask.xnb ln -sf VillageBGFire.xnb Content/Intro/Screen3/villageBGFire.xnb ln -sf VillageFG.xnb Content/Intro/Screen3/villageFG.xnb ln -sf VillageFire.xnb Content/Intro/Screen3/villageFire.xnb ln -sf Galvis1.xnb Content/Intro/Screen3/galvis1.xnb ln -sf Galvis2.xnb Content/Intro/Screen3/galvis2.xnb ln -sf Silhouettes.xnb Content/Intro/Screen4/silhouettes.xnb ln -sf FullColor.xnb Content/Intro/Screen4/fullcolor.xnb ln -sf FlamesBG.xnb Content/Intro/Screen4/flamesBG.xnb ln -sf FlamesFG.xnb Content/Intro/Screen4/flamesFG.xnb ln -sf ForegroundMask.xnb Content/Intro/Screen4/foregroundMask.xnb ln -sf ArrestFG1.xnb Content/Intro/Screen5/arrestFG1.xnb ln -sf ArrestFG2.xnb Content/Intro/Screen5/arrestFG2.xnb ln -sf ArrestFG3.xnb Content/Intro/Screen5/arrestFG3.xnb ln -sf GalvisEndingCloseUpBG1.xnb Content/Intro/Screen9/GalvisEndingCloseupBG1.xnb ln -sf ControlsPC.xnb Content/Menu/controlsPC.xnb ln -sf amethyst.xnb Content/Amethyst.xnb ln -sf BattlefieldIntro.xnb Content/BATTLEFIELDINTRO.xnb ln -sf Prison2.xnb Content/PRISON2.xnb ln -sf Prison1.xnb Content/PRISON1.xnb ln -sf Items.xml Data/items.dat ln -sf NPCs.xml Data/NPCS.dat ;; "HELLYEAH.exe" ) ln -sf QuadNoir.xnb Content/QUADNOIR.xnb ln -sf QuadBlanc.xnb Content/QUADBLANC.xnb ln -sf TRANS_Mask.xnb Content/TRANS_MASK.xnb ln -sf Popup Content/GAME/HUD/POPUP ln -sf pop_u_trung.xnb Content/GAME/HUD/Popup/POP_U_TRUNG.xnb ln -sf popup_cartouche_noir.xnb Content/GAME/HUD/Popup/POPUP_CARTOUCHE_NOIR.xnb ln -sf popup_barre_rouges.xnb Content/GAME/HUD/Popup/POPUP_BARRE_ROUGES.xnb ln -sf Menu_\(arial\).xnb Content/TITLE/FONTS/MENU_\(ARIAL\).xnb ln -sf cursor.xnb Content/PCONLY/CURSORS/CURSOR.xnb ln -sf viseur.xnb Content/PCONLY/CURSORS/VISEUR.xnb ln -sf Shaders Content/SHADERS ln -sf WhiteFlash.xnb Content/Shaders/WHITEFLASH.xnb ln -sf VaguePoison.xnb Content/Shaders/VAGUEPOISON.xnb ln -sf Black.xnb Content/Shaders/BLACK.xnb ln -sf White.xnb Content/Shaders/WHITE.xnb ln -sf BloomExtract.xnb Content/Shaders/BLOOMEXTRACT.xnb ln -sf BloomCombine.xnb Content/Shaders/BLOOMCOMBINE.xnb ln -sf GaussianBlur.xnb Content/Shaders/GAUSSIANBLUR.xnb ln -sf Sobel.xnb Content/Shaders/SOBEL.xnb ln -sf RadialBlur.xnb Content/Shaders/RADIALBLUR.xnb ln -sf VagueFeu.xnb Content/Shaders/VAGUEFEU.xnb ln -sf ColorEffects.xnb Content/Shaders/COLOREFFECTS.xnb ln -sf Explosion.xnb Content/Shaders/EXPLOSION.xnb ln -sf Ripple.xnb Content/Shaders/RIPPLE.xnb ln -sf RedFilter.xnb Content/Shaders/REDFILTER.xnb ln -sf Distortion.xnb Content/Shaders/DISTORTION.xnb ln -sf Lightning.xnb Content/Shaders/LIGHTNING.xnb ln -sf LoadingWheel.xnb Content/GAME/LOADING/LOADINGWHEEL.xnb ln -sf LOGO_Sega.xnb Content/LOGO/LOGO_SEGA.xnb ln -sf LOGO_Arkedo.xnb Content/LOGO/LOGO_ARKEDO.xnb ln -sf Sounds Content/GAME/WORLD/SOUNDS ln -sf HY_Sounds.xnb Content/GAME/WORLD/Sounds/HY_SOUNDS.xnb ln -sf picto_A.xnb Content/GAME/FONTS/PICTO/PICTO_A.xnb ln -sf picto_B.xnb Content/GAME/FONTS/PICTO/PICTO_B.xnb ln -sf picto_BK.xnb Content/GAME/FONTS/PICTO/PICTO_BK.xnb ln -sf picto_LB.xnb Content/GAME/FONTS/PICTO/PICTO_LB.xnb ln -sf picto_LS.xnb Content/GAME/FONTS/PICTO/PICTO_LS.xnb ln -sf picto_LT.xnb Content/GAME/FONTS/PICTO/PICTO_LT.xnb ln -sf picto_PAD.xnb Content/GAME/FONTS/PICTO/PICTO_PAD.xnb ln -sf picto_RB.xnb Content/GAME/FONTS/PICTO/PICTO_RB.xnb ln -sf picto_RS.xnb Content/GAME/FONTS/PICTO/PICTO_RS.xnb ln -sf picto_RT.xnb Content/GAME/FONTS/PICTO/PICTO_RT.xnb ln -sf picto_ST.xnb Content/GAME/FONTS/PICTO/PICTO_ST.xnb ln -sf picto_X.xnb Content/GAME/FONTS/PICTO/PICTO_X.xnb ln -sf picto_Y.xnb Content/GAME/FONTS/PICTO/PICTO_Y.xnb ln -sf picto_RS1.xnb Content/GAME/FONTS/PICTO/PICTO_RS1.xnb ln -sf picto_RS2.xnb Content/GAME/FONTS/PICTO/PICTO_RS2.xnb ln -sf picto_RS3.xnb Content/GAME/FONTS/PICTO/PICTO_RS3.xnb ln -sf picto_RS4.xnb Content/GAME/FONTS/PICTO/PICTO_RS4.xnb ln -sf picto_360.xnb Content/GAME/FONTS/PICTO/PICTO_360.xnb ln -sf saving.xnb Content/LOGO/SAVING.xnb ln -sf branchetonpad.xnb Content/PCONLY/MENUS/BRANCHETONPAD.xnb ln -sf pckey.xnb Content/PCONLY/MENUS/PCKEY.xnb ln -sf mousecenter.xnb Content/PCONLY/MENUS/MOUSECENTER.xnb ln -sf mousedefault.xnb Content/PCONLY/MENUS/MOUSEDEFAULT.xnb ln -sf mousedirections.xnb Content/PCONLY/MENUS/MOUSEDIRECTIONS.xnb ln -sf mouseleftbt.xnb Content/PCONLY/MENUS/MOUSELEFTBT.xnb ln -sf mouserightbt.xnb Content/PCONLY/MENUS/MOUSERIGHTBT.xnb ln -sf mousewheelup.xnb Content/PCONLY/MENUS/MOUSEWHEELUP.xnb ln -sf mousewheeldown.xnb Content/PCONLY/MENUS/MOUSEWHEELDOWN.xnb ln -sf ls.xnb Content/PCONLY/MENUS/LS.xnb ln -sf ls1.xnb Content/PCONLY/MENUS/LS1.xnb ln -sf ls2.xnb Content/PCONLY/MENUS/LS2.xnb ln -sf ls360.xnb Content/PCONLY/MENUS/LS360.xnb ln -sf rs1.xnb Content/PCONLY/MENUS/RS1.xnb ln -sf rs2.xnb Content/PCONLY/MENUS/RS2.xnb ln -sf rs3.xnb Content/PCONLY/MENUS/RS3.xnb ln -sf rs4.xnb Content/PCONLY/MENUS/RS4.xnb ln -sf "INT_Radar_(good_girl).xnb" "Content/GAME/FONTS/INT_RADAR_(GOOD_GIRL).xnb" ln -sf "DLG_Name_(trashand).xnb" "Content/GAME/FONTS/DLG_NAME_(TRASHAND).xnb" ln -sf Particules Content/TITLE/PARTICULES ln -sf boum.xnb Content/TITLE/Particules/BOUM.xnb ln -sf flameches.xnb Content/TITLE/Particules/FLAMECHES.xnb ln -sf fumee_background_flou.xnb Content/TITLE/Particules/FUMEE_BACKGROUND_FLOU.xnb ln -sf fumee_noire.xnb Content/TITLE/Particules/FUMEE_NOIRE.xnb ln -sf gaz_multicolor.xnb Content/TITLE/Particules/GAZ_MULTICOLOR.xnb ln -sf TITLE_Cartouche.xnb Content/TITLE/TITLE_CARTOUCHE.xnb ln -sf TITLE_Subtitle-EN.xnb Content/TITLE/TITLE_SUBTITLE-EN.xnb ln -sf TITLE_Logo.xnb Content/TITLE/TITLE_LOGO.xnb ln -sf TITLE_FlameScroll.xnb Content/TITLE/TITLE_FLAMESCROLL.xnb ln -sf TITLE_LogoMask.xnb Content/TITLE/TITLE_LOGOMASK.xnb ln -sf TITLE_Background.xnb Content/TITLE/TITLE_BACKGROUND.xnb ln -sf TITLE_Select.xnb Content/TITLE/TITLE_SELECT.xnb ;; "HonourRuns.exe" ) ln -sf Sprites Content/sprites ln -sf Textures Content/textures ln -sf Levels Content/levels ;; "POOF.exe" ) ln -sf QuadNoir.xnb Content/QUADNOIR.xnb ln -sf QuadBlanc.xnb Content/QUADBLANC.xnb ln -sf TRANS_Mask.xnb Content/TRANS_MASK.xnb ;; # beware, SSDD and SSDDXXL have the same named SSGame.exe "SSGame.exe" ) # SSDDXXL ln -s HUD_ShopBackground.xnb Content/textures/menus/HUD_Shopbackground.xnb ln -s GLOBAL.xnb Content/levels/global.xnb ln -s HUD_challenge_skull.xnb Content/textures/menus/hud_challenge_skull.xnb ln -s LEVEL1.xnb Content/levels/level1.xnb # SSDD ln -s FRONT.xnb Content/levels/front.xnb ;; "TheFallOfGods2.exe" ) ln -sf Data Content/data ;; "Snails.exe" ) ln -s ScreensData.xnb Content/screens/screensdata.xnb ln -s footerMessage.xnb Content/fonts/footermessage.xnb ln -s MainMenu.xnb Content/screens/mainmenu.xnb ln -s UISnailsMenu.xnb Content/screens/controls/uisnailsmenu.xnb ln -s UIMainMenuBodyPanel.xnb Content/screens/controls/uimainmenubodypanel.xnb esac done IFS=$SAVEIFS } xnasetup() { if [ ! -d "$fnadir" ] ; then if [ "$interaction" = "i" ] ; then printf '\nInstall FNA files from GitHub to for this XNA application? [y/n] ' response= until [ "$response" = "y" ] || [ "$response" = "n" ] do read -r response done elif [ "$interaction" = "y" ] ; then response="y" else response="n" fi if [ "$response" = "y" ]; then install_fna latest xna_bridge else printf '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=" " # TODO: check for errors on return of these commands echo "checking for WMA/WMV files and converting. This may take several minutes..." for afile in $(find "$gamedir" -name "*.wma"); do echo "converting $afile to OGG" 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" \ || echo "...skipping..." done for vfile in $(find . -name "*.wmv"); do echo "converting $vfile to OGV" 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" \ || echo "...skipping..." done IFS=$SAVEIFS echo " done." } get_setup_frameworkfile() { if [ -e "$gamedir/FNA.dll" ] ; then setup_frameworkfile="$gamedir/FNA.dll" elif [ -e "$gamedir/MonoGame.Framework.dll" ] ; then setup_frameworkfile="$gamedir/MonoGame.Framework.dll" fi } get_frameworkversion() { if [ -f "$1" ] ; then monodis --assembly "$1" | grep -F Version | sed -E 's/Version:[[:blank:]]*//' fi } get_frameworkmajor() { echo "$1" | sed -n -E "s/([0-9]+)\.[0-9\.]+/\1/p" } get_frameworkminor() { # strip leading zeros echo "$1" | sed -n -E "s/[0-9]+\.([0-9]+).*/\1/p" | sed "s/^0*//" } check_eagle_island() { { [ -f "$gamedir/EagleIsland.exe" ] && \ [ "$(basename "$gamedir")" != "Linux" ]; } && \ { \ debug_echo "correct gamedir for Eagle Island" gamedir="${gamedir}/Linux" } } run() { check_eagle_island [ -e "$gamedir/.fnaify_ready" ] || { echo "ERROR: setup not completed."; exit 1; } check_mono_binaries cd "$gamedir" || exit 1 selectexe check_gameconfig [ -e "$gameconfig" ] || { echo "ERROR: provided gameconfig file not existent: $gameconfig"; exit 1; } debug_echo "gameconfig: $gameconfig" # Quirks my_exe="$(echo "$my_exe" | tr -d '\:')" case "$my_exe" in Blueberry.exe|Shenzhen.exe|ThePit.exe ) exe_env="${exe_env}MONO_FORCE_COMPAT=1 " ;; Hacknet.exe ) exe_flags="${exe_flags}-disableweb " ;; ScourgeBringer.exe ) exe_flags="${exe_flags}-noSound " ;; MidBoss.exe ) if [ -e fnaify.dllmap.MidBoss.config ] && [ "$(diff -U 0 "$gameconfig" fnaify.dllmap.MidBoss.config | \ wc -l)" -eq 5 ] then debug_echo "fnaify.dllmap.MidBoss.config up to date." else debug_echo "fnaify.dllmap.MidBoss.config is missing or out of date." debug_echo "creating fnaify.dllmap.MidBoss.config from $gameconfig for MidBoss" # copy $gameconfig to fnaify.dllmap.MidBoss.config cp "$gameconfig" fnaify.dllmap.MidBoss.config # edit fnaify.dllmap.MidBoss.config to use libSDL2_image_compact.so sed -Ei 's/libSDL2_image\.so/libSDL2_image_compact.so/g' \ fnaify.dllmap.MidBoss.config # redefine gameconfig to fnaify.dllmap.MidBoss.config gameconfig=fnaify.dllmap.MidBoss.config fi ;; SSGame.exe ) mkdir -p ~/.local/share/SSDD ;; esac for f in $configbasearray; do [ -e "$f" ] && { \ debug_echo "existing symlink target: ${f}.config -> $(readlink "${f}.config")" gameconfigsymlink="${gameconfigsymlink}${f}.config " [ "$gameconfig" = "$(readlink "${f}.config")" ] \ || ln -sf "$gameconfig" "${f}.config" } done gameconfigsymlink="${gameconfigsymlink}${my_exe}.config " [ "$gameconfig" = "$(readlink "${my_exe}.config")" ] || \ ln -sf "$gameconfig" "${my_exe}.config" # format all relevant command variables depdir="$(echo "$depdir" | sed -E 's/:$//')" monopath="$(echo "$monopath" | sed -E 's/:$//' | sed -E 's/^://')" ######################################### # Heuristic for frameworkfile selection # ######################################### # Scenario: user-specified framework file (-f) if [ -n "$frameworkfile" ] ; then debug_echo "using user-supplied frameworkfile: $frameworkfile" # Scenario: MonoGame.Framework.dll elif [ -f "$gamedir/MonoGame.Framework.dll" ] ; then frameworkfile="$gamedir/MonoGame.Framework.dll" # Scenario: user-specified framework version (-F) elif [ -n "$frameworkversion" ] ; then # check fnadir and gamedir for fitting version if [ -f "$fnadir/FNA.dll" ] ; then file_fwv="$(get_frameworkversion "$fnadir/FNA.dll")" if [ "$(get_frameworkmajor "$file_fwv").$(get_frameworkminor "$file_fwv")" = "$(get_frameworkmajor "$frameworkversion").$(get_frameworkminor "$frameworkversion")" ] ; then frameworkfile="$fnadir/FNA.dll" fi fi if [ -z "$frameworkfile" ] && [ -f "$gamedir/FNA.dll" ] ; then file_fwv="$(get_frameworkversion "$gamedir/FNA.dll")" if [ "$(get_frameworkmajor "$file_fwv").$(get_frameworkminor "$file_fwv")" = "$(get_frameworkmajor "$frameworkversion").$(get_frameworkminor "$frameworkversion")" ] ; then frameworkfile="$gamedir/FNA.dll" fi fi if [ -z "$frameworkfile" ] ; then install_fna "$frameworkversion" frameworkfile="$gamedir/FNA.dll" fi # Scenario: no frameworkfile in gamedir elif [ ! -f "$gamedir/FNA.dll" ] ; then if [ -e "$gamedir/.fnaify_needfna" ] ; then # this occurs if fnaify uses system framework file if [ -f "$fnadir/FNA.dll" ] ; then frameworkfile="$fnadir/FNA.dll" else echo "ERROR: frameworkfile needed, but not found. Try re-installing?" exit fi else debug_echo "game without framework file" fi # Scenario: FNA.dll in fnadir elif [ -f "$fnadir/FNA.dll" ] ; then # existence of $gamedir/FNA.dll is implied by above elif [ ! -f $gamedir/FNA.dll ] debug_echo "by default, move FNA.dll out of gamedir if present in fnadir" mkdir -p "$gamedir/fnaify-backup" mv "$gamedir/FNA.dll" "$gamedir/fnaify-backup/" debug_echo "touch .fnaify_needfna to make it clear framework file is needed" touch "$gamedir/.fnaify_needfna" frameworkfile="$fnadir/FNA.dll" # Scenario: FNA.dll in gamedir - suboptimal for compatibility since mojoshader updates else # existence of $gamedir/FNA.dll implied by above frameworkfile="$gamedir/FNA.dll" fi if [ -z "$frameworkversion" ] ; then frameworkversion="$(get_frameworkversion "$frameworkfile")" fi datasize=$(( $(ulimit -d) / 1024 )) echo printdash "fnaify runtime configuration" echo "fnaify Version: $FNAIFY_VERSION" echo "Game Directory: $PWD" # PWD as cd'd into $gamedir happened above echo "Mono Binary: $FNAIFY_MONO" echo "Mono Path: $monopath" echo "Main Assembly: $my_exe" echo "Native Library Directories: $depdir" echo "Framework File: $frameworkfile" echo "Framework File Version: $frameworkversion" echo "Symlinks to Config: $gameconfigsymlink" echo "Config File: $gameconfig" echo "Other Runtime Environment: $exe_env" echo "Runtime Flags: $exe_flags $userflags" echo "Datasize Limit: ${datasize} M" echo [ $datasize -lt 2048 ] && { \ echo "AT LEAST 2G are recommended for most games. See ksh(1) for ulimit command documentation" echo } # check if broken symlinks exist in the game directory, e.g. libdl.so.2 IFS=' ' for l in $(find . -type l); do IFS=$SAVEIFS if [ ! -e "$l" ] ; then newl="$(latest_syslib "$(trunklibnam "$(readlink "$l")")")" if [ -z "$newl" ] ; then echo "ERROR - broken symlink: $l. Expect trouble." else ln -fs "$newl" "$l" fi fi done debug_echo "env LD_LIBRARY_PATH=\"$depdir\" MONO_PATH=\"$monopath\" $exe_env \"$FNAIFY_MONO\" $debug \"$my_exe\" $exe_flags $userflags" debug_echo "" env LD_LIBRARY_PATH="$depdir" MONO_PATH="$monopath" $exe_env "$FNAIFY_MONO" $debug "$my_exe" $exe_flags $userflags exit 0 } setup() { echo "Performing setup (mode: $interaction) ..." check_mono_binaries check_eagle_island cd "$gamedir" || exit 1 debug_echo "gamedir: $PWD" # cd'd into direct above debug_echo "Setting up the framework files" get_setup_frameworkfile if [ -z "$setup_frameworkfile" ]; then debug_echo "... no framework file found. Checking other options..." else debug_echo " framework file: $setup_frameworkfile" setup_frameworkversion="$(get_frameworkversion "$setup_frameworkfile")" debug_echo " framework version: $setup_frameworkversion" fi if [ -z "$setup_frameworkfile" ] ; then # games without framework files: # Atom Zombie Smasher, Eliza, Molek-Syntez, Exapunks, Opus Magnum (Lightning.exe), # Shenzhen I/O, SpaceChem (64bit and fullscreen update from 2020-07-15), Streets of Fury EX if [ -f "$gamedir/AtomZombieSmasher.exe" ] ; then if ls $(echo "$depdir" | tr -s ':' ' ') | grep -q "libatomstb\.so" then debug_echo "\tfound libatomstb library in a library directory" else # add libatomstb to needlibarray if [ "$(inarray libatomstb.so "$needlibarray")" = "false" ] then needlibarray[$((${#needlibarray[*]} + 1))]="$libatomstb.so" debug_echo "\tlibatomstb.so added to array" fi fi elif [ -f "$gamedir/Eliza.exe" ] \ || [ -f "$gamedir/MOLEK-SYNTEZ.exe" ] \ || [ -f "$gamedir/EXAPUNKS.exe" ] \ || [ -f "$gamedir/Lightning.exe" ] \ || [ -f "$gamedir/Shenzhen.exe" ] \ || [ -f "$gamedir/SpaceChem.exe" ] \ || [ -f "$gamedir/Hammerwatch.exe" ] \ || [ -f "$gamedir/SOR4.exe" ] \ || [ -f "$gamedir/MobiusFront83.exe" ] \ || [ -f "$gamedir/Proteus.exe" ] then debug_echo "\nCompatible game without XNA/FNA framework file found." debug_echo "Creating \$HOME/Desktop directory which is needed for Zachtronics games" mkdir -p "$HOME/Desktop" else found_xna=0 IFS=' ' for f in $(find . -maxdepth 1 \( -iname "*.exe" -o -iname "*.dll" \) -exec file {} \; \ | grep -F "Mono/.Net assembly"); do f="$(echo "$f" | cut -d : -f 1)" debug_echo "Checking $f for XNA" if ! $(monodis --assemblyref "$f" 2>&1 | grep -Fqim1 "Microsoft.Xna.Framework") then found_xna=1 break fi done IFS=$SAVEIFS if [ $found_xna -gt 0 ]; then debug_echo "Found XNA reference" xnasetup else echo "No FNA, MonoGame or XNA reference found" exit 1 fi fi fi if [ "$setup_frameworkfile" = "FNA.dll" ] && { [ "$frameworkmajor" -lt 16 ] || { [ "$frameworkmajor" -eq 16 ] && [ "$frameworkminor" -lt 5 ];};} then fna_warning=1 fi check_nlog set -A configfilesarray # empty the array nconfigfilesarray=0 # TODO: is this really needed? debug_echo "Identifying config files..." IFS=" " if [ -f "$gamedir"/*.config ]; then for cfile in "$gamedir"/*.config; do cfile="$(basename "$cfile")" debug_echo " found config file: $cfile" configfilesarray[$((${#configfilesarray[*]} + 1))]="$cfile" nconfigfilesarray=$((nconfigfilesarray + 1)) done fi IFS=$SAVEIFS debug_echo "Done identifying config files.\n" if [ $nolibcheck -eq 1 ] ; then debug_echo "skipping library checks" else # identify required libraries in .config files and lib{,64} directories # TODO: 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 [ -f "$gamedir/MidBoss*" ] && \ needlibarray[$((${#needlibarray[*]} + 1))]="libSDL2_image_compact.so" libraryname "lib64" libraryname "lib" libraryname "x64" libraryname "x86" debug_echo "\nChecking for library references in config files..." if [ $nconfigfilesarray -lt 1 ]; then debug_echo "...No config files found." else cfile="" IFS=" " for cfile in "${configfilesarray[@]}"; do IFS=$SAVEIFS debug_echo "\t$cfile" linuxlines=$(grep "os\=\"linux" "$gamedir/$cfile") \ || debug_echo "no os=linux entries in $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 if [ "$(validlib "$libstring")" = "false" ] then debug_echo " - ignored" continue fi libstring=$(trunklibnam "$libstring") debug_echo -n " -> $libstring" # add to libstring to needlibarray if not in there yet case "$(inarray "$libstring" "$needlibarray")" in "true") debug_echo " - already in array" ;; "false") needlibarray[$((${#needlibarray[*]} + 1))]="$libstring" debug_echo " - added to array" ;; *) printf '\n\t - ERROR: inarray returned with unexpected error\n\n' exit 1 ;; esac done done debug_echo "Done with identifying libraries in config files" fi debug_echo "Done with identification of needed libraries." fi debug_echo -n "\nChecking if libpng filename needs fixing..." i=1 while [ "$i" -le "${#needlibarray[@]}" ]; do needlibarray[i]="$(echo "${needlibarray[i]}" | sed -E "s/(libpng)..(\.so.*)/\1\2/")" i=$((i + 1)) done 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. debug_echo "Checking installed libraries... " # 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 debug_echo "done.\n" if [ -n "${missinglibs}" ]; then printf '\nCould not find the following libraries:\n\n%b\n' "${missinglibs}" exit 1 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 mkdir -p "$gamedir/fnaify-backup" fi IFS=" " for config_file in "${configfilesarray[@]}"; do IFS=$SAVEIFS if [ ! -h "$config_file" ] && grep -q "dllmap" "$config_file"; then mv "$config_file" "$gamedir/fnaify-backup/" fi done 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 [ "$interaction" = "i" ] ; then printf '\nWARNING: version of FNA.dll potentially incompatble!\n' 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" ] || [ "$fna_replace" = "n" ]; do read -r fna_replace done elif [ "$interaction" = "y" ] ; then fna_replace="y" else fna_replace="n" fi if [ "$fna_replace" = "y" ]; then install_fna 17.12 fna_warning=0 fi fi symlink_mg_libs iomap if [ -n "${licenses}" ]; then printf 'Installed modules are under the following license(s):\n\n' echo "${licenses}" fi if [ $fna_warning -eq 1 ]; then printf '\nWARNING: version of FNA.dll potentially incompatible!\n' printf "If the game doesn't run, try running 'fnaify -i' (or 'fnaify -y').\n\n" fi if [ $nlog_warning -eq 1 ]; then printf '\nWARNING: version of NLog.dll potentially incompatible!\n' printf "If the game doesn't run, try running 'fnaify -i' (or 'fnaify -y').\n\n" fi touch .fnaify_ready } ### END OF VARIABLES AND FUNCTIONS ### ################### ### MAIN ### ################### process_options "$@" if [ $force_setup -gt 0 ] then setup run else [ ! -e "$gamedir/.fnaify_ready" ] && setup run fi