Compare commits

...

14 Commits

Author SHA1 Message Date
GWToolbox Bot 2705ceda7b Refuse late-attach if a device is already active
RegisterExistingDevice deduped against D3D9Hooks.cpp's own g_devices map,
but the cold Direct3DCreate9(Ex) path (D3D9Wrappers.cpp) never touches that
map. If gMod is early-injected and something (e.g. GWToolbox adopting a
pre-loaded gMod) also calls SetDevice on the resulting wrapper device,
RegisterExistingDevice would treat it as new: MinHook-patch the wrapper's
own vtable on top, and construct a second TextureClient, hitting its
single-instance ASSERT. Check TextureClient::CurrentClient() first so any
already-active device (via either path) is a no-op.
2026-07-07 10:55:30 +00:00
GWToolbox Bot 2b2a817c15 Wrap D3D9 COM objects instead of MinHook-patching the cold path
CreateDevice(Ex)/CreateTexture/CreateVolumeTexture/CreateCubeTexture/
UpdateTexture/BeginScene/SetTexture/GetTexture/Release are now intercepted
via ordinary compiled virtual dispatch on wrapper objects returned from
Direct3DCreate9(Ex), instead of MinHook patching the real object's vtable
slots. Nothing is ever patched in this path, so it sidesteps every
self-modifying-code/JIT-cache concern the ARM64 emulator (Windows-on-ARM's
xtajit, or Wine on FEX-Emu/box64) has with inline code patching.

The late-attach path (SetDevice/RegisterExistingDevice/D3D9Hooks.cpp) is
unchanged: wrapping only works when gMod controls object creation from the
start, so late-attach still needs MinHook and remains gated off under ARM64
emulation.
2026-07-07 10:17:08 +00:00
henderkes 9b809519f8 1.10 2026-06-29 21:41:48 +07:00
henderkes c57a527b68 Sign release binaries with Certum and bump to v2.0.0.0
Add Certum SimplySign cloud code signing to the CD pipeline so gMod.dll
and TpfConvert.exe are signed before being published as release assets.
The signing scripts are ported from gwlauncher (commit 5ae825a).

Signing is gated on the CERTUM_OTP_URI secret, so forks/unconfigured
repos still build, just unsigned. Requires repo secrets CERTUM_OTP_URI,
CERTUM_USERID and CERTUM_CERT_SHA1.

