From 53fc9f8131b24810113c14f41d67b40d31cf7b1e Mon Sep 17 00:00:00 2001 From: gamer147 Date: Thu, 23 Jul 2026 10:06:31 -0400 Subject: [PATCH] Decode EBINIT SALLY action semantics --- docs/global-reference.md | 4 ++-- docs/name-resolution.md | 12 ++++++++---- docs/phase-b-framework.md | 6 ++++-- tools/test_extract_init.py | 22 ++++++++++++++++++++++ vm-map/globals.toml | 6 ++++-- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/docs/global-reference.md b/docs/global-reference.md index 0450a97..2477ce4 100644 --- a/docs/global-reference.md +++ b/docs/global-reference.md @@ -49,8 +49,8 @@ | `0x7267e` | unit_canonical_id | high | investigation | Maps 106 alternate, boss, brainwashed, EX, and enemy variants back to their canonical character or recruitable archetype. For example all Estelle bosses map to 12, brainwashed Sylphine maps to 5, and ordinary/EX orcs map to recruitable orc 52. Setup, menus, inventory, dismissal, and combat normalize through this id. | | `0x72a66` | unit_capturable | high | investigation | Capture eligibility for 74 ordinary enemy archetypes. SELACT and FIELD allow the capture skill only when this flag is nonzero and the acting unit's capture power meets the target-level check; successful resolution displays the explicit 'Captured the enemy' message. | | `0x72e4e` | unit_roster_state_flag_base_index | high | investigation | Persistent roster-state block for seven recruitable heroines. SALLY's first three unit actions mark offsets 1, 2, and 0 respectively; UNITECH marks the offset selected by its recruitment mode; DELCH, REMOVECH, SALLY, and EXILE mark offset 3 when the unit is removed or released. The values are relative indices into the shared flag bank, not absolute global addresses. | -| `0x73236` | unit_sally_action_unlock_requirements | high | investigation | Availability requirements for SALLY's first four per-unit actions. While constructing the action mask, SALLY keeps a positive entry only when flag_bank[value - 1] equals 1, keeps -1 as unconditional, and clears an action whose entry is zero. The table covers the seven principal recruitable heroines plus 29 summonable archetypes. | -| `0x741d6` | unit_sally_event_ids | high | investigation | Eight-slot event table for SALLY's unit actions. A populated slot is copied to scjump_decision_out and translated through the SCJUMP resource table at 0x87a57 into next_script_resource_id, handing control to the corresponding scene. Principal heroines populate all eight actions; other eligible units populate the three form-dependent event slots. EXILE also tests slot 7 when deciding whether a unit can be released. | +| `0x73236` | unit_sally_action_unlock_requirements | high | investigation | Availability requirements for SALLY's contract, brainwash, reserved, and sex-magic action slots. The SO012 button atlas and selection dispatch prove that UI choices 1, 2, and 3 select columns 0, 1, and 3; column 2 has a switch case but is deliberately skipped by both drawing and input, so it is reserved/unreachable in the shipped UI. While constructing the action mask, SALLY keeps a positive entry only when flag_bank[value - 1] equals 1, keeps -1 as unconditional, and clears an action whose entry is zero. Deploy, sacrifice, and release use separate eligibility logic. Columns: 0=contract, 1=brainwash, 2=reserved_action, 3=sex_magic. | +| `0x741d6` | unit_sally_event_ids | high | investigation | SALLY event ids for contract, brainwash, the unreachable reserved action, Lily's child/girl/adult sex-magic forms, sacrifice, and release. The action switch selects columns 0, 1, 6, and 7 directly; sex magic selects the current Lily unit id plus one, yielding columns 3..5 for Lily unit ids 2..4. A populated reachable slot is copied to scjump_decision_out and translated through the SCJUMP resource table at 0x87a57 into next_script_resource_id. Column 2 is paired with the UI-skipped reserved action and its shipped ids 1300..1306 have no SCJUMP resource mapping. EXILE also tests column 7 when deciding whether a unit can be released. Columns: 0=contract, 1=brainwash, 2=reserved_action, 3=sex_magic_lily_child, 4=sex_magic_lily_girl, 5=sex_magic_lily_adult, 6=sacrifice, 7=release. | | `0x76116` | unit_roster_variant_ids | high | investigation | Roster-form mapping for 36 recruitable units. Column 0 maps the summonable archetypes and principal heroines to their normal unit id; column 1 maps seven heroines to EBINIT ids 21..27, whose names explicitly identify their brainwashed states. SALLY replaces the selected party unit with the action-selected form, and UNITECH recruitment modes 3/4 select the same two columns. Columns: 0=normal, 1=brainwashed. | | `0x768e6` | unit_sex_magic_bonus_item_id | high | investigation | Item awarded by SALLY after the three form-dependent intimate-event actions for 22 eligible units. SALLY passes the value directly to USEITEM and then displays the selected character name followed by the literal message 'sex magic bonus' and the resolved item description. Principal heroines yield planetary stones; the remaining rows resolve to recovery items and rare materials. | | `0x76cce` | unit_essence_yield | high | investigation | Essence returned by SALLY's unit-conversion/release actions for 187 units. The normal path adds this value to current essence and the special path adds five times it, clamps to the essence cap, then applies the corresponding alignment adjustment; it is distinct from unit_deployment_cost_base. | diff --git a/docs/name-resolution.md b/docs/name-resolution.md index 1fad9e6..2c1aa1f 100644 --- a/docs/name-resolution.md +++ b/docs/name-resolution.md @@ -234,10 +234,14 @@ records use the shared accuracy-through-max-FS vocabulary; starting skills, drop brainwashed roster forms, battle portraits/cut-ins, and health-selected status art all expose named fields. Consumer control flow further divides the five CP sprite assets into normal/alternate compact and directional sheets plus the special compact sheet, and SHOWGROW proves voice column 24 is the level-up reaction. Of -EBINIT's 110 populated profile fields, 72 now have specific semantic names. The remaining 35 named-table -fallbacks are deliberately limited to the still-unresolved voice reactions and SALLY action/event slots; -three fields remain wholly anonymous (`0x7843e` and two suspicious sparse writes into runtime table -`0x4e693/300`). +EBINIT's 110 populated profile fields, 84 now have specific semantic names. SALLY's SO012 button atlas and +action dispatch resolve all four unlock columns as contract, brainwash, a reserved/unreachable slot, and +sex magic. The paired eight event columns are contract, brainwash, the same reserved slot, three +form-dependent Lily sex-magic events, sacrifice, and release. The reserved slot has a switch arm but is +deliberately skipped by both drawing and input; its event ids also lack SCJUMP mappings, so it is recorded +as unreachable rather than assigned a speculative action. The remaining 23 named-table fallbacks are all +voice reactions; three fields remain wholly anonymous (`0x7843e` and two suspicious sparse writes into +runtime table `0x4e693/300`). ### STINIT mixed stage records (2026-07-23) diff --git a/docs/phase-b-framework.md b/docs/phase-b-framework.md index d611fb3..03d5209 100644 --- a/docs/phase-b-framework.md +++ b/docs/phase-b-framework.md @@ -745,8 +745,10 @@ SKINIT parallel field as the skill-change catalog inclusion flag and separates a skill-information visibility. The remaining item/skill work is refinement rather than an unnamed populated schema. -The remaining EBINIT unknowns are now the unread `0x7843e` enum and the signed meaning within boss classes; -enemy AI appears to live outside the static EBINIT schema. STINIT's separate mixed parser is now complete: +The remaining EBINIT unknowns are now the unread `0x7843e` enum, the signed meaning within boss classes, +and most voice-bank reactions; enemy AI appears to live outside the static EBINIT schema. SALLY's four +unlock requirements and eight event ids are fully joined to their contract, brainwash, reserved, +form-dependent sex-magic, sacrifice, and release columns. STINIT's separate mixed parser is now complete: all 74 sparse stage ids retain their victory/defeat strings, six scalars, fixed-buffer cells, and 1,396 footer-array copies. AIM/FIELD/DRAWCHP consumers establish the four condition slots, stage BGM, turn limit, and turn-limit outcome. Further passes establish target clear turns, performance reward, replay behavior, diff --git a/tools/test_extract_init.py b/tools/test_extract_init.py index ac575db..08d64c2 100644 --- a/tools/test_extract_init.py +++ b/tools/test_extract_init.py @@ -315,6 +315,28 @@ def test_field_semantics() -> None: check(items[0]["semantic_fields"]["item_sort_key"] == 10, "records expose a joined semantic field view") + units, _ = extract_init.extract_name(sys4load.load(scripts["EBINIT.BIN"])) + unit_semantics = extract_init.field_semantics(units) + check( + unit_semantics["0x73236/4/0"] + == "unit_sally_action_unlock_requirements.contract", + "SALLY unlock columns expose their action semantics", + ) + check( + unit_semantics["0x741d6/8/6"] == "unit_sally_event_ids.sacrifice", + "SALLY event columns expose their dispatched action semantics", + ) + check( + unit_semantics["0x741d6/8/2"] == "unit_sally_event_ids.reserved_action", + "the unreachable SALLY action remains explicit rather than speculative", + ) + extract_init.attach_semantic_fields(units, unit_semantics) + unit_by_id = {record["id"]: record for record in units} + check( + unit_by_id[5]["semantic_fields"]["unit_sally_event_ids.release"] == 1360, + "EBINIT records join SALLY release events by semantic field name", + ) + stages, meta = extract_init.extract_mixed( sys4load.load(extract_init.resolve("STINIT")) ) diff --git a/vm-map/globals.toml b/vm-map/globals.toml index 17392b5..5ce3a7e 100644 --- a/vm-map/globals.toml +++ b/vm-map/globals.toml @@ -495,8 +495,9 @@ address = "0x73236" name = "unit_sally_action_unlock_requirements" category = "data-table" type = "int[1000][4]" +columns = { "0" = "contract", "1" = "brainwash", "2" = "reserved_action", "3" = "sex_magic" } value_domain = "-1 means unconditional, 0 means unavailable, positive values 1744..1915 are one-based indices into the flag bank at 0x6d3" -usage = "Availability requirements for SALLY's first four per-unit actions. While constructing the action mask, SALLY keeps a positive entry only when flag_bank[value - 1] equals 1, keeps -1 as unconditional, and clears an action whose entry is zero. The table covers the seven principal recruitable heroines plus 29 summonable archetypes." +usage = "Availability requirements for SALLY's contract, brainwash, reserved, and sex-magic action slots. The SO012 button atlas and selection dispatch prove that UI choices 1, 2, and 3 select columns 0, 1, and 3; column 2 has a switch case but is deliberately skipped by both drawing and input, so it is reserved/unreachable in the shipped UI. While constructing the action mask, SALLY keeps a positive entry only when flag_bank[value - 1] equals 1, keeps -1 as unconditional, and clears an action whose entry is zero. Deploy, sacrifice, and release use separate eligibility logic." source = "investigation" confidence = "high" @@ -505,8 +506,9 @@ address = "0x741d6" name = "unit_sally_event_ids" category = "data-table" type = "int[1000][8]" +columns = { "0" = "contract", "1" = "brainwash", "2" = "reserved_action", "3" = "sex_magic_lily_child", "4" = "sex_magic_lily_girl", "5" = "sex_magic_lily_adult", "6" = "sacrifice", "7" = "release" } value_domain = "SCJUMP event/decision ids 1200..1366; zero means no scripted event for that action" -usage = "Eight-slot event table for SALLY's unit actions. A populated slot is copied to scjump_decision_out and translated through the SCJUMP resource table at 0x87a57 into next_script_resource_id, handing control to the corresponding scene. Principal heroines populate all eight actions; other eligible units populate the three form-dependent event slots. EXILE also tests slot 7 when deciding whether a unit can be released." +usage = "SALLY event ids for contract, brainwash, the unreachable reserved action, Lily's child/girl/adult sex-magic forms, sacrifice, and release. The action switch selects columns 0, 1, 6, and 7 directly; sex magic selects the current Lily unit id plus one, yielding columns 3..5 for Lily unit ids 2..4. A populated reachable slot is copied to scjump_decision_out and translated through the SCJUMP resource table at 0x87a57 into next_script_resource_id. Column 2 is paired with the UI-skipped reserved action and its shipped ids 1300..1306 have no SCJUMP resource mapping. EXILE also tests column 7 when deciding whether a unit can be released." source = "investigation" confidence = "high"