use core::ffi::c_void;
use std::collections::HashMap;
use std::sync::RwLock;
use windows::Win32::Foundation::{E_FAIL, S_OK};
use windows::Win32::Graphics::Direct3D11::{
ID3D11ClassLinkage, ID3D11ClassLinkage_Vtbl, ID3D11Device, ID3D11DeviceChild_Vtbl,
};
use windows::Win32::Graphics::Dxgi::{
IDXGIDeviceSubObject, IDXGIDeviceSubObject_Vtbl, IDXGIObject, IDXGIObject_Vtbl, IDXGIResource,
IDXGISurface,
};
use windows::Win32::System::Memory::{PAGE_PROTECTION_FLAGS, PAGE_READWRITE, VirtualProtect};
use windows::core::{GUID, HRESULT, Interface, PCSTR};
use crate::log::log;
const SLOT: usize = size_of::<usize>();
const CHILD_GET_DEVICE: usize = core::mem::offset_of!(ID3D11DeviceChild_Vtbl, GetDevice) / SLOT;
const DXGI_GET_PARENT: usize = core::mem::offset_of!(IDXGIObject_Vtbl, GetParent) / SLOT;
const DXGI_GET_DEVICE: usize = core::mem::offset_of!(IDXGIDeviceSubObject_Vtbl, GetDevice) / SLOT;
const LINKAGE_GET_INSTANCE: usize = core::mem::offset_of!(ID3D11ClassLinkage_Vtbl, GetClassInstance) / SLOT;
const LINKAGE_CREATE_INSTANCE: usize =
core::mem::offset_of!(ID3D11ClassLinkage_Vtbl, CreateClassInstance) / SLOT;
static ORIGINALS: RwLock<Option<HashMap<usize, usize>>> = RwLock::new(None);
unsafe fn patch(object: *mut c_void, slot: usize, hook: usize) {
let table = unsafe { *(object as *const *mut usize) };
let at = unsafe { table.add(slot) };
if ORIGINALS
.read()
.unwrap_or_else(|e| e.into_inner())
.as_ref()
.is_some_and(|o| o.contains_key(&(at as usize)))
{
return;
}
let mut originals = ORIGINALS.write().unwrap_or_else(|e| e.into_inner());
let originals = originals.get_or_insert_with(HashMap::new);
if originals.contains_key(&(at as usize)) {
return;
}
let mut old = PAGE_PROTECTION_FLAGS(0);
if let Err(e) = unsafe { VirtualProtect(at.cast(), SLOT, PAGE_READWRITE, &mut old) } {
log!("cannot change the runtime's table at {at:p} ({e}): its objects' parents stay real");
return;
}
originals.insert(at as usize, unsafe { at.read() });
unsafe { at.write_volatile(hook) };
let mut changed = PAGE_PROTECTION_FLAGS(0);
let _ = unsafe { VirtualProtect(at.cast(), SLOT, old, &mut changed) };
}
unsafe fn original(this: *mut c_void, slot: usize) -> Option<usize> {
let at = unsafe { (*(this as *const *const usize)).add(slot) } as usize;
ORIGINALS
.read()
.unwrap_or_else(|e| e.into_inner())
.as_ref()?
.get(&at)
.copied()
}
fn ours(table: *const c_void) -> bool {
static LOCUM: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
let locum =
*LOCUM.get_or_init(|| crate::system::module_of(ours as *const c_void).map_or(0, |m| m.0 as usize));
crate::system::module_of(table).is_some_and(|m| m.0 as usize == locum)
}
pub unsafe fn adopt(object: *mut c_void) {
if object.is_null() || ours(unsafe { *(object as *const *const c_void) }) {
return;
}
unsafe { patch(object, CHILD_GET_DEVICE, child_get_device as *const () as usize) };
for iid in [IDXGIDeviceSubObject::IID, IDXGIResource::IID, IDXGISurface::IID] {
let mut face = core::ptr::null_mut();
if unsafe { crate::com::query(object, &iid, &mut face) }.is_ok() && !face.is_null() {
unsafe {
patch(face, DXGI_GET_PARENT, dxgi_get_parent as *const () as usize);
patch(face, DXGI_GET_DEVICE, dxgi_get_device as *const () as usize);
crate::faces::release(face);
}
}
}
let mut linkage = core::ptr::null_mut();
if unsafe { crate::com::query(object, &ID3D11ClassLinkage::IID, &mut linkage) }.is_ok()
&& !linkage.is_null()
{
unsafe {
patch(
linkage,
LINKAGE_GET_INSTANCE,
linkage_get_instance as *const () as usize,
);
patch(
linkage,
LINKAGE_CREATE_INSTANCE,
linkage_create_instance as *const () as usize,
);
crate::faces::release(linkage);
}
}
}
const HANDED_OUT: GUID = GUID::from_u128(0x6c6f_6375_6d00_4a5e_9d1b_7a2f_8c3e_0004);
pub unsafe fn adopt_output(output: *mut c_void) {
if output.is_null() || ours(unsafe { *(output as *const *const c_void) }) {
return;
}
if let Some(object) = unsafe { IDXGIObject::from_raw_borrowed(&output) } {
let _ = unsafe { object.SetPrivateData(&HANDED_OUT, 1, [1u8].as_ptr().cast()) };
}
unsafe { patch(output, DXGI_GET_PARENT, dxgi_get_parent as *const () as usize) };
}
unsafe fn handed_out(object: *mut c_void) -> bool {
let Some(object) = (unsafe { IDXGIObject::from_raw_borrowed(&object) }) else {
return false;
};
let mut mark = 0u8;
let mut size = 1;
unsafe { object.GetPrivateData(&HANDED_OUT, &mut size, (&raw mut mark).cast()) }.is_ok()
}
type GetDevice = unsafe extern "system" fn(*mut c_void, *mut *mut c_void);
type GetParent = unsafe extern "system" fn(*mut c_void, *const GUID, *mut *mut c_void) -> HRESULT;
type GetInstance = unsafe extern "system" fn(*mut c_void, PCSTR, u32, *mut *mut c_void) -> HRESULT;
type CreateInstance =
unsafe extern "system" fn(*mut c_void, PCSTR, u32, u32, u32, u32, *mut *mut c_void) -> HRESULT;
unsafe extern "system" fn child_get_device(this: *mut c_void, out: *mut *mut c_void) {
let Some(f) = (unsafe { original(this, CHILD_GET_DEVICE) }) else {
if !out.is_null() {
unsafe { *out = core::ptr::null_mut() };
}
return;
};
unsafe { core::mem::transmute::<usize, GetDevice>(f)(this, out) };
let _ = unsafe { crate::dxgi::give_known(S_OK, &ID3D11Device::IID, out) };
}
unsafe extern "system" fn dxgi_get_parent(
this: *mut c_void,
iid: *const GUID,
out: *mut *mut c_void,
) -> HRESULT {
let Some(f) = (unsafe { original(this, DXGI_GET_PARENT) }) else {
return E_FAIL;
};
let hr = unsafe { core::mem::transmute::<usize, GetParent>(f)(this, iid, out) };
if unsafe { handed_out(this) } {
unsafe { crate::dxgi::give(hr, iid, out) }
} else {
unsafe { crate::dxgi::give_known(hr, iid, out) }
}
}
unsafe extern "system" fn dxgi_get_device(
this: *mut c_void,
iid: *const GUID,
out: *mut *mut c_void,
) -> HRESULT {
let Some(f) = (unsafe { original(this, DXGI_GET_DEVICE) }) else {
return E_FAIL;
};
let hr = unsafe { core::mem::transmute::<usize, GetParent>(f)(this, iid, out) };
unsafe { crate::dxgi::give_known(hr, iid, out) }
}
unsafe extern "system" fn linkage_get_instance(
this: *mut c_void,
name: PCSTR,
index: u32,
out: *mut *mut c_void,
) -> HRESULT {
let Some(f) = (unsafe { original(this, LINKAGE_GET_INSTANCE) }) else {
return E_FAIL;
};
let hr = unsafe { core::mem::transmute::<usize, GetInstance>(f)(this, name, index, out) };
if hr.is_ok() && !out.is_null() {
unsafe { adopt(*out) };
}
hr
}
unsafe extern "system" fn linkage_create_instance(
this: *mut c_void,
name: PCSTR,
buffer: u32,
vector: u32,
texture: u32,
sampler: u32,
out: *mut *mut c_void,
) -> HRESULT {
let Some(f) = (unsafe { original(this, LINKAGE_CREATE_INSTANCE) }) else {
return E_FAIL;
};
let hr = unsafe {
core::mem::transmute::<usize, CreateInstance>(f)(this, name, buffer, vector, texture, sampler, out)
};
if hr.is_ok() && !out.is_null() {
unsafe { adopt(*out) };
}
hr
}