Commit Graph

1464 Commits

Author SHA1 Message Date
cecilarmitais
30ea1f34f0 Replace the Window placeholder with its real layout
struct Window was landed two commits ago as mostly filler: the fields those
functions happened to touch, and padding to reach the 0xE0 stride GetWindow
indexes by. This replaces it with the full 0xE0 layout, supplied by the project
maintainer, together with the four types it is built from -- WindowTemplate,
WindowBlock, WindowTile and CursorParams, plus Point.

Four things the layout has to satisfy, and does:

  sizeof is 0xE0, the stride GetWindow multiplies by.
  is_active is an s8 at 0xB6, which is where DeleteWindow does ldrnesb.
  WindowTemplate.width is at 0x06, where the previous partial struct, inherited
    from overlay_31_02382820.h, already had width.
  x, y, width and height at 0x04 through 0x07 account for GetWindowRectangle
    exactly: it writes y*8, y*8 + height*8, x*8 and x*8 + width*8, so its output
    is top, bottom, left, right. That function is rewritten against the named
    fields rather than shifting anonymous bytes.

Three choices worth noting for review. The struct keeps its tag, so both
struct Window and Window resolve and no existing use changes. GetWindowContents
still returns void * rather than the u32 the field is typed as, because all 22
of its callers assign the result to a pointer; the cast is a no-op and avoids 22
integer-to-pointer conversions. And width moving inside the template means
overlay_31_02382820.c reads window2->template.width, the one source change the
layout forces.

The field names here are the maintainer's, not derived in this commit. The
offsets and widths are checkable against the asm; the names are not, and should
be read as supplied rather than proven.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction, from a struct definition
supplied by the maintainer. Confirmed by a matching build with
overlay_31_02382820.o rebuilt: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 02:48:56 -07:00
cecilarmitais
643fd53bcf Decomp GetWindowContents, GetWindowRectangle and SetBothScreensWindowsColor
Decompile from asm:

  SetBothScreensWindowsColor  0x02027A80
  GetWindowRectangle          0x02028284
  GetWindowContents           0x0202833C

GetWindowContents is the function the menu accessors landed over the last several
commits all call. It reads the pointer at 0xC of a WINDOW_LIST entry, and its
callers cast that to whichever menu layout they use.

struct Window gains the fields these two touch: bytes at 0x4, 0x5, 0x7 and 0x8
around the existing width at 0x6, and the pointer at 0xC. The trailing padding
shrinks to keep the entry at 0xE0, which is the stride GetWindow indexes by.

GetWindowRectangle converts four of those bytes into a rectangle in pixels,
multiplying each by 8, writing top and bottom from 0x5 and 0x7 and left and
right from 0x4 and width. Its out parameter has no existing type and becomes
struct unk_02028284.

