362 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			362 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/bin/bash
 | |
| # Dump directory expects dir structure like this Beatmania IIDX 26/contents;Beatmania IIDX 26/devel;
 | |
| 
 | |
| ##    Constant    ##
 | |
| _C_scriptDir="$(dirname -- "$(realpath -- "$0")")"
 | |
| _C_scriptName="$(basename -- "$(realpath -- "$0")")"
 | |
| # These constants are config overridable, but it is not recommended to do so (ex. hooks may not work)
 | |
| _C_dll_override="bm2dx.dll"
 | |
| _C_lightning_model="--iidxtdj"
 | |
| _C_rtname="proton-ge-8.16"
 | |
| _C_pfxname="u573_bm2dx_w8"
 | |
| # Audio native ASIO -iidxsounddevice is 27+ only
 | |
| _C_audio_asio1=(-iidxasio ASIO4ALL -iidxsounddevice asio)
 | |
| _C_audio_asio2=(-iidxasio FlexASIO -iidxsounddevice asio)
 | |
| _C_audio_asio3=(-iidxasio WineASIO -iidxsounddevice asio)
 | |
| # Audio native(dsound,24-)/wasapi(25+)
 | |
| _C_audio_native=(-audiohookdisable)
 | |
| _C_audio_wasapi=(-iidxsounddevice wasapi)
 | |
| # Audio spice dummy routing (25+ only, uses wasapi exclusive internally, which gets routed to specific audiobackend)
 | |
| _C_audio_dummy_asio=(-iidxsounddevice wasapi -audiodummy -audiobackend asio -asiodriverid 0)
 | |
| _C_audio_dummy_waveout=(-iidxsounddevice wasapi -audiodummy -audiobackend waveout)
 | |
| _C_audio_dummy_none=(-iidxsounddevice wasapi -audiodummy -audiobackend none)
 | |
| _C_audio_dummy_pipewire=(-iidxsounddevice wasapi -audiodummy -audiobackend pipewire)
 | |
| # Network spice builtin endpoint
 | |
| _C_net_dummy_maint=(-eamaint 1 -ea)
 | |
| _C_net_dummy_local=(-ea)
 | |
| 
 | |
| ##    Core    ##
 | |
| _G_root="$_C_scriptDir/../.steam/root"
 | |
| _G_ep=("$_C_scriptDir/ep_protonnix")
 | |
| _G_exec=""
 | |
| _G_args=()
 | |
| _G_style=""
 | |
| _G_dumps=""
 | |
| _G_debug=0
 | |
| 
 | |
| ##    Game config    ##
 | |
| _G_video="stable"
 | |
| _G_display=0
 | |
| _G_refresh_rate=0
 | |
| _G_fsr=-1
 | |
| _G_url=""
 | |
| _G_pcbid=""
 | |
| _G_hooks=()
 | |
| _G_devel=()
 | |
| 
 | |
| 
 | |
| print_help() {
 | |
|   cat <<EOF
 | |
| ep_nix:ver:bm2dx
 | |
| Usage: ${_C_scriptName} [EXiV] [opt-args]
 | |
| Available arguments:
 | |
| EXiV          style (numeric, required)
 | |
| --cfg         launch config tool (spicecfg)        
 | |
| --dbg         launch debug environment (--dbg2 if no value provided, available steps through OR blitting 1: spice, 2: x64dbg, 4: wine, 8: style reserved flag)
 | |
| --root        steam library root directory location override (this is where prefixes/runtimes are saved)
 | |
| --dumps       game dumps root directory location override (assuming steam library if not provided)
 | |
| --exec        run anything inside prefix (allows for running any application aside, you may provide arguments after --)
 | |
| --            arguments past this are passed to executable instead of defaults (read spice etc.)
 | |
| -h
 | |
| --help        show help
 | |
| 
 | |
| Examples:
 | |
| ${_C_scriptName} 26 --root /mnt/steamhdd --cfg (launches /mnt/steamhdd/steamapps/common/Beatmania IIDX 26/contents/spicecfg.exe)
 | |
| ${_C_scriptName} 26  (launches $_G_root/steamapps/common/Beatmania IIDX 26/contents/spice64.exe)
 | |
| ${_C_scriptName} 26 --exec /tmp/server.exe -- -p 1108 (runs /tmp/server.exe -p 1108 in IIDX26's environment)
 | |
| ${_C_scriptName} 26 --dbg\$((4 | 1)) (launches second example with only additional wine and spice debug steps)
 | |
| EOF
 | |
| }
 | |
| 
 | |
| abort() {
 | |
|   echo "[EXIT] Aborting due to failure at ${FUNCNAME[1]}($1)"
 | |
|   exit 1
 | |
| }
 | |
| 
 | |
| arg_parse() {
 | |
|   while [ ! -z ${1+x} ]; do
 | |
|     case "$1" in
 | |
|     --help | -h)
 | |
|       shift
 | |
|       print_help
 | |
|       exit 0
 | |
|       ;;
 | |
|     --exec)
 | |
