Skip to content

Add EMS NDS Adapter+ support for DS/3DS save backup#4

Draft
pathawks wants to merge 5 commits intomainfrom
ems-nds-adapter
Draft

Add EMS NDS Adapter+ support for DS/3DS save backup#4
pathawks wants to merge 5 commits intomainfrom
ems-nds-adapter

Conversation

@pathawks
Copy link
Copy Markdown
Owner

@pathawks pathawks commented Apr 12, 2026

Summary

  • WebUSB driver for the EMS NDS Adapter+, sharing a new nds-header.ts module so future NDS save devices can reuse the same header parsing, dual-CRC validation, region decoding, and No-Intro lookup
  • DS/3DS save backup and restore (no ROM dumping — hardware limit)
  • Cart identity is fingerprinted on first read and re-verified after every dump/write, so a mid-operation cart swap fails closed instead of writing the wrong save
  • NDSSaveSystemHandler.validateDump flags duplicate-half saves, all-0x00 / all-0xFF dumps, and chip-size mismatches at the system layer rather than per driver
  • Save read switched to the default bulk IN endpoint — the original EP3 selection was based on the ndsplus reference but every observed transfer returns on EP1, and forcing EP3 silently hung on some firmware revisions
  • Save-read timeout tuned to 3 s with slow-chunk logging; in-driver recovery paths (orphan-transferIn rescue, stall counter, pre-dump GET_STATUS verify) removed after they were observed corrupting saves on real carts
  • Firmware-version word decodes the recovery bit, so the scanner can distinguish a degraded adapter from a missing one
  • Document the EMS adapter's undocumented protocol surface (UNDOCUMENTED_CMD, UPGRADE) for future reference; not invoked by the driver
  • Scanner UI requires physical disconnect between carts (no hot-swap), surfaces cart info / save size / validation warnings, and exposes the active driver on window.__nabuDriver for devtools
  • Generic CartridgeInfo<M> plus NDSCartMeta; nointro filters prerelease tags and adds an nds_save system mapping; USB transport exposes getDevice() for sibling-protocol probes; formatBytes learns MB
  • THIRD-PARTY-LICENSES: add powerslaves (kitlith, MIT) attribution and refine the existing ndstool (devkitPro) note

Test plan

  • Connect EMS NDS Adapter+ and verify device detection
  • Insert a DS cartridge and verify auto-detection (title, game code, region, save type, save size)
  • Dump a save file and verify the output size matches the chip
  • After a dump, swap to a different cart by physically disconnecting the adapter and reconnecting — confirm the scanner re-detects the new cart cleanly
  • Confirm an in-place cart swap (without disconnecting the adapter) is refused / surfaces a cart-identity-changed error rather than dumping the new cart's save under the old cart's name
  • Trigger a save-validation warning by dumping a known-empty cart (all-0xFF) and confirm the UI surfaces the warning
  • Test auto-reconnect after unplugging and re-plugging the adapter mid-session

WebUSB driver for the EMS NDS Adapter+, which reads and writes save
data from DS and 3DS cartridges. Follows the scanner pattern (like
Amiibo) with continuous polling for cartridge insertion/removal.

Key details:
- Save data reads use EP3 (not EP1) — undocumented in ndsplus
- Discriminates the EMS GB USB Smart Card (same VID/PID, different protocol)
- Detects 3DS cartridges via all-0xFF header pattern
- Supports EEPROM (512B/8KB/64KB) and FLASH save types

Protocol reference: github.com/Thulinma/ndsplus
Maker codes derived from devkitPro ndstool (GPL-3.0)
The EMS NDS Adapter+ driver is derived from the GPL-3.0 ndsplus
project by Thulinma.
# Conflicts:
#	src/App.tsx
#	src/hooks/use-connection.ts
#	src/lib/core/devices.ts
Extract NDS cart-header parsing into a new shared module so future NDS
save devices (Datel PowerSaves for 3DS, etc.) can reuse the same
header/CRC validation, region decoding, and No-Intro lookups instead
of each driver carrying its own copy.

- New `src/lib/systems/nds/nds-header.ts` — header parsing, region
  decoding, dual-CRC validation (logo CRC + header CRC), 3DS-cart
  all-0xFF detection, and an `NDSDeviceDriver` interface every NDS
  driver implements
- `nds-save-system-handler.ts` gains `validateDump`, which catches
  duplicate-half saves, all-0x00 / all-0xFF dumps, and chip-size
  mismatches at the system layer
- EMS driver refactored to use the shared header module and
  interface; cart identity is now fingerprinted (`statusFingerprint`)
  and re-verified after every dump/write so a mid-operation cart
  swap fails closed instead of silently dumping the wrong save
- Save read switched to the default bulk IN endpoint — earlier code
  selected EP3 explicitly based on the ndsplus reference, but every
  hardware-validated transfer actually returns on EP1, so removing
  the override fixed a silent hang on some firmware versions
- Save-read timeout tuned to 3 s with slow-chunk logging; in-driver
  recovery paths (orphan-transferIn rescue, stall counter, pre-dump
  GET_STATUS verify) removed after they were observed corrupting
  saves on real carts; firmware-version word now decodes the
  `recovery` bit so the scanner can distinguish a degraded adapter
  from a missing one
- Document the EMS adapter's undocumented protocol surface
  (`UNDOCUMENTED_CMD`, `UPGRADE`) for future reference; not invoked
  by the driver
- Scanner no longer hot-swaps carts — physical disconnect is
  required between dumps to keep the SPI bus from being driven
  while a new chip is energising; UI surfaces cart info, save size,
  and validation warnings during the read
- Generic `CartridgeInfo<M>` plus an `NDSCartMeta` shape; `nointro.ts`
  filters prerelease tags and maps the `nds_save` system; USB
  transport exposes `getDevice()` for sibling-protocol probes;
  `formatBytes` learns MB
- THIRD-PARTY-LICENSES: add powerslaves attribution (kitlith, MIT)
  and refine the existing ndstool note
@pathawks
Copy link
Copy Markdown
Owner Author

I bricked my EMS NDS Adaptor Plus while working on this. I was trying to replace the firmware on the device; this didn't happen while trying to read the save data.

All of this to say it may be a minute before I can merge support for this device.

The product itself uses the British spelling on the box and installer
(NDS_Adaptor_Plus). Switch user-visible strings — device name, error
text, scanner instructions, log messages — to match. Code identifiers,
file names, and JSDoc keep 'Adapter' for now to avoid churn.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant