mirror of
https://github.com/gwdevhub/gMod.git
synced 2026-07-16 07:29:30 +00:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b809519f8 | |||
| c57a527b68 | |||
| f420bbd50a | |||
| e762e1b716 | |||
| 13abd4e546 | |||
| 92c6b115f3 | |||
| 2b87e0e53a | |||
| b04b4583f4 | |||
| 18d650a422 | |||
| 55e36db265 | |||
| 379878bd6d | |||
| ac39241ff9 | |||
| 5044df11b8 | |||
| b34da98139 | |||
| 17c7d5bdfc | |||
| 63110be15a | |||
| ce23dab0cd | |||
| dfb95484ea | |||
| 5be00dd712 | |||
| 23a2c38935 | |||
| edd937f4c7 | |||
| e1900cd342 | |||
| 313c7390ca | |||
| ab1a3a19e1 | |||
| 4fa1d8e2c5 | |||
| b060e25ee2 | |||
| 326b7086c9 | |||
| 485ecd1341 | |||
| 175002a9e4 | |||
| 93032262c3 | |||
| fd9d1af477 | |||
| 866455567d | |||
| 26b4f4ce4a | |||
| 5b1bcf6638 | |||
| 766e6439aa | |||
| bca0786cff | |||
| a1f32b5963 | |||
| 21c20c80e5 | |||
| 1692a5526c | |||
| f9d69fc91e | |||
| 7dbbdc480e | |||
| 9e593e83ee | |||
| b49428bafd | |||
| ae2c93fc32 | |||
| a1cadde802 | |||
| 69b06d94a2 | |||
| 514aefa371 | |||
| f07e180b53 |
@@ -0,0 +1,48 @@
|
||||
---
|
||||
Language: Cpp
|
||||
Standard: Latest
|
||||
|
||||
# Long, hand-wrapped declarations (vtable typedefs, D3D signatures) are kept as
|
||||
# written rather than reflowed to a column.
|
||||
ColumnLimit: 0
|
||||
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
AccessModifierOffset: -4
|
||||
NamespaceIndentation: All
|
||||
IndentCaseLabels: true
|
||||
PointerAlignment: Left
|
||||
MaxEmptyLinesToKeep: 2
|
||||
Cpp11BracedListStyle: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
FixNamespaceComments: false
|
||||
|
||||
# Include order is load-bearing in the Windows headers; never reorder.
|
||||
SortIncludes: Never
|
||||
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterFunction: true
|
||||
AfterControlStatement: Never
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterStruct: false
|
||||
AfterEnum: false
|
||||
AfterUnion: false
|
||||
AfterNamespace: false
|
||||
AfterExternBlock: false
|
||||
BeforeElse: true
|
||||
BeforeCatch: true
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
# Shell scripts must stay LF or bash on the CI runner chokes on \r.
|
||||
*.sh text eol=lf
|
||||
@@ -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."
|
||||
@@ -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
|
||||
}
|
||||
@@ -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."
|
||||
@@ -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."
|
||||
+43
-56
@@ -14,11 +14,17 @@ jobs:
|
||||
matrix:
|
||||
targetplatform: [x86]
|
||||
|
||||
runs-on: windows-latest
|
||||
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
|
||||
@@ -26,67 +32,48 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Config
|
||||
run: echo "DXSDK_DIR=DXSDK" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: DXSDK
|
||||
key: DXSDK
|
||||
|
||||
- name: Cache create
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
curl -L https://download.microsoft.com/download/a/e/7/ae743f1f-632b-4809-87a9-aa1bb3458e31/DXSDK_Jun10.exe -o _DX2010_.exe
|
||||
7z x _DX2010_.exe DXSDK/Include
|
||||
7z x _DX2010_.exe DXSDK/Lib/x86
|
||||
shell: bash
|
||||
|
||||
- name: Echo cache
|
||||
run: echo ${{ env.DXSDK_DIR }}
|
||||
|
||||
- name: List dxsdk files
|
||||
run: |
|
||||
cd DXSDK
|
||||
ls
|
||||
cd Include
|
||||
ls
|
||||
cd ../Lib/x86
|
||||
ls
|
||||
|
||||
- name: Copy DirectX headers to project include directory
|
||||
run: Copy-Item -Path "${{ env.DXSDK_DIR }}/Include/*" -Destination header/ -Recurse -Force
|
||||
env:
|
||||
DXSDK_DIR: ${{ env.DXSDK_DIR }}
|
||||
shell: pwsh
|
||||
|
||||
- name: Ensure Lib directory exists and copy DirectX libraries
|
||||
run: |
|
||||
$libDir = "Lib/"
|
||||
if (-Not (Test-Path -Path $libDir)) {
|
||||
New-Item -ItemType Directory -Path $libDir
|
||||
}
|
||||
Copy-Item -Path "${{ env.DXSDK_DIR }}/Lib/x86/*" -Destination $libDir -Recurse -Force
|
||||
env:
|
||||
DXSDK_DIR: ${{ env.DXSDK_DIR }}
|
||||
shell: pwsh
|
||||
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v1.3.1
|
||||
|
||||
- name: Build CMake Files
|
||||
run: cmake -S . -B build -A Win32
|
||||
- name: Install vcpkg
|
||||
run: |
|
||||
git clone https://github.com/microsoft/vcpkg.git
|
||||
.\vcpkg\bootstrap-vcpkg.bat
|
||||
|
||||
- name: Set VCPKG_ROOT
|
||||
run: |
|
||||
echo "VCPKG_ROOT=$(Get-Location)\vcpkg" >> $env:GITHUB_ENV
|
||||
shell: powershell
|
||||
|
||||
- name: Build CMake Files with vcpkg toolchain
|
||||
run: cmake --preset=vcpkg
|
||||
env:
|
||||
DXSDK_DIR: ${{ env.DXSDK_DIR }}
|
||||
VCPKG_ROOT: ${{ env.VCPKG_ROOT }}
|
||||
|
||||
- name: Build binaries
|
||||
run: cmake --build build --config Release
|
||||
env:
|
||||
DXSDK_DIR: ${{ env.DXSDK_DIR }}
|
||||
|
||||
|
||||
# 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: |
|
||||
@@ -106,7 +93,7 @@ jobs:
|
||||
mode: update
|
||||
tag_name: v${{ steps.set_version.outputs.version }}
|
||||
release_name: gMod v${{ steps.set_version.outputs.version }}
|
||||
assets: .\bin\Release\gMod.dll
|
||||
assets: .\bin\Release\gMod.dll;.\bin\Release\TpfConvert.exe;.\bin\Release\d3dx9_43.dll
|
||||
github_token: ${{ env.GITHUB_TOKEN }}
|
||||
replace_assets: true
|
||||
body_mrkdwn: ${{ env.Changelog }}
|
||||
|
||||
+16
-58
@@ -1,4 +1,4 @@
|
||||
name: gMod CI Pipeline
|
||||
name: gMod CI Pipeline
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -8,16 +8,15 @@ on:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
targetplatform: [x86]
|
||||
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2025-vs2026
|
||||
|
||||
env:
|
||||
Configuration: Release
|
||||
@@ -29,64 +28,23 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
#https://stackoverflow.com/questions/61037714/how-to-install-an-old-version-of-the-direct-x-api-in-github-actions
|
||||
- name: Config
|
||||
run: echo "DXSDK_DIR=DXSDK" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: DXSDK
|
||||
key: DXSDK
|
||||
|
||||
- name: Cache create
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
curl -L https://download.microsoft.com/download/a/e/7/ae743f1f-632b-4809-87a9-aa1bb3458e31/DXSDK_Jun10.exe -o _DX2010_.exe
|
||||
7z x _DX2010_.exe DXSDK/Include
|
||||
7z x _DX2010_.exe DXSDK/Lib/x86
|
||||
shell: bash
|
||||
|
||||
- name: Echo cache
|
||||
run: echo ${{ env.DXSDK_DIR }}
|
||||
|
||||
- name: List dxsdk files
|
||||
run: |
|
||||
cd DXSDK
|
||||
ls
|
||||
cd Include
|
||||
ls
|
||||
cd ../Lib/x86
|
||||
ls
|
||||
|
||||
- name: Copy DirectX headers to project include directory
|
||||
run: Copy-Item -Path "${{ env.DXSDK_DIR }}/Include/*" -Destination header/ -Recurse -Force
|
||||
env:
|
||||
DXSDK_DIR: ${{ env.DXSDK_DIR }}
|
||||
shell: pwsh
|
||||
|
||||
- name: Ensure Lib directory exists and copy DirectX libraries
|
||||
run: |
|
||||
$libDir = "Lib/"
|
||||
if (-Not (Test-Path -Path $libDir)) {
|
||||
New-Item -ItemType Directory -Path $libDir
|
||||
}
|
||||
Copy-Item -Path "${{ env.DXSDK_DIR }}/Lib/x86/*" -Destination $libDir -Recurse -Force
|
||||
env:
|
||||
DXSDK_DIR: ${{ env.DXSDK_DIR }}
|
||||
shell: pwsh
|
||||
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v1.3.1
|
||||
|
||||
- name: Build CMake Files
|
||||
run: cmake -S . -B build -A Win32
|
||||
- name: Install vcpkg
|
||||
run: |
|
||||
git clone https://github.com/microsoft/vcpkg.git
|
||||
.\vcpkg\bootstrap-vcpkg.bat
|
||||
|
||||
- name: Set VCPKG_ROOT
|
||||
run: |
|
||||
echo "VCPKG_ROOT=$(Get-Location)\vcpkg" >> $env:GITHUB_ENV
|
||||
shell: powershell
|
||||
|
||||
- name: Build CMake Files with vcpkg toolchain
|
||||
run: cmake --preset=vcpkg
|
||||
env:
|
||||
DXSDK_DIR: ${{ env.DXSDK_DIR }}
|
||||
VCPKG_ROOT: ${{ env.VCPKG_ROOT }}
|
||||
|
||||
- name: Build binaries
|
||||
run: cmake --build build --config Release
|
||||
env:
|
||||
DXSDK_DIR: ${{ env.DXSDK_DIR }}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
name: gMod Lint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref || github.ref_name }}
|
||||
|
||||
# Pinned to the version the tree was formatted with so CI and local agree.
|
||||
- name: Install clang-format
|
||||
run: pipx install clang-format==22.1.5
|
||||
|
||||
- name: Apply clang-format
|
||||
run: clang-format -i header/*.h source/*.cpp modules/*.ixx TpfConvert/src/*.ixx
|
||||
|
||||
# Push / same-repo PRs: commit the fixes back. Fork PRs can't be pushed to,
|
||||
# so fail the check instead and let the contributor format.
|
||||
- name: Commit fixes
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: "ci: apply clang-format"
|
||||
- name: Verify formatting (fork PRs)
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
|
||||
run: git diff --exit-code
|
||||
+16
@@ -5,6 +5,15 @@
|
||||
*.vscode
|
||||
*.project
|
||||
*.cproject
|
||||
*.vcproj
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
**/CMakeFiles/**
|
||||
cmake_install.cmake
|
||||
CMakeCache.txt
|
||||
*.sln
|
||||
*.rc
|
||||
/*.dir
|
||||
|
||||
# Build directories
|
||||
Release
|
||||
@@ -14,6 +23,8 @@ bin/
|
||||
*/Release
|
||||
*/bin
|
||||
*/obj
|
||||
vcpkg
|
||||
vcpkg_installed
|
||||
|
||||
# Bloated Windows Databases
|
||||
*.sdf
|
||||
@@ -46,7 +57,12 @@ bin/
|
||||
*.opendb
|
||||
*.wixpdb
|
||||
|
||||
**/RelWithDebInfo/
|
||||
**/Debug/
|
||||
/bin
|
||||
/win32
|
||||
/out
|
||||
/build
|
||||
/_deps
|
||||
|
||||
.idea
|
||||
@@ -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
|
||||
+19
-34
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
cmake_minimum_required(VERSION 3.29)
|
||||
|
||||
set(CMAKE_GENERATOR_PLATFORM win32)
|
||||
|
||||
@@ -9,15 +9,9 @@ if(NOT(CMAKE_SIZEOF_VOID_P EQUAL 4))
|
||||
message(FATAL_ERROR "You are configuring a non 32-bit build, this is not supported. Run cmake with `-A Win32`")
|
||||
endif()
|
||||
|
||||
option(CLEAN "Cleans third party libraries and recompiles them" OFF)
|
||||
if(${CLEAN} MATCHES "ON")
|
||||
message("Removing directory: ${PROJECT_SOURCE_DIR}/bin/install")
|
||||
file(REMOVE_RECURSE ${PROJECT_SOURCE_DIR}/bin/install)
|
||||
endif()
|
||||
|
||||
set(VERSION_MAJOR 1)
|
||||
set(VERSION_MINOR 5)
|
||||
set(VERSION_PATCH 5)
|
||||
set(VERSION_MINOR 10)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION_TWEAK 0)
|
||||
|
||||
set(VERSION_RC "${CMAKE_CURRENT_BINARY_DIR}/version.rc")
|
||||
@@ -27,54 +21,43 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin")
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/bin/install)
|
||||
|
||||
add_compile_options(/MP /permissive-)
|
||||
|
||||
find_library(D3D9_LIB NAMES d3d9 PATHS $ENV{DXSDK_DIR}/lib/x86 NO_DEFAULT_PATH)
|
||||
find_library(D3DX9_LIB NAMES d3dx9 PATHS $ENV{DXSDK_DIR}/lib/x86 NO_DEFAULT_PATH)
|
||||
find_library(DXGUID_LIB NAMES dxguid PATHS $ENV{DXSDK_DIR}/lib/x86 NO_DEFAULT_PATH)
|
||||
|
||||
if(NOT D3D9_LIB)
|
||||
find_library(D3D9_LIB NAMES d3d9 PATHS ${CMAKE_SOURCE_DIR}/Lib)
|
||||
endif()
|
||||
|
||||
if(NOT D3DX9_LIB)
|
||||
find_library(D3DX9_LIB NAMES d3dx9 PATHS ${CMAKE_SOURCE_DIR}/Lib)
|
||||
endif()
|
||||
|
||||
if(NOT DXGUID_LIB)
|
||||
find_library(DXGUID_LIB NAMES dxguid PATHS ${CMAKE_SOURCE_DIR}/Lib)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
include(libzippp)
|
||||
find_package(libzippp CONFIG REQUIRED)
|
||||
find_package(minhook CONFIG REQUIRED)
|
||||
find_package(Microsoft.GSL CONFIG REQUIRED)
|
||||
include(dxtk)
|
||||
|
||||
add_library(gMod SHARED)
|
||||
|
||||
file(GLOB SOURCES
|
||||
"header/*.h"
|
||||
"source/*.cpp"
|
||||
"modules/*.ixx"
|
||||
${VERSION_RC}
|
||||
)
|
||||
|
||||
target_include_directories(gMod PRIVATE $ENV{DXSDK_DIR}/Include)
|
||||
target_include_directories(gMod PUBLIC
|
||||
target_include_directories(gMod PRIVATE
|
||||
"header"
|
||||
${CMAKE_INSTALL_PREFIX}/include
|
||||
)
|
||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${SOURCES})
|
||||
target_sources(gMod PRIVATE ${SOURCES})
|
||||
target_compile_options(gMod PRIVATE /W4 /WX)
|
||||
|
||||
target_link_libraries(gMod PRIVATE
|
||||
${D3D9_LIB}
|
||||
${D3DX9_LIB}
|
||||
${DXGUID_LIB}
|
||||
libzippp
|
||||
psapi
|
||||
dxguid
|
||||
libzippp::libzippp
|
||||
psapi
|
||||
minhook::minhook
|
||||
directxtex
|
||||
Microsoft.GSL::GSL
|
||||
)
|
||||
target_link_options(gMod PRIVATE
|
||||
"$<$<CONFIG:DEBUG>:/NODEFAULTLIB:LIBCMT>"
|
||||
"/LARGEADDRESSAWARE"
|
||||
)
|
||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${SOURCES})
|
||||
target_sources(gMod PRIVATE ${SOURCES})
|
||||
@@ -86,3 +69,5 @@ target_compile_definitions(gMod PRIVATE
|
||||
DIRECT_INJECTION
|
||||
LOG_MESSAGE
|
||||
)
|
||||
|
||||
add_subdirectory(TpfConvert)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"version": 2,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "vcpkg",
|
||||
"generator": "Visual Studio 18 2026",
|
||||
"architecture": "Win32",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"cacheVariables": {
|
||||
"VCPKG_OVERLAY_TRIPLETS": "${sourceDir}/triplets",
|
||||
"VCPKG_TARGET_TRIPLET": "x86-windows-mixed",
|
||||
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
***gMod***
|
||||
|
||||
Continuation of the uMod project to improve performance and stability. Integrated with [Guild Wars Launcher](https://github.com/gwdevhub/gwlauncher) and [Daybreak](https://github.com/gwdevhub/Daybreak).
|
||||
|
||||
*Usage is primarily intended with GW Launcher or Daybreak, but it can be used without.*
|
||||
|
||||
**Usage with manual gMod.dll injection:**
|
||||
- Create a file called modlist.txt in either the Guild Wars (Gw.exe) folder, or the gMod.dll folder.
|
||||
- Inject gMod.dll before d3d9.dll is loaded.
|
||||
|
||||
**Usage without dll injection:**
|
||||
- Create a file called modlist.txt in the Guild Wars (Gw.exe) folder.
|
||||
- Place gMod.dll in the Guild Wars folder
|
||||
- Rename gMod.dll to d3d9.dll
|
||||
- Launch Guild Wars
|
||||
|
||||
**Format of the modlist.txt file:**
|
||||
|
||||
Each line in the modlist.txt is the full path to a mod you want to load (eg. `D:\uMod\Borderless Cartography Made Easy 2015 1.3.tpf`)
|
||||
gMod will load all these files on startup
|
||||
|
||||
**Disclaimer about [Reshade](https://github.com/crosire/reshade)**
|
||||
|
||||
Reshade in versions > 5.0.1 is known to cause glitches with TexMod, uMod and also gMod.
|
||||
If you would like to use Reshade in combination with gMod, we recommend running version [5.0.1](https://github.com/crosire/reshade/releases/tag/v5.0.1) or [4.9.1](https://github.com/crosire/reshade/releases/tag/v4.9.1).
|
||||
|
||||
**Build from source**
|
||||
|
||||
Requirements:
|
||||
- Visual Studio 2022
|
||||
- CMake 3.29+, integrated into the Developer Powershell for VS 2022
|
||||
- vcpkg, integrated into the Developer Powershell for VS 2022
|
||||
|
||||
Compile:
|
||||
- cmake --preset=vcpkg
|
||||
- cmake --open build
|
||||
- compile
|
||||
|
||||
**TpfConvert**
|
||||
Small utility to convert old .tpf files with invalid images into .zip files with working images.
|
||||
Usage:
|
||||
- Put TpfConvert.exe and d3dx9.dll in the folder where you have your old, broken texmods with invalid images.
|
||||
- Run TpfConvert.exe, all *.tpf and *.zip files are processed. The originals are saved into a newly created backup folder.
|
||||
- Done.
|
||||
@@ -1,9 +0,0 @@
|
||||
Requirements:
|
||||
- The DirectX SDK (I use June 2010)
|
||||
- Visual Studio (2010+)
|
||||
(- Maybe CMake if you don't want to use Visual Studio)
|
||||
|
||||
Usage:
|
||||
Create a file in the root GuildWars folder (where gw.exe is located) called modlist.txt
|
||||
Each line in the modlist.txt is the full path to a mod you want to load (eg. D:Games\uMod\Mods\Borderless Cartography Made Easy 2015 1.3.tpf)
|
||||
uMod will load all these files on startup
|
||||
@@ -0,0 +1,39 @@
|
||||
add_executable(TpfConvert)
|
||||
|
||||
# Find all .ixx files in the TpfConvert folder
|
||||
file(GLOB TPF_SOURCES "src/*.ixx")
|
||||
|
||||
# Add the .ixx files to the TpfConvert target
|
||||
target_sources(TpfConvert PRIVATE ${TPF_SOURCES})
|
||||
|
||||
set_target_properties(TpfConvert PROPERTIES
|
||||
CXX_STANDARD 23
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
)
|
||||
|
||||
target_compile_definitions(TpfConvert PRIVATE
|
||||
"NOMINMAX"
|
||||
"_WIN32_WINNT=_WIN32_WINNT_WIN7"
|
||||
"WIN32_LEAN_AND_MEAN"
|
||||
"VC_EXTRALEAN"
|
||||
)
|
||||
|
||||
if(DEFINED ENV{VCPKG_ROOT})
|
||||
message(STATUS "VCPKG_ROOT: $ENV{VCPKG_ROOT}")
|
||||
else()
|
||||
message(STATUS "VCPKG_ROOT is not set")
|
||||
endif()
|
||||
|
||||
if(DEFINED CMAKE_TOOLCHAIN_FILE)
|
||||
message(STATUS "CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}")
|
||||
else()
|
||||
message(STATUS "CMAKE_TOOLCHAIN_FILE is not set")
|
||||
endif()
|
||||
|
||||
find_package(dxsdk-d3dx CONFIG REQUIRED)
|
||||
|
||||
target_link_libraries(TpfConvert PRIVATE
|
||||
libzippp::libzippp
|
||||
Microsoft::D3DX9
|
||||
d3d9
|
||||
)
|
||||
@@ -0,0 +1,179 @@
|
||||
export module ModfileLoader;
|
||||
|
||||
import std;
|
||||
import <libzippp.h>;
|
||||
import ModfileLoader.TpfReader;
|
||||
|
||||
export using HashType = uint64_t;
|
||||
|
||||
export struct TexEntry {
|
||||
std::vector<uint8_t> data{};
|
||||
HashType crc_hash = 0; // hash value
|
||||
std::string ext{};
|
||||
};
|
||||
|
||||
namespace {
|
||||
HashType GetCrcFromFilename(const std::string& filename)
|
||||
{
|
||||
const static std::regex re(R"(0[xX][0-9a-fA-F]{4,16})", std::regex::optimize);
|
||||
std::smatch match;
|
||||
if (!std::regex_search(filename, match, re)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t crc64_hash = 0;
|
||||
const auto number_str = match.str();
|
||||
try {
|
||||
crc64_hash = std::stoull(number_str, nullptr, 16);
|
||||
}
|
||||
catch (const std::invalid_argument&) {
|
||||
std::print(stderr, "Failed to parse {} as a hash\n", filename);
|
||||
return 0;
|
||||
}
|
||||
catch (const std::out_of_range&) {
|
||||
std::print(stderr, "Out of range while parsing {} as a hash\n", filename);
|
||||
return 0;
|
||||
}
|
||||
return crc64_hash;
|
||||
}
|
||||
}
|
||||
|
||||
export class ModfileLoader {
|
||||
std::filesystem::path file_name;
|
||||
const std::string TPF_PASSWORD{
|
||||
0x73, 0x2A, 0x63, 0x7D, 0x5F, 0x0A, static_cast<char>(0xA6), static_cast<char>(0xBD),
|
||||
0x7D, 0x65, 0x7E, 0x67, 0x61, 0x2A, 0x7F, 0x7F,
|
||||
0x74, 0x61, 0x67, 0x5B, 0x60, 0x70, 0x45, 0x74,
|
||||
0x5C, 0x22, 0x74, 0x5D, 0x6E, 0x6A, 0x73, 0x41,
|
||||
0x77, 0x6E, 0x46, 0x47, 0x77, 0x49, 0x0C, 0x4B,
|
||||
0x46, 0x6F};
|
||||
|
||||
public:
|
||||
ModfileLoader(const std::filesystem::path& fileName);
|
||||
|
||||
std::vector<TexEntry> GetContents() const;
|
||||
|
||||
private:
|
||||
std::vector<TexEntry> GetTpfContents() const;
|
||||
|
||||
std::vector<TexEntry> GetFileContents() const;
|
||||
|
||||
static void LoadEntries(libzippp::ZipArchive& archive, std::vector<TexEntry>& entries);
|
||||
};
|
||||
|
||||
ModfileLoader::ModfileLoader(const std::filesystem::path& fileName)
|
||||
{
|
||||
file_name = std::filesystem::absolute(fileName);
|
||||
}
|
||||
|
||||
std::vector<TexEntry> ModfileLoader::GetContents() const
|
||||
{
|
||||
try {
|
||||
return file_name.wstring().ends_with(L".tpf") ? GetTpfContents() : GetFileContents();
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
std::print(stderr, "Failed to open mod file: {}\n", file_name.string().c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<TexEntry> ModfileLoader::GetTpfContents() const
|
||||
{
|
||||
std::vector<TexEntry> entries;
|
||||
auto tpf_reader = TpfReader(file_name);
|
||||
const auto buffer = tpf_reader.ReadToEnd();
|
||||
const auto zip_archive = libzippp::ZipArchive::fromBuffer(buffer.data(), buffer.size(), false, TPF_PASSWORD);
|
||||
if (!zip_archive) {
|
||||
std::print(stderr, "Failed to open tpf file: {} - {} uint8_ts!\n", file_name.string(), buffer.size());
|
||||
return {};
|
||||
}
|
||||
zip_archive->setErrorHandlerCallback(
|
||||
[](const std::string& message, const std::string& strerror, int zip_error_code, int system_error_code) -> void {
|
||||
std::print(stderr, "GetTpfContents: {} {} {} {}\n", message, strerror, zip_error_code, system_error_code);
|
||||
});
|
||||
zip_archive->open();
|
||||
LoadEntries(*zip_archive, entries);
|
||||
zip_archive->close();
|
||||
libzippp::ZipArchive::free(zip_archive);
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
std::vector<TexEntry> ModfileLoader::GetFileContents() const
|
||||
{
|
||||
std::vector<TexEntry> entries;
|
||||
|
||||
libzippp::ZipArchive zip_archive(file_name.string());
|
||||
zip_archive.open();
|
||||
LoadEntries(zip_archive, entries);
|
||||
zip_archive.close();
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
void ParseSimpleArchive(const libzippp::ZipArchive& archive, std::vector<TexEntry>& entries)
|
||||
{
|
||||
for (const auto& entry : archive.getEntries()) {
|
||||
if (!entry.isFile())
|
||||
continue;
|
||||
const auto entry_name = entry.getName();
|
||||
const auto crc_hash = GetCrcFromFilename(entry_name);
|
||||
if (!crc_hash)
|
||||
continue;
|
||||
const auto data_ptr = static_cast<uint8_t*>(entry.readAsBinary());
|
||||
const auto size = entry.getSize();
|
||||
std::vector vec(data_ptr, data_ptr + size);
|
||||
std::filesystem::path tex_name(entry_name);
|
||||
entries.emplace_back(std::move(vec), crc_hash, tex_name.extension().string());
|
||||
delete[] data_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ParseTexmodArchive(std::vector<std::string>& lines, libzippp::ZipArchive& archive, std::vector<TexEntry>& entries)
|
||||
{
|
||||
for (const auto& line : lines) {
|
||||
// 0xC57D73F7|GW.EXE_0xC57D73F7.tga\r\n
|
||||
// match[1] | match[2]
|
||||
const static auto address_file_regex = std::regex(R"(^[\\/.]*([^|]+)\|([^\r\n]+))", std::regex::optimize);
|
||||
std::smatch match;
|
||||
if (!std::regex_search(line, match, address_file_regex))
|
||||
continue;
|
||||
const auto address_string = match[1].str();
|
||||
const auto file_path = match[2].str();
|
||||
|
||||
const auto crc_hash = GetCrcFromFilename(address_string);
|
||||
if (!crc_hash)
|
||||
continue;
|
||||
|
||||
const auto entry = archive.getEntry(file_path);
|
||||
if (entry.isNull() || !entry.isFile())
|
||||
continue;
|
||||
|
||||
const auto data_ptr = static_cast<uint8_t*>(entry.readAsBinary());
|
||||
const auto size = static_cast<size_t>(entry.getSize());
|
||||
std::vector vec(data_ptr, data_ptr + size);
|
||||
const auto tex_name = std::filesystem::path(entry.getName());
|
||||
entries.emplace_back(std::move(vec), crc_hash, tex_name.extension().string());
|
||||
delete[] data_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ModfileLoader::LoadEntries(libzippp::ZipArchive& archive, std::vector<TexEntry>& entries)
|
||||
{
|
||||
const auto def_file = archive.getEntry("texmod.def");
|
||||
if (def_file.isNull() || !def_file.isFile()) {
|
||||
ParseSimpleArchive(archive, entries);
|
||||
}
|
||||
else {
|
||||
const auto def = def_file.readAsText();
|
||||
std::istringstream iss(def);
|
||||
std::vector<std::string> lines;
|
||||
std::string line;
|
||||
|
||||
while (std::getline(iss, line)) {
|
||||
lines.push_back(line);
|
||||
}
|
||||
|
||||
ParseTexmodArchive(lines, archive, entries);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
export module ModfileLoader.TpfReader;
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
import std;
|
||||
|
||||
class XorStreamReader {
|
||||
export class TpfReader {
|
||||
public:
|
||||
XorStreamReader(const std::string& path)
|
||||
TpfReader(const std::filesystem::path& path)
|
||||
{
|
||||
file_stream = std::ifstream(path, std::ios::binary);
|
||||
if (!file_stream.seekg(0, std::ios::end).good() || !file_stream.seekg(0, std::ios::beg).good()) {
|
||||
@@ -13,7 +12,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
~XorStreamReader()
|
||||
~TpfReader()
|
||||
{
|
||||
file_stream.close();
|
||||
}
|
||||
@@ -36,18 +35,18 @@ public:
|
||||
|
||||
// in the other zip libraries, these had to be cut off, with libzip we need to zero them out
|
||||
// cutting them off makes the archive invalid
|
||||
//data.resize(last_zero);
|
||||
// data.resize(last_zero);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ifstream file_stream;
|
||||
long line_length;
|
||||
long line_length = 0;
|
||||
|
||||
[[nodiscard]] char XOR(const char b, const long position) const
|
||||
{
|
||||
if (position > line_length - 4) {
|
||||
if (position >= (line_length / 4) * 4) {
|
||||
return b ^ TPF_XOREven;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
#include <corecrt_wstdio.h>
|
||||
#include <Windows.h>
|
||||
#include <d3dx9.h>
|
||||
#include <d3d9.h>
|
||||
|
||||
import std;
|
||||
import ModfileLoader;
|
||||
import <libzippp.h>;
|
||||
|
||||
struct TexEntry;
|
||||
|
||||
namespace {
|
||||
IDirect3D9* pD3D = nullptr;
|
||||
IDirect3DDevice9* pDevice = nullptr;
|
||||
|
||||
bool InitializeDirect3D()
|
||||
{
|
||||
pD3D = Direct3DCreate9(D3D_SDK_VERSION);
|
||||
if (pD3D == nullptr) {
|
||||
std::print(stderr, "Failed to create Direct3D9 object\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
D3DPRESENT_PARAMETERS d3dpp = {};
|
||||
d3dpp.Windowed = TRUE;
|
||||
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
||||
d3dpp.hDeviceWindow = GetDesktopWindow();
|
||||
|
||||
if (FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, GetDesktopWindow(),
|
||||
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDevice))) {
|
||||
std::print(stderr, "Failed to create Direct3D9 device\n");
|
||||
pD3D->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CleanupDirect3D()
|
||||
{
|
||||
if (pDevice) pDevice->Release();
|
||||
if (pD3D) pD3D->Release();
|
||||
}
|
||||
|
||||
std::pair<std::string, std::vector<uint8_t>> SaveAsDDS(const TexEntry& entry)
|
||||
{
|
||||
IDirect3DTexture9* pTexture = nullptr;
|
||||
ID3DXBuffer* pBuffer = nullptr;
|
||||
|
||||
HRESULT hr = D3DXCreateTextureFromFileInMemoryEx(pDevice,
|
||||
entry.data.data(), entry.data.size(),
|
||||
D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0,
|
||||
D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_FILTER_NONE,
|
||||
D3DX_FILTER_NONE, 0, NULL, NULL, &pTexture);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
std::print(stderr, "Failed to create texture from memory\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
hr = D3DXSaveTextureToFileInMemory(&pBuffer, D3DXIFF_DDS, pTexture, NULL);
|
||||
if (FAILED(hr)) {
|
||||
std::print(stderr, "Failed to save texture to DDS memory buffer\n");
|
||||
pTexture->Release();
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string dds_filename = std::format("0x{:x}.dds", entry.crc_hash);
|
||||
std::vector<uint8_t> dds_data(pBuffer->GetBufferSize());
|
||||
std::memcpy(dds_data.data(), pBuffer->GetBufferPointer(), pBuffer->GetBufferSize());
|
||||
|
||||
pTexture->Release();
|
||||
pBuffer->Release();
|
||||
|
||||
return {dds_filename, dds_data};
|
||||
}
|
||||
|
||||
std::filesystem::path GetExecutablePath()
|
||||
{
|
||||
char buffer[MAX_PATH];
|
||||
GetModuleFileNameA(NULL, buffer, MAX_PATH);
|
||||
return std::filesystem::path(buffer).parent_path();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
const std::filesystem::path path = argc > 1 ? argv[1] : GetExecutablePath();
|
||||
const auto backup_path = path / "backup";
|
||||
|
||||
if (!InitializeDirect3D()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(path)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(backup_path)) {
|
||||
std::filesystem::create_directory(backup_path);
|
||||
}
|
||||
|
||||
for (const auto& modfile : std::filesystem::directory_iterator(path)) {
|
||||
if (modfile.is_regular_file() && (modfile.path().extension() == ".tpf" || modfile.path().extension() == ".zip")) {
|
||||
const auto mod_path = modfile.path();
|
||||
const auto backup_file = backup_path / mod_path.filename();
|
||||
|
||||
if (std::filesystem::exists(backup_file)) {
|
||||
std::print("Skipping previous TpfConvert output: {}\n", mod_path.filename().string());
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
std::print("Processing: {}\n", mod_path.filename().string());
|
||||
std::error_code ec;
|
||||
std::filesystem::rename(mod_path, backup_file, ec);
|
||||
}
|
||||
|
||||
const auto zip_filename = path / (mod_path.stem().string() + ".zip");
|
||||
if (std::filesystem::exists(zip_filename)) {
|
||||
std::filesystem::remove(zip_filename);
|
||||
}
|
||||
|
||||
ModfileLoader loader(backup_file);
|
||||
std::vector<std::pair<std::string, std::vector<uint8_t>>> data_entries;
|
||||
const auto entries = loader.GetContents();
|
||||
|
||||
libzippp::ZipArchive zip_archive(zip_filename.string());
|
||||
zip_archive.open(libzippp::ZipArchive::Write);
|
||||
|
||||
for (const auto& entry : entries) {
|
||||
auto [dds_filename, dds_data] = SaveAsDDS(entry);
|
||||
if (!dds_filename.empty()) {
|
||||
data_entries.emplace_back(dds_filename, std::move(dds_data));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [filename, data] : data_entries) {
|
||||
const auto success = zip_archive.addData(filename, data.data(), data.size());
|
||||
if (!success) {
|
||||
std::print(stderr, "Failed to add data to ZIP: {}\n", filename);
|
||||
}
|
||||
}
|
||||
|
||||
zip_archive.close();
|
||||
std::print("Saved to ZIP: {}\n", zip_filename.string());
|
||||
}
|
||||
}
|
||||
|
||||
CleanupDirect3D();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
include_guard()
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
DirectXTex
|
||||
GIT_REPOSITORY https://github.com/microsoft/DirectXTex
|
||||
GIT_TAG oct2023)
|
||||
FetchContent_GetProperties(directxtex)
|
||||
if (directxtex_POPULATED)
|
||||
return()
|
||||
endif()
|
||||
|
||||
FetchContent_Populate(directxtex)
|
||||
|
||||
add_library(directxtex)
|
||||
file(GLOB SOURCES
|
||||
"${directxtex_SOURCE_DIR}/DDSTextureLoader/DDSTextureLoader9.h"
|
||||
"${directxtex_SOURCE_DIR}/DDSTextureLoader/DDSTextureLoader9.cpp"
|
||||
"${directxtex_SOURCE_DIR}/WICTextureLoader/WICTextureLoader9.h"
|
||||
"${directxtex_SOURCE_DIR}/WICTextureLoader/WICTextureLoader9.cpp"
|
||||
"${directxtex_SOURCE_DIR}/DirectXTex/*.h"
|
||||
"${directxtex_SOURCE_DIR}/DirectXTex/*.cpp"
|
||||
)
|
||||
list(REMOVE_ITEM SOURCES
|
||||
"${directxtex_SOURCE_DIR}/DirectXTex/BCDirectCompute.cpp"
|
||||
)
|
||||
source_group(TREE ${directxtex_SOURCE_DIR} FILES ${SOURCES})
|
||||
target_sources(directxtex PRIVATE ${SOURCES})
|
||||
target_include_directories(directxtex PUBLIC "${directxtex_SOURCE_DIR}")
|
||||
|
||||
set_target_properties(directxtex PROPERTIES FOLDER "Dependencies/")
|
||||
@@ -1,65 +0,0 @@
|
||||
include_guard()
|
||||
include(FetchContent)
|
||||
include(zlib)
|
||||
|
||||
FetchContent_Declare(
|
||||
libzip
|
||||
GIT_REPOSITORY https://github.com/nih-at/libzip
|
||||
GIT_TAG v1.10.1)
|
||||
FetchContent_GetProperties(libzip)
|
||||
|
||||
if (libzip_POPULATED)
|
||||
message(STATUS "Skipping libzip download")
|
||||
return()
|
||||
endif()
|
||||
|
||||
FetchContent_Populate(libzip)
|
||||
if(EXISTS ${CMAKE_INSTALL_PREFIX}/lib/zip.lib)
|
||||
message(STATUS "Skipping libzip build")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(OPTIONS
|
||||
"-A" "Win32"
|
||||
"-DZLIB_LIBRARY:PATH=${CMAKE_INSTALL_PREFIX}/lib/zlibstatic.lib"
|
||||
"-DZLIB_INCLUDE_DIR:PATH=${CMAKE_INSTALL_PREFIX}/include"
|
||||
"-DENABLE_COMMONCRYPTO=OFF"
|
||||
"-DENABLE_GNUTLS=OFF"
|
||||
"-DENABLE_MBEDTLS=OFF"
|
||||
"-DENABLE_OPENSSL=OFF"
|
||||
"-DENABLE_WINDOWS_CRYPTO=ON"
|
||||
"-DENABLE_FDOPEN=OFF"
|
||||
"-DENABLE_BZIP2=OFF"
|
||||
"-DENABLE_LZMA=OFF"
|
||||
"-DENABLE_ZSTD=OFF"
|
||||
"-DBUILD_TOOLS=OFF"
|
||||
"-DBUILD_REGRESS=OFF"
|
||||
"-DBUILD_EXAMPLES=OFF"
|
||||
"-DBUILD_DOC=OFF"
|
||||
"-DLIBZIP_DO_INSTALL=ON"
|
||||
"-DBUILD_SHARED_LIBS=OFF"
|
||||
"-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
|
||||
"-DCMAKE_MSVC_RUNTIME_LIBRARY=${CMAKE_MSVC_RUNTIME_LIBRARY}"
|
||||
)
|
||||
|
||||
message("Building libzip with OPTIONS:\n${OPTIONS}")
|
||||
|
||||
# Patch CMakeLists.txt to require version 3.15 - otherwise cmake ignores MSVC_RUNTIME_LIBRARY
|
||||
file(READ ${libzip_SOURCE_DIR}/CMakeLists.txt FILE_CONTENT)
|
||||
string(REPLACE "VERSION 3.5.0" "VERSION 3.15.0" UPDATED_CONTENT "${FILE_CONTENT}")
|
||||
file(WRITE ${libzip_SOURCE_DIR}/CMakeLists.txt "${UPDATED_CONTENT}")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} ${OPTIONS} .
|
||||
WORKING_DIRECTORY ${libzip_SOURCE_DIR}
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} --build . --config Release
|
||||
WORKING_DIRECTORY ${libzip_SOURCE_DIR}
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} --install . --config Release
|
||||
WORKING_DIRECTORY ${libzip_SOURCE_DIR}
|
||||
)
|
||||
|
||||
message(STATUS "Finished libzip build")
|
||||
@@ -1,42 +0,0 @@
|
||||
include_guard()
|
||||
include(FetchContent)
|
||||
include(libzip)
|
||||
|
||||
find_library(ZLIB_LIBRARY REQUIRED
|
||||
NAMES zlibstatic
|
||||
PATHS ${CMAKE_INSTALL_PREFIX}/lib
|
||||
)
|
||||
find_library(LIBZIP_LIBRARY REQUIRED
|
||||
NAMES zip
|
||||
PATHS ${CMAKE_INSTALL_PREFIX}/lib
|
||||
)
|
||||
|
||||
FetchContent_Declare(
|
||||
libzippp
|
||||
GIT_REPOSITORY https://github.com/ctabin/libzippp
|
||||
GIT_TAG libzippp-v7.0-1.10.1)
|
||||
FetchContent_GetProperties(libzippp)
|
||||
|
||||
if (NOT libzippp_POPULATED)
|
||||
FetchContent_Populate(libzippp)
|
||||
endif()
|
||||
|
||||
add_library(libzippp)
|
||||
|
||||
set(SOURCES
|
||||
"${libzippp_SOURCE_DIR}/src/libzippp.h"
|
||||
"${libzippp_SOURCE_DIR}/src/libzippp.cpp"
|
||||
)
|
||||
source_group(TREE ${libzippp_SOURCE_DIR} FILES ${SOURCES})
|
||||
target_sources(libzippp PRIVATE ${SOURCES})
|
||||
target_include_directories(libzippp PUBLIC "${libzippp_SOURCE_DIR}/src")
|
||||
target_include_directories(libzippp PRIVATE "${CMAKE_INSTALL_PREFIX}/include")
|
||||
target_compile_definitions(libzippp PUBLIC
|
||||
LIBZIPPP_WITH_ENCRYPTION
|
||||
)
|
||||
target_link_libraries(libzippp PRIVATE
|
||||
${LIBZIP_LIBRARY}
|
||||
${ZLIB_LIBRARY}
|
||||
)
|
||||
|
||||
set_target_properties(libzippp PROPERTIES FOLDER "Dependencies/")
|
||||
@@ -1,47 +0,0 @@
|
||||
include_guard()
|
||||
include(FetchContent)
|
||||
|
||||
# Fetch zlib using FetchContent
|
||||
FetchContent_Declare(
|
||||
zlib
|
||||
GIT_REPOSITORY https://github.com/madler/zlib
|
||||
GIT_TAG v1.3
|
||||
)
|
||||
|
||||
FetchContent_GetProperties(zlib)
|
||||
|
||||
if(zlib_POPULATED)
|
||||
message(STATUS "Skipping zlib download")
|
||||
return()
|
||||
endif()
|
||||
|
||||
FetchContent_Populate(zlib)
|
||||
|
||||
if(EXISTS ${CMAKE_INSTALL_PREFIX}/lib/zlibstatic.lib)
|
||||
message(STATUS "Skipping zlib build")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(OPTIONS
|
||||
"-A" "Win32"
|
||||
"-DBUILD_SHARED_LIBS=OFF"
|
||||
"-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
|
||||
"-DCMAKE_MSVC_RUNTIME_LIBRARY=${CMAKE_MSVC_RUNTIME_LIBRARY}"
|
||||
)
|
||||
|
||||
message("Building zlib with OPTIONS:\n${OPTIONS}")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} ${OPTIONS} .
|
||||
WORKING_DIRECTORY ${zlib_SOURCE_DIR}
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} --build . --config Release
|
||||
WORKING_DIRECTORY ${zlib_SOURCE_DIR}
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} --install . --config Release
|
||||
WORKING_DIRECTORY ${zlib_SOURCE_DIR}
|
||||
)
|
||||
|
||||
message(STATUS "Finished zlib build")
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d9.h>
|
||||
|
||||
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);
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d9.h>
|
||||
#include "Defines.h"
|
||||
|
||||
// Per-texture side-state, keyed by the real texture pointer (see D3D9Hooks.h).
|
||||
// Held out-of-band so reverting the vtable hooks fully detaches gMod.
|
||||
|
||||
enum class TexType {
|
||||
Tex2D,
|
||||
Volume,
|
||||
Cube,
|
||||
};
|
||||
|
||||
// State for one texture: an "original" the game created, or a "fake" we loaded
|
||||
// from a mod to stand in for one.
|
||||
struct TexState {
|
||||
IDirect3DBaseTexture9* real = nullptr;
|
||||
// An original points at the fake bound in its place (or nullptr); a fake
|
||||
// points back at the original it replaces.
|
||||
TexState* partner = nullptr;
|
||||
IDirect3DDevice9* device = nullptr;
|
||||
TextureFileStruct* reference = nullptr; // the modfile backing a fake
|
||||
HashTuple hash = {};
|
||||
TexType type = TexType::Tex2D;
|
||||
bool isFake = false;
|
||||
};
|
||||
|
||||
// Content hash (CRC32 + optional CRC64) of an original texture. Defined in
|
||||
// D3D9Hooks.cpp.
|
||||
HashTuple GetTextureHash(const TexState* state);
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
// using HashType = DWORD32;
|
||||
using HashType = DWORD64;
|
||||
|
||||
struct HashTuple {
|
||||
HashType crc32;
|
||||
HashType crc64;
|
||||
|
||||
bool operator==(const HashTuple& other) const noexcept
|
||||
{
|
||||
return (other.crc32 == crc32) || (other.crc64 == crc64);
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return crc32 != 0 || crc64 != 0;
|
||||
}
|
||||
|
||||
explicit operator HashType() const noexcept
|
||||
{
|
||||
return crc32 ? crc32 : crc64;
|
||||
}
|
||||
};
|
||||
|
||||
struct TexEntry {
|
||||
std::vector<BYTE> data{};
|
||||
HashType crc_hash = 0; // hash value
|
||||
std::string ext{};
|
||||
};
|
||||
|
||||
struct TextureFileStruct {
|
||||
std::vector<BYTE> data{};
|
||||
HashType crc_hash = 0; // hash value
|
||||
};
|
||||
|
||||
inline void Message([[maybe_unused]] const char* format, ...)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
// const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
//[[maybe_unused]] const auto success = SetConsoleTextAttribute(hConsole, 0); // white
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void Info([[maybe_unused]] const char* format, ...)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
// const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
//[[maybe_unused]] const auto success = SetConsoleTextAttribute(hConsole, 0); // white
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void Warning([[maybe_unused]] const char* format, ...)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
const HANDLE hConsole = GetStdHandle(STD_ERROR_HANDLE);
|
||||
[[maybe_unused]] const auto success = SetConsoleTextAttribute(hConsole, 6); // yellow
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
@@ -33,12 +33,19 @@
|
||||
|
||||
// define error states
|
||||
#define uMod_ERROR_FATAL 1u
|
||||
#define uMod_ERROR_MUTEX 1u<<1
|
||||
#define uMod_ERROR_PIPE 1u<<2
|
||||
#define uMod_ERROR_MEMORY 1u<<3
|
||||
#define uMod_ERROR_TEXTURE 1u<<4
|
||||
#define uMod_ERROR_MULTIPLE_IDirect3D9 1u<<5
|
||||
#define uMod_ERROR_MULTIPLE_IDirect3DDevice9 1u<<6
|
||||
#define uMod_ERROR_UPDATE 1u<<7
|
||||
#define uMod_ERROR_SERVER 1u<<8
|
||||
#define uMod_ERROR_MUTEX 1u << 1
|
||||
#define uMod_ERROR_PIPE 1u << 2
|
||||
#define uMod_ERROR_MEMORY 1u << 3
|
||||
#define uMod_ERROR_TEXTURE 1u << 4
|
||||
#define uMod_ERROR_MULTIPLE_IDirect3D9 1u << 5
|
||||
#define uMod_ERROR_MULTIPLE_IDirect3DDevice9 1u << 6
|
||||
#define uMod_ERROR_UPDATE 1u << 7
|
||||
#define uMod_ERROR_SERVER 1u << 8
|
||||
|
||||
__declspec(noreturn) void FatalAssert(
|
||||
const char* expr,
|
||||
const char* file,
|
||||
unsigned int line,
|
||||
const char* function);
|
||||
|
||||
#define ASSERT(expr) ((void)(!!(expr) || (FatalAssert(#expr, __FILE__, (unsigned)__LINE__, __FUNCTION__), 0)))
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <ranges>
|
||||
#include "Utils.h"
|
||||
|
||||
#include <d3d9.h>
|
||||
#include <gsl/gsl>
|
||||
|
||||
#include "Defines.h"
|
||||
#include "Error.h"
|
||||
|
||||
#include "D3D9State.h"
|
||||
|
||||
#pragma warning(disable : 4477)
|
||||
|
||||
extern unsigned int gl_ErrorState;
|
||||
extern HINSTANCE gl_hThisInstance;
|
||||
inline std::filesystem::path gmod_dll_path;
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace utils {
|
||||
template <typename T>
|
||||
void erase_first(std::vector<T>& vec, const T& elem)
|
||||
{
|
||||
const auto found = std::ranges::find(vec, elem);
|
||||
if (found != std::ranges::end(vec)) {
|
||||
vec.erase(found);
|
||||
}
|
||||
}
|
||||
|
||||
inline std::wstring utf8_to_wstring(const std::string& utf8str)
|
||||
{
|
||||
if (utf8str.empty()) return {};
|
||||
|
||||
// Calculate the number of wide characters needed for the conversion
|
||||
const int count = MultiByteToWideChar(CP_UTF8, 0, utf8str.c_str(), -1, nullptr, 0);
|
||||
if (count == 0) throw std::runtime_error("Failed to convert UTF-8 to UTF-16");
|
||||
|
||||
std::wstring wstr(count, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, utf8str.c_str(), -1, wstr.data(), count);
|
||||
|
||||
return wstr;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <ranges>
|
||||
#include <libzippp.h>
|
||||
|
||||
struct TpfEntry {
|
||||
std::string name;
|
||||
std::string entry;
|
||||
uint32_t crc_hash;
|
||||
void* data;
|
||||
unsigned long long size;
|
||||
};
|
||||
|
||||
class gMod_FileLoader {
|
||||
std::string file_name;
|
||||
const std::string TPF_PASSWORD{
|
||||
0x73, 0x2A, 0x63, 0x7D, 0x5F, 0x0A, static_cast<char>(0xA6), static_cast<char>(0xBD),
|
||||
0x7D, 0x65, 0x7E, 0x67, 0x61, 0x2A, 0x7F, 0x7F,
|
||||
0x74, 0x61, 0x67, 0x5B, 0x60, 0x70, 0x45, 0x74,
|
||||
0x5C, 0x22, 0x74, 0x5D, 0x6E, 0x6A, 0x73, 0x41,
|
||||
0x77, 0x6E, 0x46, 0x47, 0x77, 0x49, 0x0C, 0x4B,
|
||||
0x46, 0x6F
|
||||
};
|
||||
|
||||
public:
|
||||
gMod_FileLoader(const std::string& fileName);
|
||||
|
||||
std::vector<TpfEntry> GetContents();
|
||||
|
||||
private:
|
||||
|
||||
std::vector<TpfEntry> GetTpfContents();
|
||||
|
||||
std::vector<TpfEntry> GetFileContents();
|
||||
|
||||
void LoadEntries(libzippp::ZipArchive& archive, std::vector<TpfEntry>& entries);
|
||||
};
|
||||
@@ -1,165 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "uMod_GlobalDefines.h"
|
||||
#include "uMod_IDirect3DTexture9.h"
|
||||
extern unsigned int gl_ErrorState;
|
||||
|
||||
struct TextureFileStruct {
|
||||
bool ForceReload; // to force a reload of the texture (if it is already modded)
|
||||
char* pData; // store texture file as file in memory
|
||||
unsigned int Size; // size of file
|
||||
int NumberOfTextures;
|
||||
int Reference; // for a fast delete in the FileHandler
|
||||
IDirect3DBaseTexture9** Textures; // pointer to the fake textures
|
||||
MyTypeHash Hash; // hash value
|
||||
};
|
||||
|
||||
class uMod_FileHandler // array to store TextureFileStruct
|
||||
{
|
||||
public:
|
||||
uMod_FileHandler();
|
||||
~uMod_FileHandler();
|
||||
|
||||
int Add(TextureFileStruct* file);
|
||||
int Remove(TextureFileStruct* file);
|
||||
|
||||
int GetNumber() { return Number; }
|
||||
|
||||
TextureFileStruct* operator [](int i)
|
||||
{
|
||||
if (i < 0 || i >= Number) {
|
||||
return nullptr;
|
||||
}
|
||||
return Files[i / FieldLength][i % FieldLength];
|
||||
}
|
||||
|
||||
protected:
|
||||
static constexpr int FieldLength = 1024;
|
||||
long Number = 0;
|
||||
int FieldCounter = 0;
|
||||
TextureFileStruct*** Files = nullptr;
|
||||
};
|
||||
|
||||
|
||||
template <class T>
|
||||
class uMod_TextureHandler // array to store uMod_IDirect3DTexture9, uMod_IDirect3DVolumeTexture9 or uMod_IDirect3DCubeTexture9
|
||||
{
|
||||
public:
|
||||
uMod_TextureHandler();
|
||||
~uMod_TextureHandler();
|
||||
|
||||
int Add(T* texture);
|
||||
int Remove(T* texture);
|
||||
|
||||
int GetNumber() { return Number; }
|
||||
|
||||
T* operator [](int i)
|
||||
{
|
||||
if (i < 0 || i >= Number) {
|
||||
return nullptr;
|
||||
}
|
||||
return Textures[i / FieldLength][i % FieldLength];
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int FieldLength = 1024;
|
||||
long Number;
|
||||
int FieldCounter;
|
||||
T*** Textures;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
uMod_TextureHandler<T>::uMod_TextureHandler()
|
||||
{
|
||||
Message("uMod_TextureHandler(): %p\n", this);
|
||||
Number = 0;
|
||||
FieldCounter = 0;
|
||||
|
||||
Textures = NULL;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uMod_TextureHandler<T>::~uMod_TextureHandler()
|
||||
{
|
||||
Message("~uMod_TextureHandler(): %p\n", this);
|
||||
if (Textures != nullptr) {
|
||||
for (int i = 0; i < FieldCounter; i++) {
|
||||
delete [] Textures[i];
|
||||
}
|
||||
delete [] Textures;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
int uMod_TextureHandler<T>::Add(T* pTexture)
|
||||
{
|
||||
Message("uMod_TextureHandler::Add( %p): %p\n", pTexture, this);
|
||||
if (gl_ErrorState & uMod_ERROR_FATAL) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
|
||||
if (pTexture->Reference >= 0) {
|
||||
return RETURN_TEXTURE_ALLREADY_ADDED;
|
||||
}
|
||||
|
||||
if (Number / FieldLength == FieldCounter) {
|
||||
T*** temp = nullptr;
|
||||
try { temp = new T**[FieldCounter + 10]; }
|
||||
catch (...) {
|
||||
gl_ErrorState |= uMod_ERROR_MEMORY | uMod_ERROR_TEXTURE;
|
||||
return RETURN_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (int i = 0; i < FieldCounter; i++) {
|
||||
temp[i] = Textures[i];
|
||||
}
|
||||
|
||||
for (int i = FieldCounter; i < FieldCounter + 10; i++) {
|
||||
temp[i] = NULL;
|
||||
}
|
||||
|
||||
FieldCounter += 10;
|
||||
|
||||
delete [] Textures;
|
||||
|
||||
Textures = temp;
|
||||
}
|
||||
if (Number % FieldLength == 0) {
|
||||
try {
|
||||
if (Textures[Number / FieldLength] == NULL) {
|
||||
Textures[Number / FieldLength] = new T*[FieldLength];
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
Textures[Number / FieldLength] = NULL;
|
||||
gl_ErrorState |= uMod_ERROR_MEMORY | uMod_ERROR_TEXTURE;
|
||||
return RETURN_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
Textures[Number / FieldLength][Number % FieldLength] = pTexture;
|
||||
pTexture->Reference = Number++;
|
||||
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
int uMod_TextureHandler<T>::Remove(T* pTexture) //will be called, if a texture is completely released
|
||||
{
|
||||
Message("uMod_TextureHandler::Remove(%p): %p\n", pTexture, this);
|
||||
if (gl_ErrorState & uMod_ERROR_FATAL) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
|
||||
int ref = pTexture->Reference;
|
||||
if (ref < 0) {
|
||||
return RETURN_OK; // returning if no TextureHandlerRef is set
|
||||
}
|
||||
|
||||
if (ref < (--Number)) {
|
||||
Textures[ref / FieldLength][ref % FieldLength] = Textures[Number / FieldLength][Number % FieldLength];
|
||||
Textures[ref / FieldLength][ref % FieldLength]->Reference = ref;
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d9.h>
|
||||
|
||||
void InitInstance(HINSTANCE hModule);
|
||||
void ExitInstance();
|
||||
void LoadOriginalDll();
|
||||
bool FindLoadedDll();
|
||||
bool IsDesiredModule(HMODULE hModule, HANDLE hProcess);
|
||||
bool HasDesiredMethods(HMODULE hModule, HANDLE hProcess);
|
||||
bool HookThisProgram(char* ret);
|
||||
|
||||
void* DetourFunc(BYTE* src, const BYTE* dst, int len);
|
||||
bool RetourFunc(BYTE* src, BYTE* restore, int len);
|
||||
IDirect3D9*APIENTRY uMod_Direct3DCreate9(UINT SDKVersion);
|
||||
HRESULT APIENTRY uMod_Direct3DCreate9Ex(UINT SDKVersion, IDirect3D9Ex** ppD3D);
|
||||
|
||||
|
||||
#ifdef DIRECT_INJECTION
|
||||
void Nothing();
|
||||
#endif
|
||||
@@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef LOG_MESSAGE
|
||||
extern FILE* gl_File;
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define Message(...) { printf(__VA_ARGS__); }
|
||||
#else
|
||||
#define Message(...)
|
||||
#endif
|
||||
#ifdef HOOK_INJECTION
|
||||
#define OpenMessage(...) {if (fopen_s( &gl_File, "uMod_log.txt", "wt")) gl_File=NULL; else fprintf( gl_File, "HI R40: 0000000\n");}
|
||||
#endif
|
||||
|
||||
#ifdef DIRECT_INJECTION
|
||||
#define OpenMessage(...)
|
||||
#endif
|
||||
|
||||
#ifdef NO_INJECTION
|
||||
#define OpenMessage(...) {if (fopen_s( &gl_File, "uMod_log.txt", "wt")) gl_File=NULL; else fprintf( gl_File, "NI R40: 0000000\n");}
|
||||
#endif
|
||||
|
||||
#define CloseMessage(...)
|
||||
|
||||
|
||||
#else
|
||||
#define OpenMessage(...)
|
||||
#define Message(...)
|
||||
#define CloseMessage(...)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __CDT_PARSER__
|
||||
typedef unsigned long DWORD64;
|
||||
typedef unsigned long DWORD32;
|
||||
|
||||
#define STDMETHOD(method) virtual HRESULT method
|
||||
#define STDMETHOD_(ret, method) virtual ret method
|
||||
#define sprintf_s(...)
|
||||
#define fprintf(...)
|
||||
#define fclose(...)
|
||||
#define fseek(...)
|
||||
#define ftell(...) 0
|
||||
#define fflush(...)
|
||||
typedef LONG HRESULT;
|
||||
|
||||
#define UNREFERENCED_PARAMETER(...)
|
||||
#endif
|
||||
@@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
//#define MyTypeHash DWORD64
|
||||
#define MyTypeHash DWORD32
|
||||
|
||||
#define BIG_BUFSIZE 1<<24
|
||||
#define SMALL_BUFSIZE 1<<10
|
||||
|
||||
using MsgStruct = struct {
|
||||
unsigned int Control;
|
||||
unsigned int Value;
|
||||
MyTypeHash Hash;
|
||||
};
|
||||
|
||||
using PipeStruct = struct {
|
||||
HANDLE In;
|
||||
HANDLE Out;
|
||||
};
|
||||
|
||||
|
||||
#define uMod_APP_DX9 L"uMod_DX9.txt"
|
||||
#define uMod_APP_DIR L"uMod"
|
||||
#define uMod_VERSION L"uMod V 1.0"
|
||||
|
||||
#define PIPE_uMod2Game L"\\\\.\\pipe\\uMod2Game"
|
||||
#define PIPE_Game2uMod L"\\\\.\\pipe\\Game2uMod"
|
||||
|
||||
#define CONTROL_ADD_TEXTURE 1
|
||||
#define CONTROL_FORCE_RELOAD_TEXTURE 2
|
||||
#define CONTROL_REMOVE_TEXTURE 3
|
||||
#define CONTROL_FORCE_RELOAD_TEXTURE_DATA 4
|
||||
#define CONTROL_ADD_TEXTURE_DATA 5
|
||||
#define CONTROL_MORE_TEXTURES 6
|
||||
|
||||
|
||||
#define CONTROL_SAVE_ALL 10
|
||||
#define CONTROL_SAVE_SINGLE 11
|
||||
#define CONTROL_SET_DIR 12
|
||||
|
||||
#define CONTROL_KEY_BACK 20
|
||||
#define CONTROL_KEY_SAVE 21
|
||||
#define CONTROL_KEY_NEXT 22
|
||||
|
||||
#define CONTROL_FONT_COLOUR 30
|
||||
#define CONTROL_TEXTURE_COLOUR 31
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d9.h>
|
||||
#include "uMod_TextureServer.h"
|
||||
|
||||
class uMod_IDirect3D9 : public IDirect3D9 {
|
||||
public:
|
||||
uMod_IDirect3D9(IDirect3D9* pOriginal, uMod_TextureServer* server);
|
||||
virtual ~uMod_IDirect3D9();
|
||||
|
||||
// The original DX9 function definitions
|
||||
HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj) override;
|
||||
ULONG __stdcall AddRef() override;
|
||||
ULONG __stdcall Release() override;
|
||||
HRESULT __stdcall RegisterSoftwareDevice(void* pInitializeFunction) override;
|
||||
UINT __stdcall GetAdapterCount() override;
|
||||
HRESULT __stdcall GetAdapterIdentifier(UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier) override;
|
||||
UINT __stdcall GetAdapterModeCount(UINT Adapter, D3DFORMAT Format) override;
|
||||
HRESULT __stdcall EnumAdapterModes(UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE* pMode) override;
|
||||
HRESULT __stdcall GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) override;
|
||||
HRESULT __stdcall CheckDeviceType(UINT iAdapter, D3DDEVTYPE DevType, D3DFORMAT DisplayFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed) override;
|
||||
HRESULT __stdcall CheckDeviceFormat(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat) override;
|
||||
HRESULT __stdcall CheckDeviceMultiSampleType(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels) override;
|
||||
HRESULT __stdcall CheckDepthStencilMatch(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat) override;
|
||||
HRESULT __stdcall CheckDeviceFormatConversion(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat) override;
|
||||
HRESULT __stdcall GetDeviceCaps(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) override;
|
||||
HMONITOR __stdcall GetAdapterMonitor(UINT Adapter) override;
|
||||
HRESULT __stdcall CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface) override;
|
||||
|
||||
private:
|
||||
IDirect3D9* m_pIDirect3D9;
|
||||
uMod_TextureServer* uMod_Server;
|
||||
};
|
||||
@@ -1,41 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d9.h>
|
||||
#include "uMod_TextureServer.h"
|
||||
|
||||
class uMod_IDirect3D9Ex : public IDirect3D9Ex {
|
||||
public:
|
||||
uMod_IDirect3D9Ex(IDirect3D9Ex* pOriginal, uMod_TextureServer* server);
|
||||
virtual ~uMod_IDirect3D9Ex();
|
||||
|
||||
// The original DX9 function definitions
|
||||
HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj) override;
|
||||
ULONG __stdcall AddRef() override;
|
||||
ULONG __stdcall Release() override;
|
||||
HRESULT __stdcall RegisterSoftwareDevice(void* pInitializeFunction) override;
|
||||
UINT __stdcall GetAdapterCount() override;
|
||||
HRESULT __stdcall GetAdapterIdentifier(UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier) override;
|
||||
UINT __stdcall GetAdapterModeCount(UINT Adapter, D3DFORMAT Format) override;
|
||||
HRESULT __stdcall EnumAdapterModes(UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE* pMode) override;
|
||||
HRESULT __stdcall GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) override;
|
||||
HRESULT __stdcall CheckDeviceType(UINT iAdapter, D3DDEVTYPE DevType, D3DFORMAT DisplayFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed) override;
|
||||
HRESULT __stdcall CheckDeviceFormat(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat) override;
|
||||
HRESULT __stdcall CheckDeviceMultiSampleType(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels) override;
|
||||
HRESULT __stdcall CheckDepthStencilMatch(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat) override;
|
||||
HRESULT __stdcall CheckDeviceFormatConversion(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat) override;
|
||||
HRESULT __stdcall GetDeviceCaps(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) override;
|
||||
HMONITOR __stdcall GetAdapterMonitor(UINT Adapter) override;
|
||||
HRESULT __stdcall CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface) override;
|
||||
|
||||
HRESULT __stdcall CreateDeviceEx(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode,
|
||||
IDirect3DDevice9Ex** ppReturnedDeviceInterface) override;
|
||||
HRESULT __stdcall EnumAdapterModesEx(UINT Adapter, const D3DDISPLAYMODEFILTER* pFilter, UINT Mode, D3DDISPLAYMODEEX* pMode) override;
|
||||
HRESULT __stdcall GetAdapterDisplayModeEx(UINT Adapter, D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation) override;
|
||||
HRESULT __stdcall GetAdapterLUID(UINT Adapter, LUID* pLUID) override;
|
||||
UINT __stdcall GetAdapterModeCountEx(UINT Adapter, const D3DDISPLAYMODEFILTER* pFilter) override;
|
||||
|
||||
private:
|
||||
IDirect3D9Ex* m_pIDirect3D9Ex;
|
||||
uMod_TextureServer* uMod_Server;
|
||||
};
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d9.h>
|
||||
|
||||
interface uMod_IDirect3DCubeTexture9 : IDirect3DCubeTexture9 {
|
||||
uMod_IDirect3DCubeTexture9(IDirect3DCubeTexture9** ppTex, IDirect3DDevice9* pIDirect3DDevice9)
|
||||
{
|
||||
m_D3Dtex = *ppTex; //Texture which will be displayed and will be passed to the game
|
||||
m_D3Ddev = pIDirect3DDevice9; //device pointer
|
||||
CrossRef_D3Dtex = nullptr; //cross reference
|
||||
// fake texture: store the pointer to the original uMod_IDirect3DCubeTexture9 object, needed if a fake texture is unselected
|
||||
// original texture: stores the pointer to the fake texture object, is needed if original texture is deleted,
|
||||
// thus the fake texture can also be deleted
|
||||
Reference = -1; //need for fast deleting
|
||||
Hash = 0u;
|
||||
FAKE = false;
|
||||
}
|
||||
|
||||
// callback interface
|
||||
IDirect3DCubeTexture9* m_D3Dtex;
|
||||
uMod_IDirect3DCubeTexture9* CrossRef_D3Dtex;
|
||||
IDirect3DDevice9* m_D3Ddev;
|
||||
int Reference;
|
||||
MyTypeHash Hash;
|
||||
bool FAKE;
|
||||
|
||||
// original interface
|
||||
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(AddDirtyRect)(D3DCUBEMAP_FACES FaceType, CONST RECT* pDirtyRect) 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;
|
||||
|
||||
|
||||
int GetHash(MyTypeHash& hash);
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline void UnswitchTextures(uMod_IDirect3DCubeTexture9* pTexture)
|
||||
{
|
||||
uMod_IDirect3DCubeTexture9* CrossRef = pTexture->CrossRef_D3Dtex;
|
||||
if (CrossRef != nullptr) {
|
||||
// switch textures back
|
||||
IDirect3DCubeTexture9* cpy = pTexture->m_D3Dtex;
|
||||
pTexture->m_D3Dtex = CrossRef->m_D3Dtex;
|
||||
CrossRef->m_D3Dtex = cpy;
|
||||
|
||||
// cancel the link
|
||||
CrossRef->CrossRef_D3Dtex = nullptr;
|
||||
pTexture->CrossRef_D3Dtex = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline int SwitchTextures(uMod_IDirect3DCubeTexture9* pTexture1, uMod_IDirect3DCubeTexture9* pTexture2)
|
||||
{
|
||||
if (pTexture1->m_D3Ddev == pTexture2->m_D3Ddev && pTexture1->CrossRef_D3Dtex == nullptr && pTexture2->CrossRef_D3Dtex == nullptr) {
|
||||
// make cross reference
|
||||
pTexture1->CrossRef_D3Dtex = pTexture2;
|
||||
pTexture2->CrossRef_D3Dtex = pTexture1;
|
||||
|
||||
// switch textures
|
||||
IDirect3DCubeTexture9* cpy = pTexture2->m_D3Dtex;
|
||||
pTexture2->m_D3Dtex = pTexture1->m_D3Dtex;
|
||||
pTexture1->m_D3Dtex = cpy;
|
||||
return RETURN_OK;
|
||||
}
|
||||
return RETURN_TEXTURE_NOT_SWITCHED;
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d9.h>
|
||||
#include <d3dx9.h>
|
||||
#include "uMod_IDirect3DTexture9.h"
|
||||
#include "uMod_IDirect3DVolumeTexture9.h"
|
||||
#include "uMod_IDirect3DCubeTexture9.h"
|
||||
|
||||
|
||||
class uMod_IDirect3DDevice9 : public IDirect3DDevice9 {
|
||||
public:
|
||||
uMod_IDirect3DDevice9(IDirect3DDevice9* pOriginal, uMod_TextureServer* server, int back_buffer_count);
|
||||
virtual ~uMod_IDirect3DDevice9();
|
||||
|
||||
// START: The original DX9 function definitions
|
||||
HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj) override;
|
||||
ULONG __stdcall AddRef() override;
|
||||
ULONG __stdcall Release() override;
|
||||
HRESULT __stdcall TestCooperativeLevel() override;
|
||||
UINT __stdcall GetAvailableTextureMem() override;
|
||||
HRESULT __stdcall EvictManagedResources() override;
|
||||
HRESULT __stdcall GetDirect3D(IDirect3D9** ppD3D9) override;
|
||||
HRESULT __stdcall GetDeviceCaps(D3DCAPS9* pCaps) override;
|
||||
HRESULT __stdcall GetDisplayMode(UINT iSwapChain, D3DDISPLAYMODE* pMode) override;
|
||||
HRESULT __stdcall GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters) override;
|
||||
HRESULT __stdcall SetCursorProperties(UINT XHotSpot, UINT YHotSpot, IDirect3DSurface9* pCursorBitmap) override;
|
||||
void __stdcall SetCursorPosition(int X, int Y, DWORD Flags) override;
|
||||
BOOL __stdcall ShowCursor(BOOL bShow) override;
|
||||
HRESULT __stdcall CreateAdditionalSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DSwapChain9** pSwapChain) override;
|
||||
HRESULT __stdcall GetSwapChain(UINT iSwapChain, IDirect3DSwapChain9** pSwapChain) override;
|
||||
UINT __stdcall GetNumberOfSwapChains() override;
|
||||
HRESULT __stdcall Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) override;
|
||||
HRESULT __stdcall Present(CONST RECT* pSourceRect,CONST RECT* pDestRect, HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) override;
|
||||
HRESULT __stdcall GetBackBuffer(UINT iSwapChain, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface9** ppBackBuffer) override;
|
||||
HRESULT __stdcall GetRasterStatus(UINT iSwapChain, D3DRASTER_STATUS* pRasterStatus) override;
|
||||
HRESULT __stdcall SetDialogBoxMode(BOOL bEnableDialogs) override;
|
||||
void __stdcall SetGammaRamp(UINT iSwapChain, DWORD Flags,CONST D3DGAMMARAMP* pRamp) override;
|
||||
void __stdcall GetGammaRamp(UINT iSwapChain, D3DGAMMARAMP* pRamp) override;
|
||||
HRESULT __stdcall CreateTexture(UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall CreateVolumeTexture(UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DVolumeTexture9** ppVolumeTexture, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall CreateCubeTexture(UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DCubeTexture9** ppCubeTexture, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall CreateVertexBuffer(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall CreateIndexBuffer(UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall CreateRenderTarget(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall CreateDepthStencilSurface(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall UpdateSurface(IDirect3DSurface9* pSourceSurface,CONST RECT* pSourceRect, IDirect3DSurface9* pDestinationSurface,CONST POINT* pDestPoint) override;
|
||||
HRESULT __stdcall UpdateTexture(IDirect3DBaseTexture9* pSourceTexture, IDirect3DBaseTexture9* pDestinationTexture) override;
|
||||
HRESULT __stdcall GetRenderTargetData(IDirect3DSurface9* pRenderTarget, IDirect3DSurface9* pDestSurface) override;
|
||||
HRESULT __stdcall GetFrontBufferData(UINT iSwapChain, IDirect3DSurface9* pDestSurface) override;
|
||||
HRESULT __stdcall StretchRect(IDirect3DSurface9* pSourceSurface,CONST RECT* pSourceRect, IDirect3DSurface9* pDestSurface,CONST RECT* pDestRect, D3DTEXTUREFILTERTYPE Filter) override;
|
||||
HRESULT __stdcall ColorFill(IDirect3DSurface9* pSurface,CONST RECT* pRect, D3DCOLOR color) override;
|
||||
HRESULT __stdcall CreateOffscreenPlainSurface(UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall SetRenderTarget(DWORD RenderTargetIndex, IDirect3DSurface9* pRenderTarget) override;
|
||||
HRESULT __stdcall GetRenderTarget(DWORD RenderTargetIndex, IDirect3DSurface9** ppRenderTarget) override;
|
||||
HRESULT __stdcall SetDepthStencilSurface(IDirect3DSurface9* pNewZStencil) override;
|
||||
HRESULT __stdcall GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface) override;
|
||||
HRESULT __stdcall BeginScene() override;
|
||||
HRESULT __stdcall EndScene() override;
|
||||
HRESULT __stdcall Clear(DWORD Count,CONST D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil) override;
|
||||
HRESULT __stdcall SetTransform(D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX* pMatrix) override;
|
||||
HRESULT __stdcall GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) override;
|
||||
HRESULT __stdcall MultiplyTransform(D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX* pMatrix) override;
|
||||
HRESULT __stdcall SetViewport(CONST D3DVIEWPORT9* pViewport) override;
|
||||
HRESULT __stdcall GetViewport(D3DVIEWPORT9* pViewport) override;
|
||||
HRESULT __stdcall SetMaterial(CONST D3DMATERIAL9* pMaterial) override;
|
||||
HRESULT __stdcall GetMaterial(D3DMATERIAL9* pMaterial) override;
|
||||
HRESULT __stdcall SetLight(DWORD Index,CONST D3DLIGHT9* pLight) override;
|
||||
HRESULT __stdcall GetLight(DWORD Index, D3DLIGHT9* pLight) override;
|
||||
HRESULT __stdcall LightEnable(DWORD Index, BOOL Enable) override;
|
||||
HRESULT __stdcall GetLightEnable(DWORD Index, BOOL* pEnable) override;
|
||||
HRESULT __stdcall SetClipPlane(DWORD Index,CONST float* pPlane) override;
|
||||
HRESULT __stdcall GetClipPlane(DWORD Index, float* pPlane) override;
|
||||
HRESULT __stdcall SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) override;
|
||||
HRESULT __stdcall GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue) override;
|
||||
HRESULT __stdcall CreateStateBlock(D3DSTATEBLOCKTYPE Type, IDirect3DStateBlock9** ppSB) override;
|
||||
HRESULT __stdcall BeginStateBlock() override;
|
||||
HRESULT __stdcall EndStateBlock(IDirect3DStateBlock9** ppSB) override;
|
||||
HRESULT __stdcall SetClipStatus(CONST D3DCLIPSTATUS9* pClipStatus) override;
|
||||
HRESULT __stdcall GetClipStatus(D3DCLIPSTATUS9* pClipStatus) override;
|
||||
HRESULT __stdcall GetTexture(DWORD Stage, IDirect3DBaseTexture9** ppTexture) override;
|
||||
HRESULT __stdcall SetTexture(DWORD Stage, IDirect3DBaseTexture9* pTexture) override;
|
||||
HRESULT __stdcall GetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue) override;
|
||||
HRESULT __stdcall SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value) override;
|
||||
HRESULT __stdcall GetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD* pValue) override;
|
||||
HRESULT __stdcall SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value) override;
|
||||
HRESULT __stdcall ValidateDevice(DWORD* pNumPasses) override;
|
||||
HRESULT __stdcall SetPaletteEntries(UINT PaletteNumber,CONST PALETTEENTRY* pEntries) override;
|
||||
HRESULT __stdcall GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries) override;
|
||||
HRESULT __stdcall SetCurrentTexturePalette(UINT PaletteNumber) override;
|
||||
HRESULT __stdcall GetCurrentTexturePalette(UINT* PaletteNumber) override;
|
||||
HRESULT __stdcall SetScissorRect(CONST RECT* pRect) override;
|
||||
HRESULT __stdcall GetScissorRect(RECT* pRect) override;
|
||||
HRESULT __stdcall SetSoftwareVertexProcessing(BOOL bSoftware) override;
|
||||
BOOL __stdcall GetSoftwareVertexProcessing() override;
|
||||
HRESULT __stdcall SetNPatchMode(float nSegments) override;
|
||||
float __stdcall GetNPatchMode() override;
|
||||
HRESULT __stdcall DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount) override;
|
||||
HRESULT __stdcall DrawIndexedPrimitive(D3DPRIMITIVETYPE PrimitiveType, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount) override;
|
||||
HRESULT __stdcall DrawPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount,CONST void* pVertexStreamZeroData, UINT VertexStreamZeroStride) override;
|
||||
HRESULT __stdcall DrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount,CONST void* pIndexData, D3DFORMAT IndexDataFormat,CONST void* pVertexStreamZeroData,
|
||||
UINT VertexStreamZeroStride) override;
|
||||
HRESULT __stdcall ProcessVertices(UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, IDirect3DVertexBuffer9* pDestBuffer, IDirect3DVertexDeclaration9* pVertexDecl, DWORD Flags) override;
|
||||
HRESULT __stdcall CreateVertexDeclaration(CONST D3DVERTEXELEMENT9* pVertexElements, IDirect3DVertexDeclaration9** ppDecl) override;
|
||||
HRESULT __stdcall SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl) override;
|
||||
HRESULT __stdcall GetVertexDeclaration(IDirect3DVertexDeclaration9** ppDecl) override;
|
||||
HRESULT __stdcall SetFVF(DWORD FVF) override;
|
||||
HRESULT __stdcall GetFVF(DWORD* pFVF) override;
|
||||
HRESULT __stdcall CreateVertexShader(CONST DWORD* pFunction, IDirect3DVertexShader9** ppShader) override;
|
||||
HRESULT __stdcall SetVertexShader(IDirect3DVertexShader9* pShader) override;
|
||||
HRESULT __stdcall GetVertexShader(IDirect3DVertexShader9** ppShader) override;
|
||||
HRESULT __stdcall SetVertexShaderConstantF(UINT StartRegister,CONST float* pConstantData, UINT Vector4fCount) override;
|
||||
HRESULT __stdcall GetVertexShaderConstantF(UINT StartRegister, float* pConstantData, UINT Vector4fCount) override;
|
||||
HRESULT __stdcall SetVertexShaderConstantI(UINT StartRegister,CONST int* pConstantData, UINT Vector4iCount) override;
|
||||
HRESULT __stdcall GetVertexShaderConstantI(UINT StartRegister, int* pConstantData, UINT Vector4iCount) override;
|
||||
HRESULT __stdcall SetVertexShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData, UINT BoolCount) override;
|
||||
HRESULT __stdcall GetVertexShaderConstantB(UINT StartRegister, BOOL* pConstantData, UINT BoolCount) override;
|
||||
HRESULT __stdcall SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9* pStreamData, UINT OffsetInBytes, UINT Stride) override;
|
||||
HRESULT __stdcall GetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9** ppStreamData, UINT* OffsetInBytes, UINT* pStride) override;
|
||||
HRESULT __stdcall SetStreamSourceFreq(UINT StreamNumber, UINT Divider) override;
|
||||
HRESULT __stdcall GetStreamSourceFreq(UINT StreamNumber, UINT* Divider) override;
|
||||
HRESULT __stdcall SetIndices(IDirect3DIndexBuffer9* pIndexData) override;
|
||||
HRESULT __stdcall GetIndices(IDirect3DIndexBuffer9** ppIndexData) override;
|
||||
HRESULT __stdcall CreatePixelShader(CONST DWORD* pFunction, IDirect3DPixelShader9** ppShader) override;
|
||||
HRESULT __stdcall SetPixelShader(IDirect3DPixelShader9* pShader) override;
|
||||
HRESULT __stdcall GetPixelShader(IDirect3DPixelShader9** ppShader) override;
|
||||
HRESULT __stdcall SetPixelShaderConstantF(UINT StartRegister,CONST float* pConstantData, UINT Vector4fCount) override;
|
||||
HRESULT __stdcall GetPixelShaderConstantF(UINT StartRegister, float* pConstantData, UINT Vector4fCount) override;
|
||||
HRESULT __stdcall SetPixelShaderConstantI(UINT StartRegister,CONST int* pConstantData, UINT Vector4iCount) override;
|
||||
HRESULT __stdcall GetPixelShaderConstantI(UINT StartRegister, int* pConstantData, UINT Vector4iCount) override;
|
||||
HRESULT __stdcall SetPixelShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData, UINT BoolCount) override;
|
||||
HRESULT __stdcall GetPixelShaderConstantB(UINT StartRegister, BOOL* pConstantData, UINT BoolCount) override;
|
||||
HRESULT __stdcall DrawRectPatch(UINT Handle,CONST float* pNumSegs,CONST D3DRECTPATCH_INFO* pRectPatchInfo) override;
|
||||
HRESULT __stdcall DrawTriPatch(UINT Handle,CONST float* pNumSegs,CONST D3DTRIPATCH_INFO* pTriPatchInfo) override;
|
||||
HRESULT __stdcall DeletePatch(UINT Handle) override;
|
||||
HRESULT __stdcall CreateQuery(D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery) override;
|
||||
// END: The original DX9 function definitions
|
||||
|
||||
|
||||
|
||||
uMod_TextureClient* GetuMod_Client() { return uMod_Client; }
|
||||
|
||||
uMod_IDirect3DTexture9* GetLastCreatedTexture() { return LastCreatedTexture; }
|
||||
|
||||
int SetLastCreatedTexture(uMod_IDirect3DTexture9* pTexture)
|
||||
{
|
||||
LastCreatedTexture = pTexture;
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
uMod_IDirect3DVolumeTexture9* GetLastCreatedVolumeTexture() { return LastCreatedVolumeTexture; }
|
||||
|
||||
int SetLastCreatedVolumeTexture(uMod_IDirect3DVolumeTexture9* pTexture)
|
||||
{
|
||||
LastCreatedVolumeTexture = pTexture;
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
uMod_IDirect3DCubeTexture9* GetLastCreatedCubeTexture() { return LastCreatedCubeTexture; }
|
||||
|
||||
int SetLastCreatedCubeTexture(uMod_IDirect3DCubeTexture9* pTexture)
|
||||
{
|
||||
LastCreatedCubeTexture = pTexture;
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
|
||||
uMod_IDirect3DTexture9* GetSingleTexture() { return SingleTexture; }
|
||||
uMod_IDirect3DVolumeTexture9* GetSingleVolumeTexture() { return SingleVolumeTexture; }
|
||||
uMod_IDirect3DCubeTexture9* GetSingleCubeTexture() { return SingleCubeTexture; }
|
||||
|
||||
private:
|
||||
int CreateSingleTexture();
|
||||
IDirect3DDevice9* m_pIDirect3DDevice9;
|
||||
|
||||
int CounterSaveSingleTexture;
|
||||
uMod_IDirect3DTexture9* SingleTexture;
|
||||
uMod_IDirect3DVolumeTexture9* SingleVolumeTexture;
|
||||
uMod_IDirect3DCubeTexture9* SingleCubeTexture;
|
||||
char SingleTextureMod;
|
||||
|
||||
D3DCOLOR TextureColour;
|
||||
ID3DXFont* OSD_Font;
|
||||
//D3DCOLOR FontColour;
|
||||
int BackBufferCount;
|
||||
bool NormalRendering;
|
||||
|
||||
int uMod_Reference;
|
||||
|
||||
uMod_IDirect3DTexture9* LastCreatedTexture;
|
||||
uMod_IDirect3DVolumeTexture9* LastCreatedVolumeTexture;
|
||||
uMod_IDirect3DCubeTexture9* LastCreatedCubeTexture;
|
||||
|
||||
uMod_TextureServer* uMod_Server;
|
||||
uMod_TextureClient* uMod_Client;
|
||||
};
|
||||
@@ -1,216 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d9.h>
|
||||
#include <d3dx9.h>
|
||||
#include "uMod_IDirect3DTexture9.h"
|
||||
#include "uMod_IDirect3DVolumeTexture9.h"
|
||||
#include "uMod_IDirect3DCubeTexture9.h"
|
||||
|
||||
|
||||
class uMod_IDirect3DDevice9Ex : public IDirect3DDevice9Ex {
|
||||
public:
|
||||
uMod_IDirect3DDevice9Ex(IDirect3DDevice9Ex* pOriginal, uMod_TextureServer* server, int back_buffer_count);
|
||||
virtual ~uMod_IDirect3DDevice9Ex();
|
||||
|
||||
// START: The original DX9 function definitions
|
||||
HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj) override;
|
||||
ULONG __stdcall AddRef() override;
|
||||
ULONG __stdcall Release() override;
|
||||
HRESULT __stdcall TestCooperativeLevel() override;
|
||||
UINT __stdcall GetAvailableTextureMem() override;
|
||||
HRESULT __stdcall EvictManagedResources() override;
|
||||
HRESULT __stdcall GetDirect3D(IDirect3D9** ppD3D9) override;
|
||||
HRESULT __stdcall GetDeviceCaps(D3DCAPS9* pCaps) override;
|
||||
HRESULT __stdcall GetDisplayMode(UINT iSwapChain, D3DDISPLAYMODE* pMode) override;
|
||||
HRESULT __stdcall GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters) override;
|
||||
HRESULT __stdcall SetCursorProperties(UINT XHotSpot, UINT YHotSpot, IDirect3DSurface9* pCursorBitmap) override;
|
||||
void __stdcall SetCursorPosition(int X, int Y, DWORD Flags) override;
|
||||
BOOL __stdcall ShowCursor(BOOL bShow) override;
|
||||
HRESULT __stdcall CreateAdditionalSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DSwapChain9** pSwapChain) override;
|
||||
HRESULT __stdcall GetSwapChain(UINT iSwapChain, IDirect3DSwapChain9** pSwapChain) override;
|
||||
UINT __stdcall GetNumberOfSwapChains() override;
|
||||
HRESULT __stdcall Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) override;
|
||||
HRESULT __stdcall Present(CONST RECT* pSourceRect,CONST RECT* pDestRect, HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) override;
|
||||
HRESULT __stdcall GetBackBuffer(UINT iSwapChain, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface9** ppBackBuffer) override;
|
||||
HRESULT __stdcall GetRasterStatus(UINT iSwapChain, D3DRASTER_STATUS* pRasterStatus) override;
|
||||
HRESULT __stdcall SetDialogBoxMode(BOOL bEnableDialogs) override;
|
||||
void __stdcall SetGammaRamp(UINT iSwapChain, DWORD Flags,CONST D3DGAMMARAMP* pRamp) override;
|
||||
void __stdcall GetGammaRamp(UINT iSwapChain, D3DGAMMARAMP* pRamp) override;
|
||||
HRESULT __stdcall CreateTexture(UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall CreateVolumeTexture(UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DVolumeTexture9** ppVolumeTexture, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall CreateCubeTexture(UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DCubeTexture9** ppCubeTexture, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall CreateVertexBuffer(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall CreateIndexBuffer(UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall CreateRenderTarget(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall CreateDepthStencilSurface(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall UpdateSurface(IDirect3DSurface9* pSourceSurface,CONST RECT* pSourceRect, IDirect3DSurface9* pDestinationSurface,CONST POINT* pDestPoint) override;
|
||||
HRESULT __stdcall UpdateTexture(IDirect3DBaseTexture9* pSourceTexture, IDirect3DBaseTexture9* pDestinationTexture) override;
|
||||
HRESULT __stdcall GetRenderTargetData(IDirect3DSurface9* pRenderTarget, IDirect3DSurface9* pDestSurface) override;
|
||||
HRESULT __stdcall GetFrontBufferData(UINT iSwapChain, IDirect3DSurface9* pDestSurface) override;
|
||||
HRESULT __stdcall StretchRect(IDirect3DSurface9* pSourceSurface,CONST RECT* pSourceRect, IDirect3DSurface9* pDestSurface,CONST RECT* pDestRect, D3DTEXTUREFILTERTYPE Filter) override;
|
||||
HRESULT __stdcall ColorFill(IDirect3DSurface9* pSurface,CONST RECT* pRect, D3DCOLOR color) override;
|
||||
HRESULT __stdcall CreateOffscreenPlainSurface(UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) override;
|
||||
HRESULT __stdcall SetRenderTarget(DWORD RenderTargetIndex, IDirect3DSurface9* pRenderTarget) override;
|
||||
HRESULT __stdcall GetRenderTarget(DWORD RenderTargetIndex, IDirect3DSurface9** ppRenderTarget) override;
|
||||
HRESULT __stdcall SetDepthStencilSurface(IDirect3DSurface9* pNewZStencil) override;
|
||||
HRESULT __stdcall GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface) override;
|
||||
HRESULT __stdcall BeginScene() override;
|
||||
HRESULT __stdcall EndScene() override;
|
||||
HRESULT __stdcall Clear(DWORD Count,CONST D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil) override;
|
||||
HRESULT __stdcall SetTransform(D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX* pMatrix) override;
|
||||
HRESULT __stdcall GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) override;
|
||||
HRESULT __stdcall MultiplyTransform(D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX* pMatrix) override;
|
||||
HRESULT __stdcall SetViewport(CONST D3DVIEWPORT9* pViewport) override;
|
||||
HRESULT __stdcall GetViewport(D3DVIEWPORT9* pViewport) override;
|
||||
HRESULT __stdcall SetMaterial(CONST D3DMATERIAL9* pMaterial) override;
|
||||
HRESULT __stdcall GetMaterial(D3DMATERIAL9* pMaterial) override;
|
||||
HRESULT __stdcall SetLight(DWORD Index,CONST D3DLIGHT9* pLight) override;
|
||||
HRESULT __stdcall GetLight(DWORD Index, D3DLIGHT9* pLight) override;
|
||||
HRESULT __stdcall LightEnable(DWORD Index, BOOL Enable) override;
|
||||
HRESULT __stdcall GetLightEnable(DWORD Index, BOOL* pEnable) override;
|
||||
HRESULT __stdcall SetClipPlane(DWORD Index,CONST float* pPlane) override;
|
||||
HRESULT __stdcall GetClipPlane(DWORD Index, float* pPlane) override;
|
||||
HRESULT __stdcall SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) override;
|
||||
HRESULT __stdcall GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue) override;
|
||||
HRESULT __stdcall CreateStateBlock(D3DSTATEBLOCKTYPE Type, IDirect3DStateBlock9** ppSB) override;
|
||||
HRESULT __stdcall BeginStateBlock() override;
|
||||
HRESULT __stdcall EndStateBlock(IDirect3DStateBlock9** ppSB) override;
|
||||
HRESULT __stdcall SetClipStatus(CONST D3DCLIPSTATUS9* pClipStatus) override;
|
||||
HRESULT __stdcall GetClipStatus(D3DCLIPSTATUS9* pClipStatus) override;
|
||||
HRESULT __stdcall GetTexture(DWORD Stage, IDirect3DBaseTexture9** ppTexture) override;
|
||||
HRESULT __stdcall SetTexture(DWORD Stage, IDirect3DBaseTexture9* pTexture) override;
|
||||
HRESULT __stdcall GetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue) override;
|
||||
HRESULT __stdcall SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value) override;
|
||||
HRESULT __stdcall GetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD* pValue) override;
|
||||
HRESULT __stdcall SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value) override;
|
||||
HRESULT __stdcall ValidateDevice(DWORD* pNumPasses) override;
|
||||
HRESULT __stdcall SetPaletteEntries(UINT PaletteNumber,CONST PALETTEENTRY* pEntries) override;
|
||||
HRESULT __stdcall GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries) override;
|
||||
HRESULT __stdcall SetCurrentTexturePalette(UINT PaletteNumber) override;
|
||||
HRESULT __stdcall GetCurrentTexturePalette(UINT* PaletteNumber) override;
|
||||
HRESULT __stdcall SetScissorRect(CONST RECT* pRect) override;
|
||||
HRESULT __stdcall GetScissorRect(RECT* pRect) override;
|
||||
HRESULT __stdcall SetSoftwareVertexProcessing(BOOL bSoftware) override;
|
||||
BOOL __stdcall GetSoftwareVertexProcessing() override;
|
||||
HRESULT __stdcall SetNPatchMode(float nSegments) override;
|
||||
float __stdcall GetNPatchMode() override;
|
||||
HRESULT __stdcall DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount) override;
|
||||
HRESULT __stdcall DrawIndexedPrimitive(D3DPRIMITIVETYPE PrimitiveType, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount) override;
|
||||
HRESULT __stdcall DrawPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount,CONST void* pVertexStreamZeroData, UINT VertexStreamZeroStride) override;
|
||||
HRESULT __stdcall DrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount,CONST void* pIndexData, D3DFORMAT IndexDataFormat,CONST void* pVertexStreamZeroData,
|
||||
UINT VertexStreamZeroStride) override;
|
||||
HRESULT __stdcall ProcessVertices(UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, IDirect3DVertexBuffer9* pDestBuffer, IDirect3DVertexDeclaration9* pVertexDecl, DWORD Flags) override;
|
||||
HRESULT __stdcall CreateVertexDeclaration(CONST D3DVERTEXELEMENT9* pVertexElements, IDirect3DVertexDeclaration9** ppDecl) override;
|
||||
HRESULT __stdcall SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl) override;
|
||||
HRESULT __stdcall GetVertexDeclaration(IDirect3DVertexDeclaration9** ppDecl) override;
|
||||
HRESULT __stdcall SetFVF(DWORD FVF) override;
|
||||
HRESULT __stdcall GetFVF(DWORD* pFVF) override;
|
||||
HRESULT __stdcall CreateVertexShader(CONST DWORD* pFunction, IDirect3DVertexShader9** ppShader) override;
|
||||
HRESULT __stdcall SetVertexShader(IDirect3DVertexShader9* pShader) override;
|
||||
HRESULT __stdcall GetVertexShader(IDirect3DVertexShader9** ppShader) override;
|
||||
HRESULT __stdcall SetVertexShaderConstantF(UINT StartRegister,CONST float* pConstantData, UINT Vector4fCount) override;
|
||||
HRESULT __stdcall GetVertexShaderConstantF(UINT StartRegister, float* pConstantData, UINT Vector4fCount) override;
|
||||
HRESULT __stdcall SetVertexShaderConstantI(UINT StartRegister,CONST int* pConstantData, UINT Vector4iCount) override;
|
||||
HRESULT __stdcall GetVertexShaderConstantI(UINT StartRegister, int* pConstantData, UINT Vector4iCount) override;
|
||||
HRESULT __stdcall SetVertexShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData, UINT BoolCount) override;
|
||||
HRESULT __stdcall GetVertexShaderConstantB(UINT StartRegister, BOOL* pConstantData, UINT BoolCount) override;
|
||||
HRESULT __stdcall SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9* pStreamData, UINT OffsetInBytes, UINT Stride) override;
|
||||
HRESULT __stdcall GetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9** ppStreamData, UINT* OffsetInBytes, UINT* pStride) override;
|
||||
HRESULT __stdcall SetStreamSourceFreq(UINT StreamNumber, UINT Divider) override;
|
||||
HRESULT __stdcall GetStreamSourceFreq(UINT StreamNumber, UINT* Divider) override;
|
||||
HRESULT __stdcall SetIndices(IDirect3DIndexBuffer9* pIndexData) override;
|
||||
HRESULT __stdcall GetIndices(IDirect3DIndexBuffer9** ppIndexData) override;
|
||||
HRESULT __stdcall CreatePixelShader(CONST DWORD* pFunction, IDirect3DPixelShader9** ppShader) override;
|
||||
HRESULT __stdcall SetPixelShader(IDirect3DPixelShader9* pShader) override;
|
||||
HRESULT __stdcall GetPixelShader(IDirect3DPixelShader9** ppShader) override;
|
||||
HRESULT __stdcall SetPixelShaderConstantF(UINT StartRegister,CONST float* pConstantData, UINT Vector4fCount) override;
|
||||
HRESULT __stdcall GetPixelShaderConstantF(UINT StartRegister, float* pConstantData, UINT Vector4fCount) override;
|
||||
HRESULT __stdcall SetPixelShaderConstantI(UINT StartRegister,CONST int* pConstantData, UINT Vector4iCount) override;
|
||||
HRESULT __stdcall GetPixelShaderConstantI(UINT StartRegister, int* pConstantData, UINT Vector4iCount) override;
|
||||
HRESULT __stdcall SetPixelShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData, UINT BoolCount) override;
|
||||
HRESULT __stdcall GetPixelShaderConstantB(UINT StartRegister, BOOL* pConstantData, UINT BoolCount) override;
|
||||
HRESULT __stdcall DrawRectPatch(UINT Handle,CONST float* pNumSegs,CONST D3DRECTPATCH_INFO* pRectPatchInfo) override;
|
||||
HRESULT __stdcall DrawTriPatch(UINT Handle,CONST float* pNumSegs,CONST D3DTRIPATCH_INFO* pTriPatchInfo) override;
|
||||
HRESULT __stdcall DeletePatch(UINT Handle) override;
|
||||
HRESULT __stdcall CreateQuery(D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery) override;
|
||||
|
||||
|
||||
|
||||
HRESULT __stdcall CheckDeviceState(HWND hWindow) override;
|
||||
HRESULT __stdcall CheckResourceResidency(IDirect3DResource9** pResourceArray, UINT32 NumResources) override;
|
||||
HRESULT __stdcall ComposeRects(IDirect3DSurface9* pSource, IDirect3DSurface9* pDestination, IDirect3DVertexBuffer9* pSrcRectDescriptors, UINT NumRects, IDirect3DVertexBuffer9* pDstRectDescriptors, D3DCOMPOSERECTSOP Operation, INT XOffset,
|
||||
INT YOffset) override;
|
||||
HRESULT __stdcall CreateDepthStencilSurfaceEx(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage) override;
|
||||
HRESULT __stdcall CreateOffscreenPlainSurfaceEx(UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage) override;
|
||||
HRESULT __stdcall CreateRenderTargetEx(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage) override;
|
||||
HRESULT __stdcall GetDisplayModeEx(UINT iSwapChain, D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation) override;
|
||||
HRESULT __stdcall GetGPUThreadPriority(INT* pPriority) override;
|
||||
HRESULT __stdcall GetMaximumFrameLatency(UINT* pMaxLatency) override;
|
||||
HRESULT __stdcall PresentEx(const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion, DWORD dwFlags) override;
|
||||
HRESULT __stdcall ResetEx(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode) override;
|
||||
HRESULT __stdcall SetConvolutionMonoKernel(UINT Width, UINT Height, float* RowWeights, float* ColumnWeights) override;
|
||||
HRESULT __stdcall SetGPUThreadPriority(INT pPriority) override;
|
||||
HRESULT __stdcall SetMaximumFrameLatency(UINT pMaxLatency) override;
|
||||
//HRESULT __stdcall TestCooperativeLevel();
|
||||
HRESULT __stdcall WaitForVBlank(UINT SwapChainIndex) override;
|
||||
|
||||
|
||||
// END: The original DX9 function definitions
|
||||
|
||||
|
||||
uMod_TextureClient* GetuMod_Client() { return uMod_Client; }
|
||||
|
||||
uMod_IDirect3DTexture9* GetLastCreatedTexture() { return LastCreatedTexture; }
|
||||
|
||||
int SetLastCreatedTexture(uMod_IDirect3DTexture9* pTexture)
|
||||
{
|
||||
LastCreatedTexture = pTexture;
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
uMod_IDirect3DVolumeTexture9* GetLastCreatedVolumeTexture() { return LastCreatedVolumeTexture; }
|
||||
|
||||
int SetLastCreatedVolumeTexture(uMod_IDirect3DVolumeTexture9* pTexture)
|
||||
{
|
||||
LastCreatedVolumeTexture = pTexture;
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
uMod_IDirect3DCubeTexture9* GetLastCreatedCubeTexture() { return LastCreatedCubeTexture; }
|
||||
|
||||
int SetLastCreatedCubeTexture(uMod_IDirect3DCubeTexture9* pTexture)
|
||||
{
|
||||
LastCreatedCubeTexture = pTexture;
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
|
||||
uMod_IDirect3DTexture9* GetSingleTexture() { return SingleTexture; }
|
||||
uMod_IDirect3DVolumeTexture9* GetSingleVolumeTexture() { return SingleVolumeTexture; }
|
||||
uMod_IDirect3DCubeTexture9* GetSingleCubeTexture() { return SingleCubeTexture; }
|
||||
|
||||
private:
|
||||
int CreateSingleTexture();
|
||||
IDirect3DDevice9Ex* m_pIDirect3DDevice9Ex;
|
||||
|
||||
int CounterSaveSingleTexture;
|
||||
uMod_IDirect3DTexture9* SingleTexture;
|
||||
uMod_IDirect3DVolumeTexture9* SingleVolumeTexture;
|
||||
uMod_IDirect3DCubeTexture9* SingleCubeTexture;
|
||||
char SingleTextureMod;
|
||||
|
||||
D3DCOLOR TextureColour;
|
||||
ID3DXFont* OSD_Font;
|
||||
//D3DCOLOR FontColour;
|
||||
int BackBufferCount;
|
||||
bool NormalRendering;
|
||||
|
||||
int uMod_Reference;
|
||||
|
||||
uMod_IDirect3DTexture9* LastCreatedTexture;
|
||||
uMod_IDirect3DVolumeTexture9* LastCreatedVolumeTexture;
|
||||
uMod_IDirect3DCubeTexture9* LastCreatedCubeTexture;
|
||||
|
||||
uMod_TextureServer* uMod_Server;
|
||||
uMod_TextureClient* uMod_Client;
|
||||
};
|
||||
@@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d9.h>
|
||||
|
||||
interface uMod_IDirect3DTexture9 : public IDirect3DTexture9 {
|
||||
uMod_IDirect3DTexture9(IDirect3DTexture9** ppTex, IDirect3DDevice9* pIDirect3DDevice9)
|
||||
{
|
||||
m_D3Dtex = *ppTex; //Texture which will be displayed and will be passed to the game
|
||||
m_D3Ddev = pIDirect3DDevice9; //device pointer
|
||||
CrossRef_D3Dtex = nullptr; //cross reference
|
||||
// fake texture: store the pointer to the original uMod_IDirect3DTexture9 object, needed if a fake texture is unselected
|
||||
// original texture: stores the pointer to the fake texture object, is needed if original texture is deleted,
|
||||
// thus the fake texture can also be deleted
|
||||
Reference = -1; //need for fast deleting
|
||||
Hash = 0u;
|
||||
FAKE = false;
|
||||
}
|
||||
|
||||
// callback interface
|
||||
IDirect3DTexture9* m_D3Dtex;
|
||||
uMod_IDirect3DTexture9* CrossRef_D3Dtex;
|
||||
IDirect3DDevice9* m_D3Ddev;
|
||||
int Reference;
|
||||
MyTypeHash Hash;
|
||||
bool FAKE;
|
||||
|
||||
// original interface
|
||||
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;
|
||||
|
||||
int GetHash(MyTypeHash& hash);
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline void UnswitchTextures(uMod_IDirect3DTexture9* pTexture)
|
||||
{
|
||||
uMod_IDirect3DTexture9* CrossRef = pTexture->CrossRef_D3Dtex;
|
||||
if (CrossRef != nullptr) {
|
||||
// switch textures back
|
||||
IDirect3DTexture9* cpy = pTexture->m_D3Dtex;
|
||||
pTexture->m_D3Dtex = CrossRef->m_D3Dtex;
|
||||
CrossRef->m_D3Dtex = cpy;
|
||||
|
||||
// cancel the link
|
||||
CrossRef->CrossRef_D3Dtex = nullptr;
|
||||
pTexture->CrossRef_D3Dtex = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline int SwitchTextures(uMod_IDirect3DTexture9* pTexture1, uMod_IDirect3DTexture9* pTexture2)
|
||||
{
|
||||
if (pTexture1->m_D3Ddev == pTexture2->m_D3Ddev && pTexture1->CrossRef_D3Dtex == nullptr && pTexture2->CrossRef_D3Dtex == nullptr) {
|
||||
// make cross reference
|
||||
pTexture1->CrossRef_D3Dtex = pTexture2;
|
||||
pTexture2->CrossRef_D3Dtex = pTexture1;
|
||||
|
||||
// switch textures
|
||||
IDirect3DTexture9* cpy = pTexture2->m_D3Dtex;
|
||||
pTexture2->m_D3Dtex = pTexture1->m_D3Dtex;
|
||||
pTexture1->m_D3Dtex = cpy;
|
||||
return RETURN_OK;
|
||||
}
|
||||
return RETURN_TEXTURE_NOT_SWITCHED;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d9.h>
|
||||
|
||||
interface uMod_IDirect3DVolumeTexture9 : IDirect3DVolumeTexture9 {
|
||||
uMod_IDirect3DVolumeTexture9(IDirect3DVolumeTexture9** ppTex, IDirect3DDevice9* pIDirect3DDevice9)
|
||||
{
|
||||
m_D3Dtex = *ppTex; //Texture which will be displayed and will be passed to the game
|
||||
m_D3Ddev = pIDirect3DDevice9; //device pointer
|
||||
CrossRef_D3Dtex = nullptr; //cross reference
|
||||
// fake texture: store the pointer to the original uMod_IDirect3DVolumeTexture9 object, needed if a fake texture is unselected
|
||||
// original texture: stores the pointer to the fake texture object, is needed if original texture is deleted,
|
||||
// thus the fake texture can also be deleted
|
||||
Reference = -1; //need for fast deleting
|
||||
Hash = 0u;
|
||||
FAKE = false;
|
||||
}
|
||||
|
||||
// callback interface
|
||||
IDirect3DVolumeTexture9* m_D3Dtex;
|
||||
uMod_IDirect3DVolumeTexture9* CrossRef_D3Dtex;
|
||||
IDirect3DDevice9* m_D3Ddev;
|
||||
int Reference;
|
||||
MyTypeHash Hash;
|
||||
bool FAKE;
|
||||
|
||||
// original interface
|
||||
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(AddDirtyBox)(CONST D3DBOX* pDirtyBox) 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;
|
||||
|
||||
|
||||
int GetHash(MyTypeHash& hash);
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline void UnswitchTextures(uMod_IDirect3DVolumeTexture9* pTexture)
|
||||
{
|
||||
uMod_IDirect3DVolumeTexture9* CrossRef = pTexture->CrossRef_D3Dtex;
|
||||
if (CrossRef != nullptr) {
|
||||
// switch textures back
|
||||
IDirect3DVolumeTexture9* cpy = pTexture->m_D3Dtex;
|
||||
pTexture->m_D3Dtex = CrossRef->m_D3Dtex;
|
||||
CrossRef->m_D3Dtex = cpy;
|
||||
|
||||
// cancel the link
|
||||
CrossRef->CrossRef_D3Dtex = nullptr;
|
||||
pTexture->CrossRef_D3Dtex = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline int SwitchTextures(uMod_IDirect3DVolumeTexture9* pTexture1, uMod_IDirect3DVolumeTexture9* pTexture2)
|
||||
{
|
||||
if (pTexture1->m_D3Ddev == pTexture2->m_D3Ddev && pTexture1->CrossRef_D3Dtex == nullptr && pTexture2->CrossRef_D3Dtex == nullptr) {
|
||||
// make cross reference
|
||||
pTexture1->CrossRef_D3Dtex = pTexture2;
|
||||
pTexture2->CrossRef_D3Dtex = pTexture1;
|
||||
|
||||
// switch textures
|
||||
IDirect3DVolumeTexture9* cpy = pTexture2->m_D3Dtex;
|
||||
pTexture2->m_D3Dtex = pTexture1->m_D3Dtex;
|
||||
pTexture1->m_D3Dtex = cpy;
|
||||
return RETURN_OK;
|
||||
}
|
||||
return RETURN_TEXTURE_NOT_SWITCHED;
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
||||
#include <d3d9.h>
|
||||
#include <d3dx9.h>
|
||||
|
||||
#include "uMod_GlobalDefines.h"
|
||||
#include "uMod_Error.h"
|
||||
#include "uMod_Defines.h"
|
||||
#include "uMod_DX9_dll.h"
|
||||
#include "uMod_TextureFunction.h"
|
||||
|
||||
#include "uMod_IDirect3D9.h"
|
||||
#include "uMod_IDirect3D9Ex.h"
|
||||
|
||||
#include "uMod_IDirect3DDevice9.h"
|
||||
#include "uMod_IDirect3DDevice9Ex.h"
|
||||
|
||||
#include "uMod_IDirect3DCubeTexture9.h"
|
||||
#include "uMod_IDirect3DTexture9.h"
|
||||
#include "uMod_IDirect3DVolumeTexture9.h"
|
||||
|
||||
#include "uMod_ArrayHandler.h"
|
||||
#include "uMod_TextureServer.h"
|
||||
#include "uMod_TextureClient.h"
|
||||
|
||||
#pragma warning(disable : 4477)
|
||||
|
||||
extern unsigned int gl_ErrorState;
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <Windows.h>
|
||||
|
||||
struct UModTexture {
|
||||
std::vector<char> data;
|
||||
std::string name;
|
||||
DWORD64 hash;
|
||||
};
|
||||
@@ -1,69 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "uMod_IDirect3DTexture9.h"
|
||||
#include "uMod_IDirect3DDevice9.h"
|
||||
#include "uMod_Error.h"
|
||||
#include "uMod_ArrayHandler.h"
|
||||
|
||||
|
||||
class uMod_TextureServer;
|
||||
|
||||
/*
|
||||
* An object of this class is owned by each d3d9 device.
|
||||
* functions called by the Server are called from the server thread instance.
|
||||
* All other functions are called from the render thread instance of the game itself.
|
||||
*/
|
||||
|
||||
class uMod_TextureClient {
|
||||
public:
|
||||
uMod_TextureClient(uMod_TextureServer* server, IDirect3DDevice9* device);
|
||||
~uMod_TextureClient();
|
||||
|
||||
int AddTexture(uMod_IDirect3DTexture9* tex); //called from uMod_IDirect3DDevice9::CreateTexture(...) or uMod_IDirect3DDevice9::BeginScene()
|
||||
int AddTexture(uMod_IDirect3DVolumeTexture9* tex); //called from uMod_IDirect3DVolumeTexture9::CreateTexture(...) or uMod_IDirect3DDevice9::BeginScene()
|
||||
int AddTexture(uMod_IDirect3DCubeTexture9* tex); //called from uMod_IDirect3DCubeTexture9::CreateTexture(...) or uMod_IDirect3DDevice9::BeginScene()
|
||||
|
||||
int RemoveTexture(uMod_IDirect3DTexture9* tex); //called from uMod_IDirect3DTexture9::Release()
|
||||
int RemoveTexture(uMod_IDirect3DVolumeTexture9* tex); //called from uMod_IDirect3DVolumeTexture9::Release()
|
||||
int RemoveTexture(uMod_IDirect3DCubeTexture9* tex); //called from uMod_IDirect3DCubeTexture9::Release()
|
||||
|
||||
int AddUpdate(TextureFileStruct* update, int number); //called from the Server, client object must delete update array
|
||||
int MergeUpdate(); //called from uMod_IDirect3DDevice9::BeginScene()
|
||||
|
||||
int LookUpToMod(uMod_IDirect3DTexture9* pTexture, int num_index_list = 0, int* index_list = nullptr); // called at the end AddTexture(...) and from Device->UpdateTexture(...)
|
||||
int LookUpToMod(uMod_IDirect3DVolumeTexture9* pTexture, int num_index_list = 0, int* index_list = nullptr); // called at the end AddTexture(...) and from Device->UpdateTexture(...)
|
||||
int LookUpToMod(uMod_IDirect3DCubeTexture9* pTexture, int num_index_list = 0, int* index_list = nullptr); // called at the end AddTexture(...) and from Device->UpdateTexture(...)
|
||||
|
||||
uMod_TextureHandler<uMod_IDirect3DTexture9> OriginalTextures; // stores the pointer to the uMod_IDirect3DTexture9 objects created by the game
|
||||
uMod_TextureHandler<uMod_IDirect3DVolumeTexture9> OriginalVolumeTextures; // stores the pointer to the uMod_IDirect3DVolumeTexture9 objects created by the game
|
||||
uMod_TextureHandler<uMod_IDirect3DCubeTexture9> OriginalCubeTextures; // stores the pointer to the uMod_IDirect3DCubeTexture9 objects created by the game
|
||||
|
||||
D3DCOLOR FontColour;
|
||||
D3DCOLOR TextureColour;
|
||||
|
||||
private:
|
||||
uMod_TextureServer* Server;
|
||||
IDirect3DDevice9* D3D9Device;
|
||||
|
||||
TextureFileStruct* Update;
|
||||
int NumberOfUpdate;
|
||||
|
||||
int LockMutex();
|
||||
int UnlockMutex();
|
||||
HANDLE Mutex;
|
||||
|
||||
int NumberToMod; // number of texture to be modded
|
||||
TextureFileStruct* FileToMod; // array which stores the file in memory and the hash of each texture to be modded
|
||||
|
||||
|
||||
int LookUpToMod(MyTypeHash hash, int num_index_list, int* index_list); // called from LookUpToMod(...);
|
||||
int LoadTexture(TextureFileStruct* file_in_memory, uMod_IDirect3DTexture9** ppTexture); // called if a target texture is found
|
||||
int LoadTexture(TextureFileStruct* file_in_memory, uMod_IDirect3DVolumeTexture9** ppTexture); // called if a target texture is found
|
||||
int LoadTexture(TextureFileStruct* file_in_memory, uMod_IDirect3DCubeTexture9** ppTexture); // called if a target texture is found
|
||||
|
||||
// and the corresponding fake texture should be loaded
|
||||
|
||||
//MyTypeHash GetHash(unsigned char *str, int len);
|
||||
//unsigned int GetCRC32(char *pcDatabuf, unsigned int ulDatalen);
|
||||
};
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
unsigned int GetCRC32(char* pcDatabuf, unsigned int ulDatalen);
|
||||
/*
|
||||
case D3DFMT_MULTI2_ARGB8:
|
||||
case D3DFMT_VERTEXDATA:
|
||||
*/
|
||||
inline int GetBitsFromFormat(D3DFORMAT format)
|
||||
{
|
||||
switch (format) //switch trough the formats to calculate the size of the raw data
|
||||
{
|
||||
case D3DFMT_A1: // 1-bit monochrome.
|
||||
{
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case D3DFMT_R3G3B2: // 8-bit RGB texture format using 3 bits for red, 3 bits for green, and 2 bits for blue.
|
||||
case D3DFMT_A8: // 8-bit alpha only.
|
||||
case D3DFMT_A8P8: // 8-bit color indexed with 8 bits of alpha.
|
||||
case D3DFMT_P8: // 8-bit color indexed.
|
||||
case D3DFMT_L8: // 8-bit luminance only.
|
||||
case D3DFMT_A4L4: // 8-bit using 4 bits each for alpha and luminance.
|
||||
case D3DFMT_FORCE_DWORD:
|
||||
case D3DFMT_S8_LOCKABLE: // A lockable 8-bit stencil buffer.
|
||||
{
|
||||
return 8;
|
||||
break;
|
||||
}
|
||||
|
||||
case D3DFMT_D16_LOCKABLE: //16-bit z-buffer bit depth.
|
||||
case D3DFMT_D15S1: // 16-bit z-buffer bit depth where 15 bits are reserved for the depth channel and 1 bit is reserved for the stencil channel.
|
||||
case D3DFMT_L6V5U5: // 16-bit bump-map format with luminance using 6 bits for luminance, and 5 bits each for v and u.
|
||||
case D3DFMT_V8U8: // 16-bit bump-map format using 8 bits each for u and v data.
|
||||
case D3DFMT_CxV8U8: // 16-bit normal compression format. The texture sampler computes the C channel from: C = sqrt(1 - U2 - V2).
|
||||
case D3DFMT_R5G6B5: // 16-bit RGB pixel format with 5 bits for red, 6 bits for green, and 5 bits for blue.
|
||||
case D3DFMT_X1R5G5B5: // 16-bit pixel format where 5 bits are reserved for each color.
|
||||
case D3DFMT_A1R5G5B5: // 16-bit pixel format where 5 bits are reserved for each color and 1 bit is reserved for alpha.
|
||||
case D3DFMT_A4R4G4B4: // 16-bit ARGB pixel format with 4 bits for each channel.
|
||||
case D3DFMT_A8R3G3B2: // 16-bit ARGB texture format using 8 bits for alpha, 3 bits each for red and green, and 2 bits for blue.
|
||||
case D3DFMT_X4R4G4B4: // 16-bit RGB pixel format using 4 bits for each color.
|
||||
case D3DFMT_L16: // 16-bit luminance only.
|
||||
case D3DFMT_R16F: // 16-bit float format using 16 bits for the red channel.
|
||||
case D3DFMT_A8L8: // 16-bit using 8 bits each for alpha and luminance.
|
||||
case D3DFMT_D16: // 16-bit z-buffer bit depth.
|
||||
case D3DFMT_INDEX16: // 16-bit index buffer bit depth.
|
||||
case D3DFMT_G8R8_G8B8: // ??
|
||||
case D3DFMT_R8G8_B8G8: // ??
|
||||
case D3DFMT_UYVY: // ??
|
||||
case D3DFMT_YUY2: // ??
|
||||
{
|
||||
return 16;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case D3DFMT_R8G8B8: //24-bit RGB pixel format with 8 bits per channel.
|
||||
{
|
||||
return 24;
|
||||
break;
|
||||
}
|
||||
|
||||
case D3DFMT_R32F: // 32-bit float format using 32 bits for the red channel.
|
||||
case D3DFMT_X8L8V8U8: // 32-bit bump-map format with luminance using 8 bits for each channel.
|
||||
case D3DFMT_A2W10V10U10: // 32-bit bump-map format using 2 bits for alpha and 10 bits each for w, v, and u.
|
||||
case D3DFMT_Q8W8V8U8: // 32-bit bump-map format using 8 bits for each channel.
|
||||
case D3DFMT_V16U16: // 32-bit bump-map format using 16 bits for each channel.
|
||||
case D3DFMT_A8R8G8B8: // 32-bit ARGB pixel format with alpha, using 8 bits per channel.
|
||||
case D3DFMT_X8R8G8B8: // 32-bit RGB pixel format, where 8 bits are reserved for each color.
|
||||
case D3DFMT_A2B10G10R10: // 32-bit pixel format using 10 bits for each color and 2 bits for alpha.
|
||||
case D3DFMT_A8B8G8R8: // 32-bit ARGB pixel format with alpha, using 8 bits per channel.
|
||||
case D3DFMT_X8B8G8R8: // 32-bit RGB pixel format, where 8 bits are reserved for each color.
|
||||
case D3DFMT_G16R16: // 32-bit pixel format using 16 bits each for green and red.
|
||||
case D3DFMT_G16R16F: // 32-bit float format using 16 bits for the red channel and 16 bits for the green channel.
|
||||
case D3DFMT_A2R10G10B10: // 32-bit pixel format using 10 bits each for red, green, and blue, and 2 bits for alpha.
|
||||
case D3DFMT_D32: // 32-bit z-buffer bit depth.
|
||||
case D3DFMT_D24S8: // 32-bit z-buffer bit depth using 24 bits for the depth channel and 8 bits for the stencil channel.
|
||||
case D3DFMT_D24X8: //32-bit z-buffer bit depth using 24 bits for the depth channel.
|
||||
case D3DFMT_D24X4S4: // 32-bit z-buffer bit depth using 24 bits for the depth channel and 4 bits for the stencil channel.
|
||||
case D3DFMT_D32F_LOCKABLE: // A lockable format where the depth value is represented as a standard IEEE floating-point number.
|
||||
case D3DFMT_D24FS8: // A non-lockable format that contains 24 bits of depth (in a 24-bit floating point format - 20e4) and 8 bits of stencil.
|
||||
case D3DFMT_D32_LOCKABLE: // A lockable 32-bit depth buffer.
|
||||
case D3DFMT_INDEX32: // 32-bit index buffer bit depth.
|
||||
//case : //
|
||||
//case : //
|
||||
//case : //
|
||||
//case : //
|
||||
{
|
||||
return 32;
|
||||
break;
|
||||
}
|
||||
|
||||
case D3DFMT_G32R32F: // 64-bit float format using 32 bits for the red channel and 32 bits for the green channel.
|
||||
case D3DFMT_Q16W16V16U16: // 64-bit bump-map format using 16 bits for each component.
|
||||
case D3DFMT_A16B16G16R16: // 64-bit pixel format using 16 bits for each component.
|
||||
case D3DFMT_A16B16G16R16F: // 64-bit float format using 16 bits for the each channel (alpha, blue, green, red).
|
||||
{
|
||||
return 64;
|
||||
break;
|
||||
}
|
||||
|
||||
case D3DFMT_A32B32G32R32F: // 128-bit float format using 32 bits for the each channel (alpha, blue, green, red).
|
||||
{
|
||||
return 128;
|
||||
break;
|
||||
}
|
||||
case D3DFMT_DXT2:
|
||||
case D3DFMT_DXT3:
|
||||
case D3DFMT_DXT4:
|
||||
case D3DFMT_DXT5: {
|
||||
return 8;
|
||||
break;
|
||||
}
|
||||
case D3DFMT_DXT1: {
|
||||
return 4;
|
||||
break;
|
||||
}
|
||||
default: //compressed formats
|
||||
{
|
||||
return 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "uMod_GlobalDefines.h"
|
||||
#include "uMod_ArrayHandler.h"
|
||||
|
||||
|
||||
/*
|
||||
* An object of this class is created only once.
|
||||
* The Mainloop functions is executed by a server thread,
|
||||
* which listen on a pipe.
|
||||
*
|
||||
* Functions called by the Client are called from the a thread instance of the game itself.
|
||||
* Nearly all other functions are called from the server thread instance.
|
||||
*/
|
||||
|
||||
|
||||
class uMod_TextureClient;
|
||||
|
||||
class uMod_TextureServer {
|
||||
public:
|
||||
uMod_TextureServer(char* name, char* uModName);
|
||||
~uMod_TextureServer();
|
||||
|
||||
int AddClient(uMod_TextureClient* client, TextureFileStruct** update, int* number); // called from a Client
|
||||
int Initialize(); // is executed in a server thread
|
||||
|
||||
|
||||
// following functions are only public for testing purpose !!
|
||||
// they should be private and only be called from the Mainloop
|
||||
|
||||
int AddFile(char* buffer, unsigned int size, MyTypeHash hash, bool force); // called from Mainloop(), if the content of the texture is sent
|
||||
|
||||
private:
|
||||
wchar_t GameName[MAX_PATH];
|
||||
char UModName[MAX_PATH];
|
||||
|
||||
void LoadModsFromFile(const char* source);
|
||||
int PropagateUpdate(uMod_TextureClient* client = nullptr); // called from Mainloop() if texture are loaded or removed
|
||||
int PrepareUpdate(TextureFileStruct** update, int* number); // called from PropagateUpdate() and AddClient()
|
||||
// generate a copy of the current texture to be modded
|
||||
// the file content of the textures are not copied, the clients get the pointer to the file content
|
||||
// but the arrays allocate by this function, must be deleted by the client
|
||||
|
||||
uMod_TextureClient* Client;
|
||||
uMod_FileHandler CurrentMod; // hold the file content of texture
|
||||
uMod_FileHandler OldMod; // hold the file content of texture which were added previously but are not needed any more
|
||||
// this is needed, because a texture clients might not have merged the last update and thus hold pointers to the file content of old textures
|
||||
};
|
||||
-157
@@ -1,157 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <Windows.h>
|
||||
|
||||
void ReplaceAll(std::wstring& str, const std::wstring& from, const std::wstring& to)
|
||||
{
|
||||
if (from.empty()) {
|
||||
return;
|
||||
}
|
||||
size_t startPos = 0;
|
||||
while ((startPos = str.find(from, startPos)) != std::wstring::npos) {
|
||||
str.replace(startPos, from.length(), to);
|
||||
startPos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
}
|
||||
|
||||
void ReplaceAll(std::string& str, const std::string& from, const std::string& to)
|
||||
{
|
||||
if (from.empty()) {
|
||||
return;
|
||||
}
|
||||
size_t startPos = 0;
|
||||
while ((startPos = str.find(from, startPos)) != std::string::npos) {
|
||||
str.replace(startPos, from.length(), to);
|
||||
startPos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring AfterFirst(const std::wstring& str, wchar_t delimiter)
|
||||
{
|
||||
const size_t pos = str.find(delimiter);
|
||||
if (pos != std::wstring::npos) {
|
||||
// Return the substring after the delimiter
|
||||
return str.substr(pos + 1);
|
||||
}
|
||||
// If the delimiter is not found, return an empty string
|
||||
return L"";
|
||||
}
|
||||
|
||||
std::string AfterFirst(const std::string& str, char delimiter)
|
||||
{
|
||||
const size_t pos = str.find(delimiter);
|
||||
if (pos != std::string::npos) {
|
||||
// Return the substring after the delimiter
|
||||
return str.substr(pos + 1);
|
||||
}
|
||||
// If the delimiter is not found, return an empty string
|
||||
return "";
|
||||
}
|
||||
|
||||
std::wstring BeforeFirst(const std::wstring& str, wchar_t delimiter)
|
||||
{
|
||||
const size_t pos = str.find(delimiter);
|
||||
return pos != std::wstring::npos ? str.substr(0, pos) : str;
|
||||
}
|
||||
|
||||
std::string BeforeFirst(const std::string& str, char delimiter)
|
||||
{
|
||||
const size_t pos = str.find(delimiter);
|
||||
return pos != std::string::npos ? str.substr(0, pos) : str;
|
||||
}
|
||||
|
||||
std::string BeforeLast(const std::string& file_path, char separator)
|
||||
{
|
||||
// Find the last occurrence of '.'
|
||||
const std::size_t last_dot_pos = file_path.find_last_of(separator);
|
||||
|
||||
// If there is a dot, and it is not at the beginning of the filename
|
||||
if (last_dot_pos != std::string::npos && last_dot_pos != 0) {
|
||||
// Extract the substring from the dot to the end of the string
|
||||
return file_path.substr(0, last_dot_pos);
|
||||
}
|
||||
// If the dot is not found, or is the first character, return an empty string
|
||||
return "";
|
||||
}
|
||||
|
||||
std::wstring BeforeLast(const std::wstring& file_path, wchar_t separator)
|
||||
{
|
||||
// Find the last occurrence of '.'
|
||||
const std::size_t last_dot_pos = file_path.find_last_of(separator);
|
||||
|
||||
// If there is a dot, and it is not at the beginning of the filename
|
||||
if (last_dot_pos != std::wstring::npos && last_dot_pos != 0) {
|
||||
// Extract the substring from the dot to the end of the string
|
||||
return file_path.substr(0, last_dot_pos);
|
||||
}
|
||||
// If the dot is not found, or is the first character, return an empty string
|
||||
return L"";
|
||||
}
|
||||
|
||||
std::string AfterLast(const std::string& file_path, char separator)
|
||||
{
|
||||
// Find the last occurrence of '.'
|
||||
const std::size_t last_dot_pos = file_path.find_last_of(separator);
|
||||
|
||||
// If there is a dot, and it is not at the beginning of the filename
|
||||
if (last_dot_pos != std::string::npos && last_dot_pos != 0) {
|
||||
// Extract the substring from the dot to the end of the string
|
||||
return file_path.substr(last_dot_pos + 1);
|
||||
}
|
||||
// If the dot is not found, or is the first character, return an empty string
|
||||
return "";
|
||||
}
|
||||
|
||||
std::wstring AfterLast(const std::wstring& file_path, wchar_t separator)
|
||||
{
|
||||
// Find the last occurrence of '.'
|
||||
const std::size_t last_dot_pos = file_path.find_last_of(separator);
|
||||
|
||||
// If there is a dot, and it is not at the beginning of the filename
|
||||
if (last_dot_pos != std::wstring::npos && last_dot_pos != 0) {
|
||||
// Extract the substring from the dot to the end of the string
|
||||
return file_path.substr(last_dot_pos + 1);
|
||||
}
|
||||
// If the dot is not found, or is the first character, return an empty string
|
||||
return L"";
|
||||
}
|
||||
|
||||
std::string GetFileExtension(const std::string& file_path)
|
||||
{
|
||||
// Find the last occurrence of '.'
|
||||
const std::size_t last_dot_pos = file_path.find_last_of(".");
|
||||
|
||||
// If there is a dot, and it is not at the beginning of the filename
|
||||
if (last_dot_pos != std::string::npos && last_dot_pos != 0) {
|
||||
// Extract the substring from the dot to the end of the string
|
||||
return file_path.substr(last_dot_pos + 1);
|
||||
}
|
||||
// If the dot is not found, or is the first character, return an empty string
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string WideStringToString(const std::wstring& wstr)
|
||||
{
|
||||
if (wstr.empty()) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
const int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], static_cast<int>(wstr.size()), nullptr, 0, nullptr, nullptr);
|
||||
std::string strTo(size_needed, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], static_cast<int>(wstr.size()), &strTo[0], size_needed, nullptr, nullptr);
|
||||
return strTo;
|
||||
}
|
||||
|
||||
|
||||
std::wstring StringToWString(const std::string& str)
|
||||
{
|
||||
if (str.empty()) {
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
const int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], static_cast<int>(str.size()), nullptr, 0);
|
||||
std::wstring wstrTo(size_needed, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, &str[0], static_cast<int>(str.size()), &wstrTo[0], size_needed);
|
||||
return wstrTo;
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
module;
|
||||
|
||||
#include "Main.h"
|
||||
#include "Defines.h"
|
||||
|
||||
export module ModfileLoader;
|
||||
|
||||
import std;
|
||||
import <libzippp.h>;
|
||||
import ModfileLoader.TpfReader;
|
||||
import TextureFunction;
|
||||
|
||||
namespace {
|
||||
bool use_64_bit_crc = false;
|
||||
|
||||
HashType GetCrcFromFilename(const std::string& filename)
|
||||
{
|
||||
const static std::regex re(R"(0[xX][0-9a-fA-F]{4,16})", std::regex::optimize);
|
||||
std::smatch match;
|
||||
if (!std::regex_search(filename, match, re)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t crc64_hash = 0;
|
||||
const auto number_str = match.str();
|
||||
try {
|
||||
crc64_hash = std::stoull(number_str, nullptr, 16);
|
||||
}
|
||||
catch (const std::invalid_argument&) {
|
||||
Warning("Failed to parse %s as a hash\n", filename.c_str());
|
||||
return 0;
|
||||
}
|
||||
catch (const std::out_of_range&) {
|
||||
Warning("Out of range while parsing %s as a hash\n", filename.c_str());
|
||||
return 0;
|
||||
}
|
||||
use_64_bit_crc = use_64_bit_crc || crc64_hash > 0xFFFFFFFF;
|
||||
return crc64_hash;
|
||||
}
|
||||
}
|
||||
|
||||
namespace HashCheck {
|
||||
export bool Use64BitCrc()
|
||||
{
|
||||
return use_64_bit_crc;
|
||||
}
|
||||
}
|
||||
|
||||
export class ModfileLoader {
|
||||
std::filesystem::path file_name;
|
||||
const std::string TPF_PASSWORD{
|
||||
0x73, 0x2A, 0x63, 0x7D, 0x5F, 0x0A, static_cast<char>(0xA6), static_cast<char>(0xBD),
|
||||
0x7D, 0x65, 0x7E, 0x67, 0x61, 0x2A, 0x7F, 0x7F,
|
||||
0x74, 0x61, 0x67, 0x5B, 0x60, 0x70, 0x45, 0x74,
|
||||
0x5C, 0x22, 0x74, 0x5D, 0x6E, 0x6A, 0x73, 0x41,
|
||||
0x77, 0x6E, 0x46, 0x47, 0x77, 0x49, 0x0C, 0x4B,
|
||||
0x46, 0x6F};
|
||||
|
||||
public:
|
||||
ModfileLoader(const std::filesystem::path& fileName);
|
||||
|
||||
std::vector<TexEntry> GetContents() const;
|
||||
|
||||
private:
|
||||
std::vector<TexEntry> GetTpfContents() const;
|
||||
|
||||
std::vector<TexEntry> GetFileContents() const;
|
||||
|
||||
static void LoadEntries(libzippp::ZipArchive& archive, std::vector<TexEntry>& entries);
|
||||
};
|
||||
|
||||
ModfileLoader::ModfileLoader(const std::filesystem::path& fileName)
|
||||
{
|
||||
file_name = std::filesystem::absolute(fileName);
|
||||
}
|
||||
|
||||
std::vector<TexEntry> ModfileLoader::GetContents() const
|
||||
{
|
||||
try {
|
||||
return file_name.wstring().ends_with(L".tpf") ? GetTpfContents() : GetFileContents();
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
Warning("Failed to open mod file: %s\n", file_name.c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<TexEntry> ModfileLoader::GetTpfContents() const
|
||||
{
|
||||
std::vector<TexEntry> entries;
|
||||
auto tpf_reader = TpfReader(file_name);
|
||||
const auto buffer = tpf_reader.ReadToEnd();
|
||||
const auto zip_archive = libzippp::ZipArchive::fromBuffer(buffer.data(), buffer.size(), false, TPF_PASSWORD);
|
||||
if (!zip_archive) {
|
||||
Warning("Failed to open tpf file: %s - %u bytes!", file_name.c_str(), buffer.size());
|
||||
return {};
|
||||
}
|
||||
zip_archive->setErrorHandlerCallback(
|
||||
[](const std::string& message, const std::string& strerror, int zip_error_code, int system_error_code) -> void {
|
||||
Message("GetTpfContents: %s %s %d %d\n", message.c_str(), strerror.c_str(), zip_error_code, system_error_code);
|
||||
});
|
||||
zip_archive->open();
|
||||
LoadEntries(*zip_archive, entries);
|
||||
zip_archive->close();
|
||||
libzippp::ZipArchive::free(zip_archive);
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
std::vector<TexEntry> ModfileLoader::GetFileContents() const
|
||||
{
|
||||
std::vector<TexEntry> entries;
|
||||
|
||||
libzippp::ZipArchive zip_archive(file_name.string());
|
||||
zip_archive.open();
|
||||
LoadEntries(zip_archive, entries);
|
||||
zip_archive.close();
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
void ParseSimpleArchive(const libzippp::ZipArchive& archive, std::vector<TexEntry>& entries)
|
||||
{
|
||||
for (const auto& entry : archive.getEntries()) {
|
||||
if (!entry.isFile())
|
||||
continue;
|
||||
const auto entry_name = entry.getName();
|
||||
const auto crc_hash = GetCrcFromFilename(entry.getName());
|
||||
if (!crc_hash) {
|
||||
Warning("Entry with name %s could not be parsed", entry_name.c_str());
|
||||
continue;
|
||||
}
|
||||
const auto data_ptr = static_cast<BYTE*>(entry.readAsBinary());
|
||||
const auto size = entry.getSize();
|
||||
std::vector vec(data_ptr, data_ptr + size);
|
||||
std::filesystem::path tex_name(entry.getName());
|
||||
entries.emplace_back(std::move(vec), crc_hash, tex_name.extension().string());
|
||||
delete[] data_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ParseTexmodArchive(std::vector<std::string>& lines, libzippp::ZipArchive& archive, std::vector<TexEntry>& entries)
|
||||
{
|
||||
for (const auto& line : lines) {
|
||||
// 0xC57D73F7|GW.EXE_0xC57D73F7.tga\r\n
|
||||
// match[1] | match[2]
|
||||
const static auto address_file_regex = std::regex(R"(^[\\/.]*([^|]+)\|([^\r\n]+))", std::regex::optimize);
|
||||
std::smatch match;
|
||||
if (!std::regex_search(line, match, address_file_regex)) {
|
||||
Warning("Failed to parse texmod.def archive line: %s - %s", line.c_str(), line.c_str());
|
||||
continue;
|
||||
}
|
||||
const auto address_string = match[1].str();
|
||||
const auto file_path = match[2].str();
|
||||
|
||||
const auto crc_hash = GetCrcFromFilename(address_string);
|
||||
if (!crc_hash)
|
||||
continue;
|
||||
|
||||
const auto entry = archive.getEntry(file_path);
|
||||
if (entry.isNull() || !entry.isFile())
|
||||
continue;
|
||||
|
||||
const auto data_ptr = static_cast<BYTE*>(entry.readAsBinary());
|
||||
const auto size = static_cast<size_t>(entry.getSize());
|
||||
std::vector vec(data_ptr, data_ptr + size);
|
||||
const auto tex_name = std::filesystem::path(entry.getName());
|
||||
entries.emplace_back(std::move(vec), crc_hash, tex_name.extension().string());
|
||||
delete[] data_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ModfileLoader::LoadEntries(libzippp::ZipArchive& archive, std::vector<TexEntry>& entries)
|
||||
{
|
||||
const auto def_file = archive.getEntry("texmod.def");
|
||||
if (def_file.isNull() || !def_file.isFile()) {
|
||||
ParseSimpleArchive(archive, entries);
|
||||
}
|
||||
else {
|
||||
const auto def = def_file.readAsText();
|
||||
std::istringstream iss(def);
|
||||
std::vector<std::string> lines;
|
||||
std::string line;
|
||||
|
||||
while (std::getline(iss, line)) {
|
||||
lines.push_back(line);
|
||||
}
|
||||
|
||||
ParseTexmodArchive(lines, archive, entries);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
export module ModfileLoader.TpfReader;
|
||||
|
||||
import std;
|
||||
|
||||
export class TpfReader {
|
||||
public:
|
||||
TpfReader(const std::filesystem::path& path)
|
||||
{
|
||||
file_stream = std::ifstream(path, std::ios::binary);
|
||||
if (!file_stream.seekg(0, std::ios::end).good() || !file_stream.seekg(0, std::ios::beg).good()) {
|
||||
throw std::invalid_argument("Provided stream needs to have SEEK set to True");
|
||||
}
|
||||
}
|
||||
|
||||
~TpfReader()
|
||||
{
|
||||
file_stream.close();
|
||||
}
|
||||
|
||||
std::vector<char> ReadToEnd()
|
||||
{
|
||||
file_stream.seekg(0, std::ios::end);
|
||||
line_length = file_stream.tellg();
|
||||
file_stream.seekg(0, std::ios::beg); // Go to the beginning of the stream
|
||||
|
||||
std::vector<char> data(static_cast<long>(line_length));
|
||||
file_stream.read(data.data(), line_length);
|
||||
for (auto i = 0u; i < data.size(); i++) {
|
||||
data[i] = XOR(data[i], i);
|
||||
}
|
||||
|
||||
for (auto i = data.size() - 1; i > 0 && data[i] != 0; i--) {
|
||||
data[i] = 0;
|
||||
}
|
||||
|
||||
// in the other zip libraries, these had to be cut off, with libzip we need to zero them out
|
||||
// cutting them off makes the archive invalid
|
||||
// data.resize(last_zero);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ifstream file_stream;
|
||||
std::streamoff line_length = 0;
|
||||
|
||||
[[nodiscard]] char XOR(const char b, const long position) const
|
||||
{
|
||||
if (position >= (line_length / 4) * 4) {
|
||||
return b ^ TPF_XOREven;
|
||||
}
|
||||
|
||||
return position % 2 == 0 ? b ^ TPF_XOREven : b ^ TPF_XOROdd;
|
||||
}
|
||||
|
||||
static constexpr char TPF_XOROdd = 0x3F;
|
||||
static constexpr char TPF_XOREven = static_cast<char>(0xA4);
|
||||
};
|
||||
@@ -0,0 +1,671 @@
|
||||
module;
|
||||
|
||||
#include "Main.h"
|
||||
#include "Error.h"
|
||||
#include "D3D9State.h"
|
||||
#include <DDSTextureLoader/DDSTextureLoader9.h>
|
||||
#include <DirectXTex/DirectXTex.h>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
export module TextureClient;
|
||||
import TextureFunction;
|
||||
import ModfileLoader;
|
||||
|
||||
export std::vector<std::pair<std::string, std::string>> modlists_contents;
|
||||
|
||||
struct PendingOp {
|
||||
enum class Kind {
|
||||
Add,
|
||||
Remove
|
||||
};
|
||||
Kind kind;
|
||||
HashType hash;
|
||||
std::vector<BYTE> data;
|
||||
};
|
||||
|
||||
// Couples a value with the mutex guarding it; the value is only reachable while
|
||||
// the lock is held.
|
||||
template <class T>
|
||||
class Guarded {
|
||||
std::mutex mutex;
|
||||
T value;
|
||||
|
||||
public:
|
||||
template <class Fn>
|
||||
auto with(Fn&& fn)
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
return fn(value);
|
||||
}
|
||||
};
|
||||
|
||||
// Owned by each d3d9 device (see D3D9Hooks.cpp). Holds per-texture side-state
|
||||
// and does the modding; the On*() methods are the vtable-hook entry points.
|
||||
export class TextureClient {
|
||||
public:
|
||||
TextureClient(IDirect3DDevice9* device);
|
||||
~TextureClient();
|
||||
|
||||
// Called from the hooked vtable slots:
|
||||
TexState* OnCreateTexture(IDirect3DBaseTexture9* texture, TexType type);
|
||||
void OnBeginScene();
|
||||
// The fake replacement when `texture` is a modded original, else `texture`.
|
||||
IDirect3DBaseTexture9* ResolveBinding(IDirect3DBaseTexture9* texture);
|
||||
// For GetTexture: the original behind one of our fakes, or nullptr.
|
||||
IDirect3DBaseTexture9* ResolveOriginalFromFake(IDirect3DBaseTexture9* texture);
|
||||
void OnUpdateTexture(IDirect3DBaseTexture9* source, IDirect3DBaseTexture9* destination);
|
||||
void OnReleaseTexture(IDirect3DBaseTexture9* texture); // called once the real refcount hit zero
|
||||
|
||||
void Initialize();
|
||||
|
||||
void EnqueueAdd(HashType hash, std::vector<BYTE> data);
|
||||
void EnqueueRemove(HashType hash);
|
||||
|
||||
static int AddFile(const std::filesystem::path& path);
|
||||
static int RemoveFile(const std::filesystem::path& path);
|
||||
static std::vector<std::filesystem::path> GetFiles();
|
||||
|
||||
// The texture Release hook has only a texture pointer, so it finds its client here.
|
||||
static TextureClient* CurrentClient();
|
||||
|
||||
private:
|
||||
IDirect3DDevice9* D3D9Device;
|
||||
|
||||
// Side-state keyed by real texture pointer: the game's textures vs. our fakes.
|
||||
std::recursive_mutex registry_mutex;
|
||||
std::unordered_map<IDirect3DBaseTexture9*, TexState*> originals;
|
||||
std::unordered_map<IDirect3DBaseTexture9*, TexState*> fakes;
|
||||
|
||||
// Hashed one creation later, once the game has filled it with data.
|
||||
IDirect3DBaseTexture9* last_created[3] = {};
|
||||
|
||||
bool loading_fake = false; // set while LoadTexture() drives CreateTexture internally
|
||||
bool shutting_down = false; // set in ~TextureClient so the Release hook stops touching us
|
||||
bool should_update = false;
|
||||
|
||||
int AddTexture(IDirect3DBaseTexture9* texture); // hash a freshly filled original + look up its mod
|
||||
int LookUpToMod(TexState* state); // switch in a fake if a mod matches the hash
|
||||
int LoadTexture(TextureFileStruct* file_in_memory, IDirect3DBaseTexture9** ppTexture, TexState** ppState);
|
||||
|
||||
static void Switch(TexState* original, TexState* fake);
|
||||
static void Unswitch(TexState* original);
|
||||
void UnswitchAndRelease(TexState* original); // unswitch + release our fake
|
||||
|
||||
int MergeUpdate();
|
||||
|
||||
int LockMutex();
|
||||
int UnlockMutex();
|
||||
HANDLE hMutex;
|
||||
|
||||
Guarded<std::vector<PendingOp>> pending_ops;
|
||||
void ProcessPendingOps();
|
||||
void RemoveModdedTexture(HashType hash);
|
||||
|
||||
std::unordered_map<HashType, gsl::owner<TextureFileStruct*>> modded_textures; // hash -> mod file in memory
|
||||
|
||||
static void LoadStartupModlists();
|
||||
|
||||
// Enqueue an Add per distinct non-zero hash, free the structs, return the hashes
|
||||
// in load order; accumulates moved bytes into loaded_bytes when given.
|
||||
static std::vector<HashType> IngestModfile(std::vector<gsl::owner<TextureFileStruct*>>& texture_file_structs, uint64_t* loaded_bytes = nullptr);
|
||||
|
||||
// Kept in load order, which is priority order: on a hash collision the
|
||||
// earlier file wins (see ProcessPendingOps).
|
||||
struct LoadedFile {
|
||||
std::filesystem::path path;
|
||||
std::vector<HashType> hashes;
|
||||
};
|
||||
|
||||
static inline std::mutex global_mutex;
|
||||
static inline TextureClient* current_client = nullptr;
|
||||
// A vector, not a map: tens of mods at most, and their order is meaningful.
|
||||
static inline std::vector<LoadedFile> loaded_files;
|
||||
|
||||
// Caller holds global_mutex; path must already be absolute.
|
||||
static std::vector<LoadedFile>::iterator FindLoadedFile(const std::filesystem::path& absolute_path)
|
||||
{
|
||||
return std::ranges::find(loaded_files, absolute_path, &LoadedFile::path);
|
||||
}
|
||||
};
|
||||
|
||||
TextureClient* TextureClient::CurrentClient()
|
||||
{
|
||||
std::lock_guard lk(global_mutex);
|
||||
return current_client;
|
||||
}
|
||||
|
||||
TextureClient::TextureClient(IDirect3DDevice9* device)
|
||||
{
|
||||
Message("TextureClient::TextureClient(): %p\n", this);
|
||||
D3D9Device = device;
|
||||
|
||||
hMutex = CreateMutex(nullptr, false, nullptr);
|
||||
|
||||
std::lock_guard lk(global_mutex);
|
||||
ASSERT(current_client == nullptr); // gMod assumes a single d3d9 device per process
|
||||
current_client = this;
|
||||
}
|
||||
|
||||
TextureClient::~TextureClient()
|
||||
{
|
||||
Message("TextureClient::~TextureClient(): %p\n", this);
|
||||
{
|
||||
std::lock_guard lk(global_mutex);
|
||||
if (current_client == this) current_client = nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lk(registry_mutex);
|
||||
shutting_down = true; // the texture Release hook now no-ops for our textures
|
||||
|
||||
// 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) {
|
||||
delete state;
|
||||
}
|
||||
originals.clear();
|
||||
}
|
||||
|
||||
if (hMutex != nullptr) {
|
||||
CloseHandle(hMutex);
|
||||
}
|
||||
for (const auto texture_file_struct : modded_textures | std::views::values) {
|
||||
delete texture_file_struct;
|
||||
}
|
||||
modded_textures.clear();
|
||||
}
|
||||
|
||||
void TextureClient::Switch(TexState* original, TexState* fake)
|
||||
{
|
||||
original->partner = fake;
|
||||
fake->partner = original;
|
||||
}
|
||||
|
||||
void TextureClient::Unswitch(TexState* original)
|
||||
{
|
||||
if (original->partner != nullptr) {
|
||||
original->partner->partner = nullptr;
|
||||
original->partner = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TextureClient::UnswitchAndRelease(TexState* original)
|
||||
{
|
||||
TexState* fake = original->partner;
|
||||
if (fake == nullptr) return;
|
||||
Unswitch(original); // detach before releasing so the fake's Release hook won't touch `original`
|
||||
if (fake->real) fake->real->Release(); // re-enters OnReleaseTexture(fake), which frees its state
|
||||
}
|
||||
|
||||
TexState* TextureClient::OnCreateTexture(IDirect3DBaseTexture9* texture, TexType type)
|
||||
{
|
||||
std::lock_guard lk(registry_mutex);
|
||||
|
||||
const auto state = new TexState();
|
||||
state->real = texture;
|
||||
state->device = D3D9Device;
|
||||
state->type = type;
|
||||
|
||||
if (loading_fake) {
|
||||
// A replacement we're creating: track as a fake, skip the originals bookkeeping.
|
||||
state->isFake = true;
|
||||
fakes.emplace(texture, state);
|
||||
return state;
|
||||
}
|
||||
|
||||
originals.emplace(texture, state);
|
||||
|
||||
// The previous texture of this type is now filled, so hash it.
|
||||
if (const auto last = last_created[static_cast<int>(type)]) {
|
||||
AddTexture(last);
|
||||
}
|
||||
last_created[static_cast<int>(type)] = texture;
|
||||
return state;
|
||||
}
|
||||
|
||||
int TextureClient::AddTexture(IDirect3DBaseTexture9* texture)
|
||||
{
|
||||
const auto it = originals.find(texture);
|
||||
if (it == originals.end()) return RETURN_OK;
|
||||
const auto state = it->second;
|
||||
|
||||
// No longer pending.
|
||||
if (last_created[static_cast<int>(state->type)] == texture) {
|
||||
last_created[static_cast<int>(state->type)] = nullptr;
|
||||
}
|
||||
|
||||
if (gl_ErrorState & uMod_ERROR_FATAL) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
|
||||
state->hash = GetTextureHash(state);
|
||||
if (!state->hash) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
|
||||
return LookUpToMod(state); // check if this texture should be modded
|
||||
}
|
||||
|
||||
int TextureClient::LookUpToMod(TexState* state)
|
||||
{
|
||||
Message("TextureClient::LookUpToMod( %p): hash: %#lX\n", state->real, state->hash);
|
||||
if (state->partner != nullptr)
|
||||
return RETURN_OK; // already switched
|
||||
|
||||
auto found = modded_textures.find(state->hash.crc32);
|
||||
if (found == modded_textures.end())
|
||||
if (found = modded_textures.find(state->hash.crc64), !state->hash.crc64 || found == modded_textures.end())
|
||||
return RETURN_OK;
|
||||
|
||||
const auto textureFileStruct = found->second;
|
||||
|
||||
IDirect3DBaseTexture9* fake_texture = nullptr;
|
||||
TexState* fake_state = nullptr;
|
||||
if (const int ret = LoadTexture(textureFileStruct, &fake_texture, &fake_state); ret != RETURN_OK)
|
||||
return ret;
|
||||
|
||||
Switch(state, fake_state);
|
||||
fake_state->reference = textureFileStruct;
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int TextureClient::LoadTexture(TextureFileStruct* file_in_memory, IDirect3DBaseTexture9** ppTexture, TexState** ppState)
|
||||
{
|
||||
Message("LoadTexture( %p, %#lX): %p\n", file_in_memory, file_in_memory->crc_hash, this);
|
||||
*ppTexture = nullptr;
|
||||
*ppState = nullptr;
|
||||
|
||||
// CreateDDSTextureFromMemoryEx calls the hooked CreateTexture; loading_fake
|
||||
// makes that hook register the result as a fake, not a game original.
|
||||
IDirect3DTexture9* texture = nullptr;
|
||||
loading_fake = true;
|
||||
const auto ret = DirectX::CreateDDSTextureFromMemoryEx(
|
||||
D3D9Device,
|
||||
file_in_memory->data.data(),
|
||||
file_in_memory->data.size(),
|
||||
0, D3DPOOL_MANAGED, false,
|
||||
&texture);
|
||||
loading_fake = false;
|
||||
|
||||
if (ret != D3D_OK || texture == nullptr) {
|
||||
Warning("LoadDDSTexture (%p, %#lX): FAILED ret: \n", file_in_memory->data.data(), file_in_memory->crc_hash, ret);
|
||||
return RETURN_TEXTURE_NOT_LOADED;
|
||||
}
|
||||
|
||||
const auto fake = static_cast<IDirect3DBaseTexture9*>(texture);
|
||||
const auto state_it = fakes.find(fake);
|
||||
ASSERT(state_it != fakes.end()); // must have been registered by the CreateTexture hook
|
||||
*ppTexture = fake;
|
||||
*ppState = state_it->second;
|
||||
|
||||
Message("LoadTexture (%p, %#lX): DONE\n", fake, file_in_memory->crc_hash);
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
IDirect3DBaseTexture9* TextureClient::ResolveBinding(IDirect3DBaseTexture9* texture)
|
||||
{
|
||||
if (texture == nullptr) return nullptr;
|
||||
std::lock_guard lk(registry_mutex);
|
||||
const auto it = originals.find(texture);
|
||||
if (it != originals.end() && it->second->partner != nullptr) {
|
||||
return it->second->partner->real; // bind the fake in place of the original
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
IDirect3DBaseTexture9* TextureClient::ResolveOriginalFromFake(IDirect3DBaseTexture9* texture)
|
||||
{
|
||||
if (texture == nullptr) return nullptr;
|
||||
std::lock_guard lk(registry_mutex);
|
||||
const auto it = fakes.find(texture);
|
||||
if (it != fakes.end() && it->second->partner != nullptr) {
|
||||
return it->second->partner->real;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TextureClient::OnBeginScene()
|
||||
{
|
||||
{
|
||||
std::lock_guard lk(registry_mutex);
|
||||
for (int type = 0; type < 3; ++type) {
|
||||
if (last_created[type] != nullptr) {
|
||||
AddTexture(last_created[type]); // hashes + clears the slot
|
||||
}
|
||||
}
|
||||
}
|
||||
MergeUpdate();
|
||||
}
|
||||
|
||||
void TextureClient::OnUpdateTexture(IDirect3DBaseTexture9* source, IDirect3DBaseTexture9* destination)
|
||||
{
|
||||
// The copy already happened; re-hash both textures and re-run the mod lookup.
|
||||
std::lock_guard lk(registry_mutex);
|
||||
|
||||
auto refresh = [this](IDirect3DBaseTexture9* texture) {
|
||||
const auto it = originals.find(texture);
|
||||
if (it == originals.end()) return;
|
||||
const auto state = it->second;
|
||||
const auto hash = GetTextureHash(state);
|
||||
if (hash == state->hash) return; // unchanged
|
||||
state->hash = hash;
|
||||
if (state->partner != nullptr) UnswitchAndRelease(state);
|
||||
if (hash) LookUpToMod(state);
|
||||
};
|
||||
refresh(source);
|
||||
refresh(destination);
|
||||
}
|
||||
|
||||
void TextureClient::OnReleaseTexture(IDirect3DBaseTexture9* texture)
|
||||
{
|
||||
std::lock_guard lk(registry_mutex);
|
||||
if (shutting_down) return; // ~TextureClient is tearing everything down itself
|
||||
|
||||
// A fake of ours being released (either by us during cleanup, or transitively).
|
||||
if (const auto fit = fakes.find(texture); fit != fakes.end()) {
|
||||
const auto state = fit->second;
|
||||
if (state->partner != nullptr) state->partner->partner = nullptr;
|
||||
fakes.erase(fit);
|
||||
delete state;
|
||||
return;
|
||||
}
|
||||
|
||||
// A game texture (original) reaching zero references.
|
||||
if (const auto oit = originals.find(texture); oit != originals.end()) {
|
||||
const auto state = oit->second;
|
||||
if (last_created[static_cast<int>(state->type)] == texture) {
|
||||
last_created[static_cast<int>(state->type)] = nullptr;
|
||||
}
|
||||
originals.erase(oit);
|
||||
UnswitchAndRelease(state); // release the fake we held for it, if any
|
||||
delete state;
|
||||
}
|
||||
}
|
||||
|
||||
int TextureClient::MergeUpdate()
|
||||
{
|
||||
const bool has_pending = pending_ops.with([](auto& ops) { return !ops.empty(); });
|
||||
if (!should_update && !has_pending) return RETURN_OK;
|
||||
if (const int ret = LockMutex()) {
|
||||
gl_ErrorState |= uMod_ERROR_TEXTURE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Message("MergeUpdate(): %p\n", this);
|
||||
|
||||
ProcessPendingOps();
|
||||
|
||||
{
|
||||
std::lock_guard lk(registry_mutex);
|
||||
for (const auto state : originals | std::views::values) {
|
||||
if (state->partner == nullptr && state->hash) {
|
||||
LookUpToMod(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
should_update = false;
|
||||
return UnlockMutex();
|
||||
}
|
||||
|
||||
void TextureClient::EnqueueAdd(HashType hash, std::vector<BYTE> data)
|
||||
{
|
||||
if (!hash) return;
|
||||
pending_ops.with([&](auto& ops) { ops.push_back(PendingOp{PendingOp::Kind::Add, hash, std::move(data)}); });
|
||||
}
|
||||
|
||||
void TextureClient::EnqueueRemove(HashType hash)
|
||||
{
|
||||
if (!hash) return;
|
||||
pending_ops.with([&](auto& ops) { ops.push_back(PendingOp{PendingOp::Kind::Remove, hash, {}}); });
|
||||
}
|
||||
|
||||
void TextureClient::ProcessPendingOps()
|
||||
{
|
||||
std::vector<PendingOp> ops;
|
||||
pending_ops.with([&](auto& pending) { ops.swap(pending); });
|
||||
for (auto& op : ops) {
|
||||
if (op.kind == PendingOp::Kind::Add) {
|
||||
if (modded_textures.contains(op.hash)) continue;
|
||||
const auto texture_file_struct = new TextureFileStruct();
|
||||
texture_file_struct->crc_hash = op.hash;
|
||||
texture_file_struct->data = std::move(op.data);
|
||||
modded_textures.emplace(op.hash, texture_file_struct);
|
||||
should_update = true;
|
||||
}
|
||||
else {
|
||||
RemoveModdedTexture(op.hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextureClient::RemoveModdedTexture(HashType hash)
|
||||
{
|
||||
const auto it = modded_textures.find(hash);
|
||||
if (it == modded_textures.end()) return;
|
||||
const auto texture_file_struct = it->second;
|
||||
|
||||
{
|
||||
std::lock_guard lk(registry_mutex);
|
||||
for (const auto original : originals | std::views::values) {
|
||||
const auto fake = original->partner;
|
||||
if (fake != nullptr && fake->reference == texture_file_struct) {
|
||||
UnswitchAndRelease(original);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modded_textures.erase(it);
|
||||
delete texture_file_struct;
|
||||
}
|
||||
|
||||
int TextureClient::LockMutex()
|
||||
{
|
||||
if ((gl_ErrorState & (uMod_ERROR_FATAL | uMod_ERROR_MUTEX))) {
|
||||
return RETURN_NO_MUTEX;
|
||||
}
|
||||
if (WAIT_OBJECT_0 != WaitForSingleObject(hMutex, 100)) {
|
||||
return RETURN_MUTEX_LOCK; // waiting 100ms, to wait infinite pass INFINITE
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int TextureClient::UnlockMutex()
|
||||
{
|
||||
if (ReleaseMutex(hMutex) == 0) {
|
||||
return RETURN_MUTEX_UNLOCK;
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
gsl::owner<TextureFileStruct*> MakeTextureFileStruct(TexEntry& entry, const bool compress)
|
||||
{
|
||||
const auto texture_file_struct = new TextureFileStruct();
|
||||
texture_file_struct->crc_hash = entry.crc_hash;
|
||||
const auto dds_blob = TextureFunction::ConvertToCompressedDDS(entry, compress);
|
||||
texture_file_struct->data.assign(static_cast<BYTE*>(dds_blob.GetBufferPointer()), static_cast<BYTE*>(dds_blob.GetBufferPointer()) + dds_blob.GetBufferSize());
|
||||
return texture_file_struct;
|
||||
}
|
||||
|
||||
std::vector<gsl::owner<TextureFileStruct*>> ProcessModfile(const std::filesystem::path& modfile, const bool compress)
|
||||
{
|
||||
const auto hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||
if (FAILED(hr)) return {};
|
||||
const auto modfile_str = modfile.string();
|
||||
Message("Initialize: loading file %s... ", modfile_str.c_str());
|
||||
auto file_loader = ModfileLoader(modfile);
|
||||
auto entries = file_loader.GetContents();
|
||||
if (entries.empty()) {
|
||||
Message("No entries found.\n");
|
||||
CoUninitialize();
|
||||
return {};
|
||||
}
|
||||
Message("%zu textures... ", entries.size());
|
||||
std::vector<gsl::owner<TextureFileStruct*>> texture_file_structs;
|
||||
texture_file_structs.reserve(entries.size());
|
||||
unsigned file_bytes_loaded = 0;
|
||||
for (auto& tpf_entry : entries) {
|
||||
const auto texture_file_struct = MakeTextureFileStruct(tpf_entry, compress);
|
||||
texture_file_structs.push_back(texture_file_struct);
|
||||
file_bytes_loaded += static_cast<unsigned>(texture_file_structs.back()->data.size());
|
||||
}
|
||||
entries.clear();
|
||||
Message("%d bytes loaded.\n", file_bytes_loaded);
|
||||
CoUninitialize();
|
||||
return texture_file_structs;
|
||||
}
|
||||
|
||||
std::vector<HashType> TextureClient::IngestModfile(std::vector<gsl::owner<TextureFileStruct*>>& texture_file_structs, uint64_t* loaded_bytes)
|
||||
{
|
||||
std::vector<HashType> hashes;
|
||||
for (auto* texture_file_struct : texture_file_structs) {
|
||||
const auto hash = texture_file_struct->crc_hash;
|
||||
if (hash && std::ranges::find(hashes, hash) == hashes.end()) {
|
||||
hashes.push_back(hash);
|
||||
if (loaded_bytes) *loaded_bytes += texture_file_struct->data.size();
|
||||
if (current_client) {
|
||||
current_client->EnqueueAdd(hash, std::move(texture_file_struct->data));
|
||||
}
|
||||
}
|
||||
delete texture_file_struct;
|
||||
}
|
||||
texture_file_structs.clear();
|
||||
return hashes;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> TextureClient::GetFiles()
|
||||
{
|
||||
std::lock_guard lk(global_mutex);
|
||||
std::vector<std::filesystem::path> result;
|
||||
result.reserve(loaded_files.size());
|
||||
for (const auto& loaded_file : loaded_files) {
|
||||
result.push_back(loaded_file.path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int TextureClient::RemoveFile(const std::filesystem::path& path)
|
||||
{
|
||||
const auto absolute_path = std::filesystem::absolute(path);
|
||||
std::lock_guard lk(global_mutex);
|
||||
const auto it = FindLoadedFile(absolute_path);
|
||||
if (it == loaded_files.end()) return RETURN_FILE_NOT_LOADED;
|
||||
if (current_client) {
|
||||
for (const auto hash : it->hashes) {
|
||||
current_client->EnqueueRemove(hash);
|
||||
}
|
||||
}
|
||||
loaded_files.erase(it);
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int TextureClient::AddFile(const std::filesystem::path& path)
|
||||
{
|
||||
const auto absolute_path = std::filesystem::absolute(path);
|
||||
{
|
||||
std::lock_guard lk(global_mutex);
|
||||
if (FindLoadedFile(absolute_path) != loaded_files.end()) return RETURN_EXISTS;
|
||||
}
|
||||
if (!std::filesystem::exists(absolute_path)) return RETURN_FILE_NOT_LOADED;
|
||||
|
||||
const auto file_size = std::filesystem::file_size(absolute_path);
|
||||
auto texture_file_structs = ProcessModfile(absolute_path, file_size > 400'000'000);
|
||||
if (texture_file_structs.empty()) return RETURN_FILE_NOT_LOADED;
|
||||
|
||||
std::lock_guard lk(global_mutex);
|
||||
// Re-check under lock; another thread may have loaded the same path concurrently.
|
||||
if (FindLoadedFile(absolute_path) != loaded_files.end()) {
|
||||
for (auto* texture_file_struct : texture_file_structs)
|
||||
delete texture_file_struct;
|
||||
return RETURN_EXISTS;
|
||||
}
|
||||
|
||||
auto hashes = IngestModfile(texture_file_structs);
|
||||
if (hashes.empty()) return RETURN_TEXTURE_NOT_LOADED;
|
||||
loaded_files.push_back({absolute_path, std::move(hashes)});
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
void TextureClient::LoadStartupModlists()
|
||||
{
|
||||
std::locale::global(std::locale(""));
|
||||
std::vector<std::filesystem::path> modfiles;
|
||||
for (const auto& content : modlists_contents | std::views::values) {
|
||||
std::istringstream iss(content);
|
||||
std::string line;
|
||||
while (std::getline(iss, line)) {
|
||||
if (line.starts_with("//") || line.starts_with("#") || line.empty()) {
|
||||
continue;
|
||||
}
|
||||
// Remove newline character
|
||||
line.erase(std::ranges::remove(line, '\r').begin(), line.end());
|
||||
line.erase(std::ranges::remove(line, '\n').begin(), line.end());
|
||||
if (line.empty()) continue;
|
||||
|
||||
auto modfile = std::filesystem::absolute(std::filesystem::path(utils::utf8_to_wstring(line)));
|
||||
if (!std::ranges::contains(modfiles, modfile)) {
|
||||
modfiles.push_back(std::move(modfile));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto total_size = 0ull;
|
||||
for (const auto& modfile : modfiles) {
|
||||
if (std::filesystem::exists(modfile)) total_size += std::filesystem::file_size(modfile);
|
||||
}
|
||||
const bool compress = total_size > 400'000'000;
|
||||
|
||||
std::vector<std::future<std::vector<gsl::owner<TextureFileStruct*>>>> futures;
|
||||
futures.reserve(modfiles.size());
|
||||
for (const auto& modfile : modfiles) {
|
||||
futures.emplace_back(std::async(std::launch::async, ProcessModfile, modfile, compress));
|
||||
}
|
||||
auto loaded_size = 0ull;
|
||||
for (size_t i = 0; i < modfiles.size(); ++i) {
|
||||
auto texture_file_structs = futures[i].get();
|
||||
|
||||
std::lock_guard lk(global_mutex);
|
||||
if (FindLoadedFile(modfiles[i]) != loaded_files.end()) {
|
||||
for (const auto* texture_file_struct : texture_file_structs)
|
||||
delete texture_file_struct;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto hashes = IngestModfile(texture_file_structs, &loaded_size);
|
||||
if (!hashes.empty()) {
|
||||
loaded_files.push_back({modfiles[i], std::move(hashes)});
|
||||
}
|
||||
}
|
||||
Info("LoadStartupModlists: %llu bytes (%llu MB)\n", loaded_size, loaded_size / 1024 / 1024);
|
||||
}
|
||||
|
||||
void TextureClient::Initialize()
|
||||
{
|
||||
const auto t1 = std::chrono::high_resolution_clock::now();
|
||||
Info("Initialize: begin\n");
|
||||
|
||||
// AddFile() before the device existed recorded files but couldn't enqueue them
|
||||
// (no client yet); enqueue them now.
|
||||
for (const auto& path : GetFiles()) {
|
||||
if (!std::filesystem::exists(path)) continue;
|
||||
const auto file_size = std::filesystem::file_size(path);
|
||||
auto texture_file_structs = ProcessModfile(path, file_size > 400'000'000);
|
||||
IngestModfile(texture_file_structs);
|
||||
}
|
||||
|
||||
LoadStartupModlists();
|
||||
|
||||
const auto t2 = std::chrono::high_resolution_clock::now();
|
||||
const auto ms = duration_cast<std::chrono::milliseconds>(t2 - t1);
|
||||
Info("Initialize: end, took %d ms\n", ms);
|
||||
}
|
||||
@@ -0,0 +1,378 @@
|
||||
module;
|
||||
|
||||
#include "Main.h"
|
||||
#include <d3d9types.h>
|
||||
#include <DirectXTex/DirectXTex.h>
|
||||
|
||||
export module TextureFunction;
|
||||
|
||||
// clang-format off
|
||||
constexpr auto crctab64 = std::to_array({
|
||||
0x0000000000000000ULL, 0x7ad870c830358979ULL, 0xf5b0e190606b12f2ULL,
|
||||
0x8f689158505e9b8bULL, 0xc038e5739841b68fULL, 0xbae095bba8743ff6ULL,
|
||||
0x358804e3f82aa47dULL, 0x4f50742bc81f2d04ULL, 0xab28ecb46814fe75ULL,
|
||||
0xd1f09c7c5821770cULL, 0x5e980d24087fec87ULL, 0x24407dec384a65feULL,
|
||||
0x6b1009c7f05548faULL, 0x11c8790fc060c183ULL, 0x9ea0e857903e5a08ULL,
|
||||
0xe478989fa00bd371ULL, 0x7d08ff3b88be6f81ULL, 0x07d08ff3b88be6f8ULL,
|
||||
0x88b81eabe8d57d73ULL, 0xf2606e63d8e0f40aULL, 0xbd301a4810ffd90eULL,
|
||||
0xc7e86a8020ca5077ULL, 0x4880fbd87094cbfcULL, 0x32588b1040a14285ULL,
|
||||
0xd620138fe0aa91f4ULL, 0xacf86347d09f188dULL, 0x2390f21f80c18306ULL,
|
||||
0x594882d7b0f40a7fULL, 0x1618f6fc78eb277bULL, 0x6cc0863448deae02ULL,
|
||||
0xe3a8176c18803589ULL, 0x997067a428b5bcf0ULL, 0xfa11fe77117cdf02ULL,
|
||||
0x80c98ebf2149567bULL, 0x0fa11fe77117cdf0ULL, 0x75796f2f41224489ULL,
|
||||
0x3a291b04893d698dULL, 0x40f16bccb908e0f4ULL, 0xcf99fa94e9567b7fULL,
|
||||
0xb5418a5cd963f206ULL, 0x513912c379682177ULL, 0x2be1620b495da80eULL,
|
||||
0xa489f35319033385ULL, 0xde51839b2936bafcULL, 0x9101f7b0e12997f8ULL,
|
||||
0xebd98778d11c1e81ULL, 0x64b116208142850aULL, 0x1e6966e8b1770c73ULL,
|
||||
0x8719014c99c2b083ULL, 0xfdc17184a9f739faULL, 0x72a9e0dcf9a9a271ULL,
|
||||
0x08719014c99c2b08ULL, 0x4721e43f0183060cULL, 0x3df994f731b68f75ULL,
|
||||
0xb29105af61e814feULL, 0xc849756751dd9d87ULL, 0x2c31edf8f1d64ef6ULL,
|
||||
0x56e99d30c1e3c78fULL, 0xd9810c6891bd5c04ULL, 0xa3597ca0a188d57dULL,
|
||||
0xec09088b6997f879ULL, 0x96d1784359a27100ULL, 0x19b9e91b09fcea8bULL,
|
||||
0x636199d339c963f2ULL, 0xdf7adabd7a6e2d6fULL, 0xa5a2aa754a5ba416ULL,
|
||||
0x2aca3b2d1a053f9dULL, 0x50124be52a30b6e4ULL, 0x1f423fcee22f9be0ULL,
|
||||
0x659a4f06d21a1299ULL, 0xeaf2de5e82448912ULL, 0x902aae96b271006bULL,
|
||||
0x74523609127ad31aULL, 0x0e8a46c1224f5a63ULL, 0x81e2d7997211c1e8ULL,
|
||||
0xfb3aa75142244891ULL, 0xb46ad37a8a3b6595ULL, 0xceb2a3b2ba0eececULL,
|
||||
0x41da32eaea507767ULL, 0x3b024222da65fe1eULL, 0xa2722586f2d042eeULL,
|
||||
0xd8aa554ec2e5cb97ULL, 0x57c2c41692bb501cULL, 0x2d1ab4dea28ed965ULL,
|
||||
0x624ac0f56a91f461ULL, 0x1892b03d5aa47d18ULL, 0x97fa21650afae693ULL,
|
||||
0xed2251ad3acf6feaULL, 0x095ac9329ac4bc9bULL, 0x7382b9faaaf135e2ULL,
|
||||
0xfcea28a2faafae69ULL, 0x8632586aca9a2710ULL, 0xc9622c4102850a14ULL,
|
||||
0xb3ba5c8932b0836dULL, 0x3cd2cdd162ee18e6ULL, 0x460abd1952db919fULL,
|
||||
0x256b24ca6b12f26dULL, 0x5fb354025b277b14ULL, 0xd0dbc55a0b79e09fULL,
|
||||
0xaa03b5923b4c69e6ULL, 0xe553c1b9f35344e2ULL, 0x9f8bb171c366cd9bULL,
|
||||
0x10e3202993385610ULL, 0x6a3b50e1a30ddf69ULL, 0x8e43c87e03060c18ULL,
|
||||
0xf49bb8b633338561ULL, 0x7bf329ee636d1eeaULL, 0x012b592653589793ULL,
|
||||
0x4e7b2d0d9b47ba97ULL, 0x34a35dc5ab7233eeULL, 0xbbcbcc9dfb2ca865ULL,
|
||||
0xc113bc55cb19211cULL, 0x5863dbf1e3ac9decULL, 0x22bbab39d3991495ULL,
|
||||
0xadd33a6183c78f1eULL, 0xd70b4aa9b3f20667ULL, 0x985b3e827bed2b63ULL,
|
||||
0xe2834e4a4bd8a21aULL, 0x6debdf121b863991ULL, 0x1733afda2bb3b0e8ULL,
|
||||
0xf34b37458bb86399ULL, 0x8993478dbb8deae0ULL, 0x06fbd6d5ebd3716bULL,
|
||||
0x7c23a61ddbe6f812ULL, 0x3373d23613f9d516ULL, 0x49aba2fe23cc5c6fULL,
|
||||
0xc6c333a67392c7e4ULL, 0xbc1b436e43a74e9dULL, 0x95ac9329ac4bc9b5ULL,
|
||||
0xef74e3e19c7e40ccULL, 0x601c72b9cc20db47ULL, 0x1ac40271fc15523eULL,
|
||||
0x5594765a340a7f3aULL, 0x2f4c0692043ff643ULL, 0xa02497ca54616dc8ULL,
|
||||
0xdafce7026454e4b1ULL, 0x3e847f9dc45f37c0ULL, 0x445c0f55f46abeb9ULL,
|
||||
0xcb349e0da4342532ULL, 0xb1eceec59401ac4bULL, 0xfebc9aee5c1e814fULL,
|
||||
0x8464ea266c2b0836ULL, 0x0b0c7b7e3c7593bdULL, 0x71d40bb60c401ac4ULL,
|
||||
0xe8a46c1224f5a634ULL, 0x927c1cda14c02f4dULL, 0x1d148d82449eb4c6ULL,
|
||||
0x67ccfd4a74ab3dbfULL, 0x289c8961bcb410bbULL, 0x5244f9a98c8199c2ULL,
|
||||
0xdd2c68f1dcdf0249ULL, 0xa7f41839ecea8b30ULL, 0x438c80a64ce15841ULL,
|
||||
0x3954f06e7cd4d138ULL, 0xb63c61362c8a4ab3ULL, 0xcce411fe1cbfc3caULL,
|
||||
0x83b465d5d4a0eeceULL, 0xf96c151de49567b7ULL, 0x76048445b4cbfc3cULL,
|
||||
0x0cdcf48d84fe7545ULL, 0x6fbd6d5ebd3716b7ULL, 0x15651d968d029fceULL,
|
||||
0x9a0d8ccedd5c0445ULL, 0xe0d5fc06ed698d3cULL, 0xaf85882d2576a038ULL,
|
||||
0xd55df8e515432941ULL, 0x5a3569bd451db2caULL, 0x20ed197575283bb3ULL,
|
||||
0xc49581ead523e8c2ULL, 0xbe4df122e51661bbULL, 0x3125607ab548fa30ULL,
|
||||
0x4bfd10b2857d7349ULL, 0x04ad64994d625e4dULL, 0x7e7514517d57d734ULL,
|
||||
0xf11d85092d094cbfULL, 0x8bc5f5c11d3cc5c6ULL, 0x12b5926535897936ULL,
|
||||
0x686de2ad05bcf04fULL, 0xe70573f555e26bc4ULL, 0x9ddd033d65d7e2bdULL,
|
||||
0xd28d7716adc8cfb9ULL, 0xa85507de9dfd46c0ULL, 0x273d9686cda3dd4bULL,
|
||||
0x5de5e64efd965432ULL, 0xb99d7ed15d9d8743ULL, 0xc3450e196da80e3aULL,
|
||||
0x4c2d9f413df695b1ULL, 0x36f5ef890dc31cc8ULL, 0x79a59ba2c5dc31ccULL,
|
||||
0x037deb6af5e9b8b5ULL, 0x8c157a32a5b7233eULL, 0xf6cd0afa9582aa47ULL,
|
||||
0x4ad64994d625e4daULL, 0x300e395ce6106da3ULL, 0xbf66a804b64ef628ULL,
|
||||
0xc5bed8cc867b7f51ULL, 0x8aeeace74e645255ULL, 0xf036dc2f7e51db2cULL,
|
||||
0x7f5e4d772e0f40a7ULL, 0x05863dbf1e3ac9deULL, 0xe1fea520be311aafULL,
|
||||
0x9b26d5e88e0493d6ULL, 0x144e44b0de5a085dULL, 0x6e963478ee6f8124ULL,
|
||||
0x21c640532670ac20ULL, 0x5b1e309b16452559ULL, 0xd476a1c3461bbed2ULL,
|
||||
0xaeaed10b762e37abULL, 0x37deb6af5e9b8b5bULL, 0x4d06c6676eae0222ULL,
|
||||
0xc26e573f3ef099a9ULL, 0xb8b627f70ec510d0ULL, 0xf7e653dcc6da3dd4ULL,
|
||||
0x8d3e2314f6efb4adULL, 0x0256b24ca6b12f26ULL, 0x788ec2849684a65fULL,
|
||||
0x9cf65a1b368f752eULL, 0xe62e2ad306bafc57ULL, 0x6946bb8b56e467dcULL,
|
||||
0x139ecb4366d1eea5ULL, 0x5ccebf68aecec3a1ULL, 0x2616cfa09efb4ad8ULL,
|
||||
0xa97e5ef8cea5d153ULL, 0xd3a62e30fe90582aULL, 0xb0c7b7e3c7593bd8ULL,
|
||||
0xca1fc72bf76cb2a1ULL, 0x45775673a732292aULL, 0x3faf26bb9707a053ULL,
|
||||
0x70ff52905f188d57ULL, 0x0a2722586f2d042eULL, 0x854fb3003f739fa5ULL,
|
||||
0xff97c3c80f4616dcULL, 0x1bef5b57af4dc5adULL, 0x61372b9f9f784cd4ULL,
|
||||
0xee5fbac7cf26d75fULL, 0x9487ca0fff135e26ULL, 0xdbd7be24370c7322ULL,
|
||||
0xa10fceec0739fa5bULL, 0x2e675fb4576761d0ULL, 0x54bf2f7c6752e8a9ULL,
|
||||
0xcdcf48d84fe75459ULL, 0xb71738107fd2dd20ULL, 0x387fa9482f8c46abULL,
|
||||
0x42a7d9801fb9cfd2ULL, 0x0df7adabd7a6e2d6ULL, 0x772fdd63e7936bafULL,
|
||||
0xf8474c3bb7cdf024ULL, 0x829f3cf387f8795dULL, 0x66e7a46c27f3aa2cULL,
|
||||
0x1c3fd4a417c62355ULL, 0x935745fc4798b8deULL, 0xe98f353477ad31a7ULL,
|
||||
0xa6df411fbfb21ca3ULL, 0xdc0731d78f8795daULL, 0x536fa08fdfd90e51ULL,
|
||||
0x29b7d047efec8728ULL
|
||||
});
|
||||
// clang-format on
|
||||
|
||||
export namespace TextureFunction {
|
||||
|
||||
uint64_t get_crc64(const char* data, unsigned int length)
|
||||
{
|
||||
uint64_t crc = 0xFFFFFFFFFFFFFFFFULL;
|
||||
|
||||
while (length--) {
|
||||
crc = crctab64[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint32_t get_crc32(const char* data_ptr, const unsigned int length)
|
||||
{
|
||||
constexpr static auto crc32_poly = 0xEDB88320u;
|
||||
constexpr static auto ul_crc_in = 0xffffffff;
|
||||
unsigned int crc = ul_crc_in;
|
||||
for (unsigned int idx = 0u; idx < length; idx++) {
|
||||
unsigned int data = *data_ptr++;
|
||||
for (unsigned int bit = 0u; bit < 8u; bit++, data >>= 1) {
|
||||
crc = crc >> 1 ^ ((crc ^ data) & 1 ? crc32_poly : 0);
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
int GetBitsFromFormat(D3DFORMAT format)
|
||||
{
|
||||
switch (format) // switch trough the formats to calculate the size of the raw data
|
||||
{
|
||||
case D3DFMT_A1: // 1-bit monochrome.
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
case D3DFMT_R3G3B2: // 8-bit RGB texture format using 3 bits for red, 3 bits for green, and 2 bits for blue.
|
||||
case D3DFMT_A8: // 8-bit alpha only.
|
||||
case D3DFMT_A8P8: // 8-bit color indexed with 8 bits of alpha.
|
||||
case D3DFMT_P8: // 8-bit color indexed.
|
||||
case D3DFMT_L8: // 8-bit luminance only.
|
||||
case D3DFMT_A4L4: // 8-bit using 4 bits each for alpha and luminance.
|
||||
case D3DFMT_FORCE_DWORD:
|
||||
case D3DFMT_S8_LOCKABLE: // A lockable 8-bit stencil buffer.
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
case D3DFMT_D16_LOCKABLE: // 16-bit z-buffer bit depth.
|
||||
case D3DFMT_D15S1: // 16-bit z-buffer bit depth where 15 bits are reserved for the depth channel and 1 bit is reserved for the stencil channel.
|
||||
case D3DFMT_L6V5U5: // 16-bit bump-map format with luminance using 6 bits for luminance, and 5 bits each for v and u.
|
||||
case D3DFMT_V8U8: // 16-bit bump-map format using 8 bits each for u and v data.
|
||||
case D3DFMT_CxV8U8: // 16-bit normal compression format. The texture sampler computes the C channel from: C = sqrt(1 - U2 - V2).
|
||||
case D3DFMT_R5G6B5: // 16-bit RGB pixel format with 5 bits for red, 6 bits for green, and 5 bits for blue.
|
||||
case D3DFMT_X1R5G5B5: // 16-bit pixel format where 5 bits are reserved for each color.
|
||||
case D3DFMT_A1R5G5B5: // 16-bit pixel format where 5 bits are reserved for each color and 1 bit is reserved for alpha.
|
||||
case D3DFMT_A4R4G4B4: // 16-bit ARGB pixel format with 4 bits for each channel.
|
||||
case D3DFMT_A8R3G3B2: // 16-bit ARGB texture format using 8 bits for alpha, 3 bits each for red and green, and 2 bits for blue.
|
||||
case D3DFMT_X4R4G4B4: // 16-bit RGB pixel format using 4 bits for each color.
|
||||
case D3DFMT_L16: // 16-bit luminance only.
|
||||
case D3DFMT_R16F: // 16-bit float format using 16 bits for the red channel.
|
||||
case D3DFMT_A8L8: // 16-bit using 8 bits each for alpha and luminance.
|
||||
case D3DFMT_D16: // 16-bit z-buffer bit depth.
|
||||
case D3DFMT_INDEX16: // 16-bit index buffer bit depth.
|
||||
case D3DFMT_G8R8_G8B8: // ??
|
||||
case D3DFMT_R8G8_B8G8: // ??
|
||||
case D3DFMT_UYVY: // ??
|
||||
case D3DFMT_YUY2: // ??
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
|
||||
|
||||
case D3DFMT_R8G8B8: // 24-bit RGB pixel format with 8 bits per channel.
|
||||
{
|
||||
return 24;
|
||||
}
|
||||
|
||||
case D3DFMT_R32F: // 32-bit float format using 32 bits for the red channel.
|
||||
case D3DFMT_X8L8V8U8: // 32-bit bump-map format with luminance using 8 bits for each channel.
|
||||
case D3DFMT_A2W10V10U10: // 32-bit bump-map format using 2 bits for alpha and 10 bits each for w, v, and u.
|
||||
case D3DFMT_Q8W8V8U8: // 32-bit bump-map format using 8 bits for each channel.
|
||||
case D3DFMT_V16U16: // 32-bit bump-map format using 16 bits for each channel.
|
||||
case D3DFMT_A8R8G8B8: // 32-bit ARGB pixel format with alpha, using 8 bits per channel.
|
||||
case D3DFMT_X8R8G8B8: // 32-bit RGB pixel format, where 8 bits are reserved for each color.
|
||||
case D3DFMT_A2B10G10R10: // 32-bit pixel format using 10 bits for each color and 2 bits for alpha.
|
||||
case D3DFMT_A8B8G8R8: // 32-bit ARGB pixel format with alpha, using 8 bits per channel.
|
||||
case D3DFMT_X8B8G8R8: // 32-bit RGB pixel format, where 8 bits are reserved for each color.
|
||||
case D3DFMT_G16R16: // 32-bit pixel format using 16 bits each for green and red.
|
||||
case D3DFMT_G16R16F: // 32-bit float format using 16 bits for the red channel and 16 bits for the green channel.
|
||||
case D3DFMT_A2R10G10B10: // 32-bit pixel format using 10 bits each for red, green, and blue, and 2 bits for alpha.
|
||||
case D3DFMT_D32: // 32-bit z-buffer bit depth.
|
||||
case D3DFMT_D24S8: // 32-bit z-buffer bit depth using 24 bits for the depth channel and 8 bits for the stencil channel.
|
||||
case D3DFMT_D24X8: // 32-bit z-buffer bit depth using 24 bits for the depth channel.
|
||||
case D3DFMT_D24X4S4: // 32-bit z-buffer bit depth using 24 bits for the depth channel and 4 bits for the stencil channel.
|
||||
case D3DFMT_D32F_LOCKABLE: // A lockable format where the depth value is represented as a standard IEEE floating-point number.
|
||||
case D3DFMT_D24FS8: // A non-lockable format that contains 24 bits of depth (in a 24-bit floating point format - 20e4) and 8 bits of stencil.
|
||||
case D3DFMT_D32_LOCKABLE: // A lockable 32-bit depth buffer.
|
||||
case D3DFMT_INDEX32: // 32-bit index buffer bit depth.
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
case D3DFMT_G32R32F: // 64-bit float format using 32 bits for the red channel and 32 bits for the green channel.
|
||||
case D3DFMT_Q16W16V16U16: // 64-bit bump-map format using 16 bits for each component.
|
||||
case D3DFMT_A16B16G16R16: // 64-bit pixel format using 16 bits for each component.
|
||||
case D3DFMT_A16B16G16R16F: // 64-bit float format using 16 bits for the each channel (alpha, blue, green, red).
|
||||
{
|
||||
return 64;
|
||||
}
|
||||
|
||||
case D3DFMT_A32B32G32R32F: // 128-bit float format using 32 bits for the each channel (alpha, blue, green, red).
|
||||
{
|
||||
return 128;
|
||||
}
|
||||
case D3DFMT_DXT2:
|
||||
case D3DFMT_DXT3:
|
||||
case D3DFMT_DXT4:
|
||||
case D3DFMT_DXT5: {
|
||||
return 8;
|
||||
}
|
||||
case D3DFMT_DXT1: {
|
||||
return 4;
|
||||
}
|
||||
default: // compressed formats
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DirectX::ScratchImage ImageConvertToBGRA(DirectX::ScratchImage& image, const TexEntry& entry)
|
||||
{
|
||||
if (image.GetMetadata().format == DXGI_FORMAT_B8G8R8A8_UNORM ||
|
||||
image.GetMetadata().format == DXGI_FORMAT_BC1_UNORM ||
|
||||
image.GetMetadata().format == DXGI_FORMAT_BC2_UNORM ||
|
||||
image.GetMetadata().format == DXGI_FORMAT_BC3_UNORM ||
|
||||
image.GetMetadata().format == DXGI_FORMAT_BC4_UNORM ||
|
||||
image.GetMetadata().format == DXGI_FORMAT_BC5_UNORM) {
|
||||
return std::move(image);
|
||||
}
|
||||
DirectX::ScratchImage bgra_image;
|
||||
const HRESULT hr = DirectX::Convert(
|
||||
image.GetImages(),
|
||||
image.GetImageCount(),
|
||||
image.GetMetadata(),
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
DirectX::TEX_FILTER_DEFAULT,
|
||||
DirectX::TEX_THRESHOLD_DEFAULT,
|
||||
bgra_image);
|
||||
if (FAILED(hr)) {
|
||||
Warning("ImageConvertToBGRA (%#lX%s): FAILED\n", entry.crc_hash, entry.ext.c_str());
|
||||
bgra_image = std::move(image);
|
||||
}
|
||||
image.Release();
|
||||
return bgra_image;
|
||||
}
|
||||
|
||||
DirectX::ScratchImage ImageGenerateMipMaps(DirectX::ScratchImage& image, const TexEntry& entry)
|
||||
{
|
||||
if (entry.ext == ".dds") {
|
||||
return std::move(image);
|
||||
}
|
||||
DirectX::ScratchImage mipmapped_image;
|
||||
const auto hr = DirectX::GenerateMipMaps(
|
||||
image.GetImages(),
|
||||
image.GetImageCount(),
|
||||
image.GetMetadata(),
|
||||
DirectX::TEX_FILTER_DEFAULT,
|
||||
0,
|
||||
mipmapped_image);
|
||||
if (FAILED(hr)) {
|
||||
Warning("GenerateMipMaps (%#lX%s): FAILED\n", entry.crc_hash, entry.ext.c_str());
|
||||
mipmapped_image = std::move(image);
|
||||
}
|
||||
image.Release();
|
||||
return mipmapped_image;
|
||||
}
|
||||
|
||||
DirectX::ScratchImage ImageCompress(DirectX::ScratchImage& image, const TexEntry& entry)
|
||||
{
|
||||
if (image.GetMetadata().format == DXGI_FORMAT_BC1_UNORM ||
|
||||
image.GetMetadata().format == DXGI_FORMAT_BC2_UNORM ||
|
||||
image.GetMetadata().format == DXGI_FORMAT_BC3_UNORM ||
|
||||
image.GetMetadata().format == DXGI_FORMAT_BC4_UNORM ||
|
||||
image.GetMetadata().format == DXGI_FORMAT_BC5_UNORM) {
|
||||
return std::move(image);
|
||||
}
|
||||
DirectX::ScratchImage compressed_image;
|
||||
const auto hr = DirectX::Compress(
|
||||
image.GetImages(),
|
||||
image.GetImageCount(),
|
||||
image.GetMetadata(),
|
||||
DXGI_FORMAT_BC3_UNORM,
|
||||
DirectX::TEX_COMPRESS_DEFAULT,
|
||||
DirectX::TEX_THRESHOLD_DEFAULT,
|
||||
compressed_image);
|
||||
if (FAILED(hr)) {
|
||||
Warning("ImageCompress (%#lX%s): FAILED\n", entry.crc_hash, entry.ext.c_str());
|
||||
compressed_image = std::move(image);
|
||||
}
|
||||
image.Release();
|
||||
return compressed_image;
|
||||
}
|
||||
|
||||
void ImageSave(const DirectX::ScratchImage& image, const TexEntry& entry)
|
||||
{
|
||||
const auto file_name = std::format("0x{:x}.dds", entry.crc_hash);
|
||||
const auto file_out = gmod_dll_path.parent_path() / "textures" / file_name;
|
||||
try {
|
||||
if (std::filesystem::exists(file_out)) {
|
||||
return;
|
||||
}
|
||||
if (!std::filesystem::exists(file_out.parent_path())) {
|
||||
std::filesystem::create_directory(file_out.parent_path());
|
||||
}
|
||||
const auto hr = DirectX::SaveToDDSFile(
|
||||
image.GetImages(),
|
||||
image.GetImageCount(),
|
||||
image.GetMetadata(),
|
||||
DirectX::DDS_FLAGS_NONE,
|
||||
file_out.c_str());
|
||||
if (FAILED(hr)) {
|
||||
Warning("SaveDDSImageToDisk (%#lX%s): FAILED\n", entry.crc_hash, entry.ext.c_str());
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Warning("SaveDDSImageToDisk (%#lX%s): %s\n", entry.crc_hash, entry.ext.c_str(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
DirectX::Blob ConvertToCompressedDDS(TexEntry& entry, const bool compress)
|
||||
{
|
||||
DirectX::ScratchImage image;
|
||||
HRESULT hr = 0;
|
||||
|
||||
if (entry.ext == ".dds") {
|
||||
hr = DirectX::LoadFromDDSMemory(entry.data.data(), entry.data.size(), DirectX::DDS_FLAGS_NONE, nullptr, image);
|
||||
}
|
||||
else if (entry.ext == ".tga") {
|
||||
hr = DirectX::LoadFromTGAMemory(entry.data.data(), entry.data.size(), DirectX::TGA_FLAGS_BGR, nullptr, image);
|
||||
}
|
||||
else if (entry.ext == ".hdr") {
|
||||
hr = DirectX::LoadFromHDRMemory(entry.data.data(), entry.data.size(), nullptr, image);
|
||||
}
|
||||
else {
|
||||
hr = DirectX::LoadFromWICMemory(entry.data.data(), entry.data.size(), DirectX::WIC_FLAGS_NONE, nullptr, image);
|
||||
if (image.GetMetadata().format == DXGI_FORMAT_B8G8R8X8_UNORM) {
|
||||
image.OverrideFormat(DXGI_FORMAT_B8G8R8A8_UNORM);
|
||||
}
|
||||
}
|
||||
entry.data.clear();
|
||||
if (FAILED(hr)) {
|
||||
Warning("LoadImageFromMemory (%#lX%s): FAILED\n", entry.crc_hash, entry.ext.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto bgra_image = ImageConvertToBGRA(image, entry);
|
||||
auto mipmapped_image = ImageGenerateMipMaps(bgra_image, entry);
|
||||
const auto compressed_image = compress ? ImageCompress(mipmapped_image, entry) : std::move(mipmapped_image);
|
||||
|
||||
DirectX::Blob dds_blob;
|
||||
hr = DirectX::SaveToDDSMemory(
|
||||
compressed_image.GetImages(),
|
||||
compressed_image.GetImageCount(),
|
||||
compressed_image.GetMetadata(),
|
||||
DirectX::DDS_FLAGS_NONE,
|
||||
dds_blob);
|
||||
if (FAILED(hr)) {
|
||||
Warning("SaveDDSImageToMemory (%#lX%s): FAILED\n", entry.crc_hash, entry.ext.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
ImageSave(compressed_image, entry);
|
||||
#endif
|
||||
return dds_blob;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,611 @@
|
||||
#include "Main.h"
|
||||
#include "D3D9Hooks.h"
|
||||
#include "MinHook.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
import TextureClient;
|
||||
import TextureFunction;
|
||||
import ModfileLoader; // HashCheck::Use64BitCrc
|
||||
|
||||
// Hooked vtable slot indices (frozen D3D9 ABI). Device9Ex shares Device9's prefix.
|
||||
|
||||
namespace {
|
||||
|
||||
// --- IDirect3D9 / IDirect3D9Ex --------------------------------------
|
||||
constexpr int kIDirect3D9_CreateDevice = 16;
|
||||
constexpr int kIDirect3D9Ex_CreateDeviceEx = 20;
|
||||
|
||||
// --- IDirect3DDevice9 -----------------------------------------------
|
||||
constexpr int kDevice_Release = 2;
|
||||
constexpr int kDevice_CreateTexture = 23;
|
||||
constexpr int kDevice_CreateVolumeTexture = 24;
|
||||
constexpr int kDevice_CreateCubeTexture = 25;
|
||||
constexpr int kDevice_UpdateTexture = 31;
|
||||
constexpr int kDevice_BeginScene = 41;
|
||||
constexpr int kDevice_GetTexture = 64;
|
||||
constexpr int kDevice_SetTexture = 65;
|
||||
|
||||
// --- 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);
|
||||
}
|
||||
|
||||
// ---- trampolines to the real implementations -----------------------
|
||||
using CreateDevice_t = HRESULT(STDMETHODCALLTYPE*)(IDirect3D9*, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS*, IDirect3DDevice9**);
|
||||
using CreateDeviceEx_t = HRESULT(STDMETHODCALLTYPE*)(IDirect3D9Ex*, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS*, D3DDISPLAYMODEEX*, IDirect3DDevice9Ex**);
|
||||
using DeviceRelease_t = ULONG(STDMETHODCALLTYPE*)(IDirect3DDevice9*);
|
||||
using CreateTexture_t = HRESULT(STDMETHODCALLTYPE*)(IDirect3DDevice9*, UINT, UINT, UINT, DWORD, D3DFORMAT, D3DPOOL, IDirect3DTexture9**, HANDLE*);
|
||||
using CreateVolumeTexture_t = HRESULT(STDMETHODCALLTYPE*)(IDirect3DDevice9*, UINT, UINT, UINT, UINT, DWORD, D3DFORMAT, D3DPOOL, IDirect3DVolumeTexture9**, HANDLE*);
|
||||
using CreateCubeTexture_t = HRESULT(STDMETHODCALLTYPE*)(IDirect3DDevice9*, UINT, UINT, DWORD, D3DFORMAT, D3DPOOL, IDirect3DCubeTexture9**, HANDLE*);
|
||||
using UpdateTexture_t = HRESULT(STDMETHODCALLTYPE*)(IDirect3DDevice9*, IDirect3DBaseTexture9*, IDirect3DBaseTexture9*);
|
||||
using BeginScene_t = HRESULT(STDMETHODCALLTYPE*)(IDirect3DDevice9*);
|
||||
using SetTexture_t = HRESULT(STDMETHODCALLTYPE*)(IDirect3DDevice9*, DWORD, IDirect3DBaseTexture9*);
|
||||
using GetTexture_t = HRESULT(STDMETHODCALLTYPE*)(IDirect3DDevice9*, DWORD, IDirect3DBaseTexture9**);
|
||||
using ResourceRelease_t = ULONG(STDMETHODCALLTYPE*)(IUnknown*);
|
||||
|
||||
CreateDevice_t o_CreateDevice = nullptr;
|
||||
CreateDeviceEx_t o_CreateDeviceEx = nullptr;
|
||||
DeviceRelease_t o_DeviceRelease = nullptr;
|
||||
CreateTexture_t o_CreateTexture = nullptr;
|
||||
CreateVolumeTexture_t o_CreateVolumeTexture = nullptr;
|
||||
CreateCubeTexture_t o_CreateCubeTexture = nullptr;
|
||||
UpdateTexture_t o_UpdateTexture = nullptr;
|
||||
BeginScene_t o_BeginScene = nullptr;
|
||||
SetTexture_t o_SetTexture = nullptr;
|
||||
GetTexture_t o_GetTexture = nullptr;
|
||||
ResourceRelease_t o_Tex2DRelease = nullptr;
|
||||
ResourceRelease_t o_VolumeRelease = nullptr;
|
||||
ResourceRelease_t o_CubeRelease = nullptr;
|
||||
|
||||
// A vtable is shared by every instance of its class, so each is hooked once.
|
||||
bool g_d3d9_hooks_installed = false;
|
||||
bool g_device_hooks_installed = false;
|
||||
bool g_tex2d_release_installed = false;
|
||||
bool g_volume_release_installed = false;
|
||||
bool g_cube_release_installed = false;
|
||||
|
||||
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;
|
||||
|
||||
TextureClient* ClientFor(IDirect3DDevice9* device)
|
||||
{
|
||||
std::lock_guard lk(g_devices_mutex);
|
||||
const auto it = g_devices.find(device);
|
||||
return it != g_devices.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
bool HookRaw(void* target, void* detour, void** original)
|
||||
{
|
||||
if (target == nullptr) return false;
|
||||
if (MH_CreateHook(target, detour, original) != MH_OK) {
|
||||
Warning("D3D9Hooks: MH_CreateHook failed for %p\n", target);
|
||||
return false;
|
||||
}
|
||||
if (MH_EnableHook(target) != MH_OK) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Funnels the fn-ptr <-> void* casts through one spot so call sites stay /WX-clean.
|
||||
template <typename TDetour, typename TOrig>
|
||||
bool Hook(void* target, TDetour detour, TOrig* original)
|
||||
{
|
||||
return HookRaw(target, reinterpret_cast<void*>(detour), reinterpret_cast<void**>(original));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE h_CreateDevice(IDirect3D9*, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS*, IDirect3DDevice9**);
|
||||
HRESULT STDMETHODCALLTYPE h_CreateDeviceEx(IDirect3D9Ex*, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS*, D3DDISPLAYMODEEX*, IDirect3DDevice9Ex**);
|
||||
ULONG STDMETHODCALLTYPE h_DeviceRelease(IDirect3DDevice9*);
|
||||
HRESULT STDMETHODCALLTYPE h_CreateTexture(IDirect3DDevice9*, UINT, UINT, UINT, DWORD, D3DFORMAT, D3DPOOL, IDirect3DTexture9**, HANDLE*);
|
||||
HRESULT STDMETHODCALLTYPE h_CreateVolumeTexture(IDirect3DDevice9*, UINT, UINT, UINT, UINT, DWORD, D3DFORMAT, D3DPOOL, IDirect3DVolumeTexture9**, HANDLE*);
|
||||
HRESULT STDMETHODCALLTYPE h_CreateCubeTexture(IDirect3DDevice9*, UINT, UINT, DWORD, D3DFORMAT, D3DPOOL, IDirect3DCubeTexture9**, HANDLE*);
|
||||
HRESULT STDMETHODCALLTYPE h_UpdateTexture(IDirect3DDevice9*, IDirect3DBaseTexture9*, IDirect3DBaseTexture9*);
|
||||
HRESULT STDMETHODCALLTYPE h_BeginScene(IDirect3DDevice9*);
|
||||
HRESULT STDMETHODCALLTYPE h_SetTexture(IDirect3DDevice9*, DWORD, IDirect3DBaseTexture9*);
|
||||
HRESULT STDMETHODCALLTYPE h_GetTexture(IDirect3DDevice9*, DWORD, IDirect3DBaseTexture9**);
|
||||
ULONG STDMETHODCALLTYPE h_Tex2DRelease(IUnknown*);
|
||||
ULONG STDMETHODCALLTYPE h_VolumeRelease(IUnknown*);
|
||||
ULONG STDMETHODCALLTYPE h_CubeRelease(IUnknown*);
|
||||
|
||||
void InstallDeviceHooks(IDirect3DDevice9* device)
|
||||
{
|
||||
if (g_device_hooks_installed) return;
|
||||
void** vt = GetVTable(device);
|
||||
Hook(vt[kDevice_Release], &h_DeviceRelease, reinterpret_cast<void**>(&o_DeviceRelease));
|
||||
Hook(vt[kDevice_CreateTexture], &h_CreateTexture, reinterpret_cast<void**>(&o_CreateTexture));
|
||||
Hook(vt[kDevice_CreateVolumeTexture], &h_CreateVolumeTexture, reinterpret_cast<void**>(&o_CreateVolumeTexture));
|
||||
Hook(vt[kDevice_CreateCubeTexture], &h_CreateCubeTexture, reinterpret_cast<void**>(&o_CreateCubeTexture));
|
||||
Hook(vt[kDevice_UpdateTexture], &h_UpdateTexture, reinterpret_cast<void**>(&o_UpdateTexture));
|
||||
Hook(vt[kDevice_BeginScene], &h_BeginScene, reinterpret_cast<void**>(&o_BeginScene));
|
||||
Hook(vt[kDevice_GetTexture], &h_GetTexture, reinterpret_cast<void**>(&o_GetTexture));
|
||||
Hook(vt[kDevice_SetTexture], &h_SetTexture, reinterpret_cast<void**>(&o_SetTexture));
|
||||
g_device_hooks_installed = true;
|
||||
}
|
||||
|
||||
// A texture vtable is reachable only via an instance, so hook Release lazily per kind.
|
||||
void InstallTextureReleaseHook(void* sample_texture, TexType type)
|
||||
{
|
||||
void** vt = GetVTable(sample_texture);
|
||||
switch (type) {
|
||||
case TexType::Tex2D:
|
||||
if (!g_tex2d_release_installed && Hook(vt[kResource_Release], &h_Tex2DRelease, reinterpret_cast<void**>(&o_Tex2DRelease)))
|
||||
g_tex2d_release_installed = true;
|
||||
break;
|
||||
case TexType::Volume:
|
||||
if (!g_volume_release_installed && Hook(vt[kResource_Release], &h_VolumeRelease, reinterpret_cast<void**>(&o_VolumeRelease)))
|
||||
g_volume_release_installed = true;
|
||||
break;
|
||||
case TexType::Cube:
|
||||
if (!g_cube_release_installed && Hook(vt[kResource_Release], &h_CubeRelease, reinterpret_cast<void**>(&o_CubeRelease)))
|
||||
g_cube_release_installed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDeviceCreated(IDirect3DDevice9* device)
|
||||
{
|
||||
if (device == nullptr) return;
|
||||
|
||||
TextureClient* client = nullptr;
|
||||
{
|
||||
// Lock spans check+install+create so racing callers can't double-register.
|
||||
std::lock_guard lk(g_devices_mutex);
|
||||
if (g_devices.contains(device)) return;
|
||||
InstallDeviceHooks(device);
|
||||
client = new TextureClient(device);
|
||||
g_devices.emplace(device, client);
|
||||
}
|
||||
client->Initialize();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE h_CreateDevice(IDirect3D9* self, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags,
|
||||
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);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE h_CreateDeviceEx(IDirect3D9Ex* self, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags,
|
||||
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);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE h_DeviceRelease(IDirect3DDevice9* self)
|
||||
{
|
||||
const ULONG count = o_DeviceRelease(self);
|
||||
if (g_unhooked) return count;
|
||||
if (count == 0) {
|
||||
TextureClient* client = nullptr;
|
||||
{
|
||||
std::lock_guard lk(g_devices_mutex);
|
||||
const auto it = g_devices.find(self);
|
||||
if (it != g_devices.end()) {
|
||||
client = it->second;
|
||||
g_devices.erase(it);
|
||||
}
|
||||
}
|
||||
delete client; // ~TextureClient drops all side-state
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE h_CreateTexture(IDirect3DDevice9* self, UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool,
|
||||
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))
|
||||
client->OnCreateTexture(static_cast<IDirect3DBaseTexture9*>(*ppTexture), TexType::Tex2D);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE h_CreateVolumeTexture(IDirect3DDevice9* self, UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool,
|
||||
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))
|
||||
client->OnCreateTexture(static_cast<IDirect3DBaseTexture9*>(*ppVolumeTexture), TexType::Volume);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE h_CreateCubeTexture(IDirect3DDevice9* self, UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool,
|
||||
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))
|
||||
client->OnCreateTexture(static_cast<IDirect3DBaseTexture9*>(*ppCubeTexture), TexType::Cube);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE h_UpdateTexture(IDirect3DDevice9* self, IDirect3DBaseTexture9* pSourceTexture, IDirect3DBaseTexture9* pDestinationTexture)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE h_BeginScene(IDirect3DDevice9* self)
|
||||
{
|
||||
if (g_unhooked) return o_BeginScene(self);
|
||||
if (auto* client = ClientFor(self))
|
||||
client->OnBeginScene();
|
||||
return o_BeginScene(self);
|
||||
}
|
||||
|
||||
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
|
||||
return o_SetTexture(self, Stage, bind);
|
||||
}
|
||||
|
||||
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)) {
|
||||
(*ppTexture)->Release(); // drop the fake reference the runtime handed back
|
||||
original->AddRef(); // return the original the game set, with a matching ref
|
||||
*ppTexture = original;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
void ReleaseTextureCleanup(IUnknown* self, ULONG count)
|
||||
{
|
||||
if (count != 0) return;
|
||||
if (auto* client = TextureClient::CurrentClient())
|
||||
client->OnReleaseTexture(reinterpret_cast<IDirect3DBaseTexture9*>(self));
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE h_Tex2DRelease(IUnknown* self)
|
||||
{
|
||||
const ULONG count = o_Tex2DRelease(self);
|
||||
if (g_unhooked) return count;
|
||||
ReleaseTextureCleanup(self, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE h_VolumeRelease(IUnknown* self)
|
||||
{
|
||||
const ULONG count = o_VolumeRelease(self);
|
||||
if (g_unhooked) return count;
|
||||
ReleaseTextureCleanup(self, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE h_CubeRelease(IUnknown* self)
|
||||
{
|
||||
const ULONG count = o_CubeRelease(self);
|
||||
if (g_unhooked) return count;
|
||||
ReleaseTextureCleanup(self, count);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
Hook(vt[kIDirect3D9Ex_CreateDeviceEx], &h_CreateDeviceEx, reinterpret_cast<void**>(&o_CreateDeviceEx));
|
||||
}
|
||||
g_d3d9_hooks_installed = true;
|
||||
}
|
||||
|
||||
bool RegisterExistingDevice(IDirect3DDevice9* device)
|
||||
{
|
||||
if (device == 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();
|
||||
|
||||
g_d3d9_hooks_installed = false;
|
||||
g_device_hooks_installed = false;
|
||||
g_tex2d_release_installed = false;
|
||||
g_volume_release_installed = false;
|
||||
g_cube_release_installed = false;
|
||||
|
||||
// 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.
|
||||
|
||||
namespace {
|
||||
|
||||
HashTuple HashBits(const void* bits, const int size)
|
||||
{
|
||||
const auto data = static_cast<const char*>(bits);
|
||||
const auto crc32 = TextureFunction::get_crc32(data, size);
|
||||
const auto crc64 = HashCheck::Use64BitCrc() ? TextureFunction::get_crc64(data, size) : 0;
|
||||
return {crc32, crc64};
|
||||
}
|
||||
|
||||
HashTuple HashTexture2D(const TexState* state)
|
||||
{
|
||||
const auto pTexture = static_cast<IDirect3DTexture9*>(state->real);
|
||||
IDirect3DDevice9* device = state->device;
|
||||
|
||||
IDirect3DSurface9* pOffscreenSurface = nullptr;
|
||||
IDirect3DSurface9* pResolvedSurface = nullptr;
|
||||
D3DLOCKED_RECT d3dlr;
|
||||
D3DSURFACE_DESC desc;
|
||||
|
||||
if (pTexture->GetLevelDesc(0, &desc) != D3D_OK) {
|
||||
Warning("GetTextureHash(2D) Failed: GetLevelDesc\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (desc.Pool == D3DPOOL_DEFAULT) {
|
||||
IDirect3DSurface9* pSurfaceLevel_orig = nullptr;
|
||||
if (pTexture->GetSurfaceLevel(0, &pSurfaceLevel_orig) != D3D_OK) {
|
||||
Warning("GetTextureHash(2D) Failed: GetSurfaceLevel 1 (D3DPOOL_DEFAULT)\n");
|
||||
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();
|
||||
Warning("GetTextureHash(2D) Failed: CreateRenderTarget (D3DPOOL_DEFAULT)\n");
|
||||
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 {};
|
||||
}
|
||||
pCopySource = pResolvedSurface;
|
||||
}
|
||||
|
||||
if (D3D_OK != device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &pOffscreenSurface, nullptr)) {
|
||||
if (pResolvedSurface != nullptr) pResolvedSurface->Release();
|
||||
pSurfaceLevel_orig->Release();
|
||||
Warning("GetTextureHash(2D) Failed: CreateOffscreenPlainSurface (D3DPOOL_DEFAULT)\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
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) {
|
||||
pOffscreenSurface->Release();
|
||||
Warning("GetTextureHash(2D) Failed: LockRect (D3DPOOL_DEFAULT)\n");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (pTexture->LockRect(0, &d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
Warning("GetTextureHash(2D) Failed: LockRect 1\n");
|
||||
if (pTexture->GetSurfaceLevel(0, &pResolvedSurface) != D3D_OK) {
|
||||
Warning("GetTextureHash(2D) Failed: GetSurfaceLevel\n");
|
||||
return {};
|
||||
}
|
||||
if (pResolvedSurface->LockRect(&d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
pResolvedSurface->Release();
|
||||
Warning("GetTextureHash(2D) Failed: LockRect 2\n");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const int size = (TextureFunction::GetBitsFromFormat(desc.Format) * desc.Width * desc.Height) / 8;
|
||||
const auto hash = HashBits(d3dlr.pBits, size);
|
||||
|
||||
if (pOffscreenSurface != nullptr) {
|
||||
pOffscreenSurface->UnlockRect();
|
||||
pOffscreenSurface->Release();
|
||||
}
|
||||
else if (pResolvedSurface != nullptr) {
|
||||
pResolvedSurface->UnlockRect();
|
||||
pResolvedSurface->Release();
|
||||
}
|
||||
else {
|
||||
pTexture->UnlockRect(0);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
HashTuple HashVolume(const TexState* state)
|
||||
{
|
||||
const auto pTexture = static_cast<IDirect3DVolumeTexture9*>(state->real);
|
||||
|
||||
IDirect3DVolume9* pResolvedSurface = nullptr;
|
||||
D3DLOCKED_BOX d3dlr;
|
||||
D3DVOLUME_DESC desc;
|
||||
|
||||
if (pTexture->GetLevelDesc(0, &desc) != D3D_OK) {
|
||||
Warning("GetTextureHash(Volume) Failed: GetLevelDesc\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (pTexture->LockBox(0, &d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
if (pTexture->GetVolumeLevel(0, &pResolvedSurface) != D3D_OK) {
|
||||
Warning("GetTextureHash(Volume) Failed: GetVolumeLevel\n");
|
||||
return {};
|
||||
}
|
||||
if (pResolvedSurface->LockBox(&d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
pResolvedSurface->Release();
|
||||
Warning("GetTextureHash(Volume) Failed: LockBox\n");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const int size = (TextureFunction::GetBitsFromFormat(desc.Format) * desc.Width * desc.Height * desc.Depth) / 8;
|
||||
const auto hash = HashBits(d3dlr.pBits, size);
|
||||
|
||||
if (pResolvedSurface != nullptr) {
|
||||
pResolvedSurface->UnlockBox();
|
||||
pResolvedSurface->Release();
|
||||
}
|
||||
else {
|
||||
pTexture->UnlockBox(0);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
HashTuple HashCube(const TexState* state)
|
||||
{
|
||||
const auto pTexture = static_cast<IDirect3DCubeTexture9*>(state->real);
|
||||
|
||||
IDirect3DSurface9* pResolvedSurface = nullptr;
|
||||
D3DLOCKED_RECT d3dlr;
|
||||
D3DSURFACE_DESC desc;
|
||||
|
||||
if (pTexture->GetLevelDesc(0, &desc) != D3D_OK) {
|
||||
Warning("GetTextureHash(Cube) Failed: GetLevelDesc\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (pTexture->LockRect(D3DCUBEMAP_FACE_POSITIVE_X, 0, &d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
if (pTexture->GetCubeMapSurface(D3DCUBEMAP_FACE_POSITIVE_X, 0, &pResolvedSurface) != D3D_OK) {
|
||||
Warning("GetTextureHash(Cube) Failed: GetCubeMapSurface\n");
|
||||
return {};
|
||||
}
|
||||
if (pResolvedSurface->LockRect(&d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
pResolvedSurface->Release();
|
||||
Warning("GetTextureHash(Cube) Failed: LockRect\n");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const int size = (TextureFunction::GetBitsFromFormat(desc.Format) * desc.Width * desc.Height) / 8;
|
||||
const auto hash = HashBits(d3dlr.pBits, size);
|
||||
|
||||
if (pResolvedSurface != nullptr) {
|
||||
pResolvedSurface->UnlockRect();
|
||||
pResolvedSurface->Release();
|
||||
}
|
||||
else {
|
||||
pTexture->UnlockRect(D3DCUBEMAP_FACE_POSITIVE_X, 0);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
HashTuple GetTextureHash(const TexState* state)
|
||||
{
|
||||
if (state == nullptr || state->real == nullptr || state->isFake) return {};
|
||||
switch (state->type) {
|
||||
case TexType::Tex2D: return HashTexture2D(state);
|
||||
case TexType::Volume: return HashVolume(state);
|
||||
case TexType::Cube: return HashCube(state);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "Error.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <cstdio>
|
||||
#include <process.h>
|
||||
#include <Main.h>
|
||||
|
||||
__declspec(noreturn) void FatalAssert(
|
||||
const char* expr,
|
||||
const char* file,
|
||||
unsigned int line,
|
||||
const char* function)
|
||||
{
|
||||
char module_path[MAX_PATH]{};
|
||||
if (gl_hThisInstance) {
|
||||
GetModuleFileName(gl_hThisInstance, module_path, _countof(module_path));
|
||||
}
|
||||
if (!*module_path) {
|
||||
strcpy_s(module_path, "Unknown");
|
||||
}
|
||||
const char* fmt = "Module: %s\n\nExpr: %s\n\nFile: %s\n\nFunction: %s, line %d";
|
||||
int len = snprintf(NULL, 0, fmt, module_path, expr, file, function, line);
|
||||
char* buf = new char[len + 1];
|
||||
snprintf(buf, len + 1, fmt, module_path, expr, file, function, line);
|
||||
|
||||
|
||||
MessageBox(0, buf, "uMod Assertion Failure", MB_OK | MB_ICONERROR);
|
||||
|
||||
delete[] buf;
|
||||
abort();
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
#include "Main.h"
|
||||
#include <Psapi.h>
|
||||
#include "MinHook.h"
|
||||
#include "D3D9Hooks.h"
|
||||
#include <atomic>
|
||||
|
||||
import TextureClient;
|
||||
|
||||
void ExitInstance(bool is_unloading);
|
||||
void InitInstance(HINSTANCE hModule);
|
||||
|
||||
namespace {
|
||||
|
||||
#define DISABLE_HOOK(var) \
|
||||
if (var) { MH_DisableHook(var); }
|
||||
|
||||
using Direct3DCreate9_type = IDirect3D9*(APIENTRY*)(UINT);
|
||||
using Direct3DCreate9Ex_type = HRESULT(APIENTRY*)(UINT SDKVersion, IDirect3D9Ex** ppD3D);
|
||||
using GetProcAddress_type = FARPROC(APIENTRY*)(HMODULE, LPCSTR);
|
||||
|
||||
// Pointer to original address of Direct3DCreate9
|
||||
Direct3DCreate9_type Direct3DCreate9_ret = nullptr;
|
||||
|
||||
// Pointer to original address of Direct3DCreate9
|
||||
Direct3DCreate9Ex_type Direct3DCreate9Ex_ret = nullptr;
|
||||
|
||||
GetProcAddress_type GetProcAddress_fn = nullptr;
|
||||
GetProcAddress_type GetProcAddress_ret = nullptr;
|
||||
|
||||
FILE* stdout_proxy;
|
||||
FILE* stderr_proxy;
|
||||
|
||||
HMODULE gMod_Loaded_d3d9_Module_Handle = nullptr;
|
||||
|
||||
HMODULE FindLoadedModuleByName(const char* name, bool include_this_module = false)
|
||||
{
|
||||
HMODULE hModules[1024];
|
||||
HANDLE hProcess;
|
||||
DWORD cbNeeded;
|
||||
unsigned int i;
|
||||
|
||||
// Get a handle to the current process.
|
||||
hProcess = GetCurrentProcess();
|
||||
if (!EnumProcessModules(hProcess, hModules, sizeof(hModules), &cbNeeded))
|
||||
return nullptr;
|
||||
for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
|
||||
if (hModules[i] == gl_hThisInstance && !include_this_module)
|
||||
continue;
|
||||
TCHAR szModuleName[MAX_PATH];
|
||||
ASSERT(GetModuleFileName(hModules[i], szModuleName, _countof(szModuleName)) > 0);
|
||||
const auto basename = strrchr(szModuleName, '\\');
|
||||
if (basename && _stricmp(basename + 1, name) == 0)
|
||||
return hModules[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HMODULE LoadD3d9Dll()
|
||||
{
|
||||
HMODULE found = FindLoadedModuleByName("d3d9.dll");
|
||||
if (!found) {
|
||||
char executable_path[MAX_PATH]{};
|
||||
ASSERT(GetModuleFileName(GetModuleHandle(nullptr), executable_path, _countof(executable_path)) > 0);
|
||||
|
||||
char dll_path[MAX_PATH]{};
|
||||
ASSERT(GetModuleFileName(gl_hThisInstance, dll_path, _countof(dll_path)) > 0);
|
||||
|
||||
const auto exe_path = std::filesystem::path(executable_path);
|
||||
const auto gmod_path = std::filesystem::path(dll_path);
|
||||
|
||||
if (exe_path.parent_path() != gmod_path.parent_path() || gmod_path.filename() != "d3d9.dll") {
|
||||
// Call basic LoadLibrary function; we're not in the same directory as the exe.
|
||||
gMod_Loaded_d3d9_Module_Handle = LoadLibrary("d3d9.dll");
|
||||
}
|
||||
if (!gMod_Loaded_d3d9_Module_Handle) {
|
||||
// Tried resolving d3d9.dll locally, didn't work. Try system directory
|
||||
char buffer[MAX_PATH];
|
||||
ASSERT(GetSystemDirectory(buffer, _countof(buffer)) > 0); // get the system directory, we need to open the original d3d9.dll
|
||||
|
||||
// Append dll name
|
||||
strcat_s(buffer, _countof(buffer), "\\d3d9.dll");
|
||||
gMod_Loaded_d3d9_Module_Handle = LoadLibrary(buffer);
|
||||
}
|
||||
|
||||
ASSERT(gMod_Loaded_d3d9_Module_Handle);
|
||||
|
||||
found = FindLoadedModuleByName("d3d9.dll");
|
||||
ASSERT(found && found == gMod_Loaded_d3d9_Module_Handle);
|
||||
}
|
||||
|
||||
DISABLE_HOOK(GetProcAddress_fn);
|
||||
// GetProcAddress, hooked via OnGetProcAddress
|
||||
Direct3DCreate9_ret = reinterpret_cast<Direct3DCreate9_type>(GetProcAddress(found, "Direct3DCreate9"));
|
||||
Direct3DCreate9Ex_ret = reinterpret_cast<Direct3DCreate9Ex_type>(GetProcAddress(found, "Direct3DCreate9Ex"));
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
FARPROC APIENTRY OnGetProcAddress(HMODULE hModule, LPCSTR lpProcName)
|
||||
{
|
||||
ASSERT(GetProcAddress_ret);
|
||||
if ((int)lpProcName < 0xffff)
|
||||
return GetProcAddress_ret(hModule, lpProcName); // lpProcName is ordinal offset, not string
|
||||
|
||||
if (strcmp(lpProcName, "Direct3DCreate9") == 0) {
|
||||
Direct3DCreate9_ret = reinterpret_cast<Direct3DCreate9_type>(GetProcAddress_ret(hModule, lpProcName));
|
||||
return reinterpret_cast<FARPROC>(Direct3DCreate9);
|
||||
}
|
||||
if (strcmp(lpProcName, "Direct3DCreate9Ex") == 0) {
|
||||
Direct3DCreate9Ex_ret = reinterpret_cast<Direct3DCreate9Ex_type>(GetProcAddress_ret(hModule, lpProcName));
|
||||
return reinterpret_cast<FARPROC>(Direct3DCreate9Ex);
|
||||
}
|
||||
return GetProcAddress_ret(hModule, lpProcName);
|
||||
}
|
||||
|
||||
// If the original d3d9 function is nullptr or points to gMod, load the actual d3d9 dll and redirect the addresses
|
||||
void CheckLoadD3d9Dll()
|
||||
{
|
||||
if (!(Direct3DCreate9_ret && Direct3DCreate9_ret != Direct3DCreate9)) {
|
||||
ASSERT(LoadD3d9Dll());
|
||||
ASSERT(Direct3DCreate9_ret && Direct3DCreate9_ret != Direct3DCreate9);
|
||||
}
|
||||
if (!(Direct3DCreate9Ex_ret && Direct3DCreate9Ex_ret != Direct3DCreate9Ex)) {
|
||||
ASSERT(LoadD3d9Dll());
|
||||
ASSERT(Direct3DCreate9Ex_ret && Direct3DCreate9Ex_ret != Direct3DCreate9Ex);
|
||||
}
|
||||
}
|
||||
|
||||
// There may be a sitation where more than 1 gmod is loaded; avoid recursions!
|
||||
bool creating_d3d9 = false;
|
||||
}
|
||||
|
||||
unsigned int gl_ErrorState = 0;
|
||||
HINSTANCE gl_hThisInstance = nullptr;
|
||||
|
||||
IDirect3D9* APIENTRY Direct3DCreate9(UINT SDKVersion)
|
||||
{
|
||||
Message("uMod_Direct3DCreate9: uMod %p\n", Direct3DCreate9);
|
||||
|
||||
ASSERT(!creating_d3d9);
|
||||
creating_d3d9 = true;
|
||||
|
||||
DISABLE_HOOK(GetProcAddress_fn);
|
||||
CheckLoadD3d9Dll();
|
||||
|
||||
IDirect3D9* pIDirect3D9_orig = Direct3DCreate9_ret(SDKVersion); // creating the original IDirect3D9 object
|
||||
ASSERT(pIDirect3D9_orig);
|
||||
|
||||
creating_d3d9 = false;
|
||||
|
||||
// Hook the vtable and hand the game back the real object untouched.
|
||||
InstallD3D9Hooks(pIDirect3D9_orig, false);
|
||||
return pIDirect3D9_orig;
|
||||
}
|
||||
|
||||
HRESULT APIENTRY Direct3DCreate9Ex(UINT SDKVersion, IDirect3D9Ex** ppD3D)
|
||||
{
|
||||
Message("uMod_Direct3DCreate9Ex: uMod %p\n", Direct3DCreate9Ex);
|
||||
|
||||
ASSERT(!creating_d3d9);
|
||||
creating_d3d9 = true;
|
||||
|
||||
|
||||
DISABLE_HOOK(GetProcAddress_fn);
|
||||
CheckLoadD3d9Dll();
|
||||
|
||||
IDirect3D9Ex* pIDirect3D9Ex_orig = nullptr;
|
||||
HRESULT ret = Direct3DCreate9Ex_ret(SDKVersion, &pIDirect3D9Ex_orig); // creating the original IDirect3D9 object
|
||||
|
||||
creating_d3d9 = false;
|
||||
|
||||
if (ret != S_OK)
|
||||
return ret;
|
||||
|
||||
// Hook the vtable (CreateDevice + CreateDeviceEx) and return the real object untouched.
|
||||
InstallD3D9Hooks(pIDirect3D9Ex_orig, true);
|
||||
*ppD3D = pIDirect3D9Ex_orig;
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call) {
|
||||
case DLL_PROCESS_ATTACH: {
|
||||
#ifdef _DEBUG
|
||||
wchar_t dllFilePath[512 + 1]{};
|
||||
GetModuleFileNameW(hModule, dllFilePath, 512);
|
||||
gmod_dll_path = dllFilePath;
|
||||
AllocConsole();
|
||||
SetConsoleTitleA("gMod Console");
|
||||
freopen_s(&stdout_proxy, "CONOUT$", "w", stdout);
|
||||
freopen_s(&stderr_proxy, "CONOUT$", "w", stderr);
|
||||
#endif
|
||||
InitInstance(hModule);
|
||||
break;
|
||||
}
|
||||
case DLL_PROCESS_DETACH: {
|
||||
// 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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoadModlists()
|
||||
{
|
||||
Message("Initialize: searching for modlist.txt\n");
|
||||
char gwpath[MAX_PATH]{};
|
||||
GetModuleFileName(GetModuleHandle(nullptr), gwpath, MAX_PATH); // ask for name and path of this executable
|
||||
char dllpath[MAX_PATH]{};
|
||||
GetModuleFileName(gl_hThisInstance, dllpath, MAX_PATH); // ask for name and path of this dll
|
||||
const auto exe_path = std::filesystem::path(gwpath).parent_path();
|
||||
const auto dll_path = std::filesystem::path(dllpath).parent_path();
|
||||
for (const auto& path : {exe_path, dll_path}) {
|
||||
const auto modlist = path / "modlist.txt";
|
||||
if (std::filesystem::exists(modlist)) {
|
||||
Message("Initialize: found %s\n", modlist.string().c_str());
|
||||
std::ifstream t(modlist, std::ios::binary);
|
||||
std::stringstream buffer;
|
||||
buffer << t.rdbuf();
|
||||
modlists_contents.emplace_back(modlist.string(), buffer.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitInstance(HINSTANCE hModule)
|
||||
{
|
||||
Message("InitInstance: %p\n", hModule);
|
||||
|
||||
// Store the handle to this module
|
||||
gl_hThisInstance = hModule;
|
||||
|
||||
LoadModlists();
|
||||
DisableThreadLibraryCalls(hModule); // reduce overhead
|
||||
|
||||
// d3d9.dll shouldn't be loaded at this point.
|
||||
[[maybe_unused]] const auto d3d9_loaded = FindLoadedModuleByName("d3d9.dll");
|
||||
// ASSERT(!d3d9_loaded);
|
||||
|
||||
MH_Initialize();
|
||||
|
||||
// Hook into LoadLibraryA - we'll do our hooks on the flip side
|
||||
GetProcAddress_fn = reinterpret_cast<GetProcAddress_type>(GetProcAddress);
|
||||
ASSERT(GetProcAddress_fn);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Exported entry for late injection: hand gMod an existing device so it hooks
|
||||
// the vtable and mods textures from here on.
|
||||
extern "C" __declspec(dllexport) int __cdecl SetDevice(IDirect3DDevice9* device)
|
||||
{
|
||||
if (!device) return RETURN_BAD_ARGUMENT;
|
||||
try {
|
||||
return RegisterExistingDevice(device) ? RETURN_OK : RETURN_EXISTS;
|
||||
}
|
||||
catch (...) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int __cdecl AddFile(const wchar_t* path)
|
||||
{
|
||||
if (!path) return RETURN_BAD_ARGUMENT;
|
||||
try {
|
||||
return TextureClient::AddFile(std::filesystem::path(path));
|
||||
}
|
||||
catch (...) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int __cdecl RemoveFile(const wchar_t* path)
|
||||
{
|
||||
if (!path) return RETURN_BAD_ARGUMENT;
|
||||
try {
|
||||
return TextureClient::RemoveFile(std::filesystem::path(path));
|
||||
}
|
||||
catch (...) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// nullptr first arg = return required size; paths come back in load (priority) order
|
||||
// returns paths separated by null terminator, e.g. "C:\foo.tpf\0C:\bar.zip\0\0"
|
||||
extern "C" __declspec(dllexport) int __cdecl GetFiles(wchar_t* buffer, const size_t buffer_size_chars)
|
||||
{
|
||||
try {
|
||||
const auto files = TextureClient::GetFiles();
|
||||
size_t required = 1;
|
||||
for (const auto& path : files) {
|
||||
required += path.native().size() + 1;
|
||||
}
|
||||
|
||||
if (buffer && buffer_size_chars > 0 && buffer_size_chars >= required) {
|
||||
wchar_t* out = buffer;
|
||||
for (const auto& path : files) {
|
||||
const auto& native = path.native();
|
||||
std::ranges::copy(native, out);
|
||||
out += native.size();
|
||||
*out++ = L'\0';
|
||||
}
|
||||
*out = L'\0';
|
||||
}
|
||||
return static_cast<int>(required);
|
||||
}
|
||||
catch (...) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
FreeLibrary(gMod_Loaded_d3d9_Module_Handle);
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (stdout_proxy)
|
||||
fclose(stdout_proxy);
|
||||
if (stderr_proxy)
|
||||
fclose(stderr_proxy);
|
||||
__try {
|
||||
FreeConsole();
|
||||
}
|
||||
__except (EXCEPTION_CONTINUE_EXECUTION) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
#include <uMod_Main.h>
|
||||
#include <filesystem>
|
||||
#include "gMod_FileLoader.h"
|
||||
#include "XorStreamReader.h"
|
||||
|
||||
gMod_FileLoader::gMod_FileLoader(const std::string& fileName)
|
||||
{
|
||||
file_name = std::filesystem::absolute(fileName).string();
|
||||
}
|
||||
|
||||
std::vector<TpfEntry> gMod_FileLoader::GetContents()
|
||||
{
|
||||
try {
|
||||
return file_name.ends_with(".tpf") ? GetTpfContents() : GetFileContents();
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
Message("Failed to open mod file: %s\n", file_name.c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<TpfEntry> gMod_FileLoader::GetTpfContents()
|
||||
{
|
||||
std::vector<TpfEntry> entries;
|
||||
auto xorreader = XorStreamReader(file_name);
|
||||
const auto buffer = xorreader.ReadToEnd();
|
||||
const auto zip_archive = libzippp::ZipArchive::fromBuffer(buffer.data(), buffer.size(), false, TPF_PASSWORD);
|
||||
if (!zip_archive) {
|
||||
Message("Failed to open tpf file: %s - %u bytes!", file_name.c_str(), buffer.size());
|
||||
return {};
|
||||
}
|
||||
zip_archive->setErrorHandlerCallback(
|
||||
[](const std::string& message, const std::string& strerror, int zip_error_code, int system_error_code) -> void {
|
||||
Message("GetTpfContents: %s %s %d %d\n", message.c_str(), strerror.c_str(), zip_error_code, system_error_code);
|
||||
});
|
||||
zip_archive->open();
|
||||
LoadEntries(*zip_archive, entries);
|
||||
zip_archive->close();
|
||||
libzippp::ZipArchive::free(zip_archive);
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
std::vector<TpfEntry> gMod_FileLoader::GetFileContents()
|
||||
{
|
||||
std::vector<TpfEntry> entries;
|
||||
|
||||
libzippp::ZipArchive zip_archive(file_name);
|
||||
zip_archive.open();
|
||||
LoadEntries(zip_archive, entries);
|
||||
zip_archive.close();
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
void ParseSimpleArchive(const libzippp::ZipArchive& archive, std::vector<TpfEntry>& entries)
|
||||
{
|
||||
for (const auto& entry : archive.getEntries()) {
|
||||
if (entry.isFile()) {
|
||||
//TODO: #6 - Implement regex search
|
||||
const auto dataPtr = entry.readAsBinary();
|
||||
const auto size = entry.getSize();
|
||||
auto name = entry.getName();
|
||||
|
||||
// Remove the part before the last underscore (if any)
|
||||
size_t firstIndex = name.find_last_of('_');
|
||||
while (firstIndex != std::string::npos) {
|
||||
if (firstIndex >= entry.getName().length() - 1) {
|
||||
name = entry.getName();
|
||||
}
|
||||
else {
|
||||
name = entry.getName().substr(firstIndex + 1);
|
||||
}
|
||||
|
||||
firstIndex = name.find_last_of('_');
|
||||
}
|
||||
|
||||
// Remove the file extension (if any)
|
||||
size_t lastIndex = name.find_last_of('.');
|
||||
if (lastIndex != std::string::npos) {
|
||||
name = name.substr(0, lastIndex);
|
||||
}
|
||||
|
||||
uint32_t crcHash;
|
||||
try {
|
||||
crcHash = std::stoul(name, nullptr, 16);
|
||||
}
|
||||
catch (const std::invalid_argument& e) {
|
||||
Message("Failed to parse %s as a hash", name.c_str());
|
||||
continue;
|
||||
}
|
||||
catch (const std::out_of_range& e) {
|
||||
Message("Out of range while parsing %s as a hash", name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
entries.push_back({name, entry.getName(), crcHash, dataPtr, size});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParseTexmodArchive(std::vector<std::string>& lines, libzippp::ZipArchive& archive, std::vector<TpfEntry>& entries)
|
||||
{
|
||||
for (const auto& line : lines) {
|
||||
std::istringstream iss(line);
|
||||
std::string part;
|
||||
std::vector<std::string> splits;
|
||||
|
||||
// Split the line by '|'
|
||||
while (std::getline(iss, part, '|')) {
|
||||
splits.push_back(part);
|
||||
}
|
||||
|
||||
if (splits.size() != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string addrstr = splits[0];
|
||||
std::string path = splits[1];
|
||||
|
||||
// Remove unwanted characters from the beginning of the path
|
||||
while (!path.empty() && (path[0] == '.' && (path[1] == '/' || path[1] == '\\')) || path[0] == '/' || path[0] == '\\') {
|
||||
path.erase(0, 1);
|
||||
}
|
||||
|
||||
// Remove trailing newline and carriage return characters
|
||||
const size_t endpos = path.find_last_not_of("\r\n");
|
||||
if (endpos != std::string::npos) {
|
||||
path.erase(endpos + 1);
|
||||
}
|
||||
else if (!path.empty()) {
|
||||
path.clear();
|
||||
}
|
||||
|
||||
const auto entry = archive.getEntry(path);
|
||||
if (entry.isNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!entry.isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto data_ptr = entry.readAsBinary();
|
||||
const auto size = entry.getSize();
|
||||
uint32_t crcHash;
|
||||
try {
|
||||
crcHash = std::stoul(addrstr, nullptr, 16);
|
||||
}
|
||||
catch (const std::invalid_argument& e) {
|
||||
Message("Failed to parse %s as a hash", addrstr.c_str());
|
||||
continue;
|
||||
}
|
||||
catch (const std::out_of_range& e) {
|
||||
Message("Out of range while parsing %s as a hash", addrstr.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
entries.push_back({addrstr, entry.getName(), crcHash, data_ptr, size});
|
||||
}
|
||||
}
|
||||
|
||||
void gMod_FileLoader::LoadEntries(libzippp::ZipArchive& archive, std::vector<TpfEntry>& entries)
|
||||
{
|
||||
// Iterate over the files in the zip archive
|
||||
const auto def_file = archive.getEntry("texmod.def");
|
||||
if (def_file.isNull() || !def_file.isFile()) {
|
||||
ParseSimpleArchive(archive, entries);
|
||||
}
|
||||
else {
|
||||
const auto def = def_file.readAsText();
|
||||
std::istringstream iss(def);
|
||||
std::vector<std::string> lines;
|
||||
std::string line;
|
||||
|
||||
while (std::getline(iss, line)) {
|
||||
lines.push_back(line);
|
||||
}
|
||||
|
||||
ParseTexmodArchive(lines, archive, entries);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
#include "uMod_Main.h"
|
||||
|
||||
uMod_FileHandler::uMod_FileHandler()
|
||||
{
|
||||
Message("uMod_FileHandler(): %p\n", this);
|
||||
}
|
||||
|
||||
uMod_FileHandler::~uMod_FileHandler()
|
||||
{
|
||||
Message("~uMod_FileHandler(): %p\n", this);
|
||||
if (Files != nullptr) {
|
||||
for (int i = 0; i < FieldCounter; i++) {
|
||||
delete [] Files[i];
|
||||
}
|
||||
delete [] Files;
|
||||
}
|
||||
}
|
||||
|
||||
int uMod_FileHandler::Add(TextureFileStruct* file)
|
||||
{
|
||||
Message("uMod_FileHandler::Add(%p): %p\n", file, this);
|
||||
if (gl_ErrorState & uMod_ERROR_FATAL) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
|
||||
if (file->Reference >= 0) {
|
||||
return RETURN_UPDATE_ALLREADY_ADDED;
|
||||
}
|
||||
|
||||
if (Number / FieldLength == FieldCounter) // get more memory
|
||||
{
|
||||
TextureFileStruct*** temp = nullptr;
|
||||
try { temp = new TextureFileStruct**[FieldCounter + 10]; }
|
||||
catch (...) {
|
||||
gl_ErrorState |= uMod_ERROR_MEMORY | uMod_ERROR_TEXTURE;
|
||||
return RETURN_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (int i = 0; i < FieldCounter; i++) {
|
||||
temp[i] = Files[i]; //copy to new allocated memory
|
||||
}
|
||||
|
||||
for (int i = FieldCounter; i < FieldCounter + 10; i++) {
|
||||
temp[i] = nullptr; // initialize unused parts to zero
|
||||
}
|
||||
|
||||
FieldCounter += 10;
|
||||
|
||||
delete [] Files;
|
||||
|
||||
Files = temp;
|
||||
}
|
||||
if (Number % FieldLength == 0) // maybe we need to get more memory
|
||||
{
|
||||
try {
|
||||
if (Files[Number / FieldLength] == nullptr) {
|
||||
Files[Number / FieldLength] = new TextureFileStruct*[FieldLength];
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
Files[Number / FieldLength] = nullptr;
|
||||
gl_ErrorState |= uMod_ERROR_MEMORY | uMod_ERROR_TEXTURE;
|
||||
return RETURN_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
Files[Number / FieldLength][Number % FieldLength] = file;
|
||||
file->Reference = Number++; //set the reference for a fast deleting
|
||||
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
|
||||
int uMod_FileHandler::Remove(TextureFileStruct* file)
|
||||
{
|
||||
Message("uMod_FileHandler::Remove(%p): %p\n", file, this);
|
||||
if (gl_ErrorState & uMod_ERROR_FATAL) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
const int ref = file->Reference;
|
||||
|
||||
if (ref < 0) {
|
||||
return RETURN_OK; // returning if no Reference is set
|
||||
}
|
||||
file->Reference = -1; //set reference outside of bound
|
||||
if (ref < (--Number)) //if reference is unequal to Number-1 we copy the last entry to the index "ref"
|
||||
{
|
||||
Files[ref / FieldLength][ref % FieldLength] = Files[Number / FieldLength][Number % FieldLength];
|
||||
Files[ref / FieldLength][ref % FieldLength]->Reference = ref; //set the new reference entry
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
#include "uMod_DX9_dll.h"
|
||||
|
||||
#include <array>
|
||||
#include <Windows.h>
|
||||
#include "uMod_Main.h"
|
||||
#include <Psapi.h>
|
||||
|
||||
HINSTANCE gl_hOriginalDll = nullptr;
|
||||
HINSTANCE gl_hThisInstance = nullptr;
|
||||
std::unique_ptr<uMod_TextureServer> gl_TextureServer = nullptr;
|
||||
|
||||
using Direct3DCreate9_type = IDirect3D9* (APIENTRY*)(UINT);
|
||||
using Direct3DCreate9Ex_type = HRESULT(APIENTRY*)(UINT SDKVersion, IDirect3D9Ex** ppD3D);
|
||||
|
||||
Direct3DCreate9_type Direct3DCreate9_fn; // we need to store the pointer to the original Direct3DCreate9 function after we have done a detour
|
||||
Direct3DCreate9Ex_type Direct3DCreate9Ex_fn; // we need to store the pointer to the original Direct3DCreate9 function after we have done a detour
|
||||
HHOOK gl_hHook = nullptr;
|
||||
|
||||
static FILE* stdout_proxy;
|
||||
static FILE* stderr_proxy;
|
||||
|
||||
/*
|
||||
* global variable which are linked external
|
||||
*/
|
||||
unsigned int gl_ErrorState = 0u;
|
||||
|
||||
#ifdef LOG_MESSAGE
|
||||
FILE* gl_File = nullptr;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DIRECT_INJECTION
|
||||
void Nothing() { (void)NULL; }
|
||||
#endif
|
||||
/*
|
||||
* dll entry routine, here we initialize or clean up
|
||||
*/
|
||||
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
|
||||
AllocConsole();
|
||||
SetConsoleTitleA("gMod Console");
|
||||
freopen_s(&stdout_proxy, "CONOUT$", "w", stdout);
|
||||
freopen_s(&stderr_proxy, "CONOUT$", "w", stderr);
|
||||
#endif
|
||||
InitInstance(hModule);
|
||||
break;
|
||||
}
|
||||
case DLL_PROCESS_DETACH: {
|
||||
ExitInstance();
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InitInstance(HINSTANCE hModule)
|
||||
{
|
||||
DisableThreadLibraryCalls(hModule); //reduce overhead
|
||||
gl_hThisInstance = hModule;
|
||||
|
||||
char game[MAX_PATH];
|
||||
if (HookThisProgram(game)) //ask if we need to hook this program
|
||||
{
|
||||
OpenMessage();
|
||||
Message("InitInstance: %p\n", hModule);
|
||||
std::array<char, MAX_PATH> uMod{};
|
||||
|
||||
GetModuleFileNameA(hModule, uMod.data(), MAX_PATH);
|
||||
Message("InitInstance: %s\n", uMod.data());
|
||||
gl_TextureServer = std::make_unique<uMod_TextureServer>(game, uMod.data()); //create the server which listen on the pipe and prepare the update for the texture clients
|
||||
LoadOriginalDll();
|
||||
if (gl_hOriginalDll) {
|
||||
Direct3DCreate9_fn = reinterpret_cast<Direct3DCreate9_type>(GetProcAddress(gl_hOriginalDll, "Direct3DCreate9"));
|
||||
if (Direct3DCreate9_fn != nullptr) {
|
||||
Message("Detour: Direct3DCreate9\n");
|
||||
Direct3DCreate9_fn = static_cast<Direct3DCreate9_type>(DetourFunc((BYTE*)Direct3DCreate9_fn, (BYTE*)uMod_Direct3DCreate9, 5));
|
||||
}
|
||||
|
||||
Direct3DCreate9Ex_fn = reinterpret_cast<Direct3DCreate9Ex_type>(GetProcAddress(gl_hOriginalDll, "Direct3DCreate9Ex"));
|
||||
if (Direct3DCreate9Ex_fn != nullptr) {
|
||||
Message("Detour: Direct3DCreate9Ex\n");
|
||||
Direct3DCreate9Ex_fn = static_cast<Direct3DCreate9Ex_type>(DetourFunc((BYTE*)Direct3DCreate9Ex_fn, (BYTE*)uMod_Direct3DCreate9Ex, 7));
|
||||
}
|
||||
}
|
||||
gl_TextureServer->Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
bool HasDesiredMethods(HMODULE hModule, HANDLE hProcess)
|
||||
{
|
||||
const auto d3dcreate9Addr = GetProcAddress(hModule, "Direct3DCreate9");
|
||||
if (!d3dcreate9Addr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto d3dcreate9ExAddr = GetProcAddress(hModule, "Direct3DCreate9Ex");
|
||||
if (!d3dcreate9ExAddr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsDesiredModule(HMODULE hModule, HANDLE hProcess)
|
||||
{
|
||||
TCHAR szModuleName[MAX_PATH];
|
||||
GetModuleBaseName(hProcess, hModule, szModuleName, sizeof(szModuleName) / sizeof(TCHAR));
|
||||
return strcmp(szModuleName, TEXT("d3d9.dll")) == 0;
|
||||
}
|
||||
|
||||
bool FindLoadedDll()
|
||||
{
|
||||
HMODULE hModules[1024];
|
||||
HANDLE hProcess;
|
||||
DWORD cbNeeded;
|
||||
unsigned int i;
|
||||
|
||||
// Get a handle to the current process.
|
||||
hProcess = GetCurrentProcess();
|
||||
if (EnumProcessModules(hProcess, hModules, sizeof(hModules), &cbNeeded)) {
|
||||
for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
|
||||
if (IsDesiredModule(hModules[i], hProcess)) {
|
||||
// If the module is d3d9.dll, store the handle or do your hooking here.
|
||||
gl_hOriginalDll = hModules[i];
|
||||
break;
|
||||
}
|
||||
if (HasDesiredMethods(hModules[i], hProcess)) {
|
||||
// If the module has the two specific methods, store the handle or do your hooking here.
|
||||
gl_hOriginalDll = hModules[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gl_hOriginalDll) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LoadOriginalDll()
|
||||
{
|
||||
if (FindLoadedDll()) {
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[MAX_PATH];
|
||||
GetSystemDirectory(buffer, MAX_PATH); //get the system directory, we need to open the original d3d9.dll
|
||||
|
||||
// Append dll name
|
||||
strcat_s(buffer, MAX_PATH, "\\d3d9.dll");
|
||||
|
||||
// try to load the system's d3d9.dll, if pointer empty
|
||||
if (!gl_hOriginalDll) {
|
||||
gl_hOriginalDll = LoadLibrary(buffer);
|
||||
}
|
||||
|
||||
if (!gl_hOriginalDll) {
|
||||
ExitProcess(0); // exit the hard way
|
||||
}
|
||||
}
|
||||
|
||||
void ExitInstance()
|
||||
{
|
||||
// Release the system's d3d9.dll
|
||||
if (gl_hOriginalDll != nullptr) {
|
||||
FreeLibrary(gl_hOriginalDll);
|
||||
gl_hOriginalDll = nullptr;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (stdout_proxy)
|
||||
fclose(stdout_proxy);
|
||||
if (stderr_proxy)
|
||||
fclose(stderr_proxy);
|
||||
FreeConsole();
|
||||
#endif
|
||||
CloseMessage();
|
||||
}
|
||||
|
||||
/*
|
||||
* We inject the dll into the game, thus we retour the original Direct3DCreate9 function to our MyDirect3DCreate9 function
|
||||
*/
|
||||
|
||||
IDirect3D9* APIENTRY uMod_Direct3DCreate9(UINT SDKVersion)
|
||||
{
|
||||
Message("uMod_Direct3DCreate9: original %p, uMod %p\n", Direct3DCreate9_fn, uMod_Direct3DCreate9);
|
||||
|
||||
// in the Internet are many tutorials for detouring functions and all of them will work without the following 5 marked lines
|
||||
// but somehow, for me it only works, if I retour the function and calling afterward the original function
|
||||
|
||||
// BEGIN
|
||||
|
||||
LoadOriginalDll();
|
||||
|
||||
RetourFunc((BYTE*)GetProcAddress(gl_hOriginalDll, "Direct3DCreate9"), (BYTE*)Direct3DCreate9_fn, 5);
|
||||
Direct3DCreate9_fn = (Direct3DCreate9_type)GetProcAddress(gl_hOriginalDll, "Direct3DCreate9");
|
||||
|
||||
/*
|
||||
if (Direct3DCreate9Ex_fn!=NULL)
|
||||
{
|
||||
RetourFunc((BYTE*) GetProcAddress( gl_hOriginalDll, "Direct3DCreate9Ex"), (BYTE*)Direct3DCreate9Ex_fn, 7);
|
||||
Direct3DCreate9Ex_fn = (Direct3DCreate9Ex_type) GetProcAddress( gl_hOriginalDll, "Direct3DCreate9Ex");
|
||||
}
|
||||
*/
|
||||
// END
|
||||
|
||||
IDirect3D9* pIDirect3D9_orig = nullptr;
|
||||
if (Direct3DCreate9_fn) {
|
||||
pIDirect3D9_orig = Direct3DCreate9_fn(SDKVersion); //creating the original IDirect3D9 object
|
||||
}
|
||||
else {
|
||||
return nullptr;
|
||||
}
|
||||
uMod_IDirect3D9* pIDirect3D9;
|
||||
if (pIDirect3D9_orig) {
|
||||
pIDirect3D9 = new uMod_IDirect3D9(pIDirect3D9_orig, gl_TextureServer.get()); //creating our uMod_IDirect3D9 object
|
||||
}
|
||||
|
||||
// we detour again
|
||||
Direct3DCreate9_fn = static_cast<Direct3DCreate9_type>(DetourFunc((BYTE*)Direct3DCreate9_fn, (BYTE*)uMod_Direct3DCreate9, 5));
|
||||
/*
|
||||
if (Direct3DCreate9Ex_fn!=NULL)
|
||||
{
|
||||
Direct3DCreate9Ex_fn = (Direct3DCreate9Ex_type)DetourFunc( (BYTE*) Direct3DCreate9Ex_fn, (BYTE*)uMod_Direct3DCreate9Ex,7);
|
||||
}
|
||||
*/
|
||||
return pIDirect3D9; //return our object instead of the "real one"
|
||||
}
|
||||
|
||||
HRESULT APIENTRY uMod_Direct3DCreate9Ex(UINT SDKVersion, IDirect3D9Ex** ppD3D)
|
||||
{
|
||||
Message("uMod_Direct3DCreate9Ex: original %p, uMod %p\n", Direct3DCreate9Ex_fn, uMod_Direct3DCreate9Ex);
|
||||
|
||||
// in the Internet are many tutorials for detouring functions and all of them will work without the following 5 marked lines
|
||||
// but somehow, for me it only works, if I retour the function and calling afterward the original function
|
||||
|
||||
// BEGIN
|
||||
|
||||
LoadOriginalDll();
|
||||
/*
|
||||
if (Direct3DCreate9_fn!=NULL)
|
||||
{
|
||||
RetourFunc((BYTE*) GetProcAddress( gl_hOriginalDll, "Direct3DCreate9"), (BYTE*)Direct3DCreate9_fn, 5);
|
||||
Direct3DCreate9_fn = (Direct3DCreate9_type) GetProcAddress( gl_hOriginalDll, "Direct3DCreate9");
|
||||
}
|
||||
*/
|
||||
RetourFunc((BYTE*)GetProcAddress(gl_hOriginalDll, "Direct3DCreate9Ex"), (BYTE*)Direct3DCreate9Ex_fn, 7);
|
||||
Direct3DCreate9Ex_fn = (Direct3DCreate9Ex_type)GetProcAddress(gl_hOriginalDll, "Direct3DCreate9Ex");
|
||||
// END
|
||||
|
||||
IDirect3D9Ex* pIDirect3D9Ex_orig = nullptr;
|
||||
HRESULT ret;
|
||||
if (Direct3DCreate9Ex_fn) {
|
||||
ret = Direct3DCreate9Ex_fn(SDKVersion, &pIDirect3D9Ex_orig); //creating the original IDirect3D9 object
|
||||
}
|
||||
else {
|
||||
return D3DERR_NOTAVAILABLE;
|
||||
}
|
||||
|
||||
if (ret != S_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uMod_IDirect3D9Ex* pIDirect3D9Ex;
|
||||
if (pIDirect3D9Ex_orig) {
|
||||
pIDirect3D9Ex = new uMod_IDirect3D9Ex(pIDirect3D9Ex_orig, gl_TextureServer.get()); //creating our uMod_IDirect3D9 object
|
||||
}
|
||||
|
||||
// we detour again
|
||||
/*
|
||||
if (Direct3DCreate9_fn!=NULL)
|
||||
{
|
||||
Direct3DCreate9_fn = (Direct3DCreate9_type)DetourFunc( (BYTE*) Direct3DCreate9_fn, (BYTE*)uMod_Direct3DCreate9,5);
|
||||
}
|
||||
*/
|
||||
Direct3DCreate9Ex_fn = static_cast<Direct3DCreate9Ex_type>(DetourFunc((BYTE*)Direct3DCreate9Ex_fn, (BYTE*)uMod_Direct3DCreate9Ex, 7));
|
||||
ppD3D = (IDirect3D9Ex**)&pIDirect3D9Ex; //return our object instead of the "real one"
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool HookThisProgram(char* ret)
|
||||
{
|
||||
char Game[MAX_PATH];
|
||||
GetModuleFileName(GetModuleHandle(nullptr), Game, MAX_PATH); //ask for name and path of this executable
|
||||
|
||||
// we inject directly
|
||||
strcpy(ret, Game);
|
||||
return true;
|
||||
}
|
||||
|
||||
void* DetourFunc(BYTE* src, const BYTE* dst, const int len)
|
||||
{
|
||||
auto jmp = static_cast<BYTE*>(malloc(len + 5));
|
||||
DWORD dwback = 0;
|
||||
VirtualProtect(jmp, len + 5, PAGE_EXECUTE_READWRITE, &dwback); //This is the addition needed for Windows 7 RC
|
||||
VirtualProtect(src, len, PAGE_READWRITE, &dwback);
|
||||
memcpy(jmp, src, len);
|
||||
jmp += len;
|
||||
jmp[0] = 0xE9;
|
||||
*(DWORD*)(jmp + 1) = static_cast<DWORD>(src + len - jmp) - 5;
|
||||
memset(src, 0x90, len);
|
||||
src[0] = 0xE9;
|
||||
*(DWORD*)(src + 1) = static_cast<DWORD>(dst - src) - 5;
|
||||
VirtualProtect(src, len, dwback, &dwback);
|
||||
return jmp - len;
|
||||
}
|
||||
|
||||
bool RetourFunc(BYTE* src, BYTE* restore, const int len)
|
||||
{
|
||||
DWORD dwback;
|
||||
if (!VirtualProtect(src, len, PAGE_READWRITE, &dwback)) { return false; }
|
||||
if (!memcpy(src, restore, len)) { return false; }
|
||||
restore[0] = 0xE9;
|
||||
*(DWORD*)(restore + 1) = static_cast<DWORD>(src - restore) - 5;
|
||||
if (!VirtualProtect(src, len, dwback, &dwback)) { return false; }
|
||||
return true;
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
#include "uMod_Main.h"
|
||||
|
||||
#ifndef PRE_MESSAGE
|
||||
#define PRE_MESSAGE "uMod_IDirect3D9"
|
||||
#endif
|
||||
|
||||
uMod_IDirect3D9::uMod_IDirect3D9(IDirect3D9* pOriginal, uMod_TextureServer* server)
|
||||
{
|
||||
Message(PRE_MESSAGE "::" PRE_MESSAGE "( %p, %p): %p\n", pOriginal, server, this);
|
||||
m_pIDirect3D9 = pOriginal;
|
||||
uMod_Server = server;
|
||||
}
|
||||
|
||||
uMod_IDirect3D9::~uMod_IDirect3D9()
|
||||
{
|
||||
Message(PRE_MESSAGE "::~" PRE_MESSAGE "(): %p\n", this);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9::QueryInterface(REFIID riid, void** ppvObj)
|
||||
{
|
||||
*ppvObj = nullptr;
|
||||
|
||||
// call this to increase AddRef at original object
|
||||
// and to check if such an interface is there
|
||||
|
||||
const HRESULT hRes = m_pIDirect3D9->QueryInterface(riid, ppvObj);
|
||||
|
||||
if (hRes == NOERROR) // if OK, send our "fake" address
|
||||
{
|
||||
*ppvObj = this;
|
||||
}
|
||||
|
||||
return hRes;
|
||||
}
|
||||
|
||||
ULONG __stdcall uMod_IDirect3D9::AddRef()
|
||||
{
|
||||
return m_pIDirect3D9->AddRef();
|
||||
}
|
||||
|
||||
ULONG __stdcall uMod_IDirect3D9::Release()
|
||||
{
|
||||
// call original routine
|
||||
const ULONG count = m_pIDirect3D9->Release();
|
||||
|
||||
// in case no further Ref is there, the Original Object has deleted itself
|
||||
if (count == 0) {
|
||||
delete this;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9::RegisterSoftwareDevice(void* pInitializeFunction)
|
||||
{
|
||||
return m_pIDirect3D9->RegisterSoftwareDevice(pInitializeFunction);
|
||||
}
|
||||
|
||||
UINT __stdcall uMod_IDirect3D9::GetAdapterCount()
|
||||
{
|
||||
return m_pIDirect3D9->GetAdapterCount();
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9::GetAdapterIdentifier(UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier)
|
||||
{
|
||||
return m_pIDirect3D9->GetAdapterIdentifier(Adapter, Flags, pIdentifier);
|
||||
}
|
||||
|
||||
UINT __stdcall uMod_IDirect3D9::GetAdapterModeCount(UINT Adapter, D3DFORMAT Format)
|
||||
{
|
||||
return m_pIDirect3D9->GetAdapterModeCount(Adapter, Format);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9::EnumAdapterModes(UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE* pMode)
|
||||
{
|
||||
return m_pIDirect3D9->EnumAdapterModes(Adapter, Format, Mode, pMode);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9::GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode)
|
||||
{
|
||||
return m_pIDirect3D9->GetAdapterDisplayMode(Adapter, pMode);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9::CheckDeviceType(UINT iAdapter, D3DDEVTYPE DevType, D3DFORMAT DisplayFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed)
|
||||
{
|
||||
return m_pIDirect3D9->CheckDeviceType(iAdapter, DevType, DisplayFormat, BackBufferFormat, bWindowed);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9::CheckDeviceFormat(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat)
|
||||
{
|
||||
return m_pIDirect3D9->CheckDeviceFormat(Adapter, DeviceType, AdapterFormat, Usage, RType, CheckFormat);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9::CheckDeviceMultiSampleType(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels)
|
||||
{
|
||||
return m_pIDirect3D9->CheckDeviceMultiSampleType(Adapter, DeviceType, SurfaceFormat, Windowed, MultiSampleType, pQualityLevels);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9::CheckDepthStencilMatch(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat)
|
||||
{
|
||||
return m_pIDirect3D9->CheckDepthStencilMatch(Adapter, DeviceType, AdapterFormat, RenderTargetFormat, DepthStencilFormat);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9::CheckDeviceFormatConversion(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat)
|
||||
{
|
||||
return m_pIDirect3D9->CheckDeviceFormatConversion(Adapter, DeviceType, SourceFormat, TargetFormat);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9::GetDeviceCaps(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps)
|
||||
{
|
||||
return m_pIDirect3D9->GetDeviceCaps(Adapter, DeviceType, pCaps);
|
||||
}
|
||||
|
||||
HMONITOR __stdcall uMod_IDirect3D9::GetAdapterMonitor(UINT Adapter)
|
||||
{
|
||||
return m_pIDirect3D9->GetAdapterMonitor(Adapter);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9::CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface)
|
||||
{
|
||||
Message(PRE_MESSAGE "::CreateDevice(): %p\n", this);
|
||||
// we intercept this call and provide our own "fake" Device Object
|
||||
const HRESULT hres = m_pIDirect3D9->CreateDevice(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface);
|
||||
|
||||
int count = 1;
|
||||
if (pPresentationParameters != nullptr) {
|
||||
count = pPresentationParameters->BackBufferCount;
|
||||
}
|
||||
const auto pIDirect3DDevice9 = new uMod_IDirect3DDevice9(*ppReturnedDeviceInterface, uMod_Server, count);
|
||||
|
||||
// store our pointer (the fake one) for returning it to the calling program
|
||||
*ppReturnedDeviceInterface = pIDirect3DDevice9;
|
||||
|
||||
return hres;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
#include "uMod_Main.h"
|
||||
|
||||
#define IDirect3D9 IDirect3D9Ex
|
||||
#define uMod_IDirect3D9 uMod_IDirect3D9Ex
|
||||
#define m_pIDirect3D9 m_pIDirect3D9Ex
|
||||
#define PRE_MESSAGE "uMod_IDirect3D9Ex"
|
||||
|
||||
// ReSharper disable once CppUnusedIncludeDirective
|
||||
#include "uMod_IDirect3D9.cpp"
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9Ex::CreateDeviceEx(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode,
|
||||
IDirect3DDevice9Ex** ppReturnedDeviceInterface)
|
||||
{
|
||||
Message("uMod_IDirect3D9Ex::CreateDeviceEx: %p\n", this);
|
||||
// we intercept this call and provide our own "fake" Device Object
|
||||
const HRESULT hres = m_pIDirect3D9Ex->CreateDeviceEx(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, pFullscreenDisplayMode, ppReturnedDeviceInterface);
|
||||
|
||||
int count = 1;
|
||||
if (pPresentationParameters != nullptr) {
|
||||
count = pPresentationParameters->BackBufferCount;
|
||||
}
|
||||
const auto pIDirect3DDevice9Ex = new uMod_IDirect3DDevice9Ex(*ppReturnedDeviceInterface, uMod_Server, count);
|
||||
|
||||
// store our pointer (the fake one) for returning it to the calling program
|
||||
*ppReturnedDeviceInterface = pIDirect3DDevice9Ex;
|
||||
|
||||
return hres;
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9Ex::EnumAdapterModesEx(UINT Adapter, const D3DDISPLAYMODEFILTER* pFilter, UINT Mode, D3DDISPLAYMODEEX* pMode)
|
||||
{
|
||||
return m_pIDirect3D9Ex->EnumAdapterModesEx(Adapter, pFilter, Mode, pMode);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9Ex::GetAdapterDisplayModeEx(UINT Adapter, D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation)
|
||||
{
|
||||
return m_pIDirect3D9Ex->GetAdapterDisplayModeEx(Adapter, pMode, pRotation);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3D9Ex::GetAdapterLUID(UINT Adapter, LUID* pLUID)
|
||||
{
|
||||
return m_pIDirect3D9Ex->GetAdapterLUID(Adapter, pLUID);
|
||||
}
|
||||
|
||||
UINT __stdcall uMod_IDirect3D9Ex::GetAdapterModeCountEx(UINT Adapter, const D3DDISPLAYMODEFILTER* pFilter)
|
||||
{
|
||||
return m_pIDirect3D9Ex->GetAdapterModeCountEx(Adapter, pFilter);
|
||||
}
|
||||
@@ -1,360 +0,0 @@
|
||||
#include "uMod_Main.h"
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DCubeTexture9::QueryInterface(REFIID riid, void** ppvObj)
|
||||
{
|
||||
if (riid == IID_IDirect3D9) {
|
||||
// This function should never be called with IID_IDirect3D9 by the game
|
||||
// thus this call comes from our own dll to ask for the texture type
|
||||
// 0x01000000L == uMod_IDirect3DTexture9
|
||||
// 0x01000001L == uMod_IDirect3DVolumeTexture9
|
||||
// 0x01000002L == uMod_IDirect3DCubeTexture9
|
||||
|
||||
*ppvObj = this;
|
||||
return 0x01000002L;
|
||||
}
|
||||
HRESULT hRes;
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
hRes = CrossRef_D3Dtex->m_D3Dtex->QueryInterface(riid, ppvObj);
|
||||
if (*ppvObj == CrossRef_D3Dtex->m_D3Dtex) {
|
||||
*ppvObj = this;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hRes = m_D3Dtex->QueryInterface(riid, ppvObj);
|
||||
if (*ppvObj == m_D3Dtex) {
|
||||
*ppvObj = this;
|
||||
}
|
||||
}
|
||||
return hRes;
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
ULONG APIENTRY uMod_IDirect3DCubeTexture9::AddRef()
|
||||
{
|
||||
if (FAKE) {
|
||||
return 1; //bug, this case should never happen
|
||||
}
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->AddRef();
|
||||
}
|
||||
return m_D3Dtex->AddRef();
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
ULONG APIENTRY uMod_IDirect3DCubeTexture9::Release()
|
||||
{
|
||||
Message("uMod_IDirect3DCubeTexture9::Release(): %p\n", this);
|
||||
|
||||
void* cpy;
|
||||
const long ret = m_D3Ddev->QueryInterface(IID_IDirect3DTexture9, &cpy);
|
||||
|
||||
ULONG count;
|
||||
if (FAKE) {
|
||||
UnswitchTextures(this);
|
||||
count = m_D3Dtex->Release(); //count must be zero, cause we don't call AddRef of fake_textures
|
||||
}
|
||||
else {
|
||||
if (CrossRef_D3Dtex != nullptr) //if this texture is switched with a fake texture
|
||||
{
|
||||
uMod_IDirect3DCubeTexture9* fake_texture = CrossRef_D3Dtex;
|
||||
count = fake_texture->m_D3Dtex->Release(); //release the original texture
|
||||
if (count == 0) //if texture is released we switch the textures back
|
||||
{
|
||||
UnswitchTextures(this);
|
||||
if (ret == 0x01000000L) {
|
||||
if (static_cast<uMod_IDirect3DDevice9*>(m_D3Ddev)->GetSingleCubeTexture() != fake_texture) {
|
||||
fake_texture->Release(); // we release the fake texture
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (static_cast<uMod_IDirect3DDevice9Ex*>(m_D3Ddev)->GetSingleCubeTexture() != fake_texture) {
|
||||
fake_texture->Release(); // we release the fake texture
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
count = m_D3Dtex->Release();
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) //if this texture is released, we clean up
|
||||
{
|
||||
// if this texture is the LastCreatedTexture, the next time LastCreatedTexture would be added,
|
||||
// the hash of a non existing texture would be calculated
|
||||
if (ret == 0x01000000L) {
|
||||
if (static_cast<uMod_IDirect3DDevice9*>(m_D3Ddev)->GetLastCreatedCubeTexture() == this) {
|
||||
static_cast<uMod_IDirect3DDevice9*>(m_D3Ddev)->SetLastCreatedCubeTexture(nullptr);
|
||||
}
|
||||
else {
|
||||
static_cast<uMod_IDirect3DDevice9*>(m_D3Ddev)->GetuMod_Client()->RemoveTexture(this); // remove this texture from the texture client
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (static_cast<uMod_IDirect3DDevice9Ex*>(m_D3Ddev)->GetLastCreatedCubeTexture() == this) {
|
||||
static_cast<uMod_IDirect3DDevice9Ex*>(m_D3Ddev)->SetLastCreatedCubeTexture(nullptr);
|
||||
}
|
||||
else {
|
||||
static_cast<uMod_IDirect3DDevice9Ex*>(m_D3Ddev)->GetuMod_Client()->RemoveTexture(this); // remove this texture from the texture client
|
||||
}
|
||||
}
|
||||
|
||||
delete(this);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
HRESULT APIENTRY uMod_IDirect3DCubeTexture9::GetDevice(IDirect3DDevice9** ppDevice)
|
||||
{
|
||||
*ppDevice = m_D3Ddev;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DCubeTexture9::SetPrivateData(REFGUID refguid,CONST void* pData, DWORD SizeOfData, DWORD Flags)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->SetPrivateData(refguid, pData, SizeOfData, Flags);
|
||||
}
|
||||
return m_D3Dtex->SetPrivateData(refguid, pData, SizeOfData, Flags);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DCubeTexture9::GetPrivateData(REFGUID refguid, void* pData, DWORD* pSizeOfData)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->GetPrivateData(refguid, pData, pSizeOfData);
|
||||
}
|
||||
return m_D3Dtex->GetPrivateData(refguid, pData, pSizeOfData);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DCubeTexture9::FreePrivateData(REFGUID refguid)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->FreePrivateData(refguid);
|
||||
}
|
||||
return m_D3Dtex->FreePrivateData(refguid);
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DCubeTexture9::SetPriority(DWORD PriorityNew)
|
||||
{
|
||||
return m_D3Dtex->SetPriority(PriorityNew);
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DCubeTexture9::GetPriority()
|
||||
{
|
||||
return m_D3Dtex->GetPriority();
|
||||
}
|
||||
|
||||
void APIENTRY uMod_IDirect3DCubeTexture9::PreLoad()
|
||||
{
|
||||
m_D3Dtex->PreLoad();
|
||||
}
|
||||
|
||||
D3DRESOURCETYPE APIENTRY uMod_IDirect3DCubeTexture9::GetType()
|
||||
{
|
||||
return m_D3Dtex->GetType();
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DCubeTexture9::SetLOD(DWORD LODNew)
|
||||
{
|
||||
return m_D3Dtex->SetLOD(LODNew);
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DCubeTexture9::GetLOD()
|
||||
{
|
||||
return m_D3Dtex->GetLOD();
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DCubeTexture9::GetLevelCount()
|
||||
{
|
||||
return m_D3Dtex->GetLevelCount();
|
||||
}
|
||||
|
||||
HRESULT APIENTRY uMod_IDirect3DCubeTexture9::SetAutoGenFilterType(D3DTEXTUREFILTERTYPE FilterType)
|
||||
{
|
||||
return m_D3Dtex->SetAutoGenFilterType(FilterType);
|
||||
}
|
||||
|
||||
D3DTEXTUREFILTERTYPE APIENTRY uMod_IDirect3DCubeTexture9::GetAutoGenFilterType()
|
||||
{
|
||||
return m_D3Dtex->GetAutoGenFilterType();
|
||||
}
|
||||
|
||||
void APIENTRY uMod_IDirect3DCubeTexture9::GenerateMipSubLevels()
|
||||
{
|
||||
m_D3Dtex->GenerateMipSubLevels();
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DCubeTexture9::AddDirtyRect(D3DCUBEMAP_FACES FaceType, CONST RECT* pDirtyRect)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->AddDirtyRect(FaceType, pDirtyRect);
|
||||
}
|
||||
return m_D3Dtex->AddDirtyRect(FaceType, pDirtyRect);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DCubeTexture9::GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->GetLevelDesc(Level, pDesc);
|
||||
}
|
||||
return m_D3Dtex->GetLevelDesc(Level, pDesc);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DCubeTexture9::GetCubeMapSurface(D3DCUBEMAP_FACES FaceType, UINT Level, IDirect3DSurface9** ppCubeMapSurface)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->GetCubeMapSurface(FaceType, Level, ppCubeMapSurface);
|
||||
}
|
||||
return m_D3Dtex->GetCubeMapSurface(FaceType, Level, ppCubeMapSurface);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DCubeTexture9::LockRect(D3DCUBEMAP_FACES FaceType, UINT Level, D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect, DWORD Flags)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->LockRect(FaceType, Level, pLockedRect, pRect, Flags);
|
||||
}
|
||||
return m_D3Dtex->LockRect(FaceType, Level, pLockedRect, pRect, Flags);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DCubeTexture9::UnlockRect(D3DCUBEMAP_FACES FaceType, UINT Level)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->UnlockRect(FaceType, Level);
|
||||
}
|
||||
return m_D3Dtex->UnlockRect(FaceType, Level);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int uMod_IDirect3DCubeTexture9::GetHash(MyTypeHash& hash)
|
||||
{
|
||||
hash = 0u;
|
||||
if (FAKE) {
|
||||
return RETURN_BAD_ARGUMENT;
|
||||
}
|
||||
IDirect3DCubeTexture9* pTexture = m_D3Dtex;
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
pTexture = CrossRef_D3Dtex->m_D3Dtex;
|
||||
}
|
||||
|
||||
//IDirect3DSurface9 *pOffscreenSurface = NULL;
|
||||
//IDirect3DCubeTexture9 *pOffscreenTexture = NULL;
|
||||
IDirect3DSurface9* pResolvedSurface = nullptr;
|
||||
D3DLOCKED_RECT d3dlr;
|
||||
D3DSURFACE_DESC desc;
|
||||
|
||||
if (pTexture->GetLevelDesc(0, &desc) != D3D_OK) //get the format and the size of the texture
|
||||
{
|
||||
Message("uMod_IDirect3DCubeTexture9::GetHash() Failed: GetLevelDesc \n");
|
||||
return RETURN_GetLevelDesc_FAILED;
|
||||
}
|
||||
|
||||
Message("uMod_IDirect3DCubeTexture9::GetHash() (%d %d) %d\n", desc.Width, desc.Height, desc.Format);
|
||||
|
||||
/*
|
||||
if (desc.Pool==D3DPOOL_DEFAULT) //get the raw data of the texture
|
||||
{
|
||||
//Message("uMod_IDirect3DCubeTexture9::GetHash() (D3DPOOL_DEFAULT)\n");
|
||||
|
||||
IDirect3DSurface9 *pSurfaceLevel_orig = NULL;
|
||||
if (pTexture->GetSurfaceLevel( 0, &pSurfaceLevel_orig)!=D3D_OK)
|
||||
{
|
||||
Message("uMod_IDirect3DCubeTexture9::GetHash() Failed: GetSurfaceLevel 1 (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
|
||||
if (desc.MultiSampleType != D3DMULTISAMPLE_NONE)
|
||||
{
|
||||
//Message("uMod_IDirect3DCubeTexture9::GetHash() MultiSampleType\n");
|
||||
if (D3D_OK!=m_D3Ddev->CreateRenderTarget( desc.Width, desc.Height, desc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &pResolvedSurface, NULL ))
|
||||
{
|
||||
pSurfaceLevel_orig->Release();
|
||||
Message("uMod_IDirect3DCubeTexture9::GetHash() Failed: CreateRenderTarget (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
if (D3D_OK!=m_D3Ddev->StretchRect( pSurfaceLevel_orig, NULL, pResolvedSurface, NULL, D3DTEXF_NONE ))
|
||||
{
|
||||
pSurfaceLevel_orig->Release();
|
||||
Message("uMod_IDirect3DCubeTexture9::GetHash() Failed: StretchRect (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
|
||||
pSurfaceLevel_orig = pResolvedSurface;
|
||||
}
|
||||
|
||||
|
||||
if (D3D_OK!=m_D3Ddev->CreateOffscreenPlainSurface( desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &pOffscreenSurface, NULL))
|
||||
{
|
||||
pSurfaceLevel_orig->Release();
|
||||
if (pResolvedSurface!=NULL) pResolvedSurface->Release();
|
||||
Message("uMod_IDirect3DCubeTexture9::GetHash() Failed: CreateOffscreenPlainSurface (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_TEXTURE_NOT_LOADED);
|
||||
}
|
||||
|
||||
if (D3D_OK!=m_D3Ddev->GetRenderTargetData( pSurfaceLevel_orig, pOffscreenSurface))
|
||||
{
|
||||
pSurfaceLevel_orig->Release();
|
||||
if (pResolvedSurface!=NULL) pResolvedSurface->Release();
|
||||
pOffscreenSurface->Release();
|
||||
Message("uMod_IDirect3DCubeTexture9::GetHash() Failed: GetRenderTargetData (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
pSurfaceLevel_orig->Release();
|
||||
|
||||
if (pOffscreenSurface->LockRect( &d3dlr, NULL, D3DLOCK_READONLY)!=D3D_OK)
|
||||
{
|
||||
if (pResolvedSurface!=NULL) pResolvedSurface->Release();
|
||||
pOffscreenSurface->Release();
|
||||
Message("uMod_IDirect3DCubeTexture9::GetHash() Failed: LockRect (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
}
|
||||
else
|
||||
*/
|
||||
if (pTexture->LockRect(D3DCUBEMAP_FACE_POSITIVE_X, 0, &d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
Message("uMod_IDirect3DCubeTexture9::GetHash() Failed: LockRect 1\n");
|
||||
if (pTexture->GetCubeMapSurface(D3DCUBEMAP_FACE_POSITIVE_X, 0, &pResolvedSurface) != D3D_OK) {
|
||||
Message("uMod_IDirect3DCubeTexture9::GetHash() Failed: GetSurfaceLevel\n");
|
||||
return RETURN_LockRect_FAILED;
|
||||
}
|
||||
if (pResolvedSurface->LockRect(&d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
pResolvedSurface->Release();
|
||||
Message("uMod_IDirect3DCubeTexture9::GetHash() Failed: LockRect 2\n");
|
||||
return RETURN_LockRect_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const int size = (GetBitsFromFormat(desc.Format) * desc.Width * desc.Height) / 8;
|
||||
|
||||
hash = GetCRC32(static_cast<char*>(d3dlr.pBits), size); //calculate the crc32 of the texture
|
||||
/*
|
||||
if (pOffscreenSurface!=NULL)
|
||||
{
|
||||
pOffscreenSurface->UnlockRect();
|
||||
pOffscreenSurface->Release();
|
||||
//pOffscreenTexture->Release();
|
||||
if (pResolvedSurface!=NULL) pResolvedSurface->Release();
|
||||
}
|
||||
else
|
||||
*/
|
||||
if (pResolvedSurface != nullptr) {
|
||||
pResolvedSurface->UnlockRect();
|
||||
pResolvedSurface->Release();
|
||||
}
|
||||
else {
|
||||
pTexture->UnlockRect(D3DCUBEMAP_FACE_POSITIVE_X, 0); //unlock the raw data
|
||||
}
|
||||
|
||||
Message("uMod_IDirect3DCubeTexture9::GetHash() %#lX (%d %d) %d = %d\n", hash, desc.Width, desc.Height, desc.Format, size);
|
||||
return RETURN_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,95 +0,0 @@
|
||||
#include "uMod_Main.h"
|
||||
|
||||
#define uMod_IDirect3DDevice9 uMod_IDirect3DDevice9Ex
|
||||
#define IDirect3DDevice9 IDirect3DDevice9Ex
|
||||
#define m_pIDirect3DDevice9 m_pIDirect3DDevice9Ex
|
||||
|
||||
#define RETURN_QueryInterface 0x01000001L
|
||||
#define PRE_MESSAGE "uMod_IDirect3DDevice9Ex"
|
||||
|
||||
// ReSharper disable once CppUnusedIncludeDirective
|
||||
#include "uMod_IDirect3DDevice9.cpp"
|
||||
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::CheckDeviceState(HWND hWindow)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->CheckDeviceState(hWindow);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::CheckResourceResidency(IDirect3DResource9** ppResourceArray, UINT32 NumResources)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->CheckResourceResidency(ppResourceArray, NumResources);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::ComposeRects(IDirect3DSurface9* pSource, IDirect3DSurface9* pDestination, IDirect3DVertexBuffer9* pSrcRectDescriptors, UINT NumRects, IDirect3DVertexBuffer9* pDstRectDescriptors, D3DCOMPOSERECTSOP Operation,
|
||||
INT XOffset, INT YOffset)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->ComposeRects(pSource, pDestination, pSrcRectDescriptors, NumRects, pDstRectDescriptors, Operation, XOffset, YOffset);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::CreateDepthStencilSurfaceEx(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->CreateDepthStencilSurfaceEx(Width, Height, Format, MultiSample, MultisampleQuality, Discard, ppSurface, pSharedHandle, Usage);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::CreateOffscreenPlainSurfaceEx(UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->CreateOffscreenPlainSurfaceEx(Width, Height, Format, Pool, ppSurface, pSharedHandle, Usage);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::CreateRenderTargetEx(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle, DWORD Usage)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->CreateRenderTargetEx(Width, Height, Format, MultiSample, MultisampleQuality, Lockable, ppSurface, pSharedHandle, Usage);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::GetDisplayModeEx(UINT iSwapChain, D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->GetDisplayModeEx(iSwapChain, pMode, pRotation);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::GetGPUThreadPriority(INT* pPriority)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->GetGPUThreadPriority(pPriority);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::GetMaximumFrameLatency(UINT* pMaxLatency)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->GetMaximumFrameLatency(pMaxLatency);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::PresentEx(const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion, DWORD dwFlags)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->PresentEx(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::ResetEx(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->ResetEx(pPresentationParameters, pFullscreenDisplayMode);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::SetConvolutionMonoKernel(UINT Width, UINT Height, float* RowWeights, float* ColumnWeights)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->SetConvolutionMonoKernel(Width, Height, RowWeights, ColumnWeights);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::SetGPUThreadPriority(INT pPriority)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->SetGPUThreadPriority(pPriority);
|
||||
}
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::SetMaximumFrameLatency(UINT pMaxLatency)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->SetMaximumFrameLatency(pMaxLatency);
|
||||
}
|
||||
|
||||
/*
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::TestCooperativeLevel()
|
||||
{
|
||||
return(m_pIDirect3DDevice9Ex->TestCooperativeLevel();
|
||||
}
|
||||
*/
|
||||
|
||||
HRESULT __stdcall uMod_IDirect3DDevice9Ex::WaitForVBlank(UINT SwapChainIndex)
|
||||
{
|
||||
return m_pIDirect3DDevice9Ex->WaitForVBlank(SwapChainIndex);
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
#include "uMod_Main.h"
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DTexture9::QueryInterface(REFIID riid, void** ppvObj)
|
||||
{
|
||||
if (riid == IID_IDirect3D9) {
|
||||
// This function should never be called with IID_IDirect3D9 by the game
|
||||
// thus this call comes from our own dll to ask for the texture type
|
||||
// 0x01000000L == uMod_IDirect3DTexture9
|
||||
// 0x01000001L == uMod_IDirect3DVolumeTexture9
|
||||
// 0x01000002L == uMod_IDirect3DCubeTexture9
|
||||
|
||||
*ppvObj = this;
|
||||
return 0x01000000L;
|
||||
}
|
||||
HRESULT hRes;
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
hRes = CrossRef_D3Dtex->m_D3Dtex->QueryInterface(riid, ppvObj);
|
||||
if (*ppvObj == CrossRef_D3Dtex->m_D3Dtex) {
|
||||
*ppvObj = this;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hRes = m_D3Dtex->QueryInterface(riid, ppvObj);
|
||||
if (*ppvObj == m_D3Dtex) {
|
||||
*ppvObj = this;
|
||||
}
|
||||
}
|
||||
return hRes;
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
ULONG APIENTRY uMod_IDirect3DTexture9::AddRef()
|
||||
{
|
||||
if (FAKE) {
|
||||
return 1; //bug, this case should never happen
|
||||
}
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->AddRef();
|
||||
}
|
||||
return m_D3Dtex->AddRef();
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
ULONG APIENTRY uMod_IDirect3DTexture9::Release()
|
||||
{
|
||||
Message("uMod_IDirect3DTexture9::Release(): %p\n", this);
|
||||
|
||||
void* cpy;
|
||||
const long ret = m_D3Ddev->QueryInterface(IID_IDirect3DTexture9, &cpy);
|
||||
|
||||
ULONG count;
|
||||
if (FAKE) {
|
||||
UnswitchTextures(this);
|
||||
count = m_D3Dtex->Release(); //count must be zero, cause we don't call AddRef of fake_textures
|
||||
}
|
||||
else {
|
||||
if (CrossRef_D3Dtex != nullptr) //if this texture is switched with a fake texture
|
||||
{
|
||||
uMod_IDirect3DTexture9* fake_texture = CrossRef_D3Dtex;
|
||||
count = fake_texture->m_D3Dtex->Release(); //release the original texture
|
||||
if (count == 0) //if texture is released we switch the textures back
|
||||
{
|
||||
UnswitchTextures(this);
|
||||
if (ret == 0x01000000L) {
|
||||
if (static_cast<uMod_IDirect3DDevice9*>(m_D3Ddev)->GetSingleTexture() != fake_texture) {
|
||||
fake_texture->Release(); // we release the fake texture
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (static_cast<uMod_IDirect3DDevice9Ex*>(m_D3Ddev)->GetSingleTexture() != fake_texture) {
|
||||
fake_texture->Release(); // we release the fake texture
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
count = m_D3Dtex->Release();
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) //if this texture is released, we clean up
|
||||
{
|
||||
// if this texture is the LastCreatedTexture, the next time LastCreatedTexture would be added,
|
||||
// the hash of a non existing texture would be calculated
|
||||
if (ret == 0x01000000L) {
|
||||
if (static_cast<uMod_IDirect3DDevice9*>(m_D3Ddev)->GetLastCreatedTexture() == this) {
|
||||
static_cast<uMod_IDirect3DDevice9*>(m_D3Ddev)->SetLastCreatedTexture(nullptr);
|
||||
}
|
||||
else {
|
||||
static_cast<uMod_IDirect3DDevice9*>(m_D3Ddev)->GetuMod_Client()->RemoveTexture(this); // remove this texture from the texture client
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (static_cast<uMod_IDirect3DDevice9Ex*>(m_D3Ddev)->GetLastCreatedTexture() == this) {
|
||||
static_cast<uMod_IDirect3DDevice9Ex*>(m_D3Ddev)->SetLastCreatedTexture(nullptr);
|
||||
}
|
||||
else {
|
||||
static_cast<uMod_IDirect3DDevice9Ex*>(m_D3Ddev)->GetuMod_Client()->RemoveTexture(this); // remove this texture from the texture client
|
||||
}
|
||||
}
|
||||
|
||||
delete(this);
|
||||
}
|
||||
|
||||
Message("uMod_IDirect3DTexture9::Release() end: %p\n", this);
|
||||
return count;
|
||||
}
|
||||
|
||||
HRESULT APIENTRY uMod_IDirect3DTexture9::GetDevice(IDirect3DDevice9** ppDevice)
|
||||
{
|
||||
*ppDevice = m_D3Ddev;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DTexture9::SetPrivateData(REFGUID refguid,CONST void* pData, DWORD SizeOfData, DWORD Flags)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->SetPrivateData(refguid, pData, SizeOfData, Flags);
|
||||
}
|
||||
return m_D3Dtex->SetPrivateData(refguid, pData, SizeOfData, Flags);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DTexture9::GetPrivateData(REFGUID refguid, void* pData, DWORD* pSizeOfData)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->GetPrivateData(refguid, pData, pSizeOfData);
|
||||
}
|
||||
return m_D3Dtex->GetPrivateData(refguid, pData, pSizeOfData);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DTexture9::FreePrivateData(REFGUID refguid)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->FreePrivateData(refguid);
|
||||
}
|
||||
return m_D3Dtex->FreePrivateData(refguid);
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DTexture9::SetPriority(DWORD PriorityNew)
|
||||
{
|
||||
return m_D3Dtex->SetPriority(PriorityNew);
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DTexture9::GetPriority()
|
||||
{
|
||||
return m_D3Dtex->GetPriority();
|
||||
}
|
||||
|
||||
void APIENTRY uMod_IDirect3DTexture9::PreLoad()
|
||||
{
|
||||
m_D3Dtex->PreLoad();
|
||||
}
|
||||
|
||||
D3DRESOURCETYPE APIENTRY uMod_IDirect3DTexture9::GetType()
|
||||
{
|
||||
return m_D3Dtex->GetType();
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DTexture9::SetLOD(DWORD LODNew)
|
||||
{
|
||||
return m_D3Dtex->SetLOD(LODNew);
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DTexture9::GetLOD()
|
||||
{
|
||||
return m_D3Dtex->GetLOD();
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DTexture9::GetLevelCount()
|
||||
{
|
||||
return m_D3Dtex->GetLevelCount();
|
||||
}
|
||||
|
||||
HRESULT APIENTRY uMod_IDirect3DTexture9::SetAutoGenFilterType(D3DTEXTUREFILTERTYPE FilterType)
|
||||
{
|
||||
return m_D3Dtex->SetAutoGenFilterType(FilterType);
|
||||
}
|
||||
|
||||
D3DTEXTUREFILTERTYPE APIENTRY uMod_IDirect3DTexture9::GetAutoGenFilterType()
|
||||
{
|
||||
return m_D3Dtex->GetAutoGenFilterType();
|
||||
}
|
||||
|
||||
void APIENTRY uMod_IDirect3DTexture9::GenerateMipSubLevels()
|
||||
{
|
||||
m_D3Dtex->GenerateMipSubLevels();
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DTexture9::GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->GetLevelDesc(Level, pDesc);
|
||||
}
|
||||
return m_D3Dtex->GetLevelDesc(Level, pDesc);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DTexture9::GetSurfaceLevel(UINT Level, IDirect3DSurface9** ppSurfaceLevel)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->GetSurfaceLevel(Level, ppSurfaceLevel);
|
||||
}
|
||||
return m_D3Dtex->GetSurfaceLevel(Level, ppSurfaceLevel);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DTexture9::LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect, DWORD Flags)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->LockRect(Level, pLockedRect, pRect, Flags);
|
||||
}
|
||||
return m_D3Dtex->LockRect(Level, pLockedRect, pRect, Flags);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DTexture9::UnlockRect(UINT Level)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->UnlockRect(Level);
|
||||
}
|
||||
return m_D3Dtex->UnlockRect(Level);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DTexture9::AddDirtyRect(CONST RECT* pDirtyRect)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->AddDirtyRect(pDirtyRect);
|
||||
}
|
||||
return m_D3Dtex->AddDirtyRect(pDirtyRect);
|
||||
}
|
||||
|
||||
|
||||
int uMod_IDirect3DTexture9::GetHash(MyTypeHash& hash)
|
||||
{
|
||||
hash = 0u;
|
||||
if (FAKE) {
|
||||
return RETURN_BAD_ARGUMENT;
|
||||
}
|
||||
IDirect3DTexture9* pTexture = m_D3Dtex;
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
pTexture = CrossRef_D3Dtex->m_D3Dtex;
|
||||
}
|
||||
|
||||
IDirect3DSurface9* pOffscreenSurface = nullptr;
|
||||
//IDirect3DTexture9 *pOffscreenTexture = NULL;
|
||||
IDirect3DSurface9* pResolvedSurface = nullptr;
|
||||
D3DLOCKED_RECT d3dlr;
|
||||
D3DSURFACE_DESC desc;
|
||||
|
||||
if (pTexture->GetLevelDesc(0, &desc) != D3D_OK) //get the format and the size of the texture
|
||||
{
|
||||
Message("uMod_IDirect3DTexture9::GetHash() Failed: GetLevelDesc \n");
|
||||
return RETURN_GetLevelDesc_FAILED;
|
||||
}
|
||||
|
||||
Message("uMod_IDirect3DTexture9::GetHash() (%d %d) %d\n", desc.Width, desc.Height, desc.Format);
|
||||
|
||||
|
||||
if (desc.Pool == D3DPOOL_DEFAULT) //get the raw data of the texture
|
||||
{
|
||||
//Message("uMod_IDirect3DTexture9::GetHash() (D3DPOOL_DEFAULT)\n");
|
||||
|
||||
IDirect3DSurface9* pSurfaceLevel_orig = nullptr;
|
||||
if (pTexture->GetSurfaceLevel(0, &pSurfaceLevel_orig) != D3D_OK) {
|
||||
Message("uMod_IDirect3DTexture9::GetHash() Failed: GetSurfaceLevel 1 (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED;
|
||||
}
|
||||
|
||||
if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) {
|
||||
//Message("uMod_IDirect3DTexture9::GetHash() MultiSampleType\n");
|
||||
if (D3D_OK != m_D3Ddev->CreateRenderTarget(desc.Width, desc.Height, desc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &pResolvedSurface, nullptr)) {
|
||||
pSurfaceLevel_orig->Release();
|
||||
Message("uMod_IDirect3DTexture9::GetHash() Failed: CreateRenderTarget (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED;
|
||||
}
|
||||
if (D3D_OK != m_D3Ddev->StretchRect(pSurfaceLevel_orig, nullptr, pResolvedSurface, nullptr, D3DTEXF_NONE)) {
|
||||
pSurfaceLevel_orig->Release();
|
||||
Message("uMod_IDirect3DTexture9::GetHash() Failed: StretchRect (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED;
|
||||
}
|
||||
|
||||
pSurfaceLevel_orig = pResolvedSurface;
|
||||
}
|
||||
|
||||
if (D3D_OK != m_D3Ddev->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &pOffscreenSurface, nullptr)) {
|
||||
pSurfaceLevel_orig->Release();
|
||||
if (pResolvedSurface != nullptr) {
|
||||
pResolvedSurface->Release();
|
||||
}
|
||||
Message("uMod_IDirect3DTexture9::GetHash() Failed: CreateOffscreenPlainSurface (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_TEXTURE_NOT_LOADED;
|
||||
}
|
||||
|
||||
if (D3D_OK != m_D3Ddev->GetRenderTargetData(pSurfaceLevel_orig, pOffscreenSurface)) {
|
||||
pSurfaceLevel_orig->Release();
|
||||
if (pResolvedSurface != nullptr) {
|
||||
pResolvedSurface->Release();
|
||||
}
|
||||
pOffscreenSurface->Release();
|
||||
Message("uMod_IDirect3DTexture9::GetHash() Failed: GetRenderTargetData (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED;
|
||||
}
|
||||
pSurfaceLevel_orig->Release();
|
||||
|
||||
if (pOffscreenSurface->LockRect(&d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
if (pResolvedSurface != nullptr) {
|
||||
pResolvedSurface->Release();
|
||||
}
|
||||
pOffscreenSurface->Release();
|
||||
Message("uMod_IDirect3DTexture9::GetHash() Failed: LockRect (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED;
|
||||
}
|
||||
}
|
||||
else if (pTexture->LockRect(0, &d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
Message("uMod_IDirect3DTexture9::GetHash() Failed: LockRect 1\n");
|
||||
if (pTexture->GetSurfaceLevel(0, &pResolvedSurface) != D3D_OK) {
|
||||
Message("uMod_IDirect3DTexture9::GetHash() Failed: GetSurfaceLevel\n");
|
||||
return RETURN_LockRect_FAILED;
|
||||
}
|
||||
if (pResolvedSurface->LockRect(&d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
pResolvedSurface->Release();
|
||||
Message("uMod_IDirect3DTexture9::GetHash() Failed: LockRect 2\n");
|
||||
return RETURN_LockRect_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
const int size = (GetBitsFromFormat(desc.Format) * desc.Width * desc.Height) / 8;
|
||||
|
||||
hash = GetCRC32(static_cast<char*>(d3dlr.pBits), size); //calculate the crc32 of the texture
|
||||
|
||||
if (pOffscreenSurface != nullptr) {
|
||||
pOffscreenSurface->UnlockRect();
|
||||
pOffscreenSurface->Release();
|
||||
if (pResolvedSurface != nullptr) {
|
||||
pResolvedSurface->Release();
|
||||
}
|
||||
}
|
||||
else if (pResolvedSurface != nullptr) {
|
||||
pResolvedSurface->UnlockRect();
|
||||
pResolvedSurface->Release();
|
||||
}
|
||||
else {
|
||||
pTexture->UnlockRect(0);
|
||||
}
|
||||
|
||||
Message("uMod_IDirect3DTexture9::GetHash() %#lX (%d %d) %d = %d\n", hash, desc.Width, desc.Height, desc.Format, size);
|
||||
return RETURN_OK;
|
||||
}
|
||||
@@ -1,386 +0,0 @@
|
||||
#include "uMod_Main.h"
|
||||
|
||||
HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::QueryInterface(REFIID riid, void** ppvObj)
|
||||
{
|
||||
if (riid == IID_IDirect3D9) {
|
||||
// This function should never be called with IID_IDirect3D9 by the game
|
||||
// thus this call comes from our own dll to ask for the texture type
|
||||
// 0x01000000L == uMod_IDirect3DTexture9
|
||||
// 0x01000001L == uMod_IDirect3DVolumeTexture9
|
||||
// 0x01000002L == uMod_IDirect3DCubeTexture9
|
||||
|
||||
*ppvObj = this;
|
||||
return 0x01000001L;
|
||||
}
|
||||
HRESULT hRes;
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
hRes = CrossRef_D3Dtex->m_D3Dtex->QueryInterface(riid, ppvObj);
|
||||
if (*ppvObj == CrossRef_D3Dtex->m_D3Dtex) {
|
||||
*ppvObj = this;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hRes = m_D3Dtex->QueryInterface(riid, ppvObj);
|
||||
if (*ppvObj == m_D3Dtex) {
|
||||
*ppvObj = this;
|
||||
}
|
||||
}
|
||||
return hRes;
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
ULONG APIENTRY uMod_IDirect3DVolumeTexture9::AddRef()
|
||||
{
|
||||
if (FAKE) {
|
||||
return 1; //bug, this case should never happen
|
||||
}
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->AddRef();
|
||||
}
|
||||
return m_D3Dtex->AddRef();
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
ULONG APIENTRY uMod_IDirect3DVolumeTexture9::Release()
|
||||
{
|
||||
Message("uMod_IDirect3DVolumeTexture9::Release(): %p\n", this);
|
||||
|
||||
void* cpy;
|
||||
const long ret = m_D3Ddev->QueryInterface(IID_IDirect3DTexture9, &cpy);
|
||||
|
||||
ULONG count;
|
||||
if (FAKE) {
|
||||
UnswitchTextures(this);
|
||||
count = m_D3Dtex->Release(); //count must be zero, cause we don't call AddRef of fake_textures
|
||||
}
|
||||
else {
|
||||
if (CrossRef_D3Dtex != nullptr) //if this texture is switched with a fake texture
|
||||
{
|
||||
uMod_IDirect3DVolumeTexture9* fake_texture = CrossRef_D3Dtex;
|
||||
count = fake_texture->m_D3Dtex->Release(); //release the original texture
|
||||
if (count == 0) //if texture is released we switch the textures back
|
||||
{
|
||||
UnswitchTextures(this);
|
||||
if (ret == 0x01000000L) {
|
||||
if (static_cast<uMod_IDirect3DDevice9*>(m_D3Ddev)->GetSingleVolumeTexture() != fake_texture) {
|
||||
fake_texture->Release(); // we release the fake texture
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (static_cast<uMod_IDirect3DDevice9Ex*>(m_D3Ddev)->GetSingleVolumeTexture() != fake_texture) {
|
||||
fake_texture->Release(); // we release the fake texture
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
count = m_D3Dtex->Release();
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) //if this texture is released, we clean up
|
||||
{
|
||||
// if this texture is the LastCreatedTexture, the next time LastCreatedTexture would be added,
|
||||
// the hash of a non existing texture would be calculated
|
||||
if (ret == 0x01000000L) {
|
||||
if (static_cast<uMod_IDirect3DDevice9*>(m_D3Ddev)->GetLastCreatedVolumeTexture() == this) {
|
||||
static_cast<uMod_IDirect3DDevice9*>(m_D3Ddev)->SetLastCreatedVolumeTexture(nullptr);
|
||||
}
|
||||
else {
|
||||
static_cast<uMod_IDirect3DDevice9*>(m_D3Ddev)->GetuMod_Client()->RemoveTexture(this); // remove this texture from the texture client
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (static_cast<uMod_IDirect3DDevice9Ex*>(m_D3Ddev)->GetLastCreatedVolumeTexture() == this) {
|
||||
static_cast<uMod_IDirect3DDevice9Ex*>(m_D3Ddev)->SetLastCreatedVolumeTexture(nullptr);
|
||||
}
|
||||
else {
|
||||
static_cast<uMod_IDirect3DDevice9Ex*>(m_D3Ddev)->GetuMod_Client()->RemoveTexture(this); // remove this texture from the texture client
|
||||
}
|
||||
}
|
||||
|
||||
delete(this);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::GetDevice(IDirect3DDevice9** ppDevice)
|
||||
{
|
||||
*ppDevice = m_D3Ddev;
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::SetPrivateData(REFGUID refguid,CONST void* pData, DWORD SizeOfData, DWORD Flags)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->SetPrivateData(refguid, pData, SizeOfData, Flags);
|
||||
}
|
||||
return m_D3Dtex->SetPrivateData(refguid, pData, SizeOfData, Flags);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::GetPrivateData(REFGUID refguid, void* pData, DWORD* pSizeOfData)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->GetPrivateData(refguid, pData, pSizeOfData);
|
||||
}
|
||||
return m_D3Dtex->GetPrivateData(refguid, pData, pSizeOfData);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::FreePrivateData(REFGUID refguid)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->FreePrivateData(refguid);
|
||||
}
|
||||
return m_D3Dtex->FreePrivateData(refguid);
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DVolumeTexture9::SetPriority(DWORD PriorityNew)
|
||||
{
|
||||
return m_D3Dtex->SetPriority(PriorityNew);
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DVolumeTexture9::GetPriority()
|
||||
{
|
||||
return m_D3Dtex->GetPriority();
|
||||
}
|
||||
|
||||
void APIENTRY uMod_IDirect3DVolumeTexture9::PreLoad()
|
||||
{
|
||||
m_D3Dtex->PreLoad();
|
||||
}
|
||||
|
||||
D3DRESOURCETYPE APIENTRY uMod_IDirect3DVolumeTexture9::GetType()
|
||||
{
|
||||
return m_D3Dtex->GetType();
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DVolumeTexture9::SetLOD(DWORD LODNew)
|
||||
{
|
||||
return m_D3Dtex->SetLOD(LODNew);
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DVolumeTexture9::GetLOD()
|
||||
{
|
||||
return m_D3Dtex->GetLOD();
|
||||
}
|
||||
|
||||
DWORD APIENTRY uMod_IDirect3DVolumeTexture9::GetLevelCount()
|
||||
{
|
||||
return m_D3Dtex->GetLevelCount();
|
||||
}
|
||||
|
||||
HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::SetAutoGenFilterType(D3DTEXTUREFILTERTYPE FilterType)
|
||||
{
|
||||
return m_D3Dtex->SetAutoGenFilterType(FilterType);
|
||||
}
|
||||
|
||||
D3DTEXTUREFILTERTYPE APIENTRY uMod_IDirect3DVolumeTexture9::GetAutoGenFilterType()
|
||||
{
|
||||
return m_D3Dtex->GetAutoGenFilterType();
|
||||
}
|
||||
|
||||
void APIENTRY uMod_IDirect3DVolumeTexture9::GenerateMipSubLevels()
|
||||
{
|
||||
m_D3Dtex->GenerateMipSubLevels();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::AddDirtyBox(CONST D3DBOX* pDirtyBox)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->AddDirtyBox(pDirtyBox);
|
||||
}
|
||||
return m_D3Dtex->AddDirtyBox(pDirtyBox);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::GetLevelDesc(UINT Level, D3DVOLUME_DESC* pDesc)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->GetLevelDesc(Level, pDesc);
|
||||
}
|
||||
return m_D3Dtex->GetLevelDesc(Level, pDesc);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::GetVolumeLevel(UINT Level, IDirect3DVolume9** ppVolumeLevel)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->GetVolumeLevel(Level, ppVolumeLevel);
|
||||
}
|
||||
return m_D3Dtex->GetVolumeLevel(Level, ppVolumeLevel);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::LockBox(UINT Level, D3DLOCKED_BOX* pLockedVolume, CONST D3DBOX* pBox, DWORD Flags)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->LockBox(Level, pLockedVolume, pBox, Flags);
|
||||
}
|
||||
return m_D3Dtex->LockBox(Level, pLockedVolume, pBox, Flags);
|
||||
}
|
||||
|
||||
//this function yields for the non switched texture object
|
||||
HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::UnlockBox(UINT Level)
|
||||
{
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
return CrossRef_D3Dtex->m_D3Dtex->UnlockBox(Level);
|
||||
}
|
||||
return m_D3Dtex->UnlockBox(Level);
|
||||
}
|
||||
|
||||
|
||||
int uMod_IDirect3DVolumeTexture9::GetHash(MyTypeHash& hash)
|
||||
{
|
||||
hash = 0u;
|
||||
if (FAKE) {
|
||||
return RETURN_BAD_ARGUMENT;
|
||||
}
|
||||
IDirect3DVolumeTexture9* pTexture = m_D3Dtex;
|
||||
if (CrossRef_D3Dtex != nullptr) {
|
||||
pTexture = CrossRef_D3Dtex->m_D3Dtex;
|
||||
}
|
||||
|
||||
//IDirect3DVolume9 *pOffscreenSurface = NULL;
|
||||
//IDirect3DVolumeTexture9 *pOffscreenTexture = NULL;
|
||||
IDirect3DVolume9* pResolvedSurface = nullptr;
|
||||
D3DLOCKED_BOX d3dlr;
|
||||
D3DVOLUME_DESC desc;
|
||||
|
||||
if (pTexture->GetLevelDesc(0, &desc) != D3D_OK) //get the format and the size of the texture
|
||||
{
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: GetLevelDesc \n");
|
||||
return RETURN_GetLevelDesc_FAILED;
|
||||
}
|
||||
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() (%d %d %d) %d\n", desc.Width, desc.Height, desc.Depth, desc.Format);
|
||||
|
||||
/*
|
||||
if (desc.Pool==D3DPOOL_DEFAULT) //get the raw data of the texture
|
||||
{
|
||||
//Message("uMod_IDirect3DVolumeTexture9::GetHash() (D3DPOOL_DEFAULT)\n");
|
||||
|
||||
IDirect3DSurface9 *pSurfaceLevel_orig = NULL;
|
||||
if (pTexture->GetSurfaceLevel( 0, &pSurfaceLevel_orig)!=D3D_OK)
|
||||
{
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: GetSurfaceLevel 1 (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
/*
|
||||
if (desc.MultiSampleType != D3DMULTISAMPLE_NONE)
|
||||
{
|
||||
//Message("uMod_IDirect3DVolumeTexture9::GetHash() MultiSampleType\n");
|
||||
if (D3D_OK!=m_D3Ddev->CreateRenderTarget( desc.Width, desc.Height, desc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &pResolvedSurface, NULL ))
|
||||
{
|
||||
pSurfaceLevel_orig->Release();
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: CreateRenderTarget (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
if (D3D_OK!=m_D3Ddev->StretchRect( pSurfaceLevel_orig, NULL, pResolvedSurface, NULL, D3DTEXF_NONE ))
|
||||
{
|
||||
pSurfaceLevel_orig->Release();
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: StretchRect (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
|
||||
pSurfaceLevel_orig = pResolvedSurface;
|
||||
}
|
||||
*/
|
||||
//CreateTexture(8, 8, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, (IDirect3DVolumeTexture9**) &SingleTexture, NULL)
|
||||
/*
|
||||
if (D3D_OK!=m_D3Ddev->CreateTexture( desc.Width, desc.Height, 1, 0, desc.Format, D3DPOOL_SYSTEMMEM, &pOffscreenTexture, NULL))
|
||||
{
|
||||
pSurfaceLevel_orig->Release();
|
||||
if (pResolvedSurface!=NULL) pResolvedSurface->Release();
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: CreateTexture (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_TEXTURE_NOT_LOADED);
|
||||
}
|
||||
if (pOffscreenTexture->GetSurfaceLevel( 0, &pOffscreenSurface)!=D3D_OK)
|
||||
{
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: GetSurfaceLevel 2 (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
|
||||
if (D3D_OK!=m_D3Ddev->GetRenderTargetData( pSurfaceLevel_orig, pOffscreenSurface))
|
||||
{
|
||||
pSurfaceLevel_orig->Release();
|
||||
if (pResolvedSurface!=NULL) pResolvedSurface->Release();
|
||||
pOffscreenSurface->Release();
|
||||
pOffscreenTexture->Release();
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: GetRenderTargetData (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
pSurfaceLevel_orig->Release();
|
||||
|
||||
if (pOffscreenSurface->LockRect( &d3dlr, NULL, D3DLOCK_READONLY)!=D3D_OK)
|
||||
{
|
||||
if (pResolvedSurface!=NULL) pResolvedSurface->Release();
|
||||
pOffscreenSurface->Release();
|
||||
pOffscreenTexture->Release();
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: LockRect (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
*/
|
||||
/*
|
||||
if (D3D_OK!=m_D3Ddev->CreateOffscreenPlainSurface( desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &pOffscreenSurface, NULL))
|
||||
{
|
||||
pSurfaceLevel_orig->Release();
|
||||
if (pResolvedSurface!=NULL) pResolvedSurface->Release();
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: CreateOffscreenPlainSurface (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_TEXTURE_NOT_LOADED);
|
||||
}
|
||||
|
||||
if (D3D_OK!=m_D3Ddev->GetRenderTargetData( pSurfaceLevel_orig, pOffscreenSurface))
|
||||
{
|
||||
pSurfaceLevel_orig->Release();
|
||||
if (pResolvedSurface!=NULL) pResolvedSurface->Release();
|
||||
pOffscreenSurface->Release();
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: GetRenderTargetData (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
pSurfaceLevel_orig->Release();
|
||||
|
||||
if (pOffscreenSurface->LockRect( &d3dlr, NULL, D3DLOCK_READONLY)!=D3D_OK)
|
||||
{
|
||||
if (pResolvedSurface!=NULL) pResolvedSurface->Release();
|
||||
pOffscreenSurface->Release();
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: LockRect (D3DPOOL_DEFAULT)\n");
|
||||
return RETURN_LockRect_FAILED);
|
||||
}
|
||||
}
|
||||
else
|
||||
*/
|
||||
if (pTexture->LockBox(0, &d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: LockRect 1\n");
|
||||
if (pTexture->GetVolumeLevel(0, &pResolvedSurface) != D3D_OK) {
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: GetSurfaceLevel\n");
|
||||
return RETURN_LockRect_FAILED;
|
||||
}
|
||||
if (pResolvedSurface->LockBox(&d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) {
|
||||
pResolvedSurface->Release();
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() Failed: LockRect 2\n");
|
||||
return RETURN_LockRect_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
const int size = (GetBitsFromFormat(desc.Format) * desc.Width * desc.Height * desc.Depth) / 8;
|
||||
|
||||
hash = GetCRC32(static_cast<char*>(d3dlr.pBits), size); //calculate the crc32 of the texture
|
||||
|
||||
|
||||
if (pResolvedSurface != nullptr) {
|
||||
pResolvedSurface->UnlockBox();
|
||||
pResolvedSurface->Release();
|
||||
}
|
||||
else {
|
||||
pTexture->UnlockBox(0);
|
||||
}
|
||||
|
||||
Message("uMod_IDirect3DVolumeTexture9::GetHash() %#lX (%d %d) %d = %d\n", hash, desc.Width, desc.Height, desc.Format, size);
|
||||
return RETURN_OK;
|
||||
}
|
||||
@@ -1,829 +0,0 @@
|
||||
#include "uMod_Main.h"
|
||||
|
||||
uMod_TextureClient::uMod_TextureClient(uMod_TextureServer* server, IDirect3DDevice9* device)
|
||||
{
|
||||
Message("uMod_TextureClient::uMod_TextureClient(): %p\n", this);
|
||||
Server = server;
|
||||
D3D9Device = device;
|
||||
NumberToMod = 0;
|
||||
FileToMod = nullptr;
|
||||
|
||||
if (Server != nullptr) {
|
||||
if (Server->AddClient(this, &FileToMod, &NumberToMod)) {
|
||||
Server = nullptr;
|
||||
NumberToMod = 0;
|
||||
FileToMod = nullptr;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < NumberToMod; i++) {
|
||||
FileToMod[i].NumberOfTextures = 0;
|
||||
FileToMod[i].Textures = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
Mutex = CreateMutex(nullptr, false, nullptr);
|
||||
|
||||
Update = nullptr;
|
||||
NumberOfUpdate = -1;
|
||||
FontColour = D3DCOLOR_ARGB(255, 255, 0, 0);
|
||||
TextureColour = D3DCOLOR_ARGB(255, 0, 255, 0);
|
||||
}
|
||||
|
||||
uMod_TextureClient::~uMod_TextureClient()
|
||||
{
|
||||
Message("uMod_TextureClient::~uMod_TextureClient(): %p\n", this);
|
||||
if (Mutex != nullptr) {
|
||||
CloseHandle(Mutex);
|
||||
}
|
||||
|
||||
delete [] Update;
|
||||
|
||||
if (FileToMod != nullptr) {
|
||||
for (int i = 0; i < NumberToMod; i++) {
|
||||
|
||||
delete [] FileToMod[i].Textures;
|
||||
|
||||
}
|
||||
delete [] FileToMod;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uMod_TextureClient::AddTexture(uMod_IDirect3DTexture9* pTexture)
|
||||
{
|
||||
void* cpy;
|
||||
const long ret = D3D9Device->QueryInterface(IID_IDirect3DTexture9, &cpy);
|
||||
if (ret == 0x01000000L) {
|
||||
static_cast<uMod_IDirect3DDevice9*>(D3D9Device)->SetLastCreatedTexture(nullptr); //this texture must no be added twice
|
||||
}
|
||||
else {
|
||||
static_cast<uMod_IDirect3DDevice9Ex*>(D3D9Device)->SetLastCreatedTexture(nullptr); //this texture must no be added twice
|
||||
}
|
||||
|
||||
if (pTexture->FAKE) {
|
||||
return RETURN_OK; // this is a fake texture
|
||||
}
|
||||
|
||||
Message("uMod_TextureClient::AddTexture( %p): %p (thread: %p)\n", pTexture, this, GetCurrentThreadId());
|
||||
|
||||
MyTypeHash hash;
|
||||
if (const int ret = pTexture->GetHash(hash)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
pTexture->Hash = hash;
|
||||
|
||||
if (gl_ErrorState & uMod_ERROR_FATAL) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
|
||||
OriginalTextures.Add(pTexture); // add the texture to the list of original texture
|
||||
|
||||
return LookUpToMod(pTexture); // check if this texture should be modded
|
||||
}
|
||||
|
||||
int uMod_TextureClient::AddTexture(uMod_IDirect3DVolumeTexture9* pTexture)
|
||||
{
|
||||
void* cpy;
|
||||
const long ret = D3D9Device->QueryInterface(IID_IDirect3DTexture9, &cpy);
|
||||
if (ret == 0x01000000L) {
|
||||
static_cast<uMod_IDirect3DDevice9*>(D3D9Device)->SetLastCreatedVolumeTexture(nullptr); //this texture must no be added twice
|
||||
}
|
||||
else {
|
||||
static_cast<uMod_IDirect3DDevice9Ex*>(D3D9Device)->SetLastCreatedVolumeTexture(nullptr); //this texture must no be added twice
|
||||
}
|
||||
|
||||
if (pTexture->FAKE) {
|
||||
return RETURN_OK; // this is a fake texture
|
||||
}
|
||||
|
||||
Message("uMod_TextureClient::AddTexture( Volume: %p): %p (thread: %p)\n", pTexture, this, GetCurrentThreadId());
|
||||
|
||||
MyTypeHash hash;
|
||||
if (const int ret = pTexture->GetHash(hash)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
pTexture->Hash = hash;
|
||||
|
||||
if (gl_ErrorState & uMod_ERROR_FATAL) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
|
||||
OriginalVolumeTextures.Add(pTexture); // add the texture to the list of original texture
|
||||
|
||||
return LookUpToMod(pTexture); // check if this texture should be modded
|
||||
}
|
||||
|
||||
int uMod_TextureClient::AddTexture(uMod_IDirect3DCubeTexture9* pTexture)
|
||||
{
|
||||
void* cpy;
|
||||
const long ret = D3D9Device->QueryInterface(IID_IDirect3DTexture9, &cpy);
|
||||
if (ret == 0x01000000L) {
|
||||
static_cast<uMod_IDirect3DDevice9*>(D3D9Device)->SetLastCreatedCubeTexture(nullptr); //this texture must no be added twice
|
||||
}
|
||||
else {
|
||||
static_cast<uMod_IDirect3DDevice9Ex*>(D3D9Device)->SetLastCreatedCubeTexture(nullptr); //this texture must no be added twice
|
||||
}
|
||||
|
||||
if (pTexture->FAKE) {
|
||||
return RETURN_OK; // this is a fake texture
|
||||
}
|
||||
|
||||
Message("uMod_TextureClient::AddTexture( Cube: %p): %p (thread: %p)\n", pTexture, this, GetCurrentThreadId());
|
||||
|
||||
MyTypeHash hash;
|
||||
if (const int ret = pTexture->GetHash(hash)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
pTexture->Hash = hash;
|
||||
|
||||
if (gl_ErrorState & uMod_ERROR_FATAL) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
|
||||
OriginalCubeTextures.Add(pTexture); // add the texture to the list of original texture
|
||||
|
||||
return LookUpToMod(pTexture); // check if this texture should be modded
|
||||
}
|
||||
|
||||
|
||||
|
||||
int uMod_TextureClient::RemoveTexture(uMod_IDirect3DTexture9* pTexture) // is called from a texture, if it is finally released
|
||||
{
|
||||
Message("uMod_TextureClient::RemoveTexture( %p, %#lX): %p\n", pTexture, pTexture->Hash, this);
|
||||
|
||||
if (gl_ErrorState & uMod_ERROR_FATAL) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
if (pTexture->FAKE) {
|
||||
// we need to set the corresponding FileToMod[X].pTexture to NULL, to avoid a link to a non existing texture object
|
||||
const int ref = pTexture->Reference;
|
||||
if (ref >= 0 && ref < NumberToMod) {
|
||||
for (int i = 0; i < FileToMod[ref].NumberOfTextures; i++) {
|
||||
if (FileToMod[ref].Textures[i] == pTexture) {
|
||||
FileToMod[ref].NumberOfTextures--;
|
||||
for (int j = i; j < FileToMod[ref].NumberOfTextures; j++) {
|
||||
FileToMod[ref].Textures[j] = FileToMod[ref].Textures[j + 1];
|
||||
}
|
||||
FileToMod[ref].Textures[FileToMod[ref].NumberOfTextures] = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return OriginalTextures.Remove(pTexture); //remove this texture form the list
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int uMod_TextureClient::RemoveTexture(uMod_IDirect3DVolumeTexture9* pTexture) // is called from a texture, if it is finally released
|
||||
{
|
||||
Message("uMod_TextureClient::RemoveTexture( Volume %p, %#lX): %p\n", pTexture, pTexture->Hash, this);
|
||||
|
||||
if (gl_ErrorState & uMod_ERROR_FATAL) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
if (pTexture->FAKE) {
|
||||
// we need to set the corresponding FileToMod[X].pTexture to NULL, to avoid a link to a non existing texture object
|
||||
const int ref = pTexture->Reference;
|
||||
if (ref >= 0 && ref < NumberToMod) {
|
||||
for (int i = 0; i < FileToMod[ref].NumberOfTextures; i++) {
|
||||
if (FileToMod[ref].Textures[i] == pTexture) {
|
||||
FileToMod[ref].NumberOfTextures--;
|
||||
for (int j = i; j < FileToMod[ref].NumberOfTextures; j++) {
|
||||
FileToMod[ref].Textures[j] = FileToMod[ref].Textures[j + 1];
|
||||
}
|
||||
FileToMod[ref].Textures[FileToMod[ref].NumberOfTextures] = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return OriginalVolumeTextures.Remove(pTexture); //remove this texture form the list
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int uMod_TextureClient::RemoveTexture(uMod_IDirect3DCubeTexture9* pTexture) // is called from a texture, if it is finally released
|
||||
{
|
||||
Message("uMod_TextureClient::RemoveTexture( Cube %p, %#lX): %p\n", pTexture, pTexture->Hash, this);
|
||||
|
||||
if (gl_ErrorState & uMod_ERROR_FATAL) {
|
||||
return RETURN_FATAL_ERROR;
|
||||
}
|
||||
if (pTexture->FAKE) {
|
||||
// we need to set the corresponding FileToMod[X].pTexture to NULL, to avoid a link to a non existing texture object
|
||||
const int ref = pTexture->Reference;
|
||||
if (ref >= 0 && ref < NumberToMod) {
|
||||
for (int i = 0; i < FileToMod[ref].NumberOfTextures; i++) {
|
||||
if (FileToMod[ref].Textures[i] == pTexture) {
|
||||
FileToMod[ref].NumberOfTextures--;
|
||||
for (int j = i; j < FileToMod[ref].NumberOfTextures; j++) {
|
||||
FileToMod[ref].Textures[j] = FileToMod[ref].Textures[j + 1];
|
||||
}
|
||||
FileToMod[ref].Textures[FileToMod[ref].NumberOfTextures] = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return OriginalCubeTextures.Remove(pTexture); //remove this texture form the list
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
|
||||
int uMod_TextureClient::AddUpdate(TextureFileStruct* update, int number) //client must delete the update array
|
||||
{
|
||||
Message("AddUpdate( %p, %d): %p\n", update, number, this);
|
||||
if (const int ret = LockMutex()) {
|
||||
gl_ErrorState |= uMod_ERROR_TEXTURE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
delete [] Update;
|
||||
|
||||
Update = update;
|
||||
NumberOfUpdate = number;
|
||||
return UnlockMutex();
|
||||
}
|
||||
|
||||
|
||||
int uMod_TextureClient::MergeUpdate()
|
||||
{
|
||||
if (NumberOfUpdate < 0) { return RETURN_OK; }
|
||||
if (const int ret = LockMutex()) {
|
||||
gl_ErrorState |= uMod_ERROR_TEXTURE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Message("MergeUpdate(): %p\n", this);
|
||||
|
||||
for (int i = 0; i < NumberOfUpdate; i++) {
|
||||
Update[i].NumberOfTextures = 0;
|
||||
Update[i].Textures = nullptr;
|
||||
} // this is already done, but safety comes first ^^
|
||||
|
||||
int pos_old = 0;
|
||||
int pos_new = 0;
|
||||
int* to_lookup = nullptr;
|
||||
if (NumberOfUpdate > 0) {
|
||||
to_lookup = new int[NumberOfUpdate];
|
||||
}
|
||||
int num_to_lookup = 0;
|
||||
|
||||
/*
|
||||
* FileToMod contains the old files (textures) which should replace the target textures (if they are loaded by the game)
|
||||
* Update contains the new files (textures) which should replace the target textures (if they are loaded by the game)
|
||||
*
|
||||
* Both arrays (FileToMod and Update) are sorted according to their hash values.
|
||||
*
|
||||
* First we go through both arrays linearly and
|
||||
* 1) take over the old entry if the hash is the same,
|
||||
* 2) release old fake texture (if target texture exist and is not in the Update)
|
||||
* 3) or mark newly added fake texture (if they are not in FileToMod)
|
||||
*/
|
||||
|
||||
while (pos_old < NumberToMod && pos_new < NumberOfUpdate) {
|
||||
if (FileToMod[pos_old].Hash > Update[pos_new].Hash) // this fake texture is new
|
||||
{
|
||||
to_lookup[num_to_lookup++] = pos_new++; // keep this fake texture in mind, we must search later for it through all original textures
|
||||
// we increase only the new counter by one
|
||||
}
|
||||
else if (FileToMod[pos_old].Hash < Update[pos_new].Hash) // this fake texture is not in the update
|
||||
{
|
||||
for (int i = FileToMod[pos_old].NumberOfTextures - 1; i >= 0; i--) {
|
||||
FileToMod[pos_old].Textures[i]->Release(); // we release the fake textures
|
||||
}
|
||||
|
||||
delete [] FileToMod[pos_old].Textures; // we delete the memory
|
||||
|
||||
FileToMod[pos_old].NumberOfTextures = 0;
|
||||
FileToMod[pos_old].Textures = nullptr;
|
||||
|
||||
pos_old++; // we increase only the old counter by one
|
||||
}
|
||||
else // the hash value is the same, thus this texture is in the array FileToMod as well as in the array Update
|
||||
{
|
||||
if (Update[pos_new].ForceReload) {
|
||||
if (FileToMod[pos_old].NumberOfTextures > 0) {
|
||||
Update[pos_new].Textures = new IDirect3DBaseTexture9*[FileToMod[pos_old].NumberOfTextures];
|
||||
}
|
||||
for (int i = 0; i < FileToMod[pos_old].NumberOfTextures; i++) {
|
||||
IDirect3DBaseTexture9* base_texture;
|
||||
int ret = FileToMod[pos_old].Textures[i]->QueryInterface(IID_IDirect3D9, (void**)&base_texture);
|
||||
switch (ret) {
|
||||
case 0x01000000L: {
|
||||
auto pTexture = static_cast<uMod_IDirect3DTexture9*>(FileToMod[pos_old].Textures[i]);//
|
||||
uMod_IDirect3DTexture9* pRefTexture = pTexture->CrossRef_D3Dtex;
|
||||
pTexture->Release();
|
||||
i--; //after the Release of the old fake texture FileToMod[pos_old].Textures[i] is overwritten by entries with index greater than i
|
||||
|
||||
uMod_IDirect3DTexture9* fake_Texture;
|
||||
if (int ret = LoadTexture(&(Update[pos_new]), &fake_Texture)) {
|
||||
return ret;
|
||||
}
|
||||
if (SwitchTextures(fake_Texture, pRefTexture)) {
|
||||
Message("MergeUpdate(): textures not switched %#lX\n", pRefTexture->Hash);
|
||||
fake_Texture->Release();
|
||||
}
|
||||
else {
|
||||
Update[pos_new].Textures[Update[pos_new].NumberOfTextures++] = fake_Texture;
|
||||
fake_Texture->Reference = pos_new;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x01000001L: {
|
||||
auto pTexture = static_cast<uMod_IDirect3DVolumeTexture9*>(FileToMod[pos_old].Textures[i]);//
|
||||
uMod_IDirect3DVolumeTexture9* pRefTexture = pTexture->CrossRef_D3Dtex;
|
||||
pTexture->Release();
|
||||
i--; //after the Release of the old fake texture FileToMod[pos_old].Textures[i] is overwritten by entries with index greater than i
|
||||
|
||||
uMod_IDirect3DVolumeTexture9* fake_Texture;
|
||||
if (int ret = LoadTexture(&(Update[pos_new]), &fake_Texture)) {
|
||||
return ret;
|
||||
}
|
||||
if (SwitchTextures(fake_Texture, pRefTexture)) {
|
||||
Message("MergeUpdate(): textures not switched %#lX\n", pRefTexture->Hash);
|
||||
fake_Texture->Release();
|
||||
}
|
||||
else {
|
||||
Update[pos_new].Textures[Update[pos_new].NumberOfTextures++] = fake_Texture;
|
||||
fake_Texture->Reference = pos_new;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x01000002L: {
|
||||
auto pTexture = static_cast<uMod_IDirect3DCubeTexture9*>(FileToMod[pos_old].Textures[i]);//
|
||||
uMod_IDirect3DCubeTexture9* pRefTexture = pTexture->CrossRef_D3Dtex;
|
||||
pTexture->Release();
|
||||
i--; //after the Release of the old fake texture FileToMod[pos_old].Textures[i] is overwritten by entries with index greater than i
|
||||
|
||||
uMod_IDirect3DCubeTexture9* fake_Texture;
|
||||
if (int ret = LoadTexture(&Update[pos_new], &fake_Texture)) {
|
||||
return ret;
|
||||
}
|
||||
if (SwitchTextures(fake_Texture, pRefTexture)) {
|
||||
Message("MergeUpdate(): textures not switched %#lX\n", pRefTexture->Hash);
|
||||
fake_Texture->Release();
|
||||
}
|
||||
else {
|
||||
Update[pos_new].Textures[Update[pos_new].NumberOfTextures++] = fake_Texture;
|
||||
fake_Texture->Reference = pos_new;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break; // this is no fake texture and QueryInterface failed, because IDirect3DBaseTexture9 object cannot be a IDirect3D9 object ;)
|
||||
}
|
||||
}
|
||||
}
|
||||
else // the texture might be loaded or not
|
||||
{
|
||||
Update[pos_new].NumberOfTextures = FileToMod[pos_old].NumberOfTextures;
|
||||
Update[pos_new].Textures = FileToMod[pos_old].Textures;
|
||||
FileToMod[pos_old].NumberOfTextures = 0;
|
||||
FileToMod[pos_old].Textures = nullptr;
|
||||
}
|
||||
// we increase both counters by one
|
||||
pos_old++;
|
||||
pos_new++;
|
||||
}
|
||||
}
|
||||
|
||||
while (pos_old < NumberToMod) //this fake textures are not in the Update
|
||||
{
|
||||
for (int i = FileToMod[pos_old].NumberOfTextures - 1; i >= 0; i--) {
|
||||
FileToMod[pos_old].Textures[i]->Release(); // we release the fake textures
|
||||
}
|
||||
//for (int i=0; i<FileToMod[pos_old].NumberOfTextures; i++) FileToMod[pos_old].Textures[i]->Release(); // we release the fake textures
|
||||
|
||||
delete [] FileToMod[pos_old].Textures; // we delete the memory
|
||||
|
||||
FileToMod[pos_old].Textures = nullptr;
|
||||
pos_old++;
|
||||
}
|
||||
while (pos_new < NumberOfUpdate) //this fake textures are newly added
|
||||
{
|
||||
to_lookup[num_to_lookup++] = pos_new++; //keep this fake texture in mind, we must search later for it through all original textures
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* if (num_to_lookup>0) we need to look through all original textures
|
||||
* because there were newly added textures and we don't know
|
||||
* if the corresponding target textures are loaded by the game or not.
|
||||
*
|
||||
* Note: to_lookup[num_to_lookup++] = pos_new++; is in ascending order,
|
||||
* thus Update[to_lookup[pos]].Hash is also sorted ascending!
|
||||
*/
|
||||
/*
|
||||
uMod_IDirect3DTexture9 *single_texture = ((uMod_IDirect3DDevice9*)D3D9Device)->GetSingleTexture();
|
||||
if (num_to_lookup>0)
|
||||
{
|
||||
int num = OriginalTextures.GetNumber();
|
||||
for (int i=0; i<num; i++)
|
||||
if (OriginalTextures[i]->CrossRef_D3Dtex==NULL || OriginalTextures[i]->CrossRef_D3Dtex==single_texture)
|
||||
// We need look only for textures, that are not switched or switched with the single_texture.
|
||||
// The single_texture is a special texture, which you can toggle through all original texture, if save single texture is turned on.
|
||||
{
|
||||
MyTypeHash hash = OriginalTextures[i]->Hash;
|
||||
|
||||
if (hash<Update[to_lookup[0]].Hash || hash>Update[to_lookup[num_to_lookup-1]].Hash) continue;
|
||||
|
||||
int index = -1;
|
||||
int pos = num_to_lookup/2;
|
||||
int begin = 0;
|
||||
int end = num_to_lookup-1;
|
||||
|
||||
// We look in the middle of the interval and each step we halve the interval,
|
||||
// unless we find the texture or the size of the interval is less than 3.
|
||||
// Note: contradicting to normal C-code here the interval includes the index "begin" and "end"!
|
||||
while (begin+1<end) // as long as the interval is longer than two
|
||||
{
|
||||
if (hash > Update[to_lookup[pos]].Hash) // the new interval is the right half of the actual interval
|
||||
{
|
||||
begin = pos+1; // the new interval does not contain the index "pos"
|
||||
pos = (begin + end)/2; // set "pos" somewhere inside the new intervall
|
||||
}
|
||||
else if (hash < Update[to_lookup[pos]].Hash) // the new interval is the left half of the actual interval
|
||||
{
|
||||
end = pos-1; // the new interval does not contain the index "pos"
|
||||
pos = (begin + end)/2; // set "pos" somewhere inside the new intervall
|
||||
}
|
||||
else {index = to_lookup[pos]; break;} // we hit the correct hash
|
||||
}
|
||||
if (index<0) // if we did not find the hash, it might be in the last interval
|
||||
{
|
||||
for (int i=begin; i<=end; i++) if (Update[to_lookup[i]].Hash==hash) index = to_lookup[i];
|
||||
}
|
||||
|
||||
if (index>=0) // target texture is loaded by the game
|
||||
{
|
||||
if (OriginalTextures[i]->CrossRef_D3Dtex!=NULL) UnswitchTextures(OriginalTextures[i]); // this texture was switched with the single texture
|
||||
|
||||
uMod_IDirect3DTexture9 *fake_Texture;
|
||||
if (int ret = LoadTexture( & (Update[index]), &fake_Texture)) return ret;
|
||||
if (SwitchTextures( fake_Texture, OriginalTextures[i]))
|
||||
{
|
||||
Message("uMod_TextureClient::LookUpToMod(): textures not switched %#lX\n", FileToMod[index].Hash);
|
||||
fake_Texture->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
IDirect3DBaseTexture9 **temp = new IDirect3DBaseTexture9*[Update[index].NumberOfTextures+1];
|
||||
for (int j=0; j<Update[index].NumberOfTextures; j++) temp[j] = Update[index].Textures[j];
|
||||
|
||||
if (Update[index].Textures!=NULL) delete [] Update[index].Textures;
|
||||
Update[index].Textures = temp;
|
||||
|
||||
Update[index].Textures[Update[index].NumberOfTextures++] = fake_Texture;
|
||||
fake_Texture->Reference = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
//for (int i=0; i<NumberToMod; i++) if (FileToMod[i].Textures!=NULL) delete [] FileToMod[i].Textures;
|
||||
delete [] FileToMod;
|
||||
|
||||
|
||||
FileToMod = Update;
|
||||
NumberToMod = NumberOfUpdate;
|
||||
|
||||
NumberOfUpdate = -1;
|
||||
Update = nullptr;
|
||||
|
||||
|
||||
if (num_to_lookup > 0) {
|
||||
uMod_IDirect3DTexture9* single_texture;
|
||||
void* cpy;
|
||||
const long ret = D3D9Device->QueryInterface(IID_IDirect3DTexture9, &cpy);
|
||||
if (ret == 0x01000000L) {
|
||||
single_texture = static_cast<uMod_IDirect3DDevice9*>(D3D9Device)->GetSingleTexture(); //this texture must no be added twice
|
||||
}
|
||||
else {
|
||||
single_texture = static_cast<uMod_IDirect3DDevice9Ex*>(D3D9Device)->GetSingleTexture(); //this texture must no be added twice
|
||||
}
|
||||
|
||||
int num = OriginalTextures.GetNumber();
|
||||
for (int i = 0; i < num; i++) {
|
||||
if (OriginalTextures[i]->CrossRef_D3Dtex == nullptr || OriginalTextures[i]->CrossRef_D3Dtex == single_texture) {
|
||||
UnswitchTextures(OriginalTextures[i]); //this we can do always, so we unswitch the single texture
|
||||
LookUpToMod(OriginalTextures[i], num_to_lookup, to_lookup);
|
||||
}
|
||||
}
|
||||
|
||||
uMod_IDirect3DVolumeTexture9* single_volume_texture;
|
||||
if (ret == 0x01000000L) {
|
||||
single_volume_texture = static_cast<uMod_IDirect3DDevice9*>(D3D9Device)->GetSingleVolumeTexture(); //this texture must no be added twice
|
||||
}
|
||||
else {
|
||||
single_volume_texture = static_cast<uMod_IDirect3DDevice9Ex*>(D3D9Device)->GetSingleVolumeTexture(); //this texture must no be added twice
|
||||
}
|
||||
num = OriginalVolumeTextures.GetNumber();
|
||||
for (int i = 0; i < num; i++) {
|
||||
if (OriginalVolumeTextures[i]->CrossRef_D3Dtex == nullptr || OriginalVolumeTextures[i]->CrossRef_D3Dtex == single_volume_texture) {
|
||||
UnswitchTextures(OriginalVolumeTextures[i]); //this we can do always, so we unswitch the single texture
|
||||
LookUpToMod(OriginalVolumeTextures[i], num_to_lookup, to_lookup);
|
||||
}
|
||||
}
|
||||
|
||||
uMod_IDirect3DCubeTexture9* single_cube_texture;
|
||||
if (ret == 0x01000000L) {
|
||||
single_cube_texture = static_cast<uMod_IDirect3DDevice9*>(D3D9Device)->GetSingleCubeTexture(); //this texture must no be added twice
|
||||
}
|
||||
else {
|
||||
single_cube_texture = static_cast<uMod_IDirect3DDevice9Ex*>(D3D9Device)->GetSingleCubeTexture(); //this texture must no be added twice
|
||||
}
|
||||
num = OriginalCubeTextures.GetNumber();
|
||||
for (int i = 0; i < num; i++) {
|
||||
if (OriginalCubeTextures[i]->CrossRef_D3Dtex == nullptr || OriginalCubeTextures[i]->CrossRef_D3Dtex == single_cube_texture) {
|
||||
UnswitchTextures(OriginalCubeTextures[i]); //this we can do always, so we unswitch the single texture
|
||||
LookUpToMod(OriginalCubeTextures[i], num_to_lookup, to_lookup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete [] to_lookup;
|
||||
|
||||
return UnlockMutex();
|
||||
}
|
||||
|
||||
|
||||
|
||||
int uMod_TextureClient::LockMutex()
|
||||
{
|
||||
if ((gl_ErrorState & (uMod_ERROR_FATAL | uMod_ERROR_MUTEX))) {
|
||||
return RETURN_NO_MUTEX;
|
||||
}
|
||||
if (WAIT_OBJECT_0 != WaitForSingleObject(Mutex, 100)) {
|
||||
return RETURN_MUTEX_LOCK; //waiting 100ms, to wait infinite pass INFINITE
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int uMod_TextureClient::UnlockMutex()
|
||||
{
|
||||
if (ReleaseMutex(Mutex) == 0) {
|
||||
return RETURN_MUTEX_UNLOCK;
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
|
||||
int uMod_TextureClient::LookUpToMod(MyTypeHash hash, int num_index_list, int* index_list)
|
||||
{
|
||||
if (NumberToMod > 0) {
|
||||
if (index_list == nullptr || num_index_list == 0) {
|
||||
if (hash < FileToMod[0].Hash || hash > FileToMod[NumberToMod - 1].Hash) {
|
||||
return -1;
|
||||
}
|
||||
int pos = NumberToMod / 2;
|
||||
int begin = 0;
|
||||
int end = NumberToMod - 1;
|
||||
|
||||
// We look in the middle of the interval and each step we halve the interval,
|
||||
// unless we find the texture or the size of the interval is less than 3.
|
||||
// Note: contradicting to normal C-code here the interval includes the index "begin" and "end"!
|
||||
while (begin + 1 < end) // as long as the interval is longer than two
|
||||
{
|
||||
if (hash > FileToMod[pos].Hash) // the new interval is the right half of the actual interval
|
||||
{
|
||||
begin = pos + 1; // the new interval does not contain the index "pos"
|
||||
pos = (begin + end) / 2; // set "pos" somewhere inside the new interval
|
||||
}
|
||||
else if (hash < FileToMod[pos].Hash) // the new interval is the left half of the actual interval
|
||||
{
|
||||
end = pos - 1; // the new interval does not contain the index "pos"
|
||||
pos = (begin + end) / 2; // set "pos" somewhere inside the new interval
|
||||
}
|
||||
else {
|
||||
return pos;
|
||||
break;
|
||||
} // we hit the correct hash
|
||||
}
|
||||
for (pos = begin; pos <= end; pos++) {
|
||||
if (FileToMod[pos].Hash == hash) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hash < FileToMod[index_list[0]].Hash || hash > FileToMod[index_list[num_index_list - 1]].Hash) {
|
||||
return -1;
|
||||
}
|
||||
int pos = num_index_list / 2;
|
||||
int begin = 0;
|
||||
int end = num_index_list - 1;
|
||||
|
||||
// We look in the middle of the interval and each step we halve the interval,
|
||||
// unless we find the texture or the size of the interval is less than 3.
|
||||
// Note: contradicting to normal C-code here the interval includes the index "begin" and "end"!
|
||||
while (begin + 1 < end) // as long as the interval is longer than two
|
||||
{
|
||||
if (hash > FileToMod[index_list[pos]].Hash) // the new interval is the right half of the actual interval
|
||||
{
|
||||
begin = pos + 1; // the new interval does not contain the index "pos"
|
||||
pos = (begin + end) / 2; // set "pos" somewhere inside the new interval
|
||||
}
|
||||
else if (hash < FileToMod[index_list[pos]].Hash) // the new interval is the left half of the actual interval
|
||||
{
|
||||
end = pos - 1; // the new interval does not contain the index "pos"
|
||||
pos = (begin + end) / 2; // set "pos" somewhere inside the new interval
|
||||
}
|
||||
else {
|
||||
return index_list[pos];
|
||||
break;
|
||||
} // we hit the correct hash
|
||||
}
|
||||
for (pos = begin; pos <= end; pos++) {
|
||||
if (FileToMod[index_list[pos]].Hash == hash) {
|
||||
return index_list[pos];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int uMod_TextureClient::LookUpToMod(uMod_IDirect3DTexture9* pTexture, int num_index_list, int* index_list) // should only be called for original textures
|
||||
{
|
||||
Message("uMod_TextureClient::LookUpToMod( %p): hash: %#lX, %p\n", pTexture, pTexture->Hash, this);
|
||||
if (pTexture->CrossRef_D3Dtex != nullptr) {
|
||||
return RETURN_OK; // bug, this texture is already switched
|
||||
}
|
||||
const int index = LookUpToMod(pTexture->Hash, num_index_list, index_list);
|
||||
if (index >= 0) {
|
||||
uMod_IDirect3DTexture9* fake_Texture;
|
||||
if (const int ret = LoadTexture(&(FileToMod[index]), &fake_Texture)) {
|
||||
return ret;
|
||||
}
|
||||
if (SwitchTextures(fake_Texture, pTexture)) {
|
||||
Message("uMod_TextureClient::LookUpToMod(): textures not switched %#lX\n", FileToMod[index].Hash);
|
||||
fake_Texture->Release();
|
||||
}
|
||||
else {
|
||||
const auto temp = new IDirect3DBaseTexture9*[FileToMod[index].NumberOfTextures + 1];
|
||||
for (int j = 0; j < FileToMod[index].NumberOfTextures; j++) {
|
||||
temp[j] = FileToMod[index].Textures[j];
|
||||
}
|
||||
|
||||
delete [] FileToMod[index].Textures;
|
||||
|
||||
FileToMod[index].Textures = temp;
|
||||
|
||||
FileToMod[index].Textures[FileToMod[index].NumberOfTextures++] = fake_Texture;
|
||||
fake_Texture->Reference = index;
|
||||
}
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int uMod_TextureClient::LookUpToMod(uMod_IDirect3DVolumeTexture9* pTexture, int num_index_list, int* index_list) // should only be called for original textures
|
||||
{
|
||||
Message("uMod_TextureClient::LookUpToMod( Volume %p): hash: %#lX, %p\n", pTexture, pTexture->Hash, this);
|
||||
if (pTexture->CrossRef_D3Dtex != nullptr) {
|
||||
return RETURN_OK; // bug, this texture is already switched
|
||||
}
|
||||
const int index = LookUpToMod(pTexture->Hash, num_index_list, index_list);
|
||||
if (index >= 0) {
|
||||
uMod_IDirect3DVolumeTexture9* fake_Texture;
|
||||
if (const int ret = LoadTexture(&(FileToMod[index]), &fake_Texture)) {
|
||||
return ret;
|
||||
}
|
||||
if (SwitchTextures(fake_Texture, pTexture)) {
|
||||
Message("uMod_TextureClient::LookUpToMod(): textures not switched %#lX\n", FileToMod[index].Hash);
|
||||
fake_Texture->Release();
|
||||
}
|
||||
else {
|
||||
const auto temp = new IDirect3DBaseTexture9*[FileToMod[index].NumberOfTextures + 1];
|
||||
for (int j = 0; j < FileToMod[index].NumberOfTextures; j++) {
|
||||
temp[j] = FileToMod[index].Textures[j];
|
||||
}
|
||||
|
||||
delete [] FileToMod[index].Textures;
|
||||
|
||||
FileToMod[index].Textures = temp;
|
||||
|
||||
FileToMod[index].Textures[FileToMod[index].NumberOfTextures++] = fake_Texture;
|
||||
fake_Texture->Reference = index;
|
||||
}
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int uMod_TextureClient::LookUpToMod(uMod_IDirect3DCubeTexture9* pTexture, int num_index_list, int* index_list) // should only be called for original textures
|
||||
{
|
||||
Message("uMod_TextureClient::LookUpToMod( Cube %p): hash: %#lX, %p\n", pTexture, pTexture->Hash, this);
|
||||
if (pTexture->CrossRef_D3Dtex != nullptr) {
|
||||
return RETURN_OK; // bug, this texture is already switched
|
||||
}
|
||||
const int index = LookUpToMod(pTexture->Hash, num_index_list, index_list);
|
||||
if (index >= 0) {
|
||||
uMod_IDirect3DCubeTexture9* fake_Texture;
|
||||
if (const int ret = LoadTexture(&(FileToMod[index]), &fake_Texture)) {
|
||||
return ret;
|
||||
}
|
||||
if (SwitchTextures(fake_Texture, pTexture)) {
|
||||
Message("uMod_TextureClient::LookUpToMod(): textures not switched %#lX\n", FileToMod[index].Hash);
|
||||
fake_Texture->Release();
|
||||
}
|
||||
else {
|
||||
const auto temp = new IDirect3DBaseTexture9*[FileToMod[index].NumberOfTextures + 1];
|
||||
for (int j = 0; j < FileToMod[index].NumberOfTextures; j++) {
|
||||
temp[j] = FileToMod[index].Textures[j];
|
||||
}
|
||||
|
||||
delete [] FileToMod[index].Textures;
|
||||
FileToMod[index].Textures = temp;
|
||||
|
||||
FileToMod[index].Textures[FileToMod[index].NumberOfTextures++] = fake_Texture;
|
||||
fake_Texture->Reference = index;
|
||||
}
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int uMod_TextureClient::LoadTexture(TextureFileStruct* file_in_memory, uMod_IDirect3DTexture9** ppTexture) // to load fake texture from a file in memory
|
||||
{
|
||||
Message("LoadTexture( %p, %p, %#lX): %p\n", file_in_memory, ppTexture, file_in_memory->Hash, this);
|
||||
if (D3D_OK != D3DXCreateTextureFromFileInMemoryEx(D3D9Device, file_in_memory->pData, file_in_memory->Size, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, nullptr, nullptr,
|
||||
(IDirect3DTexture9**)ppTexture))
|
||||
//if (D3D_OK != D3DXCreateTextureFromFileInMemory( D3D9Device, file_in_memory->pData, file_in_memory->Size, (IDirect3DTexture9 **) ppTexture))
|
||||
{
|
||||
*ppTexture = nullptr;
|
||||
return RETURN_TEXTURE_NOT_LOADED;
|
||||
}
|
||||
(*ppTexture)->FAKE = true;
|
||||
|
||||
void* cpy;
|
||||
const long ret = D3D9Device->QueryInterface(IID_IDirect3DTexture9, &cpy);
|
||||
if (ret == 0x01000000L) {
|
||||
static_cast<uMod_IDirect3DDevice9*>(D3D9Device)->SetLastCreatedTexture(nullptr); //this texture must no be added twice
|
||||
}
|
||||
else {
|
||||
static_cast<uMod_IDirect3DDevice9Ex*>(D3D9Device)->SetLastCreatedTexture(nullptr); //this texture must no be added twice
|
||||
}
|
||||
|
||||
Message("LoadTexture( %p, %#lX): DONE\n", *ppTexture, file_in_memory->Hash);
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int uMod_TextureClient::LoadTexture(TextureFileStruct* file_in_memory, uMod_IDirect3DVolumeTexture9** ppTexture) // to load fake texture from a file in memory
|
||||
{
|
||||
Message("LoadTexture( Volume %p, %p, %#lX): %p\n", file_in_memory, ppTexture, file_in_memory->Hash, this);
|
||||
if (D3D_OK != D3DXCreateVolumeTextureFromFileInMemoryEx(D3D9Device, file_in_memory->pData, file_in_memory->Size, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, nullptr,
|
||||
nullptr,
|
||||
(IDirect3DVolumeTexture9**)ppTexture))
|
||||
//if (D3D_OK != D3DXCreateVolumeTextureFromFileInMemory( D3D9Device, file_in_memory->pData, file_in_memory->Size, (IDirect3DVolumeTexture9 **) ppTexture))
|
||||
{
|
||||
*ppTexture = nullptr;
|
||||
return RETURN_TEXTURE_NOT_LOADED;
|
||||
}
|
||||
(*ppTexture)->FAKE = true;
|
||||
|
||||
void* cpy;
|
||||
const long ret = D3D9Device->QueryInterface(IID_IDirect3DTexture9, &cpy);
|
||||
if (ret == 0x01000000L) {
|
||||
static_cast<uMod_IDirect3DDevice9*>(D3D9Device)->SetLastCreatedVolumeTexture(nullptr); //this texture must no be added twice
|
||||
}
|
||||
else {
|
||||
static_cast<uMod_IDirect3DDevice9Ex*>(D3D9Device)->SetLastCreatedVolumeTexture(nullptr); //this texture must no be added twice
|
||||
}
|
||||
|
||||
Message("LoadTexture( Volume %p, %#lX): DONE\n", *ppTexture, file_in_memory->Hash);
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int uMod_TextureClient::LoadTexture(TextureFileStruct* file_in_memory, uMod_IDirect3DCubeTexture9** ppTexture) // to load fake texture from a file in memory
|
||||
{
|
||||
Message("LoadTexture( Cube %p, %p, %#lX): %p\n", file_in_memory, ppTexture, file_in_memory->Hash, this);
|
||||
if (D3D_OK != D3DXCreateCubeTextureFromFileInMemoryEx(D3D9Device, file_in_memory->pData, file_in_memory->Size, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, nullptr, nullptr,
|
||||
(IDirect3DCubeTexture9**)ppTexture))
|
||||
//if (D3D_OK != D3DXCreateCubeTextureFromFileInMemory( D3D9Device, file_in_memory->pData, file_in_memory->Size, (IDirect3DCubeTexture9 **) ppTexture))
|
||||
{
|
||||
*ppTexture = nullptr;
|
||||
return RETURN_TEXTURE_NOT_LOADED;
|
||||
}
|
||||
(*ppTexture)->FAKE = true;
|
||||
|
||||
void* cpy;
|
||||
const long ret = D3D9Device->QueryInterface(IID_IDirect3DTexture9, &cpy);
|
||||
if (ret == 0x01000000L) {
|
||||
static_cast<uMod_IDirect3DDevice9*>(D3D9Device)->SetLastCreatedCubeTexture(nullptr); //this texture must no be added twice
|
||||
}
|
||||
else {
|
||||
static_cast<uMod_IDirect3DDevice9Ex*>(D3D9Device)->SetLastCreatedCubeTexture(nullptr); //this texture must no be added twice
|
||||
}
|
||||
|
||||
Message("LoadTexture( Cube %p, %#lX): DONE\n", *ppTexture, file_in_memory->Hash);
|
||||
return RETURN_OK;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#include "uMod_Main.h"
|
||||
|
||||
/*
|
||||
MyTypeHash GetHash(unsigned char *str, int len) // estimate the hash
|
||||
{
|
||||
MyTypeHash hash = 0;
|
||||
for (int i=0; i<len; i++) hash = str[i] + (hash << 6) + (hash << 16) - hash;
|
||||
return hash);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* BIG THANKS TO RS !!
|
||||
*
|
||||
* who gave me his hashing algorithm (well or crc32 algorithm^^)
|
||||
*
|
||||
The hash function is CRC32 using polynomial 0xEDB88320.
|
||||
However, the hashed data is calculated incorrectly in TexMod: it's simply BytesPerPixel * Width * Height, from the beginning of the data (that is mapped using LockRect).
|
||||
The problem is that it doesn't take the pitch into account and BytesPerPixel may be wrong for some rare formats (not sure about that).
|
||||
*/
|
||||
|
||||
|
||||
#define CRC32POLY 0xEDB88320u /* CRC-32 Polynom */
|
||||
#define ulCrc_in 0xffffffff
|
||||
|
||||
unsigned int GetCRC32(char* pcDatabuf, unsigned int ulDatalen)
|
||||
{
|
||||
unsigned int crc = ulCrc_in;
|
||||
for (unsigned int idx = 0u; idx < ulDatalen; idx++) {
|
||||
unsigned int data = *pcDatabuf++;
|
||||
for (unsigned int bit = 0u; bit < 8u; bit++, data >>= 1) {
|
||||
crc = crc >> 1 ^ ((crc ^ data) & 1 ? CRC32POLY : 0);
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include "uMod_Main.h"
|
||||
#include "gMod_FileLoader.h"
|
||||
|
||||
unsigned long loadedSize = 0;
|
||||
|
||||
uMod_TextureServer::uMod_TextureServer(char* game, char* uModName)
|
||||
{
|
||||
Message("uMod_TextureServer(): %p\n", this);
|
||||
|
||||
Client = nullptr;
|
||||
|
||||
int len = 0;
|
||||
int path_pos = 0;
|
||||
int dot_pos = 0;
|
||||
for (len = 0; len < MAX_PATH && (game[len]); len++) {
|
||||
if (game[len] == '\\' || game[len] == '/') {
|
||||
path_pos = len + 1;
|
||||
}
|
||||
else if (game[len] == '.') {
|
||||
dot_pos = len;
|
||||
}
|
||||
}
|
||||
|
||||
if (dot_pos > path_pos) {
|
||||
len = dot_pos - path_pos;
|
||||
}
|
||||
else {
|
||||
len -= path_pos;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
GameName[i] = game[i + path_pos];
|
||||
}
|
||||
|
||||
if (len < MAX_PATH) {
|
||||
GameName[len] = 0;
|
||||
}
|
||||
else {
|
||||
GameName[0] = 0;
|
||||
}
|
||||
|
||||
strcpy(UModName, uModName);
|
||||
}
|
||||
|
||||
uMod_TextureServer::~uMod_TextureServer()
|
||||
{
|
||||
Message("~uMod_TextureServer(): %p\n", this);
|
||||
//delete the files in memory
|
||||
int num = CurrentMod.GetNumber();
|
||||
for (int i = 0; i < num; i++) {
|
||||
delete[] CurrentMod[i]->pData; //delete the file content of the texture
|
||||
}
|
||||
|
||||
num = OldMod.GetNumber();
|
||||
for (int i = 0; i < num; i++) {
|
||||
delete[] OldMod[i]->pData; //delete the file content of the texture
|
||||
}
|
||||
}
|
||||
|
||||
int uMod_TextureServer::AddClient(uMod_TextureClient* client, TextureFileStruct** update, int* number) // called from a client
|
||||
{
|
||||
Message("AddClient(%p): %p\n", client, this);
|
||||
// the following functions must not change the original uMod_IDirect3DDevice9 object
|
||||
// somehow on game start some uMod_IDirect3DDevice9 object are created, which must rest unchanged!!
|
||||
// these objects are released and are not used for rendering
|
||||
|
||||
if (const int ret = PrepareUpdate(update, number)) {
|
||||
return ret; // get a copy of all texture to be modded
|
||||
}
|
||||
|
||||
Client = client;
|
||||
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int uMod_TextureServer::AddFile(char* dataPtr, unsigned int size, MyTypeHash hash, bool force) // called from Mainloop()
|
||||
{
|
||||
Message("uMod_TextureServer::AddFile( %p %p, %#lX, %d): %p\n", dataPtr, size, hash, force, this);
|
||||
|
||||
TextureFileStruct* temp = nullptr;
|
||||
|
||||
int num = CurrentMod.GetNumber();
|
||||
for (int i = 0; i < num; i++) {
|
||||
if (CurrentMod[i]->Hash == hash) //look through all current textures
|
||||
{
|
||||
if (force) {
|
||||
temp = CurrentMod[i];
|
||||
break;
|
||||
} // we need to reload it
|
||||
|
||||
|
||||
return RETURN_EXISTS; // we still have added this texture
|
||||
}
|
||||
}
|
||||
if (temp == nullptr) // if not found, look through all old textures
|
||||
{
|
||||
num = OldMod.GetNumber();
|
||||
for (int i = 0; i < num; i++) {
|
||||
if (OldMod[i]->Hash == hash) {
|
||||
temp = OldMod[i];
|
||||
OldMod.Remove(temp);
|
||||
CurrentMod.Add(temp);
|
||||
if (force) {
|
||||
break; // we must reload it
|
||||
}
|
||||
|
||||
return RETURN_EXISTS; // we should not reload it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool new_file = true;
|
||||
if (temp != nullptr) //if it was found, we delete the old file content
|
||||
{
|
||||
new_file = false;
|
||||
|
||||
delete[] temp->pData; // todo: this tries deleting memory out of a vector of tpfentries, which won't do anything
|
||||
// todo: therefore we don't get out memory back here
|
||||
|
||||
temp->pData = nullptr;
|
||||
}
|
||||
else //if it was not found, we need to create a new object
|
||||
{
|
||||
new_file = true;
|
||||
temp = new TextureFileStruct;
|
||||
temp->Reference = -1;
|
||||
}
|
||||
|
||||
temp->pData = dataPtr;
|
||||
temp->Size = size;
|
||||
temp->NumberOfTextures = 0;
|
||||
temp->Textures = nullptr;
|
||||
temp->Hash = hash;
|
||||
|
||||
//if (new_file) temp->ForceReload = false; // no need to force a load of the texture
|
||||
//else
|
||||
temp->ForceReload = force;
|
||||
|
||||
Message("End AddFile(%#lX)\n", hash);
|
||||
if (new_file) {
|
||||
return CurrentMod.Add(temp); // new files must be added to the list of the CurrentMod
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int uMod_TextureServer::PropagateUpdate(uMod_TextureClient* client) // called from Mainloop(), send the update to all clients
|
||||
{
|
||||
Message("PropagateUpdate(%p): %p\n", client, this);
|
||||
if (client != nullptr) {
|
||||
TextureFileStruct* update;
|
||||
int number;
|
||||
if (const int ret = PrepareUpdate(&update, &number)) {
|
||||
return ret;
|
||||
}
|
||||
client->AddUpdate(update, number);
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
void cpy_file_struct(TextureFileStruct& a,TextureFileStruct& b)
|
||||
{
|
||||
a.ForceReload = b.ForceReload;
|
||||
a.pData = b.pData;
|
||||
a.Size = b.Size;
|
||||
a.NumberOfTextures = b.NumberOfTextures;
|
||||
a.Reference = b.Reference;
|
||||
a.Textures = b.Textures;
|
||||
a.Hash = b.Hash;
|
||||
}
|
||||
|
||||
int TextureFileStruct_Compare(const void* elem1, const void* elem2)
|
||||
{
|
||||
const auto tex1 = (TextureFileStruct*)elem1;
|
||||
const auto tex2 = (TextureFileStruct*)elem2;
|
||||
if (tex1->Hash < tex2->Hash) {
|
||||
return -1;
|
||||
}
|
||||
if (tex1->Hash > tex2->Hash) {
|
||||
return +1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uMod_TextureServer::PrepareUpdate(TextureFileStruct** update, int* number) // called from the PropagateUpdate() and AddClient.
|
||||
// Prepare an update for one client. The allocated memory must deleted by the client.
|
||||
{
|
||||
Message("PrepareUpdate(%p, %d): %p\n", update, number, this);
|
||||
|
||||
TextureFileStruct* temp = nullptr;
|
||||
const int num = CurrentMod.GetNumber();
|
||||
if (num > 0) {
|
||||
try { temp = new TextureFileStruct[num]; }
|
||||
catch (...) {
|
||||
gl_ErrorState |= uMod_ERROR_MEMORY | uMod_ERROR_SERVER;
|
||||
return RETURN_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num; i++) cpy_file_struct(temp[i], *CurrentMod[i]);
|
||||
qsort(temp, num, sizeof(TextureFileStruct), TextureFileStruct_Compare);
|
||||
}
|
||||
|
||||
*update = temp;
|
||||
*number = num;
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
void uMod_TextureServer::LoadModsFromFile(const char* source)
|
||||
{
|
||||
Message("Initialize: searching in %s\n", source);
|
||||
|
||||
std::ifstream file(source);
|
||||
if (file.is_open()) {
|
||||
Message("Initialize: found modlist.txt. Reading\n");
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
Message("Initialize: loading file %s\n", line.c_str());
|
||||
|
||||
// Remove newline character
|
||||
line.erase(std::ranges::remove(line, '\n').begin(), line.end());
|
||||
|
||||
auto file_loader = gMod_FileLoader(line);
|
||||
const auto entries = file_loader.GetContents();
|
||||
if (loadedSize > 1'500'000'000) {
|
||||
Message("LoadModsFromFile: Loaded %d bytes, aborting!!!", loadedSize);
|
||||
return;
|
||||
}
|
||||
if (!entries.empty()) {
|
||||
Message("Initialize: Texture count %zu %s\n", entries.size(), line.c_str());
|
||||
for (const auto& tpf_entry : entries) {
|
||||
loadedSize += tpf_entry.size;
|
||||
if (AddFile(static_cast<char*>(tpf_entry.data), static_cast<unsigned int>(tpf_entry.size), tpf_entry.crc_hash, false) == RETURN_EXISTS) {
|
||||
//Texture is already loaded, so we need to clear our data
|
||||
loadedSize -= tpf_entry.size;
|
||||
}
|
||||
|
||||
Message("LoadModsFromFile: Loaded %d bytes", loadedSize);
|
||||
}
|
||||
|
||||
PropagateUpdate(Client);
|
||||
}
|
||||
else {
|
||||
Message("Initialize: Failed to load any textures for %s\n", line.c_str());
|
||||
}
|
||||
}
|
||||
Message("Finished loading mods: Loaded %u bytes (%u mb)", loadedSize, loadedSize / 1024 / 1024);
|
||||
}
|
||||
}
|
||||
|
||||
int uMod_TextureServer::Initialize()
|
||||
{
|
||||
Message("Initialize: begin\n");
|
||||
Message("Initialize: searching for modlist.txt\n");
|
||||
char gwpath[MAX_PATH];
|
||||
GetModuleFileName(GetModuleHandle(nullptr), gwpath, MAX_PATH); //ask for name and path of this executable
|
||||
const auto exe = std::filesystem::path(gwpath).parent_path();
|
||||
const auto dll = std::filesystem::path(UModName).parent_path();
|
||||
for (const auto& path : {exe, dll}) {
|
||||
const auto modlist = path / "modlist.txt";
|
||||
if (std::filesystem::exists(modlist)) {
|
||||
LoadModsFromFile(modlist.string().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Message("Initialize: end\n");
|
||||
|
||||
return RETURN_OK;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
set(VCPKG_TARGET_ARCHITECTURE x86)
|
||||
|
||||
if(PORT MATCHES "dxsdk-d3dx")
|
||||
set(VCPKG_CRT_LINKAGE dynamic)
|
||||
set(VCPKG_LIBRARY_LINKAGE dynamic)
|
||||
else()
|
||||
set(VCPKG_CRT_LINKAGE static)
|
||||
set(VCPKG_LIBRARY_LINKAGE static)
|
||||
endif()
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"default-registry": {
|
||||
"kind": "git",
|
||||
"baseline": "7f9f0e44db287e8e67c0e888141bfa200ab45121",
|
||||
"repository": "https://github.com/microsoft/vcpkg"
|
||||
},
|
||||
"registries": [
|
||||
{
|
||||
"kind": "artifact",
|
||||
"location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip",
|
||||
"name": "microsoft"
|
||||
}
|
||||
]
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"dependencies": [
|
||||
"dxsdk-d3dx",
|
||||
{
|
||||
"name": "libzippp",
|
||||
"features": [
|
||||
"encryption"
|
||||
]
|
||||
},
|
||||
"minhook",
|
||||
"ms-gsl"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user