|       shift
 | |
|       _G_exec="$1"
 | |
|       shift
 | |
|       ;;
 | |
|     --cfg)
 | |
|       : ${_G_exec:="spicecfg.exe"}
 | |
|       shift
 | |
|       ;;
 | |
|     --dbg*)
 | |
|       _G_debug="${1:5}"
 | |
|       [ -n "$_G_debug" ] || _G_debug=2
 | |
|       shift
 | |
|       ;;
 | |
|     --root)
 | |
|       shift
 | |
|       : ${_G_root:="${1%/}"}
 | |
|       shift
 | |
|       ;;
 | |
|     --dumps)
 | |
|       shift
 | |
|       : ${_G_dumps:="${1%/}"}
 | |
|       shift
 | |
|       ;;
 | |
|     --)
 | |
|       shift
 | |
|       _G_args+=("${@}")
 | |
|       ;;
 | |
|     "")
 | |
|       shift
 | |
|       ;;
 | |
|     *)
 | |
|       [ -z "${1##*[!0-9]*}" ] || _G_style="$1"
 | |
|       [ -n "$_G_style" ] || {
 | |
|         echo "Unknown option: $1"
 | |
|         exit 2
 | |
|       }
 | |
|       shift
 | |
|       ;;
 | |
|     esac
 | |
|   done
 | |
| }
 | |
| 
 | |
| cfg_parse() {
 | |
|   # Optional config
 | |
|   cfgjson="$(jq -c "." "$_G_dumps/Beatmania IIDX ${_G_style}/contents/prop/linux.json" 2>/dev/null || echo {})"
 | |
| 
 | |
|   # Video
 | |
|   _G_video="$(jq -cr ".video.profile // \"$_G_video\"" <<<"$cfgjson")"
 | |
|   _G_display="$(jq -cr ".video.display // \"$_G_display\"" <<<"$cfgjson")"
 | |
|   _G_refresh_rate="$(jq -cr ".video.refresh_rate // \"$_G_refresh_rate\"" <<<"$cfgjson")"
 | |
|   _G_fsr="$(jq -cr ".video.fsr // \"$_G_fsr\"" <<<"$cfgjson")"
 | |
| 
 | |
|   # Network
 | |
|   _G_url="$(jq -cr ".network.url // \"$_G_url\"" <<<"$cfgjson")"
 | |
|   _G_pcbid="$(jq -cr ".network.pcbid // \"$_G_pcbid\"" <<<"$cfgjson")"
 | |
| 
 | |
|   # Extra
 | |
|   while read -r entry; do _G_hooks+=("$entry"); done < <(jq -cr '.extra.hooks // [] | .[]' <<<"$cfgjson")
 | |
|   while read -r entry; do _G_devel+=("$entry"); done < <(jq -cr '.extra.devel // [] | .[]' <<<"$cfgjson")
 | |
|   [ "$(jq -cr ".extra.lightning_support" <<<"$cfgjson")" == "false" ] && _C_lightning_model=""
 | |
|   _C_game_module="$(jq -cr ".extra.dll_override // \"$_C_game_module\"" <<<"$cfgjson")"
 | |
|   _C_rtname="$(jq -cr ".extra.rt_override // \"$_C_rtname\"" <<<"$cfgjson")"
 | |
|   _C_pfxname="$(jq -cr ".extra.pfx_override // \"$_C_pfxname\"" <<<"$cfgjson")"
 | |
| }
 | |
| 
 | |
