Skip to content

decomp: define 64 KB more boot data in C - #279

Merged
mateuszklysz merged 25 commits into
mainfrom
decomp/boot-data
Nov 3, 2026
Merged

mateuszklysz merged 25 commits into
mainfrom
decomp/boot-data

Conversation

@mateuszklysz

@mateuszklysz mateuszklysz commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Before

Boot data in C: 188,645 of 699,136 B (27.0 %). Levels: 64.9 %. All data: 62.81 %.

After

Boot data in C: 252,700 B (36.1 %). Levels: 65.6 % (their copies of the new boot data are proven in exe_data.tsv). All data: 63.95 %.

Every object follows the data rules: the type and size come from the code that reads it (loop bounds, index checks, sizes the save tables give, struct sizes), never from the gap to the next label. The owner is its only reader, the routine that fills it, or its neighbours in link order. No raw byte arrays.

  • Save records (memcard_update_state, 63.5 KB): per-level map coverage pages, gold bolt flags, bolt source stats and respawn counts, each 20 level slots, as mc_leveldata stores them.
  • Saved globals (memcard_get_name, 3.7 KB): the memory card state, item tables (37 items each, as mc_gamedata saves them), level object states and per-level counters. These follow its save path strings in link order.
  • Strings and formats of about 90 SDK, audio, save, movie, rendering and UI units, as string literals.
  • Tables: the C locale lconv, DMA channel tables, MPEG extension parsers and PES skip table, normal_lut (cos/sin of k·2π/256), shrub render classes, level select texts, vu chain sizes, environment mesh tables, map icon lists and the map coverage bit-count table.
  • GS/GIF packets with named registers, and small data in the one unit that reaches it through $gp.
  • One name per global. pause_sound_volume is the field voice_pool.group_2_volume. discount_purchase_pricing and the heli-pack flag are now item_available[...]. D_0013DD70/D_0013DF38 are ammo_bought_stats/level_bolts in boot and level code.

Data that stays in asm: zero spans that no code reads or types (for example 128 KB near _gp), data read only by asm, shared structs with no owner yet (hero, voice_pool, disc_table), and kernel patch payloads.

Verification

  • make elf ends with PASS: reconstructed boot ELF matches retail (also after each batch)
  • make overlays ends with PASS (3014 functions, 24318 level data objects)
  • make check passes
  • scripts/check-cleanup.py: no new problems; scripts/test_public_tools.py OK; changed files formatted with scripts/format.py

