#!/usr/bin/env bash # HoMM Olden Era / Steam / GOG в bottle Whisky. См. docs/games/WINE_APPS_LAUNCH_RUNBOOK.md set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # shellcheck source=config.sh source "${SCRIPT_DIR}/config.sh" HOMM_DISK_MIN_GB="${HOMM_DISK_MIN_GB:-3}" HOMM_DISK_WARN_PCT="${HOMM_DISK_WARN_PCT:-10}" usage() { cat < play — игра (DXVK + Unity windowed; Steam Logged On) game — то же, явный запуск exe steam-ui — Steam с окном (CEF + шрифты) steam — Steam -silent (без UI) reset — wineboot -k, убить Steam/GOG/игру в bottle dxvk — установить DXVK-macOS в bottle disk — свободное место на диске saves — открыть папку сейвов в Finder fix-steam-ui — шрифты + dwrite для пустого текста Steam fix-gog-auth — сеть Wine для GOG login diagnose — общая диагностика (mouse) diagnose-steam-ui diagnose-saves diagnose-gog-auth input — WineBus SDL (мышь/геймпад) gog-install — GOG Galaxy 2.0 в bottle gog — запустить GOG Galaxy gog-restart — убить GOG и запустить снова gog-bottle / gog-use-bottle / gog-setup / gog-corefonts / gog-game-install / gog-reset — GOG в отдельном bottle (см. scripts/games/gog/README.md) wine-setup — HoMM/Steam bottle (не GOG) whisky — открыть Whisky.app help — эта справка (-h / --help) EOF } cmd_wine_check() { if [[ ! -x "${WINE64}" ]]; then echo "Нет WhiskyWine: bash ${SCRIPT_DIR}/install-whiskywine-manual.sh" exit 1 fi } cmd_disk_guard() { local min_kb free_kb pct min_kb=$((HOMM_DISK_MIN_GB * 1024 * 1024)) free_kb="$(homm_disk_free_kb)" pct="$(homm_disk_free_pct)" if [[ "${free_kb}" -lt "${min_kb}" ]]; then echo "❌ Мало места (<${HOMM_DISK_MIN_GB} GB свободно на Data)." return 1 fi if [[ "${pct}" -lt "${HOMM_DISK_WARN_PCT}" ]]; then echo "⚠️ Диск заполнен ~${pct}% свободно — риск зависаний CEF/Wine." fi return 0 } cmd_disk() { df -h "$(homm_data_volume_path)" | tail -1 echo "Свободно: $(homm_disk_free_pct)% ($(homm_disk_free_kb) KB)" } homm_steam_cef_args_array() { local _IFS=$IFS IFS=' ' # shellcheck disable=SC2206 local -a _a=(${HOMM_STEAM_CEF_ARGS}) IFS=$_IFS printf '%s\n' "${_a[@]}" } homm_unity_args_array() { local -a args=() local w h if [[ "${HOMM_UNITY_WINDOWED:-1}" == "1" ]]; then IFS='x' read -r w h <<< "${HOMM_VDESKTOP:-1920x1080}" args+=(-screen-fullscreen 0 -screen-width="${w}" -screen-height="${h}") fi if [[ -n "${HOMM_UNITY_EXTRA:-}" ]]; then local _IFS=$IFS IFS=' ' # shellcheck disable=SC2206 local -a extra=(${HOMM_UNITY_EXTRA}) IFS=$_IFS args+=("${extra[@]}") fi if [[ ${#args[@]} -eq 0 ]]; then return 0 fi printf '%s\n' "${args[@]}" } cmd_reset() { cmd_wine_check export_wine_env echo "wineboot -k…" "${WINE64}" wineboot -k 2>/dev/null || true pkill -f 'HeroesOldenEra\.exe' 2>/dev/null || true pkill -f 'Steam\.exe' 2>/dev/null || true pkill -f 'steamwebhelper' 2>/dev/null || true pkill -f 'SteamService' 2>/dev/null || true pkill -f 'GalaxyClient' 2>/dev/null || true pkill -f 'GOG_Galaxy' 2>/dev/null || true pkill -f 'GalaxyWebInstaller' 2>/dev/null || true pkill -f 'explorer.exe /desktop' 2>/dev/null || true sleep 1 echo "OK: процессы bottle остановлены." } cmd_steam_ui() { local -a cef_args=() cmd_wine_check cmd_disk_guard || exit 1 homm_kill_mac_steam || true homm_warn_mac_steam || true export_wine_env_steam mapfile -t cef_args < <(homm_steam_cef_args_array) echo "Запуск Steam UI (Cmd+Tab → wine64 / Steam)…" if [[ "${HOMM_STEAM_VDESKTOP:-0}" == "1" ]]; then local w h IFS='x' read -r w h <<< "${HOMM_VDESKTOP:-1920x1080}" exec "${WINE64}" explorer "/desktop=SteamUI,${w}x${h}" "${STEAM_EXE}" "${cef_args[@]}" else exec "${WINE64}" "${STEAM_EXE}" "${cef_args[@]}" fi } cmd_steam() { cmd_wine_check export_wine_env_steam echo "Steam -silent…" exec "${WINE64}" "${STEAM_EXE}" -silent } cmd_game() { local -a unity=() cmd_wine_check cmd_disk_guard || exit 1 if [[ ! -f "${HOMM_EXE}" ]]; then echo "Нет ${HOMM_EXE} — установите Olden Era в Steam UI." exit 1 fi if [[ "${HOMM_GAME_FORCE:-0}" != "1" ]] && ! homm_steam_ready_for_game; then echo "❌ Steam не Logged On или не запущен." echo " bash ${SCRIPT_DIR}/start.sh steam-ui → login → play" exit 1 fi mapfile -t unity < <(homm_unity_args_array) export_wine_env_game echo "Запуск HoMM (DXVK + windowed)…" if [[ "${HOMM_USE_WINEVDESKTOP:-1}" == "1" && "${HOMM_LAUNCH_DIRECT:-0}" != "1" ]]; then local w h IFS='x' read -r w h <<< "${HOMM_VDESKTOP:-1920x1080}" exec "${WINE64}" explorer "/desktop=HoMM,${w}x${h}" "${HOMM_EXE}" "${unity[@]}" else exec "${WINE64}" "${HOMM_EXE}" "${unity[@]}" fi } cmd_play() { if homm_steam_ready_for_game; then cmd_game else homm_warn_mac_steam || true echo "Нужен Steam Logged On. Сначала steam-ui, затем снова play." if ! homm_wine_steam_running; then cmd_steam_ui else exit 1 fi fi } cmd_saves() { local sp sp="$(find "${BOTTLE_DIR}/drive_c/users" -type d -path '*/Unfrozen/HeroesOldenEra/users/*/saves/singleplayer' 2>/dev/null | head -1)" if [[ -z "${sp}" ]]; then echo "Папка сейвов не найдена — запустите игру хотя бы раз." exit 1 fi open "${sp}" echo "${sp}" } cmd_gog() { local gog_exe win_exe local -a cef_args=() cmd_wine_check cmd_disk_guard || exit 1 if ! gog_exe="$(homm_gog_exe)"; then echo "GOG не установлен: bash ${SCRIPT_DIR}/start.sh gog-install" exit 1 fi if homm_wine_gog_running; then echo "GOG уже запущен (Cmd+Tab → GalaxyClient)." exit 0 fi homm_kill_mac_steam || true export_wine_env_gog mapfile -t cef_args < <(homm_gog_cef_args_array) if [[ -x "${WINEPATH_BIN}" ]]; then win_exe="$("${WINEPATH_BIN}" -w "${gog_exe}")" else win_exe='C:\Program Files (x86)\GOG Galaxy\GalaxyClient.exe' fi echo "Запуск GOG Galaxy…" nohup "${WINE64}" "${win_exe}" "${cef_args[@]}" >/tmp/gog-launch.log 2>&1 & sleep 3 if homm_wine_gog_running; then echo "OK: Cmd+Tab → GalaxyClient" else echo "Не стартовал — tail /tmp/gog-launch.log" tail -10 /tmp/gog-launch.log 2>/dev/null || true exit 1 fi } cmd_gog_restart() { pkill -f 'GalaxyClient' 2>/dev/null || true pkill -f 'GOG Galaxy' 2>/dev/null || true sleep 2 cmd_gog } cmd_whisky() { open -a Whisky 2>/dev/null || open -a "Whisky.app" 2>/dev/null || { echo "Установите Whisky (franke) или откройте bottle вручную." exit 1 } } main() { local cmd="${1:-steam}" shift || true case "${cmd}" in -h|--help|help) usage; exit 0 ;; play) cmd_play "$@" ;; game) cmd_game "$@" ;; steam-ui) cmd_steam_ui "$@" ;; steam) cmd_steam "$@" ;; reset) cmd_reset "$@" ;; dxvk) exec bash "${SCRIPT_DIR}/install-dxvk-macos.sh" "$@" ;; disk) cmd_disk "$@" ;; saves) cmd_saves "$@" ;; fix-steam-ui) exec bash "${SCRIPT_DIR}/fix-homm-steam-ui.sh" "$@" ;; fix-gog-auth) exec bash "${SCRIPT_DIR}/fix-gog-auth.sh" "$@" ;; diagnose) exec bash "${SCRIPT_DIR}/diagnose-homm-mouse.sh" "$@" ;; diagnose-steam-ui) exec bash "${SCRIPT_DIR}/diagnose-homm-steam-ui.sh" "$@" ;; diagnose-saves) exec bash "${SCRIPT_DIR}/diagnose-homm-saves.sh" "$@" ;; diagnose-gog-auth) exec bash "${SCRIPT_DIR}/diagnose-gog-auth.sh" "$@" ;; input) exec bash "${SCRIPT_DIR}/fix-homm-wine-input.sh" "$@" ;; gog-install) exec bash "${SCRIPT_DIR}/install-gog-galaxy.sh" "$@" ;; gog) cmd_gog "$@" ;; gog-restart) cmd_gog_restart "$@" ;; gog-bottle) exec bash "${SCRIPT_DIR}/gog/list-bottles.sh" "$@" ;; gog-use-bottle) shift; exec bash "${SCRIPT_DIR}/gog/use-bottle.sh" "$@" ;; gog-setup) exec bash "${SCRIPT_DIR}/gog/setup-bottle.sh" "$@" ;; gog-install-all) shift; exec bash "${SCRIPT_DIR}/gog/install-all.sh" "$@" ;; gog-gdiplus) exec bash "${SCRIPT_DIR}/gog/install-gdiplus.sh" "$@" ;; gog-create-bottle) exec bash "${SCRIPT_DIR}/gog/create-bottle.sh" "$@" ;; gog-corefonts) exec bash "${SCRIPT_DIR}/gog/install-corefonts.sh" "$@" ;; gog-game-install) exec bash "${SCRIPT_DIR}/gog/run-web-installer.sh" "$@" ;; gog-reset) exec bash "${SCRIPT_DIR}/gog/reset.sh" "$@" ;; wine-setup) exec bash "${SCRIPT_DIR}/setup-wine-bottle.sh" "$@" ;; wine-corefonts) echo "Для GOG: bash ${SCRIPT_DIR}/gog/install-corefonts.sh"; exit 1 ;; whisky) cmd_whisky "$@" ;; "") cmd_steam "$@" ;; *) echo "Неизвестная команда: ${cmd}" usage exit 1 ;; esac } main "$@"