| monitor_by_id() {
 | |
|   if [ ! $(command -v ./monitor_by_id) ]; then
 | |
|     gcc "$_C_scriptDir/monitor_by_id.c" -o "$_C_scriptDir/monitor_by_id" -lX11 -lXrandr || return
 | |
|   fi
 | |
|   "$_C_scriptDir/monitor_by_id" $1
 | |
| }
 | |
| 
 | |
| args_iidx22() {
 | |
|   # dummy audio unsupported, fullscreen working fine
 | |
|   _G_args+=("${_C_audio_native[@]}")
 | |
|   [ "$_G_video" != "stable" ] || _G_args+=(-graphics-force-single-adapter)
 | |
|   [ "$_G_url" != "dummy" ] || _G_args+=("${_C_net_dummy_local[@]}")
 | |
| }
 | |
| 
 | |
| args_iidx30() {
 | |
|   # pipewire supported, fullscreen partially? working subscreen
 | |
|   _G_args+=("${_C_audio_dummy_pipewire[@]}" "${_C_lightning_model[@]}" -nolegacy -touchemuforce)
 | |
|   [ "$_G_video" != "stable" ] || _G_args+=(-graphics-force-single-adapter)
 | |
|   [ "$_G_url" != "dummy" ] || _G_args+=("${_C_net_dummy_maint[@]}")
 | |
| }
 | |
| 
 | |
| args_iidx29() {
 | |
|   # pipewire supported, fullscreen working subscreen
 | |
|   _G_args+=("${_C_audio_dummy_pipewire[@]}" "${_C_lightning_model[@]}" -nolegacy -touchemuforce)
 | |
|   [ "$_G_video" != "stable" ] || _G_args+=(-graphics-force-single-adapter)
 | |
|   [ "$_G_url" != "dummy" ] || _G_args+=("${_C_net_dummy_maint[@]}")
 | |
| }
 | |
| 
 | |
| args_iidx28() {
 | |
|   if ((_G_debug & 8)); then
 | |
|     # Workaround for audio initializing for ALSA driver (not recommended)
 | |
|     #export PULSE_LATENCY_MSEC=10
 | |
|     export PIPEWIRE_LATENCY=411/44100
 | |
|     export FIX_RATE=44100
 | |
|     export FIX_CHANNELS="[ FL FR ]"
 | |
|     export FIX_FORMAT="S16LE"
 | |
|     rate=$(pw-metadata -n settings 0 clock.rate)
 | |
|     rate="${rate#*value:\'}"
 | |
|     rate="${rate%%\'*}"
 | |
|     pw-metadata -n settings 0 clock.force-rate 44100
 | |
|     #pw-metadata -n settings 0 clock.force-quantum 441
 | |
|     nohup bash -c "sleep 30; pw-metadata -n settings 0 clock.force-rate $rate; sleep 1; pw-metadata -n settings 0 clock.force-rate --delete;" >/dev/null 2>&1 &
 | |
|     # Results in desync prone audio with alsa, static with pulse
 | |
|     _G_args+=("${_C_audio_wasapi[@]}" "${_C_audio_native[@]}" "${_C_lightning_model[@]}")
 | |
|   else
 | |
|     # pipewire supported, fullscreen working subscreen
 | |
|     _G_args+=("${_C_audio_dummy_pipewire[@]}" "${_C_lightning_model[@]}" -nolegacy -touchemuforce)
 | |
|     [ "$_G_video" != "stable" ] || _G_args+=(-graphics-force-single-adapter)
 | |
|     [ "$_G_url" != "dummy" ] || _G_args+=("${_C_net_dummy_maint[@]}")
 | |
|   fi
 | |
| 
 | |
| }
 | |
| 
 | |
| args_iidx27() {
 | |
|   # pipewire supported, fullscreen working subscreen
 | |
|   _G_args+=("${_C_audio_dummy_pipewire[@]}" "${_C_lightning_model[@]}" -nolegacy -touchemuforce)
 | |
|   [ "$_G_video" != "stable" ] || _G_args+=(-graphics-force-single-adapter)
 | |
|   [ "$_G_url" != "dummy" ] || _G_args+=("${_C_net_dummy_maint[@]}")
 | |
| }
 | |
| 
 | |
| args_iidx26() {
 | |
|   # pipewire supported, fullscreen breaks on window unfocus
 | |
|   _G_args+=("${_C_audio_dummy_pipewire[@]}")
 | |
|   [ "$_G_video" != "stable" ] || _G_args+=(-w)
 | |
|   [ "$_G_url" != "dummy" ] || _G_args+=("${_C_net_dummy_maint[@]}")
 | |
| }
 | |
