diff --git a/BUILD.md b/BUILD.md
index 847a373..f117610 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -30,6 +30,15 @@ Committed under [lib/](lib/), no install needed: `OpenAL32.lib`, `libsndfile-1.l
`assets/RP411/oalinst.exe` — or beside the exe) and `libsndfile-1.dll` (beside the
exe; a copy ships in `assets/RP411/`).
+It also needs **`d3dx9_43.dll`**, which is a hard import and is *not* part of
+Windows — it ships only with the June 2010 DirectX End-User Runtime. A dev box
+with the SDK installed has it system-wide and never notices; a clean Windows
+10/11 fails to start the exe at all (0xC0000135, before `WinMain`).
+[pack-dist.ps1](pack-dist.ps1) therefore extracts it from the SDK's
+`Redist\Jun2010_d3dx9_43_x86.cab` into `dist\`. That same file is also what
+makes the game run under Wine/Proton, whose builtin `d3dx9_43` stubs
+`D3DXConcatenateMeshes` — see [docs/RP412-LINUX.md](docs/RP412-LINUX.md).
+
## 2. Building
```powershell
diff --git a/RP_L4/RPL4.CPP b/RP_L4/RPL4.CPP
index ef57386..44be14b 100644
--- a/RP_L4/RPL4.CPP
+++ b/RP_L4/RPL4.CPP
@@ -83,6 +83,49 @@ HWND hWnd;
//
static LONG WINAPI RPL4CrashDumpFilter(EXCEPTION_POINTERS *exception)
{
+ //
+ // Wine raises 0x80000100 (EXCEPTION_WINE_STUB) when the game calls an
+ // API Wine declares but has never implemented, and puts the module and
+ // function names in the first two exception parameters as plain ANSI
+ // strings. Wine prints that to its own stderr - which a player running
+ // through Steam never sees - and then this filter caught the exception
+ // and wrote a minidump, so a perfectly self-describing failure arrived
+ // as a mystery crash. That is exactly how D3DXConcatenateMeshes cost a
+ // full investigation on 4.12.233: one stubbed function out of the
+ // twenty d3dx9 imports, and nothing in our log named it.
+ //
+ // Unlike an access violation this is an orderly raise from Wine with
+ // the heap intact, so the log stream is safe to use here. Written
+ // before the dump, in case the dump is what fails.
+ //
+ if (exception != NULL &&
+ exception->ExceptionRecord != NULL &&
+ exception->ExceptionRecord->ExceptionCode == 0x80000100 &&
+ exception->ExceptionRecord->NumberParameters >= 2)
+ {
+ const char *stub_module =
+ (const char *) exception->ExceptionRecord->ExceptionInformation[0];
+ const char *stub_function =
+ (const char *) exception->ExceptionRecord->ExceptionInformation[1];
+
+ if (stub_module != NULL && stub_function != NULL &&
+ !IsBadStringPtrA(stub_module, 128) &&
+ !IsBadStringPtrA(stub_function, 128))
+ {
+ DEBUG_STREAM << "\n*** WINE STUB: " << stub_module << "."
+ << stub_function << " is not implemented by this Wine.\n"
+ << "*** The game called an API Wine only declares. Shipping the"
+ << " real DLL beside the exe overrides Wine's builtin and is"
+ << " usually the whole fix; see docs/RP412-LINUX.md.\n"
+ << std::flush;
+ }
+ else
+ {
+ DEBUG_STREAM << "\n*** WINE STUB: an unimplemented API was called"
+ << " (names unreadable)\n" << std::flush;
+ }
+ }
+
HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
if (dbghelp != NULL)
{
@@ -225,6 +268,34 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
//
DEBUG_STREAM << "Red Planet " << RP412_VERSION_LONG << std::endl << std::flush;
+ //
+ // And whether this is Windows at all.
+ //
+ // Wine exports wine_get_version from ntdll and Windows does not, so
+ // one GetProcAddress separates the two. It matters because the
+ // failure modes differ: Wine implements Direct3D and the D3DX helpers
+ // itself, and where a helper is only a stub the game dies at the call
+ // with no hint in its own log (see the filter above). A tester's log
+ // saying which platform it came from turns that from an investigation
+ // into a lookup. Never true on Windows, where the lookup fails and
+ // nothing is printed.
+ //
+ {
+ HMODULE ntdll = GetModuleHandleA("ntdll.dll");
+ if (ntdll != NULL)
+ {
+ typedef const char * (__cdecl *WineGetVersionFn)(void);
+ WineGetVersionFn wine_get_version =
+ (WineGetVersionFn) GetProcAddress(ntdll, "wine_get_version");
+ if (wine_get_version != NULL)
+ {
+ DEBUG_STREAM << "Platform: Wine " << wine_get_version()
+ << " (not Windows) - D3DX comes from Wine's own"
+ << " implementation\n" << std::flush;
+ }
+ }
+ }
+
// load up our environment variables
//controls
if(getenv("L4CONTROLS") == NULL)
diff --git a/RP_L4/RP_L4.vcxproj b/RP_L4/RP_L4.vcxproj
index e45ccea..a6c0fd7 100644
--- a/RP_L4/RP_L4.vcxproj
+++ b/RP_L4/RP_L4.vcxproj
@@ -77,6 +77,23 @@
helper. -->
steam_api.dll;%(DelayLoadDLLs)
false
+
+ true
true