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>
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>
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>
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>
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>
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>
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>
Decompile UFixedPoint64CmpLt (0x02001A30) from asm. It compares two 64-bit
values held as high/low word pairs and returns whether the first is less than
the second.
asm/main_02001A30.s held only this function, so the file and its .inc are
removed rather than split, the function merges into the adjacent
src/main_020018D0.c, and main.lsf drops asm/main_02001A30.o.
Three details of the interface are read off the target rather than assumed,
each tested with the others held fixed. The four words arrive as separate
parameters: a 2-word struct passed by value makes the compiler push all four
argument registers and reload them, which the target does not do. Those
parameters are unsigned: the comparisons select movlo/movhi, where signed high
words give movlt/movgt. The return is word-sized: a u8-width return such as
bool8 appends and r0, r0, #0xff, which the target does not have. BOOL is used
rather than int because it is the tree's word-sized boolean, and it produces
the same bytes.
src/main_02001BB4.c declared this function from its call site, before it was
decompiled. That declaration is replaced by an include of the header. It was
never visible to the compiler at the same time as the definition, so the two
disagreeing prototypes could not be caught by a build; its parameter types were
signed, which does not describe this function.
No comments are added to any pmd-sky file.
Authored by Claude (Opus 4.8, amended by Opus 5) 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>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decompile eight ground-mode setters from asm into src/main_02065B3C.c:
SetActorTalkMainAndActorTalkSub, SetActorTalkMain, SetActorTalkSub,
SetActorEventMain, SetRandomRequestNpcs1And2, SetRandomRequestNpc03KindVar,
SetAllEventNpcs and SetNewFriendActor.
Identify the global they write, _020B0B08, as a 28-byte table of slots keyed by
the special members of enum script_entity_id (ENTITY_TALK_MAIN 46 through
ENTITY_NPC_DEMO_PARTNER 59). Every slot offset is corroborated by both a writer
above and the reader that resolves it at the matching entity id: sub_0206549C,
reached via GetScriptEntityMonsterId, and sub_02065050, reached via
GetActorMatchingStorageId. Name the type struct special_actors and relabel the
symbol _020B0B08 -> SPECIAL_ACTORS at its definition in
asm/main_rodata_020A2808.s and at every reference.
Extracting the block splits asm/main_0205EDA4.s: the trailing functions move
verbatim into asm/main_02065BD8.s, the affected .inc files are regenerated, and
main.lsf gains the two new objects.
Add enum script_entity_id to include/enums.h, taken from pmdsky-debug, since
struct special_actors refers to it.
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.
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>
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>