mirror of
https://github.com/pumpitupdev/pumptools.git
synced 2024-11-12 01:30:48 +01:00
407 lines
11 KiB
Bash
Executable File
407 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
###
|
|
# Pump It Up Environment Bootstrap (piueb)
|
|
#
|
|
# This script sets up an environment to run all (MK5/6) Linux based PIU games as well as the MK3 linux ports.
|
|
# It takes care of the following tasks:
|
|
# - Check if all necessary files and directories are available
|
|
# - Verify that local dependencies for the game are used
|
|
# - Force the game into VSYNC
|
|
# - Bootstrap the application executable with:
|
|
# - A a subset of local libs or even a private ld-linux to ensure highest compatibility accross distros
|
|
# - A pumptools hook.so
|
|
#
|
|
# Therefore, this script expects the following folder structure and naming (!) to run the game:
|
|
# - game: The game folder with vanilla assets
|
|
# - lib: All (additional) dependencies the game needs to run
|
|
# - (save): Depending on your hook configuration, this can be located somewhere else, too
|
|
# - hook.conf: Configuration file with hook specific settings
|
|
# - hook.so: The game specific hook library compiled from pumptools
|
|
# - piu: The game's executable
|
|
# - piueb: This script
|
|
#
|
|
# To get usage/help output from piueb:
|
|
# ./piueb help
|
|
#
|
|
# To run the game, simply run this script with the "run" command:
|
|
# ./piueb run
|
|
#
|
|
# Additional arguments to hook.so are also supported:
|
|
# ./piueb run arg1 arg2
|
|
#
|
|
# For example, to get help/usage output from the hook library:
|
|
# ./piueb run -h
|
|
#
|
|
# For debugging purpose, a log file (piueb.log) is written to the folder this script is in.
|
|
###
|
|
|
|
readonly VERSION="1.0"
|
|
|
|
readonly LOG_LEVEL_ERROR="E"
|
|
readonly LOG_LEVEL_WARN="W"
|
|
readonly LOG_LEVEL_INFO="I"
|
|
readonly LOG_LEVEL_DEBUG="M"
|
|
|
|
##################################################################################
|
|
|
|
SELF_DIR="$(dirname "$(readlink -f "$0")")"
|
|
|
|
LOG_FILE="$SELF_DIR/piueb.log"
|
|
|
|
GAME_EXEC="$SELF_DIR/piu"
|
|
|
|
GAME_DIR="$SELF_DIR/game"
|
|
LIBS_DIR="$SELF_DIR/lib"
|
|
|
|
HOOK_FILE="$SELF_DIR/hook.so"
|
|
HOOK_CONFIG_FILE="$SELF_DIR/hook.conf"
|
|
|
|
LIBS_LINKER="$SELF_DIR/lib/ld-linux.so.2"
|
|
|
|
##################################################################################
|
|
|
|
log_init()
|
|
{
|
|
rm -f "$LOG_FILE"
|
|
}
|
|
|
|
log()
|
|
{
|
|
local level="$1"
|
|
local msg="$2"
|
|
|
|
local timestamp="$(date "+%Y/%m/%d-%H:%M:%S:%3N")"
|
|
|
|
local color=""
|
|
|
|
case $level in
|
|
$LOG_LEVEL_ERROR)
|
|
color="\e[1m\e[31m"
|
|
;;
|
|
$LOG_LEVEL_WARN)
|
|
color="\e[33m"
|
|
;;
|
|
$LOG_LEVEL_INFO)
|
|
color="\e[34m"
|
|
;;
|
|
$LOG_LEVEL_DEBUG)
|
|
color="\e[37m"
|
|
;;
|
|
esac
|
|
|
|
echo -e "${color}[$level]\e[0m[$timestamp] ${msg}"
|
|
echo "[$level][$timestamp] ${msg}" >> "$LOG_FILE"
|
|
}
|
|
|
|
log_error()
|
|
{
|
|
log "$LOG_LEVEL_ERROR" "$@"
|
|
}
|
|
|
|
log_warn()
|
|
{
|
|
log "$LOG_LEVEL_WARN" "$@"
|
|
}
|
|
|
|
log_info()
|
|
{
|
|
log "$LOG_LEVEL_INFO" "$@"
|
|
}
|
|
|
|
log_debug()
|
|
{
|
|
log "$LOG_LEVEL_DEBUG" "$@"
|
|
}
|
|
|
|
verify_running_as_root()
|
|
{
|
|
log_debug "Verify running as root user/with sudo..."
|
|
|
|
if [ "$EUID" -ne 0 ]; then
|
|
log_error "Running as non-root user, game likely to crash!"
|
|
fi
|
|
}
|
|
|
|
verify_file_exists()
|
|
{
|
|
local file_path="$1"
|
|
local warn_only="$2"
|
|
|
|
log_debug "Verify file/dir exists $file_path..."
|
|
|
|
if [ ! -e "$file_path" ]; then
|
|
if [ "$warn_only" ]; then
|
|
log_warn "Missing file \"$file_path\""
|
|
else
|
|
log_error "Missing required file \"$file_path\""
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
verify_dir_not_empty()
|
|
{
|
|
local dir_path="$1"
|
|
|
|
if [ -z "$(ls -A $dir_path)" ]; then
|
|
log_error "Directory empty: $dir_path"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
verify_deps_all_local()
|
|
{
|
|
local exec_path="$1"
|
|
local lib_path="$2"
|
|
|
|
local ldd_output="$(ldd $exec_path >&1)"
|
|
|
|
log_debug "Verify dependencies all local..."
|
|
|
|
log_debug "ldd output for $exec_path:\\n$ldd_output"
|
|
|
|
while read -r line; do
|
|
# Skip any additional output/error messages that are not part of the library listing
|
|
if [[ "$line:0:1" != "\t" ]]; then
|
|
continue
|
|
fi
|
|
|
|
local libname="$(echo "$line" | cut -d '.' -f 1)"
|
|
local libpath="$(echo "$line" | cut -d ' ' -f 3)"
|
|
|
|
# Skip a bunch of libs that are kernel specific like bound to GPU drivers
|
|
case $libname in
|
|
"linux-gate") ;& # fallthrough
|
|
"libGL") ;& # fallthrough
|
|
"libGLX") ;& # fallthrough
|
|
"libGLdispatch") ;& # fallthrough
|
|
# That will still point to the current machine's linker
|
|
# but we will use our own further down to bootstrap the elf
|
|
"ld-linux") ;& # fallthrough
|
|
"/lib/ld-linux")
|
|
log_debug "Skipping $libname"
|
|
continue
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
|
|
log_debug "Checking $libname: $libpath..."
|
|
|
|
if [[ $libpath != $LIBS_DIR* ]]; then
|
|
log_warn "$libname is not local to the game, path $libpath"
|
|
fi
|
|
done <<< "$ldd_output"
|
|
}
|
|
|
|
verify_game_executable()
|
|
{
|
|
local exec_path="$1"
|
|
|
|
log_debug "Verifying that game is executable: $exec_path..."
|
|
|
|
if [ ! -x "$exec_path" ]; then
|
|
log_warn "Game exec $exec_path not executable, fixing..."
|
|
chmod +x "$exec_path"
|
|
fi
|
|
}
|
|
|
|
print_usage()
|
|
{
|
|
echo "Pump It Up Environment Bootstrap (piueb), version $VERSION"
|
|
echo "Easily bootstrap linux based Pump games using pumptools."
|
|
echo "Usage: piueb <cmd> ..."
|
|
echo "Commands:"
|
|
echo " run - Run the game located next to this script"
|
|
echo " strace - Run the game with local libraries and strace attached for debugging"
|
|
echo " debug - Run the game with local libraries and a GDB debugger server attached for debugging"
|
|
echo " help - Print this usage message"
|
|
}
|
|
|
|
# Supports native mk6 linux games as well as the mk3 linux ports using mk3hook
|
|
cmd_run()
|
|
{
|
|
local mode="$1"
|
|
|
|
log_init
|
|
|
|
log_info "Pump It Up Environment Bootstrap (piueb), version $VERSION"
|
|
|
|
verify_running_as_root
|
|
|
|
log_info "Current path: $SELF_DIR"
|
|
|
|
log_debug "File and path verification..."
|
|
|
|
# Some file and folder verfication
|
|
|
|
verify_file_exists "$GAME_EXEC"
|
|
|
|
verify_file_exists "$GAME_DIR"
|
|
verify_dir_not_empty "$GAME_DIR"
|
|
verify_file_exists "$LIBS_DIR"
|
|
verify_dir_not_empty "$LIBS_DIR"
|
|
|
|
verify_file_exists "$HOOK_FILE"
|
|
# Only warn to allow creating of default hook.conf if no file exists
|
|
verify_file_exists "$HOOK_CONFIG_FILE" 1
|
|
|
|
# Support two modes of setting up the runtime environment with the game's
|
|
# dependencies:
|
|
# 1. Using a separate linker application which might be necessary when having
|
|
# to run the application with different libc (and/or other library) versions.
|
|
# This avoids incompatibilities with newer libc runtime and ld-linux versions
|
|
# (see f2-crashing-on-modern-linux-post-mortem.md).
|
|
#
|
|
# 2. Use only a subset of local libraries and take everything else from the
|
|
# current system. Your typical LD_LIBRARY_PATH method.
|
|
local lib_mode=""
|
|
|
|
if [ -e "$LIBS_LINKER" ]; then
|
|
lib_mode="ld"
|
|
else
|
|
lib_mode="local"
|
|
fi
|
|
|
|
log_info "Resolving dependencies mode: $lib_mode"
|
|
|
|
# LD_LIBRARY_PATH setup to use local libraries instead of system ones
|
|
|
|
if [ -z "$LD_LIBRARY_PATH" ]; then
|
|
LD_LIBRARY_PATH="$LIBS_DIR"
|
|
else
|
|
LD_LIBRARY_PATH="$LIBS_DIR:$LD_LIBRARY_PATH"
|
|
fi
|
|
|
|
log_debug "LD_LIBRARY_PATH: $LD_LIBRARY_PATH"
|
|
|
|
# Required that ldd shows us the correct paths
|
|
export LD_LIBRARY_PATH
|
|
|
|
# Check if all dependencies used are actually bundled with the game
|
|
verify_deps_all_local "$GAME_EXEC" "$LIBS_DIR"
|
|
|
|
local LD_PRELOAD="$HOOK_FILE"
|
|
|
|
local ldd_output_hook="$(ldd $HOOK_FILE >&1)"
|
|
|
|
log_debug "ldd output for $HOOK_FILE:\\n$ldd_output_hook"
|
|
|
|
if [ "${*:1}" ]; then
|
|
log_info "Additional cmd arguments provided: ${*:1}"
|
|
fi
|
|
|
|
log_info "Executing game $GAME_EXEC, mode $mode, lib_mode $lib_mode...\n=============================================================================================================================="
|
|
|
|
# Because people forget this when setting up the data manually
|
|
chmod +x "$GAME_EXEC"
|
|
|
|
# Force the game to run to vsync blank interval for nvidia cards: __GL_SYNC_TO_VBLANK=1
|
|
|
|
if [ "$mode" = "normal" ] && [ "$lib_mode" = "ld" ]; then
|
|
# / after GAME_DIR required
|
|
# Use private linker to avoid platform incompatibility issues (see above)
|
|
exec \
|
|
env __GL_SYNC_TO_VBLANK=1 \
|
|
env LD_PRELOAD="$LD_PRELOAD" \
|
|
"$LIBS_LINKER" \
|
|
--library-path "$LD_LIBRARY_PATH" \
|
|
"$GAME_EXEC" \
|
|
"${GAME_DIR}/" \
|
|
"--options" "$HOOK_CONFIG_FILE" \
|
|
"${@:2}"
|
|
elif [ "$mode" = "normal" ] && [ "$lib_mode" = "local" ]; then
|
|
# / after GAME_DIR required
|
|
exec \
|
|
env __GL_SYNC_TO_VBLANK=1 \
|
|
env LD_LIBRARY_PATH="$LD_LIBRARY_PATH" \
|
|
env LD_PRELOAD="$LD_PRELOAD" \
|
|
"$GAME_EXEC" \
|
|
"${GAME_DIR}/" \
|
|
"--options" "$HOOK_CONFIG_FILE" \
|
|
"${@:2}"
|
|
elif [ "$mode" = "debug" ] && [ "$lib_mode" = "ld" ]; then
|
|
log_error "debug mode with lib type ld not supported"
|
|
elif [ "$mode" = "debug" ] && [ "$lib_mode" = "local" ]; then
|
|
gdbserver \
|
|
--wrapper \
|
|
env \
|
|
__GL_SYNC_TO_VBLANK=1 \
|
|
LD_LIBRARY_PATH="$LD_LIBRARY_PATH" \
|
|
LD_PRELOAD="$LD_PRELOAD" \
|
|
-- 0.0.0.0:1234 \
|
|
"$GAME_EXEC" \
|
|
"${GAME_DIR}/" \
|
|
"--options" "$HOOK_CONFIG_FILE" \
|
|
"${@:2}"
|
|
elif [ "$mode" = "strace" ] && [ "$lib_mode" = "ld" ]; then
|
|
log_error "strace mode with lib type ld not supported"
|
|
elif [ "$mode" = "strace" ] && [ "$lib_mode" = "local" ]; then
|
|
strace \
|
|
-E __GL_SYNC_TO_VBLANK=1 \
|
|
-E LD_LIBRARY_PATH="$LD_LIBRARY_PATH" \
|
|
-E LD_PRELOAD="$LD_PRELOAD" \
|
|
"$GAME_EXEC" \
|
|
"${GAME_DIR}/" \
|
|
"--options" "$HOOK_CONFIG_FILE" \
|
|
"${@:2}"
|
|
elif [ "$mode" = "valgrind" ] && [ "$lib_mode" = "ld" ]; then
|
|
log_error "valgrind mode with lib type ld not supported"
|
|
elif [ "$mode" = "valgrind" ] && [ "$lib_mode" = "local" ]; then
|
|
valgrind \
|
|
--trace-children=yes \
|
|
env \
|
|
__GL_SYNC_TO_VBLANK=1 \
|
|
LD_LIBRARY_PATH="$LD_LIBRARY_PATH" \
|
|
LD_PRELOAD="$LD_PRELOAD" \
|
|
"$GAME_EXEC" \
|
|
"${GAME_DIR}/" \
|
|
"--options" "$HOOK_CONFIG_FILE" \
|
|
"${@:2}"
|
|
else
|
|
log_error "Unsupported mode ($mode) or lib_mode (($lib_mode)"
|
|
fi
|
|
}
|
|
|
|
cmd_help()
|
|
{
|
|
print_usage
|
|
}
|
|
|
|
##################################################################################
|
|
|
|
CMD="$1"
|
|
|
|
case $CMD in
|
|
"")
|
|
echo "Insufficient arguments."
|
|
print_usage
|
|
exit 1
|
|
;;
|
|
"help")
|
|
cmd_help
|
|
exit 0
|
|
;;
|
|
"run")
|
|
cmd_run "normal" "${@:2}"
|
|
exit 0
|
|
;;
|
|
"debug")
|
|
cmd_run "debug" "${@:2}"
|
|
exit 0
|
|
;;
|
|
"strace")
|
|
cmd_run "strace" "${@:2}"
|
|
exit 0
|
|
;;
|
|
"valgrind")
|
|
cmd_run "valgrind" "${@:2}"
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Invalid command."
|
|
print_usage
|
|
exit 1
|
|
;;
|
|
esac |