Resolved the last tracked type-2 "single truth" decision using the pokered disassembly (now available under assets/references/pokered/), implemented the fix, rebuilt, and relaunched.
Decision (from the disassembly)
The game's canonical single-type storage is duplicate-of-type1, not 0xFF:
- data/pokemon/base_stats/*.asm – every single-type species stores db TYPE, TYPE (Charmander db FIRE, FIRE); dual types store two distinct values (Bulbasaur db GRASS, POISON).
- constants/type_constants.asm – valid type ids run $00(NORMAL)..$1A(DRAGON); there is no 0xFF type. 0xFF only ever appears in hacked/glitch saves.
- engine/pokemon/add_mon.asm .copyMonTypesAndMoves – party/box mon type1 & type2 are copied verbatim from the base-stats header (wMonHTypes), so a caught single-type mon always has type2 == type1.
What changed (code)
pokemonbox.cpp already wrote duplicate-of-type1 for a non-explicit single type (save()'s type2 == 0xFF -> setByte(type1) branch), so on-disk output is unchanged for normal saves and byte fidelity is untouched – a literally-loaded 0xFF still round-trips as 0xFF via type2Explicit. The real fix: every editor (re)generation path now clears type2Explicit so a generated/corrected single type serialises as the canonical duplicate, never a stale 0xFF carried over from a hacked save:
- randomize(), correctTypes(), and update()'s resetType block each add type2Explicit = false; type2ExplicitChanged();.
- load() / save() / isCorrected() comments finalized to document the resolved truth; the isCorrected() "TRACKED TEMPORARY EXCEPTION / dirty patch" framing is gone – tolerating both 0xFF and the duplicate is now the intended behaviour (both are valid single-type representations that serialise to identical bytes).
tst_pokemonbox.cpp – new box_singleTypeCanonicalForm(): Charmander is the 0xFF sentinel with !type2Explicit; correctTypes() and update(resetType) both clear a stale explicit flag.
Notes updated
Verify
- Kit build (projects/build/...Qt_6_11...Debug) green – savefile.dll relinked, exe rebuilt (VERSION reconfigure), [100%] Built target PokeredSaveEditor, exit 0.
- tst_pokemonbox (offscreen): 25 passed, 0 failed incl. the new box_singleTypeCanonicalForm.
- App rebuilt + relaunched for in-app review.
Follow-ups / flagged
- copyFrom() stat-copy typo (pokemonbox.cpp ~line 1884): spdExp = pkmn->spExp; copies Special into Speed's stat-exp (should be pkmn->spdExp). Pre-existing, unrelated to type-2; left for Twilight's call as a separate fix rather than folding an unrelated change into this commit.
- Still open from before (docs-only 2026-07-06 work rides to main with the next release): the kit-side s14 file-load crash fix compiles clean in this build but its runtime scenarios were not exercised this session; the treeview-not-API-only + hidden-search doc-theme items remain.