#!/bin/bash # Copyright (C) 2022 GenZmeY # mailto: genzmey@gmail.com # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Requirements: git-bash # https://git-scm.com/download/win set -Eeuo pipefail trap cleanup SIGINT SIGTERM ERR EXIT function reg_readkey () # $1: path, $2: key { cygpath -u "$( reg query "$1" //v "$2" | \ grep -F "$2" | \ awk '{ $1=$2=""; print $0 }')" } # Whoami ScriptFullname=$(readlink -e "$0") ScriptName=$(basename "$0") ScriptDir=$(dirname "$ScriptFullname") # Common SteamPath=$(reg_readkey "HKCU\Software\Valve\Steam" "SteamPath") DocumentsPath=$(reg_readkey "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" "Personal") ThirdPartyBin="$ScriptDir/3rd-party-bin" # Usefull KF2 executables / Paths / Configs KFDoc="$DocumentsPath/My Games/KillingFloor2" KFPath="$SteamPath/steamapps/common/killingfloor2" KFWin64="$KFPath/Binaries/Win64" KFEditor="$KFWin64/KFEditor.exe" KFEditorPatcher="$KFWin64/kfeditor_patcher.exe" KFEditorMergePackages="$KFWin64/KFEditor_mergepackages.exe" KFGame="$KFWin64/KFGame.exe" KFWorkshop="$KFPath/Binaries/WorkshopUserTool.exe" KFUnpublish="$KFDoc/KFGame/Unpublished" KFPublish="$KFDoc/KFGame/Published" KFEditorConf="$KFDoc/KFGame/Config/KFEditor.ini" # Source filesystem MutSource="$ScriptDir/.." MutPubContent="$MutSource/PublicationContent" MutConfig="$MutSource/Config" MutLocalization="$MutSource/Localization" MutBuildConfig="$MutSource/build.cfg" MutTestConfig="$MutSource/test.cfg" # Steam workshop upload filesystem KFUnpublishBrewedPC="$KFUnpublish/BrewedPC" KFUnpublishPackages="$KFUnpublishBrewedPC/Packages" KFUnpublishScript="$KFUnpublishBrewedPC/Script" KFUnpublishConfig="$KFUnpublish/Config" KFUnpublishLocalization="$KFUnpublish/Localization" KFPublishBrewedPC="$KFPublish/BrewedPC" KFPublishPackages="$KFPublishBrewedPC/Packages" KFPublishScript="$KFPublishBrewedPC/Script" KFPublishConfig="$KFPublish/Config" KFPublishLocalization="$KFPublish/Localization" # Tmp files MutWsInfo="$KFDoc/wsinfo.txt" KFEditorConfBackup="$KFEditorConf.backup" # Args ArgInitBuild="false" ArgInitTest="false" ArgCompile="false" ArgBrew="false" ArgBrewManual="false" ArgUpload="false" ArgTest="false" ArgVersion="false" ArgHelp="false" ArgDebug="false" ArgQuiet="false" ArgWarnings="false" ArgNoColors="false" function is_true () # $1: Bool arg to check { echo "$1" | grep -Piq '^true$' } function get_latest () # $1: Reponame, $2: filename, $3: output filename { local ApiUrl="https://api.github.com/repos/$1/releases/latest" local LatestTag="" LatestTag=$(curl --silent "$ApiUrl" | grep -Po '"tag_name": "\K.*?(?=")') local DownloadUrl="https://github.com/$1/releases/download/$LatestTag/$2" mkdir -p "$(dirname "$3")/" curl -LJs "$DownloadUrl" -o "$3" } function get_latest_multini () # $1: file to save { msg "download latest multini" get_latest "GenZmeY/multini" "multini-windows-amd64.exe" "$1" } function get_latest_kfeditor_patcher () # $1: file to save { msg "download latest kfeditor-patcher" get_latest "notpeelz/kfeditor-patcher" "kfeditor_patcher.exe" "$1" } function setup_colors () { if [[ -t 2 ]] && ! is_true "$ArgNoColors" && [[ "${TERM-}" != "dumb" ]]; then # shellcheck disable=SC2034 WHT='\e[37m' RED='\e[31m' # shellcheck disable=SC2034 GRN='\e[32m' # shellcheck disable=SC2034 YLW='\e[33m' DEF='\e[0m' BLD='\e[1m' else # shellcheck disable=SC2034 WHT='' RED='' # shellcheck disable=SC2034 GRN='' # shellcheck disable=SC2034 YLW='' DEF='' BLD='' fi } function err () # $1: String { if ! is_true "$ArgQuiet"; then echo -e "${RED}${1-}${DEF}" >&2 fi } function msg () # $1: String { if ! is_true "$ArgQuiet"; then echo -e "${DEF}${1-}" >&1 fi } function die () # $1: String, $2: Exit code { err "${1-}" exit "${2-3}" } function usage () { msg "${BLD}Usage:${DEF} $0 OPTIONS" msg "" msg "Build, pack, test and upload your kf2 packages to the Steam Workshop." msg "" msg "${BLD}Available options:${DEF}" msg " -ib, --init-build generate $(basename "$MutBuildConfig") with build parameters" msg " -it, --init-test generate $(basename "$MutTestConfig") with test parameters" msg " -i, --init the same as \"./$ScriptName --init-build; ./$ScriptName --init-test\"" msg " -c, --compile build package(s)" msg " -b, --brew compress *.upk and place inside *.u" msg " -bm, --brew-manual the same (almost) as above, but with patched kfeditor by @notpeelz" msg " -u, --upload upload package(s) to the Steam Workshop" msg " -t, --test run local single player test with $(basename "$MutTestConfig") parameters" msg " -q, --quiet run without output" msg " -w, --warnings do not close kf2editor automatically (to be able to read warnings)" msg " -nc, --no-colors do not use color output" msg " -d, --debug print every executed command (script debug)" msg " -v, --version show version" msg " -h, --help show this help" msg "" msg "${BLD}Short options can be combined, examples:${DEF}" msg " -cbu compile, brew, upload" msg " -cbmt compile, brew_manual, run_test" msg " -wcb compile and brew without closing kf2editor" msg " etc..." } function version () { msg "${BLD}$ScriptName $(git describe 2> /dev/null)${DEF}" } function cleanup() { trap - SIGINT SIGTERM ERR EXIT msg "cleanup..." restore_kfeditorconf } function backup_kfeditorconf () { msg "backup $KFEditorConf" cp -f "$KFEditorConf" "$KFEditorConfBackup" } function restore_kfeditorconf () { if [[ -f "$KFEditorConfBackup" ]]; then msg "restore $KFEditorConf from backup" mv -f "$KFEditorConfBackup" "$KFEditorConf" fi } function init_build () { local PackageList="" msg "create new build config ($MutBuildConfig)" :> "$MutBuildConfig" while read -r Package do if [[ -z "$PackageList" ]]; then PackageList="$Package" else PackageList="$PackageList $Package" fi done < <(find "$MutSource" -mindepth 2 -maxdepth 2 -type d -ipath '*/Classes' | sed -r 's|.+/([^/]+)/[^/]+|\1|' | sort) cat > "$MutBuildConfig" < /dev/null do ModificationTimeNew="$(stat -c %y "$KFWin64/$2")" if [[ "$ModificationTime" != "$ModificationTimeNew" ]]; then # wait for write while ps -p "$PID" &> /dev/null do ModificationTime="$ModificationTimeNew" sleep 1 ModificationTimeNew="$(stat -c %y "$KFWin64/$2")" if [[ "$ModificationTime" == "$ModificationTimeNew" ]]; then # wait for write finish kill "$PID" rm -f "$KFWin64/$1" # cleanup (auto) return 0 fi done fi sleep 1 done fi rm -f "$KFWin64/$1" # cleanup (manual) } function merge_packages () # $1: Mutator name { msg "merge packages for $1.u" cp -f "$KFUnpublishScript/$1.u" "$KFWin64" while read -r Upk do cp -f "$MutSource/$1/$Upk" "$KFWin64" merge_package "$Upk" "$1.u" done < <(find "$MutSource/$1" -type f -name '*.upk' -printf "%f\n") } function compiled () { for Package in $PackageBuildOrder do if ! test -f "$KFUnpublishScript/$Package.u"; then return 1 fi done } function compile () { local StripSourceArg="" local PID="" msg "compilation" read_build_settings if ! command -v multini &> /dev/null; then get_latest_multini "$ThirdPartyBin/multini.exe" fi backup_kfeditorconf multini --del "$KFEditorConf" 'ModPackages' 'ModPackages' for Package in $PackageBuildOrder do multini --add "$KFEditorConf" 'ModPackages' 'ModPackages' "$Package" done multini --set "$KFEditorConf" 'ModPackages' 'ModPackagesInPath' "$(cygpath -w "$MutSource")" rm -rf "$KFUnpublish" "$KFPublish" mkdir -p "$KFUnpublishPackages" "$KFUnpublishScript" for Package in $PackageBuildOrder do find "$MutSource/$Package" -type f -name '*.upk' -exec cp -f {} "$KFUnpublishPackages" \; done if [[ -d "$MutLocalization" ]]; then mkdir -p "$KFUnpublishLocalization" cp -rf "$MutLocalization"/* "$KFUnpublishLocalization" fi if [[ -d "$MutConfig" ]]; then mkdir -p "$KFUnpublishConfig" cp -rf "$MutConfig"/* "$KFUnpublishConfig" fi if is_true "$StripSource"; then StripSourceArg="-stripsource"; fi if is_true "$ArgWarnings"; then CMD //C "$(cygpath -w "$KFEditor") make $StripSourceArg -useunpublished" if ! compiled; then die "compilation failed, details in Launch.log" fi else CMD //C "$(cygpath -w "$KFEditor") make $StripSourceArg -useunpublished" & PID="$!" while ps -p "$PID" &> /dev/null do if compiled; then kill "$PID"; break; fi sleep 1 done fi find "$KFUnpublish" -type d -empty -delete restore_kfeditorconf } function publish_common () { if [[ -d "$MutLocalization" ]]; then mkdir -p "$KFPublishLocalization" cp -rf "$MutLocalization"/* "$KFPublishLocalization" fi if [[ -d "$MutConfig" ]]; then mkdir -p "$KFPublishConfig" cp -rf "$MutConfig"/* "$KFPublishConfig" fi } function brewed () { for Package in $PackageUpload do if ! test -f "$KFPublishBrewedPC/$Package.u"; then return 1 fi done } function brew_cleanup () { for Package in $PackageBuildOrder do if ! echo "$PackageUpload" | grep -Pq "(^|\s+)$Package(\s+|$)"; then find "$KFPublishBrewedPC" -type f -name "$Package.u" -delete find "$MutSource/$Package" -type f -name '*.upk' -printf "%f\n" | xargs -I{} find "$KFPublishBrewedPC" -type f -name {} -delete fi done rm -f "$KFPublishBrewedPC"/*.tmp } function brew () { local PID="" msg "brew" read_build_settings if ! compiled ; then die "You must compile packages before brewing. Use --compile option for this." 2 fi rm -rf "$KFPublish" mkdir -p "$KFPublishBrewedPC" if is_true "$ArgWarnings"; then CMD //C "cd /D $(cygpath -w "$KFWin64") && $(basename "$KFEditor") brewcontent -platform=PC $PackageUpload -useunpublished" if ! brewed; then brew_cleanup die "brewing failed, details in Launch.log" fi else CMD //C "cd /D $(cygpath -w "$KFWin64") && $(basename "$KFEditor") brewcontent -platform=PC $PackageUpload -useunpublished" & PID="$!" while ps -p "$PID" &> /dev/null do if brewed; then kill "$PID"; break; fi sleep 1 done fi publish_common brew_cleanup find "$KFPublish" -type d -empty -delete } function brew_manual () { msg "manual brew" read_build_settings if ! compiled ; then die "You must compile packages before brewing. Use --compile option for this." 2 fi rm -rf "$KFPublish" mkdir -p "$KFPublishBrewedPC" if ! [[ -x "$KFEditorPatcher" ]]; then get_latest_kfeditor_patcher "$KFEditorPatcher" fi CMD //C "cd /D $(cygpath -w "$KFWin64") && $(basename "$KFEditorPatcher")" for Package in $PackageUpload do merge_packages "$Package" mv "$KFWin64/$Package.u" "$KFPublishBrewedPC" done publish_common find "$KFPublish" -type d -empty -delete } # Uploading without brewing function publish_unpublished () { mkdir -p "$KFPublishBrewedPC" "$KFPublishScript" "$KFPublishPackages" for Package in $PackageUpload do cp -f "$KFUnpublishScript/$Package.u" "$KFPublishScript" find "$MutSource/$Package" -type f -name '*.upk' -exec cp -f {} "$KFPublishPackages" \; done publish_common find "$KFPublish" -type d -empty -delete } function upload () { local PreparedWsDir="" msg "upload to steam workshop" read_build_settings if ! compiled ; then die "You must compile packages before uploading. Use --compile option for this." 2 fi if ! [[ -d "$KFPublish" ]]; then publish_unpublished fi find "$KFPublish" -type d -empty -delete PreparedWsDir=$(mktemp -d -u -p "$KFDoc") cat > "$MutWsInfo" < "$MutTestConfig" <