3DS Virtual Console

3DS Virtual Console

There are several types of VC titles: NES/GB/GBC VC titles, SNES VC titles, GameGear VC titles and GBA VC titles. Except for GBA VC, the games are ran using emulators.

NES/GB/GBC VC #

An emulator application + VC ROM in the NCCH RomFS (among other things in the RomFS).

The original emulator builds include support for all these three platforms, not specific to just the included ROM platform. However, releases intended for NES games have a red menu, optional switching between the two controllers, a different X button mapping (B instead of menu), optional Download Play support, and 🔗 a never used multi-rom support; while Game Boy versions have a green theme and optional 3D border.

Early builds (of Ambassador NES games at least) did not support savestates.

The emulator officially used for Pokemon games removes savestate support in favor of a Link Cable “implementation” involving hooking the games' network functions according to the patch files. ( 🔗 Partial documentation of the patch format)

This emulator includes GBA support, however the GBA emulation for this this is somewhat slow. This was presumably implemented before AGB_FIRM was.

Unlike Wii VC, the 3DS VC ROMs for NES use 🔗 the “TNES” header.

RomFS #

  • “rom:/rom/” This directory contains the ROM file(s). Filenames used under here don’t matter: the filename is determined by the emulator app by doing a directory read.
  • “rom:/shaders/” This directory contains GPU shaders used by the emulator app: .shbin, .csdr, and .obj.
  • “rom:/VCM/” This directory contains graphics, audio, and text used by the emulator app.
  • “rom:/agb.bin” GBA BIOS.
  • “rom:/buildtime.txt” Emulator app build timestamp.
  • “rom:/config.ini” Emulator configuration .ini, contains sections for all supported 3DS VC platforms.
  • “rom:/<rom_name>.patch” rom_name = filename from the rom directory. This .ini contains patches for the ROM.
  • “rom:/shader.shbin” GPU shader.

Savedata #

The savedata can contain:

  • “rsm1.dat”: Same format as the below rsm2.dat. Probably used for the “restore-point”.
  • “rsm2.dat”: Current emulator save-state, for storing/loading state at VC-title launch/exit.
  • “sav.dat”: The actual savedata used by the emulated ROM.
  • “SecureValue”: The random number used by Anti Savegame Restore. No known version of the emulator implements both savestates and secure value.

Overwriting sav.dat with 0xFF-bytes doesn’t have any affect on the actual emulator. Doing that with most of the rsm*.dat data doesn’t crash anything.

SNES VC #

An emulator application + VC ROM in the NCCH RomFS (among other things in the RomFS). SNES VC titles are New 3DS exclusive.

RomFS #

  • “rom:/ErrorMessage/” This directory contains text used by the emulator app.
  • “rom:/shader/” This directory contains .shbin GPU shaders used by the emulator app.
  • “rom:/VCM/” This directory contains text used by the emulator app.
  • “rom:/caravel.bcsar” This file contains audio used by the emulator app.
  • “rom:/ctrl_change.arc” This file contains graphics used by the emulator app.
  • “rom:/data.bin” This file contains the ROM and other data. See below for documentation.
  • “rom:/ErrorMessage.arc” This file contains graphics used by the emulator app.
  • “rom:/KTR-XXXX.icn” Copy of the SMDH of the game.
  • “rom:/shader.shbin” GPU shader.
  • “rom:/nnfont_RectDrawerShader.shbin” GPU shader.
  • “rom:/VCM.arc” This file contains graphics used by the emulator app.

data.bin structure #

The file begins with a header (all values are little-endian):

STARTSIZEDESCRIPTION
0x000x4Always 0x00000100
0x040x4File size
0x080x4Always 0x00000030
0x0C0x4Always 0x00000050
0x100x4Start of the ROM (always 0x00000060 in official VC)
0x140x4End of the ROM
0x180x4Start of the footer region (presumably an index for the PCM audio samples). Matches end of file/file size if PCM data is missing
0x1C0x4Always 0
0x200x4Start of decompressed SDD-1 graphics data region. Matches end of file/file size if no SDD-1 chip is present
0x240x8Product ID (KTR-XXXX), determines filenames in savedata
0x2C0x4Always 0
0x300x1Emulation speed in FPS (always 0x3C in official VC)
0x310x3ROM size
0x340x1Always 0
0x350x3Size of the converted PCM audio samples region (starting after ROM). 0 if PCM data is missing
0x380x1Always 0
0x390x2Footer region size. 0 if PCM data is missing
0x3B0x2Always 0
0x3D0x2Preset ID (varies for each game). A full list of know Preset IDs (shared by WiiU/SNESC/Switch SNES emulators) can be found 🔗 here
0x3F0x1Always 0x2
0x400x1Sound volume
0x410x1ROM type (0x15 == HiROM/0x14 == LoROM)
0x420xEAlways 0
0x500x4Always 0x00000003
0x540x4Always 0x00000001
0x580x8Always 0

The 0x60 header is followed by the SNES ROM, often altered to replace audio samples with pointers to external PCM audio files converted from the game, presumably to speed up emulation (these pointers can be found by looking for “PCMF” in the ROM, as seen on 🔗 Wii VC). The ROM is then optionally followed by the PCM audio files, by the SDD-1 decompressed graphics data (the emulator doesn’t properly emulate the chip, presumably because of hardware constraints) and by a footer which appears to be an index for the PCM audio data. There are no separate setting fields for individual cart features, and it appears that the emulator has “game presets” stored in its own code, which determine the game-specific settings (such as the expansion chip, and presumably the presence of SRAM), selectable via the preset ID in the header. Each official VC release has 🔗 a different preset ID. The supported SNES expansion chips are: DSP1, C4 and DSP2.

