Repository navigation
Description
What happened?
On ARM64 Android (Snapdragon 8 Gen 2) running the client under Wine/Proton arm64ec with
FEX as the x86→ARM64 translator, T6MP hangs forever at Loading fastfile code_pre_gfx_mp
— no error, no crash, one thread pinned at 100% CPU indefinitely.
I traced it to the guest instruction level, and there are two separate issues. The first one
is platform-independent and is the reason this presents as a silent freeze rather than an
error message.
1. The inflate error path is an infinite spin loop (affects all platforms)
The zone chunk loader does roughly:
inflateInit2_(&strm, -15, ...) ; raw deflate
call <decrypt/deblock>
call inflate ; inflate(&strm, Z_FULL_FLUSH)
cmp eax, 1
je done ; Z_STREAM_END -> fine
test eax, eax
jne <back to the cmp> ; eax is NEVER reloaded -> spins foreverIf inflate() returns anything other than Z_OK or Z_STREAM_END, the loop re-tests the
same stale return value forever. So any corrupt or unreadable fastfile becomes a
permanent silent hang instead of hitting the existing
Fastfile for zone '...' is corrupt or unreadable. error. That would apply on Windows too,
and it makes this class of problem very hard for users to self-diagnose.
2. Signed/encrypted zones are misdetected under x86→ARM translation
The loader treats the signed retail zone as if it were an unsigned one: it reads a chunk
size at offset 0x0C and begins inflating at 0x10, instead of skipping the 0x138
signed header (TAff0100 + version + PHEEBs71 + flags + zone name[32] + RSA signature[256])
and running the Salsa20 pass first. inflate() is therefore handed still-encrypted bytes.
Evidence, read out of the live process at the hang:
z_stream.msg="invalid distance too far back"total_in = 3,total_out = 0next_inpointed at file offset0x13— i.e. inside thePHEEBs71tag- the 512 KB input buffer was byte-identical to the on-disk
.ff, header and payload —
so the decryption pass had not modified it at all
I have reported the underlying translation problem to FEX as
FEX-Emu/FEX#5966. I am not asking you to support FEX or Android —
issue 2 is arguably theirs. Issue 1 is the one I think is worth fixing on your side,
since it turns every fastfile read failure into an unexplained freeze.
A useful corroborating detail: T4 (WaW) works perfectly on the exact same device, same
client build, same translator. WaW zones are unencrypted (IWffu100), so they never take
the decrypt path — which isolates the failure to the signed/encrypted zone path rather than
to zone loading in general.
Expected result
inflate() failing should raise the normal COM_ERROR (e.g. "Fastfile for zone '...' is
corrupt or unreadable.") instead of looping on a stale return value forever. Even just
breaking out of that loop on a negative return would turn a mystery freeze into an
actionable message.
Steps to reproduce
- BO2 (TU17) with Plutonium r5316 on ARM64 Android, running the client under Wine/Proton
arm64ec with FEX (libwow64fex.dll) as the 32-bit x86 translator. - Launch T6 Multiplayer from the launcher.
- The console window prints
Loading fastfile code_pre_gfx_mpand then hangs forever with
one thread at 100% CPU. It never errors and never times out. - For contrast, launch T4 Multiplayer on the same setup — it loads and runs normally.
On Windows, issue 1 alone should be reproducible by feeding the loader any fastfile that
fails to inflate: expect a hang rather than the corrupt-fastfile error.
Affected game(s)
BO2 Multiplayer (T6MP) — the hang. The spin loop itself is in shared zone-loading code and
would affect any title.
Affected component(s)
Client (game)
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
schindlershadow commentedon Sep 16, 2026
Additional confirmation: the spin reproduces on a second, completely independent x86→ARM stack. I think that's worth separating from the platform question, because it makes the client-side half of this clearly reproducible rather than "one weird emulator".
Behaviour is identical.
plutonium-bootstrapper-win32.exestarts normally, logs in, binds DW, prints--- Cmd Initialization Complete ---, and then:…and never prints another line. The
Databasethread sits at ~100% of a core with/proc/<tid>/syscall=runningon every sample (i.e. never in a syscall) andread_bytesflat — so it isn't reading, blocking or retrying, it's spinning in userspace. I left it 70+ minutes. Same result with Box64's default tuning and with its most conservative settings (exact flags, no block merging, strict memory ordering, no call/ret optimisation).To be clear about scope: the reason the fastfile fails to inflate on these devices is an emulation problem and not yours — the decrypt step is Themida-virtualised and doesn't execute under translation, so zlib is handed still-encrypted bytes. I'm not asking you to support that.
The part that is squarely in T6's court is the error path. When
inflate()returns a failure, the loop re-tests a stale register instead of re-reading the result:That's platform-independent. Any Windows user who hits a corrupt, truncated or partially-downloaded zone gets an unkillable frozen client with nothing written to
console.log, instead of an error naming the zone. In my case thez_streamhad already recorded a perfectly good diagnosis —msgwasinvalid distance too far back— it just never reaches the user.A bounded retry, or simply surfacing the zlib error and aborting the load, would turn a silent hang into a self-explanatory failure.
Happy to run anything else on the device if it's useful.
ineedbots commentedon Sep 28, 2026
This is due to Valve's CEG 2.0 encrypted/protected code sections, which was a novel DRM solution for BO2. Valve's obfuscation techniques are most likely not being emulated accurately. This isn't anything to do themida or even plutonium related, but the vanilla's game's DRM and/or x86 emulator you are using.
DB_AuthLoad_Init is the very FIRST CEG encrypted code section you encounter, and failure to call this function results in fastfiles failing to load due to the RSA sig not being consumed from the stream on the fastfile, cause in stream reads to be offset.
Do note that the latest steam EXEs released this month no longer have CEG applied to them, as CEG is no longer being offered by valve anymore.