Commit Graph
21 Commits
Author SHA1 Message Date
megboyzzandClaude 16f1125951 GuestHeap: add opt-in red zones and free poisoning (task #45)
First piece of actual verification infrastructure, rather than
symptom-driven debugging. Off by default (kHeapDebugChecks), so a normal
build is byte-for-byte the same arithmetic as before: kRedZoneBytes is 0 and
every guard branch folds away at compile time.

With checks on:
* each block carries a 16-byte guard immediately after the bytes the caller
  asked for - deliberately not at the end of the block, where slack left by
  a reused larger block would hide a small overflow - verified on Free and
  reported with the offending block's address and size;
* freed payloads are poisoned, which matters more since Alloc stopped
  zeroing: without it a freed block keeps plausible stale contents and a
  use-after-free silently keeps "working".

The check is deliberately not fatal. A loud, precise report is the point;
aborting mid-run would make the engine harder to debug than the corruption
being reported.

BlockHeader's former alignment-only `pad` field now carries the user-visible
size, maintained whether or not checks are on, so BlockSize() reports what
the caller actually got and realloc copies the right amount.

Verified in both directions before trusting it: a deliberate one-byte
overflow was caught and attributed to the right block ("guard byte 0 of 16
is 0x41, expected 0xbe"), while a clean neighbouring block produced no
report. Then a full run to the race with checks on: zero violations, zero
rejected frees, zero faults. That is partial evidence - the guard is only
checked when a block is freed, so blocks that live to process exit are never
examined - but it is the first evidence of its kind this project has.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-19 13:49:09 +03:00
megboyzzandClaude f631a3a0a4 Keep name-lookup acceleration unregistered: two failed attempts recorded
sub_4F3704 (resource lookup by name) is 18.8% of load-time samples and does
a linear strcmp scan over 6232 entries because the game's own hash cache is
disabled (*(self+8) == 0, confirmed by a live probe: 320,000+ calls per load).

Attempt 1 crashed the process: guest strings were read through G2H and
scanned for a NUL with no bounds check, so a bad offset walked off the end
of the mapped region (SIGSEGV, SEGV_ACCERR at a host address).

Attempt 2 was memory-safe (uc_mem_read everywhere, length and count caps,
every cache hit verified against live guest memory, falls through to the
guest on any doubt) but caused a visible frame-rate drop on the prologue
loading screen. The cause was a design flaw the earlier probe log had
already shown and I misread: the cache was keyed on the table ADDRESS, yet
this game reuses one address for different tables (35 and 59 entries
alternating in the log). The descriptor comparison therefore marked the
index stale on nearly every call, and each rebuild re-read all ~6232 entry
strings byte-by-byte - far more work than the scan it replaced.

Kept in the tree, unregistered, with both mistakes documented. The fix for a
third attempt is to key the cache on the DESCRIPTOR CONTENTS rather than the
address, so alternating tables each keep their own index, plus bulk string
reads instead of per-byte.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-19 02:21:08 +03:00
megboyzzandClaude d5e6037fc7 Stable checkpoint: game reaches playable 3D gameplay on ARM64
Verified live on a Pixel 6a: the game passes its EULA, loads the prologue
and renders real 3D gameplay, with zero heap exhaustion, zero faults and
zero rejected frees over a full session.

Root causes fixed in this state, each backed by a measurement (details and
the list of refuted theories live in ARM64_TRANSLATION_LAYER.md):

* JNI varargs float->double promotion. C promotes float to double in any
  varargs call and every Call*Method form is varargs, so reading one 4-byte
  slot yielded the double's always-zero low half. EVERY float argument
  passed to Java was silently becoming 0; text was just where it showed.

* GuestHeap ~4x memory overhead. Power-of-two size classes carving the full
  class, plus segregated free lists that could never share memory between
  sizes. Reworked to exact sizing with O(log n) best-fit reuse and splitting
  (deliberately not a linear scan - this allocator already had an O(n) perf
  cliff in its history). Peak live now 207MB against the real A9's 199MB,
  fragmentation ~2.5MB. Also fixed: realloc reading past the old block on
  shrink, a 32-bit overflow in calloc, and drifting payload alignment.

* Unbounded FMOD fake-handle leak into the never-freeing permanent arena,
  which is why enlarging that arena had not helped.

* Frame presentation, corrected against A9 ground truth: the real frame has
  three default-framebuffer colour clears and ONE present at the end; this
  engine had been presenting on each of them.

Load-time acceleration (zlib_accel.cpp): host zlib now serves inflate and
crc32, the latter measured by the block profiler as the single hottest guest
routine at 17.7%. Streams are only taken over when this layer saw their own
inflateInit2_, so unknown streams (libpng's, among others) still run the
original emulated path.

name_lookup_accel.cpp is present but its hook is NOT registered - it crashed
on bad assumptions about guest table lifetime and is kept as a starting
point, with both mistakes recorded in its comments.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-19 02:12:03 +03:00
megboyzzandClaude Sonnet 5 9a5736bf3f Add GuestHeap size-classed reuse pool + oversized-allocation free-list
GuestHeap previously bump-allocated any request >64KiB and never
reclaimed it on free() - a real, sustained gameplay session eventually
exhausted the arena (traced live to two separate crashes: onCreate
never completing, and a NULL-vector dereference in sub_4BA588 caused
by a failed ~175KB allocation going unchecked by the real EA code).

Widen the existing O(1) size-class free list (already used for <=64KiB
requests) to cover the whole arena instead of adding a second reuse
mechanism next to it - oversized allocations now pool and reclaim the
same way small ones already did. Add matching desktop tests
(guest_heap_test.cpp, run via run_heap_tests.sh) covering the exact
failing size from the crash log and a sustained alloc/free cycle in an
arena too small to survive without reclaim.

Verified live on device: the crash is gone across a full 10-minute
session with sustained GLES rendering throughout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 14:12:32 +03:00
megboyzzandClaude Sonnet 5 74ee49881d Fix car class injection, two fresh-save crash bugs, medal-tracking regression; refactor lan_event_injection.h
- Hardcode car class enum for lobby car selection (fixed, small set by design)
- Fix GetCurrentCarColor() crashing on an unvalidated color index from an
  unzeroed hashmap-miss fallback record on a genuinely fresh save
- Default kEnableTrackSubstitutionHook to false: it substituted every race's
  track unconditionally, crashing the game's own scripted prologue race
- Remove GetComponentNameSkipHook from JNI_OnLoad: it unconditionally
  replaced a real name used by the same cache-context the per-event medal
  progress record resolves through, silently breaking medal/street-completion
  tracking for every real race
- Split lan_event_injection.h (3149 lines) into car_selection.h,
  crash_workarounds.h, mod_slot_tracking.h, and a shared
  util/hook_install.h trampoline helper; delete ~490 lines of confirmed-dead
  experimental code; trim comment-heavy sections to essential context

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 22:34:49 +03:00
megboyzzandClaude Sonnet 5 cde392870e Add LAN car_select flow: real event handling, GameEvents JNI bridge, live car/upgrade/color capture, Compose UI example
Fixes the synthetic car_select jump for cold sessions, makes the loadout
exit chain safe for real (non-synthetic) events, and adds a native->Kotlin
GameEvents bridge (onMapLoaded/onRaceStarted/onRaceEnded/onUpgradesAccepted/
onCarSelected) so both the UI layer and a future native RatNet client can
learn what the player picked - car id, accepted upgrades, and paint color
(name + RGBA) are all resolved live from the game's own engine state
rather than a static extracted table, so they stay correct for any car
added later. Includes a Jetpack Compose overlay as a worked example of a
UI-side GameEventListener consumer.

Full investigation history, root causes, and the several dead ends ruled
out along the way are documented in PROGRESS.md (cont. 30-63b).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 15:30:21 +03:00
megboyzzandClaude Sonnet 5 1c6324b589 Add per-street targeted LAN event injection by group-name
Replaces the "inject into every pin" visual-confirmation mode with
matching against MapTrack's own event-group-name vector (offsets
0xE0/0xE4), which is available as soon as HandleEvent(evtype=1025)
fires regardless of whether any RaceEvent has resolved yet. Injection
now targets exactly one street (kTargetGroupName) and the card's
displayed name is an explicit parameter instead of a fixed constant.

Live-tested: single-target injection hits exactly the intended widget
with zero effect on the other 11 reachable MapTrack instances. The 12
reachable group names are all region/career-progression placeholders
(region{1,2,4,5}_{foothills,desert,chicago,newyork}_track{1,2,3}) -
none correspond to the currently visible/playable on-screen streets,
which a live AddEvent-level trace confirmed never call AddEvent during
normal play at all (see PROGRESS.md 2026-08-08 cont. 9/10 for the full
investigation, including the now-reverted diagnostic hooks used to
establish this).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-09 18:44:29 +03:00
megboyzzandClaude Sonnet 5 ca55b2ea97 Add synthetic LAN event injection PoC + Soak Test disable fix
Hooks MapTrack::HandleEvent to fabricate a RaceEvent+CashReward+fake
Actor and insert it via the engine's own AddEvent, making a synthetic
"LAN: <lobby>" card appear on an already-loaded street entirely at
runtime (no game_cache/OBB/native_lib changes). Also hooks the
per-frame MapScreen tick to neutralize a QA-only "Soak Test" feature
whose index-based scan of a parallel, unsynced list was the root
cause of a delayed crash on injected entries.

Verified live on Galaxy A9 (2018): zero crashes across all reachable
pins, and a 10-minute soak test with zero crashes after the Soak Test
fix, confirmed against a genuinely responsive post-test map screen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 14:16:29 +03:00
megboyzz d8a1f34a1c Add kEnableTrackSubstitutionHook toggle flag
Lets the track-substitution hook be enabled/disabled by flipping one
constexpr bool in main.cpp instead of commenting/uncommenting code, so
before/after comparisons don't need back-and-forth edits.
2026-08-05 15:24:15 +03:00
megboyzz 1cd5547b04 Начало savemanager 2025-09-05 18:38:01 +03:00
megboyzz 8c0276c562 Более менее стабильная версия 2025-08-28 12:49:40 +03:00
megboyzz 3cc802a9ea Rename .java to .kt 2025-08-28 12:49:40 +03:00
megboyzz 8951489609 Отключил х86 исправил ипут на Waydroid 2025-08-26 12:22:46 +03:00
megboyzz 38665f3545 Удаление лишних комментариев /* loaded from: classes.dex */ 2025-08-22 20:33:43 +03:00
megboyzz 2fcc4a2a57 Исправил получение базового адреса 2025-04-06 20:59:53 +03:00
megboyzz 6dd4612a88 Добавил mpcore 2025-04-06 20:23:24 +03:00
megboyzz ea29c5a3fc Вырезание ресурсов 2025-03-31 21:50:19 +03:00
megboyzz 3b16cf38db Рабочий первый рабочий коммит 2025-03-31 21:19:55 +03:00
megboyzz 4b7a924752 Перенос с маленькими правками 2025-03-30 18:08:41 +03:00
megboyzz 0134f6d689 Перенос с маленькими правками 2025-03-30 18:07:34 +03:00
megboyzz 72f40cc560 Начало репака 128 версии 2025-03-30 15:14:17 +03:00