Skip to content

Decompile two accessor clusters and the .col2 loader - #18

Open
Moss255 wants to merge 5 commits into
DQIX:zone3d-experimentalfrom
Moss255:zone-struct-accessors
Open

Decompile two accessor clusters and the .col2 loader#18
Moss255 wants to merge 5 commits into
DQIX:zone3d-experimentalfrom
Moss255:zone-struct-accessors

Conversation

@Moss255

@Moss255 Moss255 commented Sep 7, 2026

Copy link
Copy Markdown

Rebased onto zone3d-experimental. Stacked on #17 — the first commit is that PR; review it there.

dsd check modules and dsd check symbols both pass on every commit.

Commits

functions
c7c702c 5 zone predicates, ARC member path, scene lookup — #17
49f4e29 17 BattleStruct accessors at 0x02011518
6dc87eb 21 combatant status accessors at 0x02088840
0b87bd1 15 ... extended to 0x02088bf0
31b18f4 17 ... extended to 0x02088dd8
c5822b6 2 the .col2 collision mesh loader

Structs

The accessor clusters are mostly one-liners; the work was the structs their offsets define.

  • BattleStruct now reaches 0x63e0. It was modelled to 0x3AC with a TODO: validate — that TODO is untouched, and the new padding is expressed relative to combatantList, so if that array's size is ever corrected the padding must move with it. There's a comment saying so.
  • CombatantStruct gains statusFlags (0x14), stateFlags (0x18), and statusDurations/statusCounters — two 0x23-byte arrays lying back to back at 0x5f and 0x82. All were previously inside char unk[0x132]; the total size is unchanged.

Both are shared headers, so I re-verified every existing file that uses them still matches.

Changes made for this branch

  • 0x02011644 / 0x02011650 are left alone — GrottoNameDataFile.cpp already names them GetTreasureMapLanguageData / SetTreasureMapLanguageDataPtr. Its jpn offset (0x6180 against usa's 0x63e0) shows the struct differs between versions, so the offset column in BattleList.h is documented as usa-only.
  • func_02011584 becomes GetWorldData, after the name Zone3D and ZoneFeatures already give its result. Both now call it by name and drop their jpn defines, so the jpn symbol is renamed to match (0x020112f4, size 0xc).

Notable findings

  • In .col2, the header word at +0x04 is a left shift applied to the bounding box, and the cell count is gridX * gridZ + gridZ / 2. The word at +0x00 is tested, so it is a version rather than a constant.

Caveats

  • USA only for the new source files. GetWorldData and the zone predicates are renamed in the jpn symbol table because files delinked for jpn now reference them; those addresses were matched by size sequence, but I have no JPN ROM to verify a build against. Happy to add full jpn entries if someone can check them.
  • Field and function names carry offsets where the meaning isn't established, and anything inferred is marked INFERRED with the evidence.
  • I did not run the clang-format pre-commit hook: .clang-format is BasedOnStyle: LLVM with IndentExternBlock: NoIndent, which produces unindented extern "C" { and void *x, while every committed file uses indented blocks and void* x. Running it rewrote 139 lines of a file where my change is 8. New code follows the surrounding style instead. Probably worth a separate issue — the config and the codebase disagree.

Seventeen getters, setters and tests on one struct. Their offsets establish
BattleStruct out to 0x63e0, well past the part previously modelled.

0x02011644 and 0x02011650 are left alone - GrottoNameDataFile.cpp already
names them, and its jpn offset shows the struct differs between versions,
so the offsets in the header hold for usa only.

func_02011584 becomes GetWorldData, after the name Zone3D and ZoneFeatures
already give its result. Both now call it and drop their jpn defines, so
the jpn symbol is renamed to match.
Seven status effects, each with a test, an apply and a clear. Their offsets
name statusFlags and the two byte arrays behind it in CombatantStruct.
Fifteen more: three status triples, and two mutually exclusive states that
each clear the other. Adds stateFlags at 0x18 to CombatantStruct.
Seventeen more, including four states that clear their opposite by tail
calling it. statusDurations and statusCounters are two 0x23-byte arrays.
Resolves the file's offsets, scales the bounding box by the header's shift,
and derives the cell count as gridX * gridZ + gridZ / 2.
@Moss255
Moss255 changed the base branch from main to zone3d-experimental September 8, 2026 05:24
@Moss255
Moss255 force-pushed the zone-struct-accessors branch from c2f1025 to c5822b6 Compare September 8, 2026 05:24
@Moss255 Moss255 changed the title Decompile map loading, scene overlays, two accessor clusters and the .col2 loader Decompile two accessor clusters and the .col2 loader Sep 8, 2026
@Moss255
Moss255 changed the base branch from zone3d-experimental to main September 8, 2026 05:25
@Moss255
Moss255 changed the base branch from main to zone3d-experimental September 8, 2026 05:25
@eakeys

eakeys commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

The collision mesh identification is a good find, as is the mapping out of BattleStruct. Some notational things:

  • boundsMin and boundsMax might be Vector3i / Vector3fix instead of arrays. If there are any calls to Vector3i::operator= involving these, that would confirm it. Note fix32_t is just a typedef for int but it's used for fixed point numbers and so it crops up a bunch with 3d rendering (notably any object positions tend to be fixed point numbers).
  • The codebase is C++ so we should make it reflect that - e.g. void InitCollisionMesh(CollisionMesh*, void*, const void*) is almost certainly void CollisionMesh::Initialize(void*, const void*). You'll have to deal with name mangling for this (put the mangled name in symbols.txt) - there's a mangler script in tools you can use to get the mangled name.

As for BattleStruct and CombatantStatus functions, there's generally not much use in decomping simple functions that handle individual variables until we know what they're for. Could I ask you to try and identify these? If you're using LLM tools for this, this part is best done by hand - set some breakpoints in DeSmuME / open the memory viewer to the relevant address, get into a battle and poison/confuse/sleep an enemy (or fight an enemy that can do this to you) and see what fires/which bits/bytes change

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants