feat: initial Elsword-GE builder
This commit is contained in:
@@ -0,0 +1,101 @@
|
|||||||
|
name: build-elsword-ge
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 4 * * *"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-release:
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
run: |
|
||||||
|
git init .
|
||||||
|
git config --global --add safe.directory "$PWD"
|
||||||
|
git remote add origin "${{ gitea.server_url }}/${{ gitea.repository }}.git"
|
||||||
|
git -c http.extraHeader="Authorization: token ${{ secrets.GITEA_TOKEN }}" fetch --depth 1 origin "${{ gitea.sha }}"
|
||||||
|
git checkout --detach FETCH_HEAD
|
||||||
|
|
||||||
|
- name: Build Elsword Proton-GE 10-34 tool
|
||||||
|
run: |
|
||||||
|
nix develop -c ./build.sh --no-install
|
||||||
|
|
||||||
|
- name: Create or update Gitea release
|
||||||
|
env:
|
||||||
|
GITEA_SERVER_URL: ${{ gitea.server_url }}
|
||||||
|
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||||
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
GITEA_SHA: ${{ gitea.sha }}
|
||||||
|
run: |
|
||||||
|
nix develop -c bash -lc '
|
||||||
|
set -euo pipefail
|
||||||
|
set -a
|
||||||
|
. dist/package-info.env
|
||||||
|
set +a
|
||||||
|
|
||||||
|
RELEASE_API="${GITEA_SERVER_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases"
|
||||||
|
RELEASE_TAG_URL="$(python3 -c "import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=\"\"))" "$RELEASE_TAG")"
|
||||||
|
ARCHIVE_URL_NAME="$(python3 -c "import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=\"\"))" "$ARCHIVE_FILE")"
|
||||||
|
|
||||||
|
HTTP_CODE="$(curl -s -o /tmp/release.json -w "%{http_code}" \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
"${RELEASE_API}/tags/${RELEASE_TAG_URL}")"
|
||||||
|
|
||||||
|
if [ "$HTTP_CODE" = "200" ]; then
|
||||||
|
RELEASE_ID="$(python3 -c "import json; print(json.load(open(\"/tmp/release.json\"))[\"id\"])" )"
|
||||||
|
elif [ "$HTTP_CODE" = "404" ]; then
|
||||||
|
python3 -c "import json, pathlib, os; pathlib.Path(\"/tmp/release-create.json\").write_text(json.dumps({
|
||||||
|
\"tag_name\": os.environ[\"RELEASE_TAG\"],
|
||||||
|
\"target_commitish\": os.environ[\"GITEA_SHA\"],
|
||||||
|
\"name\": os.environ[\"RELEASE_TAG\"],
|
||||||
|
\"body\": pathlib.Path(\"dist/release-notes.md\").read_text(),
|
||||||
|
\"draft\": False,
|
||||||
|
\"prerelease\": False,
|
||||||
|
}))"
|
||||||
|
curl --fail-with-body \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data-binary @/tmp/release-create.json \
|
||||||
|
"${RELEASE_API}" > /tmp/release.json
|
||||||
|
RELEASE_ID="$(python3 -c "import json; print(json.load(open(\"/tmp/release.json\"))[\"id\"])" )"
|
||||||
|
else
|
||||||
|
cat /tmp/release.json
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 -c "import json, pathlib, os; pathlib.Path(\"/tmp/release-update.json\").write_text(json.dumps({
|
||||||
|
\"tag_name\": os.environ[\"RELEASE_TAG\"],
|
||||||
|
\"target_commitish\": os.environ[\"GITEA_SHA\"],
|
||||||
|
\"name\": os.environ[\"RELEASE_TAG\"],
|
||||||
|
\"body\": pathlib.Path(\"dist/release-notes.md\").read_text(),
|
||||||
|
\"draft\": False,
|
||||||
|
\"prerelease\": False,
|
||||||
|
}))"
|
||||||
|
curl --fail-with-body \
|
||||||
|
-X PATCH \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data-binary @/tmp/release-update.json \
|
||||||
|
"${RELEASE_API}/${RELEASE_ID}" > /tmp/release.json
|
||||||
|
|
||||||
|
curl --fail-with-body \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
"${RELEASE_API}/${RELEASE_ID}/assets" > /tmp/assets.json
|
||||||
|
|
||||||
|
EXISTING_ASSET_ID="$(python3 -c "import json, os; data=json.load(open(\"/tmp/assets.json\")); target=os.environ[\"ARCHIVE_FILE\"]; print(next((str(item[\"id\"]) for item in data if item.get(\"name\") == target), \"\"))")"
|
||||||
|
if [ -n "$EXISTING_ASSET_ID" ]; then
|
||||||
|
curl --fail-with-body \
|
||||||
|
-X DELETE \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
"${RELEASE_API}/${RELEASE_ID}/assets/${EXISTING_ASSET_ID}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl --fail-with-body \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
--data-binary @"dist/${ARCHIVE_FILE}" \
|
||||||
|
"${RELEASE_API}/${RELEASE_ID}/assets?name=${ARCHIVE_URL_NAME}"
|
||||||
|
'
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
.cache/
|
||||||
|
dist/
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
# Elsword Proton-GE Builder
|
||||||
|
|
||||||
|
Dieses Repo baut per Gitea Actions versionierte Proton-GE-Kompatibilitätstools
|
||||||
|
für Elsword mit `dxvk-git` und Steam Linux Runtime 3.0 (`sniper`).
|
||||||
|
|
||||||
|
## Was gebaut wird
|
||||||
|
|
||||||
|
- Tool-Name: `Elsword-GE-(Version)`
|
||||||
|
- Beispiel: `Elsword-GE-11-1`
|
||||||
|
- Runtime: `1628350` (`Steam Linux Runtime 3.0`)
|
||||||
|
- DXVK: standardmäßig `dxvk-git`
|
||||||
|
- Ausgabe: versionierte `.tar.xz`-Archive in `dist/`
|
||||||
|
|
||||||
|
Ältere lokal installierte Versionen wie `Elsword-GE-10-30` bleiben bestehen,
|
||||||
|
weil jede Build ihren eigenen Tool-Namen bekommt.
|
||||||
|
|
||||||
|
## Ablauf
|
||||||
|
|
||||||
|
1. Das Repo nutzt standardmäßig `GE-Proton10-34`.
|
||||||
|
2. DXVK wird aus Upstream gebaut, standardmäßig als `dxvk-git`.
|
||||||
|
3. Die GE-Basis wird auf `sniper` umgestellt.
|
||||||
|
4. Die DXVK-DLLs in Proton-GE werden ersetzt.
|
||||||
|
5. Das Ergebnis wird als eigenes Steam-Compatibility-Tool paketiert.
|
||||||
|
6. Die Gitea Action erstellt oder aktualisiert ein Release und lädt das Archiv hoch.
|
||||||
|
|
||||||
|
## Standardverhalten
|
||||||
|
|
||||||
|
Ohne Parameter baut `build.sh`:
|
||||||
|
|
||||||
|
- `GE-Proton10-34`
|
||||||
|
- mit `dxvk-git`
|
||||||
|
- als Tool `Elsword-GE-(Version)`
|
||||||
|
- mit `sniper`
|
||||||
|
|
||||||
|
## Lokaler Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix develop -c ./build.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Nur Archiv bauen, nicht lokal installieren:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix develop -c ./build.sh --no-install
|
||||||
|
```
|
||||||
|
|
||||||
|
Einen bestimmten GE-Tag bauen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix develop -c ./build.sh --proton-tag GE-Proton11-1
|
||||||
|
```
|
||||||
|
|
||||||
|
Eigenen Tool-Namen setzen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix develop -c ./build.sh --tool-name Elsword-GE-test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Wichtige Optionen
|
||||||
|
|
||||||
|
- `--proton-tag TAG`
|
||||||
|
- `--proton-asset x86_64|aarch64`
|
||||||
|
- `--dxvk-channel git|stable`
|
||||||
|
- `--dxvk-tag TAG`
|
||||||
|
- `--tool-prefix PREFIX`
|
||||||
|
- `--tool-name NAME`
|
||||||
|
- `--runtime-appid APPID`
|
||||||
|
- `--no-install`
|
||||||
|
|
||||||
|
## Gitea Actions
|
||||||
|
|
||||||
|
Der Workflow liegt unter:
|
||||||
|
|
||||||
|
- `.gitea/workflows/build.yml`
|
||||||
|
|
||||||
|
Er läuft bei:
|
||||||
|
|
||||||
|
- manuellem Start per `workflow_dispatch`
|
||||||
|
- automatischem Zeitplan täglich
|
||||||
|
|
||||||
|
Für Release-Uploads braucht der Workflow ein Secret:
|
||||||
|
|
||||||
|
- `GITEA_TOKEN`
|
||||||
|
|
||||||
|
Dieses Token muss Release-Schreibrechte auf das Repo haben.
|
||||||
|
|
||||||
|
## Release-Ausgabe
|
||||||
|
|
||||||
|
Nach einem Build schreibt das Skript nach `dist/`:
|
||||||
|
|
||||||
|
- das fertige `.tar.xz`-Archiv
|
||||||
|
- `package-info.env`
|
||||||
|
- `release-notes.md`
|
||||||
|
|
||||||
|
Die Gitea Action nutzt diese Dateien direkt für das Release.
|
||||||
@@ -0,0 +1,570 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CACHE_DIR="$REPO_ROOT/.cache"
|
||||||
|
DIST_DIR="$REPO_ROOT/dist"
|
||||||
|
WORK_BASE="$CACHE_DIR/work"
|
||||||
|
DXVK_CACHE_DIR="$CACHE_DIR/dxvk"
|
||||||
|
STEAM_COMPAT_DIR="${HOME}/.local/share/Steam/compatibilitytools.d"
|
||||||
|
UPSTREAM_REPO="GloriousEggroll/proton-ge-custom"
|
||||||
|
PROTON_TAG="GE-Proton10-34"
|
||||||
|
PROTON_ASSET="x86_64"
|
||||||
|
DXVK_CHANNEL="git"
|
||||||
|
DXVK_TAG="latest"
|
||||||
|
RUNTIME_APPID="1628350"
|
||||||
|
TOOL_PREFIX="Elsword-GE"
|
||||||
|
TOOL_NAME=""
|
||||||
|
KEEP_WORK=0
|
||||||
|
DXVK_SRC_OVERRIDE=""
|
||||||
|
MINGW_PTHREAD_ROOT="${MINGW_PTHREAD_ROOT:-}"
|
||||||
|
INSTALL_TOOL=1
|
||||||
|
DXVK_DESCRIBE=""
|
||||||
|
DXVK_FULL_SHA=""
|
||||||
|
DXVK_SHORT_SHA=""
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: ./build.sh [options]
|
||||||
|
|
||||||
|
Build a custom Proton-GE compatibility tool for Elsword and replace its bundled
|
||||||
|
DXVK with a chosen DXVK build, defaulting to GE-Proton10-34 plus dxvk-git on
|
||||||
|
Steam Linux Runtime 3.0 (sniper).
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--proton-tag TAG Build a specific Proton-GE release tag, default: GE-Proton10-34
|
||||||
|
--proton-asset x86_64|aarch64 Choose which Proton-GE asset to use
|
||||||
|
--dxvk-channel git|stable Choose DXVK source channel, default: git
|
||||||
|
--dxvk-tag TAG Stable DXVK tag to use when --dxvk-channel stable
|
||||||
|
--tool-prefix PREFIX Tool name prefix, default: Elsword-GE
|
||||||
|
--tool-name NAME Override final tool name completely
|
||||||
|
--runtime-appid APPID Steam Runtime appid, default: 1628350 (sniper)
|
||||||
|
--steam-compat-dir PATH Override compatibilitytools.d path
|
||||||
|
--dxvk-src PATH Use an existing dxvk checkout instead of cache
|
||||||
|
--mingw-pthread-root PATH Path with x32/libpthread.a and x64/libpthread.a
|
||||||
|
--dist-dir PATH Output directory for sendable archives
|
||||||
|
--no-install Build/package only, do not install into Steam
|
||||||
|
--keep-work Keep temporary work directory
|
||||||
|
-h, --help Show this help
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--proton-tag)
|
||||||
|
PROTON_TAG="${2:?missing value for --proton-tag}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--proton-asset)
|
||||||
|
PROTON_ASSET="${2:?missing value for --proton-asset}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--dxvk-channel)
|
||||||
|
DXVK_CHANNEL="${2:?missing value for --dxvk-channel}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--dxvk-tag)
|
||||||
|
DXVK_TAG="${2:?missing value for --dxvk-tag}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--tool-prefix)
|
||||||
|
TOOL_PREFIX="${2:?missing value for --tool-prefix}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--tool-name)
|
||||||
|
TOOL_NAME="${2:?missing value for --tool-name}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--runtime-appid)
|
||||||
|
RUNTIME_APPID="${2:?missing value for --runtime-appid}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--steam-compat-dir)
|
||||||
|
STEAM_COMPAT_DIR="${2:?missing value for --steam-compat-dir}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--dxvk-src)
|
||||||
|
DXVK_SRC_OVERRIDE="${2:?missing value for --dxvk-src}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--mingw-pthread-root)
|
||||||
|
MINGW_PTHREAD_ROOT="${2:?missing value for --mingw-pthread-root}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--dist-dir)
|
||||||
|
DIST_DIR="${2:?missing value for --dist-dir}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--no-install)
|
||||||
|
INSTALL_TOOL=0
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--keep-work)
|
||||||
|
KEEP_WORK=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1" >&2
|
||||||
|
usage >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
case "$PROTON_ASSET" in
|
||||||
|
x86_64|aarch64) ;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported --proton-asset: $PROTON_ASSET" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$DXVK_CHANNEL" in
|
||||||
|
git|stable) ;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported --dxvk-channel: $DXVK_CHANNEL" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
require_cmd() {
|
||||||
|
command -v "$1" >/dev/null 2>&1 || {
|
||||||
|
echo "Missing required command: $1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for cmd in \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
nix \
|
||||||
|
tar \
|
||||||
|
meson \
|
||||||
|
ninja \
|
||||||
|
python3 \
|
||||||
|
realpath \
|
||||||
|
glslangValidator \
|
||||||
|
x86_64-w64-mingw32-gcc \
|
||||||
|
x86_64-w64-mingw32-g++ \
|
||||||
|
i686-w64-mingw32-gcc \
|
||||||
|
i686-w64-mingw32-g++ \
|
||||||
|
wine; do
|
||||||
|
require_cmd "$cmd"
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p "$CACHE_DIR" "$WORK_BASE" "$DIST_DIR"
|
||||||
|
if [[ "$INSTALL_TOOL" -eq 1 ]]; then
|
||||||
|
mkdir -p "$STEAM_COMPAT_DIR"
|
||||||
|
fi
|
||||||
|
WORK_DIR="$(mktemp -d "$WORK_BASE/build.XXXXXX")"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if [[ "$KEEP_WORK" -ne 1 ]]; then
|
||||||
|
rm -rf "$WORK_DIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
say() {
|
||||||
|
printf '\n==> %s\n' "$1" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
tool_version_from_tag() {
|
||||||
|
local tag="$1"
|
||||||
|
local version="${tag#GE-Proton}"
|
||||||
|
if [[ "$version" == "$tag" ]]; then
|
||||||
|
printf '%s\n' "$tag"
|
||||||
|
else
|
||||||
|
printf '%s\n' "$version"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_latest_proton_tag() {
|
||||||
|
curl -fsSL "https://api.github.com/repos/${UPSTREAM_REPO}/releases/latest" \
|
||||||
|
| grep -oP '"tag_name":\s*"\K[^"]+' \
|
||||||
|
| head -n1
|
||||||
|
}
|
||||||
|
|
||||||
|
get_latest_dxvk_tag() {
|
||||||
|
curl -fsSL https://api.github.com/repos/doitsujin/dxvk/releases/latest \
|
||||||
|
| grep -oP '"tag_name":\s*"\K[^"]+' \
|
||||||
|
| head -n1
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare_dxvk_cache() {
|
||||||
|
if [[ ! -d "$DXVK_CACHE_DIR/.git" ]]; then
|
||||||
|
say "Clone dxvk"
|
||||||
|
git clone --recursive https://github.com/doitsujin/dxvk.git "$DXVK_CACHE_DIR" >&2
|
||||||
|
else
|
||||||
|
say "Update dxvk cache"
|
||||||
|
git -C "$DXVK_CACHE_DIR" fetch --tags origin >&2
|
||||||
|
git -C "$DXVK_CACHE_DIR" checkout master >&2
|
||||||
|
git -C "$DXVK_CACHE_DIR" reset --hard origin/master >&2
|
||||||
|
git -C "$DXVK_CACHE_DIR" submodule update --init --recursive >&2
|
||||||
|
git -C "$DXVK_CACHE_DIR" clean -fdx >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare_dxvk_repo() {
|
||||||
|
local source_repo ref_name checkout_dir
|
||||||
|
checkout_dir="$WORK_DIR/dxvk-src"
|
||||||
|
|
||||||
|
if [[ -n "$DXVK_SRC_OVERRIDE" ]]; then
|
||||||
|
source_repo="$DXVK_SRC_OVERRIDE"
|
||||||
|
else
|
||||||
|
prepare_dxvk_cache
|
||||||
|
source_repo="$DXVK_CACHE_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$DXVK_CHANNEL" == "stable" ]]; then
|
||||||
|
ref_name="$DXVK_TAG"
|
||||||
|
if [[ -z "$ref_name" || "$ref_name" == "latest" ]]; then
|
||||||
|
ref_name="$(get_latest_dxvk_tag)"
|
||||||
|
fi
|
||||||
|
if [[ -z "$ref_name" ]]; then
|
||||||
|
echo "Failed to resolve DXVK stable tag" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
ref_name="HEAD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
say "Prepare DXVK source ($DXVK_CHANNEL)"
|
||||||
|
git clone --recursive "$source_repo" "$checkout_dir" >&2
|
||||||
|
|
||||||
|
if [[ "$DXVK_CHANNEL" == "stable" ]]; then
|
||||||
|
git -C "$checkout_dir" fetch --tags https://github.com/doitsujin/dxvk.git >&2
|
||||||
|
git -C "$checkout_dir" checkout "refs/tags/$ref_name" >&2
|
||||||
|
git -C "$checkout_dir" submodule update --init --recursive >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$checkout_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_dxvk() {
|
||||||
|
local dxvk_src="$1"
|
||||||
|
local dxvk_build_dir="$WORK_DIR/dxvk-out/dxvk-${DXVK_CHANNEL}"
|
||||||
|
local toolchain_dir="$WORK_DIR/toolchain"
|
||||||
|
local pthread_dir="$WORK_DIR/mingw-pthread"
|
||||||
|
|
||||||
|
DXVK_SHORT_SHA="$(git -C "$dxvk_src" rev-parse --short HEAD)"
|
||||||
|
DXVK_FULL_SHA="$(git -C "$dxvk_src" rev-parse HEAD)"
|
||||||
|
DXVK_DESCRIBE="$(git -C "$dxvk_src" describe --always --tags --dirty)"
|
||||||
|
|
||||||
|
say "Build DXVK ($DXVK_DESCRIBE)"
|
||||||
|
rm -rf "$WORK_DIR/dxvk-out" "$toolchain_dir" "$pthread_dir"
|
||||||
|
mkdir -p "$dxvk_build_dir" "$toolchain_dir" "$pthread_dir/x32" "$pthread_dir/x64"
|
||||||
|
|
||||||
|
if [[ -n "$MINGW_PTHREAD_ROOT" \
|
||||||
|
&& -f "$MINGW_PTHREAD_ROOT/x32/libpthread.a" \
|
||||||
|
&& -f "$MINGW_PTHREAD_ROOT/x64/libpthread.a" ]]; then
|
||||||
|
ln -sf "$MINGW_PTHREAD_ROOT/x32/libpthread.a" "$pthread_dir/x32/libpthread.a"
|
||||||
|
ln -sf "$MINGW_PTHREAD_ROOT/x64/libpthread.a" "$pthread_dir/x64/libpthread.a"
|
||||||
|
else
|
||||||
|
local mcfg32 mcfg64
|
||||||
|
mcfg32="$(NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM=1 nix path-info --impure nixpkgs#pkgsCross.mingw32.windows.mcfgthreads)"
|
||||||
|
mcfg64="$(NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM=1 nix path-info --impure nixpkgs#pkgsCross.mingwW64.windows.mcfgthreads)"
|
||||||
|
ln -sf "$mcfg32/lib/libmcfgthread.a" "$pthread_dir/x32/libpthread.a"
|
||||||
|
ln -sf "$mcfg64/lib/libmcfgthread.a" "$pthread_dir/x64/libpthread.a"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > "$toolchain_dir/x86_64-w64-mingw32-gcc" <<EOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
exec "$(command -v x86_64-w64-mingw32-gcc)" "\$@" -L"$pthread_dir/x64" -lmcfgthread -lpthread
|
||||||
|
EOF
|
||||||
|
cat > "$toolchain_dir/x86_64-w64-mingw32-g++" <<EOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
exec "$(command -v x86_64-w64-mingw32-g++)" "\$@" -L"$pthread_dir/x64" -lmcfgthread -lpthread
|
||||||
|
EOF
|
||||||
|
cat > "$toolchain_dir/i686-w64-mingw32-gcc" <<EOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
exec "$(command -v i686-w64-mingw32-gcc)" "\$@" -L"$pthread_dir/x32" -lmcfgthread -lpthread
|
||||||
|
EOF
|
||||||
|
cat > "$toolchain_dir/i686-w64-mingw32-g++" <<EOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
exec "$(command -v i686-w64-mingw32-g++)" "\$@" -L"$pthread_dir/x32" -lmcfgthread -lpthread
|
||||||
|
EOF
|
||||||
|
chmod +x \
|
||||||
|
"$toolchain_dir/x86_64-w64-mingw32-gcc" \
|
||||||
|
"$toolchain_dir/x86_64-w64-mingw32-g++" \
|
||||||
|
"$toolchain_dir/i686-w64-mingw32-gcc" \
|
||||||
|
"$toolchain_dir/i686-w64-mingw32-g++"
|
||||||
|
|
||||||
|
cat > "$toolchain_dir/build-win64-mcfg.txt" <<EOF
|
||||||
|
[binaries]
|
||||||
|
c = '$toolchain_dir/x86_64-w64-mingw32-gcc'
|
||||||
|
cpp = '$toolchain_dir/x86_64-w64-mingw32-g++'
|
||||||
|
ar = '$(command -v x86_64-w64-mingw32-ar)'
|
||||||
|
strip = '$(command -v x86_64-w64-mingw32-strip)'
|
||||||
|
windres = '$(command -v x86_64-w64-mingw32-windres)'
|
||||||
|
|
||||||
|
[properties]
|
||||||
|
needs_exe_wrapper = true
|
||||||
|
|
||||||
|
[host_machine]
|
||||||
|
system = 'windows'
|
||||||
|
cpu_family = 'x86_64'
|
||||||
|
cpu = 'x86_64'
|
||||||
|
endian = 'little'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$toolchain_dir/build-win32-mcfg.txt" <<EOF
|
||||||
|
[binaries]
|
||||||
|
c = '$toolchain_dir/i686-w64-mingw32-gcc'
|
||||||
|
cpp = '$toolchain_dir/i686-w64-mingw32-g++'
|
||||||
|
ar = '$(command -v i686-w64-mingw32-ar)'
|
||||||
|
strip = '$(command -v i686-w64-mingw32-strip)'
|
||||||
|
windres = '$(command -v i686-w64-mingw32-windres)'
|
||||||
|
|
||||||
|
[properties]
|
||||||
|
needs_exe_wrapper = true
|
||||||
|
|
||||||
|
[host_machine]
|
||||||
|
system = 'windows'
|
||||||
|
cpu_family = 'x86'
|
||||||
|
cpu = 'x86'
|
||||||
|
endian = 'little'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "$dxvk_src"
|
||||||
|
|
||||||
|
meson setup --cross-file "$toolchain_dir/build-win64-mcfg.txt" \
|
||||||
|
--buildtype release \
|
||||||
|
--prefix "$dxvk_build_dir" \
|
||||||
|
--strip \
|
||||||
|
--bindir x64 \
|
||||||
|
--libdir x64 \
|
||||||
|
-Db_ndebug=if-release \
|
||||||
|
"$dxvk_build_dir/build.64" >&2
|
||||||
|
ninja -C "$dxvk_build_dir/build.64" install >&2
|
||||||
|
|
||||||
|
meson setup --cross-file "$toolchain_dir/build-win32-mcfg.txt" \
|
||||||
|
--buildtype release \
|
||||||
|
--prefix "$dxvk_build_dir" \
|
||||||
|
--strip \
|
||||||
|
--bindir x32 \
|
||||||
|
--libdir x32 \
|
||||||
|
-Db_ndebug=if-release \
|
||||||
|
"$dxvk_build_dir/build.32" >&2
|
||||||
|
ninja -C "$dxvk_build_dir/build.32" install >&2
|
||||||
|
)
|
||||||
|
|
||||||
|
rm -f "$dxvk_build_dir/x64/"*.a "$dxvk_build_dir/x32/"*.a
|
||||||
|
rm -rf "$dxvk_build_dir/build.64" "$dxvk_build_dir/build.32"
|
||||||
|
|
||||||
|
echo "$DXVK_SHORT_SHA"
|
||||||
|
}
|
||||||
|
|
||||||
|
download_proton() {
|
||||||
|
local proton_tag="$1"
|
||||||
|
local asset_file
|
||||||
|
local proton_tarball
|
||||||
|
local proton_url
|
||||||
|
|
||||||
|
if [[ "$PROTON_ASSET" == "aarch64" ]]; then
|
||||||
|
asset_file="${proton_tag}-aarch64.tar.gz"
|
||||||
|
else
|
||||||
|
asset_file="${proton_tag}.tar.gz"
|
||||||
|
fi
|
||||||
|
|
||||||
|
proton_tarball="$CACHE_DIR/${asset_file}"
|
||||||
|
proton_url="https://github.com/${UPSTREAM_REPO}/releases/download/${proton_tag}/${asset_file}"
|
||||||
|
|
||||||
|
if [[ ! -f "$proton_tarball" ]]; then
|
||||||
|
say "Download Proton-GE ${proton_tag} (${PROTON_ASSET})"
|
||||||
|
curl -fL "$proton_url" -o "$proton_tarball"
|
||||||
|
else
|
||||||
|
say "Reuse cached Proton tarball"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$proton_tarball"
|
||||||
|
}
|
||||||
|
|
||||||
|
replace_tree() {
|
||||||
|
local source_dir="$1"
|
||||||
|
local target_dir="$2"
|
||||||
|
mkdir -p "$target_dir"
|
||||||
|
for dll in d3d8.dll d3d9.dll d3d10core.dll d3d11.dll dxgi.dll openvr_api_dxvk.dll; do
|
||||||
|
if [[ -f "$source_dir/$dll" ]]; then
|
||||||
|
cp -f "$source_dir/$dll" "$target_dir/$dll"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
replace_base_dlls() {
|
||||||
|
local source_dir="$1"
|
||||||
|
local target_dir="$2"
|
||||||
|
mkdir -p "$target_dir"
|
||||||
|
for dll in d3d8.dll d3d9.dll d3d10core.dll d3d11.dll ddraw.dll dxgi.dll; do
|
||||||
|
if [[ -f "$source_dir/$dll" ]]; then
|
||||||
|
cp -f "$source_dir/$dll" "$target_dir/$dll"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
patch_tool() {
|
||||||
|
local tool_root="$1"
|
||||||
|
local tool_name="$2"
|
||||||
|
local proton_tag="$3"
|
||||||
|
local dxvk_src="$4"
|
||||||
|
local dxvk_short_dir="$WORK_DIR/dxvk-out/dxvk-${DXVK_CHANNEL}"
|
||||||
|
local timestamp
|
||||||
|
|
||||||
|
timestamp="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
|
||||||
|
say "Replace DXVK payload"
|
||||||
|
replace_tree "$dxvk_short_dir/x64" "$tool_root/files/lib/wine/dxvk/x86_64-windows"
|
||||||
|
replace_tree "$dxvk_short_dir/x32" "$tool_root/files/lib/wine/dxvk/i386-windows"
|
||||||
|
replace_base_dlls "$dxvk_short_dir/x64" "$tool_root/files/lib/wine/x86_64-windows"
|
||||||
|
replace_base_dlls "$dxvk_short_dir/x32" "$tool_root/files/lib/wine/i386-windows"
|
||||||
|
replace_base_dlls "$dxvk_short_dir/x64" "$tool_root/files/share/default_pfx/drive_c/windows/system32"
|
||||||
|
replace_base_dlls "$dxvk_short_dir/x32" "$tool_root/files/share/default_pfx/drive_c/windows/syswow64"
|
||||||
|
|
||||||
|
chmod u+w \
|
||||||
|
"$tool_root/files/lib/wine/dxvk/version" \
|
||||||
|
"$tool_root/version" \
|
||||||
|
"$tool_root/toolmanifest.vdf" 2>/dev/null || true
|
||||||
|
|
||||||
|
printf '%s dxvk (%s)\n' "$DXVK_FULL_SHA" "$DXVK_DESCRIBE" > "$tool_root/files/lib/wine/dxvk/version"
|
||||||
|
printf '%s %s\n' "$(date +%s)" "$tool_name" > "$tool_root/version"
|
||||||
|
|
||||||
|
cat > "$tool_root/compatibilitytool.vdf" <<EOF
|
||||||
|
"compatibilitytools"
|
||||||
|
{
|
||||||
|
"compat_tools"
|
||||||
|
{
|
||||||
|
"${tool_name}"
|
||||||
|
{
|
||||||
|
"install_path" "."
|
||||||
|
"display_name" "${tool_name}"
|
||||||
|
"from_oslist" "windows"
|
||||||
|
"to_oslist" "linux"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$tool_root/toolmanifest.vdf" <<EOF
|
||||||
|
"manifest"
|
||||||
|
{
|
||||||
|
"version" "2"
|
||||||
|
"commandline" "/proton %verb%"
|
||||||
|
"require_tool_appid" "${RUNTIME_APPID}"
|
||||||
|
"use_sessions" "1"
|
||||||
|
"compatmanager_layer_name" "proton"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$tool_root/BUILD-INFO.txt" <<EOF
|
||||||
|
Tool name: ${tool_name}
|
||||||
|
Built at: ${timestamp}
|
||||||
|
Upstream repo: ${UPSTREAM_REPO}
|
||||||
|
Proton-GE tag: ${proton_tag}
|
||||||
|
Proton asset: ${PROTON_ASSET}
|
||||||
|
DXVK source: ${dxvk_src}
|
||||||
|
DXVK commit: ${DXVK_FULL_SHA}
|
||||||
|
DXVK describe: ${DXVK_DESCRIBE}
|
||||||
|
DXVK channel: ${DXVK_CHANNEL}
|
||||||
|
Runtime appid: ${RUNTIME_APPID}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
install_tool() {
|
||||||
|
local tool_root="$1"
|
||||||
|
local tool_name="$2"
|
||||||
|
local destination="$STEAM_COMPAT_DIR/$tool_name"
|
||||||
|
|
||||||
|
say "Install tool"
|
||||||
|
rm -rf "$destination"
|
||||||
|
mv "$tool_root" "$destination"
|
||||||
|
echo "$destination"
|
||||||
|
}
|
||||||
|
|
||||||
|
package_tool() {
|
||||||
|
local tool_root="$1"
|
||||||
|
local tool_name="$2"
|
||||||
|
local archive_path="$DIST_DIR/${tool_name}-dxvk-${DXVK_SHORT_SHA}.tar.xz"
|
||||||
|
|
||||||
|
say "Create shareable archive"
|
||||||
|
mkdir -p "$DIST_DIR"
|
||||||
|
rm -f "$archive_path"
|
||||||
|
tar -C "$(dirname "$tool_root")" -cJf "$archive_path" "$(basename "$tool_root")"
|
||||||
|
echo "$archive_path"
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dist_metadata() {
|
||||||
|
local archive_path="$1"
|
||||||
|
local tool_name="$2"
|
||||||
|
local proton_tag="$3"
|
||||||
|
mkdir -p "$DIST_DIR"
|
||||||
|
|
||||||
|
cat > "$DIST_DIR/package-info.env" <<EOF
|
||||||
|
TOOL_NAME=${tool_name}
|
||||||
|
PROTON_TAG=${proton_tag}
|
||||||
|
PROTON_VERSION=$(tool_version_from_tag "$proton_tag")
|
||||||
|
PROTON_ASSET=${PROTON_ASSET}
|
||||||
|
DXVK_CHANNEL=${DXVK_CHANNEL}
|
||||||
|
DXVK_TAG=${DXVK_TAG}
|
||||||
|
DXVK_COMMIT=${DXVK_FULL_SHA}
|
||||||
|
DXVK_DESCRIBE=${DXVK_DESCRIBE}
|
||||||
|
RUNTIME_APPID=${RUNTIME_APPID}
|
||||||
|
ARCHIVE_FILE=$(basename "$archive_path")
|
||||||
|
RELEASE_TAG=${tool_name}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$DIST_DIR/release-notes.md" <<EOF
|
||||||
|
# ${tool_name}
|
||||||
|
|
||||||
|
- Proton-GE: \`${proton_tag}\`
|
||||||
|
- DXVK: \`${DXVK_DESCRIBE}\`
|
||||||
|
- Runtime appid: \`${RUNTIME_APPID}\`
|
||||||
|
- Archive: \`$(basename "$archive_path")\`
|
||||||
|
|
||||||
|
This release is built for Elsword and uses Steam Linux Runtime 3.0 (sniper).
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ -z "$TOOL_NAME" ]]; then
|
||||||
|
TOOL_NAME="${TOOL_PREFIX}-$(tool_version_from_tag "$PROTON_TAG")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DXVK_SRC="$(prepare_dxvk_repo)"
|
||||||
|
build_dxvk "$DXVK_SRC" >/dev/null
|
||||||
|
PROTON_TARBALL="$(download_proton "$PROTON_TAG")"
|
||||||
|
|
||||||
|
say "Extract Proton-GE"
|
||||||
|
mkdir -p "$WORK_DIR/proton"
|
||||||
|
tar -xf "$PROTON_TARBALL" -C "$WORK_DIR/proton"
|
||||||
|
TOOL_ROOT="$(find "$WORK_DIR/proton" -mindepth 1 -maxdepth 1 -type d | head -n1)"
|
||||||
|
if [[ -z "$TOOL_ROOT" ]]; then
|
||||||
|
echo "Could not find extracted Proton tool root" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
patch_tool "$TOOL_ROOT" "$TOOL_NAME" "$PROTON_TAG" "$DXVK_SRC"
|
||||||
|
ARCHIVE_PATH="$(package_tool "$TOOL_ROOT" "$TOOL_NAME")"
|
||||||
|
write_dist_metadata "$ARCHIVE_PATH" "$TOOL_NAME" "$PROTON_TAG"
|
||||||
|
|
||||||
|
if [[ "$INSTALL_TOOL" -eq 1 ]]; then
|
||||||
|
INSTALLED_PATH="$(install_tool "$TOOL_ROOT" "$TOOL_NAME")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
say "Done"
|
||||||
|
printf 'Archive: %s\n' "$ARCHIVE_PATH"
|
||||||
|
if [[ "$INSTALL_TOOL" -eq 1 ]]; then
|
||||||
|
printf 'Installed: %s\n' "$INSTALLED_PATH"
|
||||||
|
fi
|
||||||
|
printf 'Proton-GE: %s (%s)\n' "$PROTON_TAG" "$PROTON_ASSET"
|
||||||
|
printf 'DXVK: %s\n' "$DXVK_DESCRIBE"
|
||||||
|
printf 'Runtime: %s\n' "$RUNTIME_APPID"
|
||||||
|
if [[ "$INSTALL_TOOL" -eq 1 ]]; then
|
||||||
|
printf '\nRestart Steam and choose "%s" as compatibility tool.\n' "$TOOL_NAME"
|
||||||
|
else
|
||||||
|
printf '\nShare the archive or unpack it into compatibilitytools.d on another system.\n'
|
||||||
|
fi
|
||||||
Generated
+27
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1782467914,
|
||||||
|
"narHash": "sha256-pGvFkM8N0xEkIIXDe5YYfbEAvHrk4IxBrjB/x8OomhE=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "e73de5be04e0eff4190a1432b946d469c794e7b4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
description = "Build versioned Elsword Proton-GE tools with DXVK and sniper runtime";
|
||||||
|
|
||||||
|
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs }:
|
||||||
|
let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
curl
|
||||||
|
git
|
||||||
|
glslang
|
||||||
|
meson
|
||||||
|
ninja
|
||||||
|
pkg-config
|
||||||
|
python3
|
||||||
|
wineWow64Packages.stable
|
||||||
|
pkgsCross.mingw32.stdenv.cc
|
||||||
|
pkgsCross.mingwW64.stdenv.cc
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user