(Switch 2) Compatibility Mode: Difference between revisions
No edit summary |
|||
(5 intermediate revisions by the same user not shown) | |||
Line 35: | Line 35: | ||
| 0xC8 || u64 || AudioControlData | | 0xC8 || u64 || AudioControlData | ||
|- | |- | ||
| 0xD0 || u64 || MiscControlData | | 0xD0 || u64 || [[#MiscControlData]] | ||
|} | |||
=== MiscControlData === | |||
{| class=wikitable | |||
! Bit || Name || Description | |||
|- | |||
| 0 || PreventShaderCacheOpenFile || Prevents game from opening file: "ROM:/data/scaleform_cache/GFxShaders.cache". | |||
|- | |||
| 1 || PreventShaderCacheGetEntryType || Prevents game from getting the entry type on 5 different hardcoded paths. | |||
|- | |||
| 2 || PreventShaderCacheOpenDirectory || Prevents game from opening directory: "host:/shaders/build/" | |||
|- | |||
| 4 || SerializeSavedataAccess || A global mutex is taken when a file starting with "savedata" is opened, and held until the file has been closed. Commit-action also takes the mutex, so the commit will not complete until the file has been closed. | |||
|- | |||
| 6 || PreventNegativeVibrationValue || | |||
|- | |||
| 10 || NeedsWaitingLaunchLogo || | |||
|- | |||
| 13 || ExtraSleepForOfflineWebApplet || | |||
|- | |||
|} | |} | ||
Line 61: | Line 81: | ||
| nn::os::SetThreadCoreMask || No change. || | | nn::os::SetThreadCoreMask || No change. || | ||
|- | |- | ||
| nn::os::GetThreadAvailableCoreMask || Returns actual core mask masked with 0x7, effectively pretending that there are only 3 CPU cores available. || | | nn::os::GetThreadAvailableCoreMask, nn::os::GetThreadAvailableCoreMask || Returns actual core mask masked with 0x7, effectively pretending that there are only 3 CPU cores available. || | ||
|- | |- | ||
| nn::audio::GetAudioInName || If the primary audio in name is "XUac", it returns the secondary device name instead. Presumably, the "XUac" device has been removed in Switch 2. || | | nn::audio::GetAudioInName || If the primary audio in name is "XUac", it returns the secondary device name instead. Presumably, the "XUac" device has been removed in Switch 2. || | ||
|- | |- | ||
| nn::audio::GetReleasedWaveBuffer || Now returns NULL under certain circumstances. || | | nn::audio::GetReleasedWaveBuffer || Now returns NULL under certain circumstances. || AudioControlData bit0 | ||
|- | |- | ||
| nn::web::OfflineWebSession::Appear || Added sleep 50000000 before calling the original function. || MiscControlData bit13 | | nn::web::OfflineWebSession::Appear || Added sleep 50000000 before calling the original function. || MiscControlData bit13 | ||
|- | |- | ||
| nn::ssl::Connection::SetSocketDescriptor || Calls Fnctl(..., 3); Fnctl(..., 4); on the socket fd. || MiscControlData bit15 | | nn::ssl::Connection::SetSocketDescriptor || Calls nn::socket::Fnctl(..., 3); nn::socket::Fnctl(..., 4); on the socket fd. || MiscControlData bit15 | ||
|- | |- | ||
| nn::swkbd::InlineKeyboard::GetImage, nn::swkbd::InlineKeyboard::Calc || Takes a shared mutex before calling original functions. No other changes. Probably prevents a race condition that only manifests on Switch 2. || | | nn::swkbd::InlineKeyboard::GetImage, nn::swkbd::InlineKeyboard::Calc || Takes a shared mutex before calling original functions. No other changes. Probably prevents a race condition that only manifests on Switch 2. || | ||
|- | |||
| nn::socket::Socket || If nn::socket::IsInitialized() is 0, sleep for 1 second, and see it reaches 1 before calling the original function. Probably prevents a race condition that only manifests on Switch 2. || MiscControlData bit9 | |||
|- | |||
| nn::os::TimedAcquireSemaphore || Force arg1=true. || MiscControlData bit8. | |||
|- | |||
| nn::os::QueryMemoryInfo,nn::os::QueryMemoryInfoForDebug || Hides stolen Heap memory that it uses for itself... || | |||
|- | |||
| nn::hid::VibrationPlayer::SetCurrentPosition || If position is negative, drop the call. || MiscControlData bit6 | |||
|} | |} | ||
Latest revision as of 11:33, 6 June 2025
When a Switch 1 game is running on the Switch 2, it is loaded together with 3 new modules:
These binaries are compiled with PAC enabled.
Hooking
Backwards-compatibility is achieved by (ab)using dynamic linking to selectively hook API functions. Three compatibility libraries are mapped into the game process. They provide symbols which override the original sdk codebin implementation, wherein a translation layer for Switch 2 is provided.
Most of these end up doing small tweaks to the parameters, and then calling into the original function implementation.
Switch 2 is mostly backwards-compatible, so most sdk codebin symbols remain unchanged and execute their original code. The overrides are mostly related to Graphics (i.e. translating to the new GPU), but there are also plenty other function overrides to deal with various system differences.
Below is a breakdown what some of the hooks and what their purpose is.
CompatibilityParameter
The system puts an additional information block in the ProgramArgument parameter for the process. This block contains certain extra information and overrides, which allows customization of the compatibility hooks depending on the game.
To retrieve this pointer, nnCompatThin intercepts the nnosInitialize function and retrieves it from there. Normally, a game doesn't use the ProgramParameter so it doesn't care this parameter was passed.
Offset | Type | Name |
---|---|---|
0x00 | ModuleSdkInfo[11] | Modules |
0xB0 | u64 | ModulesCount |
0xB8 | u64 | GraphicsControlData |
0xC0 | u64 | MovieControlData |
0xC8 | u64 | AudioControlData |
0xD0 | u64 | #MiscControlData |
MiscControlData
Bit | Name | Description |
---|---|---|
0 | PreventShaderCacheOpenFile | Prevents game from opening file: "ROM:/data/scaleform_cache/GFxShaders.cache". |
1 | PreventShaderCacheGetEntryType | Prevents game from getting the entry type on 5 different hardcoded paths. |
2 | PreventShaderCacheOpenDirectory | Prevents game from opening directory: "host:/shaders/build/" |
4 | SerializeSavedataAccess | A global mutex is taken when a file starting with "savedata" is opened, and held until the file has been closed. Commit-action also takes the mutex, so the commit will not complete until the file has been closed. |
6 | PreventNegativeVibrationValue | |
10 | NeedsWaitingLaunchLogo | |
13 | ExtraSleepForOfflineWebApplet |
ModuleSdkInfo
Offset | Type | Name |
---|---|---|
0x00 | const char* | SdkNameStringPtr. Points to a string of the form "SDK MW+Nintendo+NintendoSdk_nnSdk-XX_Y_Z-Release". |
0x08 | u32 | SdkNameStringLength |
nnCompatTrampoline
nnCompatThin
Hooked Symbol | Difference | Conditional |
---|---|---|
nnosInitialize | Captures the ProgramArgument parameter where the #CompatibilityParameter is stored. | |
nn::os::CreateThread | No change. | |
nn::os::SetThreadCoreMask | No change. | |
nn::os::GetThreadAvailableCoreMask, nn::os::GetThreadAvailableCoreMask | Returns actual core mask masked with 0x7, effectively pretending that there are only 3 CPU cores available. | |
nn::audio::GetAudioInName | If the primary audio in name is "XUac", it returns the secondary device name instead. Presumably, the "XUac" device has been removed in Switch 2. | |
nn::audio::GetReleasedWaveBuffer | Now returns NULL under certain circumstances. | AudioControlData bit0 |
nn::web::OfflineWebSession::Appear | Added sleep 50000000 before calling the original function. | MiscControlData bit13 |
nn::ssl::Connection::SetSocketDescriptor | Calls nn::socket::Fnctl(..., 3); nn::socket::Fnctl(..., 4); on the socket fd. | MiscControlData bit15 |
nn::swkbd::InlineKeyboard::GetImage, nn::swkbd::InlineKeyboard::Calc | Takes a shared mutex before calling original functions. No other changes. Probably prevents a race condition that only manifests on Switch 2. | |
nn::socket::Socket | If nn::socket::IsInitialized() is 0, sleep for 1 second, and see it reaches 1 before calling the original function. Probably prevents a race condition that only manifests on Switch 2. | MiscControlData bit9 |
nn::os::TimedAcquireSemaphore | Force arg1=true. | MiscControlData bit8. |
nn::os::QueryMemoryInfo,nn::os::QueryMemoryInfoForDebug | Hides stolen Heap memory that it uses for itself... | |
nn::hid::VibrationPlayer::SetCurrentPosition | If position is negative, drop the call. | MiscControlData bit6 |
nnCompat
Hooked Symbol | Difference |
---|---|
nn::os::GetSystemTickFrequency | Returns 31250000 instead of 19200000. |
nn::os::ConvertToTick | Uses conversion ratio 31250000 instead of 19200000. |