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 forever
If 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 = 0
next_in pointed at file offset 0x13 — i.e. inside the PHEEBs71 tag
- 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_mp and 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)
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:
If
inflate()returns anything other thanZ_OKorZ_STREAM_END, the loop re-tests thesame 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
0x0Cand begins inflating at0x10, instead of skipping the0x138signed 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.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 takethe 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 normalCOM_ERROR(e.g. "Fastfile for zone '...' iscorrupt 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
arm64ec with FEX (
libwow64fex.dll) as the 32-bit x86 translator.Loading fastfile code_pre_gfx_mpand then hangs forever withone thread at 100% CPU. It never errors and never times out.
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)