Bump the major version to 2.0.0.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 21:40:51 +07:00
Marc f420bbd50a Bump version to 1.9.0.3
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:05:51 +00:00
Marc e762e1b716 Flush instruction cache after (un)hooking so the ARM64 emulator re-JITs
MinHook flushes only the 5 patched bytes; under the x86-on-ARM64
emulator (xtajit) that can leave a stale JIT translation of the
modified prologue and trap with a non-continuable breakpoint. Flush the
whole prologue and the trampoline on install, and the restored prologue
on teardown, so the emulator re-translates them. No-op on native x86.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:03:36 +00:00
Marc 13abd4e546 Bump version to 1.9.0.2
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 15:35:14 +00:00
Marc 92c6b115f3 Make D3D9 detours fall through after unhook to avoid teardown crash
Keep the o_* trampoline pointers valid on RemoveAllD3D9Hooks and add a
g_unhooked flag each detour checks after calling the original, so a
surviving vtable patch invoked during/after teardown just returns the
real result instead of touching freed TextureClient state. Tear down
off the loader lock via a new Shutdown export and skip teardown on
process exit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 15:13:00 +00:00
Marc 2b87e0e53a Fix surface ref leak and double-release in HashTexture2D multisample path
The D3DPOOL_DEFAULT multisample branch aliased pSurfaceLevel_orig onto
pResolvedSurface, leaking the GetSurfaceLevel reference and releasing the
resolved surface twice (use-after-free). Track the read source in a separate
non-owning pointer so each surface is released exactly once on every path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 17:14:17 +00:00
Marc b04b4583f4 Bump version to 1.9.0.1
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 17:00:10 +00:00
Marc 18d650a422 Expand CLAUDE.md with build constraints, hooking model, and conventions
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 16:24:05 +00:00
Jon 55e36db265 Merge branch 'master' into dev 2026-06-08 17:15:11 +01:00
Jon 379878bd6d Claude stuff 2026-06-08 17:14:56 +01:00
Jon ac39241ff9 Release replacement textures when a client is torn down (#38)
* 1.9

* Release replacement textures when a client is torn down

TextureClient::~TextureClient deleted the per-texture side-state but never
released the replacement IDirect3DTexture9 objects gMod created. That's
fine when the client dies because the device hit refcount 0 (a texture
refs its device, so by then every fake is already released and the maps
are empty). But when gMod is unloaded via FreeLibrary while the game's
device is still alive (late-injected/SetDevice integrations), the maps
are full of textures gMod still owns and they all leak - and ExitInstance
never deleted the clients, so the destructor didn't even run.

- ~TextureClient now releases each replacement it still owns (a fake is
  still owned while it has a partner; an orphaned fake was already
  released and is left alone). No-op in the device-release path.
- Add DestroyAllTextureClients() to delete the per-device clients.
- ExitInstance calls it, gated on a genuine FreeLibrary
  (lpReserved == nullptr) so it's skipped during process termination
  where the device/d3d9 may already be gone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon <>
Co-authored-by: Marc <m@pyc.ac>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 17:14:10 +01:00
19 changed files with 2095 additions and 37 deletions
+38
View File
@@ -0,0 +1,38 @@
# Build output and artifacts
build/
bin/
Debug/
Release/
MinSizeRel/
RelWithDebInfo/
win32/
install/
# CMake generated directories
*.dir/
CMakeFiles/
_deps/
# vcpkg vendored dependencies
vcpkg_installed/
# Visual Studio files
*.vcxproj
*.vcxproj.filters
*.vcxproj.user
*.sln
*.slnx
*.sdf
*.suo
*.user
*.aps
# CMake cache/generated
CMakeCache.txt
cmake_install.cmake
ALL_BUILD*
ZERO_CHECK*
INSTALL*
# Version resource (generated)
version.rc
+2
View File
@@ -0,0 +1,2 @@
# Shell scripts must stay LF or bash on the CI runner chokes on \r.
*.sh text eol=lf
+28
View File
@@ -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 = 0
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."
+245
View File
@@ -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
}
+46
View File
@@ -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."
+66
View File
@@ -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."
+28 -1
View File
@@ -16,9 +16,15 @@ jobs:
runs-on: windows-2025-vs2026
permissions:
contents: write
env:
Configuration: Release
Actions_Allow_Unsecure_Commands: true
CERTUM_OTP_URI: ${{ secrets.CERTUM_OTP_URI }}
CERTUM_USERID: ${{ secrets.CERTUM_USERID }}
CERTUM_CERT_SHA1: ${{ secrets.CERTUM_CERT_SHA1 }}
steps:
- name: Checkout
@@ -46,7 +52,28 @@ 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.CERTUM_OTP_URI != ''
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.CERTUM_OTP_URI != ''
shell: bash
run: powershell -ExecutionPolicy Bypass -File "./.github/scripts/connect-simplysign.ps1"
- name: Sign release binaries
if: env.CERTUM_OTP_URI != ''
shell: pwsh
run: |
./.github/scripts/sign-certum.ps1 -Files @(".\bin\Release\gMod.dll", ".\bin\Release\TpfConvert.exe")
- name: Retrieve version
id: set_version
run: |
+96
View File
@@ -0,0 +1,96 @@
# gMod
A 32-bit Windows DLL that hooks Direct3D 9 to load texture mods for Guild Wars. Continuation of uMod. Integrated with [GW Launcher](https://github.com/gwdevhub/gwlauncher) and [Daybreak](https://github.com/gwdevhub/Daybreak).
## Project structure
- `header/` — header files (D3D9Hooks, D3D9State, Defines, Error, Main, Utils)
- `source/` — implementation files (D3D9Hooks.cpp, dll_main.cpp, Error.cpp)
- `modules/` — C++23 module files (ModfileLoader, ModfileLoader_TpfReader, TextureClient, TextureFunction)
- `TpfConvert/` — standalone utility to fix broken .tpf files
- `cmake/` — CMake helper scripts
- `build/` — CMake build tree (generated, ignored)
- `bin/` — compiled output (generated, ignored)
## Build
Requires Visual Studio 2022, CMake 3.29+, and vcpkg — all via the VS 2022 Developer PowerShell.
```
cmake --preset=vcpkg
cmake --open build
# then compile inside Visual Studio, or:
cmake --build build --config Release
```
- Target: 32-bit only (`-A Win32` / `CMAKE_GENERATOR_PLATFORM=win32`)
- Standard: C++23
- Output: `bin/` (gMod.dll, TpfConvert.exe)
- Version: set in `CMakeLists.txt` (`VERSION_MAJOR/MINOR/PATCH/TWEAK`)
> **Cannot be built or run in this agent environment** (Linux, no MSVC, no 32-bit
> D3D9, no Guild Wars to inject into). Don't burn turns attempting `cmake`/builds
> here — validate changes by reading and matching existing patterns, and lean on
> Windows CI (`ci.yaml`) for the real build. When an actual compile/test is needed,
> ask the user to do it in Visual Studio rather than trying locally.
## Key dependencies (via vcpkg)
- `minhook` — D3D9 function hooking
- `libzippp` / `libzip` / `zlib` — reading .tpf/.zip mod files
- `directxtex` — texture format conversion
- `Microsoft.GSL` — Guidelines Support Library
## How it works
1. `dll_main.cpp` — DLL entry point, hooks d3d9.dll via MinHook
2. `D3D9Hooks.cpp` — intercepts `Direct3DCreate9` and IDirect3DDevice9 methods
3. `ModfileLoader` — reads `modlist.txt`, loads .tpf/.zip mod archives
4. `TextureClient` / `TextureFunction` — matches and replaces textures by hash at runtime
### Hooking model (read before touching D3D9 code)
gMod uses **MinHook vtable hooks**, not proxy/subclass wrappers (the old uMod model
was replaced — see `git log` "Replace D3D9 proxy wrappers with vtable hooks"). The
game keeps the **real** `IDirect3D9` / `IDirect3DDevice9` / texture objects byte-for-byte
untouched; gMod patches only the individual vtable slots it cares about (CreateDevice,
CreateTexture/Volume/Cube, UpdateTexture, BeginScene, Set/GetTexture, Release).
- Per-texture side-state lives **out of band** in `header/D3D9State.h` (`TexState`,
keyed by the real texture pointer), so reverting the hooks fully detaches gMod.
- `RemoveAllD3D9Hooks()` must leave the host process pristine — preserve that invariant.
- A vtable is shared by all instances of a class, so each slot is hooked once; texture
Release slots are hooked lazily on first instance. Don't double-hook.
## Conventions
- **Formatting is CI-enforced** by clang-format `22.1.5` (`.clang-format`, applied by
`lint.yaml` on push to `dev`). Don't hand-reformat or "tidy" whole files — it just
churns the diff. Notable rules: `ColumnLimit: 0` (never reflow long D3D signatures /
vtable typedefs), `SortIncludes: Never` (include order is load-bearing in Windows
headers — never reorder), 4-space indent, braces on their own line after functions.
- **C++23 named modules** (`modules/*.ixx`). Editing a module *interface* triggers wide
rebuilds; prefer changing implementation over interface when possible.
- **Public exported API is a fixed surface** — four `extern "C" __declspec(dllexport)`
functions in `dll_main.cpp`: `SetDevice`, `AddFile`, `RemoveFile`, `GetFiles`. These
are consumed by GW Launcher and Daybreak; do **not** change their names, signatures,
or calling convention (`__cdecl`) without coordinating downstream.
- **Commits**: short imperative summaries, lowercase, no trailing period
(e.g. "Keep loaded_files in load order so GetFiles returns priority order").
- **Branching**: `dev` is the integration branch; PRs target `master`. CI builds on
push to `dev` and on PRs to `master`.
### Where things live (by size / how often they change)
- `modules/TextureClient.ixx` (~670 lines) — texture match/replace bookkeeping, hot
- `source/D3D9Hooks.cpp` (~580) — the vtable detours and install/remove
- `modules/TextureFunction.ixx` (~380) — texture creation/conversion helpers
- `source/dll_main.cpp` (~345) — entry point + exported C API
- `modules/ModfileLoader*.ixx` — .tpf/.zip parsing
- `header/Defines.h` — shared structs/constants (`TextureFileStruct`, `HashTuple`, etc.)
## Notes
- Must be injected before d3d9.dll loads, or renamed to d3d9.dll in the GW folder
- Reshade > 5.0.1 causes glitches; recommend 5.0.1 or 4.9.1
- `modlist.txt` contains full paths to .tpf/.zip mod files, one per line
+1 -1
View File
@@ -10,7 +10,7 @@ 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 0)
+305
View File
@@ -0,0 +1,305 @@
#pragma once
#include <d3d9.h>
#include <atomic>
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<ULONG> 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<ULONG> ref_count_;
TextureClient* client_;
void TeardownTextureClient();
};
+4
View File
@@ -6,4 +6,8 @@ void InstallD3D9Hooks(IDirect3D9* d3d9, bool is_ex);
void RemoveAllD3D9Hooks();
// Delete every per-device TextureClient, releasing its replacement textures. Only safe
// on a real FreeLibrary (device still alive), not during process termination.
void DestroyAllTextureClients();
bool RegisterExistingDevice(IDirect3DDevice9* device);
+127
View File
@@ -0,0 +1,127 @@
#pragma once
#include <d3d9.h>
#include <atomic>
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<ULONG> 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<ULONG> 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<ULONG> 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);
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#include <d3d9.h>
#include <atomic>
// 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<ULONG> 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<ULONG> ref_count_;
};
+9 -4
View File
@@ -159,10 +159,15 @@ TextureClient::~TextureClient()
std::lock_guard lk(registry_mutex);
shutting_down = true; // the texture Release hook now no-ops for our textures
// Drop side-state but don't Release the textures: the game already released
// its originals (and our fakes with them), and the device may be gone.
for (const auto state : fakes | std::views::values) {
delete state;
// Release replacements we still own (still partnered) so they aren't leaked when
// torn down with the device alive (FreeLibrary). Orphaned fakes were already
// released; a device-release teardown leaves the maps empty, so this is a no-op.
for (const auto fake : fakes | std::views::values) {
if (fake->partner != nullptr) {
fake->partner->partner = nullptr; // detach the original's back-pointer
if (fake->real) fake->real->Release();
}
delete fake;
}
fakes.clear();
for (const auto state : originals | std::views::values) {
+536
View File
@@ -0,0 +1,536 @@
#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<WrappedTexture9*>(texture)->RealTexture();
case D3DRTYPE_VOLUMETEXTURE: return static_cast<WrappedVolumeTexture9*>(texture)->RealTexture();
case D3DRTYPE_CUBETEXTURE: return static_cast<WrappedCubeTexture9*>(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); }
+76 -20
View File
@@ -2,6 +2,7 @@
#include "D3D9Hooks.h"
#include "MinHook.h"
#include <atomic>
#include <mutex>
#include <unordered_map>
#include <vector>
@@ -31,6 +32,10 @@ namespace {
// --- IDirect3D*Texture9 (IUnknown layout) ---------------------------
constexpr int kResource_Release = 2;
// Bytes to flush from a hook target / trampoline so the x86-on-ARM64 emulator re-JITs
// it. Covers MinHook's max prologue patch and its 64-byte trampoline slot.
constexpr SIZE_T kFlushSpan = 64;
void** GetVTable(void* com_object)
{
return *static_cast<void***>(com_object);
@@ -72,6 +77,9 @@ namespace {
std::vector<void*> g_hooked_targets;
// Set when teardown begins; a detour still reached after this just calls the original.
std::atomic<bool> g_unhooked{false};
// device -> owning TextureClient
std::mutex g_devices_mutex;
std::unordered_map<IDirect3DDevice9*, TextureClient*> g_devices;
@@ -94,6 +102,13 @@ namespace {
Warning("D3D9Hooks: MH_EnableHook failed for %p\n", target);
return false;
}
// Under the x86-on-ARM64 emulator, patched code is only re-translated when its
// instruction cache is flushed. MinHook flushes just the 5 patched bytes, which can
// leave a stale/guarded JIT block and trap; flush the whole prologue and the freshly
// built trampoline so the emulator re-JITs both. No-op on native x86.
const HANDLE proc = GetCurrentProcess();
FlushInstructionCache(proc, target, kFlushSpan);
if (original && *original) FlushInstructionCache(proc, *original, kFlushSpan);
g_hooked_targets.push_back(target);
return true;
}
@@ -174,6 +189,7 @@ namespace {
D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface)
{
const HRESULT hr = o_CreateDevice(self, Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface);
if (g_unhooked) return hr;
if (SUCCEEDED(hr) && ppReturnedDeviceInterface && *ppReturnedDeviceInterface) {
OnDeviceCreated(*ppReturnedDeviceInterface);
}
@@ -184,6 +200,7 @@ namespace {
D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode, IDirect3DDevice9Ex** ppReturnedDeviceInterface)
{
const HRESULT hr = o_CreateDeviceEx(self, Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, pFullscreenDisplayMode, ppReturnedDeviceInterface);
if (g_unhooked) return hr;
if (SUCCEEDED(hr) && ppReturnedDeviceInterface && *ppReturnedDeviceInterface) {
OnDeviceCreated(*ppReturnedDeviceInterface);
}
@@ -193,6 +210,7 @@ namespace {
ULONG STDMETHODCALLTYPE h_DeviceRelease(IDirect3DDevice9* self)
{
const ULONG count = o_DeviceRelease(self);
if (g_unhooked) return count;
if (count == 0) {
TextureClient* client = nullptr;
{
@@ -212,6 +230,7 @@ namespace {
IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle)
{
const HRESULT hr = o_CreateTexture(self, Width, Height, Levels, Usage, Format, Pool, ppTexture, pSharedHandle);
if (g_unhooked) return hr;
if (SUCCEEDED(hr) && ppTexture && *ppTexture) {
InstallTextureReleaseHook(*ppTexture, TexType::Tex2D);
if (auto* client = ClientFor(self))
@@ -224,6 +243,7 @@ namespace {
IDirect3DVolumeTexture9** ppVolumeTexture, HANDLE* pSharedHandle)
{
const HRESULT hr = o_CreateVolumeTexture(self, Width, Height, Depth, Levels, Usage, Format, Pool, ppVolumeTexture, pSharedHandle);
if (g_unhooked) return hr;
if (SUCCEEDED(hr) && ppVolumeTexture && *ppVolumeTexture) {
InstallTextureReleaseHook(*ppVolumeTexture, TexType::Volume);
if (auto* client = ClientFor(self))
@@ -236,6 +256,7 @@ namespace {
IDirect3DCubeTexture9** ppCubeTexture, HANDLE* pSharedHandle)
{
const HRESULT hr = o_CreateCubeTexture(self, EdgeLength, Levels, Usage, Format, Pool, ppCubeTexture, pSharedHandle);
if (g_unhooked) return hr;
if (SUCCEEDED(hr) && ppCubeTexture && *ppCubeTexture) {
InstallTextureReleaseHook(*ppCubeTexture, TexType::Cube);
if (auto* client = ClientFor(self))
@@ -248,6 +269,7 @@ namespace {
{
// Pass the real textures through, then re-evaluate mods on the changed destination.
const HRESULT hr = o_UpdateTexture(self, pSourceTexture, pDestinationTexture);
if (g_unhooked) return hr;
if (SUCCEEDED(hr)) {
if (auto* client = ClientFor(self))
client->OnUpdateTexture(pSourceTexture, pDestinationTexture);
@@ -257,6 +279,7 @@ namespace {
HRESULT STDMETHODCALLTYPE h_BeginScene(IDirect3DDevice9* self)
{
if (g_unhooked) return o_BeginScene(self);
if (auto* client = ClientFor(self))
client->OnBeginScene();
return o_BeginScene(self);
@@ -264,6 +287,7 @@ namespace {
HRESULT STDMETHODCALLTYPE h_SetTexture(IDirect3DDevice9* self, DWORD Stage, IDirect3DBaseTexture9* pTexture)
{
if (g_unhooked) return o_SetTexture(self, Stage, pTexture);
IDirect3DBaseTexture9* bind = pTexture;
if (auto* client = ClientFor(self))
bind = client->ResolveBinding(pTexture); // substitute the fake when modded
@@ -273,6 +297,7 @@ namespace {
HRESULT STDMETHODCALLTYPE h_GetTexture(IDirect3DDevice9* self, DWORD Stage, IDirect3DBaseTexture9** ppTexture)
{
const HRESULT hr = o_GetTexture(self, Stage, ppTexture);
if (g_unhooked) return hr;
if (SUCCEEDED(hr) && ppTexture && *ppTexture) {
if (auto* client = ClientFor(self)) {
if (auto* original = client->ResolveOriginalFromFake(*ppTexture)) {
@@ -295,6 +320,7 @@ namespace {
ULONG STDMETHODCALLTYPE h_Tex2DRelease(IUnknown* self)
{
const ULONG count = o_Tex2DRelease(self);
if (g_unhooked) return count;
ReleaseTextureCleanup(self, count);
return count;
}
@@ -302,6 +328,7 @@ namespace {
ULONG STDMETHODCALLTYPE h_VolumeRelease(IUnknown* self)
{
const ULONG count = o_VolumeRelease(self);
if (g_unhooked) return count;
ReleaseTextureCleanup(self, count);
return count;
}
@@ -309,6 +336,7 @@ namespace {
ULONG STDMETHODCALLTYPE h_CubeRelease(IUnknown* self)
{
const ULONG count = o_CubeRelease(self);
if (g_unhooked) return count;
ReleaseTextureCleanup(self, count);
return count;
}
@@ -317,6 +345,7 @@ namespace {
void InstallD3D9Hooks(IDirect3D9* d3d9, const bool is_ex)
{
if (d3d9 == nullptr || g_d3d9_hooks_installed) return;
g_unhooked = false; // re-arm in case a prior teardown left the flag set
void** vt = GetVTable(d3d9);
Hook(vt[kIDirect3D9_CreateDevice], &h_CreateDevice, reinterpret_cast<void**>(&o_CreateDevice));
if (is_ex) {
@@ -328,20 +357,31 @@ 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
if (!g_devices.empty()) return false; // gMod supports a single device at a time
}
g_unhooked = false; // re-arm in case a prior teardown left the flag set
OnDeviceCreated(device); // installs device hooks + TextureClient (idempotent)
return ClientFor(device) != nullptr;
}
void RemoveAllD3D9Hooks()
{
// Signal first: a detour reached after this (e.g. a surviving patch) falls through.
g_unhooked = true;
const HANDLE proc = GetCurrentProcess();
for (void* target : g_hooked_targets) {
MH_DisableHook(target);
MH_RemoveHook(target);
FlushInstructionCache(proc, target, kFlushSpan); // re-JIT the restored prologue under emulation
}
g_hooked_targets.clear();
@@ -351,19 +391,23 @@ void RemoveAllD3D9Hooks()
g_volume_release_installed = false;
g_cube_release_installed = false;
o_CreateDevice = nullptr;
o_CreateDeviceEx = nullptr;
o_DeviceRelease = nullptr;
o_CreateTexture = nullptr;
o_CreateVolumeTexture = nullptr;
o_CreateCubeTexture = nullptr;
o_UpdateTexture = nullptr;
o_BeginScene = nullptr;
o_SetTexture = nullptr;
o_GetTexture = nullptr;
o_Tex2DRelease = nullptr;
o_VolumeRelease = nullptr;
o_CubeRelease = nullptr;
// Leave the o_* trampoline pointers intact so a surviving detour can still call through.
}
void DestroyAllTextureClients()
{
// Collect under the lock, delete outside it (~TextureClient takes its own locks).
std::vector<TextureClient*> clients;
{
std::lock_guard lk(g_devices_mutex);
for (const auto& entry : g_devices) {
clients.push_back(entry.second);
}
g_devices.clear();
}
for (auto* client : clients) {
delete client;
}
}
// All three read state->real directly; gMod never swaps the underlying resource.
@@ -400,6 +444,11 @@ namespace {
return {};
}
// The surface GetRenderTargetData reads from: either the level itself, or a
// non-multisampled resolve of it. Non-owning; pSurfaceLevel_orig and
// pResolvedSurface each own a ref and are released exactly once below.
IDirect3DSurface9* pCopySource = pSurfaceLevel_orig;
if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) {
if (D3D_OK != device->CreateRenderTarget(desc.Width, desc.Height, desc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &pResolvedSurface, nullptr)) {
pSurfaceLevel_orig->Release();
@@ -407,31 +456,39 @@ namespace {
return {};
}
if (D3D_OK != device->StretchRect(pSurfaceLevel_orig, nullptr, pResolvedSurface, nullptr, D3DTEXF_NONE)) {
pResolvedSurface->Release();
pResolvedSurface = nullptr;
pSurfaceLevel_orig->Release();
Warning("GetTextureHash(2D) Failed: StretchRect (D3DPOOL_DEFAULT)\n");
return {};
}
pSurfaceLevel_orig = pResolvedSurface;
pCopySource = pResolvedSurface;
}
if (D3D_OK != device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &pOffscreenSurface, nullptr)) {
pSurfaceLevel_orig->Release();
if (pResolvedSurface != nullptr) pResolvedSurface->Release();
pSurfaceLevel_orig->Release();
Warning("GetTextureHash(2D) Failed: CreateOffscreenPlainSurface (D3DPOOL_DEFAULT)\n");
return {};
}
if (D3D_OK != device->GetRenderTargetData(pSurfaceLevel_orig, pOffscreenSurface)) {
pSurfaceLevel_orig->Release();
if (pResolvedSurface != nullptr) pResolvedSurface->Release();
if (D3D_OK != device->GetRenderTargetData(pCopySource, pOffscreenSurface)) {
pOffscreenSurface->Release();
if (pResolvedSurface != nullptr) pResolvedSurface->Release();
pSurfaceLevel_orig->Release();
Warning("GetTextureHash(2D) Failed: GetRenderTargetData (D3DPOOL_DEFAULT)\n");
return {};
}
// Copy is done; both source surfaces are finished with. Null pResolvedSurface
// so the shared cleanup below treats this as the pOffscreenSurface case.
if (pResolvedSurface != nullptr) {
pResolvedSurface->Release();
pResolvedSurface = nullptr;
}
pSurfaceLevel_orig->Release();
if (pOffscreenSurface->LockRect(&d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
if (pResolvedSurface != nullptr) pResolvedSurface->Release();
pOffscreenSurface->Release();
Warning("GetTextureHash(2D) Failed: LockRect (D3DPOOL_DEFAULT)\n");
return {};
@@ -456,7 +513,6 @@ namespace {
if (pOffscreenSurface != nullptr) {
pOffscreenSurface->UnlockRect();
pOffscreenSurface->Release();
if (pResolvedSurface != nullptr) pResolvedSurface->Release();
}
else if (pResolvedSurface != nullptr) {
pResolvedSurface->UnlockRect();
+212
View File
@@ -0,0 +1,212 @@
#include "Main.h"
#include "D3D9TextureWrappers.h"
#include <mutex>
#include <unordered_map>
import TextureClient;
namespace {
std::mutex g_real_to_wrapper_mutex;
std::unordered_map<IDirect3DBaseTexture9*, IDirect3DBaseTexture9*> 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); }
+166
View File
@@ -0,0 +1,166 @@
#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 <class Wrapper, class Real>
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<WrappedDirect3DDevice9>(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<WrappedDirect3DDevice9>(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<WrappedDirect3DDevice9Ex>(real_device);
real_device->Release();
*ppReturnedDeviceInterface = wrapped;
return hr;
}
HRESULT WrappedDirect3D9Ex::GetAdapterLUID(UINT Adapter, LUID* pLUID) { return real_->GetAdapterLUID(Adapter, pLUID); }
+41 -11
View File
@@ -2,10 +2,12 @@
#include <Psapi.h>
#include "MinHook.h"
#include "D3D9Hooks.h"
#include "D3D9Wrappers.h"
#include <atomic>
import TextureClient;
void ExitInstance();
void ExitInstance(bool is_unloading);
void InitInstance(HINSTANCE hModule);
namespace {
@@ -147,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)
@@ -171,16 +175,16 @@ 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;
}
BOOL WINAPI DllMain(HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
UNREFERENCED_PARAMETER(lpReserved);
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: {
#ifdef _DEBUG
@@ -196,7 +200,12 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
break;
}
case DLL_PROCESS_DETACH: {
ExitInstance();
// Process exit (lpReserved != nullptr): other threads are gone and the OS
// reclaims everything, so touching MinHook/the device here only risks a hang.
if (lpReserved != nullptr)
break;
// FreeLibrary unload: the device is still live, so tear down cleanly.
ExitInstance(true);
break;
}
default: break;
@@ -248,6 +257,10 @@ void InitInstance(HINSTANCE hModule)
if (GetProcAddress_fn) {
MH_CreateHook(GetProcAddress_fn, OnGetProcAddress, (void**)&GetProcAddress_ret);
MH_EnableHook(GetProcAddress_fn);
// Re-JIT the patched prologue + trampoline under the x86-on-ARM64 emulator (no-op native).
const HANDLE proc = GetCurrentProcess();
FlushInstructionCache(proc, (void*)GetProcAddress_fn, 64);
if (GetProcAddress_ret) FlushInstructionCache(proc, (void*)GetProcAddress_ret, 64);
}
}
@@ -314,13 +327,30 @@ extern "C" __declspec(dllexport) int __cdecl GetFiles(wchar_t* buffer, const siz
}
}
void ExitInstance()
// Optional clean-shutdown entry: call this from the host (on a normal thread) BEFORE
// FreeLibrary so teardown runs off the loader lock, where MinHook can safely suspend
// the render thread. A later FreeLibrary then unloads with nothing left to undo.
extern "C" __declspec(dllexport) void __cdecl Shutdown()
{
ExitInstance(true);
}
void ExitInstance(bool is_unloading)
{
// Teardown must run exactly once, whether reached via Shutdown() or DllMain detach.
static std::atomic<bool> torn_down{false};
if (torn_down.exchange(true)) return;
DISABLE_HOOK(GetProcAddress_fn);
// Revert every D3D9 vtable hook so the original objects are left pristine.
RemoveAllD3D9Hooks();
// On a real unload the device is still alive; release our replacement textures.
if (is_unloading) {
DestroyAllTextureClients();
}
MH_Uninitialize();
if (gMod_Loaded_d3d9_Module_Handle)