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