Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions MMLaunch/ModUtil.fs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ module DdsUtil =

type HeaderInfo = { FourCC : string; HasDX10 : bool }

let PathToTexCli = [ "TPlib"; "..\\TPLib"; "..\\..\\TPLib"; "..\\..\\..\\TPLib"; ]
let PathToSnapshotProfiles = [ "SnapshotProfiles"; "..\\SnapshotProfiles"; "..\\..\\SnapshotProfiles"; "..\\..\\..\\SnapshotProfiles" ]
let PathToTexCli = [ "TPlib"; "ModelMod\\TPLib"; "..\\TPLib"; "..\\..\\TPLib"; "..\\..\\..\\TPLib"; ]
let PathToSnapshotProfiles = [ "SnapshotProfiles"; "ModelMod\\SnapshotProfiles"; "..\\SnapshotProfiles"; "..\\..\\SnapshotProfiles"; "..\\..\\..\\SnapshotProfiles" ]
let SnapTexFormatFile = "TexFormat.txt"
let TexConvName = "texconv.exe"

Expand Down
35 changes: 28 additions & 7 deletions Native/d3dx/src/d3dx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,26 @@ pub fn load_lib(mm_root: &Option<String>, device: &DevicePointer) -> Result<D3DX
},
DevicePointer::D3D11(_device) => {
let base_names = vec!["d3dx11_43.dll", "d3dx11_42.dll"];
// just try loading it first from the system
let mut handle = base_names.iter().find_map(|base_name| {
match util::load_lib(base_name) {
Ok(handle) => Some(handle),
Err(_) => None,
}
});

let mut handle:Option<HMODULE> = None;

let load_from_sys = || {
base_names.iter().find_map(|base_name| {
match util::load_lib(base_name) {
Ok(handle) => Some(handle),
Err(_) => None,
}
})
};

let mut searched:Vec<String> = vec![];

// on actual windows try load from sys first; don't do this on wine because it will load, but
// we'll get wine's partially implemented version of the associated functions (on wine
// prefer the copy stored in TPLib)
if !util::is_wine() {
handle = load_from_sys();
}
if handle.is_none() {
// not found so look into tplib dir, try looking for arch specific folder
// or file with an _arch suffix in tplib
Expand Down Expand Up @@ -116,9 +128,18 @@ pub fn load_lib(mm_root: &Option<String>, device: &DevicePointer) -> Result<D3DX
}
}

