# ACE patch (Windows, clients ≤ 8.9) Builds a patched `BH3.exe` from a capture of the clean client's initialized state. Only `BH3.exe` is written, without a backup. 9.0+ clients use HoYoKProtect instead. ## Discovery (`ace/discovery.rs`) Read-only; all checks must pass: | File | Required layout | | --- | --- | | `BH3.exe` | 7 sections all named `.ace*`, 4 KiB section alignment, no TLS directory, entry calls the `BH3Base.dll` ordinal-1 import | | `BH3_Data/sharedassets0.assets` | `ChannelConfig` resolves to a known region | | `UnityPlayer.dll` | exports `UnityMain`; three writable decoder globals found via a `.text` code pattern | | `BH3_Data/Native/UserAssembly.dll` | exports `il2cpp_get_api_table`; executable `.tvm0` section with a virtual tail larger than its raw size | ## Capture (`ace/capture.rs`, `ace/process.rs`) After confirmation, launches the client (Windows directly; Linux via `umu-run` + Proton) and reads its memory read-only — no injection or protection tampering. Once the state is stable for 2 s (120 s timeout), it captures the `.tvm0` section, the three decoder states and the committed private executable allocations. ## Normalize and rebuild (`ace/runtime.rs`, `ace/generate.rs`) `runtime.rs` checks the captured `.tvm0` against the original after PE relocations, resolves `FF 25` jump slots against the captured allocations, and emits a position-independent blob and two thunk tables. `generate.rs` appends `.bh3code` (the `bootstrap.bin` stub, patched with the discovered RVAs/sizes per `bootstrap.json`) and `.bh3imp` (imports, decoder states and the runtime blob), then repoints the entry point. A `HI3AUTO1` receipt with SHA-256 hashes of the patched image, `UnityPlayer.dll` and `UserAssembly.dll` is appended; a repeat run that validates it is a no-op. Written via `util::replace::verified`.