| 
 | |
| args_iidx25() {
 | |
|   # pipewire supported, fullscreen breaks on window unfocus
 | |
|   _G_args+=("${_C_audio_dummy_pipewire[@]}")
 | |
|   [ "$_G_video" != "stable" ] || _G_args+=(-w)
 | |
|   [ "$_G_url" != "dummy" ] || _G_args+=("${_C_net_dummy_local[@]}")
 | |
| }
 | |
| 
 | |
| gen_args() {
 | |
|   # Skip for override (--) mode
 | |
|   [ "${#_G_args[@]}" -eq 0 ] || {
 | |
|     _G_ep+=(--args "${_G_args[*]}")
 | |
|     return 0
 | |
|   }
 | |
| 
 | |
|   # Basic
 | |
|   _G_args+=(-io -iidx -modules modules "$_C_dll_override")
 | |
| 
 | |
|   # Style
 | |
|   if [[ $(type -t "args_iidx${_G_style}") == function ]]; then
 | |
|     "args_iidx${_G_style}"
 | |
|   else
 | |
|     _G_args+=("${_C_net_dummy[@]}" "${_C_audio_native[@]}")
 | |
|   fi
 | |
| 
 | |
|   # Video
 | |
|   _G_args+=(-monitor $_G_display)
 | |
|   [ "$_G_refresh_rate" -gt 0 ] && _G_args+=(-graphics-force-refresh $_G_refresh_rate)
 | |
|   if [ "$_G_video" = "force_window" ]; then
 | |
|     _G_args+=(-w)
 | |
|   elif [ "$_G_video" = "force_fullscreen" ]; then
 | |
|     _G_args+=(-graphics-force-single-adapter)
 | |
|   fi
 | |
| 
 | |
|   # Network
 | |
|   [ -n "$_G_url" ] && [ "$_G_url" != "dummy" ] && _G_args+=(-url "$_G_url")
 | |
|   [ -n "$_G_pcbid" ] && _G_args+=(-p "$_G_pcbid")
 | |
| 
 | |
|   # Hooks
 | |
|   for hook in "${_G_hooks[@]}"; do _G_args+=(-k "$hook"); done
 | |
| 
 | |
|   # Devel
 | |
|   ((_G_debug & 1)) && _G_args+=(${_G_devel[@]})
 | |
|   if ((_G_debug & 2)); then
 | |
|     # _REV: could write line directly into db/*.cmdline instead
 | |
|     echo "[INFO] x64dbg session argument recommendation: '${_G_args[*]} -w'"
 | |
|   else
 | |
|     _G_ep+=(--args "${_G_args[*]}")
 | |
|   fi
 | |
| 
 | |
| }
 | |
| 
 | |