// final (or first if we skipped it earlier) try from sys
if handle.is_none() {
handle = load_from_sys();
}
match handle {
None => return Err(HookError::LoadLibFailed(format!("D3DX11 not found in system or {:?}", searched))),
Some(handle) => {
// Log exactly which d3dx11 DLL the loader resolved.
match util::get_module_path(handle) {
Ok(path) => write_log_file(&format!("loaded d3dx11 from: {}", path)),
Err(e) => write_log_file(&format!("loaded d3dx11 (path unavailable: {:?})", e)),
}
unsafe {
Ok(D3DXFn::DX11(D3DX11Fn {
D3DX11SaveTextureToFileW: std::mem::transmute(util::get_proc_address(
Expand Down
37 changes: 33 additions & 4 deletions Native/hook_snapshot/src/hook_snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,27 @@ pub unsafe fn take(devptr:&mut DevicePointer, sd:&mut types::interop::SnapshotDa
}
}

/// Human-readable name for the DXGI formats we care about when logging texture snapshots.
/// Covers the block-compressed range, plus a few common uncompressed ones; anything else is reported as "?".
fn dxgi_format_name(fmt: DXGI_FORMAT) -> &'static str {
match fmt {
0 => "UNKNOWN",
2 => "R32G32B32A32_FLOAT",
10 => "R16G16B16A16_FLOAT",
28 => "R8G8B8A8_UNORM",
29 => "R8G8B8A8_UNORM_SRGB",
70 => "BC1_TYPELESS", 71 => "BC1_UNORM", 72 => "BC1_UNORM_SRGB",
73 => "BC2_TYPELESS", 74 => "BC2_UNORM", 75 => "BC2_UNORM_SRGB",
76 => "BC3_TYPELESS", 77 => "BC3_UNORM", 78 => "BC3_UNORM_SRGB",
79 => "BC4_TYPELESS", 80 => "BC4_UNORM", 81 => "BC4_SNORM",
82 => "BC5_TYPELESS", 83 => "BC5_UNORM", 84 => "BC5_SNORM",
87 => "B8G8R8A8_UNORM", 88 => "B8G8R8X8_UNORM", 91 => "B8G8R8A8_UNORM_SRGB",
94 => "BC6H_TYPELESS", 95 => "BC6H_UF16", 96 => "BC6H_SF16",
97 => "BC7_TYPELESS", 98 => "BC7_UNORM", 99 => "BC7_UNORM_SRGB",
_ => "?",
}
}

unsafe fn save_textures(devtr:&mut DevicePointer, buffers:&Box<dyn SnapDeviceBuffers>, snap_dir:&str, snap_prefix:&str) -> Result<()> {
// in d3d9 the managed code already did this
let device = match devtr {
Expand Down Expand Up @@ -345,12 +366,20 @@ unsafe fn save_textures(devtr:&mut DevicePointer, buffers:&Box<dyn SnapDeviceBuf
idx, heightwidth_format, desc.Usage, desc.CPUAccessFlags, desc.BindFlags, desc.MiscFlags, desc.Format));

let out = format!("{}/{}_texture{}.dds", snap_dir, snap_prefix, idx);
let out = util::to_wide_str(&out);
let outw = util::to_wide_str(&out);
const D3DX11_IFF_DDS: u32 = 4;
let hr = (d3dx_fn.D3DX11SaveTextureToFileW)(context, resptr, D3DX11_IFF_DDS, out.as_ptr());
const E_NOTIMPL: i32 = 0x80004001u32 as i32;
let hr = (d3dx_fn.D3DX11SaveTextureToFileW)(context, resptr, D3DX11_IFF_DDS, outw.as_ptr());
if hr != 0 {
// write out an error but keep going to see if we can get others
write_log_file(&format!("failed to save texture from srv {}: {}", idx, hr));
// write out an error but keep going to see if we can get others.
// print the HRESULT in hex (easier to recognize than signed decimal) and the format.
let extra = if hr == E_NOTIMPL {
" -- E_NOTIMPL: this d3dx11 does not implement the save; under Proton this is likely Wine's built-in stub (see 'loaded d3dx11 from' line above)"
} else {
""
};
write_log_file(&format!("failed to save texture from srv {}: {:#010x} (format {} {}){}",
idx, hr as u32, desc.Format, dxgi_format_name(desc.Format), extra));
} else {
num_saved += 1;
}
Expand Down
25 changes: 23 additions & 2 deletions Native/util/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ use std::sync::MutexGuard;

use shared_dx::error::*;

use winapi::um::libloaderapi::{GetModuleHandleA};

pub fn is_wine() -> bool {
unsafe {
let ntdll = GetModuleHandleA(b"ntdll.dll\0".as_ptr() as *const i8);
if ntdll.is_null() {
return false;
}
!GetProcAddress(ntdll, b"wine_get_version\0".as_ptr() as *const i8).is_null()
}
}

pub unsafe fn protect_memory(
target: *mut winapi::ctypes::c_void,
size: usize,
Expand Down Expand Up @@ -327,14 +339,23 @@ pub fn to_wide_str(s: &str) -> Vec<u16> {
}

pub fn get_module_name() -> Result<String> {
use std::os::windows::prelude::*;
use winapi::um::libloaderapi::*;

let handle = unsafe { GetModuleHandleW(std::ptr::null_mut()) };
get_module_path(handle)
}

/// Return the on-disk path for a loaded module handle (as returned by `load_lib`).
/// Useful for logging exactly which DLL the OS loader resolved for a given name,
/// which can differ between the system copy and a game- or app-local copy.
pub fn get_module_path(handle: HMODULE) -> Result<String> {
use std::os::windows::prelude::*;
use winapi::um::libloaderapi::GetModuleFileNameW;

unsafe {
let ssize = 65535;
let mut mpath: Vec<u16> = Vec::with_capacity(ssize);

let handle = GetModuleHandleW(std::ptr::null_mut());
let r = GetModuleFileNameW(handle, mpath.as_mut_ptr(), ssize as DWORD);
if r == 0 {
return Err(HookError::ModuleNameError(format!(
Expand Down
Loading