#!/bin/bash
# Paratext 9.5 on macOS (Apple Silicon) with Wine Staging 11.x
# From https://ryder.dev/paratext-on-mac-with-wine
#
# What I tested this on (Sept-Nov 2026):
#   macOS 26.5, Apple Silicon (M5), Rosetta 2
#   Wine Staging 11.10 (the Gcenx build, installed at /Applications/Wine Staging.app)
#   Paratext 9.5.110.1 online installer, which then updated itself to 9.5.110.9 inside Wine
#
# You need:
#   - /Applications/Wine Staging.app   (https://github.com/Gcenx/macOS_Wine_builds/releases)
#   - Rosetta 2                        (softwareupdate --install-rosetta)
#   - Xcode Command Line Tools         (xcode-select --install) - only to compile cabextract
#   - The Paratext installer .exe      (from registry.paratext.org, after you register)
#
# Usage:
#   bash setup-paratext-wine.sh ~/Downloads/Paratext_9.5.110.1_InstallerOnline.exe
#   bash setup-paratext-wine.sh --quiet <installer>   # silent install; this ACCEPTS the
#                                                     # Paratext license for you, so read it first
#
# It builds a separate Wine prefix (~/.wine-paratext by default) and does not touch ~/.wine.
# Downloads come straight from Microsoft, winetricks (GitHub) and cabextract.org.uk.
# Nothing here is redistributed.

set -uo pipefail

QUIET=0
if [ "${1:-}" = "--quiet" ]; then QUIET=1; shift; fi
INSTALLER="${1:-}"
[ -f "$INSTALLER" ] || { echo "usage: $0 [--quiet] /path/to/Paratext_..._Installer*.exe"; exit 1; }

WORK="${WORK:-$HOME/paratext-wine}"
export WINEPREFIX="${WINEPREFIX:-$HOME/.wine-paratext}"
export WINEBIN="/Applications/Wine Staging.app/Contents/Resources/wine/bin"
export PATH="$WORK/bin:$WINEBIN:/usr/bin:/bin:/usr/sbin:/sbin"
export WINEARCH=win64 WINEDEBUG=-all MVK_CONFIG_LOG_LEVEL=0
W_CACHE="$HOME/.cache/winetricks"
W="$WINEPREFIX/drive_c/windows"
CV='HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
NETK='HKLM\Software\Microsoft\.NETFramework'

die()  { echo; echo "!! $*"; exit 1; }
step() { echo; echo "==> $*"; }

[ -x "$WINEBIN/wine" ] || die "Wine Staging not found at $WINEBIN"
/usr/bin/pgrep -q oahd || echo "warning: Rosetta does not seem to be running (softwareupdate --install-rosetta)"
[ -e "$WINEPREFIX" ] && die "$WINEPREFIX already exists. Move it away, or set WINEPREFIX to a new path."
mkdir -p "$WORK/bin"

set_win10() {
  winetricks -q win10 >/dev/null 2>&1
  # Wine doesn't set ReleaseId, so Paratext's installer decides you're on an old
  # Windows 10 and stops with "Paratext requires Windows 10 1607 or later".
  for v in 64 32; do
    wine reg add "$CV" /v ReleaseId      /t REG_SZ /d 2009 /f /reg:$v >/dev/null
    wine reg add "$CV" /v DisplayVersion /t REG_SZ /d 22H2 /f /reg:$v >/dev/null
  done
}

step "1/8 tools: winetricks + cabextract (no Homebrew needed)"
if [ ! -x "$WORK/bin/winetricks" ]; then
  curl -fsSL -o "$WORK/bin/winetricks" https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks || die "could not download winetricks"
  chmod +x "$WORK/bin/winetricks"
fi
if ! command -v cabextract >/dev/null; then
  ( cd "$WORK" && curl -fsSL https://www.cabextract.org.uk/cabextract-1.11.tar.gz | tar xz \
    && cd cabextract-1.11 && ./configure >/dev/null && make >/dev/null 2>&1 && cp cabextract "$WORK/bin/" ) \
    || die "could not build cabextract (is xcode-select --install done?)"
fi

step "2/8 new Wine prefix at $WINEPREFIX (without Wine Mono/Gecko)"
WINEDLLOVERRIDES="mscoree,mshtml=" wine wineboot --init >/dev/null 2>&1
wineserver -w
[ -d "$WINEPREFIX/drive_c" ] || die "prefix was not created"

