Files

273 lines
15 KiB
YAML

emulator: Kenji-NX
type: standalone
core_classification: community_fork
source: "https://git.ryujinx.app/projects/Kenji-NX"
source_branch: libryujinx_bionic
upstream: "https://git.ryujinx.app/projects/Ryubing"
profiled_date: "2026-08-11"
source_commit: "53d32a761e61dc8e9b5c0744199a890fc8d934d5"
core_version: "2.1.0-pr.2"
display_name: "Nintendo - Switch (Kenji-NX)"
cores:
- kenji-nx
- kenjinx
- "KENJI-NX"
systems:
- nintendo-switch
mode: standalone
notes: |
Nintendo Switch emulator in C#, a fork of the Ryujinx line maintained by
KeatonTheBot. Two builds come out of one repository: the desktop Avalonia
front end on master, and the Android app on libryujinx_bionic, which carries
the whole desktop tree plus src/KenjinxAndroid and src/LibKenjinx. That branch
is the one read here, since master has no Android code at all. The Android
package is org.kenjinx.android and ES-DE launches it with the action
org.kenjinx.android.LAUNCH_GAME and the ROM in EXTRA_bootPath.
ref: README.md:15-24, src/KenjinxAndroid/app/build.gradle:11-15,
src/KenjinxAndroid/app/src/main/AndroidManifest.xml:8,69
Every external file hangs off one data root. AppDataManager.Initialize takes a
base path, keeps a portable directory next to the binary when one exists and
otherwise falls back to %APPDATA%/Ryujinx, the default directory name being
still Ryujinx. Android hands it getExternalFilesDir(null), which differs from
the user profile path and already exists, so the launch mode is Custom and the
~/.switch fallback that ReloadKeySet consults in UserProfile mode never fires
there. games, profiles and system are created under the root on every start.
ref: src/Ryujinx.Common/Configuration/AppDataManager.cs:11-17,47,50-104,223-230,
src/LibKenjinx/LibKenjinx.cs:59-100,
src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt:264-298
Firmware is installed rather than placed. ContentManager takes a directory, a
zip or an xci with an update partition, verifies each NCA against the key set
and refuses a package carrying unrelated archives, then registers the contents
under bis/system/Contents/registered. Android passes a file descriptor instead
of a path but reaches the same two methods. Nothing gates the launch on it:
homebrew NRO goes through ProcessLoader without touching the key set, and the
failure is deferred to the first service that needs a system archive.
ref: src/Ryujinx.HLE/FileSystem/ContentManager.cs:431-498,675-749,893-903,
src/LibKenjinx/Android/JniExportedMethods.cs:462-498,
src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs:189-205
Two read paths belong to the desktop build alone, because the Android build
drops LibKenjinx.Native.cs and wires those subsystems differently.
SDL_GameControllerDB.txt is read by SDL3Driver, whose only callers are the
SDL3 audio, gamepad and keyboard drivers; on Android JnaDeviceInitialize
builds an OpenALHardwareDeviceDriver and InitializeInput builds a
VirtualGamepadDriver, so SDL3 never initialises. Amiibo diverge the same way:
the desktop scan decrypts a dump through AmiiboBinReader and therefore needs
key_retail.bin, while the Android entry buffers the raw tag in
KenjinxAmiiboShim and INfp reads the figure id straight out of offset 0x1DC.
ref: src/LibKenjinx/LibKenjinx.csproj:11-13,
src/LibKenjinx/Android/JniExportedMethods.cs:350-351,1049-1063,
src/LibKenjinx/LibKenjinx.Input.cs:28-39,
src/Ryujinx.HLE/Kenjinx/AmiiboBridge.cs:10-44,
src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/KenjinxAmiiboShim.cs:15-41,
src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs:149-166,257-279
Config.json, Profiles.json, updates.json, dlc.json, mods.json, metadata.json
and system/amiibo/<amiibo id>.json are written by the emulator itself, and
system/amiibo/Amiibo.json is a catalogue the desktop window downloads from
AmiiboAPI and rewrites. None of them is user supplied. The Android app keeps
its own settings in Android shared preferences.
ref: src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs:198-243,
src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs:61-63,
src/Ryujinx.Common/ReleaseInformation.cs:14-19,
src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/QuickSettings.kt:92
Distribution moved off GitHub: Kenji-NX/Android-Releases returns HTTP 451
under the Nintendo notice of 2026-02-12, and both the source and the builds
are served by the Forgejo instance, which is where ES-DE points as well.
ref: https://github.com/github/dmca/blob/master/2026/02/2026-02-12-nintendo.md,
es-de ANDROID.md:470-474
files:
- name: prod.keys
required: true
path: system/prod.keys
description: "Production key set for NCA decryption"
note: >-
Read from the system directory under the data root, and additionally from
~/.switch when the launch mode is UserProfile, which Android never is. The
four key files are looked up independently, so each is optional to the
reader, but an empty header key stops NCA header decryption outright and
no encrypted content loads. The desktop importer accepts a .keys file or a
zip of them and rejects any line that is not "name = value" in lowercase
hexadecimal; the Android importer accepts the name prod.keys only and
copies it straight into the system directory.
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:231-266, src/Ryujinx.HLE/FileSystem/ContentManager.cs:500-537,681-684,1082-1118, src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/SettingsViewModel.kt:254-301"
- name: title.keys
required: false
path: system/title.keys
description: "Per-title key set mapping rights id to title key"
note: >-
Read beside prod.keys and passed to the same key file reader. Tickets
found in a container are imported into the key set at load time, so a
title shipping its own ticket does not need an entry here. The importer
validates each line as two 32-character hexadecimal fields.
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:241,250-253,266,269-297, src/Ryujinx.HLE/FileSystem/ContentManager.cs:1086,1100-1102"
- name: console.keys
required: false
path: system/console.keys
description: "Console-unique key set"
note: >-
Read beside prod.keys and passed to the same key file reader as the
console-specific set. Validated as "name = value" lines like prod.keys.
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:242,255-258,266, src/Ryujinx.HLE/FileSystem/ContentManager.cs:1103-1105"
- name: dev.keys
required: false
path: system/dev.keys
description: "Development-unit key set"
note: >-
Read beside prod.keys and handed to the key file reader as the development
set, alongside the production one rather than instead of it. Validated as
"name = value" lines.
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:243,260-263,266, src/Ryujinx.HLE/FileSystem/ContentManager.cs:1104-1105"
- name: "<firmware>.zip"
aliases: ["<firmware>.xci"]
required: false
storage: large_file
description: "Switch system firmware package"
note: >-
Chosen through a file picker and read whole at install time, by path on
desktop and by file descriptor on Android. A zip is walked entry by entry
and an xci is opened at its update partition; a directory of NCAs is also
accepted on desktop. Each NCA is decrypted with the key set to read its
title id and content type, the package is rejected when it carries
archives that belong to no system title, and the version string comes out
of title 0100000000000809. The NCAs are then registered under
bis/system/Contents/registered and read from there, never as loose files.
No digest of the package itself is compared against anything.
source_ref: "src/Ryujinx.HLE/FileSystem/ContentManager.cs:431-498, 675-749, 751-1045, src/LibKenjinx/Android/JniExportedMethods.cs:462-498, src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/SettingsViewModel.kt:308-364"
- name: key_retail.bin
required: false
path: system/key_retail.bin
size: 160
validation: [size]
description: "Amiibo master keys used to decrypt tag dumps"
note: >-
Read from the system directory under the data root and split into two
80-byte master keys, the data key then the tag key, each being a 16-byte
HMAC key, a 14-byte type string, a reserved byte, a magic size byte, 16
magic bytes and a 32-byte XOR pad. Any total other than 160 bytes throws
and the scan fails. The desktop window hides its amiibo-from-dump entry
when the file is absent. Only the desktop scan path decrypts a dump; the
Android slots hand the raw bytes to the tag buffer instead.
source_ref: "src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs:22-44,331-336, src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDecryptor.cs:10-16, src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboMasterKey.cs:15-39, src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs:385,2243"
- name: SDL_GameControllerDB.txt
required: false
path: SDL_GameControllerDB.txt
category: game_data
description: "SDL gamepad mapping database"
note: >-
Read from the data root and handed to SDL_AddGamepadMappingsFromFile when
the SDL3 driver starts, which happens on the desktop build only. Guarded
by an existence test, and SDL carries its own built-in mappings, so a
missing file costs only the extra pad definitions.
source_ref: "src/Ryujinx.SDL3.Common/SDL3Driver.cs:100-105, src/Ryujinx.Input.SDL3/SDL3GamepadDriver.cs:53, src/Ryujinx.Audio.Backends.SDL3/SDL3HardwareDeviceDriver.cs:36, src/Ryujinx/UI/Windows/MainWindow.axaml.cs:110"
- name: "<amiibo>.bin"
required: false
unsourceable: "dump of one physical amiibo figure, tied to the tag it was read from and never distributed generically"
description: "Amiibo tag dump"
note: >-
Read whole from wherever the picker points. The desktop path decrypts it
with key_retail.bin, accepts 532 bytes by padding to the 540 of 135 pages
of 4, and writes the tag back when a game edits its application area. The
Android slots read the bytes through the content resolver and buffer them
for the NFP service, which takes the figure id from offset 0x1DC without
decrypting anything.
source_ref: "src/Ryujinx.HLE/HOS/Horizon.cs:355-363, src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs:22-44, 151-283, src/LibKenjinx/LibKenjinx.cs:705-734, src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/GameViews.kt:328-349"
- name: "<CaCertificateId>.der"
required: false
path: "system/ssl/<CaCertificateId>.der"
unsourceable: "replacement certificate the user picks for one certificate authority, with no canonical artefact behind the name"
description: "Override for one built-in root certificate"
note: >-
Looked up under system/ssl by the enumeration name of the certificate
being read out of the firmware certificate store, and substituted for the
store entry when the file exists. The store itself comes from title
0100000000000800, so the absence of an override changes nothing.
source_ref: "src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs:25,91-112,114-180"
- name: "<driver>.zip"
required: false
path: "drivers/<driver>.zip"
unsourceable: "third-party Vulkan driver built for one GPU family, not an emulator artefact"
description: "Replacement Vulkan driver for the Android build"
note: >-
Imported through a picker filtered on application/zip and unpacked into a
folder named after the archive under drivers in the data root. A folder is
offered only when it holds a meta.json naming a libraryName that resolves
to a file present beside it; the chosen path is remembered in a file named
selected. At renderer start the folder is copied into internal storage,
because the external filesystem cannot carry an executable mapping, loaded
through the rootless Adreno driver loader, and the resulting handle is
passed to the Vulkan loader. An empty selection uses the system loader.
source_ref: "src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/VulkanDriverViewModel.kt:13-167, src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt:202-243, src/LibKenjinx/Android/JniExportedMethods.cs:535-558, src/LibKenjinx/VulkanLoader.cs:27-29"
# System archives read out of the installed firmware, never as loose files.
firmware_titles:
- id: "0100000000000809"
name: "System version"
content: ["file"]
required: false
note: "Version string reported for the installed firmware, and the value the installer returns after verifying a package."
source_ref: "src/Ryujinx.HLE/FileSystem/ContentManager.cs:32,1047-1080"
- id: "010000000000080E"
name: "TimeZoneBinary"
content: ["binaryList.txt", "zoneinfo/"]
required: false
note: "Location list and zone rules. Without it the location cache holds UTC alone and the time zone manager is marked initialised without rules."
source_ref: "src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs:24,62-86,88-119"
- id: "0100000000000810"
name: "FontNintendoExtended"
content: ["nintendo_ext_003.bfttf"]
required: false
note: "Shared font, XOR-decoded from BFTTF into the shared memory the pl:u service exposes."
source_ref: "src/Ryujinx.HLE/FileSystem/ContentManager.cs:73,93, src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs:63-125,159-180"
- id: "0100000000000811"
name: "FontStandard"
content: ["nintendo_udsg-r_std_003.bfttf"]
required: false
note: "Shared font. A missing font title throws InvalidSystemResourceException, so a title that asks for shared fonts stops there."
source_ref: "src/Ryujinx.HLE/FileSystem/ContentManager.cs:68,88, src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs:63-125"
- id: "0100000000000812"
name: "FontKorean"
content: ["nintendo_udsg-r_ko_003.bfttf"]
required: false
note: "Shared font."
source_ref: "src/Ryujinx.HLE/FileSystem/ContentManager.cs:71,91"
- id: "0100000000000813"
name: "FontChineseTraditional"
content: ["nintendo_udjxh-db_zh-tw_003.bfttf"]
required: false
note: "Shared font."
source_ref: "src/Ryujinx.HLE/FileSystem/ContentManager.cs:70,92"
- id: "0100000000000814"
name: "FontChineseSimplified"
content: ["nintendo_udsg-r_org_zh-cn_003.bfttf", "nintendo_udsg-r_ext_zh-cn_003.bfttf"]
required: false
note: "Shared fonts. One title carries both the base and the extended simplified faces."
source_ref: "src/Ryujinx.HLE/FileSystem/ContentManager.cs:69-70,89-90"
- id: "0100000000000800"
name: "Certificate store"
content: ["ssl_TrustedCerts.bdf", "ssl_TrustedCerts.tcf"]
required: false
note: "Trusted root certificates for the ssl service, the .tcf name being the one used up to firmware 2.3.0. Absent, the service logs and serves nothing."
source_ref: "src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs:25,114-180"