printfloat, init_once, print_register_values_and_halt, dec_bs0, mpeg_error, snd_start_sound_system, snd_returns, snd_bank_load_from_ee_cb and snd_send_iop_command_no_wait now define the strings only they read; their RODATA/SDATA entries place them.
Each string is read by one unit only; the unit defines it before its functions (after them where retail placed it behind the unit's literals or in small data) and its RODATA/SDATA entry places it.
_localeconv_r (struct lconv and its two strings), _dtoa_r (Infinity/NaN/0) and pow5mult (5, 25, 125) each own the read-only data only they read.
… skip table in C

sceDmaReset (DmaChannelsUsed), sceDmaPutEnv (STS/STD/MFD channel codes), _extensionAndUserData (parser per extension id) and _PES_packet (optional-field skip bits) own their read-only tables. _ctype_ goes back to assembly: check-cleanup counts its 129 class-flag entries as raw bytes. Drops the symbol_addrs/undefined_syms lines of labels C now defines.
memcard_get_name writes the disc's region and product code into these six path strings, so it owns them; the other readers keep their externs.
…d counter and the T10K time zone in C

sceGsPutDrawEnv and sceGsExecLoadImage own their DMA timeout messages, video_dec_flush its sequence end code, memcard_restore_data its gp-relative differing-block counter (defined ahead of the function, which drops the _gp alias), sceScfGetTimeZone the T10K time zone.
sceCdRead's request is sized by the sizeof(*command) its RPC sends; scePad2Init and scePad2CheckDma own the flag and the two words only they touch.
snd_start_sound_system sets snd_batch_command_buffers, snd_batch_free_bytes, snd_stream_buffers and snd_batch_return_buffers, so it defines them (with the same initial values) ahead of the function, which reaches them gp-relative.
Format strings, glyph labels, layout values and colours that one unit
reads, placed through SDATA_OVERLAYS.
…ion units

Small-data values, format strings, the quad GIF tag and the nibble
bit-count table of the units that alone read them.
… world small data in C

map_icon_lists_by_level (19 levels, the reader bounds the index) and
menu_preview_slot_draw_enabled (24 slots, the loop bound) go to
DATA_OVERLAYS; scalars and the hud allocation tag one unit reads go to
SDATA_OVERLAYS, and update_hud takes its two neighbouring counters.
…UT in C

shrub_render_classes[64] goes to its registrar, whose range starts right
after it. The level map labels are the label field of a 19-entry level
select text table at D_001DDD40, which the menu widgets and a pause menu
label read too; it is defined as that table. normal_lut is the 256 cos/sin
pairs process_moby_anim_data copies (0x800 bytes) into scratchpad.
The per-level records between the help history and the memory card
buffers (0x141EA0-0x14EED0) join memcard_update_state's data range. Their
sizes come from the mc_leveldata save table (row sizes, 20 level records
as memcard_make_whole_save loops) and, for the respawn counters and the
16 mission counters at the end, from the level code that reads them. The
map coverage pages and gold bolt flags get names from their readers; the
Veldin 2 gold bolt row is now read through the gold bolt array.
pause_sound_volume was voice_pool.group_2_volume (loading_data_menu sets
the same field to the same 80 % value), so set_pause_sound_volume writes
the field and no longer defines a variable inside voice_pool.
item_available and items_acquired hold 37 items (mc_gamedata saves 0x25
bytes of each); the heli-pack, map id shift and vendor discount flags
that were declared as separate variables are elements of item_available.
memory_card_state (sizeof 0xF8) starts where memcard_get_name's save
directory and file paths end; in retail link order it belongs to the same
memory card unit.
alternate_item_available, item_unlocked, weapon_ammo_counts,
item_available and items_acquired follow memory_card_state in the same
unit, with the sizes the mc_gamedata save table gives (0x80, 0x20, 0x94,
0x25, 0x25).
…state

The save-table globals from D_0013D510 to the per-level hero counters
(0x13D510-0x13E028) continue memcard_get_name's range. Sizes: mc_gamedata
and mc_leveldata rows (0x50, 0x790 = 121 level object records, 0x14,
0x94 = 37 weapon counters, 4 bytes x 20 levels); the per-level coverage
best between them has one slot per level record. Weapon counter names
come from the level code that reads them.
Each of these scalars is reached with a gp-relative access in exactly one
unit (the defining file under SN's small-data rule) and with lui in the
others: the sound options volumes, the help history record count, the
pending space load flag, process_moby_anim_data's second argument, the
zone lookup tables of find_zone_at_point and the shrub instance pointer
next to initialize_level_render_state's other small data.
@mateuszklysz mateuszklysz added the enhancement New feature or request label Nov 3, 2026 — with Claude
@mateuszklysz mateuszklysz self-assigned this Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

Decompilation progress

C_EXACT Change Matched bytes Functions C_FUZZY
All code 100.00 % — 3,547,932 / 3,547,932 B (—) 4,244 / 4,244 (—) 100.00 %
Boot executable 100.00 % — 354,504 / 354,504 B (—) 1,230 / 1,230 (—) 100.00 %
Game 100.00 % — 264,872 / 264,872 B (—) 850 / 850 (—) 100.00 %
SDK 100.00 % — 89,632 / 89,632 B (—) 380 / 380 (—) 100.00 %
Level overlays 100.00 % — 3,193,428 / 3,193,428 B (—) 3,014 / 3,014 (—) 100.00 %
Shared level code 100.00 % — 1,130,492 / 1,130,492 B (—) 1,583 / 1,583 (—) 100.00 %
Per-level code 100.00 % — 2,062,936 / 2,062,936 B (—) 1,431 / 1,431 (—) 100.00 %
Data in C Change Matched bytes
All data 63.95 % +1.14 pp 7,950,692 / 12,433,372 B (+141,614 B)
Boot executable 36.14 % +9.16 pp 252,700 / 699,136 B (+64,055 B)
Level overlays 65.60 % +0.66 pp 7,697,992 / 11,734,236 B (+77,559 B)

No function changed its match state compared with main (cda8433).

Compared with main (cda8433). C_FUZZY uses main's last measurement.

…names from evidence

Give the zone tables their pointer types in one header, read level texts
through their fields, type strings as char, keep includes and externs at
the top, name the save paths, dtoa strings, buried bolt states and the O2
mask item, use LEVEL_RECORD_COUNT and LEVEL_COUNT for per-level sizes, and
write memory_card_state with designated fields.
@mateuszklysz
mateuszklysz merged commit 6513252 into main Nov 3, 2026
4 checks passed
@mateuszklysz
mateuszklysz deleted the decomp/boot-data branch November 3, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant