arm64: flat guest mapping, audio, crash reporting, self-extracting data

The run of work that took the translated build from "boots" to "playable".

Engine:
- Flat guest mapping replaces the software MMU on aarch64 hosts. A 4 GiB
  PROT_NONE reservation lets a 32-bit guest address zero-extend safely, so
  tcg_out_qemu_ld/st short-circuit before tcg_out_tlb_read and the prologue
  materialises the base into X28. Measured 1.24x (51 vs 41 fps, interleaved
  A/B). Note the base must be set BEFORE UC_INIT - Unicorn inits lazily, and
  setting it after uc_open silently falls back to softmmu.
- num_get<char> facet implemented, which was the real cause of the crash after
  the prologue race; a full race is now playable end to end.
- Thread-stack free list + ReleaseThreadEngine, fixing the arena leak that
  showed up as a black screen when entering a race. kMaxGuestThreads 16 -> 64.
- Real ARM32 FMOD now runs in-engine via the Java FMODAudioDevice bridge, with
  a per-thread JNIEnv. Two of the three blockers were our own single-image-era
  guards.

Host/app:
- Native crash handler: async-signal-safe, decodes the host fault back to a
  guest address, writes a report file and nothing else. CrashReportActivity
  picks it up on the NEXT launch, zips it, and offers to share. No backend, no
  automatic upload.
- Game data ships inside the APK and self-extracts on first launch, so a tester
  installs one file and plays. Copy-to-.part-then-rename, with a free-space
  check up front.
- EGL context preserved across pause, fixing black textures on resume.
- Navigation bar hidden and re-hidden on focus gain; volume keys reported as
  system keys, checked before the loading-state gate.
- x86_64 added to abiFilters: the ARM32 guest runs under tcg/i386 with no
  houdini in the path. The flat mapping is aarch64-only, so that host falls
  back to the software MMU - commented at the abiFilters line.

Ignore rules added for app/translated/ (611 MB of signed release APK, which
also carries the bundled OBB) and ostream_repro/build/.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-09-22 23:17:47 +03:00
co-authored by Claude
parent 80768652ea
commit 725ffbd8ed
44 changed files with 3096 additions and 86 deletions
+33
View File
@@ -0,0 +1,33 @@
#pragma once
// Native crash reporting (task #66 / BETA_TELEMETRY_PLAN.md).
//
// Most crashes in this project are NATIVE - a SIGSEGV inside JIT-generated
// code - so Java's uncaught-exception handler never sees them and the tester
// sees only "the game closed". This writes a report the moment it happens.
//
// What it does NOT do, deliberately: show any UI. A signal handler runs on a
// process that has already gone wrong, where only async-signal-safe calls are
// legal - no malloc, no JNI, no Activity. It writes one file with write() and
// then lets the process die. The report screen is shown by Java on the NEXT
// launch, which is how every serious crash reporter handles native crashes.
//
// The report lands on external storage (Android/data/<pkg>/files/crashes), so
// a tester can reach it over USB or a file manager without any permission.
#include <cstdint>
// Call once, early, with the directory reports should be written to (the app's
// external files dir). Creates the directory if needed, pre-builds every string
// the handler will need, installs an alternate signal stack so a stack-overflow
// crash can still be reported, and hooks the fatal signals.
//
// Safe to call more than once; only the first call installs anything.
void InstallCrashHandler(const char* crashDir, const char* buildStamp);
// Tells the handler where guest memory starts, so a host fault address can be
// reported as the GUEST address that caused it. Without this a report says
// "fault at 0x6f464c459b", which means nothing to anyone; with it the report
// also says "guest 0x464c459b", which is the number worth reading. Called by
// GuestEngine once its region is mapped.
void SetCrashHandlerGuestBase(uint64_t flatBase, uint32_t regionSize);