diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5012f36 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Shell scripts must stay LF or bash on the CI runner chokes on \r. +*.sh text eol=lf diff --git a/.github/scripts/configure-simplysign.ps1 b/.github/scripts/configure-simplysign.ps1 new file mode 100644 index 0000000..14ccafb --- /dev/null +++ b/.github/scripts/configure-simplysign.ps1 @@ -0,0 +1,28 @@ +# Pre-configure SimplySign Desktop for unattended use: +# - auto-show the login dialog on launch (so the TOTP keystrokes have a target) +# - cache the smart-card PIN in the CSP, so signtool signs without a per-call +# PIN prompt, and forget it again when the session disconnects. +# Mirrors the settings used by the blinkdisk / docuscope CI signing setups. + +$ErrorActionPreference = "Stop" + +$RegistryPath = "HKCU:\Software\Certum\SimplySign" + +$settings = [ordered]@{ + ShowLoginDialogOnStart = 1 + ShowLoginDialogOnAppRequest = 1 + RememberLastUserName = 0 + Autostart = 0 + UnregisterCertificatesOnDisconnect = 1 + RememberPINinCSP = 1 + ForgetPINinCSPonDisconnect = 1 + LangID = 9 +} + +Write-Host "=== Configuring SimplySign Desktop registry ===" +New-Item -Path $RegistryPath -Force | Out-Null +foreach ($name in $settings.Keys) { + Set-ItemProperty -Path $RegistryPath -Name $name -Value $settings[$name] -Type DWord + Write-Host " $name = $($settings[$name])" +} +Write-Host "Done." diff --git a/.github/scripts/connect-simplysign.ps1 b/.github/scripts/connect-simplysign.ps1 new file mode 100644 index 0000000..2d312ed --- /dev/null +++ b/.github/scripts/connect-simplysign.ps1 @@ -0,0 +1,245 @@ +# Authenticate SimplySign Desktop non-interactively. +# +# Certum's SimplySign cloud has no headless login: the certificate only reaches +# the Windows store after the GUI client authenticates. So we generate the +# current TOTP from the otpauth:// secret (CERTUM_OTP_URI) and paste the +# credentials into the login dialog. This needs an interactive desktop session, +# which GitHub-hosted Windows runners provide. +# +# Based on https://www.devas.life/how-to-automate-signing-your-windows-app-with-certum/ +# and the refinements in blinkdisk's connect-simplysign.ps1. + +param( + [string]$OtpUri = $env:CERTUM_OTP_URI, + [string]$UserId = $env:CERTUM_USERID, + [string]$ExePath = $env:CERTUM_EXE_PATH +) + +if (-not $OtpUri) { Write-Host "ERROR: CERTUM_OTP_URI not provided"; exit 1 } +if (-not $UserId) { Write-Host "ERROR: CERTUM_USERID not provided"; exit 1 } +if (-not $ExePath) { + $ExePath = "C:\Program Files\Certum\SimplySign Desktop\SimplySignDesktop.exe" +} + +Write-Host "=== SimplySign Desktop TOTP authentication ===" +if (-not (Test-Path $ExePath)) { + Write-Host "ERROR: SimplySign Desktop not found at $ExePath" + exit 1 +} + +# --- Parse the otpauth:// URI (works on PowerShell 5.1 and 7+) --------------- +$uri = [Uri]$OtpUri +try { + $q = [System.Web.HttpUtility]::ParseQueryString($uri.Query) +} catch { + $q = @{} + foreach ($part in $uri.Query.TrimStart('?') -split '&') { + $kv = $part -split '=', 2 + if ($kv.Count -eq 2) { $q[$kv[0]] = [Uri]::UnescapeDataString($kv[1]) } + } +} + +$Base32 = $q['secret'] +$Digits = if ($q['digits']) { [int]$q['digits'] } else { 6 } +$Period = if ($q['period']) { [int]$q['period'] } else { 30 } +$Algorithm = if ($q['algorithm']) { $q['algorithm'].ToUpper() } else { 'SHA256' } + +if (-not $Base32) { Write-Host "ERROR: otpauth URI has no 'secret'"; exit 1 } +if ($Algorithm -notin @('SHA1', 'SHA256', 'SHA512')) { + Write-Host "ERROR: unsupported TOTP algorithm: $Algorithm" + exit 1 +} + +# --- TOTP generator (RFC 6238), inline C# so there are no dependencies -------- +Add-Type -Language CSharp @" +using System; +using System.Security.Cryptography; + +public static class Totp +{ + private const string B32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + + private static byte[] Base32Decode(string s) + { + s = s.TrimEnd('=').ToUpperInvariant(); + byte[] bytes = new byte[s.Length * 5 / 8]; + int bitBuffer = 0, bitsLeft = 0, idx = 0; + foreach (char c in s) + { + int val = B32.IndexOf(c); + if (val < 0) throw new ArgumentException("Invalid Base32 char: " + c); + bitBuffer = (bitBuffer << 5) | val; + bitsLeft += 5; + if (bitsLeft >= 8) { bytes[idx++] = (byte)(bitBuffer >> (bitsLeft - 8)); bitsLeft -= 8; } + } + return bytes; + } + + private static HMAC Hmac(string algorithm, byte[] key) + { + switch (algorithm.ToUpper()) + { + case "SHA1": return new HMACSHA1(key); + case "SHA256": return new HMACSHA256(key); + case "SHA512": return new HMACSHA512(key); + default: throw new ArgumentException("Unsupported algorithm: " + algorithm); + } + } + + public static string Now(string secret, int digits, int period, string algorithm) + { + byte[] key = Base32Decode(secret); + long counter = DateTimeOffset.UtcNow.ToUnixTimeSeconds() / period; + byte[] cnt = BitConverter.GetBytes(counter); + if (BitConverter.IsLittleEndian) Array.Reverse(cnt); + + byte[] hash; + using (var h = Hmac(algorithm, key)) { hash = h.ComputeHash(cnt); } + + int offset = hash[hash.Length - 1] & 0x0F; + int binary = + ((hash[offset] & 0x7F) << 24) | + ((hash[offset + 1] & 0xFF) << 16) | + ((hash[offset + 2] & 0xFF) << 8) | + (hash[offset + 3] & 0xFF); + int otp = binary % (int)Math.Pow(10, digits); + return otp.ToString(new string('0', digits)); + } +} +"@ + +function Find-UiByName($el, $walker, $name) { + $c = $walker.GetFirstChild($el) + while ($c) { + if ($c.Current.Name -eq $name) { return $c } + $r = Find-UiByName $c $walker $name + if ($r) { return $r } + $c = $walker.GetNextSibling($c) + } + return $null +} + +# An outdated SimplySign build pops a modal "New version found - download?" box +# over the login form, which swallows the credential keystrokes. Decline it by +# clicking "No" (Invoke / legacy default action / click the element's point). +function Dismiss-UpdatePrompt { + try { Add-Type -AssemblyName UIAutomationClient, UIAutomationTypes -ErrorAction Stop } catch { return $false } + if (-not ('Win32Mouse' -as [type])) { + Add-Type @" +using System; +using System.Runtime.InteropServices; +public static class Win32Mouse { + [DllImport("user32.dll")] static extern bool SetCursorPos(int x, int y); + [DllImport("user32.dll")] static extern void mouse_event(uint f, uint x, uint y, uint d, int e); + public static void Click(int x, int y){ SetCursorPos(x,y); mouse_event(0x02,0,0,0,0); mouse_event(0x04,0,0,0,0); } +} +"@ + } + $procIds = @((Get-Process -Name '*SimplySign*' -ErrorAction SilentlyContinue).Id) + $walker = [System.Windows.Automation.TreeWalker]::ControlViewWalker + $top = $walker.GetFirstChild([System.Windows.Automation.AutomationElement]::RootElement) + while ($top) { + if ($procIds -contains $top.Current.ProcessId) { + $no = Find-UiByName $top $walker "No" + if ($no) { + Write-Host "Update prompt detected; declining (clicking 'No')." + try { ($no.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern)).Invoke(); return $true } catch {} + try { ($no.GetCurrentPattern([System.Windows.Automation.LegacyIAccessiblePattern]::Pattern)).DoDefaultAction(); return $true } catch {} + try { $pt = $no.GetClickablePoint(); [Win32Mouse]::Click([int]$pt.X, [int]$pt.Y); return $true } catch { Write-Host "Click 'No' failed: $($_.Exception.Message)" } + } + } + $top = $walker.GetNextSibling($top) + } + return $false +} + +# Start from a clean slate so a stale window/session can't swallow the keystrokes. +if ($existing = Get-Process -Name "SimplySignDesktop" -ErrorAction Ignore) { + Write-Host "Killing existing SimplySignDesktop process..." + $existing | Stop-Process -Force + Start-Sleep -Seconds 1 +} + +Write-Host "Launching SimplySign Desktop..." +$proc = Start-Process -FilePath $ExePath -PassThru +Start-Sleep -Seconds 3 + +$wshell = New-Object -ComObject WScript.Shell + +Write-Host "Focusing the login window..." +$focused = $wshell.AppActivate($proc.Id) +if (-not $focused) { $focused = $wshell.AppActivate('SimplySign Desktop') } +for ($i = 0; (-not $focused) -and ($i -lt 10); $i++) { + Start-Sleep -Milliseconds 500 + $focused = $wshell.AppActivate($proc.Id) -or $wshell.AppActivate('SimplySign Desktop') +} +if (-not $focused) { + Write-Host "ERROR: could not bring SimplySign Desktop to the foreground" + exit 1 +} + +# Decline the "new version available" modal if it's covering the login form. +if (Dismiss-UpdatePrompt) { Start-Sleep -Milliseconds 800 } + +# Re-assert focus: dismissing the dialog can move the foreground window, and +# keystrokes sent to the wrong window are silently dropped. +$wshell.AppActivate($proc.Id) | Out-Null +$wshell.AppActivate('SimplySign Desktop') | Out-Null +Start-Sleep -Milliseconds 400 + +# Paste rather than type: SendKeys mangles characters like + ^ % ( ) and can +# drop characters; pasting delivers the exact string. Falls back to typing if +# the clipboard is unavailable. +function Set-Field([string]$text) { + try { + Set-Clipboard -Value $text -ErrorAction Stop + Start-Sleep -Milliseconds 150 + $wshell.SendKeys("^v") + } catch { + Write-Host "Clipboard unavailable ($($_.Exception.Message)); typing instead." + $wshell.SendKeys($text) + } + Start-Sleep -Milliseconds 250 +} + +Write-Host "Injecting credentials..." +Set-Field $UserId +$wshell.SendKeys("{TAB}") +Start-Sleep -Milliseconds 200 + +# Generate the code right before sending it so it can't expire while we focus. +$otp = [Totp]::Now($Base32, $Digits, $Period, $Algorithm) +Set-Field $otp +Start-Sleep -Milliseconds 200 +$wshell.SendKeys("{ENTER}") + +# Don't leave the OTP / username sitting on the clipboard afterwards. +try { Set-Clipboard -Value " " -ErrorAction Stop } catch {} + +Write-Host "Waiting for authentication to settle..." +Start-Sleep -Seconds 5 + +if (-not (Get-Process -Id $proc.Id -ErrorAction SilentlyContinue)) { + Write-Host "ERROR: SimplySign Desktop exited - authentication failed." + exit 1 +} + +# Confirm the login actually mounted the signing certificate into the store. +$thumb = if ($env:CERTUM_CERT_SHA1) { ($env:CERTUM_CERT_SHA1 -replace '\s', '').ToUpperInvariant() } else { $null } +Write-Host "Verifying the signing certificate reached the store..." +$deadline = (Get-Date).AddSeconds(60) +$found = $false +do { + $certs = Get-ChildItem Cert:\CurrentUser\My, Cert:\LocalMachine\My -ErrorAction SilentlyContinue + if ($thumb) { $found = [bool]($certs | Where-Object { $_.Thumbprint -eq $thumb }) } + else { $found = [bool]($certs | Where-Object { $_.Subject -like '*Certum*' -or $_.Issuer -like '*Certum*' }) } + if ($found) { break } + Start-Sleep -Seconds 3 +} while ((Get-Date) -lt $deadline) + +if ($found) { + Write-Host "=== Authentication complete: signing certificate is available. ===" +} else { + Write-Host "ERROR: signing certificate did not appear after authentication." + exit 1 +} diff --git a/.github/scripts/disconnect-simplysign.ps1 b/.github/scripts/disconnect-simplysign.ps1 new file mode 100644 index 0000000..b2db639 --- /dev/null +++ b/.github/scripts/disconnect-simplysign.ps1 @@ -0,0 +1,20 @@ +$ErrorActionPreference = "Stop" + +$processes = @(Get-Process -Name "SimplySignDesktop" -ErrorAction SilentlyContinue) +foreach ($process in $processes) { + if ($process.CloseMainWindow()) { + $process.WaitForExit(5000) | Out-Null + } +} + +$remaining = @(Get-Process -Name "SimplySignDesktop" -ErrorAction SilentlyContinue) +if ($remaining.Count -gt 0) { + $remaining | Stop-Process -Force + $remaining | Wait-Process -Timeout 10 +} + +if (Get-Process -Name "SimplySignDesktop" -ErrorAction SilentlyContinue) { + throw "SimplySign Desktop is still running after disconnect." +} + +Write-Host "SimplySign Desktop disconnected." diff --git a/.github/scripts/install-simplysign.sh b/.github/scripts/install-simplysign.sh new file mode 100644 index 0000000..6e4f30a --- /dev/null +++ b/.github/scripts/install-simplysign.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Install Certum SimplySign Desktop on the (ephemeral) Windows runner. +# SimplySign Desktop is what mounts the cloud signing certificate as a virtual +# smart card into the Windows certificate store; signtool then selects it by +# thumbprint. The version + checksum are pinned for reproducibility. + +set -euo pipefail + +VERSION="9.3.4.72" +URL="https://files.certum.eu/software/SimplySignDesktop/Windows/${VERSION}/SimplySignDesktop-${VERSION}-64-bit-en.msi" +EXPECTED_SHA256="bd51ebbaaac20fc7d59ab7103b5ed532b7800586df4e31f6999d03a394f9c515" +INSTALLER="SimplySignDesktop.msi" +INSTALL_DIR="/c/Program Files/Certum/SimplySign Desktop" + +echo "=== Installing Certum SimplySign Desktop ${VERSION} ===" + +# Idempotent: a cached/pre-provisioned image may already have it. +if [ -d "$INSTALL_DIR" ]; then + echo "Already installed at: $INSTALL_DIR" + exit 0 +fi + +echo "Downloading installer..." +curl -L "$URL" -o "$INSTALLER" --fail --max-time 600 + +ACTUAL_SHA256=$(sha256sum "$INSTALLER" | awk '{print $1}') +if [ "$EXPECTED_SHA256" != "$ACTUAL_SHA256" ]; then + echo "ERROR: checksum mismatch" + echo " expected: $EXPECTED_SHA256" + echo " actual: $ACTUAL_SHA256" + exit 1 +fi +echo "Checksum verified." + +echo "Running msiexec (silent)..." +# Start-Process inherits this CWD, so the relative installer path resolves here. +powershell -Command "Start-Process msiexec.exe -ArgumentList '/i','${INSTALLER}','/quiet','/norestart','/l*v','install.log','ALLUSERS=1','REBOOT=ReallySuppress' -Wait -NoNewWindow" + +if [ ! -d "$INSTALL_DIR" ]; then + echo "ERROR: installation directory not found after install" + echo "Last 20 lines of install.log:" + tail -20 install.log 2>/dev/null || echo "(no install.log)" + exit 1 +fi + +echo "SimplySign Desktop installed." diff --git a/.github/scripts/sign-certum.ps1 b/.github/scripts/sign-certum.ps1 new file mode 100644 index 0000000..7f679f2 --- /dev/null +++ b/.github/scripts/sign-certum.ps1 @@ -0,0 +1,66 @@ +# Sign the given files with the Certum cloud certificate (selected by SHA1 +# thumbprint from the store) and verify each signature. Assumes SimplySign +# Desktop has already authenticated (connect-simplysign.ps1). + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string[]]$Files, + [string]$CertSha1 = $env:CERTUM_CERT_SHA1, + [string]$TimestampUrl = "http://time.certum.pl" +) + +$ErrorActionPreference = "Stop" + +if (-not $CertSha1) { throw "CERTUM_CERT_SHA1 not provided." } +$thumb = ($CertSha1 -replace '\s', '').ToUpperInvariant() + +# Resolve the newest signtool.exe from the installed Windows SDK(s). +$kitsBin = "C:\Program Files (x86)\Windows Kits\10\bin" +$signtool = Get-ChildItem -Path $kitsBin -Recurse -Filter signtool.exe -File -ErrorAction SilentlyContinue | + Where-Object { $_.FullName -like '*\x64\*' } | + Sort-Object { [version]$_.Directory.Parent.Name } | + Select-Object -Last 1 -ExpandProperty FullName +if (-not $signtool) { throw "signtool.exe not found under $kitsBin" } +Write-Host "Using signtool: $signtool" + +# The SimplySign virtual smart card registers in the store a moment after the +# desktop client authenticates; poll for our certificate before signing. +Write-Host "Waiting for certificate $thumb to appear in the store..." +$deadline = (Get-Date).AddSeconds(120) +$cert = $null +$elapsed = 0 +do { + $cert = Get-ChildItem Cert:\CurrentUser\My, Cert:\LocalMachine\My -ErrorAction SilentlyContinue | + Where-Object { $_.Thumbprint -eq $thumb } | Select-Object -First 1 + if ($cert) { break } + Start-Sleep -Seconds 3 + $elapsed += 3 + Write-Host " ...still waiting (${elapsed}s)" +} while ((Get-Date) -lt $deadline) + +if (-not $cert) { + Write-Host "Certificate not found. CurrentUser\My + LocalMachine\My contents:" + Get-ChildItem Cert:\CurrentUser\My, Cert:\LocalMachine\My -ErrorAction SilentlyContinue | + Select-Object Thumbprint, Subject, HasPrivateKey | Format-Table -AutoSize | Out-String | Write-Host + Write-Host "SimplySign processes:" + Get-Process -Name '*SimplySign*' -ErrorAction SilentlyContinue | + Select-Object Name, Id, Responding, MainWindowTitle | Format-Table -AutoSize | Out-String | Write-Host + throw "Certificate $thumb not found in the store - SimplySign authentication likely failed (see dump above)." +} +Write-Host "Found signing certificate: $($cert.Subject)" + +$failed = @() +foreach ($file in $Files) { + if (-not (Test-Path $file)) { throw "File to sign not found: $file" } + Write-Host "=== Signing $file ===" + & $signtool sign /sha1 $thumb /fd sha256 /td sha256 /tr $TimestampUrl /v $file + if ($LASTEXITCODE -ne 0) { $failed += $file; continue } + & $signtool verify /pa /v $file + if ($LASTEXITCODE -ne 0) { $failed += $file } +} + +if ($failed.Count -gt 0) { + throw "Signing/verification failed for: $($failed -join ', ')" +} +Write-Host "All binaries signed and verified." diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 1253427..3168ab4 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -16,9 +16,12 @@ jobs: runs-on: windows-2025-vs2026 + permissions: + contents: write + env: Configuration: Release - Actions_Allow_Unsecure_Commands: true + HAS_CERTUM_CREDENTIALS: ${{ secrets.CERTUM_OTP_URI != '' && secrets.CERTUM_USERID != '' && secrets.CERTUM_CERT_SHA1 != '' }} steps: - name: Checkout @@ -46,7 +49,39 @@ jobs: - name: Build binaries run: cmake --build build --config Release - + + # Signing only runs when the Certum secrets are present (i.e. on the real + # repo, not forks); without them the build still produces unsigned binaries. + - name: Set up Certum SimplySign + if: env.HAS_CERTUM_CREDENTIALS == 'true' + shell: bash + run: | + chmod +x ./.github/scripts/install-simplysign.sh + ./.github/scripts/install-simplysign.sh + powershell -ExecutionPolicy Bypass -File "./.github/scripts/configure-simplysign.ps1" + + - name: Authenticate Certum SimplySign + if: env.HAS_CERTUM_CREDENTIALS == 'true' + shell: bash + env: + CERTUM_OTP_URI: ${{ secrets.CERTUM_OTP_URI }} + CERTUM_USERID: ${{ secrets.CERTUM_USERID }} + CERTUM_CERT_SHA1: ${{ secrets.CERTUM_CERT_SHA1 }} + run: powershell -ExecutionPolicy Bypass -File "./.github/scripts/connect-simplysign.ps1" + + - name: Sign release binaries + if: env.HAS_CERTUM_CREDENTIALS == 'true' + shell: pwsh + env: + CERTUM_CERT_SHA1: ${{ secrets.CERTUM_CERT_SHA1 }} + run: | + ./.github/scripts/sign-certum.ps1 -Files @(".\bin\Release\gMod.dll", ".\bin\Release\TpfConvert.exe") + + - name: Disconnect Certum SimplySign + if: always() && env.HAS_CERTUM_CREDENTIALS == 'true' + shell: pwsh + run: ./.github/scripts/disconnect-simplysign.ps1 + - name: Retrieve version id: set_version run: | @@ -56,19 +91,37 @@ jobs: exit 1 } else { Write-Host "FileVersionInfo: $fileVersionInfo" - echo "::set-output name=version::$fileVersionInfo" + Add-Content -LiteralPath $env:GITHUB_OUTPUT -Value "version=$fileVersionInfo" -Encoding utf8 } shell: pwsh - name: Publish release - uses: Xotl/cool-github-releases@v1.1.8 - with: - mode: update - tag_name: v${{ steps.set_version.outputs.version }} - release_name: gMod v${{ steps.set_version.outputs.version }} - assets: .\bin\Release\gMod.dll;.\bin\Release\TpfConvert.exe;.\bin\Release\d3dx9_43.dll - github_token: ${{ env.GITHUB_TOKEN }} - replace_assets: true - body_mrkdwn: ${{ env.Changelog }} env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ steps.set_version.outputs.version }} + shell: pwsh + run: | + $tag = "v$env:VERSION" + $title = "gMod v$env:VERSION" + + gh release view $tag *> $null + if ($LASTEXITCODE -eq 0) { + gh release edit $tag --title $title + if ($LASTEXITCODE -ne 0) { throw "Failed to update release $tag." } + } else { + $notes = [System.IO.Path]::GetTempFileName() + try { + Set-Content -LiteralPath $notes -Value "" -NoNewline + gh release create $tag --title $title --notes-file $notes + if ($LASTEXITCODE -ne 0) { throw "Failed to create release $tag." } + } finally { + Remove-Item -LiteralPath $notes -Force -ErrorAction SilentlyContinue + } + } + + gh release upload $tag ` + .\bin\Release\gMod.dll ` + .\bin\Release\TpfConvert.exe ` + .\bin\Release\d3dx9_43.dll ` + --clobber + if ($LASTEXITCODE -ne 0) { throw "Failed to upload assets for release $tag." } diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d734590..1f15941 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,7 +20,6 @@ jobs: env: Configuration: Release - Actions_Allow_Unsecure_Commands: true steps: - name: Checkout diff --git a/CMakeLists.txt b/CMakeLists.txt index c0c21eb..78b3511 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,9 +10,9 @@ if(NOT(CMAKE_SIZEOF_VOID_P EQUAL 4)) endif() set(VERSION_MAJOR 1) -set(VERSION_MINOR 9) +set(VERSION_MINOR 10) set(VERSION_PATCH 0) -set(VERSION_TWEAK 3) +set(VERSION_TWEAK 0) set(VERSION_RC "${CMAKE_CURRENT_BINARY_DIR}/version.rc") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in" "${VERSION_RC}" @ONLY) diff --git a/header/D3D9DeviceWrapper.h b/header/D3D9DeviceWrapper.h new file mode 100644 index 0000000..d092c17 --- /dev/null +++ b/header/D3D9DeviceWrapper.h @@ -0,0 +1,305 @@ +#pragma once + +#include +#include + +class TextureClient; + +// Wraps a real IDirect3DDevice9 so CreateTexture/CreateVolumeTexture/CreateCubeTexture/ +// UpdateTexture/BeginScene/SetTexture/GetTexture/Release are intercepted via ordinary +// compiled virtual dispatch instead of a MinHook-patched vtable slot (unstable under +// ARM64 emulation). Every other method is a pure forward to the real device. +class WrappedDirect3DDevice9 : public IDirect3DDevice9 { +public: + explicit WrappedDirect3DDevice9(IDirect3DDevice9* real); + + IDirect3DDevice9* RealDevice() const { return real_; } + TextureClient* Client() const { return client_; } + void SetClient(TextureClient* client) { client_ = client; } + + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj) override; + STDMETHOD_(ULONG, AddRef)() override; + STDMETHOD_(ULONG, Release)() override; + + STDMETHOD(TestCooperativeLevel)() override; + STDMETHOD_(UINT, GetAvailableTextureMem)() override; + STDMETHOD(EvictManagedResources)() override; + STDMETHOD(GetDirect3D)(IDirect3D9** ppD3D9) override; + STDMETHOD(GetDeviceCaps)(D3DCAPS9* pCaps) override; + STDMETHOD(GetDisplayMode)(UINT iSwapChain, D3DDISPLAYMODE* pMode) override; + STDMETHOD(GetCreationParameters)(D3DDEVICE_CREATION_PARAMETERS* pParameters) override; + STDMETHOD(SetCursorProperties)(UINT XHotSpot, UINT YHotSpot, IDirect3DSurface9* pCursorBitmap) override; + STDMETHOD_(void, SetCursorPosition)(int X, int Y, DWORD Flags) override; + STDMETHOD_(BOOL, ShowCursor)(BOOL bShow) override; + STDMETHOD(CreateAdditionalSwapChain)(D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DSwapChain9** pSwapChain) override; + STDMETHOD(GetSwapChain)(UINT iSwapChain, IDirect3DSwapChain9** pSwapChain) override; + STDMETHOD_(UINT, GetNumberOfSwapChains)() override; + STDMETHOD(Reset)(D3DPRESENT_PARAMETERS* pPresentationParameters) override; + STDMETHOD(Present)(const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion) override; + STDMETHOD(GetBackBuffer)(UINT iSwapChain, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface9** ppBackBuffer) override; + STDMETHOD(GetRasterStatus)(UINT iSwapChain, D3DRASTER_STATUS* pRasterStatus) override; + STDMETHOD(SetDialogBoxMode)(BOOL bEnableDialogs) override; + STDMETHOD_(void, SetGammaRamp)(UINT iSwapChain, DWORD Flags, const D3DGAMMARAMP* pRamp) override; + STDMETHOD_(void, GetGammaRamp)(UINT iSwapChain, D3DGAMMARAMP* pRamp) override; + STDMETHOD(CreateTexture)(UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle) override; + STDMETHOD(CreateVolumeTexture)(UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DVolumeTexture9** ppVolumeTexture, HANDLE* pSharedHandle) override; + STDMETHOD(CreateCubeTexture)(UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DCubeTexture9** ppCubeTexture, HANDLE* pSharedHandle) override; + STDMETHOD(CreateVertexBuffer)(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle) override; + STDMETHOD(CreateIndexBuffer)(UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pSharedHandle) override; + STDMETHOD(CreateRenderTarget)(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) override; + STDMETHOD(CreateDepthStencilSurface)(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) override; + STDMETHOD(UpdateSurface)(IDirect3DSurface9* pSourceSurface, const RECT* pSourceRect, IDirect3DSurface9* pDestinationSurface, const POINT* pDestPoint) override; + STDMETHOD(UpdateTexture)(IDirect3DBaseTexture9* pSourceTexture, IDirect3DBaseTexture9* pDestinationTexture) override; + STDMETHOD(GetRenderTargetData)(IDirect3DSurface9* pRenderTarget, IDirect3DSurface9* pDestSurface) override; + STDMETHOD(GetFrontBufferData)(UINT iSwapChain, IDirect3DSurface9* pDestSurface) override; + STDMETHOD(StretchRect)(IDirect3DSurface9* pSourceSurface, const RECT* pSourceRect, IDirect3DSurface9* pDestSurface, const RECT* pDestRect, D3DTEXTUREFILTERTYPE Filter) override; + STDMETHOD(ColorFill)(IDirect3DSurface9* pSurface, const RECT* pRect, D3DCOLOR color) override; + STDMETHOD(CreateOffscreenPlainSurface)(UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) override; + STDMETHOD(SetRenderTarget)(DWORD RenderTargetIndex, IDirect3DSurface9* pRenderTarget) override; + STDMETHOD(GetRenderTarget)(DWORD RenderTargetIndex, IDirect3DSurface9** ppRenderTarget) override; + STDMETHOD(SetDepthStencilSurface)(IDirect3DSurface9* pNewZStencil) override; + STDMETHOD(GetDepthStencilSurface)(IDirect3DSurface9** ppZStencilSurface) override; + STDMETHOD(BeginScene)() override; + STDMETHOD(EndScene)() override; + STDMETHOD(Clear)(DWORD Count, const D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil) override; + STDMETHOD(SetTransform)(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix) override; + STDMETHOD(GetTransform)(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) override; + STDMETHOD(MultiplyTransform)(D3DTRANSFORMSTATETYPE, const D3DMATRIX*) override; + STDMETHOD(SetViewport)(const D3DVIEWPORT9* pViewport) override; + STDMETHOD(GetViewport)(D3DVIEWPORT9* pViewport) override; + STDMETHOD(SetMaterial)(const D3DMATERIAL9* pMaterial) override; + STDMETHOD(GetMaterial)(D3DMATERIAL9* pMaterial) override; + STDMETHOD(SetLight)(DWORD Index, const D3DLIGHT9*) override; + STDMETHOD(GetLight)(DWORD Index, D3DLIGHT9*) override; + STDMETHOD(LightEnable)(DWORD Index, BOOL Enable) override; + STDMETHOD(GetLightEnable)(DWORD Index, BOOL* pEnable) override; + STDMETHOD(SetClipPlane)(DWORD Index, const float* pPlane) override; + STDMETHOD(GetClipPlane)(DWORD Index, float* pPlane) override; + STDMETHOD(SetRenderState)(D3DRENDERSTATETYPE State, DWORD Value) override; + STDMETHOD(GetRenderState)(D3DRENDERSTATETYPE State, DWORD* pValue) override; + STDMETHOD(CreateStateBlock)(D3DSTATEBLOCKTYPE Type, IDirect3DStateBlock9** ppSB) override; + STDMETHOD(BeginStateBlock)() override; + STDMETHOD(EndStateBlock)(IDirect3DStateBlock9** ppSB) override; + STDMETHOD(SetClipStatus)(const D3DCLIPSTATUS9* pClipStatus) override; + STDMETHOD(GetClipStatus)(D3DCLIPSTATUS9* pClipStatus) override; + STDMETHOD(GetTexture)(DWORD Stage, IDirect3DBaseTexture9** ppTexture) override; + STDMETHOD(SetTexture)(DWORD Stage, IDirect3DBaseTexture9* pTexture) override; + STDMETHOD(GetTextureStageState)(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue) override; + STDMETHOD(SetTextureStageState)(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value) override; + STDMETHOD(GetSamplerState)(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD* pValue) override; + STDMETHOD(SetSamplerState)(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value) override; + STDMETHOD(ValidateDevice)(DWORD* pNumPasses) override; + STDMETHOD(SetPaletteEntries)(UINT PaletteNumber, const PALETTEENTRY* pEntries) override; + STDMETHOD(GetPaletteEntries)(UINT PaletteNumber, PALETTEENTRY* pEntries) override; + STDMETHOD(SetCurrentTexturePalette)(UINT PaletteNumber) override; + STDMETHOD(GetCurrentTexturePalette)(UINT* PaletteNumber) override; + STDMETHOD(SetScissorRect)(const RECT* pRect) override; + STDMETHOD(GetScissorRect)(RECT* pRect) override; + STDMETHOD(SetSoftwareVertexProcessing)(BOOL bSoftware) override; + STDMETHOD_(BOOL, GetSoftwareVertexProcessing)() override; + STDMETHOD(SetNPatchMode)(float nSegments) override; + STDMETHOD_(float, GetNPatchMode)() override; + STDMETHOD(DrawPrimitive)(D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount) override; + STDMETHOD(DrawIndexedPrimitive)(D3DPRIMITIVETYPE, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount) override; + STDMETHOD(DrawPrimitiveUP)(D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride) override; + STDMETHOD(DrawIndexedPrimitiveUP)(D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount, const void* pIndexData, D3DFORMAT IndexDataFormat, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride) override; + STDMETHOD(ProcessVertices)(UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, IDirect3DVertexBuffer9* pDestBuffer, IDirect3DVertexDeclaration9* pVertexDecl, DWORD Flags) override; + STDMETHOD(CreateVertexDeclaration)(const D3DVERTEXELEMENT9* pVertexElements, IDirect3DVertexDeclaration9** ppDecl) override; + STDMETHOD(SetVertexDeclaration)(IDirect3DVertexDeclaration9* pDecl) override; + STDMETHOD(GetVertexDeclaration)(IDirect3DVertexDeclaration9** ppDecl) override; + STDMETHOD(SetFVF)(DWORD FVF) override; + STDMETHOD(GetFVF)(DWORD* pFVF) override; + STDMETHOD(CreateVertexShader)(const DWORD* pFunction, IDirect3DVertexShader9** ppShader) override; + STDMETHOD(SetVertexShader)(IDirect3DVertexShader9* pShader) override; + STDMETHOD(GetVertexShader)(IDirect3DVertexShader9** ppShader) override; + STDMETHOD(SetVertexShaderConstantF)(UINT StartRegister, const float* pConstantData, UINT Vector4fCount) override; + STDMETHOD(GetVertexShaderConstantF)(UINT StartRegister, float* pConstantData, UINT Vector4fCount) override; + STDMETHOD(SetVertexShaderConstantI)(UINT StartRegister, const int* pConstantData, UINT Vector4iCount) override; + STDMETHOD(GetVertexShaderConstantI)(UINT StartRegister, int* pConstantData, UINT Vector4iCount) override; + STDMETHOD(SetVertexShaderConstantB)(UINT StartRegister, const BOOL* pConstantData, UINT BoolCount) override; + STDMETHOD(GetVertexShaderConstantB)(UINT StartRegister, BOOL* pConstantData, UINT BoolCount) override; + STDMETHOD(SetStreamSource)(UINT StreamNumber, IDirect3DVertexBuffer9* pStreamData, UINT OffsetInBytes, UINT Stride) override; + STDMETHOD(GetStreamSource)(UINT StreamNumber, IDirect3DVertexBuffer9** ppStreamData, UINT* pOffsetInBytes, UINT* pStride) override; + STDMETHOD(SetStreamSourceFreq)(UINT StreamNumber, UINT Setting) override; + STDMETHOD(GetStreamSourceFreq)(UINT StreamNumber, UINT* pSetting) override; + STDMETHOD(SetIndices)(IDirect3DIndexBuffer9* pIndexData) override; + STDMETHOD(GetIndices)(IDirect3DIndexBuffer9** ppIndexData) override; + STDMETHOD(CreatePixelShader)(const DWORD* pFunction, IDirect3DPixelShader9** ppShader) override; + STDMETHOD(SetPixelShader)(IDirect3DPixelShader9* pShader) override; + STDMETHOD(GetPixelShader)(IDirect3DPixelShader9** ppShader) override; + STDMETHOD(SetPixelShaderConstantF)(UINT StartRegister, const float* pConstantData, UINT Vector4fCount) override; + STDMETHOD(GetPixelShaderConstantF)(UINT StartRegister, float* pConstantData, UINT Vector4fCount) override; + STDMETHOD(SetPixelShaderConstantI)(UINT StartRegister, const int* pConstantData, UINT Vector4iCount) override; + STDMETHOD(GetPixelShaderConstantI)(UINT StartRegister, int* pConstantData, UINT Vector4iCount) override; + STDMETHOD(SetPixelShaderConstantB)(UINT StartRegister, const BOOL* pConstantData, UINT BoolCount) override; + STDMETHOD(GetPixelShaderConstantB)(UINT StartRegister, BOOL* pConstantData, UINT BoolCount) override; + STDMETHOD(DrawRectPatch)(UINT Handle, const float* pNumSegs, const D3DRECTPATCH_INFO* pRectPatchInfo) override; + STDMETHOD(DrawTriPatch)(UINT Handle, const float* pNumSegs, const D3DTRIPATCH_INFO* pTriPatchInfo) override; + STDMETHOD(DeletePatch)(UINT Handle) override; + STDMETHOD(CreateQuery)(D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery) override; + +protected: + IDirect3DDevice9* real_; + std::atomic ref_count_; + TextureClient* client_; + + // Shared by Release() here and by WrappedDirect3DDevice9Ex::Release(); deletes + // client_ exactly as h_DeviceRelease does, via TextureClient::CurrentClient(). + void TeardownTextureClient(); +}; + +// Adds IDirect3DDevice9Ex's own methods via ordinary forwards, plus CreateDeviceEx-style +// interception is one level up (in WrappedDirect3D9Ex); this class only needs to override +// the base's virtuals once (inherited) and add the Ex-only ones. +class WrappedDirect3DDevice9Ex : public IDirect3DDevice9Ex { +public: + explicit WrappedDirect3DDevice9Ex(IDirect3DDevice9Ex* real); + + IDirect3DDevice9Ex* RealDeviceEx() const { return real_; } + TextureClient* Client() const { return client_; } + void SetClient(TextureClient* client) { client_ = client; } + + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj) override; + STDMETHOD_(ULONG, AddRef)() override; + STDMETHOD_(ULONG, Release)() override; + + STDMETHOD(TestCooperativeLevel)() override; + STDMETHOD_(UINT, GetAvailableTextureMem)() override; + STDMETHOD(EvictManagedResources)() override; + STDMETHOD(GetDirect3D)(IDirect3D9** ppD3D9) override; + STDMETHOD(GetDeviceCaps)(D3DCAPS9* pCaps) override; + STDMETHOD(GetDisplayMode)(UINT iSwapChain, D3DDISPLAYMODE* pMode) override; + STDMETHOD(GetCreationParameters)(D3DDEVICE_CREATION_PARAMETERS* pParameters) override; + STDMETHOD(SetCursorProperties)(UINT XHotSpot, UINT YHotSpot, IDirect3DSurface9* pCursorBitmap) override; + STDMETHOD_(void, SetCursorPosition)(int X, int Y, DWORD Flags) override; + STDMETHOD_(BOOL, ShowCursor)(BOOL bShow) override; + STDMETHOD(CreateAdditionalSwapChain)(D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DSwapChain9** pSwapChain) override; + STDMETHOD(GetSwapChain)(UINT iSwapChain, IDirect3DSwapChain9** pSwapChain) override; + STDMETHOD_(UINT, GetNumberOfSwapChains)() override; + STDMETHOD(Reset)(D3DPRESENT_PARAMETERS* pPresentationParameters) override; + STDMETHOD(Present)(const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion) override; + STDMETHOD(GetBackBuffer)(UINT iSwapChain, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface9** ppBackBuffer) override; + STDMETHOD(GetRasterStatus)(UINT iSwapChain, D3DRASTER_STATUS* pRasterStatus) override; + STDMETHOD(SetDialogBoxMode)(BOOL bEnableDialogs) override; + STDMETHOD_(void, SetGammaRamp)(UINT iSwapChain, DWORD Flags, const D3DGAMMARAMP* pRamp) override; + STDMETHOD_(void, GetGammaRamp)(UINT iSwapChain, D3DGAMMARAMP* pRamp) override; + STDMETHOD(CreateTexture)(UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle) override; + STDMETHOD(CreateVolumeTexture)(UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DVolumeTexture9** ppVolumeTexture, HANDLE* pSharedHandle) override; + STDMETHOD(CreateCubeTexture)(UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DCubeTexture9** ppCubeTexture, HANDLE* pSharedHandle) override; + STDMETHOD(CreateVertexBuffer)(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle) override; + STDMETHOD(CreateIndexBuffer)(UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pSharedHandle) override; + STDMETHOD(CreateRenderTarget)(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) override; + STDMETHOD(CreateDepthStencilSurface)(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) override; + STDMETHOD(UpdateSurface)(IDirect3DSurface9* pSourceSurface, const RECT* pSourceRect, IDirect3DSurface9* pDestinationSurface, const POINT* pDestPoint) override; + STDMETHOD(UpdateTexture)(IDirect3DBaseTexture9* pSourceTexture, IDirect3DBaseTexture9* pDestinationTexture) override; + STDMETHOD(GetRenderTargetData)(IDirect3DSurface9* pRenderTarget, IDirect3DSurface9* pDestSurface) override; + STDMETHOD(GetFrontBufferData)(UINT iSwapChain, IDirect3DSurface9* pDestSurface) override; + STDMETHOD(StretchRect)(IDirect3DSurface9* pSourceSurface, const RECT* pSourceRect, IDirect3DSurface9* pDestSurface, const RECT* pDestRect, D3DTEXTUREFILTERTYPE Filter) override; + STDMETHOD(ColorFill)(IDirect3DSurface9* pSurface, const RECT* pRect, D3DCOLOR color) override; + STDMETHOD(CreateOffscreenPlainSurface)(UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) override; + STDMETHOD(SetRenderTarget)(DWORD RenderTargetIndex, IDirect3DSurface9* pRenderTarget) override; + STDMETHOD(GetRenderTarget)(DWORD RenderTargetIndex, IDirect3DSurface9** ppRenderTarget) override; + STDMETHOD(SetDepthStencilSurface)(IDirect3DSurface9* pNewZStencil) override; + STDMETHOD(GetDepthStencilSurface)(IDirect3DSurface9** ppZStencilSurface) override; + STDMETHOD(BeginScene)() override; + STDMETHOD(EndScene)() override; + STDMETHOD(Clear)(DWORD Count, const D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil) override; + STDMETHOD(SetTransform)(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix) override; + STDMETHOD(GetTransform)(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) override; + STDMETHOD(MultiplyTransform)(D3DTRANSFORMSTATETYPE, const D3DMATRIX*) override; + STDMETHOD(SetViewport)(const D3DVIEWPORT9* pViewport) override; + STDMETHOD(GetViewport)(D3DVIEWPORT9* pViewport) override; + STDMETHOD(SetMaterial)(const D3DMATERIAL9* pMaterial) override; + STDMETHOD(GetMaterial)(D3DMATERIAL9* pMaterial) override; + STDMETHOD(SetLight)(DWORD Index, const D3DLIGHT9*) override; + STDMETHOD(GetLight)(DWORD Index, D3DLIGHT9*) override; + STDMETHOD(LightEnable)(DWORD Index, BOOL Enable) override; + STDMETHOD(GetLightEnable)(DWORD Index, BOOL* pEnable) override; + STDMETHOD(SetClipPlane)(DWORD Index, const float* pPlane) override; + STDMETHOD(GetClipPlane)(DWORD Index, float* pPlane) override; + STDMETHOD(SetRenderState)(D3DRENDERSTATETYPE State, DWORD Value) override; + STDMETHOD(GetRenderState)(D3DRENDERSTATETYPE State, DWORD* pValue) override; + STDMETHOD(CreateStateBlock)(D3DSTATEBLOCKTYPE Type, IDirect3DStateBlock9** ppSB) override; + STDMETHOD(BeginStateBlock)() override; + STDMETHOD(EndStateBlock)(IDirect3DStateBlock9** ppSB) override; + STDMETHOD(SetClipStatus)(const D3DCLIPSTATUS9* pClipStatus) override; + STDMETHOD(GetClipStatus)(D3DCLIPSTATUS9* pClipStatus) override; + STDMETHOD(GetTexture)(DWORD Stage, IDirect3DBaseTexture9** ppTexture) override; + STDMETHOD(SetTexture)(DWORD Stage, IDirect3DBaseTexture9* pTexture) override; + STDMETHOD(GetTextureStageState)(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue) override; + STDMETHOD(SetTextureStageState)(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value) override; + STDMETHOD(GetSamplerState)(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD* pValue) override; + STDMETHOD(SetSamplerState)(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value) override; + STDMETHOD(ValidateDevice)(DWORD* pNumPasses) override; + STDMETHOD(SetPaletteEntries)(UINT PaletteNumber, const PALETTEENTRY* pEntries) override; + STDMETHOD(GetPaletteEntries)(UINT PaletteNumber, PALETTEENTRY* pEntries) override; + STDMETHOD(SetCurrentTexturePalette)(UINT PaletteNumber) override; + STDMETHOD(GetCurrentTexturePalette)(UINT* PaletteNumber) override; + STDMETHOD(SetScissorRect)(const RECT* pRect) override; + STDMETHOD(GetScissorRect)(RECT* pRect) override; + STDMETHOD(SetSoftwareVertexProcessing)(BOOL bSoftware) override; + STDMETHOD_(BOOL, GetSoftwareVertexProcessing)() override; + STDMETHOD(SetNPatchMode)(float nSegments) override; + STDMETHOD_(float, GetNPatchMode)() override; + STDMETHOD(DrawPrimitive)(D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount) override; + STDMETHOD(DrawIndexedPrimitive)(D3DPRIMITIVETYPE, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount) override; + STDMETHOD(DrawPrimitiveUP)(D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride) override; + STDMETHOD(DrawIndexedPrimitiveUP)(D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount, const void* pIndexData, D3DFORMAT IndexDataFormat, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride) override; + STDMETHOD(ProcessVertices)(UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, IDirect3DVertexBuffer9* pDestBuffer, IDirect3DVertexDeclaration9* pVertexDecl, DWORD Flags) override; + STDMETHOD(CreateVertexDeclaration)(const D3DVERTEXELEMENT9* pVertexElements, IDirect3DVertexDeclaration9** ppDecl) override; + STDMETHOD(SetVertexDeclaration)(IDirect3DVertexDeclaration9* pDecl) override; + STDMETHOD(GetVertexDeclaration)(IDirect3DVertexDeclaration9** ppDecl) override; + STDMETHOD(SetFVF)(DWORD FVF) override; + STDMETHOD(GetFVF)(DWORD* pFVF) override; + STDMETHOD(CreateVertexShader)(const DWORD* pFunction, IDirect3DVertexShader9** ppShader) override; + STDMETHOD(SetVertexShader)(IDirect3DVertexShader9* pShader) override; + STDMETHOD(GetVertexShader)(IDirect3DVertexShader9** ppShader) override; + STDMETHOD(SetVertexShaderConstantF)(UINT StartRegister, const float* pConstantData, UINT Vector4fCount) override; + STDMETHOD(GetVertexShaderConstantF)(UINT StartRegister, float* pConstantData, UINT Vector4fCount) override; + STDMETHOD(SetVertexShaderConstantI)(UINT StartRegister, const int* pConstantData, UINT Vector4iCount) override; + STDMETHOD(GetVertexShaderConstantI)(UINT StartRegister, int* pConstantData, UINT Vector4iCount) override; + STDMETHOD(SetVertexShaderConstantB)(UINT StartRegister, const BOOL* pConstantData, UINT BoolCount) override; + STDMETHOD(GetVertexShaderConstantB)(UINT StartRegister, BOOL* pConstantData, UINT BoolCount) override; + STDMETHOD(SetStreamSource)(UINT StreamNumber, IDirect3DVertexBuffer9* pStreamData, UINT OffsetInBytes, UINT Stride) override; + STDMETHOD(GetStreamSource)(UINT StreamNumber, IDirect3DVertexBuffer9** ppStreamData, UINT* pOffsetInBytes, UINT* pStride) override; + STDMETHOD(SetStreamSourceFreq)(UINT StreamNumber, UINT Setting) override; + STDMETHOD(GetStreamSourceFreq)(UINT StreamNumber, UINT* pSetting) override; + STDMETHOD(SetIndices)(IDirect3DIndexBuffer9* pIndexData) override; + STDMETHOD(GetIndices)(IDirect3DIndexBuffer9** ppIndexData) override; + STDMETHOD(CreatePixelShader)(const DWORD* pFunction, IDirect3DPixelShader9** ppShader) override; + STDMETHOD(SetPixelShader)(IDirect3DPixelShader9* pShader) override; + STDMETHOD(GetPixelShader)(IDirect3DPixelShader9** ppShader) override; + STDMETHOD(SetPixelShaderConstantF)(UINT StartRegister, const float* pConstantData, UINT Vector4fCount) override; + STDMETHOD(GetPixelShaderConstantF)(UINT StartRegister, float* pConstantData, UINT Vector4fCount) override; + STDMETHOD(SetPixelShaderConstantI)(UINT StartRegister, const int* pConstantData, UINT Vector4iCount) override; + STDMETHOD(GetPixelShaderConstantI)(UINT StartRegister, int* pConstantData, UINT Vector4iCount) override; + STDMETHOD(SetPixelShaderConstantB)(UINT StartRegister, const BOOL* pConstantData, UINT BoolCount) override; + STDMETHOD(GetPixelShaderConstantB)(UINT StartRegister, BOOL* pConstantData, UINT BoolCount) override; + STDMETHOD(DrawRectPatch)(UINT Handle, const float* pNumSegs, const D3DRECTPATCH_INFO* pRectPatchInfo) override; + STDMETHOD(DrawTriPatch)(UINT Handle, const float* pNumSegs, const D3DTRIPATCH_INFO* pTriPatchInfo) override; + STDMETHOD(DeletePatch)(UINT Handle) override; + STDMETHOD(CreateQuery)(D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery) override; + + STDMETHOD(SetConvolutionMonoKernel)(UINT width, UINT height, float* rows, float* columns) override; + STDMETHOD(ComposeRects)(IDirect3DSurface9* pSrc, IDirect3DSurface9* pDst, IDirect3DVertexBuffer9* pSrcRectDescs, UINT NumRects, IDirect3DVertexBuffer9* pDstRectDescs, D3DCOMPOSERECTSOP Operation, int Xoffset, int Yoffset) override; + STDMETHOD(PresentEx)(const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion, DWORD dwFlags) override; + STDMETHOD(GetGPUThreadPriority)(INT* pPriority) override; + STDMETHOD(SetGPUThreadPriority)(INT Priority) override; + STDMETHOD(WaitForVBlank)(UINT iSwapChain) override; + STDMETHOD(CheckResourceResidency)(IDirect3DResource9** pResourceArray, UINT32 NumResources) override; + STDMETHOD(SetMaximumFrameLatency)(UINT MaxLatency) override; + STDMETHOD(GetMaximumFrameLatency)(UINT* pMaxLatency) override; + STDMETHOD(CheckDeviceState)(HWND hDestinationWindow) override; + STDMETHOD(CreateRenderTargetEx)(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage) override; + STDMETHOD(CreateOffscreenPlainSurfaceEx)(UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage) override; + STDMETHOD(CreateDepthStencilSurfaceEx)(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage) override; + STDMETHOD(ResetEx)(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode) override; + STDMETHOD(GetDisplayModeEx)(UINT iSwapChain, D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation) override; + +private: + IDirect3DDevice9Ex* real_; + std::atomic ref_count_; + TextureClient* client_; + + void TeardownTextureClient(); +}; diff --git a/header/D3D9TextureWrappers.h b/header/D3D9TextureWrappers.h new file mode 100644 index 0000000..ab34edc --- /dev/null +++ b/header/D3D9TextureWrappers.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include + +class TextureClient; + +// Wraps a real IDirect3D*Texture9 so every method is ordinary compiled virtual dispatch +// instead of a MinHook-patched vtable slot. See D3D9DeviceWrapper.h for why this matters. +class WrappedTexture9 : public IDirect3DTexture9 { +public: + explicit WrappedTexture9(IDirect3DTexture9* real); + + IDirect3DTexture9* RealTexture() const { return real_; } + + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj) override; + STDMETHOD_(ULONG, AddRef)() override; + STDMETHOD_(ULONG, Release)() override; + + STDMETHOD(GetDevice)(IDirect3DDevice9** ppDevice) override; + STDMETHOD(SetPrivateData)(REFGUID refguid, const void* pData, DWORD SizeOfData, DWORD Flags) override; + STDMETHOD(GetPrivateData)(REFGUID refguid, void* pData, DWORD* pSizeOfData) override; + STDMETHOD(FreePrivateData)(REFGUID refguid) override; + STDMETHOD_(DWORD, SetPriority)(DWORD PriorityNew) override; + STDMETHOD_(DWORD, GetPriority)() override; + STDMETHOD_(void, PreLoad)() override; + STDMETHOD_(D3DRESOURCETYPE, GetType)() override; + + STDMETHOD_(DWORD, SetLOD)(DWORD LODNew) override; + STDMETHOD_(DWORD, GetLOD)() override; + STDMETHOD_(DWORD, GetLevelCount)() override; + STDMETHOD(SetAutoGenFilterType)(D3DTEXTUREFILTERTYPE FilterType) override; + STDMETHOD_(D3DTEXTUREFILTERTYPE, GetAutoGenFilterType)() override; + STDMETHOD_(void, GenerateMipSubLevels)() override; + + STDMETHOD(GetLevelDesc)(UINT Level, D3DSURFACE_DESC* pDesc) override; + STDMETHOD(GetSurfaceLevel)(UINT Level, IDirect3DSurface9** ppSurfaceLevel) override; + STDMETHOD(LockRect)(UINT Level, D3DLOCKED_RECT* pLockedRect, const RECT* pRect, DWORD Flags) override; + STDMETHOD(UnlockRect)(UINT Level) override; + STDMETHOD(AddDirtyRect)(const RECT* pDirtyRect) override; + +private: + IDirect3DTexture9* real_; + std::atomic ref_count_; +}; + +class WrappedVolumeTexture9 : public IDirect3DVolumeTexture9 { +public: + explicit WrappedVolumeTexture9(IDirect3DVolumeTexture9* real); + + IDirect3DVolumeTexture9* RealTexture() const { return real_; } + + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj) override; + STDMETHOD_(ULONG, AddRef)() override; + STDMETHOD_(ULONG, Release)() override; + + STDMETHOD(GetDevice)(IDirect3DDevice9** ppDevice) override; + STDMETHOD(SetPrivateData)(REFGUID refguid, const void* pData, DWORD SizeOfData, DWORD Flags) override; + STDMETHOD(GetPrivateData)(REFGUID refguid, void* pData, DWORD* pSizeOfData) override; + STDMETHOD(FreePrivateData)(REFGUID refguid) override; + STDMETHOD_(DWORD, SetPriority)(DWORD PriorityNew) override; + STDMETHOD_(DWORD, GetPriority)() override; + STDMETHOD_(void, PreLoad)() override; + STDMETHOD_(D3DRESOURCETYPE, GetType)() override; + + STDMETHOD_(DWORD, SetLOD)(DWORD LODNew) override; + STDMETHOD_(DWORD, GetLOD)() override; + STDMETHOD_(DWORD, GetLevelCount)() override; + STDMETHOD(SetAutoGenFilterType)(D3DTEXTUREFILTERTYPE FilterType) override; + STDMETHOD_(D3DTEXTUREFILTERTYPE, GetAutoGenFilterType)() override; + STDMETHOD_(void, GenerateMipSubLevels)() override; + + STDMETHOD(GetLevelDesc)(UINT Level, D3DVOLUME_DESC* pDesc) override; + STDMETHOD(GetVolumeLevel)(UINT Level, IDirect3DVolume9** ppVolumeLevel) override; + STDMETHOD(LockBox)(UINT Level, D3DLOCKED_BOX* pLockedVolume, const D3DBOX* pBox, DWORD Flags) override; + STDMETHOD(UnlockBox)(UINT Level) override; + STDMETHOD(AddDirtyBox)(const D3DBOX* pDirtyBox) override; + +private: + IDirect3DVolumeTexture9* real_; + std::atomic ref_count_; +}; + +class WrappedCubeTexture9 : public IDirect3DCubeTexture9 { +public: + explicit WrappedCubeTexture9(IDirect3DCubeTexture9* real); + + IDirect3DCubeTexture9* RealTexture() const { return real_; } + + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj) override; + STDMETHOD_(ULONG, AddRef)() override; + STDMETHOD_(ULONG, Release)() override; + + STDMETHOD(GetDevice)(IDirect3DDevice9** ppDevice) override; + STDMETHOD(SetPrivateData)(REFGUID refguid, const void* pData, DWORD SizeOfData, DWORD Flags) override; + STDMETHOD(GetPrivateData)(REFGUID refguid, void* pData, DWORD* pSizeOfData) override; + STDMETHOD(FreePrivateData)(REFGUID refguid) override; + STDMETHOD_(DWORD, SetPriority)(DWORD PriorityNew) override; + STDMETHOD_(DWORD, GetPriority)() override; + STDMETHOD_(void, PreLoad)() override; + STDMETHOD_(D3DRESOURCETYPE, GetType)() override; + + STDMETHOD_(DWORD, SetLOD)(DWORD LODNew) override; + STDMETHOD_(DWORD, GetLOD)() override; + STDMETHOD_(DWORD, GetLevelCount)() override; + STDMETHOD(SetAutoGenFilterType)(D3DTEXTUREFILTERTYPE FilterType) override; + STDMETHOD_(D3DTEXTUREFILTERTYPE, GetAutoGenFilterType)() override; + STDMETHOD_(void, GenerateMipSubLevels)() override; + + STDMETHOD(GetLevelDesc)(UINT Level, D3DSURFACE_DESC* pDesc) override; + STDMETHOD(GetCubeMapSurface)(D3DCUBEMAP_FACES FaceType, UINT Level, IDirect3DSurface9** ppCubeMapSurface) override; + STDMETHOD(LockRect)(D3DCUBEMAP_FACES FaceType, UINT Level, D3DLOCKED_RECT* pLockedRect, const RECT* pRect, DWORD Flags) override; + STDMETHOD(UnlockRect)(D3DCUBEMAP_FACES FaceType, UINT Level) override; + STDMETHOD(AddDirtyRect)(D3DCUBEMAP_FACES FaceType, const RECT* pDirtyRect) override; + +private: + IDirect3DCubeTexture9* real_; + std::atomic ref_count_; +}; + +// Real texture pointer -> wrapper, for the D3D9 calls that only ever hand back real +// pointers (e.g. IDirect3DDevice9::GetTexture) so they can be turned back into the +// wrapper identity the game must see. Populated by each wrapper's constructor, erased +// in its destructor/Release. +IDirect3DBaseTexture9* FindWrapperForRealTexture(IDirect3DBaseTexture9* real); +void RegisterWrapperForRealTexture(IDirect3DBaseTexture9* real, IDirect3DBaseTexture9* wrapper); +void UnregisterWrapperForRealTexture(IDirect3DBaseTexture9* real); diff --git a/header/D3D9Wrappers.h b/header/D3D9Wrappers.h new file mode 100644 index 0000000..6ac2d63 --- /dev/null +++ b/header/D3D9Wrappers.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +// Wraps a real IDirect3D9 so CreateDevice is intercepted via ordinary compiled virtual +// dispatch instead of a MinHook-patched vtable slot (unstable under ARM64 emulation). +// Every other method is a pure forward to the real object. +class WrappedDirect3D9 : public IDirect3D9 { +public: + explicit WrappedDirect3D9(IDirect3D9* real); + + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj) override; + STDMETHOD_(ULONG, AddRef)() override; + STDMETHOD_(ULONG, Release)() override; + + STDMETHOD(RegisterSoftwareDevice)(void* pInitializeFunction) override; + STDMETHOD_(UINT, GetAdapterCount)() override; + STDMETHOD(GetAdapterIdentifier)(UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier) override; + STDMETHOD_(UINT, GetAdapterModeCount)(UINT Adapter, D3DFORMAT Format) override; + STDMETHOD(EnumAdapterModes)(UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE* pMode) override; + STDMETHOD(GetAdapterDisplayMode)(UINT Adapter, D3DDISPLAYMODE* pMode) override; + STDMETHOD(CheckDeviceType)(UINT Adapter, D3DDEVTYPE DevType, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed) override; + STDMETHOD(CheckDeviceFormat)(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat) override; + STDMETHOD(CheckDeviceMultiSampleType)(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels) override; + STDMETHOD(CheckDepthStencilMatch)(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat) override; + STDMETHOD(CheckDeviceFormatConversion)(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat) override; + STDMETHOD(GetDeviceCaps)(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) override; + STDMETHOD_(HMONITOR, GetAdapterMonitor)(UINT Adapter) override; + STDMETHOD(CreateDevice)(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface) override; + +protected: + IDirect3D9* real_; + std::atomic ref_count_; +}; + +class WrappedDirect3D9Ex : public IDirect3D9Ex { +public: + explicit WrappedDirect3D9Ex(IDirect3D9Ex* real); + + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj) override; + STDMETHOD_(ULONG, AddRef)() override; + STDMETHOD_(ULONG, Release)() override; + + STDMETHOD(RegisterSoftwareDevice)(void* pInitializeFunction) override; + STDMETHOD_(UINT, GetAdapterCount)() override; + STDMETHOD(GetAdapterIdentifier)(UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier) override; + STDMETHOD_(UINT, GetAdapterModeCount)(UINT Adapter, D3DFORMAT Format) override; + STDMETHOD(EnumAdapterModes)(UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE* pMode) override; + STDMETHOD(GetAdapterDisplayMode)(UINT Adapter, D3DDISPLAYMODE* pMode) override; + STDMETHOD(CheckDeviceType)(UINT Adapter, D3DDEVTYPE DevType, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed) override; + STDMETHOD(CheckDeviceFormat)(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat) override; + STDMETHOD(CheckDeviceMultiSampleType)(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels) override; + STDMETHOD(CheckDepthStencilMatch)(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat) override; + STDMETHOD(CheckDeviceFormatConversion)(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat) override; + STDMETHOD(GetDeviceCaps)(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) override; + STDMETHOD_(HMONITOR, GetAdapterMonitor)(UINT Adapter) override; + STDMETHOD(CreateDevice)(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface) override; + + STDMETHOD_(UINT, GetAdapterModeCountEx)(UINT Adapter, const D3DDISPLAYMODEFILTER* pFilter) override; + STDMETHOD(EnumAdapterModesEx)(UINT Adapter, const D3DDISPLAYMODEFILTER* pFilter, UINT Mode, D3DDISPLAYMODEEX* pMode) override; + STDMETHOD(GetAdapterDisplayModeEx)(UINT Adapter, D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation) override; + STDMETHOD(CreateDeviceEx)(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode, IDirect3DDevice9Ex** ppReturnedDeviceInterface) override; + STDMETHOD(GetAdapterLUID)(UINT Adapter, LUID* pLUID) override; + +private: + IDirect3D9Ex* real_; + std::atomic ref_count_; +}; diff --git a/source/D3D9DeviceWrapper.cpp b/source/D3D9DeviceWrapper.cpp new file mode 100644 index 0000000..ee31820 --- /dev/null +++ b/source/D3D9DeviceWrapper.cpp @@ -0,0 +1,769 @@ +#include "Main.h" +#include "D3D9DeviceWrapper.h" +#include "D3D9TextureWrappers.h" + +import TextureClient; + +namespace { + + // Unwraps a WrappedTexture9/WrappedVolumeTexture9/WrappedCubeTexture9 to the real + // pointer the real device needs; passes non-wrapper (or null) pointers through as-is. + IDirect3DBaseTexture9* Unwrap(IDirect3DBaseTexture9* texture) + { + if (texture == nullptr) return nullptr; + switch (texture->GetType()) { + case D3DRTYPE_TEXTURE: return static_cast(texture)->RealTexture(); + case D3DRTYPE_VOLUMETEXTURE: return static_cast(texture)->RealTexture(); + case D3DRTYPE_CUBETEXTURE: return static_cast(texture)->RealTexture(); + default: return texture; + } + } + +} + +// ---- WrappedDirect3DDevice9 ---------------------------------------------- + +WrappedDirect3DDevice9::WrappedDirect3DDevice9(IDirect3DDevice9* real) : real_(real), ref_count_(1), client_(nullptr) +{ + real_->AddRef(); +} + +HRESULT WrappedDirect3DDevice9::QueryInterface(REFIID riid, void** ppvObj) +{ + if (riid == IID_IUnknown || riid == IID_IDirect3DDevice9) { + AddRef(); + *ppvObj = this; + return S_OK; + } + // Unrecognized-but-successful QueryInterface hands back an unwrapped real pointer. + return real_->QueryInterface(riid, ppvObj); +} + +ULONG WrappedDirect3DDevice9::AddRef() +{ + return ++ref_count_; +} + +void WrappedDirect3DDevice9::TeardownTextureClient() +{ + if (auto* client = TextureClient::CurrentClient()) + delete client; +} + +ULONG WrappedDirect3DDevice9::Release() +{ + const ULONG count = --ref_count_; + if (count == 0) { + TeardownTextureClient(); + real_->Release(); + delete this; + return 0; + } + return count; +} + +HRESULT WrappedDirect3DDevice9::TestCooperativeLevel() +{ return real_->TestCooperativeLevel(); } +UINT WrappedDirect3DDevice9::GetAvailableTextureMem() +{ return real_->GetAvailableTextureMem(); } +HRESULT WrappedDirect3DDevice9::EvictManagedResources() +{ return real_->EvictManagedResources(); } +HRESULT WrappedDirect3DDevice9::GetDirect3D(IDirect3D9** ppD3D9) +{ return real_->GetDirect3D(ppD3D9); } +HRESULT WrappedDirect3DDevice9::GetDeviceCaps(D3DCAPS9* pCaps) +{ return real_->GetDeviceCaps(pCaps); } +HRESULT WrappedDirect3DDevice9::GetDisplayMode(UINT iSwapChain, D3DDISPLAYMODE* pMode) +{ return real_->GetDisplayMode(iSwapChain, pMode); } +HRESULT WrappedDirect3DDevice9::GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters) +{ return real_->GetCreationParameters(pParameters); } +HRESULT WrappedDirect3DDevice9::SetCursorProperties(UINT XHotSpot, UINT YHotSpot, IDirect3DSurface9* pCursorBitmap) +{ return real_->SetCursorProperties(XHotSpot, YHotSpot, pCursorBitmap); } +void WrappedDirect3DDevice9::SetCursorPosition(int X, int Y, DWORD Flags) +{ real_->SetCursorPosition(X, Y, Flags); } +BOOL WrappedDirect3DDevice9::ShowCursor(BOOL bShow) +{ return real_->ShowCursor(bShow); } +HRESULT WrappedDirect3DDevice9::CreateAdditionalSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DSwapChain9** pSwapChain) +{ return real_->CreateAdditionalSwapChain(pPresentationParameters, pSwapChain); } +HRESULT WrappedDirect3DDevice9::GetSwapChain(UINT iSwapChain, IDirect3DSwapChain9** pSwapChain) +{ return real_->GetSwapChain(iSwapChain, pSwapChain); } +UINT WrappedDirect3DDevice9::GetNumberOfSwapChains() +{ return real_->GetNumberOfSwapChains(); } +HRESULT WrappedDirect3DDevice9::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) +{ return real_->Reset(pPresentationParameters); } +HRESULT WrappedDirect3DDevice9::Present(const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion) +{ return real_->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); } +HRESULT WrappedDirect3DDevice9::GetBackBuffer(UINT iSwapChain, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface9** ppBackBuffer) +{ return real_->GetBackBuffer(iSwapChain, iBackBuffer, Type, ppBackBuffer); } +HRESULT WrappedDirect3DDevice9::GetRasterStatus(UINT iSwapChain, D3DRASTER_STATUS* pRasterStatus) +{ return real_->GetRasterStatus(iSwapChain, pRasterStatus); } +HRESULT WrappedDirect3DDevice9::SetDialogBoxMode(BOOL bEnableDialogs) +{ return real_->SetDialogBoxMode(bEnableDialogs); } +void WrappedDirect3DDevice9::SetGammaRamp(UINT iSwapChain, DWORD Flags, const D3DGAMMARAMP* pRamp) +{ real_->SetGammaRamp(iSwapChain, Flags, pRamp); } +void WrappedDirect3DDevice9::GetGammaRamp(UINT iSwapChain, D3DGAMMARAMP* pRamp) +{ real_->GetGammaRamp(iSwapChain, pRamp); } + +HRESULT WrappedDirect3DDevice9::CreateTexture(UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle) +{ + IDirect3DTexture9* real_texture = nullptr; + const HRESULT hr = real_->CreateTexture(Width, Height, Levels, Usage, Format, Pool, &real_texture, pSharedHandle); + if (FAILED(hr) || real_texture == nullptr) { + if (ppTexture) *ppTexture = nullptr; + return hr; + } + const auto wrapped = new WrappedTexture9(real_texture); + real_texture->Release(); // the wrapper holds its own AddRef'd reference + if (client_) client_->OnCreateTexture(wrapped, TexType::Tex2D); + *ppTexture = wrapped; + return hr; +} + +HRESULT WrappedDirect3DDevice9::CreateVolumeTexture(UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DVolumeTexture9** ppVolumeTexture, HANDLE* pSharedHandle) +{ + IDirect3DVolumeTexture9* real_texture = nullptr; + const HRESULT hr = real_->CreateVolumeTexture(Width, Height, Depth, Levels, Usage, Format, Pool, &real_texture, pSharedHandle); + if (FAILED(hr) || real_texture == nullptr) { + if (ppVolumeTexture) *ppVolumeTexture = nullptr; + return hr; + } + const auto wrapped = new WrappedVolumeTexture9(real_texture); + real_texture->Release(); + if (client_) client_->OnCreateTexture(wrapped, TexType::Volume); + *ppVolumeTexture = wrapped; + return hr; +} + +HRESULT WrappedDirect3DDevice9::CreateCubeTexture(UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DCubeTexture9** ppCubeTexture, HANDLE* pSharedHandle) +{ + IDirect3DCubeTexture9* real_texture = nullptr; + const HRESULT hr = real_->CreateCubeTexture(EdgeLength, Levels, Usage, Format, Pool, &real_texture, pSharedHandle); + if (FAILED(hr) || real_texture == nullptr) { + if (ppCubeTexture) *ppCubeTexture = nullptr; + return hr; + } + const auto wrapped = new WrappedCubeTexture9(real_texture); + real_texture->Release(); + if (client_) client_->OnCreateTexture(wrapped, TexType::Cube); + *ppCubeTexture = wrapped; + return hr; +} + +HRESULT WrappedDirect3DDevice9::CreateVertexBuffer(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle) +{ return real_->CreateVertexBuffer(Length, Usage, FVF, Pool, ppVertexBuffer, pSharedHandle); } +HRESULT WrappedDirect3DDevice9::CreateIndexBuffer(UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pSharedHandle) +{ return real_->CreateIndexBuffer(Length, Usage, Format, Pool, ppIndexBuffer, pSharedHandle); } +HRESULT WrappedDirect3DDevice9::CreateRenderTarget(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) +{ return real_->CreateRenderTarget(Width, Height, Format, MultiSample, MultisampleQuality, Lockable, ppSurface, pSharedHandle); } +HRESULT WrappedDirect3DDevice9::CreateDepthStencilSurface(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) +{ return real_->CreateDepthStencilSurface(Width, Height, Format, MultiSample, MultisampleQuality, Discard, ppSurface, pSharedHandle); } +HRESULT WrappedDirect3DDevice9::UpdateSurface(IDirect3DSurface9* pSourceSurface, const RECT* pSourceRect, IDirect3DSurface9* pDestinationSurface, const POINT* pDestPoint) +{ return real_->UpdateSurface(pSourceSurface, pSourceRect, pDestinationSurface, pDestPoint); } + +HRESULT WrappedDirect3DDevice9::UpdateTexture(IDirect3DBaseTexture9* pSourceTexture, IDirect3DBaseTexture9* pDestinationTexture) +{ + const HRESULT hr = real_->UpdateTexture(Unwrap(pSourceTexture), Unwrap(pDestinationTexture)); + if (SUCCEEDED(hr) && client_) + client_->OnUpdateTexture(pSourceTexture, pDestinationTexture); // re-hash by wrapper identity + return hr; +} + +HRESULT WrappedDirect3DDevice9::GetRenderTargetData(IDirect3DSurface9* pRenderTarget, IDirect3DSurface9* pDestSurface) +{ return real_->GetRenderTargetData(pRenderTarget, pDestSurface); } +HRESULT WrappedDirect3DDevice9::GetFrontBufferData(UINT iSwapChain, IDirect3DSurface9* pDestSurface) +{ return real_->GetFrontBufferData(iSwapChain, pDestSurface); } +HRESULT WrappedDirect3DDevice9::StretchRect(IDirect3DSurface9* pSourceSurface, const RECT* pSourceRect, IDirect3DSurface9* pDestSurface, const RECT* pDestRect, D3DTEXTUREFILTERTYPE Filter) +{ return real_->StretchRect(pSourceSurface, pSourceRect, pDestSurface, pDestRect, Filter); } +HRESULT WrappedDirect3DDevice9::ColorFill(IDirect3DSurface9* pSurface, const RECT* pRect, D3DCOLOR color) +{ return real_->ColorFill(pSurface, pRect, color); } +HRESULT WrappedDirect3DDevice9::CreateOffscreenPlainSurface(UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) +{ return real_->CreateOffscreenPlainSurface(Width, Height, Format, Pool, ppSurface, pSharedHandle); } +HRESULT WrappedDirect3DDevice9::SetRenderTarget(DWORD RenderTargetIndex, IDirect3DSurface9* pRenderTarget) +{ return real_->SetRenderTarget(RenderTargetIndex, pRenderTarget); } +HRESULT WrappedDirect3DDevice9::GetRenderTarget(DWORD RenderTargetIndex, IDirect3DSurface9** ppRenderTarget) +{ return real_->GetRenderTarget(RenderTargetIndex, ppRenderTarget); } +HRESULT WrappedDirect3DDevice9::SetDepthStencilSurface(IDirect3DSurface9* pNewZStencil) +{ return real_->SetDepthStencilSurface(pNewZStencil); } +HRESULT WrappedDirect3DDevice9::GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface) +{ return real_->GetDepthStencilSurface(ppZStencilSurface); } + +HRESULT WrappedDirect3DDevice9::BeginScene() +{ + if (client_) client_->OnBeginScene(); + return real_->BeginScene(); +} + +HRESULT WrappedDirect3DDevice9::EndScene() +{ return real_->EndScene(); } +HRESULT WrappedDirect3DDevice9::Clear(DWORD Count, const D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil) +{ return real_->Clear(Count, pRects, Flags, Color, Z, Stencil); } +HRESULT WrappedDirect3DDevice9::SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix) +{ return real_->SetTransform(State, pMatrix); } +HRESULT WrappedDirect3DDevice9::GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) +{ return real_->GetTransform(State, pMatrix); } +HRESULT WrappedDirect3DDevice9::MultiplyTransform(D3DTRANSFORMSTATETYPE state, const D3DMATRIX* matrix) +{ return real_->MultiplyTransform(state, matrix); } +HRESULT WrappedDirect3DDevice9::SetViewport(const D3DVIEWPORT9* pViewport) +{ return real_->SetViewport(pViewport); } +HRESULT WrappedDirect3DDevice9::GetViewport(D3DVIEWPORT9* pViewport) +{ return real_->GetViewport(pViewport); } +HRESULT WrappedDirect3DDevice9::SetMaterial(const D3DMATERIAL9* pMaterial) +{ return real_->SetMaterial(pMaterial); } +HRESULT WrappedDirect3DDevice9::GetMaterial(D3DMATERIAL9* pMaterial) +{ return real_->GetMaterial(pMaterial); } +HRESULT WrappedDirect3DDevice9::SetLight(DWORD Index, const D3DLIGHT9* light) +{ return real_->SetLight(Index, light); } +HRESULT WrappedDirect3DDevice9::GetLight(DWORD Index, D3DLIGHT9* light) +{ return real_->GetLight(Index, light); } +HRESULT WrappedDirect3DDevice9::LightEnable(DWORD Index, BOOL Enable) +{ return real_->LightEnable(Index, Enable); } +HRESULT WrappedDirect3DDevice9::GetLightEnable(DWORD Index, BOOL* pEnable) +{ return real_->GetLightEnable(Index, pEnable); } +HRESULT WrappedDirect3DDevice9::SetClipPlane(DWORD Index, const float* pPlane) +{ return real_->SetClipPlane(Index, pPlane); } +HRESULT WrappedDirect3DDevice9::GetClipPlane(DWORD Index, float* pPlane) +{ return real_->GetClipPlane(Index, pPlane); } +HRESULT WrappedDirect3DDevice9::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) +{ return real_->SetRenderState(State, Value); } +HRESULT WrappedDirect3DDevice9::GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue) +{ return real_->GetRenderState(State, pValue); } +HRESULT WrappedDirect3DDevice9::CreateStateBlock(D3DSTATEBLOCKTYPE Type, IDirect3DStateBlock9** ppSB) +{ return real_->CreateStateBlock(Type, ppSB); } +HRESULT WrappedDirect3DDevice9::BeginStateBlock() +{ return real_->BeginStateBlock(); } +HRESULT WrappedDirect3DDevice9::EndStateBlock(IDirect3DStateBlock9** ppSB) +{ return real_->EndStateBlock(ppSB); } +HRESULT WrappedDirect3DDevice9::SetClipStatus(const D3DCLIPSTATUS9* pClipStatus) +{ return real_->SetClipStatus(pClipStatus); } +HRESULT WrappedDirect3DDevice9::GetClipStatus(D3DCLIPSTATUS9* pClipStatus) +{ return real_->GetClipStatus(pClipStatus); } + +HRESULT WrappedDirect3DDevice9::GetTexture(DWORD Stage, IDirect3DBaseTexture9** ppTexture) +{ + const HRESULT hr = real_->GetTexture(Stage, ppTexture); + if (FAILED(hr) || !ppTexture || !*ppTexture) return hr; + + // The real call only ever returns real pointers; recover the wrapper identity. + auto* wrapper = FindWrapperForRealTexture(*ppTexture); + if (wrapper == nullptr) { + // Not one of ours (created before hooks/pass-through path) - nothing to translate. + return hr; + } + (*ppTexture)->Release(); // drop the real ref the runtime handed back + IDirect3DBaseTexture9* result = wrapper; + + if (client_) { + if (auto* original = client_->ResolveOriginalFromFake(wrapper)) { + original->AddRef(); + result = original; + } + else { + wrapper->AddRef(); + } + } + else { + wrapper->AddRef(); + } + *ppTexture = result; + return hr; +} + +HRESULT WrappedDirect3DDevice9::SetTexture(DWORD Stage, IDirect3DBaseTexture9* pTexture) +{ + IDirect3DBaseTexture9* bind = client_ ? client_->ResolveBinding(pTexture) : pTexture; + return real_->SetTexture(Stage, Unwrap(bind)); +} + +HRESULT WrappedDirect3DDevice9::GetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue) +{ return real_->GetTextureStageState(Stage, Type, pValue); } +HRESULT WrappedDirect3DDevice9::SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value) +{ return real_->SetTextureStageState(Stage, Type, Value); } +HRESULT WrappedDirect3DDevice9::GetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD* pValue) +{ return real_->GetSamplerState(Sampler, Type, pValue); } +HRESULT WrappedDirect3DDevice9::SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value) +{ return real_->SetSamplerState(Sampler, Type, Value); } +HRESULT WrappedDirect3DDevice9::ValidateDevice(DWORD* pNumPasses) +{ return real_->ValidateDevice(pNumPasses); } +HRESULT WrappedDirect3DDevice9::SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries) +{ return real_->SetPaletteEntries(PaletteNumber, pEntries); } +HRESULT WrappedDirect3DDevice9::GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries) +{ return real_->GetPaletteEntries(PaletteNumber, pEntries); } +HRESULT WrappedDirect3DDevice9::SetCurrentTexturePalette(UINT PaletteNumber) +{ return real_->SetCurrentTexturePalette(PaletteNumber); } +HRESULT WrappedDirect3DDevice9::GetCurrentTexturePalette(UINT* PaletteNumber) +{ return real_->GetCurrentTexturePalette(PaletteNumber); } +HRESULT WrappedDirect3DDevice9::SetScissorRect(const RECT* pRect) +{ return real_->SetScissorRect(pRect); } +HRESULT WrappedDirect3DDevice9::GetScissorRect(RECT* pRect) +{ return real_->GetScissorRect(pRect); } +HRESULT WrappedDirect3DDevice9::SetSoftwareVertexProcessing(BOOL bSoftware) +{ return real_->SetSoftwareVertexProcessing(bSoftware); } +BOOL WrappedDirect3DDevice9::GetSoftwareVertexProcessing() +{ return real_->GetSoftwareVertexProcessing(); } +HRESULT WrappedDirect3DDevice9::SetNPatchMode(float nSegments) +{ return real_->SetNPatchMode(nSegments); } +float WrappedDirect3DDevice9::GetNPatchMode() +{ return real_->GetNPatchMode(); } +HRESULT WrappedDirect3DDevice9::DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount) +{ return real_->DrawPrimitive(PrimitiveType, StartVertex, PrimitiveCount); } +HRESULT WrappedDirect3DDevice9::DrawIndexedPrimitive(D3DPRIMITIVETYPE type, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount) +{ return real_->DrawIndexedPrimitive(type, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount); } +HRESULT WrappedDirect3DDevice9::DrawPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride) +{ return real_->DrawPrimitiveUP(PrimitiveType, PrimitiveCount, pVertexStreamZeroData, VertexStreamZeroStride); } +HRESULT WrappedDirect3DDevice9::DrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount, const void* pIndexData, D3DFORMAT IndexDataFormat, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride) +{ return real_->DrawIndexedPrimitiveUP(PrimitiveType, MinVertexIndex, NumVertices, PrimitiveCount, pIndexData, IndexDataFormat, pVertexStreamZeroData, VertexStreamZeroStride); } +HRESULT WrappedDirect3DDevice9::ProcessVertices(UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, IDirect3DVertexBuffer9* pDestBuffer, IDirect3DVertexDeclaration9* pVertexDecl, DWORD Flags) +{ return real_->ProcessVertices(SrcStartIndex, DestIndex, VertexCount, pDestBuffer, pVertexDecl, Flags); } +HRESULT WrappedDirect3DDevice9::CreateVertexDeclaration(const D3DVERTEXELEMENT9* pVertexElements, IDirect3DVertexDeclaration9** ppDecl) +{ return real_->CreateVertexDeclaration(pVertexElements, ppDecl); } +HRESULT WrappedDirect3DDevice9::SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl) +{ return real_->SetVertexDeclaration(pDecl); } +HRESULT WrappedDirect3DDevice9::GetVertexDeclaration(IDirect3DVertexDeclaration9** ppDecl) +{ return real_->GetVertexDeclaration(ppDecl); } +HRESULT WrappedDirect3DDevice9::SetFVF(DWORD FVF) +{ return real_->SetFVF(FVF); } +HRESULT WrappedDirect3DDevice9::GetFVF(DWORD* pFVF) +{ return real_->GetFVF(pFVF); } +HRESULT WrappedDirect3DDevice9::CreateVertexShader(const DWORD* pFunction, IDirect3DVertexShader9** ppShader) +{ return real_->CreateVertexShader(pFunction, ppShader); } +HRESULT WrappedDirect3DDevice9::SetVertexShader(IDirect3DVertexShader9* pShader) +{ return real_->SetVertexShader(pShader); } +HRESULT WrappedDirect3DDevice9::GetVertexShader(IDirect3DVertexShader9** ppShader) +{ return real_->GetVertexShader(ppShader); } +HRESULT WrappedDirect3DDevice9::SetVertexShaderConstantF(UINT StartRegister, const float* pConstantData, UINT Vector4fCount) +{ return real_->SetVertexShaderConstantF(StartRegister, pConstantData, Vector4fCount); } +HRESULT WrappedDirect3DDevice9::GetVertexShaderConstantF(UINT StartRegister, float* pConstantData, UINT Vector4fCount) +{ return real_->GetVertexShaderConstantF(StartRegister, pConstantData, Vector4fCount); } +HRESULT WrappedDirect3DDevice9::SetVertexShaderConstantI(UINT StartRegister, const int* pConstantData, UINT Vector4iCount) +{ return real_->SetVertexShaderConstantI(StartRegister, pConstantData, Vector4iCount); } +HRESULT WrappedDirect3DDevice9::GetVertexShaderConstantI(UINT StartRegister, int* pConstantData, UINT Vector4iCount) +{ return real_->GetVertexShaderConstantI(StartRegister, pConstantData, Vector4iCount); } +HRESULT WrappedDirect3DDevice9::SetVertexShaderConstantB(UINT StartRegister, const BOOL* pConstantData, UINT BoolCount) +{ return real_->SetVertexShaderConstantB(StartRegister, pConstantData, BoolCount); } +HRESULT WrappedDirect3DDevice9::GetVertexShaderConstantB(UINT StartRegister, BOOL* pConstantData, UINT BoolCount) +{ return real_->GetVertexShaderConstantB(StartRegister, pConstantData, BoolCount); } +HRESULT WrappedDirect3DDevice9::SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9* pStreamData, UINT OffsetInBytes, UINT Stride) +{ return real_->SetStreamSource(StreamNumber, pStreamData, OffsetInBytes, Stride); } +HRESULT WrappedDirect3DDevice9::GetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9** ppStreamData, UINT* pOffsetInBytes, UINT* pStride) +{ return real_->GetStreamSource(StreamNumber, ppStreamData, pOffsetInBytes, pStride); } +HRESULT WrappedDirect3DDevice9::SetStreamSourceFreq(UINT StreamNumber, UINT Setting) +{ return real_->SetStreamSourceFreq(StreamNumber, Setting); } +HRESULT WrappedDirect3DDevice9::GetStreamSourceFreq(UINT StreamNumber, UINT* pSetting) +{ return real_->GetStreamSourceFreq(StreamNumber, pSetting); } +HRESULT WrappedDirect3DDevice9::SetIndices(IDirect3DIndexBuffer9* pIndexData) +{ return real_->SetIndices(pIndexData); } +HRESULT WrappedDirect3DDevice9::GetIndices(IDirect3DIndexBuffer9** ppIndexData) +{ return real_->GetIndices(ppIndexData); } +HRESULT WrappedDirect3DDevice9::CreatePixelShader(const DWORD* pFunction, IDirect3DPixelShader9** ppShader) +{ return real_->CreatePixelShader(pFunction, ppShader); } +HRESULT WrappedDirect3DDevice9::SetPixelShader(IDirect3DPixelShader9* pShader) +{ return real_->SetPixelShader(pShader); } +HRESULT WrappedDirect3DDevice9::GetPixelShader(IDirect3DPixelShader9** ppShader) +{ return real_->GetPixelShader(ppShader); } +HRESULT WrappedDirect3DDevice9::SetPixelShaderConstantF(UINT StartRegister, const float* pConstantData, UINT Vector4fCount) +{ return real_->SetPixelShaderConstantF(StartRegister, pConstantData, Vector4fCount); } +HRESULT WrappedDirect3DDevice9::GetPixelShaderConstantF(UINT StartRegister, float* pConstantData, UINT Vector4fCount) +{ return real_->GetPixelShaderConstantF(StartRegister, pConstantData, Vector4fCount); } +HRESULT WrappedDirect3DDevice9::SetPixelShaderConstantI(UINT StartRegister, const int* pConstantData, UINT Vector4iCount) +{ return real_->SetPixelShaderConstantI(StartRegister, pConstantData, Vector4iCount); } +HRESULT WrappedDirect3DDevice9::GetPixelShaderConstantI(UINT StartRegister, int* pConstantData, UINT Vector4iCount) +{ return real_->GetPixelShaderConstantI(StartRegister, pConstantData, Vector4iCount); } +HRESULT WrappedDirect3DDevice9::SetPixelShaderConstantB(UINT StartRegister, const BOOL* pConstantData, UINT BoolCount) +{ return real_->SetPixelShaderConstantB(StartRegister, pConstantData, BoolCount); } +HRESULT WrappedDirect3DDevice9::GetPixelShaderConstantB(UINT StartRegister, BOOL* pConstantData, UINT BoolCount) +{ return real_->GetPixelShaderConstantB(StartRegister, pConstantData, BoolCount); } +HRESULT WrappedDirect3DDevice9::DrawRectPatch(UINT Handle, const float* pNumSegs, const D3DRECTPATCH_INFO* pRectPatchInfo) +{ return real_->DrawRectPatch(Handle, pNumSegs, pRectPatchInfo); } +HRESULT WrappedDirect3DDevice9::DrawTriPatch(UINT Handle, const float* pNumSegs, const D3DTRIPATCH_INFO* pTriPatchInfo) +{ return real_->DrawTriPatch(Handle, pNumSegs, pTriPatchInfo); } +HRESULT WrappedDirect3DDevice9::DeletePatch(UINT Handle) +{ return real_->DeletePatch(Handle); } +HRESULT WrappedDirect3DDevice9::CreateQuery(D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery) +{ return real_->CreateQuery(Type, ppQuery); } + +// ---- WrappedDirect3DDevice9Ex --------------------------------------------- + +WrappedDirect3DDevice9Ex::WrappedDirect3DDevice9Ex(IDirect3DDevice9Ex* real) : real_(real), ref_count_(1), client_(nullptr) +{ + real_->AddRef(); +} + +HRESULT WrappedDirect3DDevice9Ex::QueryInterface(REFIID riid, void** ppvObj) +{ + if (riid == IID_IUnknown || riid == IID_IDirect3DDevice9 || riid == IID_IDirect3DDevice9Ex) { + AddRef(); + *ppvObj = this; + return S_OK; + } + // Unrecognized-but-successful QueryInterface hands back an unwrapped real pointer. + return real_->QueryInterface(riid, ppvObj); +} + +ULONG WrappedDirect3DDevice9Ex::AddRef() +{ + return ++ref_count_; +} + +void WrappedDirect3DDevice9Ex::TeardownTextureClient() +{ + if (auto* client = TextureClient::CurrentClient()) + delete client; +} + +ULONG WrappedDirect3DDevice9Ex::Release() +{ + const ULONG count = --ref_count_; + if (count == 0) { + TeardownTextureClient(); + real_->Release(); + delete this; + return 0; + } + return count; +} + +HRESULT WrappedDirect3DDevice9Ex::TestCooperativeLevel() +{ return real_->TestCooperativeLevel(); } +UINT WrappedDirect3DDevice9Ex::GetAvailableTextureMem() +{ return real_->GetAvailableTextureMem(); } +HRESULT WrappedDirect3DDevice9Ex::EvictManagedResources() +{ return real_->EvictManagedResources(); } +HRESULT WrappedDirect3DDevice9Ex::GetDirect3D(IDirect3D9** ppD3D9) +{ return real_->GetDirect3D(ppD3D9); } +HRESULT WrappedDirect3DDevice9Ex::GetDeviceCaps(D3DCAPS9* pCaps) +{ return real_->GetDeviceCaps(pCaps); } +HRESULT WrappedDirect3DDevice9Ex::GetDisplayMode(UINT iSwapChain, D3DDISPLAYMODE* pMode) +{ return real_->GetDisplayMode(iSwapChain, pMode); } +HRESULT WrappedDirect3DDevice9Ex::GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters) +{ return real_->GetCreationParameters(pParameters); } +HRESULT WrappedDirect3DDevice9Ex::SetCursorProperties(UINT XHotSpot, UINT YHotSpot, IDirect3DSurface9* pCursorBitmap) +{ return real_->SetCursorProperties(XHotSpot, YHotSpot, pCursorBitmap); } +void WrappedDirect3DDevice9Ex::SetCursorPosition(int X, int Y, DWORD Flags) +{ real_->SetCursorPosition(X, Y, Flags); } +BOOL WrappedDirect3DDevice9Ex::ShowCursor(BOOL bShow) +{ return real_->ShowCursor(bShow); } +HRESULT WrappedDirect3DDevice9Ex::CreateAdditionalSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DSwapChain9** pSwapChain) +{ return real_->CreateAdditionalSwapChain(pPresentationParameters, pSwapChain); } +HRESULT WrappedDirect3DDevice9Ex::GetSwapChain(UINT iSwapChain, IDirect3DSwapChain9** pSwapChain) +{ return real_->GetSwapChain(iSwapChain, pSwapChain); } +UINT WrappedDirect3DDevice9Ex::GetNumberOfSwapChains() +{ return real_->GetNumberOfSwapChains(); } +HRESULT WrappedDirect3DDevice9Ex::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) +{ return real_->Reset(pPresentationParameters); } +HRESULT WrappedDirect3DDevice9Ex::Present(const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion) +{ return real_->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); } +HRESULT WrappedDirect3DDevice9Ex::GetBackBuffer(UINT iSwapChain, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface9** ppBackBuffer) +{ return real_->GetBackBuffer(iSwapChain, iBackBuffer, Type, ppBackBuffer); } +HRESULT WrappedDirect3DDevice9Ex::GetRasterStatus(UINT iSwapChain, D3DRASTER_STATUS* pRasterStatus) +{ return real_->GetRasterStatus(iSwapChain, pRasterStatus); } +HRESULT WrappedDirect3DDevice9Ex::SetDialogBoxMode(BOOL bEnableDialogs) +{ return real_->SetDialogBoxMode(bEnableDialogs); } +void WrappedDirect3DDevice9Ex::SetGammaRamp(UINT iSwapChain, DWORD Flags, const D3DGAMMARAMP* pRamp) +{ real_->SetGammaRamp(iSwapChain, Flags, pRamp); } +void WrappedDirect3DDevice9Ex::GetGammaRamp(UINT iSwapChain, D3DGAMMARAMP* pRamp) +{ real_->GetGammaRamp(iSwapChain, pRamp); } + +HRESULT WrappedDirect3DDevice9Ex::CreateTexture(UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle) +{ + IDirect3DTexture9* real_texture = nullptr; + const HRESULT hr = real_->CreateTexture(Width, Height, Levels, Usage, Format, Pool, &real_texture, pSharedHandle); + if (FAILED(hr) || real_texture == nullptr) { + if (ppTexture) *ppTexture = nullptr; + return hr; + } + const auto wrapped = new WrappedTexture9(real_texture); + real_texture->Release(); + if (client_) client_->OnCreateTexture(wrapped, TexType::Tex2D); + *ppTexture = wrapped; + return hr; +} + +HRESULT WrappedDirect3DDevice9Ex::CreateVolumeTexture(UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DVolumeTexture9** ppVolumeTexture, HANDLE* pSharedHandle) +{ + IDirect3DVolumeTexture9* real_texture = nullptr; + const HRESULT hr = real_->CreateVolumeTexture(Width, Height, Depth, Levels, Usage, Format, Pool, &real_texture, pSharedHandle); + if (FAILED(hr) || real_texture == nullptr) { + if (ppVolumeTexture) *ppVolumeTexture = nullptr; + return hr; + } + const auto wrapped = new WrappedVolumeTexture9(real_texture); + real_texture->Release(); + if (client_) client_->OnCreateTexture(wrapped, TexType::Volume); + *ppVolumeTexture = wrapped; + return hr; +} + +HRESULT WrappedDirect3DDevice9Ex::CreateCubeTexture(UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DCubeTexture9** ppCubeTexture, HANDLE* pSharedHandle) +{ + IDirect3DCubeTexture9* real_texture = nullptr; + const HRESULT hr = real_->CreateCubeTexture(EdgeLength, Levels, Usage, Format, Pool, &real_texture, pSharedHandle); + if (FAILED(hr) || real_texture == nullptr) { + if (ppCubeTexture) *ppCubeTexture = nullptr; + return hr; + } + const auto wrapped = new WrappedCubeTexture9(real_texture); + real_texture->Release(); + if (client_) client_->OnCreateTexture(wrapped, TexType::Cube); + *ppCubeTexture = wrapped; + return hr; +} + +HRESULT WrappedDirect3DDevice9Ex::CreateVertexBuffer(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle) +{ return real_->CreateVertexBuffer(Length, Usage, FVF, Pool, ppVertexBuffer, pSharedHandle); } +HRESULT WrappedDirect3DDevice9Ex::CreateIndexBuffer(UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pSharedHandle) +{ return real_->CreateIndexBuffer(Length, Usage, Format, Pool, ppIndexBuffer, pSharedHandle); } +HRESULT WrappedDirect3DDevice9Ex::CreateRenderTarget(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) +{ return real_->CreateRenderTarget(Width, Height, Format, MultiSample, MultisampleQuality, Lockable, ppSurface, pSharedHandle); } +HRESULT WrappedDirect3DDevice9Ex::CreateDepthStencilSurface(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) +{ return real_->CreateDepthStencilSurface(Width, Height, Format, MultiSample, MultisampleQuality, Discard, ppSurface, pSharedHandle); } +HRESULT WrappedDirect3DDevice9Ex::UpdateSurface(IDirect3DSurface9* pSourceSurface, const RECT* pSourceRect, IDirect3DSurface9* pDestinationSurface, const POINT* pDestPoint) +{ return real_->UpdateSurface(pSourceSurface, pSourceRect, pDestinationSurface, pDestPoint); } + +HRESULT WrappedDirect3DDevice9Ex::UpdateTexture(IDirect3DBaseTexture9* pSourceTexture, IDirect3DBaseTexture9* pDestinationTexture) +{ + const HRESULT hr = real_->UpdateTexture(Unwrap(pSourceTexture), Unwrap(pDestinationTexture)); + if (SUCCEEDED(hr) && client_) + client_->OnUpdateTexture(pSourceTexture, pDestinationTexture); + return hr; +} + +HRESULT WrappedDirect3DDevice9Ex::GetRenderTargetData(IDirect3DSurface9* pRenderTarget, IDirect3DSurface9* pDestSurface) +{ return real_->GetRenderTargetData(pRenderTarget, pDestSurface); } +HRESULT WrappedDirect3DDevice9Ex::GetFrontBufferData(UINT iSwapChain, IDirect3DSurface9* pDestSurface) +{ return real_->GetFrontBufferData(iSwapChain, pDestSurface); } +HRESULT WrappedDirect3DDevice9Ex::StretchRect(IDirect3DSurface9* pSourceSurface, const RECT* pSourceRect, IDirect3DSurface9* pDestSurface, const RECT* pDestRect, D3DTEXTUREFILTERTYPE Filter) +{ return real_->StretchRect(pSourceSurface, pSourceRect, pDestSurface, pDestRect, Filter); } +HRESULT WrappedDirect3DDevice9Ex::ColorFill(IDirect3DSurface9* pSurface, const RECT* pRect, D3DCOLOR color) +{ return real_->ColorFill(pSurface, pRect, color); } +HRESULT WrappedDirect3DDevice9Ex::CreateOffscreenPlainSurface(UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) +{ return real_->CreateOffscreenPlainSurface(Width, Height, Format, Pool, ppSurface, pSharedHandle); } +HRESULT WrappedDirect3DDevice9Ex::SetRenderTarget(DWORD RenderTargetIndex, IDirect3DSurface9* pRenderTarget) +{ return real_->SetRenderTarget(RenderTargetIndex, pRenderTarget); } +HRESULT WrappedDirect3DDevice9Ex::GetRenderTarget(DWORD RenderTargetIndex, IDirect3DSurface9** ppRenderTarget) +{ return real_->GetRenderTarget(RenderTargetIndex, ppRenderTarget); } +HRESULT WrappedDirect3DDevice9Ex::SetDepthStencilSurface(IDirect3DSurface9* pNewZStencil) +{ return real_->SetDepthStencilSurface(pNewZStencil); } +HRESULT WrappedDirect3DDevice9Ex::GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface) +{ return real_->GetDepthStencilSurface(ppZStencilSurface); } + +HRESULT WrappedDirect3DDevice9Ex::BeginScene() +{ + if (client_) client_->OnBeginScene(); + return real_->BeginScene(); +} + +HRESULT WrappedDirect3DDevice9Ex::EndScene() +{ return real_->EndScene(); } +HRESULT WrappedDirect3DDevice9Ex::Clear(DWORD Count, const D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil) +{ return real_->Clear(Count, pRects, Flags, Color, Z, Stencil); } +HRESULT WrappedDirect3DDevice9Ex::SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix) +{ return real_->SetTransform(State, pMatrix); } +HRESULT WrappedDirect3DDevice9Ex::GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) +{ return real_->GetTransform(State, pMatrix); } +HRESULT WrappedDirect3DDevice9Ex::MultiplyTransform(D3DTRANSFORMSTATETYPE state, const D3DMATRIX* matrix) +{ return real_->MultiplyTransform(state, matrix); } +HRESULT WrappedDirect3DDevice9Ex::SetViewport(const D3DVIEWPORT9* pViewport) +{ return real_->SetViewport(pViewport); } +HRESULT WrappedDirect3DDevice9Ex::GetViewport(D3DVIEWPORT9* pViewport) +{ return real_->GetViewport(pViewport); } +HRESULT WrappedDirect3DDevice9Ex::SetMaterial(const D3DMATERIAL9* pMaterial) +{ return real_->SetMaterial(pMaterial); } +HRESULT WrappedDirect3DDevice9Ex::GetMaterial(D3DMATERIAL9* pMaterial) +{ return real_->GetMaterial(pMaterial); } +HRESULT WrappedDirect3DDevice9Ex::SetLight(DWORD Index, const D3DLIGHT9* light) +{ return real_->SetLight(Index, light); } +HRESULT WrappedDirect3DDevice9Ex::GetLight(DWORD Index, D3DLIGHT9* light) +{ return real_->GetLight(Index, light); } +HRESULT WrappedDirect3DDevice9Ex::LightEnable(DWORD Index, BOOL Enable) +{ return real_->LightEnable(Index, Enable); } +HRESULT WrappedDirect3DDevice9Ex::GetLightEnable(DWORD Index, BOOL* pEnable) +{ return real_->GetLightEnable(Index, pEnable); } +HRESULT WrappedDirect3DDevice9Ex::SetClipPlane(DWORD Index, const float* pPlane) +{ return real_->SetClipPlane(Index, pPlane); } +HRESULT WrappedDirect3DDevice9Ex::GetClipPlane(DWORD Index, float* pPlane) +{ return real_->GetClipPlane(Index, pPlane); } +HRESULT WrappedDirect3DDevice9Ex::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) +{ return real_->SetRenderState(State, Value); } +HRESULT WrappedDirect3DDevice9Ex::GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue) +{ return real_->GetRenderState(State, pValue); } +HRESULT WrappedDirect3DDevice9Ex::CreateStateBlock(D3DSTATEBLOCKTYPE Type, IDirect3DStateBlock9** ppSB) +{ return real_->CreateStateBlock(Type, ppSB); } +HRESULT WrappedDirect3DDevice9Ex::BeginStateBlock() +{ return real_->BeginStateBlock(); } +HRESULT WrappedDirect3DDevice9Ex::EndStateBlock(IDirect3DStateBlock9** ppSB) +{ return real_->EndStateBlock(ppSB); } +HRESULT WrappedDirect3DDevice9Ex::SetClipStatus(const D3DCLIPSTATUS9* pClipStatus) +{ return real_->SetClipStatus(pClipStatus); } +HRESULT WrappedDirect3DDevice9Ex::GetClipStatus(D3DCLIPSTATUS9* pClipStatus) +{ return real_->GetClipStatus(pClipStatus); } + +HRESULT WrappedDirect3DDevice9Ex::GetTexture(DWORD Stage, IDirect3DBaseTexture9** ppTexture) +{ + const HRESULT hr = real_->GetTexture(Stage, ppTexture); + if (FAILED(hr) || !ppTexture || !*ppTexture) return hr; + + auto* wrapper = FindWrapperForRealTexture(*ppTexture); + if (wrapper == nullptr) { + return hr; + } + (*ppTexture)->Release(); + IDirect3DBaseTexture9* result = wrapper; + + if (client_) { + if (auto* original = client_->ResolveOriginalFromFake(wrapper)) { + original->AddRef(); + result = original; + } + else { + wrapper->AddRef(); + } + } + else { + wrapper->AddRef(); + } + *ppTexture = result; + return hr; +} + +HRESULT WrappedDirect3DDevice9Ex::SetTexture(DWORD Stage, IDirect3DBaseTexture9* pTexture) +{ + IDirect3DBaseTexture9* bind = client_ ? client_->ResolveBinding(pTexture) : pTexture; + return real_->SetTexture(Stage, Unwrap(bind)); +} + +HRESULT WrappedDirect3DDevice9Ex::GetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue) +{ return real_->GetTextureStageState(Stage, Type, pValue); } +HRESULT WrappedDirect3DDevice9Ex::SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value) +{ return real_->SetTextureStageState(Stage, Type, Value); } +HRESULT WrappedDirect3DDevice9Ex::GetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD* pValue) +{ return real_->GetSamplerState(Sampler, Type, pValue); } +HRESULT WrappedDirect3DDevice9Ex::SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value) +{ return real_->SetSamplerState(Sampler, Type, Value); } +HRESULT WrappedDirect3DDevice9Ex::ValidateDevice(DWORD* pNumPasses) +{ return real_->ValidateDevice(pNumPasses); } +HRESULT WrappedDirect3DDevice9Ex::SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries) +{ return real_->SetPaletteEntries(PaletteNumber, pEntries); } +HRESULT WrappedDirect3DDevice9Ex::GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries) +{ return real_->GetPaletteEntries(PaletteNumber, pEntries); } +HRESULT WrappedDirect3DDevice9Ex::SetCurrentTexturePalette(UINT PaletteNumber) +{ return real_->SetCurrentTexturePalette(PaletteNumber); } +HRESULT WrappedDirect3DDevice9Ex::GetCurrentTexturePalette(UINT* PaletteNumber) +{ return real_->GetCurrentTexturePalette(PaletteNumber); } +HRESULT WrappedDirect3DDevice9Ex::SetScissorRect(const RECT* pRect) +{ return real_->SetScissorRect(pRect); } +HRESULT WrappedDirect3DDevice9Ex::GetScissorRect(RECT* pRect) +{ return real_->GetScissorRect(pRect); } +HRESULT WrappedDirect3DDevice9Ex::SetSoftwareVertexProcessing(BOOL bSoftware) +{ return real_->SetSoftwareVertexProcessing(bSoftware); } +BOOL WrappedDirect3DDevice9Ex::GetSoftwareVertexProcessing() +{ return real_->GetSoftwareVertexProcessing(); } +HRESULT WrappedDirect3DDevice9Ex::SetNPatchMode(float nSegments) +{ return real_->SetNPatchMode(nSegments); } +float WrappedDirect3DDevice9Ex::GetNPatchMode() +{ return real_->GetNPatchMode(); } +HRESULT WrappedDirect3DDevice9Ex::DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount) +{ return real_->DrawPrimitive(PrimitiveType, StartVertex, PrimitiveCount); } +HRESULT WrappedDirect3DDevice9Ex::DrawIndexedPrimitive(D3DPRIMITIVETYPE type, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount) +{ return real_->DrawIndexedPrimitive(type, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount); } +HRESULT WrappedDirect3DDevice9Ex::DrawPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride) +{ return real_->DrawPrimitiveUP(PrimitiveType, PrimitiveCount, pVertexStreamZeroData, VertexStreamZeroStride); } +HRESULT WrappedDirect3DDevice9Ex::DrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount, const void* pIndexData, D3DFORMAT IndexDataFormat, const void* pVertexStreamZeroData, UINT VertexStreamZeroStride) +{ return real_->DrawIndexedPrimitiveUP(PrimitiveType, MinVertexIndex, NumVertices, PrimitiveCount, pIndexData, IndexDataFormat, pVertexStreamZeroData, VertexStreamZeroStride); } +HRESULT WrappedDirect3DDevice9Ex::ProcessVertices(UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, IDirect3DVertexBuffer9* pDestBuffer, IDirect3DVertexDeclaration9* pVertexDecl, DWORD Flags) +{ return real_->ProcessVertices(SrcStartIndex, DestIndex, VertexCount, pDestBuffer, pVertexDecl, Flags); } +HRESULT WrappedDirect3DDevice9Ex::CreateVertexDeclaration(const D3DVERTEXELEMENT9* pVertexElements, IDirect3DVertexDeclaration9** ppDecl) +{ return real_->CreateVertexDeclaration(pVertexElements, ppDecl); } +HRESULT WrappedDirect3DDevice9Ex::SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl) +{ return real_->SetVertexDeclaration(pDecl); } +HRESULT WrappedDirect3DDevice9Ex::GetVertexDeclaration(IDirect3DVertexDeclaration9** ppDecl) +{ return real_->GetVertexDeclaration(ppDecl); } +HRESULT WrappedDirect3DDevice9Ex::SetFVF(DWORD FVF) +{ return real_->SetFVF(FVF); } +HRESULT WrappedDirect3DDevice9Ex::GetFVF(DWORD* pFVF) +{ return real_->GetFVF(pFVF); } +HRESULT WrappedDirect3DDevice9Ex::CreateVertexShader(const DWORD* pFunction, IDirect3DVertexShader9** ppShader) +{ return real_->CreateVertexShader(pFunction, ppShader); } +HRESULT WrappedDirect3DDevice9Ex::SetVertexShader(IDirect3DVertexShader9* pShader) +{ return real_->SetVertexShader(pShader); } +HRESULT WrappedDirect3DDevice9Ex::GetVertexShader(IDirect3DVertexShader9** ppShader) +{ return real_->GetVertexShader(ppShader); } +HRESULT WrappedDirect3DDevice9Ex::SetVertexShaderConstantF(UINT StartRegister, const float* pConstantData, UINT Vector4fCount) +{ return real_->SetVertexShaderConstantF(StartRegister, pConstantData, Vector4fCount); } +HRESULT WrappedDirect3DDevice9Ex::GetVertexShaderConstantF(UINT StartRegister, float* pConstantData, UINT Vector4fCount) +{ return real_->GetVertexShaderConstantF(StartRegister, pConstantData, Vector4fCount); } +HRESULT WrappedDirect3DDevice9Ex::SetVertexShaderConstantI(UINT StartRegister, const int* pConstantData, UINT Vector4iCount) +{ return real_->SetVertexShaderConstantI(StartRegister, pConstantData, Vector4iCount); } +HRESULT WrappedDirect3DDevice9Ex::GetVertexShaderConstantI(UINT StartRegister, int* pConstantData, UINT Vector4iCount) +{ return real_->GetVertexShaderConstantI(StartRegister, pConstantData, Vector4iCount); } +HRESULT WrappedDirect3DDevice9Ex::SetVertexShaderConstantB(UINT StartRegister, const BOOL* pConstantData, UINT BoolCount) +{ return real_->SetVertexShaderConstantB(StartRegister, pConstantData, BoolCount); } +HRESULT WrappedDirect3DDevice9Ex::GetVertexShaderConstantB(UINT StartRegister, BOOL* pConstantData, UINT BoolCount) +{ return real_->GetVertexShaderConstantB(StartRegister, pConstantData, BoolCount); } +HRESULT WrappedDirect3DDevice9Ex::SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9* pStreamData, UINT OffsetInBytes, UINT Stride) +{ return real_->SetStreamSource(StreamNumber, pStreamData, OffsetInBytes, Stride); } +HRESULT WrappedDirect3DDevice9Ex::GetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9** ppStreamData, UINT* pOffsetInBytes, UINT* pStride) +{ return real_->GetStreamSource(StreamNumber, ppStreamData, pOffsetInBytes, pStride); } +HRESULT WrappedDirect3DDevice9Ex::SetStreamSourceFreq(UINT StreamNumber, UINT Setting) +{ return real_->SetStreamSourceFreq(StreamNumber, Setting); } +HRESULT WrappedDirect3DDevice9Ex::GetStreamSourceFreq(UINT StreamNumber, UINT* pSetting) +{ return real_->GetStreamSourceFreq(StreamNumber, pSetting); } +HRESULT WrappedDirect3DDevice9Ex::SetIndices(IDirect3DIndexBuffer9* pIndexData) +{ return real_->SetIndices(pIndexData); } +HRESULT WrappedDirect3DDevice9Ex::GetIndices(IDirect3DIndexBuffer9** ppIndexData) +{ return real_->GetIndices(ppIndexData); } +HRESULT WrappedDirect3DDevice9Ex::CreatePixelShader(const DWORD* pFunction, IDirect3DPixelShader9** ppShader) +{ return real_->CreatePixelShader(pFunction, ppShader); } +HRESULT WrappedDirect3DDevice9Ex::SetPixelShader(IDirect3DPixelShader9* pShader) +{ return real_->SetPixelShader(pShader); } +HRESULT WrappedDirect3DDevice9Ex::GetPixelShader(IDirect3DPixelShader9** ppShader) +{ return real_->GetPixelShader(ppShader); } +HRESULT WrappedDirect3DDevice9Ex::SetPixelShaderConstantF(UINT StartRegister, const float* pConstantData, UINT Vector4fCount) +{ return real_->SetPixelShaderConstantF(StartRegister, pConstantData, Vector4fCount); } +HRESULT WrappedDirect3DDevice9Ex::GetPixelShaderConstantF(UINT StartRegister, float* pConstantData, UINT Vector4fCount) +{ return real_->GetPixelShaderConstantF(StartRegister, pConstantData, Vector4fCount); } +HRESULT WrappedDirect3DDevice9Ex::SetPixelShaderConstantI(UINT StartRegister, const int* pConstantData, UINT Vector4iCount) +{ return real_->SetPixelShaderConstantI(StartRegister, pConstantData, Vector4iCount); } +HRESULT WrappedDirect3DDevice9Ex::GetPixelShaderConstantI(UINT StartRegister, int* pConstantData, UINT Vector4iCount) +{ return real_->GetPixelShaderConstantI(StartRegister, pConstantData, Vector4iCount); } +HRESULT WrappedDirect3DDevice9Ex::SetPixelShaderConstantB(UINT StartRegister, const BOOL* pConstantData, UINT BoolCount) +{ return real_->SetPixelShaderConstantB(StartRegister, pConstantData, BoolCount); } +HRESULT WrappedDirect3DDevice9Ex::GetPixelShaderConstantB(UINT StartRegister, BOOL* pConstantData, UINT BoolCount) +{ return real_->GetPixelShaderConstantB(StartRegister, pConstantData, BoolCount); } +HRESULT WrappedDirect3DDevice9Ex::DrawRectPatch(UINT Handle, const float* pNumSegs, const D3DRECTPATCH_INFO* pRectPatchInfo) +{ return real_->DrawRectPatch(Handle, pNumSegs, pRectPatchInfo); } +HRESULT WrappedDirect3DDevice9Ex::DrawTriPatch(UINT Handle, const float* pNumSegs, const D3DTRIPATCH_INFO* pTriPatchInfo) +{ return real_->DrawTriPatch(Handle, pNumSegs, pTriPatchInfo); } +HRESULT WrappedDirect3DDevice9Ex::DeletePatch(UINT Handle) +{ return real_->DeletePatch(Handle); } +HRESULT WrappedDirect3DDevice9Ex::CreateQuery(D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery) +{ return real_->CreateQuery(Type, ppQuery); } + +HRESULT WrappedDirect3DDevice9Ex::SetConvolutionMonoKernel(UINT width, UINT height, float* rows, float* columns) +{ return real_->SetConvolutionMonoKernel(width, height, rows, columns); } +HRESULT WrappedDirect3DDevice9Ex::ComposeRects(IDirect3DSurface9* pSrc, IDirect3DSurface9* pDst, IDirect3DVertexBuffer9* pSrcRectDescs, UINT NumRects, IDirect3DVertexBuffer9* pDstRectDescs, D3DCOMPOSERECTSOP Operation, int Xoffset, int Yoffset) +{ return real_->ComposeRects(pSrc, pDst, pSrcRectDescs, NumRects, pDstRectDescs, Operation, Xoffset, Yoffset); } +HRESULT WrappedDirect3DDevice9Ex::PresentEx(const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion, DWORD dwFlags) +{ return real_->PresentEx(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); } +HRESULT WrappedDirect3DDevice9Ex::GetGPUThreadPriority(INT* pPriority) +{ return real_->GetGPUThreadPriority(pPriority); } +HRESULT WrappedDirect3DDevice9Ex::SetGPUThreadPriority(INT Priority) +{ return real_->SetGPUThreadPriority(Priority); } +HRESULT WrappedDirect3DDevice9Ex::WaitForVBlank(UINT iSwapChain) +{ return real_->WaitForVBlank(iSwapChain); } +HRESULT WrappedDirect3DDevice9Ex::CheckResourceResidency(IDirect3DResource9** pResourceArray, UINT32 NumResources) +{ return real_->CheckResourceResidency(pResourceArray, NumResources); } +HRESULT WrappedDirect3DDevice9Ex::SetMaximumFrameLatency(UINT MaxLatency) +{ return real_->SetMaximumFrameLatency(MaxLatency); } +HRESULT WrappedDirect3DDevice9Ex::GetMaximumFrameLatency(UINT* pMaxLatency) +{ return real_->GetMaximumFrameLatency(pMaxLatency); } +HRESULT WrappedDirect3DDevice9Ex::CheckDeviceState(HWND hDestinationWindow) +{ return real_->CheckDeviceState(hDestinationWindow); } +HRESULT WrappedDirect3DDevice9Ex::CreateRenderTargetEx(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage) +{ return real_->CreateRenderTargetEx(Width, Height, Format, MultiSample, MultisampleQuality, Lockable, ppSurface, pSharedHandle, Usage); } +HRESULT WrappedDirect3DDevice9Ex::CreateOffscreenPlainSurfaceEx(UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage) +{ return real_->CreateOffscreenPlainSurfaceEx(Width, Height, Format, Pool, ppSurface, pSharedHandle, Usage); } +HRESULT WrappedDirect3DDevice9Ex::CreateDepthStencilSurfaceEx(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage) +{ return real_->CreateDepthStencilSurfaceEx(Width, Height, Format, MultiSample, MultisampleQuality, Discard, ppSurface, pSharedHandle, Usage); } +HRESULT WrappedDirect3DDevice9Ex::ResetEx(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode) +{ return real_->ResetEx(pPresentationParameters, pFullscreenDisplayMode); } +HRESULT WrappedDirect3DDevice9Ex::GetDisplayModeEx(UINT iSwapChain, D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation) +{ return real_->GetDisplayModeEx(iSwapChain, pMode, pRotation); } diff --git a/source/D3D9Hooks.cpp b/source/D3D9Hooks.cpp index 7b9cfda..673d14d 100644 --- a/source/D3D9Hooks.cpp +++ b/source/D3D9Hooks.cpp @@ -357,6 +357,11 @@ void InstallD3D9Hooks(IDirect3D9* d3d9, const bool is_ex) bool RegisterExistingDevice(IDirect3DDevice9* device) { if (device == nullptr) return false; + // Already active, whether registered via this path before or already wrapped by the + // cold Direct3DCreate9(Ex) path (D3D9Wrappers.cpp) - that path never touches g_devices, + // so without this check we'd double-hook the wrapper's own vtable and construct a second + // TextureClient, hitting its single-instance ASSERT. + if (TextureClient::CurrentClient() != nullptr) return false; { std::lock_guard lk(g_devices_mutex); if (g_devices.contains(device)) return false; // already known diff --git a/source/D3D9TextureWrappers.cpp b/source/D3D9TextureWrappers.cpp new file mode 100644 index 0000000..afabd02 --- /dev/null +++ b/source/D3D9TextureWrappers.cpp @@ -0,0 +1,269 @@ +#include "Main.h" +#include "D3D9TextureWrappers.h" + +#include +#include + +import TextureClient; + +namespace { + std::mutex g_real_to_wrapper_mutex; + std::unordered_map g_real_to_wrapper; +} + +IDirect3DBaseTexture9* FindWrapperForRealTexture(IDirect3DBaseTexture9* real) +{ + if (real == nullptr) return nullptr; + std::lock_guard lk(g_real_to_wrapper_mutex); + const auto it = g_real_to_wrapper.find(real); + return it != g_real_to_wrapper.end() ? it->second : nullptr; +} + +void RegisterWrapperForRealTexture(IDirect3DBaseTexture9* real, IDirect3DBaseTexture9* wrapper) +{ + std::lock_guard lk(g_real_to_wrapper_mutex); + g_real_to_wrapper[real] = wrapper; +} + +void UnregisterWrapperForRealTexture(IDirect3DBaseTexture9* real) +{ + std::lock_guard lk(g_real_to_wrapper_mutex); + g_real_to_wrapper.erase(real); +} + +// ---- WrappedTexture9 ---------------------------------------------------- + +WrappedTexture9::WrappedTexture9(IDirect3DTexture9* real) : real_(real), ref_count_(1) +{ + real_->AddRef(); + RegisterWrapperForRealTexture(real_, this); +} + +HRESULT WrappedTexture9::QueryInterface(REFIID riid, void** ppvObj) +{ + if (riid == IID_IUnknown || riid == IID_IDirect3DTexture9) { + AddRef(); + *ppvObj = this; + return S_OK; + } + // Unrecognized-but-successful QueryInterface hands back an unwrapped real pointer. + return real_->QueryInterface(riid, ppvObj); +} + +ULONG WrappedTexture9::AddRef() +{ + return ++ref_count_; +} + +ULONG WrappedTexture9::Release() +{ + const ULONG count = --ref_count_; + if (count == 0) { + if (auto* client = TextureClient::CurrentClient()) + client->OnReleaseTexture(this); + UnregisterWrapperForRealTexture(real_); + real_->Release(); + delete this; + return 0; + } + return count; +} + +HRESULT WrappedTexture9::GetDevice(IDirect3DDevice9** ppDevice) +{ return real_->GetDevice(ppDevice); } +HRESULT WrappedTexture9::SetPrivateData(REFGUID refguid, const void* pData, DWORD SizeOfData, DWORD Flags) +{ return real_->SetPrivateData(refguid, pData, SizeOfData, Flags); } +HRESULT WrappedTexture9::GetPrivateData(REFGUID refguid, void* pData, DWORD* pSizeOfData) +{ return real_->GetPrivateData(refguid, pData, pSizeOfData); } +HRESULT WrappedTexture9::FreePrivateData(REFGUID refguid) +{ return real_->FreePrivateData(refguid); } +DWORD WrappedTexture9::SetPriority(DWORD PriorityNew) +{ return real_->SetPriority(PriorityNew); } +DWORD WrappedTexture9::GetPriority() +{ return real_->GetPriority(); } +void WrappedTexture9::PreLoad() +{ real_->PreLoad(); } +D3DRESOURCETYPE WrappedTexture9::GetType() +{ return real_->GetType(); } + +DWORD WrappedTexture9::SetLOD(DWORD LODNew) +{ return real_->SetLOD(LODNew); } +DWORD WrappedTexture9::GetLOD() +{ return real_->GetLOD(); } +DWORD WrappedTexture9::GetLevelCount() +{ return real_->GetLevelCount(); } +HRESULT WrappedTexture9::SetAutoGenFilterType(D3DTEXTUREFILTERTYPE FilterType) +{ return real_->SetAutoGenFilterType(FilterType); } +D3DTEXTUREFILTERTYPE WrappedTexture9::GetAutoGenFilterType() +{ return real_->GetAutoGenFilterType(); } +void WrappedTexture9::GenerateMipSubLevels() +{ real_->GenerateMipSubLevels(); } + +HRESULT WrappedTexture9::GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) +{ return real_->GetLevelDesc(Level, pDesc); } +HRESULT WrappedTexture9::GetSurfaceLevel(UINT Level, IDirect3DSurface9** ppSurfaceLevel) +{ return real_->GetSurfaceLevel(Level, ppSurfaceLevel); } +HRESULT WrappedTexture9::LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect, const RECT* pRect, DWORD Flags) +{ return real_->LockRect(Level, pLockedRect, pRect, Flags); } +HRESULT WrappedTexture9::UnlockRect(UINT Level) +{ return real_->UnlockRect(Level); } +HRESULT WrappedTexture9::AddDirtyRect(const RECT* pDirtyRect) +{ return real_->AddDirtyRect(pDirtyRect); } + +// ---- WrappedVolumeTexture9 ----------------------------------------------- + +WrappedVolumeTexture9::WrappedVolumeTexture9(IDirect3DVolumeTexture9* real) : real_(real), ref_count_(1) +{ + real_->AddRef(); + RegisterWrapperForRealTexture(real_, this); +} + +HRESULT WrappedVolumeTexture9::QueryInterface(REFIID riid, void** ppvObj) +{ + if (riid == IID_IUnknown || riid == IID_IDirect3DVolumeTexture9) { + AddRef(); + *ppvObj = this; + return S_OK; + } + // Unrecognized-but-successful QueryInterface hands back an unwrapped real pointer. + return real_->QueryInterface(riid, ppvObj); +} + +ULONG WrappedVolumeTexture9::AddRef() +{ + return ++ref_count_; +} + +ULONG WrappedVolumeTexture9::Release() +{ + const ULONG count = --ref_count_; + if (count == 0) { + if (auto* client = TextureClient::CurrentClient()) + client->OnReleaseTexture(this); + UnregisterWrapperForRealTexture(real_); + real_->Release(); + delete this; + return 0; + } + return count; +} + +HRESULT WrappedVolumeTexture9::GetDevice(IDirect3DDevice9** ppDevice) +{ return real_->GetDevice(ppDevice); } +HRESULT WrappedVolumeTexture9::SetPrivateData(REFGUID refguid, const void* pData, DWORD SizeOfData, DWORD Flags) +{ return real_->SetPrivateData(refguid, pData, SizeOfData, Flags); } +HRESULT WrappedVolumeTexture9::GetPrivateData(REFGUID refguid, void* pData, DWORD* pSizeOfData) +{ return real_->GetPrivateData(refguid, pData, pSizeOfData); } +HRESULT WrappedVolumeTexture9::FreePrivateData(REFGUID refguid) +{ return real_->FreePrivateData(refguid); } +DWORD WrappedVolumeTexture9::SetPriority(DWORD PriorityNew) +{ return real_->SetPriority(PriorityNew); } +DWORD WrappedVolumeTexture9::GetPriority() +{ return real_->GetPriority(); } +void WrappedVolumeTexture9::PreLoad() +{ real_->PreLoad(); } +D3DRESOURCETYPE WrappedVolumeTexture9::GetType() +{ return real_->GetType(); } + +DWORD WrappedVolumeTexture9::SetLOD(DWORD LODNew) +{ return real_->SetLOD(LODNew); } +DWORD WrappedVolumeTexture9::GetLOD() +{ return real_->GetLOD(); } +DWORD WrappedVolumeTexture9::GetLevelCount() +{ return real_->GetLevelCount(); } +HRESULT WrappedVolumeTexture9::SetAutoGenFilterType(D3DTEXTUREFILTERTYPE FilterType) +{ return real_->SetAutoGenFilterType(FilterType); } +D3DTEXTUREFILTERTYPE WrappedVolumeTexture9::GetAutoGenFilterType() +{ return real_->GetAutoGenFilterType(); } +void WrappedVolumeTexture9::GenerateMipSubLevels() +{ real_->GenerateMipSubLevels(); } + +HRESULT WrappedVolumeTexture9::GetLevelDesc(UINT Level, D3DVOLUME_DESC* pDesc) +{ return real_->GetLevelDesc(Level, pDesc); } +HRESULT WrappedVolumeTexture9::GetVolumeLevel(UINT Level, IDirect3DVolume9** ppVolumeLevel) +{ return real_->GetVolumeLevel(Level, ppVolumeLevel); } +HRESULT WrappedVolumeTexture9::LockBox(UINT Level, D3DLOCKED_BOX* pLockedVolume, const D3DBOX* pBox, DWORD Flags) +{ return real_->LockBox(Level, pLockedVolume, pBox, Flags); } +HRESULT WrappedVolumeTexture9::UnlockBox(UINT Level) +{ return real_->UnlockBox(Level); } +HRESULT WrappedVolumeTexture9::AddDirtyBox(const D3DBOX* pDirtyBox) +{ return real_->AddDirtyBox(pDirtyBox); } + +// ---- WrappedCubeTexture9 ------------------------------------------------- + +WrappedCubeTexture9::WrappedCubeTexture9(IDirect3DCubeTexture9* real) : real_(real), ref_count_(1) +{ + real_->AddRef(); + RegisterWrapperForRealTexture(real_, this); +} + +HRESULT WrappedCubeTexture9::QueryInterface(REFIID riid, void** ppvObj) +{ + if (riid == IID_IUnknown || riid == IID_IDirect3DCubeTexture9) { + AddRef(); + *ppvObj = this; + return S_OK; + } + // Unrecognized-but-successful QueryInterface hands back an unwrapped real pointer. + return real_->QueryInterface(riid, ppvObj); +} + +ULONG WrappedCubeTexture9::AddRef() +{ + return ++ref_count_; +} + +ULONG WrappedCubeTexture9::Release() +{ + const ULONG count = --ref_count_; + if (count == 0) { + if (auto* client = TextureClient::CurrentClient()) + client->OnReleaseTexture(this); + UnregisterWrapperForRealTexture(real_); + real_->Release(); + delete this; + return 0; + } + return count; +} + +HRESULT WrappedCubeTexture9::GetDevice(IDirect3DDevice9** ppDevice) +{ return real_->GetDevice(ppDevice); } +HRESULT WrappedCubeTexture9::SetPrivateData(REFGUID refguid, const void* pData, DWORD SizeOfData, DWORD Flags) +{ return real_->SetPrivateData(refguid, pData, SizeOfData, Flags); } +HRESULT WrappedCubeTexture9::GetPrivateData(REFGUID refguid, void* pData, DWORD* pSizeOfData) +{ return real_->GetPrivateData(refguid, pData, pSizeOfData); } +HRESULT WrappedCubeTexture9::FreePrivateData(REFGUID refguid) +{ return real_->FreePrivateData(refguid); } +DWORD WrappedCubeTexture9::SetPriority(DWORD PriorityNew) +{ return real_->SetPriority(PriorityNew); } +DWORD WrappedCubeTexture9::GetPriority() +{ return real_->GetPriority(); } +void WrappedCubeTexture9::PreLoad() +{ real_->PreLoad(); } +D3DRESOURCETYPE WrappedCubeTexture9::GetType() +{ return real_->GetType(); } + +DWORD WrappedCubeTexture9::SetLOD(DWORD LODNew) +{ return real_->SetLOD(LODNew); } +DWORD WrappedCubeTexture9::GetLOD() +{ return real_->GetLOD(); } +DWORD WrappedCubeTexture9::GetLevelCount() +{ return real_->GetLevelCount(); } +HRESULT WrappedCubeTexture9::SetAutoGenFilterType(D3DTEXTUREFILTERTYPE FilterType) +{ return real_->SetAutoGenFilterType(FilterType); } +D3DTEXTUREFILTERTYPE WrappedCubeTexture9::GetAutoGenFilterType() +{ return real_->GetAutoGenFilterType(); } +void WrappedCubeTexture9::GenerateMipSubLevels() +{ real_->GenerateMipSubLevels(); } + +HRESULT WrappedCubeTexture9::GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) +{ return real_->GetLevelDesc(Level, pDesc); } +HRESULT WrappedCubeTexture9::GetCubeMapSurface(D3DCUBEMAP_FACES FaceType, UINT Level, IDirect3DSurface9** ppCubeMapSurface) +{ return real_->GetCubeMapSurface(FaceType, Level, ppCubeMapSurface); } +HRESULT WrappedCubeTexture9::LockRect(D3DCUBEMAP_FACES FaceType, UINT Level, D3DLOCKED_RECT* pLockedRect, const RECT* pRect, DWORD Flags) +{ return real_->LockRect(FaceType, Level, pLockedRect, pRect, Flags); } +HRESULT WrappedCubeTexture9::UnlockRect(D3DCUBEMAP_FACES FaceType, UINT Level) +{ return real_->UnlockRect(FaceType, Level); } +HRESULT WrappedCubeTexture9::AddDirtyRect(D3DCUBEMAP_FACES FaceType, const RECT* pDirtyRect) +{ return real_->AddDirtyRect(FaceType, pDirtyRect); } diff --git a/source/D3D9Wrappers.cpp b/source/D3D9Wrappers.cpp new file mode 100644 index 0000000..9ceef90 --- /dev/null +++ b/source/D3D9Wrappers.cpp @@ -0,0 +1,196 @@ +#include "Main.h" +#include "D3D9Wrappers.h" +#include "D3D9DeviceWrapper.h" + +import TextureClient; + +namespace { + + // Constructs the device wrapper + its TextureClient. The client must be built with + // the wrapper pointer (not the real device) so DirectX::CreateDDSTextureFromMemoryEx's + // internal CreateTexture call re-enters the wrapper's intercepted CreateTexture, which + // is what makes loading_fake-gated registration (see TextureClient::OnCreateTexture) + // see that call at all. + template + Wrapper* WrapDevice(Real* real_device) + { + const auto wrapper = new Wrapper(real_device); + const auto client = new TextureClient(wrapper); + wrapper->SetClient(client); + client->Initialize(); + return wrapper; + } + +} + +WrappedDirect3D9::WrappedDirect3D9(IDirect3D9* real) : real_(real), ref_count_(1) +{ + real_->AddRef(); +} + +HRESULT WrappedDirect3D9::QueryInterface(REFIID riid, void** ppvObj) +{ + if (riid == IID_IUnknown || riid == IID_IDirect3D9) { + AddRef(); + *ppvObj = this; + return S_OK; + } + // Unrecognized-but-successful QueryInterface hands back an unwrapped real pointer. + return real_->QueryInterface(riid, ppvObj); +} + +ULONG WrappedDirect3D9::AddRef() +{ + return ++ref_count_; +} + +ULONG WrappedDirect3D9::Release() +{ + const ULONG count = --ref_count_; + if (count == 0) { + real_->Release(); + delete this; + return 0; + } + return count; +} + +HRESULT WrappedDirect3D9::RegisterSoftwareDevice(void* pInitializeFunction) +{ return real_->RegisterSoftwareDevice(pInitializeFunction); } +UINT WrappedDirect3D9::GetAdapterCount() +{ return real_->GetAdapterCount(); } +HRESULT WrappedDirect3D9::GetAdapterIdentifier(UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier) +{ return real_->GetAdapterIdentifier(Adapter, Flags, pIdentifier); } +UINT WrappedDirect3D9::GetAdapterModeCount(UINT Adapter, D3DFORMAT Format) +{ return real_->GetAdapterModeCount(Adapter, Format); } +HRESULT WrappedDirect3D9::EnumAdapterModes(UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE* pMode) +{ return real_->EnumAdapterModes(Adapter, Format, Mode, pMode); } +HRESULT WrappedDirect3D9::GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) +{ return real_->GetAdapterDisplayMode(Adapter, pMode); } +HRESULT WrappedDirect3D9::CheckDeviceType(UINT Adapter, D3DDEVTYPE DevType, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed) +{ return real_->CheckDeviceType(Adapter, DevType, AdapterFormat, BackBufferFormat, bWindowed); } +HRESULT WrappedDirect3D9::CheckDeviceFormat(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat) +{ return real_->CheckDeviceFormat(Adapter, DeviceType, AdapterFormat, Usage, RType, CheckFormat); } +HRESULT WrappedDirect3D9::CheckDeviceMultiSampleType(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels) +{ return real_->CheckDeviceMultiSampleType(Adapter, DeviceType, SurfaceFormat, Windowed, MultiSampleType, pQualityLevels); } +HRESULT WrappedDirect3D9::CheckDepthStencilMatch(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat) +{ return real_->CheckDepthStencilMatch(Adapter, DeviceType, AdapterFormat, RenderTargetFormat, DepthStencilFormat); } +HRESULT WrappedDirect3D9::CheckDeviceFormatConversion(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat) +{ return real_->CheckDeviceFormatConversion(Adapter, DeviceType, SourceFormat, TargetFormat); } +HRESULT WrappedDirect3D9::GetDeviceCaps(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) +{ return real_->GetDeviceCaps(Adapter, DeviceType, pCaps); } +HMONITOR WrappedDirect3D9::GetAdapterMonitor(UINT Adapter) +{ return real_->GetAdapterMonitor(Adapter); } + +HRESULT WrappedDirect3D9::CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface) +{ + IDirect3DDevice9* real_device = nullptr; + const HRESULT hr = real_->CreateDevice(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, &real_device); + if (FAILED(hr) || real_device == nullptr) { + if (ppReturnedDeviceInterface) *ppReturnedDeviceInterface = nullptr; + return hr; + } + const auto wrapped = WrapDevice(real_device); + real_device->Release(); // the wrapper holds its own AddRef'd reference + *ppReturnedDeviceInterface = wrapped; + return hr; +} + +// ---- WrappedDirect3D9Ex --------------------------------------------------- + +WrappedDirect3D9Ex::WrappedDirect3D9Ex(IDirect3D9Ex* real) : real_(real), ref_count_(1) +{ + real_->AddRef(); +} + +HRESULT WrappedDirect3D9Ex::QueryInterface(REFIID riid, void** ppvObj) +{ + if (riid == IID_IUnknown || riid == IID_IDirect3D9 || riid == IID_IDirect3D9Ex) { + AddRef(); + *ppvObj = this; + return S_OK; + } + // Unrecognized-but-successful QueryInterface hands back an unwrapped real pointer. + return real_->QueryInterface(riid, ppvObj); +} + +ULONG WrappedDirect3D9Ex::AddRef() +{ + return ++ref_count_; +} + +ULONG WrappedDirect3D9Ex::Release() +{ + const ULONG count = --ref_count_; + if (count == 0) { + real_->Release(); + delete this; + return 0; + } + return count; +} + +HRESULT WrappedDirect3D9Ex::RegisterSoftwareDevice(void* pInitializeFunction) +{ return real_->RegisterSoftwareDevice(pInitializeFunction); } +UINT WrappedDirect3D9Ex::GetAdapterCount() +{ return real_->GetAdapterCount(); } +HRESULT WrappedDirect3D9Ex::GetAdapterIdentifier(UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier) +{ return real_->GetAdapterIdentifier(Adapter, Flags, pIdentifier); } +UINT WrappedDirect3D9Ex::GetAdapterModeCount(UINT Adapter, D3DFORMAT Format) +{ return real_->GetAdapterModeCount(Adapter, Format); } +HRESULT WrappedDirect3D9Ex::EnumAdapterModes(UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE* pMode) +{ return real_->EnumAdapterModes(Adapter, Format, Mode, pMode); } +HRESULT WrappedDirect3D9Ex::GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) +{ return real_->GetAdapterDisplayMode(Adapter, pMode); } +HRESULT WrappedDirect3D9Ex::CheckDeviceType(UINT Adapter, D3DDEVTYPE DevType, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed) +{ return real_->CheckDeviceType(Adapter, DevType, AdapterFormat, BackBufferFormat, bWindowed); } +HRESULT WrappedDirect3D9Ex::CheckDeviceFormat(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat) +{ return real_->CheckDeviceFormat(Adapter, DeviceType, AdapterFormat, Usage, RType, CheckFormat); } +HRESULT WrappedDirect3D9Ex::CheckDeviceMultiSampleType(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels) +{ return real_->CheckDeviceMultiSampleType(Adapter, DeviceType, SurfaceFormat, Windowed, MultiSampleType, pQualityLevels); } +HRESULT WrappedDirect3D9Ex::CheckDepthStencilMatch(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat) +{ return real_->CheckDepthStencilMatch(Adapter, DeviceType, AdapterFormat, RenderTargetFormat, DepthStencilFormat); } +HRESULT WrappedDirect3D9Ex::CheckDeviceFormatConversion(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat) +{ return real_->CheckDeviceFormatConversion(Adapter, DeviceType, SourceFormat, TargetFormat); } +HRESULT WrappedDirect3D9Ex::GetDeviceCaps(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) +{ return real_->GetDeviceCaps(Adapter, DeviceType, pCaps); } +HMONITOR WrappedDirect3D9Ex::GetAdapterMonitor(UINT Adapter) +{ return real_->GetAdapterMonitor(Adapter); } + +HRESULT WrappedDirect3D9Ex::CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface) +{ + IDirect3DDevice9* real_device = nullptr; + const HRESULT hr = real_->CreateDevice(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, &real_device); + if (FAILED(hr) || real_device == nullptr) { + if (ppReturnedDeviceInterface) *ppReturnedDeviceInterface = nullptr; + return hr; + } + const auto wrapped = WrapDevice(real_device); + real_device->Release(); + *ppReturnedDeviceInterface = wrapped; + return hr; +} + +UINT WrappedDirect3D9Ex::GetAdapterModeCountEx(UINT Adapter, const D3DDISPLAYMODEFILTER* pFilter) +{ return real_->GetAdapterModeCountEx(Adapter, pFilter); } +HRESULT WrappedDirect3D9Ex::EnumAdapterModesEx(UINT Adapter, const D3DDISPLAYMODEFILTER* pFilter, UINT Mode, D3DDISPLAYMODEEX* pMode) +{ return real_->EnumAdapterModesEx(Adapter, pFilter, Mode, pMode); } +HRESULT WrappedDirect3D9Ex::GetAdapterDisplayModeEx(UINT Adapter, D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation) +{ return real_->GetAdapterDisplayModeEx(Adapter, pMode, pRotation); } + +HRESULT WrappedDirect3D9Ex::CreateDeviceEx(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode, IDirect3DDevice9Ex** ppReturnedDeviceInterface) +{ + IDirect3DDevice9Ex* real_device = nullptr; + const HRESULT hr = real_->CreateDeviceEx(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, pFullscreenDisplayMode, &real_device); + if (FAILED(hr) || real_device == nullptr) { + if (ppReturnedDeviceInterface) *ppReturnedDeviceInterface = nullptr; + return hr; + } + const auto wrapped = WrapDevice(real_device); + real_device->Release(); + *ppReturnedDeviceInterface = wrapped; + return hr; +} + +HRESULT WrappedDirect3D9Ex::GetAdapterLUID(UINT Adapter, LUID* pLUID) +{ return real_->GetAdapterLUID(Adapter, pLUID); } diff --git a/source/dll_main.cpp b/source/dll_main.cpp index d359b72..3314754 100644 --- a/source/dll_main.cpp +++ b/source/dll_main.cpp @@ -2,6 +2,7 @@ #include #include "MinHook.h" #include "D3D9Hooks.h" +#include "D3D9Wrappers.h" #include import TextureClient; @@ -148,9 +149,11 @@ IDirect3D9* APIENTRY Direct3DCreate9(UINT SDKVersion) creating_d3d9 = false; - // Hook the vtable and hand the game back the real object untouched. - InstallD3D9Hooks(pIDirect3D9_orig, false); - return pIDirect3D9_orig; + // Hand the game a wrapper object instead of the real one: CreateDevice is intercepted + // via ordinary virtual dispatch, so nothing here is ever patched (see D3D9Wrappers.h). + const auto wrapped = new WrappedDirect3D9(pIDirect3D9_orig); + pIDirect3D9_orig->Release(); // the wrapper holds its own AddRef'd reference + return wrapped; } HRESULT APIENTRY Direct3DCreate9Ex(UINT SDKVersion, IDirect3D9Ex** ppD3D) @@ -172,9 +175,11 @@ HRESULT APIENTRY Direct3DCreate9Ex(UINT SDKVersion, IDirect3D9Ex** ppD3D) if (ret != S_OK) return ret; - // Hook the vtable (CreateDevice + CreateDeviceEx) and return the real object untouched. - InstallD3D9Hooks(pIDirect3D9Ex_orig, true); - *ppD3D = pIDirect3D9Ex_orig; + // Hand the game a wrapper object instead of the real one: CreateDevice/CreateDeviceEx + // are intercepted via ordinary virtual dispatch, so nothing here is ever patched. + const auto wrapped = new WrappedDirect3D9Ex(pIDirect3D9Ex_orig); + pIDirect3D9Ex_orig->Release(); // the wrapper holds its own AddRef'd reference + *ppD3D = wrapped; return ret; }