feat: test the DirectX June 2010 runtimes installed and used in both widths

This commit is contained in:
Mirko Brombin committed 2026-09-21 23:48:03 +00:00
1 parent 93927378aa
commit deee36d7a5
3 files changed
+180

No files matched your search

+19
View File
@@ -1937,6 +1937,25 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64|AMD64)$")
PASS_REGULAR_EXPRESSION "PASS: ")
endforeach()
endif()
# The DirectX End-User Runtimes of June 2010 (directx_Jun2010_redist.exe in the same directory), unpacked and installed as a
# game's setup does (scripts/native-directx-redist.py); a program of each width then uses what they installed.
if(TARGET raven_wayland AND RAVEN_LABWC AND RAVEN_GRIM AND RAVEN_PIL_PYTHON AND RAVEN_REDIST_DIRECTORY AND RAVEN_MINGW32
AND EXISTS ${RAVEN_REDIST_DIRECTORY}/directx_Jun2010_redist.exe)
set(directx_fixtures ${CMAKE_CURRENT_BINARY_DIR}/directx-fixtures)
add_custom_command(OUTPUT ${directx_fixtures}/directx-check64.exe ${directx_fixtures}/directx-check32.exe
COMMAND ${CMAKE_COMMAND} -E make_directory ${directx_fixtures}
COMMAND ${RAVEN_MINGW64} -O1 -nostdlib -fno-stack-protector -fno-builtin -mno-stack-arg-probe -Wl,--entry,raven_entry
-o ${directx_fixtures}/directx-check64.exe ${LOADER_SOURCES}/directx_check.c -lkernel32
COMMAND ${RAVEN_MINGW32} -O1 -nostdlib -fno-stack-protector -fno-builtin -Wl,--entry,_raven_entry
-o ${directx_fixtures}/directx-check32.exe ${LOADER_SOURCES}/directx_check.c -lkernel32
DEPENDS ${LOADER_SOURCES}/directx_check.c VERBATIM)
add_custom_target(directx_check_fixtures ALL DEPENDS ${directx_fixtures}/directx-check64.exe ${directx_fixtures}/directx-check32.exe)
add_test(NAME native_redist_directx_jun2010 COMMAND ${RAVEN_PIL_PYTHON} ${PROJECT_SOURCE_DIR}/scripts/native-directx-redist.py
$<TARGET_FILE:raven> ${RAVEN_REDIST_DIRECTORY}/directx_Jun2010_redist.exe ${directx_fixtures}/directx-check64.exe
${directx_fixtures}/directx-check32.exe ${CMAKE_CURRENT_BINARY_DIR}/directx-redist-evidence)
set_tests_properties(native_redist_directx_jun2010 PROPERTIES TIMEOUT 3600 RESOURCE_LOCK raven_compositor
PASS_REGULAR_EXPRESSION "PASS: ")
endif()
add_test(NAME native_x86_process COMMAND ${Python3_EXECUTABLE}
${PROJECT_SOURCE_DIR}/scripts/native-x86-process.py $<TARGET_FILE:raven> ${CMAKE_CURRENT_BINARY_DIR}/x86-client.exe)
set_tests_properties(native_x86_process PROPERTIES TIMEOUT 30)
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
"""Install the DirectX End-User Runtimes of June 2010 through Raven, as a game's setup does, and use what they install.
The package Microsoft publishes unpacks itself into a folder, then DXSETUP.exe runs silent from there. Before it runs, a program of each
width that needs the libraries finds none; after it, the shader compiler, D3DX, X3DAudio, XAudio2 and XInput load from the system
directories and work, every library is where Windows puts it, and DirectX's own log says the installation succeeded. A second run over
the installed runtimes succeeds as well, and the programs still work.
usage: native-directx-redist.py RAVEN PACKAGE CHECK64 CHECK32 EVIDENCE_DIR
"""
from pathlib import Path
import importlib.util
import shutil
import sys
# The compositor and the runs are the ones of the Visual C++ workflow, loaded without leaving compiled files in the tree.
sys.dont_write_bytecode = True
_spec = importlib.util.spec_from_file_location("native_redist", Path(__file__).with_name("native-redist.py"))
native_redist = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(native_redist)
# The libraries the runtimes install for Windows 10 in both system directories, by family and version.
LIBRARIES = ([f"d3dx9_{version}.dll" for version in range(24, 44)] + [f"d3dx10_{version}.dll" for version in range(33, 44)] +
["d3dx11_42.dll", "d3dx11_43.dll", "d3dcsx_42.dll", "d3dcsx_43.dll"] + [f"d3dcompiler_{version}.dll" for version in range(33, 44)] +
[f"xinput1_{version}.dll" for version in range(1, 4)] + [f"xaudio2_{version}.dll" for version in range(0, 8)] +
[f"x3daudio1_{version}.dll" for version in range(0, 8)] + [f"xapofx1_{version}.dll" for version in range(0, 6)] +
[f"xactengine2_{version}.dll" for version in range(0, 11)] + [f"xactengine3_{version}.dll" for version in range(0, 8)])
def check(raven, prefix, program, evidence, step, expected):
"""Runs a check program from the prefix's drive, as an installed game runs; it returns the number of the first check that failed."""
target = prefix / "drive_c" / program.name
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy(program, target)
log = evidence / f"{step}-{program.stem}.log"
native_redist.run(raven, prefix, target, [], log, timeout=120)
if f"Native entry returned {expected} ".encode() not in log.read_bytes():
raise RuntimeError(f"{step}: {program.name} did not return {expected}, see {log.name}")
def setup(raven, prefix, evidence, step):
code = native_redist.run(raven, prefix, prefix / "drive_c" / "dx" / "DXSETUP.exe", ["/silent"], evidence / f"{step}.log", timeout=900)
logs = prefix / "drive_c" / "windows" / "Logs"
text = (logs / "DirectX.log").read_text(encoding="utf-8", errors="replace")
shutil.copy(logs / "DirectX.log", evidence / f"{step}-DirectX.log")
if (logs / "DXError.log").exists():
shutil.copy(logs / "DXError.log", evidence / f"{step}-DXError.log")
last = [line for line in text.splitlines() if "Installation ended with value" in line]
if code != 0 or not last or "value 0 " not in last[-1]:
raise RuntimeError(f"{step}: DXSETUP ended with status {code}: {last[-1] if last else 'no result in DirectX.log'}")
def main():
raven, package, check64, check32, evidence = map(Path, sys.argv[1:6])
evidence.mkdir(parents=True, exist_ok=True)
prefix = evidence / "prefix"
shutil.rmtree(prefix, ignore_errors=True)
log = (evidence / "compositor.log").open("wb")
compositor = native_redist.start_compositor(log)
try:
# Without the runtimes the programs find no shader compiler.
check(raven, prefix, check64, evidence, "before", 1)
check(raven, prefix, check32, evidence, "before", 1)
code = native_redist.run(raven, prefix, package, ["/Q", "/T:C:\\dx"], evidence / "unpack.log", timeout=300)
if code != 0 or not (prefix / "drive_c" / "dx" / "DXSETUP.exe").exists():
raise RuntimeError(f"the package did not unpack (status {code})")
setup(raven, prefix, evidence, "install")
for folder in ("system32", "syswow64"):
present = {path.name.lower() for path in (prefix / "drive_c" / "windows" / folder).iterdir()}
missing = [name for name in LIBRARIES if name not in present]
if missing:
raise RuntimeError(f"{folder} lacks {', '.join(missing)}")
check(raven, prefix, check64, evidence, "installed", 0)
check(raven, prefix, check32, evidence, "installed", 0)
setup(raven, prefix, evidence, "reinstall")
check(raven, prefix, check64, evidence, "reinstalled", 0)
check(raven, prefix, check32, evidence, "reinstalled", 0)
print(f"PASS: DirectX June 2010 installed {len(LIBRARIES)} libraries in each system directory, both widths use them, installed again")
finally:
if compositor:
compositor.terminate()
compositor.wait(timeout=10)
if __name__ == "__main__":
main()
+74
View File
@@ -0,0 +1,74 @@
/* What a game finds after the DirectX redistributable of June 2010 has installed: its libraries load from the system directory and work.
* The shader compiler compiles a pixel shader, D3DX multiplies two matrices, X3DAudio initializes for stereo, and the XAudio2 and
* XInput libraries load. Built for both widths; the entry returns the number of the first check that failed. */
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef struct Blob Blob;
typedef struct {
HRESULT (WINAPI *QueryInterface)(Blob *, const GUID *, void **);
ULONG (WINAPI *AddRef)(Blob *);
ULONG (WINAPI *Release)(Blob *);
void *(WINAPI *GetBufferPointer)(Blob *);
SIZE_T (WINAPI *GetBufferSize)(Blob *);
} BlobMethods;
struct Blob {
const BlobMethods *methods;
};
typedef HRESULT (WINAPI *Compile)(const void *, SIZE_T, const char *, const void *, void *, const char *, const char *, UINT, UINT, Blob **,
Blob **);
typedef float *(WINAPI *Multiply)(float *, const float *, const float *);
/* Version 1.7 of X3DAudio returns nothing; it fills the handle in. */
typedef void (__cdecl *Initialize)(UINT32, float, BYTE *);
static const char shader[] = "float4 main() : SV_Target { return float4(1, 0.5, 0.25, 1); }";
int raven_entry(void)
{
HMODULE compiler = LoadLibraryA("d3dcompiler_43.dll");
if (!compiler)
return 1;
const Compile compile = (Compile)GetProcAddress(compiler, "D3DCompile");
if (!compile)
return 2;
Blob *code = NULL, *errors = NULL;
if (compile(shader, sizeof shader - 1, "check", NULL, NULL, "main", "ps_4_0", 0, 0, &code, &errors) != S_OK || !code)
return 3;
/* The bytecode is a DXBC container. */
const unsigned char *bytes = code->methods->GetBufferPointer(code);
if (code->methods->GetBufferSize(code) < 32 || bytes[0] != 'D' || bytes[1] != 'X' || bytes[2] != 'B' || bytes[3] != 'C')
return 4;
code->methods->Release(code);
HMODULE d3dx = LoadLibraryA("d3dx9_43.dll");
if (!d3dx)
return 5;
const Multiply multiply = (Multiply)GetProcAddress(d3dx, "D3DXMatrixMultiply");
if (!multiply)
return 6;
/* A scale by two followed by a translation of (3, 4, 5), in D3DX's row-vector order. */
float scale[16] = {2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1}, move[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3, 4, 5, 1}, result[16];
if (multiply(result, scale, move) != result || result[0] != 2 || result[5] != 2 || result[10] != 2 || result[12] != 3 || result[13] != 4 ||
result[14] != 5 || result[15] != 1)
return 7;
HMODULE audio = LoadLibraryA("X3DAudio1_7.dll");
if (!audio)
return 8;
const Initialize initialize = (Initialize)GetProcAddress(audio, "X3DAudioInitialize");
BYTE handle[20] = {0};
if (!initialize)
return 9;
initialize(3, 343.5f, handle);
BOOL filled = FALSE;
for (int index = 0; index < 20; ++index)
filled |= handle[index] != 0;
if (!filled)
return 12;
if (!LoadLibraryA("XAudio2_7.dll"))
return 10;
if (!LoadLibraryA("xinput1_3.dll"))
return 11;
return 0;
}