Switch 2: Compatibility Mode: Difference between revisions

Created page with "When a Switch 1 game is running on the Switch 2, it is loaded together with 3 new modules: * nnCompatTrampoline * nnCompatThin * nnCompat 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 co..."
 
No edit summary
 
(15 intermediate revisions by one other user not shown)
Line 7: Line 7:
These binaries are compiled with PAC enabled.
These binaries are compiled with PAC enabled.


= NPDM Adjustments =
* Game has access to SVC 0x80 even though it's not in the game NPDM.
* Game has CoreMask 0x3F (so access to core0-5) even though the game NPDM only has CoreMask 0x7.
* It's unknown if the service whitelist is updated for the game when running in Compatibility mode.


= Hooking =
= Hooking =
Line 18: Line 22:


== CompatibilityParameter ==
== CompatibilityParameter ==
The system puts an additional information block in the ProgramArgument parameter for the LaunchParameter for the process. This block contains certain extra information and overrides, which allows customization of the compatibility hooks depending on the game.
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|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.
To retrieve this pointer, [[#nnCompatThin|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.


{| class=wikitable
! Offset || Type || Name
|-
| 0x00 || ModuleSdkInfo[11] || Modules
|-
| 0xB0 || u64 || ModulesCount
|-
| 0xB8 || u64 || GraphicsControlData
|-
| 0xC0 || u64 || MovieControlData
|-
| 0xC8 || u64 || AudioControlData
|-
| 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 ||
|-
|}
=== ModuleSdkInfo ===
{| class=wikitable
! 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
|-
|}
= ApplicationCompatibilityInfo =
The [[Switch 2: Title list|ApplicationCompatibilityInfo]] SystemData contains "ApplicationCompatibilityInfo.json".
The json "records" contains an array, each object entry then contains the following:
* "application_id": "0x<lowercase hex ApplicationId>"
* "target_max_version": <value> (normally u32-max)
* "application_layer_info": This object contains various number fields, only fields needed by the current entry are present. "graphics_config", "misc_config", ...
** The data from here is likely passed as input to [[#CompatibilityParameter|CompatibilityParameter]].


= nnCompatTrampoline =
= nnCompatTrampoline =
Line 28: Line 86:


{| class=wikitable
{| class=wikitable
! Hooked Symbol || Difference
! 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
|-
|-
| nnosInitialize || Captures the ProgramArgument parameter where the [[#CompatibilityParameter]] is stored.
| nn::ssl::Connection::SetSocketDescriptor || Calls nn::socket::Fnctl(..., 3); nn::socket::Fnctl(..., 4); on the socket fd. || MiscControlData bit15
|-
|-
| nn::os::CreateThread || No change.
| 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::os::SetThreadCoreMask || No change.
| 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::GetThreadAvailableCoreMask || Returns actual core mask masked with 0x7, effectively pretending that there are only 3 CPU cores available.
| nn::os::TimedAcquireSemaphore || Force arg1=true. || MiscControlData bit8.
|-
|-
| 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::os::QueryMemoryInfo,nn::os::QueryMemoryInfoForDebug || Hides stolen Heap memory that it uses for itself... ||
|-
|-
| nn::audio::GetReleasedWaveBuffer || Now returns NULL under certain circumstances.
| nn::hid::VibrationPlayer::SetCurrentPosition || If position is negative, drop the call. || MiscControlData bit6
|}
|}


Line 51: Line 123:
|-
|-
| nn::os::ConvertToTick || Uses conversion ratio 31250000 instead of 19200000.
| nn::os::ConvertToTick || Uses conversion ratio 31250000 instead of 19200000.
|-
| gl*, _gl*, egl* || OpenGL API translation for new GPU?
|-
| vk* || Vulkan API translation for new GPU?
|-
| NvOsFopen ||
|-
| NvOsGetTimeMS || Adjusted for new clock frequency.
|-
| nn::vi::* || Translation layer because vi:u was removed?
|}
|}