A similar structure can be found on the Wii U, SNES Classic and Switch Mini emulator 🔗 1

Savedata #

The savedata contains:

  • “KTR-XXXX.cfg”: Appears to contain the “preset ID” and possibly more game/save information.
  • “KTR-XXXX.vea”: Current emulator save-state, for storing/loading state at VC-title launch/exit.
  • “KTR-XXXX.ves”: The actual savedata used by the emulated ROM.

Filenames are determined in the ROM header.

GBA VC #

GBA VC is run by AGB_FIRM. RomFS isn’t used for GBA VC titles, but can be found empty within GBA VC titles. The NCCH ExeFS contains the same files as a normal application. The ExeFS:/.code contains the GBA VC ROM followed by a 0x360 byte long footer.

All values in the GBA VC footer and related structures are little-endian.

STARTSIZEDESCRIPTION
0x000x4Magic ‘.CAA’
0x040x4Must be 1
0x080x4Offset to array of config descriptors
0x0C0x4Number of config descriptors << 4

Config descriptor #

STARTSIZEDESCRIPTION
0x00x4If 1, then this config descriptor is used. If 0, and the following value is not 0, then the GBA VC load fails outright (causes result 0xC900464F). Otherwise, this config descriptor is skipped.
0x40x4Offset to config
0x80x4Size of config (unused by the function that parses this, which hardcodes the config size (0x324) to memcpy)
0xC0x4Padding

Config #

STARTSIZEDESCRIPTION
0x0000x4Padding
0x0040x4GBA ROM Filesize
0x0080x4Save type (see below for supported values and here for details)
0x00C0x2Padding (set to FF FF usually?)
0x00E0x2Sleep mode button combo (utilizes the same bit masks as the HID_PAD register, with flipped bits).
If the GBA title supports a button-combo based sleep mode and it's set here, Agbbg spoofs this combo when closing the 3DS' lid to enter proper sleep mode.
0x0100x10Flash and EEPROM configuration (see the target registers here)
0x0200x4LCD ghosting (01-FF, lower values equal more ghosting)
0x0240x300Video LUT (black to full, rgbrgbrgb...)?,
three different types of this data have been observed.

Save types:

  • 0x0: EEPROM 8k for < 256 Mbit titles
  • 0x1: EEPROM 8k for 256 Mbit titles
  • 0x2: EEPROM 64k for < 256 Mbit titles
  • 0x3: EEPROM 64k for 256 Mbit titles
  • 0x4: Flash 512k (Atmel, ID: 0x3D1F) + RTC
  • 0x5: Flash 512k (Atmel, ID: 0x3D1F)
  • 0x6: Flash 512k (SST, ID: 0xD4BF) + RTC
  • 0x7: Flash 512k (SST, ID: 0xD4BF)
  • 0x8: Flash 512k (Panasonic, ID: 0x1B32) + RTC
  • 0x9: Flash 512k (Panasonic, ID: 0x1B32)
  • 0xA: Flash 1Mbit (Macronix, ID: 0x09C2) + RTC
  • 0xB: Flash 1Mbit (Macronix, ID: 0x09C2)
  • 0xC: Flash 1Mbit (Sanyo, ID: 0x1362) + RTC
  • 0xD: Flash 1Mbit (Sanyo, ID: 0x1362)
  • 0xE: SRAM/FRAM 256k

Everything above 0xE results in no save chip and nothing being saved to NAND.

NAND Savegame #

AGB_FIRM saves its active save memory to NAND on exit, this is then immediately picked up by NATIVE_FIRM on reboot by checking CFG_BOOTENV. From there, this is verified and copied out to SD (also see below). The savegame format is as follows:

STARTSIZEDESCRIPTION
0x00x4Magic (’.SAV')
0x40xCAlways 0xFF
0x100x10AES-CMAC of the SHA256 hash of 0x30..0x200 + the entire save itself, keyslot 0x24, keyY from process9 .rodata
0x200x10Always 0xFF
0x300x4Always 0x1
0x340x4Number of times saved (unused?)
0x380x8AGB TitleID
0x400x10SD card CID from the console the save was made on (verified on load)
0x500x4Save start addr (always 0x200)
0x540x4Save size
0x580x8Always 0xFF (?)
0x600x4See here
0x640x4See here
0x680x198Always 0xFF

NAND Savegame on SD #

A NAND savegame copied to the SD by process9 is identical to its counterpart on the NAND partition, save for the CMAC. For SD copies on retail units, the CMAC is recalculated as the AES-CMAC of the (SHA256 hash of (“CTR-SIGN” + AGB TitleID (little endian) + SHA256 hash of (“CTR-SAV0” + SHA256 hash of (0x30..0x200 + the entire save itself)))), using keyslot 0x30 set up with the keyY from movable.sed. For SD copies on devkit units, the CMAC is recalculated using the SHA256 hash of 0x30..0x200 + the entire save itself, using a different key from process9 .rodata.