From 46a49caa21c16d3cc563798b8c26ba936cdd30e3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 20 Jul 2021 15:38:58 +0300 Subject: [PATCH] Add loading of custom shipped d3dcompiler_47.dll. --- validate_d3d_compiler.cmake | 82 ++++++++++++++ validate_d3d_compiler.py | 35 ++++++ win_directx_helper/CMakeLists.txt | 2 + win_directx_helper/win_directx_helper.cpp | 126 +++++++++++++++++++++- 4 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 validate_d3d_compiler.cmake create mode 100644 validate_d3d_compiler.py diff --git a/validate_d3d_compiler.cmake b/validate_d3d_compiler.cmake new file mode 100644 index 0000000..5330b52 --- /dev/null +++ b/validate_d3d_compiler.cmake @@ -0,0 +1,82 @@ +# This file is part of Desktop App Toolkit, +# a set of libraries for developing nice desktop applications. +# +# For license and copyright information please follow this link: +# https://github.com/desktop-app/legal/blob/master/LEGAL + +function(validate_d3d_error text) + if (NOT DESKTOP_APP_SPECIAL_TARGET STREQUAL "") + message(FATAL_ERROR ${text}) + else() + message(WARNING ${text}) + endif() +endfunction() + +function(validate_d3d_compiler target_name) + if (build_win64) + set(modules_subdir x64) + else() + set(modules_subdir x86) + endif() + set(modules_hash_loc ${CMAKE_BINARY_DIR}/modules/${modules_subdir}) + set(modules_debug_loc ${CMAKE_BINARY_DIR}/Debug/modules/${modules_subdir}) + set(modules_release_loc ${CMAKE_BINARY_DIR}/Release/modules/${modules_subdir}) + + set(key_path ${modules_hash_loc}/d3d/d3dcompiler_47) + set(module_debug_path ${modules_debug_loc}/d3d) + set(module_release_path ${modules_release_loc}/d3d) + + set(key "") + if (EXISTS ${key_path}) + file(READ ${key_path} key) + endif() + + if (NOT "$ENV{WindowsSdkDir}" STREQUAL "") + set(windows_sdk_loc $ENV{WindowsSdkDir} CACHE INTERNAL "Windows SDK Path" FORCE) + endif() + + string(LENGTH "${key}" key_length) + if (NOT "${key_length}" STREQUAL "32" + OR NOT EXISTS ${module_debug_path}/d3dcompiler_47.dll + OR NOT EXISTS ${module_release_path}/d3dcompiler_47.dll) + + if ("${windows_sdk_loc}" STREQUAL "") + validate_d3d_error("Could not find Windows SDK.") + return() + endif() + set(sdk_compiler ${windows_sdk_loc}/Redist/D3D/${modules_subdir}/d3dcompiler_47.dll) + + find_package(Python REQUIRED) + execute_process( + COMMAND + ${Python_EXECUTABLE} + ${cmake_helpers_loc}/validate_d3d_compiler.py + ${sdk_compiler} + OUTPUT_VARIABLE key + ERROR_VARIABLE error + ) + if (NOT "${error}" STREQUAL "") + validate_d3d_error(${error}) + return() + endif() + + file(MAKE_DIRECTORY ${modules_debug_loc}/d3d) + file(COPY ${sdk_compiler} DESTINATION ${module_debug_path}) + + file(MAKE_DIRECTORY ${modules_release_loc}/d3d) + file(COPY ${sdk_compiler} DESTINATION ${module_release_path}) + + file(MAKE_DIRECTORY ${modules_hash_loc}/d3d) + file(WRITE ${key_path} ${key}) + endif() + + target_compile_definitions(${target_name} + PRIVATE + DESKTOP_APP_D3DCOMPILER_HASH=${key} + ) + + target_link_libraries(${target_name} + PRIVATE + desktop-app::external_openssl + ) +endfunction() diff --git a/validate_d3d_compiler.py b/validate_d3d_compiler.py new file mode 100644 index 0000000..3106ea0 --- /dev/null +++ b/validate_d3d_compiler.py @@ -0,0 +1,35 @@ +# This file is part of Desktop App Toolkit, +# a set of libraries for developing nice desktop applications. +# +# For license and copyright information please follow this link: +# https://github.com/desktop-app/legal/blob/master/LEGAL + +from __future__ import print_function +import sys, os, re, hashlib + +def error(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + sys.exit(1) + +try: + from win32api import GetFileVersionInfo +except ImportError: + error('Python module "pywin32" is not installed.\n\nTry "' + sys.executable + ' -m pip install pywin32".') + +if len(sys.argv) < 2: + error('Expected input path to "d3dcompiler_47.dll".') + +inputPath = sys.argv[1] +if not os.path.exists(inputPath): + error('File "' + inputPath + '" doesn\'t exist.') + +info = GetFileVersionInfo(inputPath, '\\') +version = [ info['FileVersionMS'] // 65536, info['FileVersionMS'] % 65536, info['FileVersionLS'] // 65536, info['FileVersionLS'] % 65536 ] +if (version != [10, 0, 20348, 1 ]): + error('Bad "d3dcompiler_47.dll" version: ' + '.'.join(str(x) for x in version)) + +bufferSize = 1024 * 1024 +sha256 = hashlib.sha256() + +with open(inputPath, 'rb') as f: + print(hashlib.sha256(f.read()).hexdigest()) diff --git a/win_directx_helper/CMakeLists.txt b/win_directx_helper/CMakeLists.txt index c98935c..19ade2c 100644 --- a/win_directx_helper/CMakeLists.txt +++ b/win_directx_helper/CMakeLists.txt @@ -12,3 +12,5 @@ nice_target_sources(win_directx_helper ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE win_directx_helper.cpp ) + +validate_d3d_compiler(win_directx_helper) diff --git a/win_directx_helper/win_directx_helper.cpp b/win_directx_helper/win_directx_helper.cpp index d987a32..36098dd 100644 --- a/win_directx_helper/win_directx_helper.cpp +++ b/win_directx_helper/win_directx_helper.cpp @@ -7,15 +7,43 @@ #include #include +#include +#include #include +#include +#include #define LOAD_SYMBOL(handle, func) LoadSymbol(handle, #func, func) namespace DirectX { namespace { +constexpr auto kMaxPathLong = 32767; + using Handle = HINSTANCE; +// d3dcompiler_47.dll + +HRESULT (__stdcall *D3DCompile)( + LPCVOID pSrcData, + SIZE_T SrcDataSize, + LPCSTR pFileName, + CONST D3D_SHADER_MACRO* pDefines, + ID3DInclude* pInclude, + LPCSTR pEntrypoint, + LPCSTR pTarget, + UINT Flags1, + UINT Flags2, + ID3DBlob** ppCode, + ID3DBlob** ppErrorMsgs); + +HRESULT (__stdcall *D3DDisassemble)( + _In_reads_bytes_(SrcDataSize) LPCVOID pSrcData, + _In_ SIZE_T SrcDataSize, + _In_ UINT Flags, + _In_opt_ LPCSTR szComments, + _Out_ ID3DBlob** ppDisassembly); + // d3d9.dll IDirect3D9 * (__stdcall *Direct3DCreate9)(UINT SDKVersion); @@ -56,6 +84,100 @@ inline bool LoadSymbol(Handle handle, const char *name, Function &func) { return (func != nullptr); } +// For win_directx_helper. +std::string FileSha256(const wchar_t *path) { + using uchar = unsigned char; + constexpr auto kLimit = 10 * 1024 * 1024; + auto buffer = std::vector(kLimit); + auto size = DWORD(); + + const auto file = CreateFile( + path, + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + if (file == INVALID_HANDLE_VALUE) { + return {}; + } + const auto read = ReadFile(file, buffer.data(), kLimit, &size, nullptr); + CloseHandle(file); + + if (!read || !size || size >= kLimit) { + return {}; + } + auto binary = std::array{}; + SHA256(buffer.data(), size, binary.data()); + const auto hex = [](uchar value) { + return (value >= 10) ? ('a' + (value - 10)) : ('0' + value); + }; + auto result = std::string(); + result.reserve(binary.size() * 2); + auto index = 0; + for (const auto byte : binary) { + result.push_back(hex(byte / 16)); + result.push_back(hex(byte % 16)); + } + return result; +} + +bool ResolveD3DCompiler(const wchar_t *path) { + const auto d3dcompiler = LoadLibrary(path); + return true + && LOAD_SYMBOL(d3dcompiler, D3DCompile) + && LOAD_SYMBOL(d3dcompiler, D3DDisassemble); +} + +bool ResolveD3DCompiler() { + static const auto loaded = [] { +#ifdef DESKTOP_APP_D3DCOMPILER_HASH + auto exePath = std::array{ 0 }; + const auto exeLength = GetModuleFileName( + nullptr, + exePath.data(), + kMaxPathLong + 1); + if (!exeLength || exeLength >= kMaxPathLong + 1) { + return false; + } + const auto exe = std::wstring(exePath.data()); + const auto last1 = exe.find_last_of('\\'); + const auto last2 = exe.find_last_of('/'); + const auto last = std::max( + (last1 == std::wstring::npos) ? -1 : int(last1), + (last2 == std::wstring::npos) ? -1 : int(last2)); + if (last < 0) { + return false; + } + +#if defined _WIN32 + const auto arch = L"x86"; +#elif defined _WIN64 // _WIN32 + const auto arch = L"x64"; +#else // _WIN32 || _WIN64 +#error "Invalid configuration." +#endif // _WIN32 || _WIN64 + +#define DESKTOP_APP_STRINGIFY2(x) #x +#define DESKTOP_APP_STRINGIFY(x) DESKTOP_APP_STRINGIFY2(x) + const auto hash = DESKTOP_APP_STRINGIFY(DESKTOP_APP_D3DCOMPILER_HASH); +#undef DESKTOP_APP_STRINGIFY +#undef DESKTOP_APP_STRINGIFY2 + + const auto compiler = exe.substr(0, last + 1) + + L"modules\\" + arch + L"\\d3d\\d3dcompiler_47.dll"; + const auto path = compiler.c_str(); + if (FileSha256(path) == hash && ResolveD3DCompiler(path)) { + return true; + } +#endif // DESKTOP_APP_D3DCOMPILER_HASH + + return ResolveD3DCompiler(L"d3dcompiler_47.dll"); + }(); + return loaded; +} + bool ResolveD3D9() { static const auto loaded = [] { const auto d3d9 = LoadLibrary(L"d3d9.dll"); @@ -63,7 +185,7 @@ bool ResolveD3D9() { LOAD_SYMBOL(d3d9, D3DPERF_EndEvent); LOAD_SYMBOL(d3d9, D3DPERF_SetMarker); LOAD_SYMBOL(d3d9, D3DPERF_GetStatus); - return true + return ResolveD3DCompiler() && LOAD_SYMBOL(d3d9, Direct3DCreate9); }(); return loaded; @@ -72,7 +194,7 @@ bool ResolveD3D9() { bool ResolveD3D11() { static const auto loaded = [] { const auto d3d11 = LoadLibrary(L"d3d11.dll"); - return true + return ResolveD3DCompiler() && LOAD_SYMBOL(d3d11, D3D11CreateDevice); }(); return loaded;