Skip to content

Select NRR layouts from header bytes and report unsupported CRA containers - #1102

Closed
oliverames wants to merge 1 commit into
seedds:mainfrom
oliverames:nro-nrr-switch2
Closed

oliverames wants to merge 1 commit into
seedds:mainfrom
oliverames:nro-nrr-switch2

Conversation

@oliverames

@oliverames oliverames commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

What and why

nro_collect.py could read signature bytes as hash-table metadata in Mario Tennis Fever's newer NRR layout, producing a huge count and an invalid scan. The parser now selects the layout from the header rather than taking the first plausible table.

The SDK accessor checks NRR0 and the byte tuple at offsets (0x06, 0x05, 0x20). Tuple (0, 0, 0) selects metadata at 0x330; (1, 1, 1) selects 0x4b0. Other tuples stop with an error naming the header. The collector reads the table offset/count at metadata +0x10/+0x14 and checks the declared file size, minimum header length and table bounds. It retains the CRA. container diagnostic, which identifies a reader gap without claiming the archive is supported.

The selector is traced to Fever SDK build 32a9ac1480a6f0e9e2e8a813ae9cd73181897bb0, accessor +0x158660; caller +0x156458 reads the table offset from metadata +0x10. SDK SHA-256: e8a2c5852508f330663d8ad639d4c20609e636c1bc6d5fcf7b01cd4bd3e0428f. Base and update SDK copies are identical, so this is one independent implementation. The newer count field is supported by the legacy structure and two Fever NRR samples, not independently proven by that accessor trace. Field names, additional layouts and signature authentication are outside this change.

@seedds, this addresses your header-selection request. It is part of the upstream reconciliation for Switch 2 games running on iPhone, alongside the DKB work on iPhone 17 Pro Max. This contribution changes extraction only; it does not claim a DKB rendering, pacing or fur-shimmer improvement. Fresh hosted CI and maintainer review remain pending.

Updated to main a70f071. The maintainer landed the equivalent mesh-stage fix in e556f7a, so #1154 is closed as superseded and no longer appears in this diff. The NRR diff is again limited to its parser, tests and services reference. Updated branch: 19 NRO tests, 11 documentation tests and full Python ratchet pass. Hosted CI remains pending.

Checked

  • Tests: all 19 NRO tests and 11 documentation tests pass on the updated branch. The six new header tests include conflicting plausible tables, unknown/mixed tuples, truncated headers, swapped fields and invalid bounds. Pinned Python ratchet passes: 391 existing findings, zero new or stale. Ruff and whitespace checks pass. Independent review found no actionable issues and reran the six header tests successfully. Reproduce with python -m unittest discover -s dm/tools/tests -p 'test_nro_*.py', python -m unittest discover -s dm/tools/tests -p test_docs.py, and python dm/tools/lint_python.py using the repository's documented Python dependencies. Hosted CI remains pending.
  • Games run, with title ID and version: none run. Both existing Fever NRR samples return their expected one-hash sets; no archives or keys were read during this verification. The game's title ID and version are not stated.
  • Devices and OS: not stated.
  • scripts/regress.py line: not stated. The change is to dm/tools/nro_collect.py only.
  • Measurements before and after: not applicable (not a speed, memory or power change).

Front ends

None: no front end has to act on this.

Docs

  • The finding is in its game note or its docs/reference/<subsystem>.md entry, and python3 dm/tools/docs_symptoms.py was run. Note: the diff changes only dm/tools/nro_collect.py and dm/tools/tests/test_nro_collect.py. The description says the diff includes a services reference entry, but none is in the current file list, so this cannot be ticked.
  • A chain tool changed: dmsh_chain.py --header was run and dm/gfx/nvn/dmsh_chain.h is part of the change. Not applicable: no chain tool changed.
  • No game files, keys or data extracted from a game in the change or the attachments (verified from the file list: one tool and one test file; the description says no archives or keys were read).

@github-actions github-actions Bot added the tools Tools and scripts (dm/tools, scripts) label Nov 3, 2026
@seedds

seedds commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Thanks. The [RED] line for a CRA. container looks good. For the hash table I'd rather not pick a layout by which (offset, count) pair looks plausible: a Switch 1 file whose bytes at 0x4c0 happen to look plausible would be read wrong without a word. Could the parse read the layout from the NRR header instead (its magic and version or size field, whatever tells the Switch 2 revision apart), and stop with an error naming the header when it's neither? I'd land that.

@github-actions github-actions Bot added the docs Documentation label Nov 4, 2026
@oliverames oliverames changed the title nro_collect: the Switch 2 .nrr's hash-table layout, and a RED line for the containers no reader covers Select NRR layouts from header bytes and report unsupported CRA containers Nov 4, 2026
@oliverames

Copy link
Copy Markdown
Contributor Author

@seedds, replaced the plausible-table search with deterministic header selection. In Fever SDK build 32a9ac1480a6f0e9e2e8a813ae9cd73181897bb0, the accessor at +0x158660 checks NRR0 and bytes (0x06, 0x05, 0x20): all zero selects metadata +0x330, all one selects +0x4b0, and other tuples reject. The caller at +0x156458 reads the table offset from metadata +0x10. I independently checked those instructions after the initial investigation.

The regression constructs a valid-looking table at both locations and verifies that the header-selected table wins. Unknown tuples and malformed/truncated bounds fail with a named header error. Both Fever NRR samples and the 17 NRO tests pass; the Python ratchet and 11 docs tests also pass. The description and services reference document the evidence and its limits, including the newer count field's sample-based support. No claim that the whole word at +4 is a version remains.

@oliverames

Copy link
Copy Markdown
Contributor Author

Closing: main's nro_collect.py now selects the NRR table offset by header version (0x340, or 0x4c0 for version 1), which covers the Mario Tennis Fever layout this PR fixed. The extra header validation and unsupported-CRA error here are not needed for any game we have, so I'm dropping them rather than asking for review.

@oliverames oliverames closed this Nov 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) tools Tools and scripts (dm/tools, scripts)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants