Files
ayozetr 498a37d24b Runtime: run the game's movbe on processors without MOVBE
The game's code was built for the PS4's CPU and has 987 movbe. AMD's FX
processors of the Piledriver generation have every other instruction the
game uses but not this one, so the game stopped with SIGILL (exit code 132)
at the first one, while reading a file header (#39).

The SIGILL handler now decodes the movbe at the faulting address, does the
load or store with its bytes swapped and resumes after it. On a processor
with MOVBE this never runs. The CPU check no longer refuses to start
without MOVBE and says in the log that the port runs them; the log also
counts them every 10 s (a few thousand a second while loading, about ten a
second while playing).

BB_MOVBE_TRAP=file turns the movbe listed by tools/movbe_sites.py into ud2,
so the emulation can be tested on any processor. tests/test_movbe.c checks
the forms the game uses.
2026-11-04 11:11:26 +00:00

153 lines
9.7 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
cd -- "$(dirname -- "$0")"
mkdir -p out
if [[ -z ${CC:-} ]]; then
CC=$(command -v cc || command -v gcc || true)
if [[ -z $CC ]]; then
for candidate in /nix/store/*-gcc-wrapper-*/bin/gcc; do
if [[ -x $candidate ]]; then CC=$candidate; break; fi
done
fi
fi
if [[ -z ${CC:-} ]]; then echo 'Install GCC/Clang or set CC.' >&2; exit 1; fi
# Dependencies come from pkg-config (Vulkan loader/headers, SDL3). On NixOS the
# environment is provided by shell.nix; re-enter it automatically if needed.
if ! { command -v pkg-config >/dev/null && pkg-config --exists vulkan sdl3 && command -v cmake >/dev/null && command -v ninja >/dev/null; }; then
if [[ -z ${BB_IN_NIX_SHELL:-} ]] && command -v nix-shell >/dev/null; then
exec env BB_IN_NIX_SHELL=1 nix-shell shell.nix --run "bash build.sh $*"
fi
echo 'Need pkg-config with vulkan and sdl3, cmake and ninja (see shell.nix).' >&2; exit 1
fi
read -r -a includes <<< "$(pkg-config --cflags vulkan sdl3)"
read -r -a libraries <<< "$(pkg-config --libs vulkan sdl3)"
# GPU library (shadPS4 video core + drivers), built by CMake into out/gpu/libbbgpu.so.
# BB_PGO: generate (instrumented build that writes pgo/ while the game runs), use, off.
# Default: use the profile in pgo/ when there is one. BB_LTO=OFF disables link-time optimization.
pgo=${BB_PGO:-}
if [[ -z $pgo ]]; then
if [[ -n $(find pgo -name '*.gcda' -print -quit 2>/dev/null) ]]; then pgo=use; else pgo=off; fi
fi
# A profile only applies when its files are named as this build names them (relative to out/gpu,
# -fprofile-prefix-path): one recorded elsewhere matched nothing and PGO was silently off.
if [[ $pgo == use && -z $(find pgo -name 'CMakeFiles#bbgpu.dir#*.gcda' -print -quit 2>/dev/null) ]]; then
echo 'PGO: the profile in pgo/ is from another build layout (rebuild it: BB_PGO=generate); built without it' >&2
pgo=off
fi
mkdir -p pgo
# Submodules (git clone --recursive, or: git submodule update --init) and this port's changes
# to FSR-Vulkan (gpu/patches/fsr-vulkan), applied to its working tree once.
if [[ ! -f gpu/third_party/fsr-vulkan/CMakeLists.txt || ! -f gpu/third_party/imgui/imgui.h ]]; then
git submodule update --init --recursive
fi
# Issue #124: a checkout updated from an earlier version keeps that version's patch applied, and
# the new one applies neither way ("patch does not apply"): the submodule's tree holds nothing but
# these patches, so it is reset and they are applied again.
fsr_patches_apply() {
for patch in gpu/patches/fsr-vulkan/*.patch; do
if ! git -C gpu/third_party/fsr-vulkan apply --reverse --check "$PWD/$patch" 2>/dev/null; then
git -C gpu/third_party/fsr-vulkan apply "$@" "$PWD/$patch" || return 1
fi
done
}
if ! fsr_patches_apply --check 2>/dev/null; then
echo 'FSR-Vulkan: an earlier version of the port changed it; resetting it and patching again'
git -C gpu/third_party/fsr-vulkan checkout -- .
fi
fsr_patches_apply
cmake -S gpu -B out/gpu -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBB_PGO="$pgo" \
-DBB_LTO="${BB_LTO:-ON}" -DBB_PGO_DIR="$PWD/pgo" > out/gpu-cmake.log
grep '^-- Online module' out/gpu-cmake.log || true
echo "GPU library: PGO $pgo, LTO ${BB_LTO:-ON}"
# A failed GPU build must stop here: an older libbbgpu.so would otherwise be used silently.
if ! ninja -C out/gpu bbgpu $(grep -q "^build bbnet20:" out/gpu/build.ninja && echo bbnet20) > out/gpu-build.log 2>&1; then
grep -v '^\[' out/gpu-build.log | tail -40 >&2
echo 'GPU library build failed (full log: out/gpu-build.log)' >&2; exit 1
fi
# Optional DLSS (NVIDIA RTX): DLSS_SDK_ROOT=<github.com/NVIDIA/DLSS checkout> builds the bridge
# (gpu/dlss_bridge, the only code using NVIDIA's SDK) and puts it next to bb-probe with NVIDIA's
# libnvidia-ngx-dlss.so (Super Resolution) and libnvidia-ngx-dlssg.so (Frame Generation, GeForce
# RTX 40 and newer; SDK 310.7 or newer has it). Without them the DLSS upscaler and DLSS Frame
# Generation are listed as unavailable.
if [[ -n ${DLSS_SDK_ROOT:-} ]]; then
cmake -S gpu/dlss_bridge -B out/dlss-bridge -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DDLSS_SDK_ROOT="$DLSS_SDK_ROOT" >/dev/null
ninja -C out/dlss-bridge >/dev/null
rm -f out/libnvidia-ngx-dlss.so.* out/libnvidia-ngx-dlssg.so.*
cp out/dlss-bridge/libbbport_dlss.so "$DLSS_SDK_ROOT"/lib/Linux_x86_64/rel/libnvidia-ngx-dlss.so.* out/
if ls "$DLSS_SDK_ROOT"/lib/Linux_x86_64/rel/libnvidia-ngx-dlssg.so.* >/dev/null 2>&1; then
cp "$DLSS_SDK_ROOT"/lib/Linux_x86_64/rel/libnvidia-ngx-dlssg.so.* out/
echo "DLSS Frame Generation library: $(ls out/libnvidia-ngx-dlssg.so.*)"
else
echo "DLSS Frame Generation library not in this SDK (lib/Linux_x86_64/rel/libnvidia-ngx-dlssg.so.*): DLSS Super Resolution only"
fi
cp "$DLSS_SDK_ROOT/LICENSE.txt" out/NVIDIA-DLSS-LICENSE.txt
echo "DLSS bridge: out/libbbport_dlss.so"
fi
# Installing from PS4 packages (launcher: Install from PKG, scripts/pkg_install.py): pkgextract
# (github.com/paulomanrique/ps4-pkg-extractor, Unlicense) at a reviewed revision, with its
# Cargo.lock. Needs cargo; without it the launcher asks for a game folder only.
pkgextract_rev=f430ce1634fc7953834b545a3946bfbd5fa216fa
if [[ ! -x out/pkgextract/bin/pkgextract ]] && command -v cargo >/dev/null; then
if cargo install --quiet --locked --root out/pkgextract \
--git https://github.com/paulomanrique/ps4-pkg-extractor --rev "$pkgextract_rev" pkgextract \
>out/pkgextract-build.log 2>&1; then
echo 'PKG extractor: out/pkgextract/bin/pkgextract'
else
echo 'pkgextract build failed (out/pkgextract-build.log): Install from PKG is not offered' >&2
fi
fi
# $ORIGIN/gpu: packaged copies keep the library next to the binary without patching it.
gpu=(-Lout/gpu -lbbgpu -Wl,-rpath,'$ORIGIN/gpu' -Wl,-rpath,"$PWD/out/gpu" -rdynamic)
runtime=(src/runtime*.c)
# Third-party decoders: compiled once, without this project's -Werror policy.
atrac9=(third_party/LibAtrac9/C/src/*.c)
if [[ ! -f out/libatrac9.a || -n $(find third_party/LibAtrac9/C/src -newer out/libatrac9.a -name '*.c') ]]; then
rm -rf out/atrac9 && mkdir -p out/atrac9
for source in "${atrac9[@]}"; do "$CC" -std=c99 -O2 -g -w -c "$source" -o "out/atrac9/$(basename "${source%.c}").o"; done
ar rcs out/libatrac9.a out/atrac9/*.o
fi
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -pthread -no-pie "${includes[@]}" -I. -Isrc src/probe.c "${runtime[@]}" src/vulkan_smoke.c out/libatrac9.a -lm "${gpu[@]}" "${libraries[@]}" -o out/bb-probe
echo "Built $PWD/out/bb-probe"
# GPU check for run.sh (live_resolution=auto) and the launcher's gamepad list (--gamepads).
"$CC" -std=c11 -O2 -Wall -Wextra -Werror "${includes[@]}" tools/gpu_capabilities.c "${libraries[@]}" -o out/bb-gpu-capabilities
if [[ ${1:-} == --test ]]; then
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -pthread "${includes[@]}" -I. -Isrc tests/test_pad.c "${libraries[@]}" -lm -o out/pad-test
out/pad-test
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -pthread "${includes[@]}" -I. -Isrc tests/test_pad_atlas.c "${libraries[@]}" -lm -o out/pad-atlas-test
out/pad-atlas-test
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -pthread -I. -Isrc tests/test_runtime.c "${runtime[@]}" out/libatrac9.a -lm "${gpu[@]}" "${libraries[@]}" -o out/runtime-test
out/runtime-test
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -Isrc tests/test_movbe.c src/runtime_movbe.c -o out/movbe-test
out/movbe-test
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -pthread -Isrc tests/test_file_mods.c src/runtime_remote.c -o out/file-mods-test
out/file-mods-test
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -pthread -I. -Isrc tests/test_sema.c "${runtime[@]}" out/libatrac9.a -lm "${gpu[@]}" "${libraries[@]}" -o out/sema-test
out/sema-test
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -pthread -I. -Isrc tests/test_online.c "${runtime[@]}" out/libatrac9.a -lm "${gpu[@]}" "${libraries[@]}" -o out/online-test
out/online-test
"$CC" -std=c11 -D_GNU_SOURCE -O2 -g -Wall -Wextra -Werror -I. -Isrc tests/test_content.c src/runtime_content.c -o out/content-test
out/content-test
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -pthread -Isrc tests/test_trophy.c src/runtime_trophy.c -o out/trophy-test
out/trophy-test out/trophy_test_tmp
# Hunter's Atlas: its guarded game-memory readers on synthetic images.
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -Isrc tests/test_map_location.c src/runtime_map.c -lm -o out/map-location-test
out/map-location-test
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -pthread -Isrc tests/test_atlas_state.c src/runtime_atlas_state.c -lm -o out/atlas-state-test
out/atlas-state-test
# Hunter's Atlas: its tile's state machine, map styles, floor interiors, controller shortcut.
# (-Wno-misleading-indentation: Atlas's compact one-line style.) bbport (Linux fork):
# nlohmann/json from the system, as gpu/CMakeLists.txt.
atlas_cxx=(-std=c++20 -O2 -g -Wall -Wextra -Werror -Wno-misleading-indentation -Igpu/shim -Igpu/third_party/imgui "${includes[@]}")
for test in test_bbport_atlas_menu_state test_bbport_atlas_style test_bbport_atlas_interiors; do
"${CXX:-c++}" "${atlas_cxx[@]}" "tests/$test.cpp" -o "out/$test" || exit 1
(cd out && "./$test") || exit 1
done
"${CXX:-c++}" "${atlas_cxx[@]}" tests/test_map_controller.cpp gpu/shim/bbport_map_controller.cpp "${libraries[@]}" -o out/map-controller-test
out/map-controller-test
"${CXX:-c++}" "${atlas_cxx[@]}" tests/test_atlas_features.cpp gpu/shim/bbport_map_features.cpp "${libraries[@]}" -o out/atlas-features-test
rm -rf out/atlas-features && out/atlas-features-test out/atlas-features
"${CXX:-c++}" "${atlas_cxx[@]}" tests/test_atlas_i18n.cpp -o out/atlas-i18n-test
out/atlas-i18n-test
fi