249 lines
8.2 KiB
PowerShell
249 lines
8.2 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[ValidateSet('Debug', 'Release')]
|
|
[string]$Configuration = 'Release',
|
|
[string]$ClientPath = '',
|
|
[string]$PackagePath = '',
|
|
[switch]$Deploy,
|
|
[switch]$NativeRenderer
|
|
)
|
|
|
|
function Enable-LargeAddressAware {
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]$Path
|
|
)
|
|
|
|
$bytes = [System.IO.File]::ReadAllBytes($Path)
|
|
|
|
if ($bytes.Length -lt 64) {
|
|
throw "File is too small to be a valid PE executable: $Path"
|
|
}
|
|
|
|
# DOS header: MZ
|
|
if ($bytes[0] -ne 0x4D -or $bytes[1] -ne 0x5A) {
|
|
throw "DOS MZ signature was not found: $Path"
|
|
}
|
|
|
|
# IMAGE_DOS_HEADER.e_lfanew
|
|
$peOffset = [BitConverter]::ToInt32($bytes, 0x3C)
|
|
|
|
if ($peOffset -lt 0 -or ($peOffset + 24) -gt $bytes.Length) {
|
|
throw "Invalid PE header offset in: $Path"
|
|
}
|
|
|
|
# PE\0\0 signature
|
|
$peSignature = [BitConverter]::ToUInt32($bytes, $peOffset)
|
|
|
|
if ($peSignature -ne 0x00004550) {
|
|
throw "PE signature was not found: $Path"
|
|
}
|
|
|
|
# IMAGE_FILE_HEADER starts after the 4-byte PE signature.
|
|
# Characteristics is at offset 18 inside IMAGE_FILE_HEADER.
|
|
$characteristicsOffset = $peOffset + 4 + 18
|
|
$characteristics = [BitConverter]::ToUInt16(
|
|
$bytes,
|
|
$characteristicsOffset
|
|
)
|
|
|
|
$newCharacteristics = [uint16](
|
|
$characteristics -bor 0x0020
|
|
)
|
|
|
|
if ($newCharacteristics -ne $characteristics) {
|
|
$encoded = [BitConverter]::GetBytes($newCharacteristics)
|
|
|
|
$bytes[$characteristicsOffset] = $encoded[0]
|
|
$bytes[$characteristicsOffset + 1] = $encoded[1]
|
|
|
|
[System.IO.File]::WriteAllBytes($Path, $bytes)
|
|
|
|
Write-Host (
|
|
"LAA enabled for {0}: 0x{1:X4} -> 0x{2:X4}" -f
|
|
$Path,
|
|
$characteristics,
|
|
$newCharacteristics
|
|
) -ForegroundColor Green
|
|
}
|
|
else {
|
|
Write-Host (
|
|
"LAA is already enabled for {0}: 0x{1:X4}" -f
|
|
$Path,
|
|
$characteristics
|
|
) -ForegroundColor DarkGreen
|
|
}
|
|
}
|
|
|
|
function Initialize-MissingSubmodules {
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]$RepositoryPath
|
|
)
|
|
|
|
$modulesFile = Join-Path $RepositoryPath '.gitmodules'
|
|
if (-not (Test-Path -LiteralPath $modulesFile -PathType Leaf)) {
|
|
return
|
|
}
|
|
|
|
$configuredModules = @(
|
|
& git -C $RepositoryPath config --file .gitmodules --get-regexp '^submodule\..*\.path$'
|
|
)
|
|
if ($LASTEXITCODE -gt 1) {
|
|
throw "Failed to read submodules from $modulesFile"
|
|
}
|
|
|
|
foreach ($configuredModule in $configuredModules) {
|
|
$fields = $configuredModule -split '\s+', 2
|
|
if ($fields.Count -ne 2) {
|
|
continue
|
|
}
|
|
|
|
$relativePath = $fields[1].Trim()
|
|
$modulePath = Join-Path $RepositoryPath $relativePath
|
|
$gitMarker = Join-Path $modulePath '.git'
|
|
|
|
# Updating an initialized submodule checks out the SHA recorded by its parent and detaches
|
|
# any active development branch. Only initialize genuinely missing worktrees.
|
|
if (-not (Test-Path -LiteralPath $gitMarker)) {
|
|
Write-Host "Initializing missing submodule: $modulePath"
|
|
& git -C $RepositoryPath submodule update --init -- $relativePath
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to initialize submodule: $modulePath"
|
|
}
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $modulePath -PathType Container) {
|
|
Initialize-MissingSubmodules -RepositoryPath $modulePath
|
|
}
|
|
}
|
|
}
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$repoRoot = $PSScriptRoot
|
|
. (Join-Path $repoRoot 'pipeline-layout.ps1')
|
|
$cmake = 'C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe'
|
|
if (-not (Test-Path -LiteralPath $cmake)) {
|
|
$cmakeCommand = Get-Command cmake -ErrorAction SilentlyContinue
|
|
if (-not $cmakeCommand) { throw 'CMake 3.20+ was not found.' }
|
|
$cmake = $cmakeCommand.Source
|
|
}
|
|
|
|
Initialize-MissingSubmodules -RepositoryPath $repoRoot
|
|
|
|
$win32BuildDir = Join-Path $repoRoot 'build\warcraftxl-win32'
|
|
$stockExe = Join-Path $repoRoot 'Wow_Original.exe'
|
|
|
|
if ($Deploy -or -not [string]::IsNullOrWhiteSpace($PackagePath)) {
|
|
if (-not (Test-Path -LiteralPath $stockExe -PathType Leaf)) {
|
|
throw "Stock executable was not found: $stockExe"
|
|
}
|
|
$expectedStockHash = 'AA63A5750D60EF16746C686B3D5E26876D98953EAB08B1C026CD0FAF78E88CB8'
|
|
$stockHash = (Get-FileHash -LiteralPath $stockExe -Algorithm SHA256).Hash
|
|
if ($stockHash -ne $expectedStockHash) {
|
|
throw "Wow_Original.exe is not the verified MoonWell 3.3.5a stock executable (SHA-256 $stockHash)."
|
|
}
|
|
}
|
|
|
|
& $cmake -S $repoRoot -B $win32BuildDir -A Win32 '-DCLIENT_PATH='
|
|
if ($LASTEXITCODE -ne 0) { throw 'WarcraftXL Win32 configure failed.' }
|
|
& $cmake --build $win32BuildDir --config $Configuration --parallel 4
|
|
if ($LASTEXITCODE -ne 0) { throw 'WarcraftXL Win32 build failed.' }
|
|
|
|
$win32ArtifactDir = Join-Path $win32BuildDir "vendor\warcraftxl\$Configuration"
|
|
$warcraftXL = Join-Path $win32ArtifactDir 'WarcraftXL.dll'
|
|
$selectedProxy = Join-Path $win32ArtifactDir 'd3d9.dll'
|
|
$recoveryProxy = Join-Path $win32BuildDir "artifacts\$Configuration\d3d9-native.dll"
|
|
$extensionNames = Get-MoonWellExtensionNames
|
|
|
|
if ($NativeRenderer) {
|
|
Write-Warning '-NativeRenderer is no longer needed: WarcraftXL 1.1 uses native D3D9 by default.'
|
|
}
|
|
|
|
function Install-WarcraftXLArtifacts {
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]$Destination
|
|
)
|
|
|
|
Remove-MoonWellRuntimeGarbage -Destination $Destination
|
|
|
|
$utils = Join-Path $Destination 'Utils'
|
|
New-Item `
|
|
-ItemType Directory `
|
|
-Path $Destination, $utils `
|
|
-Force |
|
|
Out-Null
|
|
|
|
$destinationExe = Join-Path $Destination 'Wow.exe'
|
|
|
|
Copy-Item `
|
|
-LiteralPath $stockExe `
|
|
-Destination $destinationExe `
|
|
-Force
|
|
|
|
Enable-LargeAddressAware -Path $destinationExe
|
|
|
|
Copy-Item `
|
|
-LiteralPath $warcraftXL `
|
|
-Destination (Join-Path $Destination 'WarcraftXL.dll') `
|
|
-Force
|
|
|
|
Copy-Item `
|
|
-LiteralPath $selectedProxy `
|
|
-Destination (Join-Path $Destination 'd3d9.dll') `
|
|
-Force
|
|
|
|
Copy-Item `
|
|
-LiteralPath $recoveryProxy `
|
|
-Destination (Join-Path $utils 'd3d9-native.dll') `
|
|
-Force
|
|
|
|
foreach ($extensionName in $extensionNames) {
|
|
$extensionSource = Join-Path $win32BuildDir "$Configuration\$extensionName.dll"
|
|
$extensionDestination = Join-Path $Destination "Extensions\$extensionName"
|
|
New-Item -ItemType Directory -Path $extensionDestination -Force | Out-Null
|
|
Copy-Item `
|
|
-LiteralPath $extensionSource `
|
|
-Destination (Join-Path $extensionDestination "$extensionName.dll") `
|
|
-Force
|
|
}
|
|
}
|
|
|
|
if ($Deploy) {
|
|
if ([string]::IsNullOrWhiteSpace($ClientPath)) {
|
|
$ClientPath = 'C:\Program Files (x86)\World of Warcraft'
|
|
}
|
|
$ClientPath = [System.IO.Path]::GetFullPath($ClientPath)
|
|
if (-not (Test-Path -LiteralPath $ClientPath -PathType Container)) {
|
|
throw "Client directory was not found: $ClientPath"
|
|
}
|
|
|
|
$clientExe = Join-Path $ClientPath 'Wow.exe'
|
|
if (Test-Path -LiteralPath $clientExe -PathType Leaf) {
|
|
$clientHash = (Get-FileHash -LiteralPath $clientExe -Algorithm SHA256).Hash
|
|
if ($clientHash -ne $stockHash) {
|
|
$legacyBackup = Join-Path $ClientPath 'Wow.moonwell-patched.backup.exe'
|
|
if (-not (Test-Path -LiteralPath $legacyBackup)) {
|
|
Copy-Item -LiteralPath $clientExe -Destination $legacyBackup
|
|
}
|
|
Write-Host "Legacy executable saved as $legacyBackup"
|
|
}
|
|
}
|
|
|
|
Install-WarcraftXLArtifacts -Destination $ClientPath
|
|
Write-Host "Installed WarcraftXL 1.1 runtime and extensions in $ClientPath"
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($PackagePath)) {
|
|
$PackagePath = [System.IO.Path]::GetFullPath($PackagePath)
|
|
Assert-SafePackagePath `
|
|
-PackagePath $PackagePath `
|
|
-RepositoryPath $repoRoot `
|
|
-ClientPath $ClientPath
|
|
Install-WarcraftXLArtifacts -Destination $PackagePath
|
|
Write-Host "Packaged stock Wow.exe and WarcraftXL artifacts in $PackagePath"
|
|
}
|
|
|
|
Write-Host "WarcraftXL 1.1 build complete (native D3D9 proxy): $win32ArtifactDir"
|