[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DB203BCZZ9CP48FTV4J3P0":3,"skill-same:01M6DB203BCZZ9CP48FTV4J3P0":66},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":33,"linkOnly":34,"markdownContent":35,"markdownBody":36,"frontMatter":37,"refName":40,"commitSha":41,"skillDirectory":42,"forksCount":14,"repositoryHtmlUrl":43,"repositoryHomepageUrl":6,"ownerHtmlUrl":44,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":45,"install":46,"resources":59},"01M6DB203BCZZ9CP48FTV4J3P0","battlecats-event-unlock",null,"Use when working on Battle Cats (EN 15.4.1, rooted BlueStacks) special\u002Fevent\u002Ftower stage visibility or on-demand unlock RE - gives the proven dual-dependency model (tree-admission + valid schedule row), the decisive 15.4.1 offsets, the Event Summoner tool, the full event atlas cross-links, and the approaches already proven DEAD. Earlier-version offsets in a changelog.","Prekzursil\u002Fagent-skills-toolchain","Prekzursil","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F54636077?v=4","01M6DACZ1PCQHD99SCR1YXG28B","Andrei Mihai Visalon","01M6DACZ670WZF535V3WYYSS4Z",0,"MIT","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fbattlecats-event-unlock\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32],"battlecats event unlock","valid","changelog","atlas","tool","model","schedule","use","version","dependency","en","active","full","stage","2026-11-10 10:12:30",false,"---\nname: battlecats-event-unlock\ndescription: Use when working on Battle Cats (EN 15.4.1, rooted BlueStacks) special\u002Fevent\u002Ftower stage visibility or on-demand unlock RE - gives the proven dual-dependency model (tree-admission + valid schedule row), the decisive 15.4.1 offsets, the Event Summoner tool, the full event atlas cross-links, and the approaches already proven DEAD. Earlier-version offsets in a changelog.\nmetadata:\n  status: active\n  valid_as_of: 2026-08-27  # tbcml pointer re-verified (Codeberg canonical; GitHub archived, PyPI stale)\n---\n\n# Battle Cats — Special \u002F Event Stage Unlock\n\nReverse-engineering on-demand unlock\u002Fvisibility for special, event, and tower\n(Heavenly Tower) stages in Battle Cats **EN v15.4.1** (`jp.co.ponos.battlecatsen`,\nclientVersion `150400`, app_version_code `1504010`), x86_64 `lib\u002Fx86_64\u002Flibnative-lib.so`,\non rooted BlueStacks Pie64 + frida-server, ADB `127.0.0.1:5555`. Module imagebase 0x0,\nso **RVA == file offset** (unchanged from 15.3).\n\n- **Binary identity (15.4.1):** native build-id `d907e66ecce8388948f9ff4cde96a93735acbe92`;\n  `so_sha256 = 9605648b86c9176283f620b638f80e171b1df5731fdc3bed3295201883257676`\n  (so_size 12 666 808). All offsets below are `bindiff`-verified against this exact\n  on-device image (confirm-basis: `so_sha256` byte-matched the running BC pid 3807 on\n  2026-06-16 — the running binary IS the manifest-pinned binary, so every static offset is\n  correct by construction). Pinned manifest: `tools\u002Foffsets\u002Fen-15.4.1.json`.\n- **getGlobal fingerprint (15.4.1):** `488d0599fe5b00c3` (AOB `48 8D 05 ?? ?? ?? ?? C3`,\n  `lea_ret`; 13502 call-xrefs; ptr table `0xC1D7A0`).\n- **Older versions:** 15.3.0 offsets\u002Fidentity preserved in the **Version history \u002F changelog**\n  at the end of this file (do not delete — reference for cross-version RE).\n\n## The one rule — read this first\n\nA tile **RENDERS only if BOTH** of these hold:\n\n1. **Tree admission** — the mapid is admitted to the active-event-window RB-tree\n   (game_state + 11736 = +0x2DD8; read side `sub_5F8AC0`), which is built **ONCE at launch**\n   by the calendar-rebuild `sub_525AB0`.\n2. **Valid schedule row** — a valid `event_0` (sale.tsv) row covers \"now\" for that mapid.\n\nNeither half alone works:\n- **Row alone** → record loads + passes the activeness gate, but **0 tiles render**\n  (mapid not in the tree).\n- **Admit alone** → **CRASHES** (null-deref: tree entry with no row body to draw).\n- **Time-travel** supplies both, but trips HGT \u002F Recovery Mode → rejected (see below).\n\nYou must satisfy BOTH halves, with no clock change and no save edit.\n\n## PROVEN DEAD — do NOT retry these (rigid; do not adapt them away)\n\nThese were each tried and failed on this exact stack. Re-attempting them is wasted work.\n\n- **Forcing the render\u002Fdisplay gate `sub_817020` TRUE** (the date\u002Factiveness gate, callers\n  near `0x7f5d17` \u002F `0x81bb53` in 15.3 numbering). Too late in the pipeline to create a tree\n  entry. Tower ids 7000–7010 were 11\u002F11 in the candidate map yet **0\u002F11 rendered** when forced\n  this way. The old stage-unlock trainer does this — it does NOT surface tree-absent events.\n- **Forcing an OFF-WINDOW `sale.tsv` row + forcing `sub_817020`.** A forced\u002Foff-calendar row\n  that is made \"active\" by hammering the display gate does NOT create a tree entry (Tower 7xxx\n  routes to its own container `[r13+0x374388]`; gauntlet\u002Fall-day fall through). This is the\n  scope of the original \"towers can't be injected\" finding — see the correction in WHAT WORKS:\n  a **calendar-active always-on sentinel** row is a *different* thing and DOES work.\n- **Always-on `sale.tsv` row ALONE for a mapid that routes to a foreign container** (no tree\n  admission for gauntlet\u002Fdojo\u002Fcolosseum bands). A 470-row always-on patch rendered **0 new\n  tiles** for those. For Special-Stages-tree bands a sentinel row IS sufficient (see below) —\n  the dead case is the foreign-container bands, which need the patch.\n- **Blanket-forcing the rebuild call site `0x5292e4`-class** → **CRASHES** BC. Any injection\n  must be scoped and structurally valid (one mapid, with a backing row).\n- **Forced tree-admission ALONE (no valid row body)** → **CRASHES** on the menu rebuild\n  (null-deref on missing row data). Admission must be backed by a row.\n- **Naive time-skipping \u002F OS clock jump \u002F MITM `getTime` + `.dat` cache-clear.**\n  Trips BC **Recovery Mode** (which HIDES events) and can cascade into the\n  **99-day half-XP HGT penalty**. NEVER do MITM, cache-clear, or time-endpoint\n  manipulation on this stack. (It was the *delivery*, not the data, that did the damage.)\n\n## WHAT WORKS\n\n**Primary path (verified live 2026-05-26 on 15.3 — data-only, no Frida, no `.so` patch;\ndata path unchanged on 15.4.x):**\n\n- **Patch `event_0`** with an always-on row (`20300101` end-date sentinel,\n  `20160101` start, `min_ver 50700`) for the target mapid(s). A calendar-active row makes\n  the admission filter `sub_819E20` return true *naturally* during the launch-time\n  tree-populate loop → the mapid enters the calendar tree → the Special-Stages menu\n  rebuild draws the tile → entry gates allow play. **Neither a Frida hook nor a static\n  `.so` patch is required** for any mapid with `local_assets=Y` **that routes to the\n  Special-Stages tree** (towers, Legend Quest, Challenge Battle, Behemoth Culling, Grand\n  Abyss, Way of the Cat, cyclones, seasonal\u002Fanniversary\u002Fcollab-EN). Persistent, reversible,\n  survives relaunch.\n- **TOWERS ARE DATA-ONLY ACTIVATABLE** (correction to the old \"towers PROVEN DEAD\" note):\n  Heavenly Tower 7000 rendered + was **PLAYED on an unmodified `.so`** via a calendar-active\n  sentinel row (2026-05-26 [VALIDATED]). The earlier \"tower 7xxx can't be injected via\n  sale.tsv\" finding was scoped to **off-window forced rows + forcing `sub_817020`** — it does\n  NOT apply to an always-on sentinel row whose window covers now. **7011 Fascinating Fallen\n  Tower content IS on EN** (stageID 7011 \u002F `MapStageDataV_011.csv`); it just lacks an event_0\n  row in either file → append one sentinel row to activate.\n\n**Reset-risk caveat (the \"Golden Tower bug\" class):** re-admitting a **repeatable** stage as\na fresh always-on window presents it as uncleared → **clear-progress \u002F treasure RESETS**\n(reset is keyed to the event-window *instance* for repeatables — helpers\n`sub_4DF750`\u002F`sub_4DF8B0`\u002F`sub_4DFA10`). HIGHEST risk: **Golden Tower 7010** (treasure re-roll\n= the named bug). Other reset_risk=Y: all Towers 7000–7011, Legend Quest 16000, Grand Abyss\n33000, Challenge Battle 12000, all Cyclones, all Gauntlets, Colosseum 36xxx, all Collabs.\n**Behemoth Culling 31000–31002 is the ONE reset-SAFE repeatable** (it keeps genuine daily\nstock rows, not sentinel-only injection — natural daily cadence, nothing to lose).\n**Mitigation:** prefer a **scoped Frida `sub_819E20→1`** admit (reverses on detach, never\nrewrites the persistent window) over a permanent `20300101` row when in-progress state\nmatters; or snapshot the save (BCSFE, read-only) FIRST.\n\n**Edge case — scoped Frida admission OR static `.so` patches** (when a mapid routes to a\nforeign container — gauntlet 24xxx \u002F dojo \u002F colosseum 36xxx — so a row alone doesn't admit;\nwhen you want a mapid admitted *without* making its row calendar-active, e.g. RE\u002Fdiagnostics;\nor when unlock should persist as part of an APK build rather than a data file):\n\n- Force `sub_819E20(rec, now)` @ **`0x823B00`** → return **1**, at the populate-loop call site\n  inside `sub_525AB0` @ **`0x52CE20`**, **scoped to one target mapid only** (mapid is read at\n  `rec+0x5C` \u002F `rec+0x60`). Fires during the Special Stages menu rebuild — attach and navigate.\n- Supporting offsets (15.4.1; use only as needed, scoped):\n  - `sub_5F8DF0` @ **`0x600D70`** — id-band fallback gate (GATE-C; true for 900–999 \u002F 35000–35045).\n  - Render\u002Factiveness bridge + tree-insert helpers: re-derive on 15.4 if needed (the 15.3\n    `sub_5F8A10`\u002F`sub_81BAF0` numbers no longer apply; not in the 15.4.1 manifest — host-fn\n    delta pass).\n  - Entry gates **to play** the stage: `sub_8A3350` @ **`0x8AD600`** → 0 (entry-status),\n    `sub_8A3520` @ **`0x8AD7D0`** → 1 (in-window predicate).\n- **HARD BOUNDARY:** if a mapid has no local StageData \u002F assets on-device, **no hook makes\n  it playable**. It needs a content bake (tbcml route, out of scope for the hook approach).\n  tbcml canonical home is **Codeberg** (`codeberg.org\u002Ffieryhenry\u002Ftbcml`, source install) — the\n  GitHub repo is ARCHIVED and PyPI 1.1.0 is stale (2023); do not fetch from either.\n  Forcing a tile for a mapid with absent content crashes on tile-build. For **collabs**,\n  `local_assets=Y` is metadata only — retired (旧) collab art can be GC'd → **crash on icon\n  draw if forced**; gate on EN-named + non-旧 + all-on-covered.\n- **General lesson:** single-function Frida hooks are **reliable** here — the working\n  stage-progression trainer proves it. The historical failure was *conceptual* (missing\n  the data + admission dual requirement), not a hooking-skill gap. Do not overcomplicate\n  with multi-hook forging schemes.\n\nStatic byte stubs (rebuilt-APK route, all HGT-safe — lie about gate answers only, never time):\n`B8 01 00 00 00 C3` = `mov eax,1; ret` (for `sub_819E20` @ `0x823B00`, `sub_5F8DF0` @\n`0x600D70`, `sub_8A3520` @ `0x8AD7D0`); `31 C0 C3` = `xor eax,eax; ret` (for `sub_8A3350` @\n`0x8AD600`). The live preference is the scoped Frida hook, which forces these per-target and\nreverses on detach.\n\n## Trainer self-verify + GUI (15.4.x)\n\nThe trainer now **self-verifies the binary identity at launch** before writing any memory:\n`tools\u002Foffsets\u002Fbc_self_verify.py` hashes the on-device `.so` (resolved from `\u002Fproc\u002F\u003Cpid>\u002Fmaps`\nfor the attached PID — TOCTOU-bound) against the manifest pin (`en-15.4.1.json` `so_sha256`).\n**MATCH → offsets are correct by construction → writes enabled; anything else fails closed.**\nRe-derives the verdict on any reconnect \u002F PID change. The PySide6 trainer GUI ships as a\nself-contained `.exe`. The legacy stage-unlock path is unchanged: the **data-only event_0\npatch** + the **`enable-combined` Frida hook** (offsets bumped to the 15.4.x values above).\n\n## Event atlas (the full per-stage knowledge base — cross-link, do not duplicate)\n\nThe complete activation atlas for EVERY non-regular stage lives in five read-only\ngame-knowledge docs (synthesized 2026-06-16, EN 15.3.0 content authority \u002F 15.4.1 game-data):\n\n- `D:\\ReverseTools\\Android\\battlecats-toolkit\\docs\\game-knowledge\\BC-EVENT-ARCHITECTURE.md`\n  — **activation-model authority**: dual-dependency model, the event_0 row schema, the\n  band→category map, the master table of ALL specials, the **activation matrix**\n  (DATA-ONLY \u002F NEEDS-TREE-ADMIT-patch \u002F NEEDS-BAKE), and the reset-risk class.\n- `BC-STAGE-COMPENDIUM.md` — living 8-dimension atlas (cross-region asset matrix, cat-linkage,\n  semi-story\u002Fprogressive chains, version history, critic gap-closures).\n- `BC-EVENT-GLOSSARY.md` — every event family named + defined.\n- `BC-EVENT-ROSTER.generated.tsv` — per-stage name roster (647 rows; regenerate on version bump).\n- `BC-DATA-FORMATS.md` — field-by-field decode of every cited data file (event_0, MapStageData,\n  Map_option, All_day_event, ...).\n\nKey structural facts these docs add (fold into any unlock plan):\n- **THREE mapid namespaces**, not two: content (`Map_option` stageID), schedule (`event_0`\n  MapStageData mapid), and a **runtime 5xxxx** space (`stagedatamgr_dump.json` — 50321\u002F50732\u002F\n  51035 + gauntlet 5100\u002F5110).\n- **Six schedule-only artifact bands** (8xxx \u002F 9089–9682 \u002F 15xxx \u002F 17xxx \u002F 26xxx \u002F 28xxx) carry\n  well-formed live PONOS rows but have **ZERO EN content** → a row cannot fabricate a tile\n  without content (these need a bake or are non-events). 9xxx (74 ids) is the largest collab hole.\n- **event_0 row-schema correction:** field 7 = `n_slot_groups` (NOT \"map_count\"); structure is\n  `8-field header → N slot-groups → [map_count, mapid × map_count, trailing_flag?]`. The\n  multi-mapid carrier-row template (Way of the Cat 37000–37011 = ONE row, `map_count=12`,\n  `min_ver 140000`) is now documented. Full decode in `BC-DATA-FORMATS.md` §1.\n\n## Tools (point to these; do not reproduce their code)\n\nLocated in `D:\\ReverseTools\\Android\\battlecats-toolkit\\tools\\frida-trainer\\`:\n\n- `bc_event_index.py` — `list` \u002F `resolve \u003Cname|mapid>` → `{name, mapid, windows,\n  active_now, local_assets}`. Decodes `event_0.dat` (AES-128-ECB, key `89a0f99078419c28`).\n- `bc_event_summon.py` — host CLI (attaches, does **not** spawn):\n  `probe` \u002F `summon --mapid \u003Cid> [--play] [--render-support] [--keep-alive]` \u002F `stop-host`.\n- `bc_event_summon_agent.js` — the Frida agent (probe \u002F summon RPC; fully reversible).\n- **event_0 patch:** `dat-cache-decode\u002Fbuild_event0_allon.py` + `push_event0.py`.\n  HGT-safe in-place push = `su -c 'cat new > files\u002F\u003Cmd5(event_0)>.dat'`, then chown 10065,\n  restorecon, and **force-stop first**. On dup-device setups push manually with\n  `adb -s 127.0.0.1:5555`. Never delete-and-recreate (changes owner\u002FSELinux context).\n- **Identity self-verify:** `tools\u002Foffsets\u002Fbc_self_verify.py` (hashes on-device `.so` vs the\n  `en-15.4.1.json` pin; MATCH → writes safe; read-only, fail-closed).\n- **BCSFE time-fix** (clears a NEW drift penalty — not an active recovery countdown):\n  `BCSFE-Python` → `Fixes.fix_time_errors`.\n\n## Workflow (the actionable core)\n\n**For \"play any event on demand\" — DATA-ONLY (no Frida needed; Special-Stages-tree bands):**\n\n1. **Resolve target.** `bc_event_index.py resolve \u003Cname>` → mapid; confirm\n   `local_assets == true`. If false, STOP — needs a content bake, no patch helps. Check the\n   activation matrix (`BC-EVENT-ARCHITECTURE.md` §4): if it routes to a foreign container\n   (gauntlet\u002Fdojo\u002Fcolosseum), the data-only path won't admit it — use the Frida\u002Fpatch edge case.\n2. **Build patched event_0** (or use a bundled all-on file, dojo excluded):\n   `python dat-cache-decode\u002Fbuild_event0_allon.py`\n3. **HGT-safe in-place push** (round-trip check + force-stop happen for you):\n   `python dat-cache-decode\u002Fpush_event0.py push \u003Cplain.tsv>`\n4. **Launch BC. PASS** = tile renders, stage plays, NO Frida, NO HGT\u002FRecovery, **no\n   save-timestamp change**, persists across relaunches.\n5. **Revert to stock anytime:** `python dat-cache-decode\u002Fpush_event0.py revert`\n   (restores from bundled `event0_backup\u002Fev0_binpull.dat`).\n6. **Repeatable + want to keep progress?** Snapshot the save (BCSFE, read-only) first, or use\n   the scoped Frida admit instead of a permanent row (see reset-risk caveat above).\n\n**Edge case — Frida scoped admit (foreign-container bands \u002F off-calendar admission \u002F RE):**\n\na. **Read-only probe** (stock boot). `bc_event_summon.py probe` → confirm the mapid\n   is ABSENT from the tree.\nb. **Scoped admit.** `summon --mapid \u003Cid> --keep-alive`, then navigate to Special\n   Stages to trigger the rebuild; `treedump` to confirm admission. Detach restores\n   stock.\n\n**Discipline:** single actor, serial for any live Frida \u002F ADB step — the emulator is\na shared resource. Subagents do code\u002Fdocs only; they never touch the live game, ADB,\nclock, or save.\n\n## Reference\n\nFull detail lives in:\n- `D:\\ReverseTools\\Android\\battlecats-toolkit\\docs\\EVENT-UNLOCK-PLAYBOOK.md`\n- The five `docs\\game-knowledge\\BC-EVENT-*` docs (event atlas, above).\n- `docs\\research\\2026-05-25-DEFINITIVE-TRUTH.md`\n- `docs\\event-summoner-design.md`\n- `dat-cache-decode\\STARTUP_FILTER_RE.md`\n- `tools\\offsets\\en-15.4.1.json` (the pinned offset manifest) + `bc_self_verify.py`.\n\n---\n\n## Version history \u002F changelog\n\nOffsets are pinned per game version in `tools\u002Foffsets\u002Fen-\u003Cver>.json`. Earlier versions are\nkept here packed for cross-version RE reference (NOT scraped). When BC updates, re-derive via\n`bc_self_verify.py` + a bindiff pass and add a new subsection — do not overwrite this history.\n\n### 15.4.0 \u002F 15.4.1 (clientVersion 150400, app_version_code 1504010) — CURRENT\n\n- **Identity:** native build-id `d907e66ecce8388948f9ff4cde96a93735acbe92`;\n  `so_sha256 9605648b86c9176283f620b638f80e171b1df5731fdc3bed3295201883257676`; so_size\n  12 666 808; getGlobal fingerprint `488d0599fe5b00c3`. Verified live 2026-06-16 (BC pid 3807).\n- **Offset drift vs 15.3.0** (all bindiff-verified against `en-15.4.1.json`):\n\n  | symbol | 15.3.0 RVA | 15.4.1 RVA | drift | note |\n  |---|---|---|---|---|\n  | `getGlobal` | `0x653C80` | `0x65D900` | **+0x9C80** | NOT the +0x53A0 wallet cluster — its own drift |\n  | `getCash` | `0x37C170` | `0x381510` | +0x53A0 | wallet table |\n  | `setCash` | — | `0x381550` | +0x53A0 | — |\n  | `getCap` | — | `0x3815D0` | +0x53A0 | wallet base 0x468→0x450 |\n  | `sub_819E20` (admission filter) | `0x819E20` | `0x823B00` | **+0x9CE0** | field 0xfd8→0xfc8 |\n  | `sub_817020` (activeness gate) | `0x817020` | `0x820D00` | **+0x9CE0** | — |\n  | `sub_5F8DF0` (id-band GATE-C) | `0x5F8DF0` | `0x600D70` | +0x7F80 | field 0x2ea8→0x2e98; band 0x88B8 kept |\n  | `sub_8A3350` (entry-status →0) | `0x8A3350` | `0x8AD600` | **+0xA2B0** | masks 0x188000\u002F0x402001 kept |\n  | `sub_8A3520` (in-window →1) | `0x8A3520` | `0x8AD7D0` | +0xA2B0 | — |\n  | `sub_525AB0` (calendar rebuild) | `0x525AB0` | `0x52CE20` | +0x7370 | r2 body-anchored, MED-HIGH conf |\n  | `sub_4F1E20` (guerrilla getter) | `0x4F1E20` | `0x4F9060` | +0x7240 | tiny-leaf byte-identical |\n  | `SPEED_OFF` (game_state field) | `0x317B30`* | `0x31D7A0` | — | reader site 0x4B4DE2→0x4BB392 |\n  | `GLOBAL_WALLET_OFFSET` | — | `0x23E0` | — | moved DOWN -0x10 (IDA-resolved) |\n  | `INBATTLE_OFF` | — | `0x37D9F8` | — | `mov [rbx+0x37D9F8],1` |\n  | `OUTBREAK_OFFSET` | — | `0x321F80` | — | zombie_event_remaining_time double |\n\n  *15.3 `SPEED_OFF` shown for orientation; the load-bearing event-unlock offsets are the\n  `sub_*` rows. The three drift bands are **+0x9CE0** (admission\u002Factiveness), **+0xA2B0**\n  (entry gates), **+0x53A0** (getGlobal \u002F wallet cluster); `sub_5F8DF0` (+0x7F80) and\n  `sub_525AB0` (+0x7370) drift independently.\n\n- **LIVE-ONLY (no static candidate — re-derive on device each session):** calendar tree\n  `gs+0x2DD8`, guerrilla array `gs+0x11B8`, Aku block, the render\u002Fforge\u002Flist-builder code\n  sites (`RA_RENDERER` \u002F `RA_FORGE` \u002F `RA_LISTBUILDER`), and the outbreak Interceptor anchors —\n  all null in the 15.4.1 manifest (feature-gated off \u002F layout drifted; a wrong RA on the render\n  path crashes). Use IDA host-fn delta pass when a feature is re-enabled.\n- **What ALSO changed (non-offset):**\n  - **Trainer self-verify** added (`bc_self_verify.py`) — launch-time identity hash gate;\n    the PySide6 GUI ships as a self-contained `.exe`.\n  - **Towers correction:** towers ARE data-only activatable via a calendar-active sentinel row\n    (the old \"PROVEN DEAD\" entry was scoped to off-window forced rows + forcing `sub_817020`).\n  - **7011 Fascinating Fallen Tower** content confirmed present on EN.\n  - **THREE mapid namespaces** + **six schedule-only artifact bands** documented (event atlas).\n  - **event_0 row-schema correction:** field 7 = `n_slot_groups`, not \"map_count\".\n- **The data-only event_0 path is binary-version-INDEPENDENT** — it patches a data file, not\n  the `.so`, so it carried over from 15.3 unchanged. Only the Frida-hook \u002F static-patch RVAs\n  needed the bump above.\n\n### 15.3.0 (clientVersion 150300) — SUPERSEDED\n\n- **Identity:** x86_64 `lib\u002Fx86_64\u002Flibnative-lib.so`, IDA imagebase `0x0` ⇒ RVA == file offset;\n  `so_sha256 99bb6f36db1fc82a082f2a720c88325f9c146346fa0e0dbebae75205ab9eec7a`. Stack:\n  BlueStacks Pie64 + Magisk\u002FKitsune + frida-server 17.9.1, ADB `127.0.0.1:5555`.\n- **Offsets (15.3.0; RVA == file offset)** — superseded by the 15.4.1 table above:\n\n  | Address | Symbol | Role |\n  |---|---|---|\n  | `0x819E20` | `sub_819E20` | **PRIMARY LEVER** — startup tree-populate admission filter (call site `0x52653f` in `sub_525AB0`) |\n  | `0x817020` | `sub_817020` | display\u002Factiveness gate (date+ver+platform+slots); callers `0x7f5d17`, `0x81bb53`, rebuild `0x5292e4` — **PROVEN DEAD** as a lever |\n  | `0x5F8DF0` | `sub_5F8DF0` | id-band wall (GATE-C): true for 900–999 \u002F 35000–35045 |\n  | `0x4D2430` | `sub_4D2430` | main-chapter catalog (ids 1..721); −1 for all specials (GATE-C other half) |\n  | `0x5F8AC0` | `sub_5F8AC0` | calendar-tree enable query (Gate 1, read side) |\n  | `0x5F8A10` | `sub_5F8A10` | calendar-tree insert \u002F set-enable (Map A, write side) |\n  | `0x81BAF0` | `sub_81BAF0` | sale↔mapid activeness bridge (row provides date window) |\n  | `0x7F52D0` | `sub_7F52D0` | unified special\u002Fevent menu builder (the renderer) |\n  | `0x525AB0` | `sub_525AB0` | per-day recalc: builds tree + HGT gate + calls builder |\n  | `0x527270` | `sub_527270` | menu parent |\n  | `0x8A3350` | `sub_8A3350` | stage-entry status gate (`→0` to allow entry) |\n  | `0x8A3520` | `sub_8A3520` | in-window entry predicate (`→1` to allow entry) |\n  | `0x7F6EAF` | (row-skip branch) | builder skip of completed\u002Fprogress-gated rows (reversible 6-byte NOP) |\n\n- **15.3 facts that changed in 15.4:** all `sub_*` RVAs drifted (+0x9CE0 \u002F +0xA2B0 \u002F +0x7F80 \u002F\n  +0x7370 — see 15.4.1 table); the towers \"PROVEN DEAD\" framing was corrected (towers ARE\n  data-only activatable via a calendar-active sentinel row); `sub_5F8A10` \u002F `sub_81BAF0` \u002F\n  `sub_4D2430` \u002F `sub_5F8AC0` \u002F `sub_7F52D0` \u002F `sub_527270` are NOT in the 15.4.1 pinned\n  manifest (re-derive via host-fn delta if a hook needs them). The dual-dependency model,\n  the event_0 crypto (`89a0f99078419c28`), the `20300101` sentinel, and the HGT-safe in-place\n  delivery recipe are all UNCHANGED.\n","# Battle Cats — Special \u002F Event Stage Unlock\n\nReverse-engineering on-demand unlock\u002Fvisibility for special, event, and tower\n(Heavenly Tower) stages in Battle Cats **EN v15.4.1** (`jp.co.ponos.battlecatsen`,\nclientVersion `150400`, app_version_code `1504010`), x86_64 `lib\u002Fx86_64\u002Flibnative-lib.so`,\non rooted BlueStacks Pie64 + frida-server, ADB `127.0.0.1:5555`. Module imagebase 0x0,\nso **RVA == file offset** (unchanged from 15.3).\n\n- **Binary identity (15.4.1):** native build-id `d907e66ecce8388948f9ff4cde96a93735acbe92`;\n  `so_sha256 = 9605648b86c9176283f620b638f80e171b1df5731fdc3bed3295201883257676`\n  (so_size 12 666 808). All offsets below are `bindiff`-verified against this exact\n  on-device image (confirm-basis: `so_sha256` byte-matched the running BC pid 3807 on\n  2026-06-16 — the running binary IS the manifest-pinned binary, so every static offset is\n  correct by construction). Pinned manifest: `tools\u002Foffsets\u002Fen-15.4.1.json`.\n- **getGlobal fingerprint (15.4.1):** `488d0599fe5b00c3` (AOB `48 8D 05 ?? ?? ?? ?? C3`,\n  `lea_ret`; 13502 call-xrefs; ptr table `0xC1D7A0`).\n- **Older versions:** 15.3.0 offsets\u002Fidentity preserved in the **Version history \u002F changelog**\n  at the end of this file (do not delete — reference for cross-version RE).\n\n## The one rule — read this first\n\nA tile **RENDERS only if BOTH** of these hold:\n\n1. **Tree admission** — the mapid is admitted to the active-event-window RB-tree\n   (game_state + 11736 = +0x2DD8; read side `sub_5F8AC0`), which is built **ONCE at launch**\n   by the calendar-rebuild `sub_525AB0`.\n2. **Valid schedule row** — a valid `event_0` (sale.tsv) row covers \"now\" for that mapid.\n\nNeither half alone works:\n- **Row alone** → record loads + passes the activeness gate, but **0 tiles render**\n  (mapid not in the tree).\n- **Admit alone** → **CRASHES** (null-deref: tree entry with no row body to draw).\n- **Time-travel** supplies both, but trips HGT \u002F Recovery Mode → rejected (see below).\n\nYou must satisfy BOTH halves, with no clock change and no save edit.\n\n## PROVEN DEAD — do NOT retry these (rigid; do not adapt them away)\n\nThese were each tried and failed on this exact stack. Re-attempting them is wasted work.\n\n- **Forcing the render\u002Fdisplay gate `sub_817020` TRUE** (the date\u002Factiveness gate, callers\n  near `0x7f5d17` \u002F `0x81bb53` in 15.3 numbering). Too late in the pipeline to create a tree\n  entry. Tower ids 7000–7010 were 11\u002F11 in the candidate map yet **0\u002F11 rendered** when forced\n  this way. The old stage-unlock trainer does this — it does NOT surface tree-absent events.\n- **Forcing an OFF-WINDOW `sale.tsv` row + forcing `sub_817020`.** A forced\u002Foff-calendar row\n  that is made \"active\" by hammering the display gate does NOT create a tree entry (Tower 7xxx\n  routes to its own container `[r13+0x374388]`; gauntlet\u002Fall-day fall through). This is the\n  scope of the original \"towers can't be injected\" finding — see the correction in WHAT WORKS:\n  a **calendar-active always-on sentinel** row is a *different* thing and DOES work.\n- **Always-on `sale.tsv` row ALONE for a mapid that routes to a foreign container** (no tree\n  admission for gauntlet\u002Fdojo\u002Fcolosseum bands). A 470-row always-on patch rendered **0 new\n  tiles** for those. For Special-Stages-tree bands a sentinel row IS sufficient (see below) —\n  the dead case is the foreign-container bands, which need the patch.\n- **Blanket-forcing the rebuild call site `0x5292e4`-class** → **CRASHES** BC. Any injection\n  must be scoped and structurally valid (one mapid, with a backing row).\n- **Forced tree-admission ALONE (no valid row body)** → **CRASHES** on the menu rebuild\n  (null-deref on missing row data). Admission must be backed by a row.\n- **Naive time-skipping \u002F OS clock jump \u002F MITM `getTime` + `.dat` cache-clear.**\n  Trips BC **Recovery Mode** (which HIDES events) and can cascade into the\n  **99-day half-XP HGT penalty**. NEVER do MITM, cache-clear, or time-endpoint\n  manipulation on this stack. (It was the *delivery*, not the data, that did the damage.)\n\n## WHAT WORKS\n\n**Primary path (verified live 2026-05-26 on 15.3 — data-only, no Frida, no `.so` patch;\ndata path unchanged on 15.4.x):**\n\n- **Patch `event_0`** with an always-on row (`20300101` end-date sentinel,\n  `20160101` start, `min_ver 50700`) for the target mapid(s). A calendar-active row makes\n  the admission filter `sub_819E20` return true *naturally* during the launch-time\n  tree-populate loop → the mapid enters the calendar tree → the Special-Stages menu\n  rebuild draws the tile → entry gates allow play. **Neither a Frida hook nor a static\n  `.so` patch is required** for any mapid with `local_assets=Y` **that routes to the\n  Special-Stages tree** (towers, Legend Quest, Challenge Battle, Behemoth Culling, Grand\n  Abyss, Way of the Cat, cyclones, seasonal\u002Fanniversary\u002Fcollab-EN). Persistent, reversible,\n  survives relaunch.\n- **TOWERS ARE DATA-ONLY ACTIVATABLE** (correction to the old \"towers PROVEN DEAD\" note):\n  Heavenly Tower 7000 rendered + was **PLAYED on an unmodified `.so`** via a calendar-active\n  sentinel row (2026-05-26 [VALIDATED]). The earlier \"tower 7xxx can't be injected via\n  sale.tsv\" finding was scoped to **off-window forced rows + forcing `sub_817020`** — it does\n  NOT apply to an always-on sentinel row whose window covers now. **7011 Fascinating Fallen\n  Tower content IS on EN** (stageID 7011 \u002F `MapStageDataV_011.csv`); it just lacks an event_0\n  row in either file → append one sentinel row to activate.\n\n**Reset-risk caveat (the \"Golden Tower bug\" class):** re-admitting a **repeatable** stage as\na fresh always-on window presents it as uncleared → **clear-progress \u002F treasure RESETS**\n(reset is keyed to the event-window *instance* for repeatables — helpers\n`sub_4DF750`\u002F`sub_4DF8B0`\u002F`sub_4DFA10`). HIGHEST risk: **Golden Tower 7010** (treasure re-roll\n= the named bug). Other reset_risk=Y: all Towers 7000–7011, Legend Quest 16000, Grand Abyss\n33000, Challenge Battle 12000, all Cyclones, all Gauntlets, Colosseum 36xxx, all Collabs.\n**Behemoth Culling 31000–31002 is the ONE reset-SAFE repeatable** (it keeps genuine daily\nstock rows, not sentinel-only injection — natural daily cadence, nothing to lose).\n**Mitigation:** prefer a **scoped Frida `sub_819E20→1`** admit (reverses on detach, never\nrewrites the persistent window) over a permanent `20300101` row when in-progress state\nmatters; or snapshot the save (BCSFE, read-only) FIRST.\n\n**Edge case — scoped Frida admission OR static `.so` patches** (when a mapid routes to a\nforeign container — gauntlet 24xxx \u002F dojo \u002F colosseum 36xxx — so a row alone doesn't admit;\nwhen you want a mapid admitted *without* making its row calendar-active, e.g. RE\u002Fdiagnostics;\nor when unlock should persist as part of an APK build rather than a data file):\n\n- Force `sub_819E20(rec, now)` @ **`0x823B00`** → return **1**, at the populate-loop call site\n  inside `sub_525AB0` @ **`0x52CE20`**, **scoped to one target mapid only** (mapid is read at\n  `rec+0x5C` \u002F `rec+0x60`). Fires during the Special Stages menu rebuild — attach and navigate.\n- Supporting offsets (15.4.1; use only as needed, scoped):\n  - `sub_5F8DF0` @ **`0x600D70`** — id-band fallback gate (GATE-C; true for 900–999 \u002F 35000–35045).\n  - Render\u002Factiveness bridge + tree-insert helpers: re-derive on 15.4 if needed (the 15.3\n    `sub_5F8A10`\u002F`sub_81BAF0` numbers no longer apply; not in the 15.4.1 manifest — host-fn\n    delta pass).\n  - Entry gates **to play** the stage: `sub_8A3350` @ **`0x8AD600`** → 0 (entry-status),\n    `sub_8A3520` @ **`0x8AD7D0`** → 1 (in-window predicate).\n- **HARD BOUNDARY:** if a mapid has no local StageData \u002F assets on-device, **no hook makes\n  it playable**. It needs a content bake (tbcml route, out of scope for the hook approach).\n  tbcml canonical home is **Codeberg** (`codeberg.org\u002Ffieryhenry\u002Ftbcml`, source install) — the\n  GitHub repo is ARCHIVED and PyPI 1.1.0 is stale (2023); do not fetch from either.\n  Forcing a tile for a mapid with absent content crashes on tile-build. For **collabs**,\n  `local_assets=Y` is metadata only — retired (旧) collab art can be GC'd → **crash on icon\n  draw if forced**; gate on EN-named + non-旧 + all-on-covered.\n- **General lesson:** single-function Frida hooks are **reliable** here — the working\n  stage-progression trainer proves it. The historical failure was *conceptual* (missing\n  the data + admission dual requirement), not a hooking-skill gap. Do not overcomplicate\n  with multi-hook forging schemes.\n\nStatic byte stubs (rebuilt-APK route, all HGT-safe — lie about gate answers only, never time):\n`B8 01 00 00 00 C3` = `mov eax,1; ret` (for `sub_819E20` @ `0x823B00`, `sub_5F8DF0` @\n`0x600D70`, `sub_8A3520` @ `0x8AD7D0`); `31 C0 C3` = `xor eax,eax; ret` (for `sub_8A3350` @\n`0x8AD600`). The live preference is the scoped Frida hook, which forces these per-target and\nreverses on detach.\n\n## Trainer self-verify + GUI (15.4.x)\n\nThe trainer now **self-verifies the binary identity at launch** before writing any memory:\n`tools\u002Foffsets\u002Fbc_self_verify.py` hashes the on-device `.so` (resolved from `\u002Fproc\u002F\u003Cpid>\u002Fmaps`\nfor the attached PID — TOCTOU-bound) against the manifest pin (`en-15.4.1.json` `so_sha256`).\n**MATCH → offsets are correct by construction → writes enabled; anything else fails closed.**\nRe-derives the verdict on any reconnect \u002F PID change. The PySide6 trainer GUI ships as a\nself-contained `.exe`. The legacy stage-unlock path is unchanged: the **data-only event_0\npatch** + the **`enable-combined` Frida hook** (offsets bumped to the 15.4.x values above).\n\n## Event atlas (the full per-stage knowledge base — cross-link, do not duplicate)\n\nThe complete activation atlas for EVERY non-regular stage lives in five read-only\ngame-knowledge docs (synthesized 2026-06-16, EN 15.3.0 content authority \u002F 15.4.1 game-data):\n\n- `D:\\ReverseTools\\Android\\battlecats-toolkit\\docs\\game-knowledge\\BC-EVENT-ARCHITECTURE.md`\n  — **activation-model authority**: dual-dependency model, the event_0 row schema, the\n  band→category map, the master table of ALL specials, the **activation matrix**\n  (DATA-ONLY \u002F NEEDS-TREE-ADMIT-patch \u002F NEEDS-BAKE), and the reset-risk class.\n- `BC-STAGE-COMPENDIUM.md` — living 8-dimension atlas (cross-region asset matrix, cat-linkage,\n  semi-story\u002Fprogressive chains, version history, critic gap-closures).\n- `BC-EVENT-GLOSSARY.md` — every event family named + defined.\n- `BC-EVENT-ROSTER.generated.tsv` — per-stage name roster (647 rows; regenerate on version bump).\n- `BC-DATA-FORMATS.md` — field-by-field decode of every cited data file (event_0, MapStageData,\n  Map_option, All_day_event, ...).\n\nKey structural facts these docs add (fold into any unlock plan):\n- **THREE mapid namespaces**, not two: content (`Map_option` stageID), schedule (`event_0`\n  MapStageData mapid), and a **runtime 5xxxx** space (`stagedatamgr_dump.json` — 50321\u002F50732\u002F\n  51035 + gauntlet 5100\u002F5110).\n- **Six schedule-only artifact bands** (8xxx \u002F 9089–9682 \u002F 15xxx \u002F 17xxx \u002F 26xxx \u002F 28xxx) carry\n  well-formed live PONOS rows but have **ZERO EN content** → a row cannot fabricate a tile\n  without content (these need a bake or are non-events). 9xxx (74 ids) is the largest collab hole.\n- **event_0 row-schema correction:** field 7 = `n_slot_groups` (NOT \"map_count\"); structure is\n  `8-field header → N slot-groups → [map_count, mapid × map_count, trailing_flag?]`. The\n  multi-mapid carrier-row template (Way of the Cat 37000–37011 = ONE row, `map_count=12`,\n  `min_ver 140000`) is now documented. Full decode in `BC-DATA-FORMATS.md` §1.\n\n## Tools (point to these; do not reproduce their code)\n\nLocated in `D:\\ReverseTools\\Android\\battlecats-toolkit\\tools\\frida-trainer\\`:\n\n- `bc_event_index.py` — `list` \u002F `resolve \u003Cname|mapid>` → `{name, mapid, windows,\n  active_now, local_assets}`. Decodes `event_0.dat` (AES-128-ECB, key `89a0f99078419c28`).\n- `bc_event_summon.py` — host CLI (attaches, does **not** spawn):\n  `probe` \u002F `summon --mapid \u003Cid> [--play] [--render-support] [--keep-alive]` \u002F `stop-host`.\n- `bc_event_summon_agent.js` — the Frida agent (probe \u002F summon RPC; fully reversible).\n- **event_0 patch:** `dat-cache-decode\u002Fbuild_event0_allon.py` + `push_event0.py`.\n  HGT-safe in-place push = `su -c 'cat new > files\u002F\u003Cmd5(event_0)>.dat'`, then chown 10065,\n  restorecon, and **force-stop first**. On dup-device setups push manually with\n  `adb -s 127.0.0.1:5555`. Never delete-and-recreate (changes owner\u002FSELinux context).\n- **Identity self-verify:** `tools\u002Foffsets\u002Fbc_self_verify.py` (hashes on-device `.so` vs the\n  `en-15.4.1.json` pin; MATCH → writes safe; read-only, fail-closed).\n- **BCSFE time-fix** (clears a NEW drift penalty — not an active recovery countdown):\n  `BCSFE-Python` → `Fixes.fix_time_errors`.\n\n## Workflow (the actionable core)\n\n**For \"play any event on demand\" — DATA-ONLY (no Frida needed; Special-Stages-tree bands):**\n\n1. **Resolve target.** `bc_event_index.py resolve \u003Cname>` → mapid; confirm\n   `local_assets == true`. If false, STOP — needs a content bake, no patch helps. Check the\n   activation matrix (`BC-EVENT-ARCHITECTURE.md` §4): if it routes to a foreign container\n   (gauntlet\u002Fdojo\u002Fcolosseum), the data-only path won't admit it — use the Frida\u002Fpatch edge case.\n2. **Build patched event_0** (or use a bundled all-on file, dojo excluded):\n   `python dat-cache-decode\u002Fbuild_event0_allon.py`\n3. **HGT-safe in-place push** (round-trip check + force-stop happen for you):\n   `python dat-cache-decode\u002Fpush_event0.py push \u003Cplain.tsv>`\n4. **Launch BC. PASS** = tile renders, stage plays, NO Frida, NO HGT\u002FRecovery, **no\n   save-timestamp change**, persists across relaunches.\n5. **Revert to stock anytime:** `python dat-cache-decode\u002Fpush_event0.py revert`\n   (restores from bundled `event0_backup\u002Fev0_binpull.dat`).\n6. **Repeatable + want to keep progress?** Snapshot the save (BCSFE, read-only) first, or use\n   the scoped Frida admit instead of a permanent row (see reset-risk caveat above).\n\n**Edge case — Frida scoped admit (foreign-container bands \u002F off-calendar admission \u002F RE):**\n\na. **Read-only probe** (stock boot). `bc_event_summon.py probe` → confirm the mapid\n   is ABSENT from the tree.\nb. **Scoped admit.** `summon --mapid \u003Cid> --keep-alive`, then navigate to Special\n   Stages to trigger the rebuild; `treedump` to confirm admission. Detach restores\n   stock.\n\n**Discipline:** single actor, serial for any live Frida \u002F ADB step — the emulator is\na shared resource. Subagents do code\u002Fdocs only; they never touch the live game, ADB,\nclock, or save.\n\n## Reference\n\nFull detail lives in:\n- `D:\\ReverseTools\\Android\\battlecats-toolkit\\docs\\EVENT-UNLOCK-PLAYBOOK.md`\n- The five `docs\\game-knowledge\\BC-EVENT-*` docs (event atlas, above).\n- `docs\\research\\2026-05-25-DEFINITIVE-TRUTH.md`\n- `docs\\event-summoner-design.md`\n- `dat-cache-decode\\STARTUP_FILTER_RE.md`\n- `tools\\offsets\\en-15.4.1.json` (the pinned offset manifest) + `bc_self_verify.py`.\n\n---\n\n## Version history \u002F changelog\n\nOffsets are pinned per game version in `tools\u002Foffsets\u002Fen-\u003Cver>.json`. Earlier versions are\nkept here packed for cross-version RE reference (NOT scraped). When BC updates, re-derive via\n`bc_self_verify.py` + a bindiff pass and add a new subsection — do not overwrite this history.\n\n### 15.4.0 \u002F 15.4.1 (clientVersion 150400, app_version_code 1504010) — CURRENT\n\n- **Identity:** native build-id `d907e66ecce8388948f9ff4cde96a93735acbe92`;\n  `so_sha256 9605648b86c9176283f620b638f80e171b1df5731fdc3bed3295201883257676`; so_size\n  12 666 808; getGlobal fingerprint `488d0599fe5b00c3`. Verified live 2026-06-16 (BC pid 3807).\n- **Offset drift vs 15.3.0** (all bindiff-verified against `en-15.4.1.json`):\n\n  | symbol | 15.3.0 RVA | 15.4.1 RVA | drift | note |\n  |---|---|---|---|---|\n  | `getGlobal` | `0x653C80` | `0x65D900` | **+0x9C80** | NOT the +0x53A0 wallet cluster — its own drift |\n  | `getCash` | `0x37C170` | `0x381510` | +0x53A0 | wallet table |\n  | `setCash` | — | `0x381550` | +0x53A0 | — |\n  | `getCap` | — | `0x3815D0` | +0x53A0 | wallet base 0x468→0x450 |\n  | `sub_819E20` (admission filter) | `0x819E20` | `0x823B00` | **+0x9CE0** | field 0xfd8→0xfc8 |\n  | `sub_817020` (activeness gate) | `0x817020` | `0x820D00` | **+0x9CE0** | — |\n  | `sub_5F8DF0` (id-band GATE-C) | `0x5F8DF0` | `0x600D70` | +0x7F80 | field 0x2ea8→0x2e98; band 0x88B8 kept |\n  | `sub_8A3350` (entry-status →0) | `0x8A3350` | `0x8AD600` | **+0xA2B0** | masks 0x188000\u002F0x402001 kept |\n  | `sub_8A3520` (in-window →1) | `0x8A3520` | `0x8AD7D0` | +0xA2B0 | — |\n  | `sub_525AB0` (calendar rebuild) | `0x525AB0` | `0x52CE20` | +0x7370 | r2 body-anchored, MED-HIGH conf |\n  | `sub_4F1E20` (guerrilla getter) | `0x4F1E20` | `0x4F9060` | +0x7240 | tiny-leaf byte-identical |\n  | `SPEED_OFF` (game_state field) | `0x317B30`* | `0x31D7A0` | — | reader site 0x4B4DE2→0x4BB392 |\n  | `GLOBAL_WALLET_OFFSET` | — | `0x23E0` | — | moved DOWN -0x10 (IDA-resolved) |\n  | `INBATTLE_OFF` | — | `0x37D9F8` | — | `mov [rbx+0x37D9F8],1` |\n  | `OUTBREAK_OFFSET` | — | `0x321F80` | — | zombie_event_remaining_time double |\n\n  *15.3 `SPEED_OFF` shown for orientation; the load-bearing event-unlock offsets are the\n  `sub_*` rows. The three drift bands are **+0x9CE0** (admission\u002Factiveness), **+0xA2B0**\n  (entry gates), **+0x53A0** (getGlobal \u002F wallet cluster); `sub_5F8DF0` (+0x7F80) and\n  `sub_525AB0` (+0x7370) drift independently.\n\n- **LIVE-ONLY (no static candidate — re-derive on device each session):** calendar tree\n  `gs+0x2DD8`, guerrilla array `gs+0x11B8`, Aku block, the render\u002Fforge\u002Flist-builder code\n  sites (`RA_RENDERER` \u002F `RA_FORGE` \u002F `RA_LISTBUILDER`), and the outbreak Interceptor anchors —\n  all null in the 15.4.1 manifest (feature-gated off \u002F layout drifted; a wrong RA on the render\n  path crashes). Use IDA host-fn delta pass when a feature is re-enabled.\n- **What ALSO changed (non-offset):**\n  - **Trainer self-verify** added (`bc_self_verify.py`) — launch-time identity hash gate;\n    the PySide6 GUI ships as a self-contained `.exe`.\n  - **Towers correction:** towers ARE data-only activatable via a calendar-active sentinel row\n    (the old \"PROVEN DEAD\" entry was scoped to off-window forced rows + forcing `sub_817020`).\n  - **7011 Fascinating Fallen Tower** content confirmed present on EN.\n  - **THREE mapid namespaces** + **six schedule-only artifact bands** documented (event atlas).\n  - **event_0 row-schema correction:** field 7 = `n_slot_groups`, not \"map_count\".\n- **The data-only event_0 path is binary-version-INDEPENDENT** — it patches a data file, not\n  the `.so`, so it carried over from 15.3 unchanged. Only the Frida-hook \u002F static-patch RVAs\n  needed the bump above.\n\n### 15.3.0 (clientVersion 150300) — SUPERSEDED\n\n- **Identity:** x86_64 `lib\u002Fx86_64\u002Flibnative-lib.so`, IDA imagebase `0x0` ⇒ RVA == file offset;\n  `so_sha256 99bb6f36db1fc82a082f2a720c88325f9c146346fa0e0dbebae75205ab9eec7a`. Stack:\n  BlueStacks Pie64 + Magisk\u002FKitsune + frida-server 17.9.1, ADB `127.0.0.1:5555`.\n- **Offsets (15.3.0; RVA == file offset)** — superseded by the 15.4.1 table above:\n\n  | Address | Symbol | Role |\n  |---|---|---|\n  | `0x819E20` | `sub_819E20` | **PRIMARY LEVER** — startup tree-populate admission filter (call site `0x52653f` in `sub_525AB0`) |\n  | `0x817020` | `sub_817020` | display\u002Factiveness gate (date+ver+platform+slots); callers `0x7f5d17`, `0x81bb53`, rebuild `0x5292e4` — **PROVEN DEAD** as a lever |\n  | `0x5F8DF0` | `sub_5F8DF0` | id-band wall (GATE-C): true for 900–999 \u002F 35000–35045 |\n  | `0x4D2430` | `sub_4D2430` | main-chapter catalog (ids 1..721); −1 for all specials (GATE-C other half) |\n  | `0x5F8AC0` | `sub_5F8AC0` | calendar-tree enable query (Gate 1, read side) |\n  | `0x5F8A10` | `sub_5F8A10` | calendar-tree insert \u002F set-enable (Map A, write side) |\n  | `0x81BAF0` | `sub_81BAF0` | sale↔mapid activeness bridge (row provides date window) |\n  | `0x7F52D0` | `sub_7F52D0` | unified special\u002Fevent menu builder (the renderer) |\n  | `0x525AB0` | `sub_525AB0` | per-day recalc: builds tree + HGT gate + calls builder |\n  | `0x527270` | `sub_527270` | menu parent |\n  | `0x8A3350` | `sub_8A3350` | stage-entry status gate (`→0` to allow entry) |\n  | `0x8A3520` | `sub_8A3520` | in-window entry predicate (`→1` to allow entry) |\n  | `0x7F6EAF` | (row-skip branch) | builder skip of completed\u002Fprogress-gated rows (reversible 6-byte NOP) |\n\n- **15.3 facts that changed in 15.4:** all `sub_*` RVAs drifted (+0x9CE0 \u002F +0xA2B0 \u002F +0x7F80 \u002F\n  +0x7370 — see 15.4.1 table); the towers \"PROVEN DEAD\" framing was corrected (towers ARE\n  data-only activatable via a calendar-active sentinel row); `sub_5F8A10` \u002F `sub_81BAF0` \u002F\n  `sub_4D2430` \u002F `sub_5F8AC0` \u002F `sub_7F52D0` \u002F `sub_527270` are NOT in the 15.4.1 pinned\n  manifest (re-derive via host-fn delta if a hook needs them). The dual-dependency model,\n  the event_0 crypto (`89a0f99078419c28`), the `20300101` sentinel, and the HGT-safe in-place\n  delivery recipe are all UNCHANGED.\n",{"name":5,"metadata":38,"description":7},{"status":30,"valid_as_of":39},1787788800000,"main","a298c929b6cbd662e1fce54302ce520d5ceea1a5",".agents\u002Fskills\u002Fbattlecats-event-unlock","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain","https:\u002F\u002Fgithub.com\u002FPrekzursil","2026-11-02 14:30:57",{"source":8,"skillName":5,"commands":47,"prompt":57,"zipAvailable":58},[48,51,54],{"tool":49,"command":50},"npx","npx skills add Prekzursil\u002Fagent-skills-toolchain --skill battlecats-event-unlock",{"tool":52,"command":53},"bunx","bunx skills add Prekzursil\u002Fagent-skills-toolchain --skill battlecats-event-unlock",{"tool":55,"command":56},"pnpm","pnpm dlx skills add Prekzursil\u002Fagent-skills-toolchain --skill battlecats-event-unlock","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fbattlecats-event-unlock\u002FSKILL.md",true,[60],{"path":61,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":64,"sourceHtmlUrl":65},"EVENT-UNLOCK-PLAYBOOK.md","REFERENCE","参考文档",37313,"https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fbattlecats-event-unlock\u002FEVENT-UNLOCK-PLAYBOOK.md",[67,88,118,145,159,175],{"skillNo":68,"name":69,"nameZh":6,"description":70,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":71,"categories":72,"keywords":73,"publishedAt":33},"01M6DF7D7478R55P46REYH4QAC","clarify-requirements","Resolve genuine pre-execution ambiguity by scanning repo context, generating 1-6 grounded options, and asking 1-3 focused questions via AskUserQuestion. NOT for mid-task use (injection risk); NOT when the goal is already unambiguous; use to disambiguate vague prompts before any file edits.","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Fworkspace-hooks\u002Fskills\u002Fclarify-requirements\u002FSKILL.md",[],[74,75,76,77,78,79,80,81,82,26,83,84,85,86,87],"clarify requirements","clarify","requirements","risk","options","task","prompts","goal","scanning","context","repo","injection","execution","file",{"skillNo":89,"name":90,"nameZh":6,"description":91,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":92,"categories":93,"keywords":99,"publishedAt":33},"01M6DF79CA4BHJS66J8EZDBPWJ","openmm-md","Run FREE, open-source, Windows-native GPU molecular dynamics via the openmm-md MCP (OpenMM + CUDA on the local NVIDIA GPU). Use for protein \u002F protein-ligand MD when you want a free stack or are on Windows without a Schrodinger license. Covers the full workflow (prepare, solvate, minimize, equilibrate, production, analyze, mmgbsa) AND an honest decision guide vs Schrodinger Desmond, including how to run Desmond legitimately on Linux or a cloud GPU with a real license. This is a deliberately LESSER free alternative and does NOT replace the schrodinger MCP tools. Do NOT use it to pick a free tool when a validated FEP+, WaterMap, IFD-MD, or Prime MM-GBSA result is actually required.","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Fmcp\u002Fopenmm-md-mcp\u002Fskill\u002Fopenmm-md\u002FSKILL.md",[94],{"categoryNo":95,"categoryName":96,"taxonomyType":97,"taxonomyTypeDesc":98},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[100,101,102,103,104,105,23,106,107,108,109,110,111,112,26,113,114,115,116,117],"openmm md","how","decision","pick","linux","analyze","stack","open","NVIDIA","gpu","license","workflow","windows","free","molecular dynamics","local","do","MCP",{"skillNo":119,"name":120,"nameZh":6,"description":121,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":122,"categories":123,"keywords":130,"publishedAt":33},"01M6DF76C1MG85VEWP8VZCBNMN","visual-review","Take screenshots of web pages and UI using Playwright for visual review and iteration","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fvisual-review\u002FSKILL.md",[124,127],{"categoryNo":125,"categoryName":126,"taxonomyType":97,"taxonomyTypeDesc":98},"01JBSEEDCAT000000000000003","演示文稿",{"categoryNo":128,"categoryName":129,"taxonomyType":97,"taxonomyTypeDesc":98},"01JBSEEDCAT000000000000008","测试与质量",[131,132,133,134,135,136,137,138,139,140,101,141,142,143,144],"review","visual","visual review","pages","web","false","iteration","take","check","slides","ui","screenshot","playwright","screenshots",{"skillNo":146,"name":147,"nameZh":6,"description":148,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":149,"categories":150,"keywords":151,"publishedAt":33},"01M6DF73AQVCBZKEN0YXEX9FXP","status","Diagnostic status report — shows metaswarm installation state, project setup, and potential issues","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fstatus\u002FSKILL.md",[],[147,152,153,154,155,156,157,158],"setup","report","project","installation","project setup","issues","state",{"skillNo":160,"name":161,"nameZh":6,"description":162,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":163,"categories":164,"keywords":166,"publishedAt":33},"01M6DF6X6C2XB0P6GX76P7TTYV","start","Use when starting work on any task, when the user mentions metaswarm, or when the user wants to begin tracked development work","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fstart\u002FSKILL.md",[165],{"categoryNo":95,"categoryName":96,"taxonomyType":97,"taxonomyTypeDesc":98},[161,167,79,168,169,170,26,171,172,173,174],"start issue","issue","ready","work on issue","user","work","work on","development",{"skillNo":176,"name":152,"nameZh":6,"description":177,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":178,"categories":179,"keywords":180,"publishedAt":33},"01M6DF6QMJ9AJKFKHHKRT005PQ","Interactive project setup — detects your project, configures metaswarm, writes project-local files","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fsetup\u002FSKILL.md",[],[152,154,115,181,156,182],"files","interactive"]