fix(erf): align restype numbers with upstream neverwinter.nim #69

Merged
archvillainette merged 2 commits from fix/erf-restype-upstream-64 into main 2026-07-28 22:18:04 +00:00
Owner

Closes #64.

Five entries in internal/erf/erf.go's restype table disagreed with upstream neverwinter.nim (neverwinter/restype.nim). HAKResourceTypeForExtension packs arbitrary source files, so a .jpg packed into a HAK today is written as restype 2076, which the game reads as tml.

ext was now
gff 2039 (upstream bte) 2037
ltr 2067 (upstream bak) 2036
jpg 2076 (upstream tml) 2081
dfa 2045, name typo dft, 2045 (number was already right)
mdb 2070 (upstream xbc) removed

Upstream registers no restype for mdb, so there is no correct number to give it, and leaving it squatting on xbc silently mislabels the resource. It is dropped from AssetExtensions too, which turns a .mdb source into a loud "unsupported extension" build error instead of a corrupt HAK entry. This is the one judgment call the issue left open — if mdb should instead stay as a deliberate local addition like lyt/vis/gr2, say so and I will move it to a free number in the 0x0BB8+ local range.

No asset in the current corpus uses any of these paths, so nothing in shipped content changes.

The six local additions upstream does not have (lyt 3000, vis 3001, mdx 3008, wlk 3020, xml 3021, gr2 4003) are left alone and now carry a comment saying they are deliberate.

Root cause

The init() consistency guard only panicked when a number was missing from the reverse map. Its if canonicalExt == ext { continue } branch did nothing when the two maps disagreed, so a mismatch was never caught. The guard now panics on disagreement and also checks the reverse direction.

Tests

  • TestRestypeTableMatchesUpstream pins every number shared with upstream, and asserts the four numbers upstream reserves for other extensions (2039 bte, 2067 bak, 2070 xbc, 2076 tml) stay unclaimed.
  • TestRestypeTablesAreMutualInverses is the check init() is meant to enforce.

make check passes.

Closes #64. Five entries in `internal/erf/erf.go`'s restype table disagreed with upstream neverwinter.nim (`neverwinter/restype.nim`). `HAKResourceTypeForExtension` packs arbitrary source files, so a `.jpg` packed into a HAK today is written as restype 2076, which the game reads as `tml`. | ext | was | now | |-----|-----|-----| | gff | 2039 (upstream `bte`) | 2037 | | ltr | 2067 (upstream `bak`) | 2036 | | jpg | 2076 (upstream `tml`) | 2081 | | dfa | 2045, name typo | `dft`, 2045 (number was already right) | | mdb | 2070 (upstream `xbc`) | removed | Upstream registers no restype for `mdb`, so there is no correct number to give it, and leaving it squatting on `xbc` silently mislabels the resource. It is dropped from `AssetExtensions` too, which turns a `.mdb` source into a loud "unsupported extension" build error instead of a corrupt HAK entry. **This is the one judgment call the issue left open** — if `mdb` should instead stay as a deliberate local addition like `lyt`/`vis`/`gr2`, say so and I will move it to a free number in the 0x0BB8+ local range. No asset in the current corpus uses any of these paths, so nothing in shipped content changes. The six local additions upstream does not have (`lyt` 3000, `vis` 3001, `mdx` 3008, `wlk` 3020, `xml` 3021, `gr2` 4003) are left alone and now carry a comment saying they are deliberate. ## Root cause The `init()` consistency guard only panicked when a number was missing from the reverse map. Its `if canonicalExt == ext { continue }` branch did nothing when the two maps disagreed, so a mismatch was never caught. The guard now panics on disagreement and also checks the reverse direction. ## Tests - `TestRestypeTableMatchesUpstream` pins every number shared with upstream, and asserts the four numbers upstream reserves for other extensions (2039 `bte`, 2067 `bak`, 2070 `xbc`, 2076 `tml`) stay unclaimed. - `TestRestypeTablesAreMutualInverses` is the check `init()` is meant to enforce. `make check` passes.
archvillainette added 1 commit 2026-07-28 22:03:18 +00:00
Five entries in the restype table disagreed with upstream
neverwinter.nim's restype.nim, so HAKs written from those source
extensions were labelled with a type the game reads as something else:

  gff  0x07F7 (2039, upstream bte) -> 0x07F5 (2037)
  ltr  0x0813 (2067, upstream bak) -> 0x07F4 (2036)
  jpg  0x081C (2076, upstream tml) -> 0x0821 (2081)
  dfa                              -> dft (0x07FD/2045 was already right,
                                      only the name was a typo)
  mdb  0x0816 (2070, upstream xbc) -> removed

