[
    {
        "UpdateStageName": "Instance Directory Creation",
        "UpdateSourcePlatform": "Linux",
        "UpdateSource": "CreateDirectory",
        "UpdateSourceArgs": "{{$FullBaseDir}}game",
        "SkipOnFailure": false
    },
    {
        "UpdateStageName": "Proton GE Download",
        "UpdateSourcePlatform": "Linux",
        "UpdateSource": "Executable",
        "UpdateSourceData": "/bin/bash",
        "UpdateSourceArgs": "-c \"cd \\\"{{$FullRootDir}}\\\"; mkdir -p \\\"{{$FullRootDir}}.proton/compatdata\\\" \\\"{{$FullBaseDir}}.steam/steam\\\" \\\"{{$FullBaseDir}}.config/protonfixes\\\" || exit 1; ProtonVersion=\\\"{{SpecificReleaseVersion}}\\\"; ProtonVersion=\\\"${ProtonVersion// /}\\\"; if [[ -x \\\"{{$FullRootDir}}.proton/proton\\\" && -f \\\"{{$FullRootDir}}.proton/version\\\" ]]; then read _ InstalledVersion < \\\"{{$FullRootDir}}.proton/version\\\"; else InstalledVersion=\\\"\\\"; fi; if [[ -z \\\"$ProtonVersion\\\" ]]; then ProtonVersion=$(wget -qO- https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest | jq -r .tag_name); fi; if [[ ! \\\"$ProtonVersion\\\" =~ ^GE-Proton[0-9]+-[0-9]+$ ]]; then echo \\\"Invalid Proton GE release version specified\\\" && exit 1; elif [[ -n \\\"$InstalledVersion\\\" && \\\"$InstalledVersion\\\" == \\\"$ProtonVersion\\\"* ]]; then echo \\\"Proton GE version $ProtonVersion already installed. Skipping\\\" && exit 0; else rm -f GE-Proton.tar.gz >/dev/null 2>&1; suffix=\\\"\\\"; ver=\\\"${ProtonVersion#GE-Proton}\\\"; major=\\\"${ver%%-*}\\\"; minor=\\\"${ver#*-}\\\"; if (( major > 11 || (major == 11 && minor >= 4) )); then suffix=\\\"-x86_64\\\"; fi; wget -qO GE-Proton.tar.gz https://github.com/GloriousEggroll/proton-ge-custom/releases/download/$ProtonVersion/${ProtonVersion}${suffix}.tar.gz || { echo \\\"Proton GE download failed from GitHub. Aborting\\\"; exit 1; }; rm -rf \\\"{{$FullRootDir}}.proton/compatdata/\\\"* >/dev/null 2>&1; tar -xzf GE-Proton.tar.gz -C \\\"{{$FullRootDir}}.proton\\\" --strip-components=1 && rm -f GE-Proton.tar.gz >/dev/null 2>&1 && echo \\\"Proton GE version $ProtonVersion installed\\\" && { STEAM_COMPAT_DATA_PATH=\\\"{{$FullRootDir}}.proton/compatdata\\\" STEAM_COMPAT_CLIENT_INSTALL_PATH=\\\"{{$FullBaseDir}}.steam/steam\\\" HOME=\\\"{{$FullBaseDir}}\\\" WINEDEBUG=-all \\\"{{$FullRootDir}}.proton/proton\\\" runinprefix cmd /c exit >/dev/null 2>&1; exit 0; }; fi\"",
        "SkipOnFailure": false
    },
    {
        "UpdateStageName": "Game Download and Install",
        "UpdateSourcePlatform": "Linux",
        "UpdateSource": "Executable",
        "UpdateSourceData": "/bin/bash",
        "UpdateSourceArgs": "-c \"#!/bin/bash\nset -uo pipefail\n\nKEY=\\\"\\\"; DOWNLOAD_URL=\\\"\\\"; GAME_DIR=\\\"\\\"; WORK_DIR=\\\"\\\"; PROTON=\\\"\\\"; COMPAT_DIR=\\\"\\\"; STEAM_DIR=\\\"\\\"; HOME_DIR=\\\"\\\"\nwhile [[ $# -gt 0 ]]; do\n    case \\\"$1\\\" in\n        --key) KEY=\\\"$2\\\"; shift 2 ;;\n        --url) DOWNLOAD_URL=\\\"$2\\\"; shift 2 ;;\n        --gamedir) GAME_DIR=\\\"${2%/}\\\"; shift 2 ;;\n        --workdir) WORK_DIR=\\\"${2%/}\\\"; shift 2 ;;\n        --proton) PROTON=\\\"$2\\\"; shift 2 ;;\n        --compatdata) COMPAT_DIR=\\\"${2%/}\\\"; shift 2 ;;\n        --steamdir) STEAM_DIR=\\\"${2%/}\\\"; shift 2 ;;\n        --home) HOME_DIR=\\\"${2%/}\\\"; shift 2 ;;\n        *) echo \\\"ERROR: unknown argument '$1'\\\"; exit 1 ;;\n    esac\ndone\n[[ -n \\\"$GAME_DIR\\\" && -n \\\"$WORK_DIR\\\" ]] || { echo \\\"ERROR: --gamedir and --workdir are required\\\"; exit 1; }\n\nPORTAL=\\\"https://eshop.giants-software.com/downloads.php\\\"\nUA=\\\"Mozilla/5.0 (Windows NT 10.0; Win64; x64)\\\"\nREQUIRED_GB=70\n\nINSTALLED_MARKER=\\\"$GAME_DIR/.amp-install-complete\\\"\nINSTALLED_SOURCE=\\\"\\\"\n[[ -f \\\"$INSTALLED_MARKER\\\" ]] && INSTALLED_SOURCE=$(tr -d '[:space:]' < \\\"$INSTALLED_MARKER\\\" 2>/dev/null)\n\ninstalled_version() { [[ -s \\\"$GAME_DIR/VERSION\\\" ]] && tr -d '[:space:]' < \\\"$GAME_DIR/VERSION\\\" || printf 'an unknown version'; }\n\nIMG=\\\"$WORK_DIR/fs25_download.img\\\"\nUNPACK=\\\"$WORK_DIR/fs25_unpack\\\"\n\nREUSE=0\nREUSE_SOURCE=\\\"\\\"\nshopt -s nullglob nocaseglob\nfor cand in \\\"$UNPACK\\\"/Setup.exe \\\"$UNPACK\\\"/*.exe; do [[ -f \\\"$cand\\\" ]] && REUSE=1 && break; done\nshopt -u nullglob nocaseglob\n[[ -f \\\"$UNPACK/.amp-source\\\" ]] && REUSE_SOURCE=$(tr -d '[:space:]' < \\\"$UNPACK/.amp-source\\\" 2>/dev/null)\nif (( REUSE )); then\n    echo \\\"Reusing the installer already unpacked in $UNPACK\\\"\n    rm -f \\\"$IMG\\\"\n    REQUIRED_GB=45\nfi\n\nif (( ! REUSE )) && [[ -z \\\"$DOWNLOAD_URL\\\" ]]; then\n    if [[ -z \\\"$KEY\\\" ]]; then\n        if [[ -n \\\"$INSTALLED_SOURCE\\\" ]]; then\n            echo \\\"Farming Simulator 25 $(installed_version) is installed and no product key is set to check for a newer version. Skipping\\\"\n            exit 0\n        fi\n        echo \\\"ERROR: No product key is set, so the download link cannot be looked up.\\\"\n        echo \\\"       Set the Product Key setting on this instance, then update it again.\\\"\n        exit 1\n    fi\n    echo \\\"Looking up the download link from the GIANTS download portal...\\\"\n    PAGE=$(curl -sS -A \\\"$UA\\\" --data-urlencode \\\"activationKey=$KEY\\\" --data-urlencode \\\"foobar=DOWNLOAD\\\" \\\"$PORTAL\\\") || PAGE=\\\"\\\"\n    if [[ -z \\\"$PAGE\\\" ]]; then\n        if [[ -n \\\"$INSTALLED_SOURCE\\\" ]]; then\n            echo \\\"Could not reach the GIANTS download portal, so Farming Simulator 25 $(installed_version) has been left as it is\\\"\n            exit 0\n        fi\n        echo \\\"ERROR: Could not reach the GIANTS download portal.\\\"\n        exit 1\n    fi\n    mapfile -t LINKS < <(grep -oE 'https://cdn[0-9]*\\.giants-software\\.com/[^\\\"]+' <<<\\\"$PAGE\\\" | sort -u)\n    if [[ ${#LINKS[@]} -eq 0 ]]; then\n        echo \\\"ERROR: The download portal returned no downloads for that product key.\\\"\n        echo \\\"       Check the Product Key setting, or confirm the key at the portal in a browser.\\\"\n        exit 1\n    fi\n    DOWNLOAD_URL=$(printf '%s\\n' \\\"${LINKS[@]}\\\" | grep -E 'FarmingSimulator25[^/]*\\.img$' | head -1)\n    if [[ -z \\\"$DOWNLOAD_URL\\\" ]]; then\n        echo \\\"ERROR: No Farming Simulator 25 download was found for that product key.\\\"\n        exit 1\n    fi\n    echo \\\"Found ${DOWNLOAD_URL##*/}\\\"\nfi\n\nAVAILABLE_SOURCE=\\\"${DOWNLOAD_URL##*/}\\\"\n[[ -z \\\"$AVAILABLE_SOURCE\\\" ]] && AVAILABLE_SOURCE=\\\"$REUSE_SOURCE\\\"\n\nif [[ -n \\\"$INSTALLED_SOURCE\\\" ]]; then\n    if [[ \\\"$INSTALLED_SOURCE\\\" == \\\"$AVAILABLE_SOURCE\\\" ]]; then\n        echo \\\"Farming Simulator 25 $(installed_version) already installed. Skipping\\\"\n        exit 0\n    elif [[ \\\"$INSTALLED_SOURCE\\\" != *.img ]]; then\n        echo \\\"Farming Simulator 25 $(installed_version) is installed but the download it came from was never recorded, so it is being reinstalled once\\\"\n    else\n        echo \\\"Updating Farming Simulator 25: the download has changed from $INSTALLED_SOURCE to $AVAILABLE_SOURCE\\\"\n    fi\nelif [[ -f \\\"$GAME_DIR/dedicatedServer.exe\\\" ]]; then\n    echo \\\"A previous install did not finish; installing over it\\\"\nfi\n\nrm -f \\\"$INSTALLED_MARKER\\\"\n\nmkdir -p \\\"$GAME_DIR\\\" \\\"$WORK_DIR\\\" || exit 1\n\nFREE_GB=$(( $(df -P -k \\\"$GAME_DIR\\\" | awk 'NR==2{print $4}') / 1024 / 1024 ))\nif [[ $FREE_GB -lt $REQUIRED_GB ]]; then\n    echo \\\"ERROR: Only ${FREE_GB}GB is free on the instance drive, and installing Farming Simulator 25 needs about ${REQUIRED_GB}GB.\\\"\n    exit 1\nfi\n\nif (( ! REUSE )); then\nrm -rf \\\"$UNPACK\\\"; rm -f \\\"$IMG\\\"\n\necho \\\"Downloading Farming Simulator 25. This is a very large download and will take a while\\\"\nTOTAL=$(curl -fsSLI -A \\\"$UA\\\" -e \\\"$PORTAL\\\" \\\"$DOWNLOAD_URL\\\" 2>/dev/null \\\n        | tr -d '\\r' | awk 'BEGIN{IGNORECASE=1} /^content-length:/{n=$2} END{print n+0}')\ncurl -fsS -A \\\"$UA\\\" -e \\\"$PORTAL\\\" -o \\\"$IMG\\\" \\\"$DOWNLOAD_URL\\\" &\nCURL_PID=$!\nwhile kill -0 \\\"$CURL_PID\\\" 2>/dev/null; do\n    sleep 10\n    CUR=$(stat -c%s \\\"$IMG\\\" 2>/dev/null || echo 0)\n    (( TOTAL > 0 && CUR > 0 )) && awk -v c=\\\"$CUR\\\" -v t=\\\"$TOTAL\\\" \\\n        'BEGIN { printf \\\"Downloading: %.1f%% (%.2f GB of %.2f GB)\\n\\\", c*100/t, c/1073741824, t/1073741824 }'\ndone\nif ! wait \\\"$CURL_PID\\\"; then\n    echo \\\"ERROR: The download failed.\\\"\n    echo \\\"       Clear the Download Link setting so a fresh link is looked up from your key.\\\"\n    rm -f \\\"$IMG\\\"; exit 1\nfi\necho \\\"Downloaded $(awk -v b=\\\"$(stat -c%s \\\"$IMG\\\")\\\" 'BEGIN{printf \\\"%.2f\\\", b/1073741824}') GB\\\"\n\nu8()  { echo \\\"${BYTES[$1]}\\\"; }\nu32() { echo $(( ${BYTES[$1]} | ${BYTES[$(($1+1))]}<<8 | ${BYTES[$(($1+2))]}<<16 | ${BYTES[$(($1+3))]}<<24 )); }\n\nextract_iso() {\n    local img=\\\"$1\\\" dest=\\\"$2\\\"\n    local magic\n    magic=$(dd if=\\\"$img\\\" bs=1 skip=32769 count=5 2>/dev/null)\n    [[ \\\"$magic\\\" == \\\"CD001\\\" ]] || { echo \\\"ERROR: the download is not an ISO9660 disc image\\\"; return 1; }\n\n    mapfile -t BYTES < <(dd if=\\\"$img\\\" bs=2048 skip=16 count=1 2>/dev/null | od -An -tu1 -v | tr -s ' ' '\\n' | grep -v '^$')\n    local root_lba root_size\n    root_lba=$(u32 158); root_size=$(u32 166)\n    mapfile -t BYTES < <(dd if=\\\"$img\\\" bs=2048 skip=\\\"$root_lba\\\" count=$(( (root_size + 2047) / 2048 )) 2>/dev/null | od -An -tu1 -v | tr -s ' ' '\\n' | grep -v '^$')\n\n    mkdir -p \\\"$dest\\\"\n    local p=0 len lba size flags filen name first i c\n    while (( p < root_size )); do\n        len=$(u8 $p)\n        if (( len == 0 )); then p=$(( (p / 2048 + 1) * 2048 )); continue; fi\n        lba=$(u32 $((p+2))); size=$(u32 $((p+10)))\n        flags=$(u8 $((p+25))); filen=$(u8 $((p+32))); first=${BYTES[$((p+33))]}\n        if (( filen == 1 && first <= 1 )); then p=$(( p + len )); continue; fi\n        name=\\\"\\\"\n        for (( i=0; i<filen; i++ )); do\n            c=${BYTES[$((p+33+i))]}\n            name+=$(printf \\\"\\\\$(printf '%03o' \\\"$c\\\")\\\")\n        done\n        p=$(( p + len ))\n        (( flags & 2 )) && continue\n        name=\\\"${name%%;*}\\\"; name=\\\"${name%.}\\\"\n        [[ -n \\\"$name\\\" ]] || continue\n        dd if=\\\"$img\\\" bs=2048 skip=\\\"$lba\\\" count=$(( (size + 2047) / 2048 )) 2>/dev/null | head -c \\\"$size\\\" > \\\"$dest/$name\\\"\n    done\n}\n\necho \\\"Reading the disc image\\\"\nextract_iso \\\"$IMG\\\" \\\"$UNPACK\\\" || { rm -f \\\"$IMG\\\"; exit 1; }\nprintf '%s' \\\"$AVAILABLE_SOURCE\\\" > \\\"$UNPACK/.amp-source\\\"\nrm -f \\\"$IMG\\\"\nfi\n\nshopt -s nullglob nocaseglob\nfor f in \\\"$UNPACK\\\"/*_*.bin; do\n    base=\\\"${f##*/}\\\"\n    alt=\\\"${base//_/-}\\\"\n    [[ \\\"$alt\\\" == \\\"$base\\\" || -e \\\"$UNPACK/$alt\\\" ]] && continue\n    ln \\\"$f\\\" \\\"$UNPACK/$alt\\\" 2>/dev/null || cp \\\"$f\\\" \\\"$UNPACK/$alt\\\"\ndone\nSETUP=\\\"\\\"\nfor cand in \\\"$UNPACK\\\"/Setup.exe \\\"$UNPACK\\\"/*.exe; do [[ -f \\\"$cand\\\" ]] && SETUP=\\\"$cand\\\" && break; done\nshopt -u nullglob nocaseglob\n[[ -n \\\"$SETUP\\\" ]] || { echo \\\"ERROR: the installer was not found in the disc image\\\"; rm -rf \\\"$UNPACK\\\"; exit 1; }\n\n[[ -n \\\"$PROTON\\\" && -x \\\"$PROTON\\\" ]] || { echo \\\"ERROR: Proton was not found at '$PROTON'\\\"; exit 1; }\n[[ -n \\\"$COMPAT_DIR\\\" ]] || { echo \\\"ERROR: --compatdata is required to run the installer under Proton\\\"; exit 1; }\nmkdir -p \\\"$COMPAT_DIR\\\" \\\"${STEAM_DIR:-$COMPAT_DIR}\\\" || exit 1\nexport STEAM_COMPAT_DATA_PATH=\\\"$COMPAT_DIR\\\"\n[[ -n \\\"$STEAM_DIR\\\" ]] && export STEAM_COMPAT_CLIENT_INSTALL_PATH=\\\"$STEAM_DIR\\\"\n[[ -n \\\"$HOME_DIR\\\" ]] && { mkdir -p \\\"$HOME_DIR\\\"; export HOME=\\\"$HOME_DIR\\\"; }\nexport WINEDEBUG=\\\"${WINEDEBUG:--all}\\\"\n\nwinpath() { printf 'Z:%s' \\\"$(sed 's|/|\\\\|g' <<<\\\"$1\\\")\\\"; }\nLOG=\\\"$WORK_DIR/fs25_install.log\\\"\n\nXVFB_PID=\\\"\\\"\nif command -v Xvfb >/dev/null 2>&1; then\n    for n in $(seq 90 120); do\n        [[ -e \\\"/tmp/.X${n}-lock\\\" || -e \\\"/tmp/.X11-unix/X${n}\\\" ]] && continue\n        Xvfb \\\":$n\\\" -screen 0 1024x768x24 >/dev/null 2>&1 &\n        XVFB_PID=$!\n        export DISPLAY=\\\":$n\\\"\n        sleep 2\n        break\n    done\nfi\nif [[ -z \\\"$XVFB_PID\\\" ]]; then\n    echo \\\"ERROR: Xvfb is not installed, and the installer cannot run without a display.\\\"\n    exit 1\nfi\n\necho \\\"Installing Farming Simulator 25. This takes a long time\\\"\nPROTON_LOG=\\\"$WORK_DIR/fs25_proton.log\\\"\n\\\"$PROTON\\\" runinprefix \\\"$SETUP\\\" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /NOCANCEL /SP- \\\n    \\\"/DIR=$(winpath \\\"$GAME_DIR\\\")\\\" \\\"/LOG=$(winpath \\\"$LOG\\\")\\\" >\\\"$PROTON_LOG\\\" 2>&1\nrc=$?\n[[ -n \\\"$XVFB_PID\\\" ]] && kill \\\"$XVFB_PID\\\" >/dev/null 2>&1\n\nif [[ ! -f \\\"$GAME_DIR/dedicatedServer.exe\\\" ]]; then\n    echo \\\"ERROR: The installer finished (exit $rc) but no dedicated server was installed.\\\"\n    if [[ -f \\\"$LOG\\\" ]]; then\n        echo \\\"--- installer log ---\\\"\n        tail -20 \\\"$LOG\\\" | sed 's/^/    /'\n        echo \\\"---------------------\\\"\n    fi\n    echo \\\"Full Wine output: $PROTON_LOG\\\"\n    echo \\\"The unpacked installer has been left in $UNPACK for a retry.\\\"\n    exit 1\nfi\n\nrm -rf \\\"$UNPACK\\\"\nprintf '%s' \\\"${AVAILABLE_SOURCE:-unknown}\\\" > \\\"$INSTALLED_MARKER\\\"\necho \\\"Farming Simulator 25 $(installed_version) installed from ${AVAILABLE_SOURCE:-an unrecorded download}\\\"\n\" fs-install --key \"{{ActivationKey}}\" --url \"{{DownloadURL}}\" --gamedir \"{{$FullBaseDir}}game\" --workdir \"{{$FullBaseDir}}work\" --proton \"{{$FullRootDir}}.proton/proton\" --compatdata \"{{$FullRootDir}}.proton/compatdata\" --steamdir \"{{$FullBaseDir}}.steam/steam\" --home \"{{$FullBaseDir}}\"",
        "SkipOnFailure": false,
        "ProcessToolOutput": true
    }
]
