From 60f96c1b7782788fa2d03e13c2d40c49cb971209 Mon Sep 17 00:00:00 2001 From: rengstrom Date: Wed, 9 Sep 2026 17:14:00 +0000 Subject: [PATCH 1/2] Decompile the effect_052 overlay X-Potion runs the engine Curaga already carries, so 99 of its 118 functions are the library: nineteen units, listed in its own link order, and a symbol file that maps their names onto its addresses. Deriving that map is the whole of the work -- align unit by unit and position by position, because two functions of one unit can hash alike when they differ only in which sibling they call, and pair the relocation streams off against each other. 244 addresses paired with nothing left in conflict. What is X-Potion's own is the entry, a root dispatcher and seventeen script functions, and all but one of those are byte-identical to Curaga's or Cure's, so they came across with the addresses rewritten. Only the bank carve was written here. Its rings are Curaga's sizes, so its parameter header is the same two counts; what differs is the pool: X-Potion has no trailing-spark pool, which is why its entry sets up three where Curaga sets up four and its dispatcher drains three. Three things the overlay needed from its config. splat does not carve out a lone `jr ra` no-op, and three of them sit between script functions, so they are named in the symbol file or the binary comes out short and every later address shifts. The asset blob was declared from 0x9BF0, which swallowed the script dispatch tables; it starts at 0x9C68. And the jump tables carry the tail pad ASFLAGS suppress, so the pad between particlestep's and list's is declared. --- Makefile | 2 +- config/ff8.yaml | 32 +- config/symbols.effect_052.txt | 116 +++++ include/effect/effect_052.h | 60 +++ .../effect/params/effect_052/effect_params.h | 13 + src/effect/effect_052.c | 448 +++++++++--------- src/effect/effect_052_entry.c | 61 +++ 7 files changed, 512 insertions(+), 220 deletions(-) create mode 100644 config/symbols.effect_052.txt create mode 100644 include/effect/effect_052.h create mode 100644 include/effect/params/effect_052/effect_params.h create mode 100644 src/effect/effect_052_entry.c diff --git a/Makefile b/Makefile index 113aee26..929082e8 100644 --- a/Makefile +++ b/Makefile @@ -140,7 +140,7 @@ CODE_OVERLAYS := field_init intro field \ tripletriad battle_render battle world ALL_EFFECT_OVERLAYS := $(filter effect_%,$(SPLAT_BINARIES)) -EFFECTS ?= effect_001 effect_025 effect_028 +EFFECTS ?= effect_001 effect_025 effect_028 effect_052 EFFECT_OVERLAYS := $(if $(filter all,$(EFFECTS)),$(ALL_EFFECT_OVERLAYS),$(EFFECTS)) OVERLAYS := $(MENU_OVERLAYS) $(CODE_OVERLAYS) $(EFFECT_OVERLAYS) ALL_OVERLAYS := $(MENU_OVERLAYS) $(CODE_OVERLAYS) $(ALL_EFFECT_OVERLAYS) diff --git a/config/ff8.yaml b/config/ff8.yaml index ae67bb81..53db92e2 100644 --- a/config/ff8.yaml +++ b/config/ff8.yaml @@ -1496,15 +1496,41 @@ binaries: options: target_path: original/effect/effect_052.bin src_path: src/effect + section_order: [.text, .rodata, .data, .bss] segments: - name: effect_052 type: code start: 0x0 vram: 0x801A0000 subsegments: - - [0x0, c, effect_052] - - [0x9BDC, .rodata, effect_052] - - [0x9BF0, bin, effect_052] + - [0x0, c, effect_052_entry] + - [0x1EC, c, lib/common] + - [0x95C, c, lib/rotz] + - [0xAE8, c, lib/heading] + - [0xB18, c, lib/entity] + - [0x1688, c, lib/tint] + - [0x1964, c, lib/vector] + - [0x20D0, c, lib/emit] + - [0x3F04, c, lib/packet] + - [0x410C, c, lib/particlestep] + - [0x5400, c, lib/particlealloc] + - [0x551C, c, lib/draw] + - [0x6178, c, lib/emitteralloc] + - [0x6298, c, lib/spawn] + - [0x804C, c, lib/list] + - [0x8668, c, lib/trail] + - [0x87DC, c, lib/strands] + - [0x8CC8, c, lib/tables] + - [0x8FDC, c, lib/bankclear] + - [0x9034, c, lib/drawscript] + - [0x9498, c, effect_052] + # particlestep's jump table, then the pad its section end carried, then + # list's two, then the script dispatch tables. + - [0x9BDC, .rodata, lib/particlestep] + - [0x9BF0, pad] + - [0x9BF4, .rodata, lib/list] + - [0x9C20, .rodata, effect_052] + - [0x9C68, bin, effect_052] - [0x3C6D8] - name: effect_053 sha1: 738b1bb7de8b266db68995f925e9602f88fca51f diff --git a/config/symbols.effect_052.txt b/config/symbols.effect_052.txt new file mode 100644 index 00000000..4601a392 --- /dev/null +++ b/config/symbols.effect_052.txt @@ -0,0 +1,116 @@ +effectTextInit = 0x801A01EC; // type:func +effectTextHex = 0x801A033C; // type:func +effectTextNewline = 0x801A03E8; // type:func +effectNop = 0x801A0408; // type:func +effectDrawQuad128 = 0x801A0410; // type:func +effectDrawQuad256 = 0x801A050C; // type:func +effectMatrixIdentity = 0x801A0608; // type:func +effectMatrixRotX = 0x801A0640; // type:func +effectMatrixRotY = 0x801A07D0; // type:func +effectMatrixRotZ = 0x801A095C; // type:func +effectMatrixHeading = 0x801A0AE8; // type:func +effectReleaseWait = 0x801A0B18; // type:func +effectSpawnTask = 0x801A0B40; // type:func +effectCacheSlotAnchors = 0x801A0CCC; // type:func +effectSlotAnchor0 = 0x801A0E00; // type:func +effectSlotAnchor1 = 0x801A0E30; // type:func +effectSlotAnchorMid = 0x801A0E60; // type:func +effectCacheAnimSlotAnchors = 0x801A0E90; // type:func +effectAnimSlotAnchor0 = 0x801A0FCC; // type:func +effectAnimSlotAnchor1 = 0x801A0FFC; // type:func +effectAnimSlotAnchorMid = 0x801A102C; // type:func +effectUpdateModelBounds = 0x801A105C; // type:func +effectGetModelBounds = 0x801A12A8; // type:func +effectModelRadius = 0x801A12F8; // type:func +effectModelHalfHeight = 0x801A139C; // type:func +effectModelHalfWidth = 0x801A13CC; // type:func +effectModelHeight = 0x801A1424; // type:func +effectModelTop = 0x801A1448; // type:func +effectModelBottom = 0x801A1490; // type:func +effectModelRandomY = 0x801A14D8; // type:func +effectModelCentreY = 0x801A1590; // type:func +effectModelCentre = 0x801A15E4; // type:func +effectZeroWords = 0x801A1650; // type:func +effectTintPlay = 0x801A1688; // type:func +effectRandBetween = 0x801A1964; // type:func +effectRandProduct = 0x801A19E8; // type:func +effectRandVector = 0x801A1A8C; // type:func +effectMatrixFromAngles = 0x801A1B04; // type:func +effectJitterAngle = 0x801A1BD0; // type:func +effectJitterVector = 0x801A1C64; // type:func +effectJitterAngleS = 0x801A1CDC; // type:func +effectJitterSVector = 0x801A1D6C; // type:func +effectCopyVector = 0x801A1DE4; // type:func +effectFadeValue = 0x801A1E14; // type:func +effectFadeVector = 0x801A1E70; // type:func +effectBlendPointSets = 0x801A1F3C; // type:func +effectEmitTris = 0x801A20D0; // type:func +effectEmitQuads = 0x801A239C; // type:func +effectEmitTexTris = 0x801A26C0; // type:func +effectEmitTexQuads = 0x801A2A48; // type:func +effectEmitGouraudTris = 0x801A2E2C; // type:func +effectEmitGouraudQuads = 0x801A3134; // type:func +effectEmitGouraudTexTris = 0x801A34BC; // type:func +effectEmitGouraudTexQuads = 0x801A3878; // type:func +effectEmitStream = 0x801A3CC0; // type:func +effectBuildMeshPacket = 0x801A3F04; // type:func +effectParticleLateStep = 0x801A410C; // type:func +effectParticleAdvance = 0x801A41C4; // type:func +effectParticleAgeHold = 0x801A49CC; // type:func +effectParticlePose = 0x801A4AC0; // type:func +effectParticleAlloc = 0x801A5400; // type:func +effectStepRunKind = 0x801A551C; // type:func +effectStepFire = 0x801A5554; // type:func +effectParticleStep = 0x801A5600; // type:func +effectEmitterCountStep = 0x801A56EC; // type:func +effectSpriteEmit = 0x801A5704; // type:func +effectSpritePacket = 0x801A5D30; // type:func +effectParticleDrawPacket = 0x801A5F98; // type:func +effectDrawParticles = 0x801A6058; // type:func +effectEmitterAlloc = 0x801A6178; // type:func +effectEmitterAge = 0x801A6298; // type:func +effectEmitterSpawn = 0x801A6300; // type:func +effectEmitterSpawnShare = 0x801A6D9C; // type:func +effectEmitterStep = 0x801A6E9C; // type:func +effectEmitterOnStrand = 0x801A6F3C; // type:func +effectEmitterOnTrail = 0x801A71C4; // type:func +effectEmitterOnOwner = 0x801A73CC; // type:func +effectEmitterSlide = 0x801A75DC; // type:func +effectListAppend = 0x801A804C; // type:func +effectListRemove = 0x801A8088; // type:func +effectListClear = 0x801A80C8; // type:func +effectListRun = 0x801A80E0; // type:func +effectEmitterSetup = 0x801A8168; // type:func +effectStepFireSlots = 0x801A839C; // type:func +effectEmittersOnCue = 0x801A8450; // type:func +effectTrailPush = 0x801A858C; // type:func +effectTrailAim = 0x801A8668; // type:func +effectStrandsSeed = 0x801A87DC; // type:func +effectStrandsPush = 0x801A88E8; // type:func +effectStrandsAim = 0x801A8A00; // type:func +effectPoseTablesRelocate = 0x801A8CC8; // type:func +effectStartChildScript = 0x801A8F6C; // type:func +effectBanksClear = 0x801A8FDC; // type:func +effectDrawScriptStart = 0x801A9034; // type:func +effectDrawScriptRun = 0x801A92DC; // type:func +effectDrawListSpan = 0x801A9404; // type:func +func_801A953C = 0x801A953C; // type:func +func_801A96F8 = 0x801A96F8; // type:func +func_801A99E4 = 0x801A99E4; // type:func +g_effectStackBase = 0x801D399C; +g_effectStackTop = 0x801D39A0; +g_effectEmitterBank = 0x801D39A8; +g_effectParticleBank = 0x801D39AC; +g_effectDrawList = 0x801D39B0; +g_effectEmitterCursor = 0x801D39B4; +g_effectParticleCursor = 0x801D39B6; +g_effectParticleTotal = 0x801D39B8; +g_effectLiveTotal = 0x801D39BA; +g_effectBankUnk90 = 0x801D39BC; +g_effectBankUnk92 = 0x801D39BE; +g_effectChildPool = 0x801D41B4; +g_effectPrimCursor = 0x801D41C4; +g_effectHexGlyphs = 0x801D4504; +g_effectTextX = 0x801D4518; +g_effectTextY = 0x801D451C; +g_effectTextColour = 0x801D4520; diff --git a/include/effect/effect_052.h b/include/effect/effect_052.h new file mode 100644 index 00000000..7992fead --- /dev/null +++ b/include/effect/effect_052.h @@ -0,0 +1,60 @@ +#ifndef EFFECT_EFFECT_052_H +#define EFFECT_EFFECT_052_H + +#include "effect.h" +#include "effect/lib/particle.h" +#include "effect/lib/tint.h" + +/** + * @file effect_052.h + * @brief X-Potion: the overlay's own storage. + */ + +/** @brief Argument the frame-20 child is started with. */ +extern EffectTintStep D_801A9C68[]; + +/** @brief Sound cue the take-model script fires on its first frame. */ +extern u8 D_801A9C74[]; + +/** @brief Pool the trailing sparks are allocated from. */ +extern s32 D_801D4424; + +/** @brief Pool the frame-20 child is allocated from. */ +extern s32 D_801D44F4; + +/** @brief Pose script the model-taking handler runs. */ +extern EffectPoseTables D_801D4524; + +/** @brief Cleared when the effect starts. */ +extern s32 D_801D3994; +extern s32 D_801D3998; + +/** @brief Task pools the script's children run in. */ +extern s32 D_801D42B4; +extern s32 D_801D41E4; +extern s32 D_801D42C4; +extern s32 D_801D4434; +extern s32 D_801D39C4; + +/** @brief Table of two offsets battle.bin resolves when the effect starts. */ +extern u8 D_801A9DD4; + +/** @brief TIM uploaded to VRAM when the effect starts; also the first bank. */ +extern u8 D_801AA974; + +/** @brief Cursor the record banks are carved off, moving up as they are taken. */ +extern u8 *D_801D39A4; + +/** @brief Cursor the effect's second prim buffer is written through. */ +extern void *D_801D41C8; + +/** @brief The four bank pointers the emitters and the renderer read. */ +extern void *D_801D41CC; +extern void *D_801D41D0; +extern void *D_801D41D4; +extern void *D_801D41D8; + +/** @brief Run one frame of the effect: its opcode, then every task pool it owns. */ +s32 func_801A99EC(EffectEntity *entity); + +#endif /* EFFECT_EFFECT_052_H */ diff --git a/include/effect/params/effect_052/effect_params.h b/include/effect/params/effect_052/effect_params.h new file mode 100644 index 00000000..3ce28748 --- /dev/null +++ b/include/effect/params/effect_052/effect_params.h @@ -0,0 +1,13 @@ +#ifndef EFFECT_PARAMS_H +#define EFFECT_PARAMS_H + +/** + * @file effect_params.h + * @brief X-Potion: the ring sizes its particle engine is built with. + */ + +/** @brief Highest index each ring reaches before it wraps. */ +#define EFFECT_EMITTER_COUNT 0x59 +#define EFFECT_PARTICLE_COUNT 0x8B + +#endif /* EFFECT_PARAMS_H */ diff --git a/src/effect/effect_052.c b/src/effect/effect_052.c index ce370ca6..ba8bc84c 100644 --- a/src/effect/effect_052.c +++ b/src/effect/effect_052.c @@ -3,243 +3,259 @@ * @brief X-Potion */ #include "common.h" - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0000); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A01EC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A033C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A03E8); - -void func_801A0408(void) { +#include "game.h" +#include "effect.h" +#include "effect/effect_052.h" +#include "effect/lib/particle.h" +#include "effect/lib/entity.h" +#include "effect/lib/tint.h" +#include "psxsdk/libgte.h" +#include "effect/lib/bankclear.h" +#include "effect/lib/tables.h" +#include "effect/lib/drawscript.h" +#include "effect/lib/common.h" +#include "btl_entity.h" + +/** @brief Where in the scratchpad the effect keeps its view matrix. */ +#define EFFECT_SCRATCHPAD ((MATRIX *)0x1F8002E0) + +/** @brief @ref EffectEntity::unk05C -- the frame counter's low bit picks a bank. */ +#define EFFECT_FRAME_ODD 0x1 + +static void func_801A9498(EffectEntity *entity); +static void func_801A94F4(EffectEntity *entity); +static void func_801A953C(EffectEntity *entity); +static s32 func_801A9544(EffectEntity *entity); +static void func_801A95E4(EffectEntity *entity); +static void func_801A963C(EffectEntity *entity); +static void func_801A9670(EffectEntity *entity); +static void func_801A96F8(EffectEntity *entity); +static s32 func_801A9700(EffectEntity *entity); +static void func_801A9810(EffectEntity *entity); +static void func_801A9824(EffectEntity *entity); +static void func_801A9838(EffectEntity *entity); +static void func_801A98AC(EffectEntity *entity); +static void func_801A9904(EffectEntity *entity); +static void func_801A9964(EffectEntity *entity); +static void func_801A9978(EffectEntity *entity); +static void func_801A99A0(EffectEntity *entity); +static void func_801A99B4(EffectEntity *entity); +static void func_801A99C8(EffectEntity *entity); +static void func_801A99E4(EffectEntity *entity); + +/** @brief Opcode handler: draw and retire once the frame count is reached. */ +static void func_801A9498(EffectEntity *entity) { + EffectDrawScript *script = (EffectDrawScript *)entity; + + if (entity->unk024 >= script->stopFrame) { + /* Called with no argument: the entity is already in $a0 and stays there. */ + ((void (*)())effectDrawScriptStart)(); + effectDrawScriptRun(script); + entity->pc++; + } } -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0410); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A050C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0608); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0640); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A07D0); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A095C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0AE8); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0B18); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0B40); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0CCC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0E00); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0E30); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0E60); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0E90); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0FCC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A0FFC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A102C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A105C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A12A8); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A12F8); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A139C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A13CC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1424); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1448); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1490); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A14D8); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1590); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A15E4); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1650); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1688); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1964); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A19E8); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1A8C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1B04); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1BD0); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1C64); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1CDC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1D6C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1DE4); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1E14); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1E70); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A1F3C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A20D0); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A239C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A26C0); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A2A48); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A2E2C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A3134); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A34BC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A3878); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A3CC0); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A3F04); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A410C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A41C4); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A49CC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A4AC0); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A5400); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A551C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A5554); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A5600); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A56EC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A5704); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A5D30); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A5F98); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A6058); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A6178); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A6298); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A6300); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A6D9C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A6E9C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A6F3C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A71C4); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A73CC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A75DC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A804C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A8088); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A80C8); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A80E0); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A8168); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A839C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A8450); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A858C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A8668); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A87DC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A88E8); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A8A00); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A8CC8); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A8F6C); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A8FDC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A9034); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A92DC); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A9404); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A9498); - -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A94F4); +/** @brief Opcode handler: hold still until the count runs out. */ +static void func_801A94F4(EffectEntity *entity) { + if (effectDrawScriptRun((EffectDrawScript *)entity) != 0) { + entity->flags |= EFFECT_FLAG_STOP; + entity->pc++; + } +} -void func_801A953C(void) { +/** @brief Opcode handler: no-op. */ +static void func_801A953C(EffectEntity *entity) { } -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A9544); +/** + * @brief Script dispatcher: run this frame's step, draw, and retire the entity. + * + * @return 2 once the script has stopped and its children have drained, 0 while + * it is still running. + */ +static s32 func_801A9544(EffectEntity *entity) { + EffectHandler handlers[3] = { func_801A9498, func_801A94F4, func_801A953C }; + + handlers[entity->pc](entity); + entity->unk024++; + if (entity->flags & EFFECT_FLAG_STOP) { + if (entity->wait != 0) { + return 0; + } + effectReleaseWait(entity); + return 2; + } + return 0; +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A95E4); +/** @brief Opcode handler: start the pose script on the taken model. */ +static void func_801A95E4(EffectEntity *entity) { + effectStartChildScript(entity, func_801A9544, &D_801D4524, 0, 0x2D, 0); + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A963C); +/** @brief Opcode handler: release the model once the count reaches 30. */ +static void func_801A963C(EffectEntity *entity) { + if (entity->unk024 == 30) { + entity->unk010->unk063 = 0; + entity->pc++; + } +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A9670); +/** @brief Opcode handler: hand the current part to battle, then stop. */ +static void func_801A9670(EffectEntity *entity) { + if (entity->unk024 >= 30) { + func_800BFE1C(&entity->animSet->anims[entity->unk02A].parts[entity->unk02B]); + entity->flags |= EFFECT_FLAG_STOP; + entity->pc++; + } +} -void func_801A96F8(void) { +/** @brief Opcode handler: no-op. */ +static void func_801A96F8(EffectEntity *entity) { } -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A9700); +/** @brief Script dispatcher: the take-model script, with its two timed cues. */ +static s32 func_801A9700(EffectEntity *entity) { + EffectHandler handlers[4] = { func_801A95E4, func_801A963C, func_801A9670, func_801A96F8 }; + + effectCacheSlotAnchors(entity); + effectUpdateModelBounds(entity); + handlers[entity->pc](entity); + if (entity->unk024 == 0) { + func_800C4764(D_801A9C74, 0, 0x80); + } + if (entity->unk024 == 20) { + EffectTintScript *child = + effectSpawnTask(&D_801D44F4, effectTintPlay, sizeof(EffectTintScript), entity); + + child->steps = D_801A9C68; + } + entity->unk024++; + if (entity->flags & EFFECT_FLAG_STOP) { + if (entity->wait != 0) { + return 0; + } + effectReleaseWait(entity); + return 2; + } + return 0; +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A9810); +/** @brief Opcode handler: consume the opcode and do nothing else. */ +static void func_801A9810(EffectEntity *entity) { + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A9824); +/** @brief Opcode handler: consume the opcode and do nothing else. */ +static void func_801A9824(EffectEntity *entity) { + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A9838); +/** @brief Opcode handler: carve both record banks and start the draw script. */ +static void func_801A9838(EffectEntity *entity) { + if (entity->wait == 0) { + /* Both banks are carved out of the same byte arena behind the TIM. */ + g_effectEmitterBank = (EffectEmitter *)D_801D39A4; + D_801D39A4 += EFFECT_EMITTER_BANK_BYTES; + g_effectParticleBank = (EffectParticle *)D_801D39A4; + D_801D39A4 += EFFECT_PARTICLE_BANK_BYTES; + effectBanksClear(); + entity->pc++; + } +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A98AC); +/** @brief Opcode handler: start a task running @ref func_801A9700. */ +static void func_801A98AC(EffectEntity *entity) { + entity->unk063 = 1; + effectSpawnTask(&D_801D4424, func_801A9700, 0x58, entity); + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A9904); +/** @brief Opcode handler: repeat the preceding opcode until the count runs out. */ +static void func_801A9904(EffectEntity *entity) { + if (entity->unk063 != 0) { + return; + } + if (entity->unk02A < entity->unk058) { + entity->unk02A++; + entity->unk02E++; + entity->pc--; + } else { + entity->pc++; + } +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A9964); +/** @brief Opcode handler: consume the opcode and do nothing else. */ +static void func_801A9964(EffectEntity *entity) { + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A9978); +/** @brief Opcode handler: stall here until @c unk05E reaches zero. */ +static void func_801A9978(EffectEntity *entity) { + if (entity->unk05E == 0) { + entity->pc++; + } +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A99A0); +/** @brief Opcode handler: consume the opcode and do nothing else. */ +static void func_801A99A0(EffectEntity *entity) { + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A99B4); +/** @brief Opcode handler: consume the opcode and do nothing else. */ +static void func_801A99B4(EffectEntity *entity) { + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A99C8); +/** @brief Opcode handler: raise @ref EFFECT_FLAG_STOP and consume the opcode. */ +static void func_801A99C8(EffectEntity *entity) { + entity->flags |= EFFECT_FLAG_STOP; + entity->pc++; +} -void func_801A99E4(void) { +/** @brief Opcode handler: no-op. */ +static void func_801A99E4(EffectEntity *entity) { } -INCLUDE_ASM("asm/ovl/effect_052/nonmatchings/effect_052", func_801A99EC); +/** + * @brief Run one frame of the effect: its opcode, then every task pool it owns. + * + * @return 2 once the script has stopped and its children have drained. + */ +s32 func_801A99EC(EffectEntity *entity) { + EffectHandler handlers[11] = { + func_801A9810, func_801A9824, func_801A9838, func_801A98AC, func_801A9904, + func_801A9964, func_801A9978, func_801A99A0, func_801A99B4, func_801A99C8, + func_801A99E4 + }; + MATRIX *view = EFFECT_SCRATCHPAD; + + *view = D_800F02C8; + g_effectStackTop = (u8 *)view; + g_effectStackBase = view; + if (entity->unk05C & EFFECT_FRAME_ODD) { + g_effectPrimCursor = D_801D41CC; + D_801D41C8 = D_801D41D4; + } else { + g_effectPrimCursor = D_801D41D0; + D_801D41C8 = D_801D41D8; + } + effectCacheAnimSlotAnchors(entity); + handlers[entity->pc](entity); + g_effectParticleTotal = 0; + entity->unk05E = 0; + g_effectLiveTotal = 0; + entity->unk05E += func_800B2B68(&D_801D4424); + entity->unk05E += func_800B2B68(&D_801D44F4); + entity->unk05E += func_800B2B68(&g_effectChildPool); + entity->unk05C++; + entity->unk024++; + if (entity->flags & EFFECT_FLAG_STOP) { + if (entity->wait == 0) { + effectReleaseWait(entity); + return 2; + } + } + return 0; +} diff --git a/src/effect/effect_052_entry.c b/src/effect/effect_052_entry.c new file mode 100644 index 00000000..f3edd25d --- /dev/null +++ b/src/effect/effect_052_entry.c @@ -0,0 +1,61 @@ +/** + * @file effect_052_entry.c + * @brief X-Potion: the overlay entry point. + */ +#include "common.h" +#include "effect.h" +#include "effect/lib/entity.h" +#include "effect/lib/tables.h" +#include "effect/effect_052.h" +#include "effect/effect_028_entry.h" + +/** @brief Stride between the prim and frame banks carved out of the TIM. */ +#define EFFECT_BANK_SIZE 0x6000 + +/** + * @brief Start the effect's script and hand back its task pool. + * + * @param animSet Animation set the effect plays. + * @return The pool the root entity lives in. + */ +void *func_801A0000(EffectAnimSet *animSet) { + u8 *bank0; + EffectEntity *entity; + u8 *bank1; + u8 *frames; + + D_801D3994 = 0; + D_801D3998 = 0; + func_800B2A00(&D_801D42B4, &D_801D41E4, 0x64, 2); + entity = effectSpawnTask(&D_801D42B4, func_801A99EC, 0x64, NULL); + entity->animSet = animSet; + entity->unk02C = animSet->anims[entity->unk02A].unk000; + entity->unk02D = + entity->animSet->anims[entity->unk02A].parts[entity->unk02B].unk000; + entity->pc = 0; + entity->unk05A = animSet->anims->unk010; + entity->unk058 = animSet->anims->unk011; + entity->unk02F = entity->unk05A - 1; + if (entity->unk02F < entity->unk058) { + entity->unk02F = entity->unk058; + } + if (!(animSet->flags & EFFECT_ANIMSET_FLAG_LOADED)) { + func_800C3BE0(&D_801A9DD4); + func_800BB084(&D_801AA974); + } + D_801D39A4 = &D_801AA974; + bank0 = &D_801AA974; + D_801D41CC = bank0; + bank1 = bank0 + EFFECT_BANK_SIZE; + D_801D41D4 = bank0 + EFFECT_BANK_SIZE; + D_801D39A4 = bank0 + EFFECT_BANK_SIZE; + frames = bank1; + D_801D41D0 = frames; + frames += EFFECT_BANK_SIZE; + D_801D39A4 = frames; + D_801D41D8 = frames; + func_800B2A00(&D_801D4424, &D_801D42C4, 0x58, 4); + func_800B2A00(&D_801D44F4, &D_801D4434, 0x3C, 3); + func_800B2A00(&g_effectChildPool, &D_801D39C4, 0x2A4, 3); + return &D_801D42B4; +} From 612fa36317c124c9f4e9e3c61e0140f46e3702dd Mon Sep 17 00:00:00 2001 From: rengstrom Date: Wed, 9 Sep 2026 17:15:19 +0000 Subject: [PATCH 2/2] effect_052: give the entry its own header The port left the entry taking its prototype from effect_028_entry.h, the overlay it was copied from. Both overlays declare the same signature under the same name, so it compiled and the binary matched -- nothing but reading the include would have shown it. X-Potion's entry now owns its declaration, the way the other three overlays' do. --- include/effect/effect_052_entry.h | 19 +++++++++++++++++++ src/effect/effect_052_entry.c | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 include/effect/effect_052_entry.h diff --git a/include/effect/effect_052_entry.h b/include/effect/effect_052_entry.h new file mode 100644 index 00000000..1f582804 --- /dev/null +++ b/include/effect/effect_052_entry.h @@ -0,0 +1,19 @@ +#ifndef EFFECT_EFFECT_052_ENTRY_H +#define EFFECT_EFFECT_052_ENTRY_H + +#include "effect.h" + +/** + * @file effect_052_entry.h + * @brief X-Potion: the overlay entry point. + */ + +/** + * @brief Start the effect's script and hand back its task pool. + * + * @param animSet Animation set the effect plays. + * @return The pool the root entity lives in. + */ +void *func_801A0000(EffectAnimSet *animSet); + +#endif /* EFFECT_EFFECT_052_ENTRY_H */ diff --git a/src/effect/effect_052_entry.c b/src/effect/effect_052_entry.c index f3edd25d..e2294ce6 100644 --- a/src/effect/effect_052_entry.c +++ b/src/effect/effect_052_entry.c @@ -7,7 +7,7 @@ #include "effect/lib/entity.h" #include "effect/lib/tables.h" #include "effect/effect_052.h" -#include "effect/effect_028_entry.h" +#include "effect/effect_052_entry.h" /** @brief Stride between the prim and frame banks carved out of the TIM. */ #define EFFECT_BANK_SIZE 0x6000