Upstream registers no restype for mdb, so there is no correct number to
give it; keeping it squatting on xbc silently mislabels the resource.
It is dropped from AssetExtensions too, which turns .mdb into a loud
"unsupported extension" build error instead of a corrupt HAK entry. No
asset in the current corpus uses any of these paths.

Root cause of the miss: the init() consistency guard only panicked when
a number was missing from the reverse map. Its `if canonicalExt == ext
{ continue }` branch did nothing when the two maps disagreed, so a
mismatch was never caught. The guard now panics on disagreement and
also checks the reverse direction.

Adds a conformance test pinning the shared numbers against upstream,
including the four numbers upstream reserves for other extensions, and
a test that the two maps are mutual inverses.

Refs ShadowsOverWestgate/sow-tools#64

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
archvillainette added 1 commit 2026-07-28 22:10:57 +00:00
Follow-up to the mdb removal in the previous commit. Three of the six
extensions #64 listed as deliberate local additions are NWN2 formats,
not NWN:EE ones, and belong in an NWN:EE tool no more than mdb did.

Checked against xoreos (src/aurora/types.h), which covers every Aurora
game and so distinguishes the games' tables:

  gr2  4003, sits in the NWN2 block (MDB2 4000, MDA2 4001, SPT2 4002,
       GR2 4003, PWC 4008). Granny is NWN2; NWN:EE does not use it.
  wlk  xoreos numbers it 20004 and xml 20003, both above
  xml  kFileTypeMAXArchive in the section headed "Entries for files not
       found in archives with numerical type IDs / Found in NWN2's ZIP
       files". Neither has an archive restype in any Aurora game, so
       Crucible's 0x0BCC and 0x0BCD were invented outright, in a band
       Aurora does use elsewhere (3022 FSM, 3023 ART).

NWN:EE covers those jobs with formats already in the table: wok, pwk
and dwk for walkmeshes, and EE's own UI XML loads from ui/ovr rather
than from a HAK by restype, which is consistent with upstream
neverwinter.nim registering no xml number.