step "3/8 download the .NET 4.8 and 4.0 installers from Microsoft (cached)"
mkdir -p "$W_CACHE/dotnet48" "$W_CACHE/dotnet40"
N48="$W_CACHE/dotnet48/ndp48-x86-x64-allos-enu.exe"
N40="$W_CACHE/dotnet40/dotNetFx40_Full_x86_x64.exe"
[ -f "$N48" ] || curl -fL -o "$N48" https://download.visualstudio.microsoft.com/download/pr/7afca223-55d2-470a-8edc-6a1739ae3252/abd170b4b0ec15ad0222a809b761a036/ndp48-x86-x64-allos-enu.exe || die ".NET 4.8 download failed"
[ -f "$N40" ] || curl -fL -o "$N40" https://download.microsoft.com/download/9/5/A/95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE/dotNetFx40_Full_x86_x64.exe || die ".NET 4.0 download failed"

step "4/8 install .NET 4.8 in Windows 7 mode (5-10 min)"
# On 'Windows 10' the installer thinks 4.8 is built into the OS and exits without installing.
# Also remove Wine's placeholder mscoree.dll files; the real one comes in step 5.
rm -f "$W/system32/mscoree.dll" "$W/syswow64/mscoree.dll"
winetricks -q win7 >/dev/null 2>&1
wine "$N48" /q /norestart >/dev/null 2>&1
wineserver -w
wine reg query 'HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' /v Release 2>/dev/null | grep -q 0x80e \
  || die ".NET 4.8 did not install (no Release key)"

step "5/8 copy the real mscoree.dll out of the .NET 4.0 package"
# The 4.8 package doesn't ship mscoree.dll (Windows normally provides it). The 4.0
# installer crashes under Rosetta ("unsupported privilege level"), so we only extract it.
wine "$N40" /x:'C:\ndp40x' /q >/dev/null 2>&1
wineserver -w
X="$WINEPREFIX/drive_c/ndp40x"
mkdir -p "$X/core"
cabextract -q -d "$X/core" "$X/netfx_Core.mzz" >/dev/null 2>&1
[ -f "$X/core/_003_mscoree_dll_amd64" ] || die "could not find mscoree.dll in the .NET 4.0 package"
cp "$X/core/_003_mscoree_dll_amd64" "$W/system32/mscoree.dll"
cp "$X/core/_003_mscoree_dll_x86"   "$W/syswow64/mscoree.dll"
wine reg add 'HKCU\Software\Wine\DllOverrides' /v mscoree /t REG_SZ /d native /f >/dev/null

step "6/8 registry keys that tell mscoree where .NET lives"
wine reg add "$NETK" /v InstallRoot /t REG_SZ /d 'C:\windows\Microsoft.NET\Framework64\' /f /reg:64 >/dev/null
wine reg add "$NETK" /v InstallRoot /t REG_SZ /d 'C:\windows\Microsoft.NET\Framework\'   /f /reg:32 >/dev/null
for v in 64 32; do
  wine reg add "$NETK\Policy\v4.0" /v 30319 /t REG_SZ /d 30319-30319 /f /reg:$v >/dev/null
  wine reg add "$NETK\Policy\v2.0" /v 50727 /t REG_SZ /d 50727-50727 /f /reg:$v >/dev/null
done

step "7/8 back to Windows 10 (+ ReleaseId)"
set_win10
wineserver -w

step "8/8 Paratext installer"
mkdir -p "$WINEPREFIX/drive_c/install"
cp "$INSTALLER" "$WINEPREFIX/drive_c/install/"
cd "$WINEPREFIX/drive_c/install" || die "no install folder"
if [ "$QUIET" = 1 ]; then
  wine "$(basename "$INSTALLER")" /quiet /norestart -l 'C:\install\paratext-install.log' >/dev/null 2>&1
else
  echo "The installer window opens now (it can open behind other windows)."
  echo "Read and accept the license, click Install, and close it when it's done."
  wine "$(basename "$INSTALLER")" -l 'C:\install\paratext-install.log' >/dev/null 2>&1
fi
wineserver -w

PT="$WINEPREFIX/drive_c/Program Files/Paratext 9/Paratext.exe"
[ -f "$PT" ] || die "Paratext did not install. Logs: $WINEPREFIX/drive_c/install/ (read the *_PtMsiPack.log first)"

LAUNCH="$WORK/paratext.sh"
cat > "$LAUNCH" <<EOF
#!/bin/bash
# Start Paratext 9 under Wine
export WINEPREFIX="$WINEPREFIX" WINEDEBUG=-all MVK_CONFIG_LOG_LEVEL=0
cd "$WINEPREFIX/drive_c/Program Files/Paratext 9"
exec "$WINEBIN/wine" Paratext.exe
EOF
chmod +x "$LAUNCH"

echo
echo "Done. Start Paratext with:  bash $LAUNCH"
echo "Paratext's own log:         $WINEPREFIX/drive_c/users/$USER/AppData/Local/Paratext95/ParatextLog.log"
