Skip to content

v0.6 - Pokemon creator (XD + Colosseum + Gen 3), EV legality, cold-boot scan fix

Choose a tag to compare

@logdog2325 logdog2325 released this 27 May 19:58
· 1 commit to main since this release

⚠️ Back up your saves first

v0.6 carries forward v0.5's memory-card writeback. Always back up your .gci files with GCMM before using "Save to memory card." The self-verify step still runs, but there's no undo if a bug slips past.

What's new since v0.5

🆕 Pokemon creator for empty slots — XD and Colosseum

The Phase 1 creator (added late in v0.5 for GBA Gen 3) now covers Pokemon XD and Pokemon Colosseum too. Press A on any empty box or party slot → confirm → pick species → drops into the regular editor for tweaks.

  • pb_xk3_create_default builds a full 196-byte XK3 record (XD)
  • pb_ck3_create_default builds a full 312-byte CK3 record (Colosseum)
  • Defaults: level 5, Tackle, IVs/EVs 0, friendship 70, Poké Ball, no shadow status, English NTSC-U
  • Inherits your save's OT name / TID / SID so the mon shows as yours
  • Auto-nicknames with the species name (matches what the games do for freshly caught mons)
  • National-dex → XD/Colosseum internal species ID via PKHeX's Table3NationalToInternal (the games share this table)
  • Validity bits set correctly (XK3 0x1D XDPKMFLAGS, CK3 0xCD) so XD/Colo won't reject the record
  • ShadowID = 0, so no Purification table entanglement

XD and Colosseum load these without complaint. PKHeX will flag them as illegal (no encounter generator match), and they'll be quarantined by Pokemon HOME. Phase 2 ports PKHeX's MethodH/E PID-IV generators for HOME-eligible creation.

🆕 EV editor enforces Gen 3 legal caps

The EV editor now respects what's actually legal in Gen 3:

  • Per-stat max: 252 (anything 253-255 is wasted — stats only update on multiples of 4)
  • Total max: 510 across all six stats
  • Increments that would overflow the total are clamped to whatever room is left
  • Running Total X / 510 and Remaining Y displayed prominently
  • "AT TOTAL LIMIT" flash when capped, "Near limit" warning within 8 of cap
  • Progress bars now scale to 252 (so maxing a stat fills the bar)
  • A button: max selected stat (252 if room, otherwise whatever's left)
  • START: zero selected stat (frees up total)

You can still hit 510 exactly via legal spreads like 252/252/6 or 252/252/4 = 508.

🐛 Cold-boot memory-card scan fix (the big one)

v0.5 had a tester-reported bug where the first memcard scan after a fresh power-on would freeze on slot A. Running GCMM first to back up a save unfroze it, but that was an awful workaround.

Root cause, diagnosed via real-hardware testing:

  • libaesnd init on cold-boot EXI state breaks subsequent CARD_Mount. Even though the research says libaesnd should coexist with libcard, on a fresh EXI bus the DSP init disturbs the controller in a way CARD_Mount can't recover from.
  • After GCMM has touched the bus, EXI is "warmed up" enough that AESND init no longer breaks it.

Fix: defer pb_audio_init until after the user's first menu action. The flow is:

  1. Boot → menu appears silent
  2. User picks first action (memcard scan, demo load, etc.)
  3. Action completes → return to main menu → AESND inits → music starts on the first playlist track
  4. Subsequent memcard ops pause/resume music via the existing AESND_Pause path

You lose music for ~3 seconds at boot. You gain a memcard scan that works on cold power-on without GCMM warmup.

Also added (defense in depth, didn't hurt to have):

  • CARD_Probe before CARD_Mount — skip empty slots immediately instead of hanging
  • EXI_Unlock drain + EXI_Detach before each memcard op — clears any libfat-held lock state on the shared EXI channel
  • 500 ms VSync settle between the unlock and the mount — EXI debounce
  • Per-slot UI feedback — "Scanning slot A..." / "Scanning slot B..." renders before each attempt, so freezes show exactly which slot is the problem (helped pin down the root cause)

🐛 Explicit Swiss reload-stub jump on exit

Picking "Exit" from the main menu was returning to the GameCube IPL (boot logo) instead of Swiss. Plain return 0 from main() only invokes the STUBHAXX magic check in modern libogc (~2021+); older devkitPPC builds just SYS_HOTRESET to IPL.

Fix: check for the reload stub at 0x80001800 (STUBHAXX magic at 0x80001804..0x8000180B, or legacy PSO-SDload mfmsr r3 instruction at 0x80001800) and jump explicitly. Falls back to SYS_HOTRESET if no stub present.

The confirm screen now reads stub state and shows "Return to Swiss?" vs "Return to GameCube IPL?" accurately.

Status

Format Read Edit Write Create
Gen 3 GBA (Ruby/Sapphire/Emerald/FireRed/LeafGreen)
pokeemerald-expansion ROM hacks (Seaglass, Lazarus, etc.)
Pokémon XD: Gale of Darkness (SD + memcard) ✅ (Phase 1)
Pokémon Colosseum (SD + memcard) ✅ (Phase 1)
GBA cart over link cable 🟡 ported, blocked by SI-mod hardware n/a n/a n/a
Pokémon Box: Ruby & Sapphire 🟡 memcard detection only

Install

Drop the attached pokebridge.dol onto your Swiss SD at sd:/apps/pokebridge/boot.dol and boot via Swiss. Works on real GameCube hardware and Wii in GameCube mode (Nintendont, Swiss-r, disc-channel backcompat).

Credits

PKHeX (XK3/CK3 byte layouts + species mapping table), Suloku's GCMM (the EXI_Unlock + Detach + retry recipe that fixed cold-boot scan), Extrems (libasnd-vs-libcard diagnosis), TuxSH's PkmGCTools, and devkitPro.