The remaining three local additions stay. lyt 3000, vis 3001 and mdx
3008 are real Aurora archive types that NWN1 ships in its own
data/*.bif; xoreos gives the same numbers. Upstream simply does not
list them.

No asset in the corpus uses gr2, wlk or xml, so nothing stops building.
Also drops them from AssetExtensions, and records the rule in a comment
on the table plus a test, so the next NWN2 format gets caught.

Refs ShadowsOverWestgate/sow-tools#64

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Author
Owner

Second commit pushed: gr2, wlk and xml removed too.

#64 listed six extensions as deliberate local additions rather than bugs. Three of them are NWN2 formats, same class of problem as mdb. Checked against xoreos src/aurora/types.h, which covers every Aurora game and so keeps the games' tables apart:

ext Crucible had xoreos verdict
lyt 3000 3000, room layout real, NWN1/EE — kept
vis 3001 3001, room visibilities real, NWN1/EE — kept
mdx 3008 3008, model mesh data real, NWN1/EE — kept
gr2 4003 4003, in the NWN2 block (MDB2 4000, MDA2 4001, SPT2 4002, GR2 4003, PWC 4008) NWN2 (Granny) — removed
wlk 3020 20004, "Found in NWN2's ZIP files" NWN2, number was invented — removed
xml 3021 20003, same NWN2 ZIP section NWN2, number was invented — removed

The 20000+ range sits above kFileTypeMAXArchive, under the heading "Entries for files not found in archives with numerical type IDs". So wlk and xml have no archive restype in any Aurora game — 0x0BCC and 0x0BCD were made up, and they sit in a band Aurora does use elsewhere (3022 FSM, 3023 ART).

NWN:EE already covers those jobs with formats in the table: wok/pwk/dwk for walkmeshes, and EE's own UI XML loads from ui/ovr rather than from a HAK by restype — consistent with upstream neverwinter.nim registering no xml number.

lyt/vis/mdx stay: real Aurora archive types that NWN1 ships in its own data/*.bif, with xoreos giving the same numbers. Upstream just does not list them.

Corroboration for mdb while I was here: the only .mdb files anywhere near this project are nine in _contentpool/!GunsPackage/, a downloaded NWN2 package — the files begin with the literal magic bytes NWN2. Not shipped corpus, and unusable in NWN:EE whatever restype we give them.

Added TestNWN2FormatsAreNotRegistered plus a comment on the table stating the rule, so the next NWN2 format gets caught at test time instead of in a HAK. make check passes.

Second commit pushed: `gr2`, `wlk` and `xml` removed too. #64 listed six extensions as deliberate local additions rather than bugs. Three of them are NWN2 formats, same class of problem as `mdb`. Checked against [xoreos `src/aurora/types.h`](https://github.com/xoreos/xoreos/blob/master/src/aurora/types.h), which covers every Aurora game and so keeps the games' tables apart: | ext | Crucible had | xoreos | verdict | |-----|--------------|--------|---------| | lyt | 3000 | 3000, room layout | real, NWN1/EE — **kept** | | vis | 3001 | 3001, room visibilities | real, NWN1/EE — **kept** | | mdx | 3008 | 3008, model mesh data | real, NWN1/EE — **kept** | | gr2 | 4003 | 4003, in the NWN2 block (MDB2 4000, MDA2 4001, SPT2 4002, GR2 4003, PWC 4008) | NWN2 (Granny) — removed | | wlk | 3020 | **20004**, "Found in NWN2's ZIP files" | NWN2, number was invented — removed | | xml | 3021 | **20003**, same NWN2 ZIP section | NWN2, number was invented — removed | The 20000+ range sits above `kFileTypeMAXArchive`, under the heading "Entries for files not found in archives with numerical type IDs". So `wlk` and `xml` have no archive restype in *any* Aurora game — 0x0BCC and 0x0BCD were made up, and they sit in a band Aurora does use elsewhere (3022 FSM, 3023 ART). NWN:EE already covers those jobs with formats in the table: `wok`/`pwk`/`dwk` for walkmeshes, and EE's own UI XML loads from `ui`/`ovr` rather than from a HAK by restype — consistent with upstream neverwinter.nim registering no `xml` number. `lyt`/`vis`/`mdx` stay: real Aurora archive types that NWN1 ships in its own `data/*.bif`, with xoreos giving the same numbers. Upstream just does not list them. Corroboration for `mdb` while I was here: the only `.mdb` files anywhere near this project are nine in `_contentpool/!GunsPackage/`, a downloaded **NWN2** package — the files begin with the literal magic bytes `NWN2`. Not shipped corpus, and unusable in NWN:EE whatever restype we give them. Added `TestNWN2FormatsAreNotRegistered` plus a comment on the table stating the rule, so the next NWN2 format gets caught at test time instead of in a HAK. `make check` passes.
archvillainette scheduled this pull request to auto merge when all checks succeed 2026-07-28 22:17:24 +00:00
xtul approved these changes 2026-07-28 22:18:01 +00:00
archvillainette merged commit 2f4685e470 into main 2026-07-28 22:18:04 +00:00
archvillainette deleted branch fix/erf-restype-upstream-64 2026-07-28 22:18:04 +00:00
Sign in to join this conversation.