| gen_base() {
 | |
|   # Core setup
 | |
|   styledir="$_G_dumps/Beatmania IIDX ${_G_style}"
 | |
|   patchid="0005731108"
 | |
|   if [ "$_G_style" -lt "25" ]; then
 | |
|     arch=32
 | |
|   else
 | |
|     arch=64
 | |
|   fi
 | |
| 
 | |
|   # Proton setup
 | |
|   [ -n "$LD_LIBRARY_PATH" ] && LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:"
 | |
|   [ -n "$WINEDLLPATH" ] && LD_LIBRARY_PATH="${WINEDLLPATH}:"
 | |
|   LD_LIBRARY_PATH="${LD_LIBRARY_PATH}${styledir}/contents/modules"
 | |
|   WINEDLLPATH="${WINEDLLPATH}${styledir}/contents/modules"
 | |
|   export WINEDLLPATH
 | |
|   export LD_LIBRARY_PATH
 | |
|   if ((_G_debug & 4)); then
 | |
|     : ${PROTON_LOG_DIR:="$styledir/contents"}
 | |
|     : ${PROTON_LOG:=1}
 | |
|     : ${WINEDEBUG:="+loaddll,+module"}
 | |
|     export WINEDEBUG
 | |
|     export PROTON_LOG
 | |
|     export PROTON_LOG_DIR
 | |
|     echo "[INFO] Proton log file: '$PROTON_LOG_DIR/steam-${patchid}.log'"
 | |
|   fi
 | |
|   export PROTON_STYLE_PATCH="$PROTON_STYLE_PATCH:$_G_style"
 | |
| 
 | |
|   # Video fixes (AMD FSR, NVidia VSync)
 | |
|   WINE_FULLSCREEN_FSR=0
 | |
|   [ -z ${__GL_SYNC_DISPLAY_DEVICE+x} ] && __GL_SYNC_DISPLAY_DEVICE="$(monitor_by_id $_G_display)"
 | |
|   if [ "$_G_fsr" -ge 0 ]; then
 | |
|     WINE_FULLSCREEN_FSR=1
 | |
|     export WINE_FULLSCREEN_FSR_STRENGTH=$_G_fsr
 | |
|     if [ "$_G_style" -ge "30" ]; then
 | |
|       export WINE_FULLSCREEN_FSR_CUSTOM_MODE=1920x1080
 | |
|     elif [ "$_G_style" -ge "20" ]; then
 | |
|       export WINE_FULLSCREEN_FSR_CUSTOM_MODE=1280x720
 | |
|     else
 | |
|       export WINE_FULLSCREEN_FSR_CUSTOM_MODE=640x480
 | |
|     fi
 | |
|   fi
 | |
|   export WINE_FULLSCREEN_FSR
 | |
|   export __GL_SYNC_DISPLAY_DEVICE
 | |
| 
 | |
|   # Debug/normal environment generation
 | |
|   if ((_G_debug & 2)); then
 | |
|     styledir="$styledir/devel/debug/x64dbg/x${arch}"
 | |
|     [ -n "$_G_exec" ] || _G_exec="x${arch}dbg.exe"
 | |
|   else
 | |
|     styledir="$styledir/contents"
 | |
|     [ -n "$_G_exec" ] || if [ "$arch" -eq "32" ]; then _G_exec="spice.exe"; else _G_exec="spice${arch}.exe"; fi
 | |
|   fi
 | |
| 
 | |
|   # Apply environment
 | |
|   cd "$styledir" || abort "INVALID PATH"
 | |
|   _G_ep+=(--exec "$_G_exec" --uniqueid "$patchid" --runtime "$_G_root/steamapps/common/$_C_rtname" --prefix "$_G_root/steamapps/compatdata/$_C_pfxname" --steam-dir "$_G_root")
 | |
| }
 | |
| 
 | |
| sanity_check() {
 | |
|   # IIDX
 | |
|   [ -n "$_G_style" ] || abort "INVALID STYLE"
 | |
|   [ -n "$_G_dumps" ] || _G_dumps="$_G_root/steamapps/common"
 | |
|   [ -d "$_G_dumps" ] || abort "INVALID PATH"
 | |
| 
 | |
|   # Proton
 | |
|   [ -d "$_G_root/steamapps/common" ] || abort "INVALID PATH"
 | |
|   [ -d "$_G_root/steamapps/compatdata" ] || abort "INVALID PATH"
 | |
| 
 | |
|   # Manual features warnings
 | |
|   [ -z ${__GL_SYNC_DISPLAY_DEVICE+x} ] || echo "[WARN] User envar override detected, if game runs at wrong framerates, try unsetting __GL_SYNC_DISPLAY_DEVICE"
 | |
| 
 | |
|   # Test only
 | |
|   echo "_G_display: $_G_display"
 | |
|   echo "_G_refresh_rate: $_G_refresh_rate"
 | |
|   echo "_G_fsr: $_G_fsr"
 | |
|   echo "_G_url: $_G_url"
 | |
|   echo "_G_pcbid: $_G_pcbid"
 | |
|   echo "_G_hooks[${#_G_hooks[@]}]: ${_G_hooks[*]}"
 | |
|   echo "_G_devel[${#_G_devel[@]}]: ${_G_devel[*]}"
 | |
| 
 | |
| }
 | |
| 
 | |
| # Set-up environment
 | |
| arg_parse "${@}"
 | |
| cfg_parse "${@}"
 | |
| sanity_check
 | |
| 
 | |
| # command generation
 | |
| gen_base
 | |
| gen_args
 | |
| 
 | |
| #abort "__GL_SYNC_DISPLAY_DEVICE=$__GL_SYNC_DISPLAY_DEVICE ${_G_ep[*]}"
 | |
| exec "${_G_ep[@]}"
 |