Crusader_Decomp/docs/jp-remorse-windows9x-investigation.md

200 lines
8.7 KiB
Markdown
Raw Normal View History

2026-03-29 01:14:09 +01:00
# Japanese No Remorse Windows 9x Investigation
## Question
Investigate the claim that the Japanese build of Crusader: No Remorse supports Windows 9x and can run natively on Windows 95 instead of requiring a DOS boot path.
Active Ghidra target for this note: `/ja/CRUSADER.EXE`.
## Verdict
Current static-analysis verdict: **strongly supports the claim**.
The loaded Japanese executable is not just a DOS executable with a small helper around it. It is a native flat Win32 image with Windows API startup, Windows registry configuration, DirectDraw/DirectSound initialization, and one explicit Win9x-aware compatibility branch.
What is proven from static analysis:
- the Japanese binary is a PE-style Win32 program loaded at `00400000`, with sections such as `BEGTEXT`, `DGROUP`, `.idata`, `.reloc`, and `.rsrc`
- it creates a real top-level Windows game window and runs through Win32 startup code
- it initializes DirectDraw and DirectSound directly
- it reads and writes settings in the Windows registry
- it contains Japanese/Win9x-facing support clues, including IME handling and a `GetVersion`-based compatibility branch
What is **not** proven by this pass:
- an actual runtime launch on real Windows 95 hardware or emulation
- whether extra runtime prerequisites such as the expected DirectX version are always present on a bare Windows 95 install
- whether every shipped JP build variant behaves the same way
So the safest wording is:
> The claim that the Japanese release has a native Windows 9x execution path is strongly supported by the code. The stricter claim that it will always run on a stock Windows 95 machine without additional runtime requirements is not fully closed by static analysis alone.
## Main Evidence
### 1. The loaded JP executable is a Win32 image, not the old DOS/NE game binary
Ghidra reports the active program as `/ja/CRUSADER.EXE` with entry at `004729e0`.
Relevant image layout recovered from Ghidra:
- `Headers: 00400000 - 004003ff`
- `BEGTEXT: 00401000 - 0047afff`
- `DGROUP: 0047b000 - 00481bff`
- `.bss: 00482000 - 00495fff`
- `.idata: 00496000 - 00496dff`
- `.reloc: 00497000 - 0049e3ff`
- `.rsrc: 0049f000 - 004a03ff`
That is a normal PE/Win32 layout, not the segmented NE or DOS-extender layout used by the original DOS executable.
### 2. The import table is decisively Windows-native
Recovered imports include all the major Windows subsystems expected of a native Win9x game executable:
- process and OS: `GetVersion`, `CreateThread`, `ExitProcess`, `TlsAlloc`, `VirtualAlloc`, `GetCommandLineA`, `GetModuleHandleA`, `GetModuleFileNameA`
- window/UI: `CreateWindowExA`, `RegisterClassA`, `DefWindowProcA`, `DispatchMessageA`, `PeekMessageA`, `ShowWindow`, `RegisterWindowMessageA`, `MessageBoxA`
- graphics: `DirectDrawCreate`, `CreateBitmap`, `CreateCompatibleDC`, `CreatePalette`, `TextOutA`
- audio/timing/input: `DirectSoundCreate`, `DirectSoundEnumerateA/W`, `timeGetTime`, `joyGetDevCapsA`, `joyGetPosEx`
- configuration/storage: `RegCreateKeyExA`, `RegOpenKeyExA`, `RegQueryValueExA`, `RegSetValueExA`, `CreateFileA`, `ReadFile`, `WriteFile`
- Japanese text/input support: `IsDBCSLeadByte`, `GetCPInfo`, `WINNLSEnableIME`
This is not a DOS launcher plus one or two helper calls. It is a full Windows application stack.
### 3. The main window path is explicit and native
`00445f40` is now named `win32_create_main_window`.
Key behavior recovered from decompilation:
- registers a window class with `RegisterClassA`
- creates a fullscreen popup window with `CreateWindowExA`
- uses the title string `"Crusader: No Remorse"`
- calls `ShowWindow(..., 10)`
- calls `WINNLSEnableIME(0,0)` immediately after successful creation
- registers `"MSWHEEL_ROLLMSG"`
This is direct evidence of a native Windows UI path. The IME call is especially relevant in a Japanese build because it shows the executable is consciously managing Windows-side JP text/input behavior.
### 4. DirectDraw and DirectSound are initialized directly by the game
`004459b0` is now named `video_init_directdraw_and_directsound`.
Recovered behavior:
- calls `DirectDrawCreate()`
- sets cooperative level and display mode on the DirectDraw object
- creates the palette and primary surface
- calls `DirectSoundCreate()`
- hides the cursor with `ShowCursor(0)`
This is a classic Windows 9x era DirectX setup path. It strongly supports the idea that this build was intended to run as a native Windows game executable.
### 5. Registry-backed configuration is built in
String at `0047b178`:
- `Software\Electronic Arts\Crusader: No Remorse\J1.21`
`004138e8` is now named `config_load_registry_and_cfg`.
Recovered behavior:
- reads `installpath`, `cdpath`, and `flicpath` from `HKEY_LOCAL_MACHINE`
- reads user preferences such as `video`, `subtitles`, `limitblasts`, `animation`, `frameskip`, `musicvolume`, `soundvolume`, and `mousespeed` from `HKEY_CURRENT_USER`
- falls back to parsing `crusader.cfg`
`00413760` is now named `config_write_registry_string` and writes string values back into the same registry branch.
This is strong evidence of a normal Windows-installed configuration model rather than a DOS-only setup flow.
### 6. There is a real Win9x-specific compatibility branch
`00472c41` is now named `win32_runtime_capture_process_context`.
Recovered behavior:
- captures environment strings
- captures module filename and command line
- calls `GetVersion()`
- stores split version fields into globals at `0x47c09f`, `0x47c0a0`, and `0x47c0a1`
The more important follow-up is `00476616`, now named `win32_tls_alloc_with_win9x_guard`.
Recovered behavior:
- calls `TlsAlloc()`
- checks the saved `GetVersion()` word
- if the version word has the high `0x8000` bit set, it retries while the TLS slot is less than `3`
That is not generic Windows code. It is exactly the kind of branch you expect when a program needs to behave differently on the Win9x line, where `GetVersion()` reports the platform using the high bit and some low TLS slots are treated specially.
This is the strongest single code-level clue that the executable was designed with Win9x behavior in mind, not just NT-family Windows.
## Secondary Evidence
### Japanese-specific Windows integration
The import set and main-window path together show several JP-specific Windows integration points:
- `WINNLSEnableIME`
- `IsDBCSLeadByte`
- `GetCPInfo`
That does not by itself prove Windows 95 compatibility, but it does reinforce that this is a localized Windows build, not a DOS build being loosely wrapped.
### WinMM and joystick APIs
The import table also includes:
- `joyGetDevCapsA`
- `joyGetPosEx`
- `timeGetTime`
These are ordinary Windows multimedia/input APIs and fit the same native-executable picture.
## Interpretation
The claim can be split into two parts.
### Part A: "The Japanese version supports Windows 9x"
This is strongly supported.
The binary:
- is a Win32 image
- uses Win32 startup and window management
- uses DirectX-era Windows multimedia APIs
- uses Windows registry storage
- has a `GetVersion` branch that specifically distinguishes Win9x-style version reporting
### Part B: "It can run on Windows 95 natively without switching to DOS first"
This is also strongly supported, but with one caveat.
The executable is clearly built to run as a native Windows application, and the version/TLS branch is the best evidence that Win9x behavior was considered explicitly. The remaining uncertainty is practical, not architectural: an actual Windows 95 runtime may still need the expected DirectX/runtime environment installed.
So the architectural answer is yes; the deployment answer is very likely yes, but not fully closed until runtime-tested on a Win95 environment.
## Ghidra Changes Made During This Pass
Renamed and commented in the active `/ja/CRUSADER.EXE` database:
- `00472c41` -> `win32_runtime_capture_process_context`
- `00476616` -> `win32_tls_alloc_with_win9x_guard`
- `00445f40` -> `win32_create_main_window`
- `004459b0` -> `video_init_directdraw_and_directsound`
- `004138e8` -> `config_load_registry_and_cfg`
- `00413760` -> `config_write_registry_string`
Each of those now has a decompiler comment explaining why it matters to the Win9x/native-Windows investigation.
## Remaining Follow-Up
1. Run the JP executable under an actual Windows 95 target or a faithful Win95 VM to confirm runtime behavior.
2. Identify the expected DirectX version from installer files or runtime error strings.
3. Trace the message pump and main game loop entry after window creation for a fuller WinMain reconstruction.
4. Locate the import thunks or callsites for `GetCPInfo` and `IsDBCSLeadByte` to document the JP text path more precisely.
5. Check whether the JP installer writes the same `J1.21` registry branch or whether the executable can self-heal missing keys.
6. Compare the JP Windows binary against the English DOS binary to isolate which subsystems were ported versus shared.