GetWindowContents needed the entry address bound to a pointer local. Returning
WINDOW_LIST[window_id].field_0xC folds the field offset into the literal pool
entry, giving a .word WINDOW_LIST+0xc and an indexed load; the target keeps
WINDOW_LIST as the literal and reaches the field with mla plus ldr [r1, #0xc].

SetScreenWindowsColor is still asm and had no declaration in the tree, so its
prototype is provisional.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 02:28:23 -07:00
cecilarmitais
aade0cdfc8 Decomp GetWindow; retype overlay_31's window callbacks as ids
Decompile from asm:

  GetWindow  0x020275F8

It indexes WINDOW_LIST with a stride of 0xE0, so its argument is a window id
rather than a pointer, and its result is the address of one entry.

overlay_31_02382820.c declared it as taking a struct Window * and passed one,
which cannot be what the target computes: multiplying a pointer by 0xE0 and
adding it to WINDOW_LIST is meaningless. The value it passes is an id, and the
same value goes to DrawTextInWindow and UpdateWindow, so those take ids too.
This retypes the three window callbacks and both of those prototypes
accordingly, along with the callback type CreateTextBox stores.

That retyping is byte-neutral -- a pointer and an id both travel in r0 -- but
overlay_31_02382820.o was already matching, so it is rebuilt here to confirm
rather than assumed.

struct Window keeps its existing return type and gains padding to its real 0xE0
size, which is what lets &WINDOW_LIST[id] stride correctly. Its first seven
bytes are unchanged, so the width field overlay_31 reads is where it was. The
struct moves from the overlay's header into include/window.h, which already
exists and is where a main-binary function returning one can reach it; the
overlay includes it rather than the main binary including an overlay header.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build with overlay_31_02382820.o rebuilt:
build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 02:09:00 -07:00
cecilarmitais
2472ad54f7 Decomp four allocation, keyboard and area-name accessors
Decompile from asm:

  AllocUnkBagStruct        0x02042B98
  FreeUnkBagStruct         0x02042BBC
  SetAreaNameBoxState3     0x0202FD3C
  GetKeyboardStringResult  0x0203755C

AllocUnkBagStruct and FreeUnkBagStruct manage the pointer at offset 8 of
UNK_STORAGE_STRUCT_0XC, the field the selection accessors in the previous commit
read. The allocation is 0xC8 bytes, which is 0x32 words -- the same count
ClearBagSelectedItemTable zeroes and the same inventory size the bag walkers
use.

MemAlloc is still asm and has no owning header, so its prototype is provisional
and declared beside the caller, matching the signature overlay_31_02382820.c
already uses. That is now a second declaration of it; both should collapse into
one header when MemAlloc lands.

SetAreaNameBoxState3 writes a word at 0xA0 of the window contents, so the filler
before 0x198 in struct unk_0202AAA8 is split to expose it. Nothing after it
moves.

GetKeyboardStringResult reads through the unnamed pointer _020AFDF0, which keeps
its placeholder name, into a word at 0xF8 of whatever it points at.

GetWindow was looked at and deliberately left in asm. It indexes WINDOW_LIST
with a stride of 0xE0, so its argument is an index, but overlay_31_02382820.c
declares it as taking a struct Window * and passes one. Landing it correctly
means retyping that caller, and struct Window there describes only its first 7
bytes, so the two disagree about more than this function.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 01:49:58 -07:00
cecilarmitais
80c33c3540 Decomp six storage-selection and notify-note accessors
Decompile from asm:

  ClearBagSelectedItemTable   0x02042AF8
  IsBagItemIndexSelected      0x02042B84
  IsStorageItemIndexSelected  0x02043568
  GetNotifyNote               0x020484A0
  SetNotifyNote               0x020484B0
  EventFlagBackupVeneer       0x02048758

UNK_STORAGE_STRUCT_0XC and UNK_STORAGE_STRUCT_0X14 already carry those names in
asm. Only their field at offset 8 is touched here, a pointer to an array, so
they are typed with placeholder structs holding that field and filler before it.
The element widths come from the loads: words for the bag table, bytes for the
storage one.

Their struct names use the globals' own addresses, 0x020AFED4 and 0x020AFEE0,
derived by walking back from UNK_STORAGE_STRUCT_0X8_PTR_1, which sits at the
labelled address 0x020AFEF4, through the two definitions' byte counts. That is
arithmetic on the data rather than a lookup, and a reviewer may want to confirm
it; nothing in this commit depends on the names being right, only on the layout.

ClearBagSelectedItemTable zeroes 0x32 entries, the same count the bag walkers in
earlier commits use for an inventory.

EventFlagBackup is still asm and had no declaration in the tree, so its
prototype is provisional.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 01:36:47 -07:00
cecilarmitais
f7fbcd80ba Decomp six collection-menu setters; add a second window-contents view
Decompile from asm:

  SetCollectionMenuField0x1BC  0x0202C5E0
  SetCollectionMenuField0x1C8  0x0202C794
  SetCollectionMenuField0x1A0  0x0202C7A8
  SetCollectionMenuField0x1A4  0x0202C7BC
  SetCollectionMenuVoidFn      0x0202C7D0
  SetCollectionMenuField0x1B2  0x0202D0D8

These reach the same buffer as the parent, simple and advanced menu accessors,
through the same GetWindowContents, but they do not share its layout: this set
writes a word at 0x1A0 where CheckParentMenuField0x1A0 reads a byte there, and a
byte at 0x1B2 which falls inside the word the advanced text box uses at 0x1B0.

So they are given their own view, struct unk_0202C5E0, named for the
lowest-addressed function that takes it, rather than forcing one struct to
describe both. GetWindowContents returns void *, so each menu type casting the
buffer to its own layout is consistent with what the target does; a single
merged struct would have to assert that the two layouts agree, which the stores
show they do not.

Two of the offsets are inferred from the store width alone and nothing else:
0x1A8 is written with a word and the function is named VoidFn, so it is typed
void *, but the target would look the same for any pointer or 32-bit value.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 01:16:24 -07:00
cecilarmitais
ec362491e1 Decomp six menu and text-box setters
Decompile from asm:

  SetSimpleMenuField0x1AC       0x0202BA0C
  CloseAdvancedMenu             0x0202BC44
  IsAdvancedMenuActive2         0x0202BCBC
  SetAdvancedTextBoxField0x1C4  0x020307A4
  SetAdvancedTextBoxField0x1C2  0x0203083C
  SetAdvancedTextBoxState5      0x0203088C

struct unk_0202AAA8 gains fields at 0x1AC, 0x1BC, 0x1C2, 0x1C3 and 0x1C4. The
filler that stood between 0x1A8 and 0x1B0 is split to expose 0x1AC; nothing
after it moves.

CloseAdvancedMenu differs from the simple- and parent-menu versions: it frees
only the window contents, not the pointer at 0x198 those two free first. That is
what the target does and is not an omission here.

IsAdvancedMenuActive2 tests the same states as IsSimpleMenuActive, 7 and 8, and
like it binds the state to a local so the field is loaded once.

SetAdvancedTextBoxField0x1C2 stores a constant 1 rather than an argument, so it
takes only the window id.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 01:06:05 -07:00
cecilarmitais
363d3edb85 Decomp GetSelectedMenuItemIdx and five window-id wrappers
Decompile from asm:

  GetAdvancedMenuCurrentOption    0x0202BCFC
  GetWindowIdSelectedMenuItemIdx  0x0202C748
  IsDialogueBoxActive             0x0202F180
  GetWindowIdPageStart            0x02030A18
  GetAdvancedTextBoxFlags2        0x02030A40
  GetSelectedMenuItemIdx          0x02032578

GetSelectedMenuItemIdx computes the absolute item index from the paging struct
added in the previous commit, current page times items per page plus the
selection within the page, which the target folds into one mla.

GetWindowIdPageStart, GetWindowIdSelectedMenuItemIdx and
GetAdvancedMenuCurrentOption resolve a window and forward to the paging
functions at offset 4 of its contents. The last two have identical bodies and
are decompiled as written rather than one calling the other, since that is what
the target does.

struct unk_0202AAA8 gains two fields the other two functions read: a byte at 0x8
and a word at 0x1B0. The filler before 0x198 is split to expose the first
without moving anything after it.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 00:57:43 -07:00
cecilarmitais
75c76fa71f Decomp seven menu paging getters
Decompile from asm:

  GetSelectedItemOnPage  0x02032558
  GetCurrentPage         0x02032560
  GetPageStart           0x02032568
  GetTotalNumMenuItems   0x0203258C
  GetNumItemsOnPage      0x02032594
  GetMaxItemsOnPage      0x0203259C
  GetTotalNumPages       0x020325A4

Six read one word each from a struct with no existing type, introduced here as
struct unk_02032558 with placeholder fields at 0xBC through 0xD0. GetPageStart
multiplies two of them, current page by max items per page, which is consistent
with the names but is an observation about the arithmetic rather than something
the bytes label.

GetSelectedItemOnPage was declared provisionally in include/main_0202AAA8.h last
commit, taking void *, because it was still asm. That declaration is removed and
its caller now includes the real header. The argument reaching it there is a
void * offset by 4 from the window contents, which converts implicitly; the
caller's object is rebuilt here and still matches.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 00:38:40 -07:00
cecilarmitais
75f2f81394 Restore four literal-pool annotations stripped by earlier commits
Earlier commits on this branch stripped `; =VALUE` annotations off loads that
survived a file split, because precommit.py discarded the removals rule 2
matches against. Four such lines remain in the tree; the rest were on code that
has since been extracted to C, so they left legitimately with their functions.

  asm/main_020108B4.s          ldr r5, _02010AC4 ; =0x0000270F
  asm/main_02010BC0.s          ldr r5, _02010DC8 ; =0x0000270F
  lib/DSE/asm/main_02072054.s  ldr r1, _02072140 ; =0x04000208
  lib/DSE/asm/main_02072054.s  ldr r2, _02072140 ; =0x04000208

These are regenerated from each file's own literal pool rather than recovered
from history: the annotation is the `.word` of the label the instruction
references, so it is derivable and exact. Two earlier attempts to restore
comments by matching instruction text were discarded instead of committed --
repeated instructions made them paste annotations onto unrelated lines.

The convention is unambiguous upstream: across the two files sampled, 263 of 263
pool-referencing loads carry an annotation and none lack one.

No comments are added to any pmd-sky file beyond these, which are restorations
of upstream's own annotations rather than anything authored here.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 00:28:35 -07:00
cecilarmitais
fb757337d0 Decomp five simple-menu accessors
Decompile from asm:

  GetWindowIdSelectedItemOnPage  0x0202AB80
  CloseSimpleMenu                0x0202B4C4
  IsSimpleMenuActive             0x0202B520
  CheckSimpleMenuField0x1A0      0x0202B540
  GetSimpleMenuField0x1A4        0x0202B558

These are the simple-menu counterparts of the parent-menu accessors in the
previous commit and reach the same window contents through GetWindowContents,
so they reuse struct unk_0202AAA8, which gains a field at 0x1A4.

IsSimpleMenuActive tests states 7 and 8 where the parent-menu version tests 8
and 9. As there, the state is bound to a local: comparing the field against two
values inline emits two loads and the target loads it once.

DeleteWindow moves into the shared header and MemFree is taken from
main_02001188.h, rather than each source declaring them again. Two of these
functions need both, and a second local copy of a prototype is the kind of
disagreement no build can report.

The asm files this split produces keep upstream's jump-table annotations. An
earlier run of precommit.py stripped them, having discarded the removals that
rule 2 matches against; that is fixed separately in the workspace repo, and the
158 comments in this split are intact here.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 00:19:24 -07:00
cecilarmitais
3fc19ec702 Decomp IsEmptyString and four parent-menu accessors
Decompile from asm:

  IsEmptyString              0x0202A66C
  SetParentMenuState7        0x0202AAA8
  CloseParentMenu            0x0202AABC
  IsParentMenuActive         0x0202AB40
  CheckParentMenuField0x1A0  0x0202AB60

The four menu functions all resolve their window through GetWindowContents,
which src/overlay_10_022BCC60.c already declares as returning void *. What it
returns has fields at 0x198, 0x19C and 0x1A0 and no existing type, so it is
introduced as struct unk_0202AAA8, named for the lowest-addressed function that
uses it, with placeholder fields.

The struct and the GetWindowContents prototype live in include/main_0202AAA8.h
and are included by the second source rather than repeated in it. Two copies of
a struct definition in two translation units is the same drift risk as two
prototypes, and nothing in the build would report them disagreeing.

Return widths differ across these and are read off the target. IsEmptyString and
CheckParentMenuField0x1A0 end in and r0, r0, #0xff, so they return bool8;
IsParentMenuActive does not, so it is word-sized despite reading like a
predicate.

IsParentMenuActive also needed its state field bound to a local. Comparing
menu->field_0x19C against two values inline emits two loads; the target loads it
once.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:59:35 -07:00
cecilarmitais
6f907c680c Decomp eight veneers and wrappers
Decompile from asm:

  PlayBgmByIdVeneer              0x02017B58
  PlayBgmByIdVolumeVeneer        0x02017B64
  PlaySeByIdVolumeWrapper        0x02017C80
  GroupOamAttributesWrapper      0x0201BA9C
  CopyAttributesToOamWrapper     0x0201BAAC
  CopyAndInterleaveWrapper       0x0201BFF0
  SetAnimationControlPausedFlag  0x0201D198
  DeleteWanTableEntryVeneer      0x0201D72C

Most are tail calls the compiler reaches with bx rather than bl, either passing
their arguments through unchanged or supplying one: PlaySeByIdVolumeWrapper
fixes the volume at 0x100, the two Oam wrappers offset their argument by 0x20,
and CopyAndInterleaveWrapper halves its length.

That halving is a shift, not a division. Written as len / 2 the compiler adds the
round-toward-zero correction a signed divide needs; the target has a bare asr #1,
which is what len >> 1 produces.

SetAnimationControlPausedFlag sets or clears bit 0x4000 of the bitfield at the
start of struct animation_control, which already exists in graphics.h.

Two cautions for a reviewer. The callees are all still asm and had no
declarations in the tree, so every prototype here is provisional and declared
beside its caller. And a veneer that only forwards constrains nothing about its
own argument list -- the arguments are already in the right registers, so
declaring none, one or several all produce the same bytes. The parameter lists
are chosen to read sensibly against each callee's name, not read off the target.
The exception is PlaySeByIdVolumeWrapper, whose signature is pinned by an
existing C caller in overlay_25_init.c that passes a single s32.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 22:43:20 -07:00
cecilarmitais
152f6c7ea6 Decomp six shop and item helpers; split bag_items' filler
Decompile from asm:

  SetMoneyStored                            0x0201070C
  GetCurrentKecleonShop1ItemByIndex         0x02010898
  GetCurrentKecleonShop2ItemByIndex         0x02010BA4
  GetExclusiveItemOffset                    0x02010E40
  SwapShopFreeDoublePointer                 0x020114F8
  IsMonsterAffectedByGravelyrockGroundMode  0x02011830

The two Kecleon accessors index arrays hanging off pointers at 0x132C and
0x1370. Unlike the previous two extensions those offsets fall inside
fill2[0x1009] rather than past the struct's end, so the filler is split into
three runs with the two pointers between them. maybeMoney still lands at 0x1394
and nothing after it moves, which the rebuild of every struct bag_items user
confirms.

SetMoneyStored clamps to 0x0098967F, decimal 9999999, and to zero below.

GetExclusiveItemOffset returns zero unless the item is in
CATEGORY_EXCLUSIVE_ITEMS, which item.h already defines as 15, and otherwise the
item's distance from 0x1bc, narrowed to 16 bits.

IsMonsterAffectedByGravelyrockGroundMode normalises the species through
FemaleToMaleForm and compares against two ids, left as raw values because naming
them is not this commit's job. Its return is word-sized: bool8 appends
and r0, r0, #0xff, which the target does not have.

SwapShopFreeDoublePointer frees through two levels of indirection and clears the
outer pointer, returning early when it is already null.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 17:37:04 -07:00
cecilarmitais
35134eced4 Decomp twelve game-state and recycle-shop accessors
Decompile from asm:

  AddMoneyStored                      0x02010758
  SetEggSpecies                       0x02010794
  SetUnkGameState0x13a6               0x020107C4
  SetEggHatchTimer                    0x020107F4
  DecrementEggHatchTimer              0x0201080C
  GetRecycleItemId                    0x02011DF0
  RecycleItemHasTradeTypePrizeTicket  0x02011DFC
  GetRecycleItemBonusOdds             0x02011E18
  ClearRecycleShopOffer               0x02011F14
  GetGameStateRecycleCount            0x02011F30
  GetRankForRecycleShop               0x02011F48
  GetRecycleOfferCooldown             0x0201227C

Nine of these reach BAG_ITEMS_PTR_MIRROR. Five are the setters matching getters
already landed; the other four use offsets past them, so struct bag_items gains
four more fields, up to 0x13B4. As with the previous extension they are appended
past the end, and the field at 0x13AC is left to align naturally rather than
declaring a byte at 0x13AB that nothing reads.

AddMoneyStored tail-calls SetMoneyStored, which is still asm; its prototype is
provisional. DecrementEggHatchTimer only decrements a non-zero timer, which the
target expresses with predicated subne/strneh rather than a branch.

The remaining three take a pointer to a pointer to a small struct with fields at
0x0, 0x4 and 0x12. No existing type matches that layout, so it is introduced as
struct unk_02011DF0, named for the lowest-addressed function that takes it, with
placeholder fields. The bytes between 0x8 and 0x12 are unexamined and left as
filler.

Every one of the twelve matched on the first candidate.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 17:28:27 -07:00
cecilarmitais
06c828e19b Decomp CountItemTypeInBag and GetEquippedThrowableItem
Decompile from asm:

  CountItemTypeInBag        0x0200EE88
  GetEquippedThrowableItem  0x0200F208

CountItemTypeInBag totals matching slots, adding the stack size for thrown items
and one for everything else, using IsThrownItem from item_util.c.

GetEquippedThrowableItem returns the index of the first existing slot flagged
usable by L+R, or -1. It reads item->flags through struct item_volatile. The
target loads the flags byte twice, once for each test, where a plain struct item
lets the compiler reuse the first load; item.h already carries item_volatile for
exactly this purpose and says so. The cast is written at each access, following
dungeon_ai_items.c and special_move_types.c rather than casting the cursor once,
though both spellings score 0.

Only the second flag test is left as a bare mask. The existence test goes through
an explicit bool8 because the target boolifies it -- tst, movne, moveq, tst
#0xff -- while the second test branches straight off tst, which is what a bare
mask in an if produces.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 17:16:47 -07:00
cecilarmitais
a49bcedffc Decomp CountNbItemsOfTypeInBag and HasStorableItems
Decompile from asm:

  CountNbItemsOfTypeInBag  0x0200EE4C
  HasStorableItems         0x0200F0FC

CountNbItemsOfTypeInBag counts every slot whose id matches, without checking the
existence flag, so empty slots are counted when the caller asks for whatever id
they hold. That is what the target does; it is not an omission here.

HasStorableItems checks the existence flag through the same explicit bool8 the
other bag walkers use, then calls IsStorableItem, which is already decompiled in
item_util_4.c, and returns on the first slot satisfying both.

HasStorableItems sits at a lower address than GetItemIndex, so it is prepended
to that file. The extern for BAG_ITEMS_PTR_MIRROR was below the existing
function and is moved to the top, above both, since the new first function needs
it too.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 17:06:03 -07:00
cecilarmitais
3c5de72d23 Decomp IsItemWithFlagsInBag and GetItemIndex
Decompile from asm:

  IsItemWithFlagsInBag  0x0200EF20
  GetItemIndex          0x0200F14C

IsItemWithFlagsInBag matches an item by id and then by any of a flag mask, and
returns on the first item satisfying both. The two conditions can be written as
a single && or as nested ifs; both score 0, so the target does not distinguish
them and the && form is used as the more direct reading.

GetItemIndex takes a struct item pointer rather than an id. The target compares
the walking cursor against the argument, not a field of it, so this returns the
index of a bag slot the caller already holds a pointer to, and -1 when the
pointer is not in the active inventory. The index is returned through a 16-bit
narrowing, which is why the return type is s16 rather than the s32 the loop
counter uses.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 16:57:10 -07:00
cecilarmitais
fdbc544fcb Decomp GetNbItemsInBag and IsItemInBag
Decompile from asm:

  GetNbItemsInBag  0x0200EDFC
  IsItemInBag      0x0200EEE0

Both walk the active inventory with a cursor rather than indexing, with the
index and the cursor both advanced in the for clause, which is what puts the two
adds at the top of the loop body as the target has them.

GetNbItemsInBag's existence test goes through an explicit boolean:
(item->flags & ITEM_FLAG_EXISTS) != 0 assigned to a bool8 before the branch.
That is what produces the target's tst / movne / moveq / tst #0xff sequence;
testing the masked value directly collapses it.

IsItemInBag returns early on the first match, which the target reaches with
bxeq lr mid-loop, and falls through to a single return of zero.

GetNbItemsInBag lands in the existing src/main_0200EDC0.c, which already
includes item.h; IsItemInBag needs a new file and declares
BAG_ITEMS_PTR_MIRROR alongside it, matching how the other bag sources in this
tree declare it.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 16:48:08 -07:00
cecilarmitais
399618a493 Decomp GetItemAtIdx and RemoveEmptyItemsInBag
Decompile from asm:

  GetItemAtIdx           0x0200F340
  RemoveEmptyItemsInBag  0x0200F370

GetItemAtIdx returns a pointer into the active inventory, or null for a negative
index. Its index parameter is s16, not s32: struct item is 6 bytes, and at 16-bit
width the compiler folds the index scaling and the base into a single smlabb,
which is what the target emits. An s32 index produces a separate multiply and
add, at score 1100.

RemoveEmptyItemsInBag tail-calls RemoveEmptyItems over the active inventory with
INVENTORY_SIZE, which is already 50 in item.h and matches the 0x32 immediate.

RemoveEmptyItems is still asm and was not declared anywhere in the tree, so its
prototype is provisional and declared here.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 16:42:09 -07:00
cecilarmitais
d4b2c2fc01 Decomp two bag helpers that forward to asm callees
Decompile from asm:

  RemoveFirstUnequippedItemOfType  0x0200F798
  AddItemToBagNoHeld              0x0200F874

Both are thin forwarders. AddItemToBagNoHeld tail-calls AddItemToBag with a zero
second argument, which the target reaches with bx rather than bl.
RemoveFirstUnequippedItemOfType feeds GetFirstUnequippedItemOfType's result
straight into RemoveItemNoHoleCheck.

All three callees are still asm and had no declaration anywhere in the tree, so
their prototypes are provisional and declared in the new headers with the
loosest types that compile. They should collapse into the callees' own headers
when those land.

The return types are not determined by these functions' bytes. Both forward
whatever the callee returns without touching it, so declaring them void scores 0
as well. They are written as returning u32 because that is what a pass-through
of a word-sized result reads as, but a reviewer should treat the return type as
a guess rather than something the target settles.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 16:34:39 -07:00
cecilarmitais
27c9ec9af6 Decomp four bag accessors; extend struct bag_items past maybeMoney
Decompile from asm:

  GetMoneyStored         0x0201070C
  GetEggSpecies          0x0201077C
  GetUnkGameState0x13a6  0x020107AC
  GetEggHatchTimer       0x020107DC

Each dereferences BAG_ITEMS_PTR_MIRROR and reads one field. struct bag_items in
include/item.h ended at 0x13A0, immediately before every offset these four use,
so it gains four fields: a word at 0x13A0, a signed halfword at 0x13A4 and
halfwords at 0x13A6 and 0x13A8. The widths come from the loads -- GetEggSpecies
uses ldrsh where the other two halfword readers use ldrh.

The fields are placeholders rather than named after their accessors. The
function names are suggestive, and a later pmdsky-debug sync can name them; the
offsets and widths are facts read off the asm, which is all this commit claims.

Extending a shared struct is the risk here, so it was checked rather than
assumed. The four fields are appended past the end, after maybeMoney, so no
existing member moves. All four translation units that use struct bag_items --
dungeon_ai_items.c, main_0200ECFC.c, main_0200EDC0.c and
overlay_31_02383478.c -- are rebuilt by this commit's build and the ROM still
matches.

The two constants the target splits the offset into differ per function
(0x1300 + 0xa8 against 0x1000 + 0x3a0) because each is a valid ARM immediate
encoding of the same address; the compiler reproduces the choice from the field
offset alone.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 16:28:07 -07:00
cecilarmitais
0888986aaf Decomp the tempo and signal track events
Decompile from asm:

  DseTrackEvent_SetBpm   0x02071AE0
  DseTrackEvent_SetBpm2  0x02071B20
  DseTrackEvent_Signal   0x0207296C

SetBpm and SetBpm2 have identical bodies. The two differ only in the label their
literal pool uses, so they are the same routine reached from two opcodes rather
than variants of one; both are decompiled as written rather than one calling the
other, because that is what the target does.

They scale the sequence's tempo fade by the new bpm and divide 0x03938700 --
sixty million microseconds, i.e. one minute -- by the result to get
microseconds_per_beat, guarding a zero divisor by substituting 1. The shifts
type the intermediate: the tempo is read with an arithmetic shift and the
product with a logical one, so the value handed to _u32_div_f is unsigned.

Signal writes the stream byte to the sequence and passes it to the sequence's
signal_callback with code 8, following DseTrackEvent_SetInstrument in
lib/DSE/src/main_02071BF4.c, which makes the same call with a different code.
The callback's four arguments -- id, code, value, callback_arg -- come from the
existing function-pointer type in dse.h, not from the call site.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 16:14:25 -07:00
cecilarmitais
59fad373cc Decomp three LFO setup track events; complete struct dse_lfo_settings
Decompile from asm:

  DseTrackEvent_SetupVolumeLfo  0x020724A8
  DseTrackEvent_SetupPanLfo     0x020726C4
  DseTrackEvent_SetupLfo        0x02072770

Each reads five bytes and initialises one lfo_settings entry: waveform index,
a signed 16-bit amplitude, a phase-change time, and zeroes for the envelope
fields. The two fixed-slot handlers also set type and output_type, using the
same slot-to-output_type mapping as the Use handlers -- 2 for volume, 3 for pan.
SetupLfo is the indexed form and takes its slot from channel + 0x61.

struct dse_lfo_settings gains field_0xE. All four of these handlers store a zero
byte at that offset, which the struct previously described as trailing padding.
The struct's size is unchanged: it already rounded to 0x10 for the s32
amplitude's alignment, and the 0x10 stride is confirmed by the shift-by-4 the
asm uses to index the array. Adding the field is therefore byte-neutral to
every existing user, which the build confirms.

All five stream bytes are read into locals before the first store. Reading them
inline instead interleaves the loads between stores, and the target issues all
five ldrb up front.

DseTrackEvent_SetupKeyBendLfo is deliberately not included. It is the same
shape and reaches an instruction-for-instruction identical body -- every
mnemonic, offset and immediate matches -- differing only in which registers the
allocator picks, at score 65. Reordering the locals, introducing an explicit
amplitude temporary, and dropping the pointer local were all tried and moved the
score without closing it. It is left in asm rather than landed as a near-match.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 16:06:07 -07:00
cecilarmitais
882d2edf64 Decomp the three tuning-delta track events
Decompile from asm:

  DseTrackEvent_TuningDeltaCoarse  0x02071EB4
  DseTrackEvent_TuningDeltaFine    0x02071F3C
  DseTrackEvent_TuningDeltaFull    0x02071FC4

All three are DseTrackEvent_SetTuning with the new tuning derived from the old
one instead of replacing it, and they share its tail verbatim: recompute
bend_final from the tuning, the channel's bend fade and the synth's bend, then
mask interrupts and set update_flags bit 0x10 on every voice in the channel.

They differ only in how the delta is scaled. Coarse shifts the signed byte left
8, Fine shifts it left 2, and Full takes a little-endian pair of bytes and adds
it whole.

Coarse and Fine need the delta written before the existing tuning --
(delta << n) + channel->tuning, not channel->tuning + (delta << n). The target
loads the stream byte first and folds the shift into the add's second operand;
the other order reverses both the loads and the add, at score 215. Full is
unaffected because its byte pair is assembled before the add either way.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 15:55:57 -07:00
cecilarmitais
019b4543a2 Decomp the volume, expression and pan track events
Decompile from asm:

  DseTrackEvent_SetVolume      0x0207227C
  DseTrackEvent_VolumeDelta    0x02072310
  DseTrackEvent_SetExpression  0x0207241C
  DseTrackEvent_SetPan         0x02072554
  DseTrackEvent_PanDelta       0x020725D4

All five follow DseTrackEvent_SetTuning in lib/DSE/src/main_02071BF4.c, which
was already landed and is a complete template for this shape: write the fade,
recompute the final value, then mask interrupts through IME while walking
channel->voice_list and setting an update_flags bit on every voice. The bit
differs by what changed -- 0x20 for volume and expression, 0x40 for pan, where
SetTuning uses 0x10.

Offsets all resolve against existing dse.h types. struct dse_fade is 0x10, so
the channel's bend, volume and pan fades sit at 0x1c, 0x2c and 0x3c, which is
what the stores at 0x2c/0x34/0x38 and 0x3c/0x44/0x48 address. container is at
0xc4 and struct dse_synth puts pan at +7 and song_and_global_volume at +8, both
of which the asm loads with ldrsb.

The volume handlers scale by song_and_global_volume * volume * expression and
divide by 127 * 127; the compiler reproduces the target's smull-based division
by that constant, so the magic word 0x82061029 needs no special handling.

Two details that were not obvious. The final pan is v + (container->pan - 0x40),
not (v + container->pan) - 0x40: the target subtracts before adding, and the
other grouping emits the two instructions in the opposite order. And the discarded
read of IME before restoring it only survives if IME is declared through a
volatile type; without volatile the compiler drops the load and the restore is
all that remains.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 15:48:27 -07:00
cecilarmitais
c356d53c74 Decomp the bank and key-bend track events; restore stripped label addresses
Decompile from asm:

  DseTrackEvent_SetBank     0x02071B60
  DseTrackEvent_SetBankMsb  0x02071B8C
  DseTrackEvent_SetKeyBend  0x02072184

All three assemble a 16-bit value from the event stream and hand it to a
DseChannel_ setter, following DseTrackEvent_SetBankLsb in
lib/DSE/src/main_02071BF4.c. SetBankLsb replaces the low byte of swd_id;
SetBankMsb is its mirror and replaces the high byte, which is why its operands
read (*ptr_next_byte << 8) + (channel->swd_id & 0xFF). Written the other way
round the addition is emitted in the wrong order and scores 30.

SetBank and SetKeyBend take both bytes from the stream. They differ only in the
width cast the target applies before the call -- lsr for the unsigned bank,
asr for the signed bend -- which is what types each callee's second parameter.

DseChannel_SetBank and DseChannel_SetKeyBend are still asm. Their prototypes are
provisional and declared in the new headers. Note DseChannel_SetBank is now
declared in two places: here and in lib/DSE/src/main_02071BF4.c, which declared
it from its own call site earlier. They agree, but nothing in the build checks
that, and the duplicate should collapse into one header when the callee lands.

This commit also restores four `; 0xADDRESS` label comments in
lib/DSE/asm/main_02071AE0.s that a previous precommit run stripped. The addresses
came from the pre-split file at 00d4d642. Nothing was mismatched by their
absence -- the ROM built fine without them -- but extract_function.py finds a
function by searching its label line for `; 0x`, so SetBank and SetBankMsb could
not be extracted until they were back. precommit.py now keeps that form.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 15:39:34 -07:00
cecilarmitais
acff5cf391 Decomp the generic-LFO and note-random-region track events
Decompile from asm:

  DseTrackEvent_SetNoteRandomRegion  0x02072144
  DseTrackEvent_SetupLfoEnvelope     0x020727C8
  DseTrackEvent_UseLfo               0x02072938

SetNoteRandomRegion reads two bytes and stores the smaller in
note_random_region_begin and the larger in note_random_region_end, so the pair
is sorted regardless of the order it was written in.

SetupLfoEnvelope and UseLfo are the indexed forms of the per-slot handlers added
in the previous commit: instead of a fixed lfo_settings entry they take the
index from channel + 0x61, which UseLfo also writes. That byte falls inside
dse.h's field_0x5A[10], so it is spelled field_0x5A[7]; a name for it would have
to come from a real pmdsky-debug sync.

UseLfo needed the sub-struct bound to a pointer local. Indexing
channel->lfo_settings[idx] directly folds the array's 0x74 base into each
store's offset, giving strb [r1, #0x75]; the target computes the element address
once, add r1, r3, #0x74 then add r1, r1, idx lsl #4, and stores at [r1, #1] and
[r1, #2]. Binding struct dse_lfo_settings *lfo also reproduces the frame push
the target has, which no arrangement of the indexed form did.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 15:29:55 -07:00
cecilarmitais
909484fb99 Decomp the six DSE LFO track-event handlers
Decompile from asm:

  DseTrackEvent_SetupKeyBendLfoEnvelope  0x0207222C
  DseTrackEvent_UseKeyBendLfo            0x0207225C
  DseTrackEvent_SetupVolumeLfoEnvelope   0x02072504
  DseTrackEvent_UseVolumeLfo             0x02072534
  DseTrackEvent_SetupPanLfoEnvelope      0x02072720
  DseTrackEvent_UsePanLfo                0x02072750

All six index struct dse_channel's existing lfo_settings[4], which sits at
offset 0x74 with a 0x10 stride -- the stride the asm computes as ip lsl #4, and
the offset another handler in this family loads directly as channel + 0x74. The
three pairs address entries 0, 1 and 2, so key bend, volume and pan each own one
lfo_settings slot. Within an entry the setters write +0xa and +0xc, which are
msec_until_lfo_started and lfo_envelope_len_msec, and the Use handlers write +1
and +2, which are type and output_type.

output_type is set to 1, 2 and 3 for key bend, volume and pan respectively,
which is consistent with the slot each one uses; that is an observation about
the constants, not something the bytes label.

The Use handlers needed an explicit if/else rather than a ternary. Every
conditional-expression spelling tried -- v ? N : 0, v == 0 ? 0 : N, !v ? 0 : N,
and (v != 0) -- emits movne before moveq, where the target emits moveq before
movne. The statement form reverses the pair and matches.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 15:18:27 -07:00
cecilarmitais
ed153d5fce Use the established parameter names for the arm9 DSE handlers
The eleven handlers decompiled in the two previous commits used
(position, seq, track, channel). The handlers already landed in
lib/DSE/src/main_02071A98.c, which are the same dispatch family reached from the
same SMD_EVENTS_FUN_TABLE, use (ptr_next_byte, sequence, track, channel). This
renames the arm9 ones to match so the family reads consistently, and follows the
existing pointer spelling for the sub-loop local.

Parameter names do not affect code generation; the rebuild below is to confirm
that rather than to assume it.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 15:11:09 -07:00
cecilarmitais
3046c8b010 Decomp six DSE track-event handlers in lib/DSE
Decompile from asm:

  DseTrackEvent_SetOctave                 0x02071AC0
  DseTrackEvent_OctaveDelta               0x02071ACC
  DseTrackEvent_SetTuningJitterAmplitude  0x0207216C
  DseTrackEvent_SetUnknown2               0x020721B0
  DseTrackEvent_SetKeyBendRange           0x020721C8
  DseTrackEvent_Dummy2Bytes2              0x0207299C

These are the same handler family as the previous two commits, but they live in
lib/DSE, which is compiled with different flags: -O4,p and -enum int, where the
arm9 modules use -O4,s and -enum min. Signatures follow the parameter names
already used by the landed handlers in lib/DSE/src/main_02071A98.c rather than
the ones the previous two commits introduced.

Field names come from struct dse_channel in lib/DSE/include/dse.h and match the
offsets the asm uses: bend_jitter_amplitude at 0xa, field_0x56 at 0x56, and
bend_sensitivity_override at 0x59, whose meaning agrees with SetKeyBendRange
writing to it.

SetTuningJitterAmplitude and SetUnknown2 read their two operand bytes in
opposite orders -- little-endian for the first, big-endian for the second --
which the asm's shift operands show directly.

Landing these needed two adjustments the arm9 path does not. extract_function.py
hardcodes asm/, src/, include/ and main.lsf, so it was run with lib/DSE as the
working directory; the object lines it then inserts into main.lsf carry no
lib/DSE/ prefix and were corrected by hand. It also emits
.include "main_XXXX.inc", which is right for arm9 where the search path is
./asm/include, but lib asm is built with -Ilib/DSE/asm/ and its existing files
use .include "include/main_XXXX.inc"; the four split files were fixed to match.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 15:01:54 -07:00
cecilarmitais
00d4d6428c Decomp the last four DSE track-event handlers in arm9
Decompile from asm:

  DseTrackEvent_WaitUntilFadeout  0x0207199C
  DseTrackEvent_EndTrack          0x020719C8
  DseTrackEvent_SubLoopBegin      0x02071A1C
  DseTrackEvent_SubLoopEnd        0x02071A5C

This empties asm/main_0207199C.s and asm/main_02071A1C.s, so both files and
their .inc are removed rather than split, and the handlers merge into the
adjacent sources added by the previous commit.

EndTrack confirms the second parameter's type, which the previous commit could
only infer from the dispatcher: it writes 1 to the byte at offset 3 of that
argument, which is field_0x0.has_looped in struct dse_sequence. A track that
reaches the end of its main loop setting has_looped is consistent, and no other
struct in dse.h places a byte with that meaning at offset 3.

SubLoopBegin and SubLoopEnd index track->loop_stack, the existing
struct dse_sub_loop[4] at offset 0x24 with a 12-byte stride, which is what the
asm's multiply by 0xc computes.

SubLoopEnd needed two changes past the obvious form. Its index and count locals
are int, not u8: at byte width the compiler inserts and rX, rX, #0xff after each
decrement, which the target does not have. And the early return has to be the
fall-through path -- written as an early return the compiler if-converts the
tail into predicated stores, where the target branches with bxeq lr. Inverting
the condition, so the longer body sits inside the if, produces the branch.

DseVoice_CountNumActiveInChannel and DseChannel_DeallocateVoices are still asm;
their prototypes are provisional and declared in this file's header. Neither was
declared anywhere else in the tree.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 14:42:05 -07:00
cecilarmitais
262177377d Decomp seven DSE track-event handlers
Decompile from asm:

  DseTrackEvent_Invalid        0x0207191C
  DseTrackEvent_WaitSame       0x02071928
  DseTrackEvent_WaitDelta      0x02071934
  DseTrackEvent_Wait8          0x0207194C
  DseTrackEvent_Wait16         0x0207195C
  DseTrackEvent_Wait24         0x02071978
  DseTrackEvent_MainLoopBegin  0x02071A14

These are handlers in SMD_EVENTS_FUN_TABLE, dispatched by ParseDseEvent. No
new types are introduced: struct dse_track and struct dse_sub_loop already
exist in lib/DSE/include/dse.h, and every offset these functions touch lines up
with a named field there -- is_playing at 2, wait_ticks_left at 0xc,
previous_wait_ticks at 0x10, loop_start at 0x20.

The shared four-argument signature is read off the dispatcher rather than
guessed. ParseDseEvent loads the handler from SMD_EVENTS_FUN_TABLE and calls it
with r0 already advanced past the opcode byte, r1 its own first argument, r2 the
track, and r3 track->channel from offset 0x54; the returned r0 becomes the
track's new position. Its caller passes a sequence and that sequence's track at
offset 0xa8, which is what types the second parameter.

The second parameter is unused by all seven bodies, so their own bytes do not
constrain its type; it is typed from the dispatcher's call site, and the
handlers that do use it are still asm.

Six of these are contiguous and merge into one source file; MainLoopBegin sits
after WaitUntilFadeout and EndTrack, which remain asm, so it splits separately.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 14:31:22 -07:00
cecilarmitais
e6c5c87501 Decomp five leaf functions; replace four stale prototypes
Decompile from asm:

  IsMonsterIdInNormalRange          0x02056294
  Rgb8ToRgb5                        0x02004FCC
  Arm9LoadUnkFieldNa0x2029EC8       0x02029EC8
  Arm9StoreUnkFieldNa0x2029ED8      0x02029ED8
  CanMonsterBeUsedForMissionWrapper 0x02062A58

Each was extracted with extract_function.py, splitting its .s and adding the
new objects to main.lsf. PREV_MENU_ITEMS is declared alongside the two
accessors that use it, sized 0x18 from the .space in main_bss_020B3380.s.

Four prototypes previously declared from call sites are replaced by includes of
the new headers: IsMonsterIdInNormalRange in main_02056D70.c, Rgb8ToRgb5 in
main_0201E380.c, and both Arm9 accessors in overlay_31_02382820.c.

Arm9LoadUnkFieldNa0x2029EC8 keeps the two-parameter prototype that was already
declared in overlay_31_02382820.c. Its body reads only the first argument -- the
second register is overwritten with the table address before any use -- so the
function's own bytes do not distinguish a one-parameter form from a two-
parameter one, and both score 0 against the target. The call site does
distinguish them: overlay_31_02382820.c passes two arguments and already
matches, so the existing declaration is the load-bearing evidence. That object
is rebuilt here and still matches.

CanMonsterBeUsedForMission is still asm, so its prototype is provisional and
declared in the caller's header. Its second parameter is byte-width: a caller
in main_0205D1F4.s masks with and r1, r1, #0xff before the call.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build of all five together: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 14:22:17 -07:00
cecilarmitais
0f2e0ea8c4 Decomp OverlayIsLoaded; migrate its prog_pos_info array to C
Decompile OverlayIsLoaded (0x02003ED0) from asm/main_02003DB8.s. It maps an
overlay group id to whether that group is currently loaded, via a jump table
over ids 0-0x24: group 0 is always loaded, and the other ranges check one of
the three LOADED_OVERLAY_GROUP_0 slots, gated on the parent slot holding the
expected group. Out-of-range ids fail an assert through Debug_FatalError with a
prog_pos_info record for overlay.c line 177.

That record is the reason this commit also moves data. The function copies
_02092558[2] to the stack to pass its address, and the copy's two loads only
schedule in the target's order when the compiler can see the array's
initializers -- an extern declaration of the same array compiles to the loads
transposed. So the five-record array moves into the C file as a const
definition. The values are the ones decoded from the data: five entries sharing
the overlay.c filename with lines 887, 534, 177, 269 and 626.

The array's neighbours cannot move with it. The two strings that follow it in
retail cannot be produced by this compiler in place: mwccarm 2.0/sp2p2 emits
anonymous string literals into writable .data sections, which the link places in
the far data region, and it has no -str readonly (only [no]reuse and [no]pool);
a named const u8 string does land in .rodata but is emitted before the array
regardless of definition order. The strings therefore stay as assembly, in a new
asm/main_rodata_02092580.s holding exactly overlay.c and check group error.

asm/main_rodata_020908D4.s is accordingly split: it now ends just before
_02092558, and the remainder from _020925A0 onward becomes
asm/main_rodata_020925A0.s. Both new rodata files include the original
main_rodata_020908D4.inc, which other rodata files also include as a shared
declaration header and which is left untouched. main.lsf orders the pieces
src/main_02003ED0.o, asm/main_rodata_02092580.o, asm/main_rodata_020925A0.o, so
the linker lays out the array, the strings and the remaining data at their
retail addresses; the extraction also splits the text file, with the trailing
functions moving verbatim into asm/main_020040AC.s.

No new names are introduced: OverlayIsLoaded, struct prog_pos_info,
Debug_FatalError, LOADED_OVERLAY_GROUP_0 and the three data placeholders all
already exist in the tree.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK. Intermediate layouts were verified by
binary-diffing the built main.sbin against a clean build, which is what located
each divergence exactly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:45:36 -07:00
cecilarmitais
4f33a43bf9 Decomp ov11_022E96E4
Decompile ov11_022E96E4 from asm/overlay_11.s. It returns the byte at offset 4 of
ov11_02324FA0. This is overlay_11's first decompiled function, so it creates
src/overlay_11_022E96E4.c rather than merging; the trailing functions move
verbatim into asm/overlay_11_022E96F4.s, the .inc files are regenerated, and the
two objects are added inside main.lsf's Overlay OVY_11 section.

ov11_02324FA0 is declared as u8[0x20] rather than given a struct. The symbol is
.space 0x20 in the data, so the size is a fact, and other functions in the
overlay touch offsets 0x0, 0xc, 0x10, 0x14 and 0x1c -- none of which are
decompiled, so a struct would have to invent a layout around the one field this
function reads. The array asserts only the size.

The return type is u8. The load is ldrb, so the width is known, but every
candidate return type produces identical bytes here; bool8 would additionally
assert that the value is a truth value, which is only an inference from the two
callers testing it in a condition. u8 states what is known and nothing more.

sub_02065050 and sub_0206549C call it and had a provisional int prototype in
main_02064FFC.h while only the call site was understood. That is replaced by an
include of the new header, and both were rebuilt to confirm they stay
byte-identical.

No new names are introduced: both ov11_022E96E4 and ov11_02324FA0 are
pre-existing labels.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:45:14 -07:00
cecilarmitais
e1879864e2 Decomp sub_020564B0 and sub_0201E380
Decompile two more callees of the actor cluster, from asm/main_02055894.s and
asm/main_020151E4.s. Each splits its file; the trailing functions move verbatim
into asm/main_02056504.s and asm/main_0201E3AC.s, the affected .inc files are
regenerated, and main.lsf gains the four objects.

sub_020564B0 takes a member index and reports whether it is the appointed
leader's. It repeats the roster read that GetAppointedLeaderMemberIdx performs --
active_team selects one of the number_active_team_members fields, and that value
indexes active_roster_member_idxs -- and compares the result against its
argument, returning false if either the argument or the intermediate is -1. With
this, both NPC-collection filters are accounted for: GetAdventureNpcIds excludes
the appointed leader, and GetUnitNpcIds excludes the three main characters.

sub_0201E380 writes one palette entry. It converts an 8-bit RGB triple through
Rgb8ToRgb5 into the halfword at index (c << 8) + d of the table its first
argument points at, then sets a byte at index c in a second table, which reads as
marking that palette dirty.

That first argument is a struct with a halfword pointer at 0x18 and a byte
pointer at 0x1c. No global has that type -- callers pass an interior offset of
whatever _020AFC70 points at -- so it takes the address of the function that
receives it, struct unk_0201E380, with fields named for their offsets. The
parameter was previously declared u8* while only the call site was understood,
so sub_02064F94 now casts at that call site; it was rebuilt to confirm it stays
byte-identical. src/main_02056D70.c likewise drops its local extern for
sub_020564B0 in favour of the new header.

No other names are introduced. Rgb8ToRgb5, TEAM_MEMBER_TABLE_PTR,
struct team_member_table and its fields all already exist, and both functions
keep their sub_ names.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:45:14 -07:00
cecilarmitais
6371544da2 Decomp GetUnitNpcIds and GetAdventureNpcIds
Decompile both from asm/main_02056960.s. Each walks the four entries of
TEAM_MEMBER_TABLE_PTR->active_roster and, for every entry with bit 1 of its flags
byte set, takes member_index and keeps it if IsMonsterIdInNormalRange accepts it
and a filter rejects it: GetUnitNpcIds excludes the three main characters via
sub_02055410, sub_02055474 and sub_020554D8, and GetAdventureNpcIds excludes via
sub_020564B0. Matching indices are appended to the caller's buffer and the count
is returned.

Both tolerate a null buffer. The target reserves eight bytes of stack and
redirects the pointer there when the argument is null, which is why the frame
reserves space nothing else uses. That is reproduced with a local s16 scratch[4].

They merge into a new src/main_02056D70.c; extracting them splits
asm/main_02056960.s, the trailing functions move verbatim into
asm/main_02056EB8.s, and main.lsf gains the two objects.

The buffer parameter is s16*, which is what both call sites pass and what the
stores write. Their prototypes move out of main_02064FFC.h, where they were
declared as void* while their callers were decompiled, into the new header. That
tightening also required sub_02065050's two scratch buffers to change from
u8[8] to s16[4] -- the same eight bytes, and already how sub_0206549C declares
its own. sub_02065050 was rebuilt to confirm it stays byte-identical.

The flag test reads the first byte of struct team_member directly, as
(*(u8 *)m & 2). common.h models that byte as a bitfield of f_is_valid:1 followed
by flags_unk1:7, and no access through those members reproduces the target's
single tst: reading flags_unk1 emits a shift pair to extract the field first, and
splitting bit 1 into its own one-bit member is worse still. The raw byte read is
the only form that matches, so it is used here rather than changing a struct
shared with the rest of the tree.

No new names are introduced. struct team_member, struct team_member_table, their
fields and TEAM_MEMBER_TABLE_PTR already exist, and both function names were
already labelled.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:45:14 -07:00
cecilarmitais
86d3db8f56 Decomp sub_02055410, sub_02055474 and sub_020554D8
Decompile the three member-index predicates from asm/main_02054E4C.s. Each takes
a member index, checks that TEAM_MEMBER_TABLE_PTR->members[idx] is valid, and
returns whether that index is the one a particular main character occupies for
the current game mode: sub_02055410 tests index 2 during a special episode and 0
otherwise, sub_02055474 tests 3 and 1, and sub_020554D8 tests 4 and is false
outside a special episode. Those are the same indices GetMainCharacter1,
GetMainCharacter2 and GetMainCharacter3 select, so each predicate answers
"is this index that main character" without going through the accessor.

They merge into a new src/main_02055410.c; extracting them splits
asm/main_02054E4C.s, the trailing functions move verbatim into
asm/main_02055528.s, and main.lsf gains the two objects.

The return type is bool8 rather than int, and the parameter is subscripted as
members[(s16)idx] so the index multiply compiles to smulbb. Their prototypes move
out of main_02064FFC.h, where they were declared while sub_02065050 was
decompiled, into the new header, which main_02064FFC.h now includes;
sub_02065050 was rebuilt to confirm the return-type change leaves it
byte-identical.

The new header includes util.h. Without it the header is not self-contained:
bool8 is declared there, and a header that uses it before common.h is reached
makes the compiler infer int and then conflict with the real typedef.

No new names are introduced. struct team_member_table, its fields,
TEAM_MEMBER_TABLE_PTR, GetGameMode and GAME_MODE_SPECIAL_EPISODE already exist in
the tree, and all three functions keep their sub_ names.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:45:14 -07:00
cecilarmitais
3256b16116 Decomp GetAppointedLeaderMemberIdx and sub_02056914
Decompile the last two accessors of this cluster from asm/main_02055894.s. Both
resolve the appointed leader through the roster region of team_member_table:
they read the active team from active_team, index the three
number_active_team_members fields by it, and use that value to index
active_roster_member_idxs. GetAppointedLeaderMemberIdx returns the resulting
member index, or -1 when the first read is -1. sub_02056914 is its pointer
counterpart, returning &members[index] or NULL when either read is -1 -- the
same index/pointer pairing as GetHeroMemberIdx and GetHero.

Each sits in the middle of asm/main_02055894.s and is not adjacent to the other,
so each extraction splits the file: the trailing functions move verbatim into
asm/main_020568DC.s and asm/main_02056960.s, the affected .inc files are
regenerated, and main.lsf gains the four objects.

GetAppointedLeaderMemberIdx returns u32, consistent with the other MemberIdx
functions in this cluster. Its prototype and sub_02056914's move out of
main_02064FFC.h, where they were declared while their callers were decompiled,
into the new headers; main_02064FFC.h now includes those. sub_02065050 and
sub_0206549C call them and were rebuilt to confirm the u32 change leaves both
byte-identical.

The three number_active_team_members fields are separate scalars in common.h but
are indexed here as an array, which is what the asm does; the expression relies
on them being contiguous, which the struct layout guarantees.

No new names are introduced. struct team_member_table, struct ground_monster,
their fields and TEAM_MEMBER_TABLE_PTR all already exist in the tree, and both
function labels were already present.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:45:14 -07:00
cecilarmitais
ac61cc341c Decomp five team-member index accessors
Decompile GetHeroMemberIdx, GetPartnerMemberIdx, GetMainCharacter1MemberIdx,
GetMainCharacter2MemberIdx and GetMainCharacter3MemberIdx from
asm/main_02054E4C.s. Each returns the index of a TEAM_MEMBER_TABLE_PTR->members
entry when that entry's is_valid bit is set, or -1 otherwise: hero is index 0,
partner index 1, and the GetMainCharacter functions choose the index by game
mode -- 2/3/4 during a special episode, 0/1/none otherwise. They are the index
returning counterparts of the pointer accessors in the previous commit and merge
into a new src/main_0205567C.c.

The return type is u32. This matters: with a signed int return, the compiler
recognises `valid ? 0 : -1` as the idiom -(!valid) and emits an arithmetic
negate, but the target uses a conditional-move select. Under u32, -1 is
0xFFFFFFFF, the negate idiom no longer applies, and the compiler emits the
select the target has. The distinction is only observable for hero, whose index
is the constant 0; for the others the index is non-zero or runtime, so both
signednesses produce the select. The whole family is typed u32 for consistency,
and sub_02065050, which calls two of these, was rebuilt to confirm the type
change leaves it byte-identical.

No new names are introduced. struct team_member_table, struct ground_monster and
TEAM_MEMBER_TABLE_PTR already exist in the tree, and the five function names were
already labelled; this commit only fills in bodies.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:45:13 -07:00
cecilarmitais
45761ded07 Decomp five team-member accessors
Decompile GetHero, GetPartner, GetMainCharacter1, GetMainCharacter2 and
GetMainCharacter3 from asm/main_02054E4C.s. Each returns a pointer to a
struct ground_monster in TEAM_MEMBER_TABLE_PTR->members, or NULL when that
entry's is_valid bit is clear: GetHero returns members[0], GetPartner
members[1], and the GetMainCharacter functions pick the index from the game
mode -- members[2]/[3]/[4] during a special episode, members[0]/[1]/NULL
otherwise.

They merge into a new src/main_02055770.c; extracting them splits
asm/main_02054E4C.s, the trailing functions move verbatim into
asm/main_02055894.s, and main.lsf gains the two objects. Their prototypes were
temporarily declared in main_02064FFC.h for the previous commits; they move to
the new main_02055770.h, which main_02064FFC.h now includes. sub_02056914 keeps
its prototype there since it is still asm.

No new names are introduced. struct ground_monster and struct team_member_table
already exist in common.h, and these six function names were already labelled in
the tree; this commit only fills in bodies.

Decompiling these confirms by construction what the previous commits declared:
the accessors return struct ground_monster*, so the ->id reads that feed the
monster-id output of sub_0206549C are reading struct ground_monster::id, the
documented "Monster ID" field.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:45:13 -07:00
cecilarmitais
4032598287 Use canonical ground_monster / team_member_table in sub_0206549C
The previous commit declared struct unk_020B0A48 for the records that
TEAM_MEMBER_TABLE_PTR points at. That global is already typed in the tree as
struct team_member_table (common.h), whose members[] elements are
struct ground_monster -- a 0x44-byte record whose field at 0x4 is the documented
s16 Monster ID. The placeholder was a redundant reinvention of it, and it
declared TEAM_MEMBER_TABLE_PTR a second time, conflicting with the extern that
two other decompiled files already use.

Replace it with the canonical types: TEAM_MEMBER_TABLE_PTR becomes
struct team_member_table*, the six accessors return struct ground_monster*, and
the nine reads that fed *out through ->field_0x4 now read ->id (or
->members[i].id for the table-indexed pair). struct unk_020B0A48 is removed and
common.h is included for the definitions.

members[] sits at offset 0 of team_member_table and ground_monster has the same
0x44 stride and 0x4 field as the placeholder, so the reads compute identical
addresses. Confirmed unchanged by a matching build.

Beyond removing a duplicate struct, this makes the output type legible from the
decompilation itself: every value written to *out in sub_0206549C now reads,
in-tree, as a monster id -- a ground_monster.id, a monster-valued script
variable, or an entry of a table that decodes to species.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:45:13 -07:00
cecilarmitais
39a8db1b00 Decomp sub_0206549C; make monster_id and a third field signed
Decompile sub_0206549C (0x0206549C), which completes the actor cluster. It calls
sub_02065050 to resolve the entity id in place, then maps the result to a
monster id written through its second argument: entity kinds of 3 resolve
against the loaded unit or adventure NPC list, and everything else dispatches
through a switch over the script entity ids, reading team members, script
variables or SPECIAL_ACTORS slots. It merges into src/main_02064FFC.c, which
removes the last of asm/main_0206549C.s; main.lsf drops the object.

enum monster_id gains MONSTER_INVALID = -1, for the same reason
enum script_entity_id gained ENTITY_NONE in the previous commit. This function
reads nine SPECIAL_ACTORS slots of that type with ldrsh, and an enum with no
negative enumerator is unsigned under -enum min, so those compiled to ldrh. The
name avoids MONSTER_NONE, which the _MONSTER_ID_GENDERED macro already defines
as 0. The sentinel was verified in isolation -- added alone, with nothing else
in the tree modified -- and the ROM still matches, so it is byte-neutral for all
existing code and only affects what reads such a field.

struct unk_020A7FF0 field_0x2 becomes s16 on the same evidence: the switch's
default case reads it with ldrsh. That is the third field corrected this way,
after field_0x0 and the script_entity_id slots, and the reason they all surfaced
late is that storing to a field cannot reveal its signedness.

struct unk_020B0A48 is declared for the 0x44-byte records that
TEAM_MEMBER_TABLE_PTR points at, named for that pointer's address, which is
derived by counting to the next label in the data. GetMainCharacter1,
GetMainCharacter2, GetMainCharacter3, GetHero, GetPartner and sub_02056914 all
return it, and none of them had a declaration.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:45:13 -07:00
cecilarmitais
580644e646 Decomp sub_02065050; make two script-entity fields signed
Decompile sub_02065050 (0x02065050), the resolver that rewrites a script entity
id in place and reports which storage it maps to. It redirects the four special
ids 0x2E-0x31 through SPECIAL_ACTORS, then dispatches on the entity's kind
field: kind 1 resolves the player slot from script variable 0x34, kind 2
resolves a team member from variables 0x35 and 0x36, kind 3 range-checks against
the loaded unit or adventure NPC list, and anything else handles the appointed
leader. It merges into src/main_02064FFC.c; asm/main_02065050.s splits, with
sub_0206549C moving verbatim into asm/main_0206549C.s.

Two type declarations are corrected, both diagnosed from the load instruction
rather than assumed. Storing to a field cannot reveal its signedness -- strh is
the same either way -- so neither error was observable until something read
these fields, and this is the first decompiled function that does.

enum script_entity_id gains ENTITY_NONE = -1. Under -enum min an enum with no
negative enumerator is unsigned, so the SPECIAL_ACTORS redirect slots compiled
to ldrh where the target has ldrsh. An explicit (s16) cast does not help: the
destination store is 16-bit, so the compiler folds the conversion away. The name
follows EPISODE_NONE = -1, already in this file. The sentinel was verified in
isolation, with no other change in the tree, and the ROM still matches -- it is
byte-neutral everywhere else and only affects code that reads such a field.

struct unk_020A7FF0 field_0x0 becomes s16 for the same reason. It was declared
u16 when the struct was introduced for sub_02065B14, which only reads field_0x8
-- genuinely unsigned, ldrh. Both resolvers read field_0x0 with ldrsh.

sub_02055410 is declared as taking the member index rather than void. The
argument is already in r0 from GetAppointedLeaderMemberIdx, so the call needs no
register move, which is what leaves r0 occupied across the sentinel comparison.
Declared void, the function is otherwise identical but allocates that constant
to r0 instead of r1.

The nine functions this calls that had no declaration are declared alongside;
parameter names are positional where only the call site is understood.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:45:13 -07:00
cecilarmitais
4463a320b6 Decomp four script/palette helpers ahead of the actor resolvers
Decompile sub_02064F94, sub_02064FE4, sub_02064FFC and sub_02065014 from asm.
All four merge into a new src/main_02064FFC.c; extracting them splits
asm/main_0205EDA4.s, the trailing functions move verbatim into
asm/main_02065050.s, the affected .inc files are regenerated, and main.lsf gains
src/main_02064FFC.o and asm/main_02065050.o. The two remaining functions of the
cluster, sub_02065050 and sub_0206549C, are now alone in that tail file.

sub_02064FFC and sub_02065014 index the same 12-byte table. EVENTS already
exists in the tree; its record type is declared here as struct unk_020A5488,
named for the table's address. Worth knowing for anyone reading the diff against
the asm: _020A5490 is a separate .global sitting at EVENTS + 8, inside the first
record, so sub_02064FFC's literal pool entry reads .word _020A5490 in the target
and EVENTS+0x8 from this source. Both resolve to the same address, which the
matching build confirms.

sub_02064F94 walks an array calling sub_0201E380, and sub_02064FE4 is a tail
call into it. Their two argument structs have no global, so they take the
address of the function that receives them -- struct unk_02064F94 and struct
unk_02064FE4 -- per the placeholder convention. sub_0201E380 and _020AFC70 are
still asm and have no header of their own, so both are declared alongside; move
them if a better home appears.

Nothing here is named for what it means. The structs and their fields are
placeholders, and local names stay structural: data is a cursor over 4-byte
records, count is the loop bound, value is the field the comparison reads.
sub_0201E380's parameters are positional, since only its call site is understood
and not the function itself. Anything more specific would be inferred from code
two calls downstream rather than read off the asm.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:45:13 -07:00
cecilarmitais
8157b86c69 Decomp sub_02065B14 and GetScriptEntityMonsterId
Decompile two script-entity accessors from asm.

sub_02065B14 (0x02065B14) merges into src/main_02065B3C.c, ahead of the
special-actor setters. It returns 0 for an id of -1, and otherwise a 16-bit
field of the ENTITIES record for that id. ENTITIES already exists in the tree
and keeps its name; the record type it needs is declared here as struct
unk_020A7FF0, 12 bytes, fields named for their offsets. Identifying that table
was not part of this change, so nothing here asserts what the fields mean --
only the layout, which is read off the asm: the stride comes from the smlabb
multiplier and the returned field from the ldrh displacement.

GetScriptEntityMonsterId (0x02065974) lands in a new src/main_02065974.c. It
takes the address of its own parameter and an s16 local, hands both to
sub_0206549C, and returns the local. sub_0206549C is still asm and has no header
of its own, so its prototype is declared alongside; move it if a better home
appears.

Extracting it splits asm/main_0205EDA4.s: the functions after it move verbatim
into asm/main_02065998.s, the affected .inc files are regenerated, and main.lsf
gains src/main_02065974.o and asm/main_02065998.o.

No comments are added to any pmd-sky file.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:44:46 -07:00
cecilarmitais
b989319248 Decomp RandomizeDemoActors; declare DEMO_TEAMS
Decompile RandomizeDemoActors (0x02065C48) from asm into src/main_02065C48.c.
It picks one of the 18 title-screen demo teams at random and stores that team's
hero/partner species into the SPECIAL_ACTORS demo slots.

DEMO_TEAMS is declared as an array of a 4-byte record, 18 entries, two 16-bit
fields read with ldrsh. The record type and its fields keep placeholder names --
struct unk_020A68C8, field_0x0, field_0x2 -- because identifying this table was
not part of this change. The offsets are facts read off the asm; what the fields
mean is not, so nothing here asserts it. DEMO_TEAMS itself already exists in the
tree and keeps its name.

Note that a separate .global, _020A68CA, sits at DEMO_TEAMS + 2, on the second
field of the first record; both symbols resolve to the same address.

The species are cast to enum monster_id when stored into the slots: enum
monster_id declares no negative enumerator, so under -enum min it is unsigned
and cannot reproduce the signed (ldrsh) reads. Whether monster_id should instead
declare a -1 sentinel -- the game does return -1 from GetScriptEntityMonsterId
-- is deliberately left open for upstream discussion rather than diverging from
pmdsky-debug here.

Extracting the function splits asm/main_02065BD8.s, which keeps the three
functions ahead of it; the trailing functions move verbatim into
asm/main_02065C84.s, the affected .inc files are regenerated, and main.lsf gains
src/main_02065C48.o and asm/main_02065C84.o.

No comments are added to any pmd-sky file; comments cannot be checked by the
matching build, so the code is left to stand on its own.

Based on the decomp-actor-globals branch, which introduces SPECIAL_ACTORS.

Authored by Claude (Opus 4.8) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:44:46 -07:00
AnonymousRandomPerson
86ec9772f2 Merge pull request #289 from SethBarberee/overlay_24
Some checks failed
build / build (push) Has been cancelled
Overlay 24 and 25
2026-08-12 18:34:57 -04:00
AnonymousRandomPerson
55678e48b2 Merge branch 'main' into overlay_24 2026-08-12 18:22:00 -04:00