Migration services: Difference between revisions

No edit summary
 
(64 intermediate revisions by the same user not shown)
Line 4: Line 4:


= mig:usr =
= mig:usr =
This is "nn::migration::user::IService"
This is "nn::migration::user::IService".


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 107: Line 107:


== CreateSaveDataMigrationServer ==
== CreateSaveDataMigrationServer ==
Takes a total of 0x20-bytes of input, a type-0x19 input buffer, a TransferMemory handle, returns an [[#IServer_2|IServer]].
Takes an u32 tmem_size, an [[Account_services|Uid]], an [[NCM_services#ApplicationId|ApplicationId]], a type-0x19 input buffer containing a [[#ServerProfile|ServerProfile]], a TransferMemory handle, returns an [[#IServer_2|IServer]].


== ResumeSaveDataMigrationServer ==
== ResumeSaveDataMigrationServer ==
Takes an input u32, a TransferMemory handle, returns an [[#IServer_2|IServer]].
Takes an input u32 [[#Cmd1110|tmem_size]], a TransferMemory handle, returns an [[#IServer_2|IServer]].


== Cmd1110 ==
== Cmd1110 ==
Takes a total of 0x18-bytes of input, a type-0x19 input buffer, a type-0x5 input buffer containing an array of u64s, and a TransferMemory handle. Returns an [[#IServer_2|IServer]].
Takes an u32 tmem_size, an [[Account_services|Uid]], a type-0x19 input buffer containing a [[#ServerProfile|ServerProfile]], a type-0x5 input buffer containing an array of [[NCM_services#ApplicationId|ApplicationIds]], and a TransferMemory handle. Returns an [[#IServer_2|IServer]].


This is identical to [[#CreateSaveDataMigrationServer|CreateSaveDataMigrationServer]] except the u64-array is passed directly instead of from a single input u64.
This is identical to [[#CreateSaveDataMigrationServer|CreateSaveDataMigrationServer]] except the input array is passed directly instead of from a single input [[NCM_services#ApplicationId|ApplicationId]].
 
The tmem_size must be at least 0x504BB0.


== CreateSaveDataMigrationClient ==
== CreateSaveDataMigrationClient ==
Takes an input u32, a type-0x19 input buffer, a TransferMemory handle, returns an [[#IClient_2|IClient]].
Takes an input u32 tmem_size, a type-0x19 input buffer containing a [[#ClientProfile|ClientProfile]], a TransferMemory handle, returns an [[#IClient_2|IClient]].
 
The tmem_size must be at least 0x50EA80.


== ResumeSaveDataMigrationClient ==
== ResumeSaveDataMigrationClient ==
Takes an input u32, a TransferMemory handle, returns an [[#IClient_2|IClient]].
Takes an input u32 tmem_size, a TransferMemory handle, returns an [[#IClient_2|IClient]].


== Cmd2100 ==
== Cmd2100 ==
Line 163: Line 167:


== IServer ==
== IServer ==
This is "nn::migration::user::IServer"
This is "nn::migration::user::IServer".


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 198: Line 202:


== IClient ==
== IClient ==
This is "nn::migration::user::IClient"
This is "nn::migration::user::IClient".


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 296: Line 300:
| 2 || [[#GetServerProfile_2|GetServerProfile]]
| 2 || [[#GetServerProfile_2|GetServerProfile]]
|-
|-
| 3 || [17.0.0+] ListApplicationIds
| 3 || [17.0.0+] [[#ListApplicationIds|ListApplicationIds]]
|-
|-
| 100 || [[#PrepareAsync|PrepareAsync]]
| 100 || [[#PrepareAsync|PrepareAsync]]
Line 308: Line 312:
| 201 || GetClientProfile
| 201 || GetClientProfile
|-
|-
| 202 || [20.0.0+]
| 202 || [20.0.0+] [[#GetClientProductModel|GetClientProductModel]]
|-
|-
| 203 || [20.0.0+]
| 203 || [20.0.0+] [[#GetTransferEventId|GetTransferEventId]]
|-
|-
| 204 || [22.0.0+]
| 204 || [22.0.0+]
|-
|-
| 210 || [8.0.0+] WaitAcceptanceAsync
| 210 || [8.0.0+] [[#WaitAcceptanceAsync|WaitAcceptanceAsync]]
|-
|-
| 220 || [21.0.0+]  
| 220 || [21.0.0+]  
|-
|-
| 300 || ProcessTransferAsync
| 300 || [[#ProcessTransferAsync|ProcessTransferAsync]]
|-
|-
| 400 || CompleteAsync
| 400 || [[#CompleteAsync|CompleteAsync]]
|-
|-
| 500 || [7.0.0-19.0.1] Abort
| 500 || [7.0.0-19.0.1] [[#Abort|Abort]]
|-
|-
| 510 || [19.0.0-20.5.0]
| 510 || [19.0.0-20.5.0] [[#AbortAsync|AbortAsync]]
|-
|-
| 511 || [21.0.0+]
| 511 || [21.0.0+] [[#AbortAsync2|AbortAsync2]]
|-
|-
| 998 || [8.0.0+] DebugTryGetState
| 998 || [8.0.0+] DebugTryGetState
|-
|-
| 999 || [8.0.0+] DebugWaitStateSynchronizationFinalizedAsync
| 999 || [8.0.0+] [[#DebugWaitStateSynchronizationFinalizedAsync|DebugWaitStateSynchronizationFinalizedAsync]]
|}
|}


=== GetServerProfile ===
=== GetServerProfile ===
Takes a type-0x1A output buffer containing a [[#ServerProfile|ServerProfile]].
Takes a type-0x1A output buffer containing a [[#ServerProfile|ServerProfile]].
=== ListApplicationIds ===
Takes a s32 offset, a type-0x6 output buffer containing an array of [[NCM_services#ApplicationId|ApplicationIds]], returns a s32 total_out.


=== PrepareAsync ===
=== PrepareAsync ===
Line 342: Line 349:


[20.0.0+] The above policy functionality is no longer present. This now eventually uses network request [[Network|transfer_events/start]].
[20.0.0+] The above policy functionality is no longer present. This now eventually uses network request [[Network|transfer_events/start]].
The async task does the following (handling any errors as needed / calling any logging funcs as needed):
* ...
* Calls a vfunc, returns 0 if ret is true.
* Handles state cleanup if needed.
* Calls a func. This uses various [[OLSC_services|olsc:s]] functionality.
* Sets the state flag which was used for the above cleanup.
* Calls a func which uses [[NS_services#IManagerForSystemService|IManagerForSystemService]] GetAccountId. The output is unused.
* Calls a vfunc, returns 0 if ret is true.
* Calls a func. This handles initializing the contents of the fs file used with [[#GetList|GetList]] with the output of [[Filesystem_services|FindSaveDataWithFilter]] with SaveDataSpaceId = User. A state field is also updated.
* Handles [[Sockets_services|sockets]]/etc initialization, enters a nifm block.
* Uses network request [[Network|transfer_events/start]]. The output [[Network|transfer_event_id]] from this func is copied into state.
* Nifm usage ends here. Handles [[Sockets_services|sockets]] cleanup, etc.


=== Cmd102 ===
=== Cmd102 ===
Line 349: Line 369:
No input, returns an [[#IAsyncContext|IAsyncContext]].
No input, returns an [[#IAsyncContext|IAsyncContext]].


The async task does the following:
The async task does the following (handling any errors as needed / calling any logging funcs as needed):
* ...
* ...
* Initializes the data used for the AdvertiseData.
* Initializes the data used for the AdvertiseData.
Line 362: Line 382:
** Uses [[LDN_services|SetAdvertiseData]] with buf/size = 0 (empty AdvertiseData).
** Uses [[LDN_services|SetAdvertiseData]] with buf/size = 0 (empty AdvertiseData).
** Uses [[LDN_services|SetStationAcceptPolicy]] with value 1 (AlwaysReject).
** Uses [[LDN_services|SetStationAcceptPolicy]] with value 1 (AlwaysReject).
** Enters a loop waiting for a state field to become value 0x3. Two funcs are called repeatedly in this loop, with cleanup and return being handled if these return error.
** Enters a loop waiting for a state field to become value 0x3 (network message [[#Initialize|Initialize]]/[[#Initialize|Initialize2]] handled). Two funcs are called repeatedly in this loop. These essentially receive [[#Messages|messages]] + process them ([[#Data|Data]] messages not allowed), and send the response network message.
*** The first func receives socket data and handles it. The second func calls a func to get data for sending, then sends the socket data. Both have timeout etc handling.
** Updates state and returns 0.
** Updates state and returns 0.
* ...
* Calls a vfunc. When the ret is true:
** Calls a func, which receives [[#SendClientProfile|SendClientProfile]] and sends the response. Also calls a func to process the received data.
** Receives [[#RequestServerProfile|RequestServerProfile]] and sends the response.
* Otherwise when the vfunc ret is false:
** Calls a func identical to the first network-message func called in the above block, except a different func is called at the end.
** Runs the same [[#RequestServerProfile|RequestServerProfile]] handling as above.
** Calls a func. This calls a func, then receives [[#GetList|GetList]] and sends the response.
** Calls a func. This runs functionality similar to the earlier message-loop, except this waits for the state field to change to !=0x3 (any network message besides [[#Initialize|Initialize]]/[[#Initialize|Initialize2]] handled). The socket is closed, then a func is called which handles [[LDN_services|ldn]] + [[Sockets_services|sockets]] cleanup.
** Calls a func. This essentially calls the network-creation func from earlier, with the input AdvertiseData being the [[#AdvertiseData|Uuid]].
** Calls a func. This receives [[#Suspend|Suspend]] or [[#SynchronizeState|SynchronizeState]] and sends the response.
* Updates a state flag and returns 0.
 
The "nn::migration::device::IServer" version of this is roughly equivalent, except the funcs called after [[#RequestServerProfile|RequestServerProfile]] above are not used.
 
=== GetClientProductModel ===
Unofficial name.


=== Cmd202 ===
No input, returns an output u32.
No input, returns an output u32.


This loads a field from state.
This loads a field from state. This originates from the [[Settings_services#ProductModel|ProductModel]] field in the [[#SendClientProfile|SendClientProfile]] request.
 
=== GetTransferEventId ===
Unofficial name.


=== Cmd203 ===
No input, returns an output s64.
No input, returns an output s64.


This loads a field from state, with an error being returned when the value is -1.
This loads the [[#RequestServerProfile|transfer_event_id]] from state, with an error being returned when the value is -1.


=== Cmd204 ===
=== Cmd204 ===
No input, returns an output u64.
No input, returns an output [[NCM_services#ApplicationId|ApplicationId]].
 
This loads a field from state, with 0 being returned and out=0 when this is -1. Otherwise this is then used as an arrayindex to load the data copied to output (equivalent to [[#ListApplicationIds|ListApplicationIds]] with the state field as the offset).
 
=== WaitAcceptanceAsync ===
No input, returns an [[#IAsyncContext|IAsyncContext]].
 
The async task essentially (after state validation) just calls a func which handles receiving + responding to a network message ([[#AcceptDecline|AcceptDecline]] or [[#Suspend|Suspend]]).
 
=== Cmd220 ===
No input, returns a 0x10-byte struct.
 
This gets the 0x10-byte struct in state, which was received by [[#WaitAcceptanceAsync|WaitAcceptanceAsync]] from [[#AcceptDecline|AcceptDecline]] (originates from [[#DeclineAsync2|DeclineAsync2]]).
 
=== ProcessTransferAsync ===
No input, returns an [[#IAsyncContext|IAsyncContext]].


This loads a field from state, with 0 being returned and out=0 when this is -1. Otherwise this is then used as an arrayindex to load the data copied to output.
The async task does the following (handling any errors as needed / calling any logging funcs as needed):
* ...
* Verifies that the [[#WaitConnectionAsync|connected]] state flag is set.
* Calls a vfunc. When ret is false, call another vfunc.
* Call the above vfunc again. If the ret is still false, return an error.
* When a state flag is not set, calls a func.
** Calls a func. This receives [[#Suspend|Suspend]] or [[#PrepareTransferDisconnect2|PrepareTransferDisconnect2]] and sends the response.
** When the output bool from [[#PrepareTransferDisconnect2|PrepareTransferDisconnect2]] above is true, just return 0.
** Calls the same func as [[#WaitConnectionAsync|WaitConnectionAsync]], which has a message-loop + socket-closing and cleanup.
** Calls the same func as [[#TransferNextAsync|TransferNextAsync]] for using the network request.
** Abort if a state flag is not set.
** Calls the network-creation func from [[#WaitConnectionAsync|WaitConnectionAsync]], with the input AdvertiseData being the [[#AdvertiseData|Uuid]].
* Enters a loop.
** Calls a func.
*** Initializes stack data.
*** Calls a func. This receives [[#Suspend|Suspend]], [[#SynchronizeState|SynchronizeState]], or [[#PrepareTransfer2|PrepareTransfer2]] and sends the response. Besides handling Suspend afterwards, this also copies a field to output (this is the output s32 mentioned below).
*** Calls a vfunc. When ret is true, cleanup and return 0.
*** Receives [[#Transfer2|Transfer2]] and sends the response.
** Calls a vfunc. When ret is true, return 0.
** If the Result from the above func is within the fs range for ResultDataCorrupted, and the output s32 from the func is not negative:
*** When a state field is less than the above s32, set another state field to that s32.
*** Break from the loop.
** If the above func returned error, break from the loop.


=== Cmd510 ===
=== CompleteAsync ===
No input, returns an [[#IAsyncContext|IAsyncContext]].
 
The async task does the following (handling any errors as needed / calling any logging funcs as needed):
* ...
* When the [[#WaitConnectionAsync|connected]] state flag is set:
** Calls the same two funcs as [[#DebugWaitStateSynchronizationFinalizedAsync|DebugWaitStateSynchronizationFinalizedAsync]].
** Handles closing the socket (similar to elsewhere).
** Calls the func which handles cleanup for [[LDN_services|ldn]] + [[Sockets_services|sockets]].
** Clears the connected state flag.
* Calls a vfunc, throwing an error when ret is false.
* Calls a func. This essentially loops through the fs data also used by [[#GetList|GetList]], using [[NS_services|IApplicationManagerInterface DeleteSaveData]] with each entry (with the SaveDataId field from the entry and SaveDataSpaceId = User).
* Calls vfuncs, then returns.
 
=== Abort ===
No input/output.
 
This does the following (handling any errors as needed / calling any logging funcs as needed):
* ...
* Runs essentially the same code-block as [[#CompleteAsync|CompleteAsync]] for the connected flag, without the network messages.
* When a vfunc returns true, call the same savedata-deletion func as [[#CompleteAsync|CompleteAsync]].
* Calls vfuncs then returns.
 
=== AbortAsync ===
Unofficial name.
 
No input, returns an [[#IAsyncContext|IAsyncContext]].
 
On 19.x the async task is the cmd impl func used by [[#Abort|Abort]].
 
=== AbortAsync2 ===
Unofficial name.
 
Takes an input bool, returns an [[#IAsyncContext|IAsyncContext]].
 
This is roughly the same as [[#AbortAsync|AbortAsync]] except for the additional functionality done by the async task (etc), for when the input bool is set.
 
The async task does the following in that block when the input bool is false (handling any errors as needed / calling any logging funcs as needed):
* This block also only runs when a vfunc ret is false.
** Calls a func.
*** Calls a func to get the [[#GetTransferEventId|transfer_event_id]], if this wasn't set yet this just returns 0.
*** Calls a func.
*** Handles [[Sockets_services|sockets]]/etc initialization.
*** Enters a block which uses nifm.
*** Uses network request [[Network|transfer_events/%lld/abort_transfer]].
*** Nifm usage ends here. Handles [[Sockets_services|sockets]] cleanup, etc.
** If the above func returned an error, depending on the Result this may call a func which logs a report with [[PSC_services|srepo]]. If this code-path is used it also sets the Result returned later to 0.
 
=== DebugWaitStateSynchronizationFinalizedAsync ===
No input, returns an [[#IAsyncContext|IAsyncContext]].
No input, returns an [[#IAsyncContext|IAsyncContext]].


Besides other functionality, this async task uses network request [[Network|transfer_events/%lld/abort_transfer]].
The async task does the following (handling any errors as needed / calling any logging funcs as needed):
* ...
* Calls a func. This receives [[#Suspend|Suspend]] or [[#SynchronizeState|SynchronizeState]], then sends the response.
* Calls a func. This is basically identical to the above func, except a field written to stack is value 5 instead of 4.


== IClient ==
== IClient ==
Line 422: Line 545:
| 303 || GetApplicationId
| 303 || GetApplicationId
|-
|-
| 304 || [17.0.0+] ListApplicationIds
| 304 || [17.0.0+] [[#ListApplicationIds|ListApplicationIds]]
|-
|-
| 310 || GetCurrentTransferInfo
| 310 || GetCurrentTransferInfo
Line 428: Line 551:
| 320 || [[#TransferNextAsync|TransferNextAsync]]
| 320 || [[#TransferNextAsync|TransferNextAsync]]
|-
|-
| 350 || SuspendAsync
| 350 || [[#SuspendAsync|SuspendAsync]]
|-
|-
| 400 || [[#CompleteAsync|CompleteAsync]]
| 400 || [[#CompleteAsync_2|CompleteAsync]]
|-
|-
| 500 || [7.0.0-19.0.1] Abort
| 500 || [7.0.0-19.0.1] [[#Abort_2|Abort]]
|-
|-
| 510 || [19.0.0+]
| 510 || [19.0.0+] [[#AbortAsync_2|AbortAsync]]
|-
|-
| 996 || [8.0.0+] DebugTryGetState
| 996 || [8.0.0+] DebugTryGetState
|-
|-
| 997 || [8.0.0+] DebugSynchronizeStateInFinalization0Async
| 997 || [8.0.0+] [[#DebugSynchronizeStateInFinalization0Async|DebugSynchronizeStateInFinalization0Async]]
|-
|-
| 998 || [8.0.0+] DebugSynchronizeStateInFinalization1Async
| 998 || [8.0.0+] [[#DebugSynchronizeStateInFinalization1Async|DebugSynchronizeStateInFinalization1Async]]
|-
|-
| 999 || DebugSynchronizeStateFinalizedAsync
| 999 || [[#DebugSynchronizeStateFinalizedAsync|DebugSynchronizeStateFinalizedAsync]]
|}
|}


Line 452: Line 575:


=== ListServers ===
=== ListServers ===
Takes a type-0x6 output buffer containing an array of a 0x128-byte struct, returns an output u32 total_out.
[20.0.0+] The struct size is now 0x130-bytes instead of 0x128-bytes.
[20.0.0+] The struct size is now 0x130-bytes instead of 0x128-bytes.


Line 460: Line 585:


* ...
* ...
* Calls a func.
* Calls the same olsc func as [[#PrepareAsync|PrepareAsync]].
* ...
* ...
* Loops through an array to find an entry matching the 0x10-bytes from {func input param}, returning an error if not found. (entryptr is the located entry once done)
* Loops through an array to find an entry matching the 0x10-bytes from {func input param}, returning an error if not found. (entryptr is the located entry once done)
Line 478: Line 603:
*** The funcs called from the above flag-block are roughly same. These send a message, handle receiving a message, then send another message + receive a message. For flag==false these are [[#Messages|MessageId]] 0x0/0x1, while for flag==true these are MessageId 0x8/0x9.
*** The funcs called from the above flag-block are roughly same. These send a message, handle receiving a message, then send another message + receive a message. For flag==false these are [[#Messages|MessageId]] 0x0/0x1, while for flag==true these are MessageId 0x8/0x9.
** A func is called which copies 0x10-bytes from entryptr+0x10+0x26C into state.
** A func is called which copies 0x10-bytes from entryptr+0x10+0x26C into state.
* Clears stack. Calls a func.
* Calls a func to get the data which is sent with SendClientProfile below.
* Calls a func. This sends [[#ClientProfileNetwork|ClientProfileNetwork]], then receives network data.
* Calls a func. This uses network message [[#SendClientProfile|SendClientProfile]].
* Calls a vfunc.
* Calls a vfunc.
* When ret is true:
* When ret is true:
** Calls a func. This essentially handles sending/receiving network data, etc.
** Calls a func. This uses network message [[#RequestServerProfile|RequestServerProfile]].
** Sets a state flag, then returns 0.
** Sets a state flag, then returns 0.
* Otherwise when the vfunc ret is false:
* Otherwise when the vfunc ret is false:
** Calls a func. This is very similar to the func used in the above block.
** Calls a func. This is very similar to the func for RequestServerProfile used above.
** Calls a func.
** Calls a func.
*** Clears stack, and calls a func.
*** Clears stack, and calls a func.
*** Calls a func which handles sending/receiving network data.
*** Uses network message [[#GetList|GetList]].
*** Calls a func.
*** Calls a func.
** Calls a func. (fs related?)
** Calls a func. This loops through the fs data previously received by [[#GetList|GetList]], to sum the size field (entry+0x4) with each entry. Once finished this updates state then writes data into a separate fs file.
** Calls a func. This sends a network message with [[#Messages|MessageId]] 0x10, then receives network data. Then the socket is closed. Lastly, a func is called which handles closing [[LDN_services|ldn]] (CloseStation, Finalize) and [[Sockets_services|sockets]].
** Calls a func. This uses network message [[#Leave|Leave]]. Then the socket is closed. Lastly, a func is called which handles closing [[LDN_services|ldn]] (CloseStation, Finalize) and [[Sockets_services|sockets]].
** Calls a func.
** Calls a func.
** Clears stack. Enters a block which uses nifm.
** Clears stack. Enters a block which uses nifm.
Line 502: Line 627:
** Calls a func which handles [[Sockets_services|sockets]] cleanup.
** Calls a func which handles [[Sockets_services|sockets]] cleanup.
** Calls a func. This essentially does ldn setup again / reconnects to the network. The previously mentioned network messages func used after connection is then called here, with the bool flag being previously updated it would now use MessageId 0x8/0x9. State is also updated.
** Calls a func. This essentially does ldn setup again / reconnects to the network. The previously mentioned network messages func used after connection is then called here, with the bool flag being previously updated it would now use MessageId 0x8/0x9. State is also updated.
** Calls a func. This sends data via a [[#Data|Data]] transfer, then receives network data. Afterwards it then calls a vfunc.
** Calls a func. This uses network message [[#SynchronizeState|SynchronizeState]] with value 1. Afterwards it then calls a vfunc.
** Sets a state flag, then returns 0.
** Sets a state flag, then returns 0.


Line 534: Line 659:
* ...
* ...
* Calls vfuncs.
* Calls vfuncs.
* Calls various funcs to check state. This includes calling a fs-related func?
* Calls various funcs to check state. Depending on state this may call a func which:
* If a state flag is not set, a func is called: (this flag is usually set?)
** Loops through the fs data previously received by [[#GetList|GetList]]. With each entry, calls a func. This uses [[Filesystem_services|FindSaveDataWithFilter]] and uses the output with [[NS_services|IApplicationManagerInterface DeleteSaveData]].
** Uses network message [[#PrepareTransferDisconnect|PrepareTransferDisconnect]].
* If a state flag is not set, a func is called:
** If the state fields checked by [[#PrepareTransferDisconnect|PrepareTransferDisconnect]] for header+0x1 match, this just returns.
** Uses network message [[#PrepareTransferDisconnect2|PrepareTransferDisconnect2]].
** If the state fields checked by [[#PrepareTransferDisconnect2|PrepareTransferDisconnect2]] for header+0x1 match, this just returns.
** Calls the func which handles disconnecting from the network.
** Calls the func which handles disconnecting from the network.
** Calls two funcs.
** Calls two funcs.
Line 543: Line 669:
** Calls the func which handles reconnecting to the network.
** Calls the func which handles reconnecting to the network.
* Calls a func when a previously loaded state field is 0.
* Calls a func when a previously loaded state field is 0.
* Calls a func. (fs related?)
* Calls a func to read the current entry, from the fs data previously received with [[#GetList|GetList]].
* Calls a func which validates the output from an eariler func.
* Calls a func which validates the entry data output from the above func (u8 +0x0).
* Logs a report with [[PSC_services|srepo]].
* Logs a report with [[PSC_services|srepo]]. Includes data from the above entry, etc.
* Uses network message [[#PrepareTransfer|PrepareTransfer]]. Uses [[Filesystem_services|ISaveDataTransferManagerWithDivision SetLocalKeySeedPackage]] with the relevant response data.
* Uses network message [[#PrepareTransfer2|PrepareTransfer2]]. Uses [[Filesystem_services|ISaveDataTransferManagerWithDivision SetLocalKeySeedPackage]] with the relevant response data.
* Logs a report with [[PSC_services|srepo]]. "tr_size" is logged by adding two u64 fields from the 0x20-byte struct returned by [[#PrepareTransfer|PrepareTransfer]]: +0x18 and +0x8.
* Logs a report with [[PSC_services|srepo]]. "tr_size" is logged by adding two u64 fields from the 0x20-byte struct returned by [[#PrepareTransfer2|PrepareTransfer2]]: +0x18 and +0x8.
* Calls a func.
* Calls a func, for loading data from the above entry.
* Calls a func for getting state data.
* Calls a func for getting state data.
* Calls a func which writes the various input to the output struct. This is then passed to the the following func which is called:
* Calls a func which writes the various input to the output struct. This is then passed to the the following func which is called:
** Calls a func.
** Calls a func.
** Uses network message [[#TransferNext|TransferNext]].
** Uses network message [[#Transfer2|Transfer2]].
** Calls a func.
** Calls a func.
* Logs a report with [[PSC_services|srepo]].
* Logs a report with [[PSC_services|srepo]].
* If the output from two funcs which load state match, calls a func.
* If the output from two funcs which load state match, calls a func.
* Cleanup, then returns.
* Cleanup, then returns.
=== SuspendAsync ===
No input, returns an [[#IAsyncContext|IAsyncContext]].
The async task essentially just verifies the [[#ConnectByServerIdAsync|connected]] state flag is set, uses network message [[#Suspend|Suspend]], closes the socket, calls the func which closes [[LDN_services|ldn]] + [[Sockets_services|sockets]], then clears the connected state flag.


=== CompleteAsync ===
=== CompleteAsync ===
No input, returns an [[#IAsyncContext|IAsyncContext]].
No input, returns an [[#IAsyncContext|IAsyncContext]].


Besides other functionality, this async task uses network request [[Network|transfer_events/%lld/finish_transfer]].
This async task does the following (handling any errors as needed / calling any logging funcs as needed):
 
* ...
* Calls a vfunc. When the ret is false:
** Throws an error if not [[#ConnectByServerIdAsync|connected]].
** If a state flag is not set, calls the same func as [[#TransferNextAsync|TransferNextAsync]] for generate_key_seed_package.
** Calls the same func as [[#DebugSynchronizeStateInFinalization0Async|DebugSynchronizeStateInFinalization0Async]].
** Calls the same func as [[#DebugSynchronizeStateInFinalization1Async|DebugSynchronizeStateInFinalization1Async]].
** Calls the same func as [[#DebugSynchronizeStateFinalizedAsync|DebugSynchronizeStateFinalizedAsync]].
** Calls a func which handles disconnecting from the network, without using the network message. This also clears the connected state flag.
* Calls the same vfunc as above again, throwing an error if the ret is still false.
* Handles [[Sockets_services|sockets]] initialization.
* Calls a func.
* Enters a block which uses nifm.
* Calls a func which uses network request [[Network|transfer_events/%lld/finish_transfer]].
* Nifm usage ends here.
* Handles cleanup for [[Sockets_services|sockets]].
* When successful at this point it runs the following block:
** Calls a func. This loops through the fs data previously received by [[#GetList|GetList]]. With each entry, uses [[NS_services|IApplicationVersionInterface UpgradeLaunchRequiredVersion]] with the data from the entry. Uses [[OLSC_services|IRemoteStorageController]] cmd28, then when successful uses [[OLSC_services|IRemoteStorageController GetLoadedDataInfo]]. Then [[OLSC_services|IOlscServiceForSystemService SetUserSaveDataProperty]] is used with the entry data. Once finished, it also uses [[OLSC_services|IDaemonController RunTransferTaskAutonomyRegistration]].
** Calls a func.
** Calls a func, which logs a report with [[PSC_services|srepo]].
** Calls a vfunc.
** Calls a vfunc (with locking before/after).
 
=== Abort ===
No input/output.
 
When the [[#ConnectByServerIdAsync|connected]] state flag is set, this runs code similar to [[#SuspendAsync|SuspendAsync]], without the network message. Then:
* Calls a vfunc. When ret is true:
** Calls a vfunc. When ret is true, calls the same func for UpgradeLaunchRequiredVersion as [[#CompleteAsync_2|CompleteAsync]].
** Otherwise when false:
*** Calls a vfunc.
*** Calls the same savedata-deletion func as [[#TransferNextAsync|TransferNextAsync]].
* Calls vfuncs.
 
=== AbortAsync ===
Unofficial name.
 
No input, returns an [[#IAsyncContext|IAsyncContext]].
 
On 19.x the async task is the cmd impl func used by [[#Abort_2|Abort]].
 
=== DebugSynchronizeStateInFinalization0Async ===
No input, returns an [[#IAsyncContext|IAsyncContext]].
 
Besides various validation, the async task essentially just calls a func which calls vfuncs and uses network message [[#SynchronizeState|SynchronizeState]] with value 3.
 
=== DebugSynchronizeStateInFinalization1Async ===
No input, returns an [[#IAsyncContext|IAsyncContext]].
 
Besides various validation, the async task essentially just calls a func which calls vfuncs and uses network message [[#SynchronizeState|SynchronizeState]] with value 4.
 
=== DebugSynchronizeStateFinalizedAsync ===
No input, returns an [[#IAsyncContext|IAsyncContext]].
 
Besides various validation, the async task essentially just calls a func which calls a vfunc and uses network message [[#SynchronizeState|SynchronizeState]] with value 5.


== IServer ==
== IServer ==
Line 600: Line 786:
| 130 ||  
| 130 ||  
|-
|-
| 200 ||  
| 200 || [[#WaitConnectionAsync|WaitConnectionAsync]]
|-
|-
| 230 ||  
| 230 ||  
Line 606: Line 792:
| 290 ||  
| 290 ||  
|-
|-
| 300 ||  
| 300 || ProcessTransferAsync
|-
|-
| 400 ||  
| 400 || CompleteAsync
|-
|-
| 510 || [20.0.0-20.5.0]
| 510 || [20.0.0-20.5.0]
|-
|-
| 511 || [21.0.0+]
| 511 || [21.0.0+] AbortAsync2
|-
|-
| 900 ||  
| 900 ||  
Line 834: Line 1,020:
= Protocol =
= Protocol =
Once connected with [[LDN_services|ldn]], the client node connects to the server with TCP port 441.
Once connected with [[LDN_services|ldn]], the client node connects to the server with TCP port 441.
With [20.0.0+] [[LDN_services|ldn]] SetProtocol is used with a state field (Protocol3). It's unknown whether the protocol changes later during S2-only savedata transfer.


== AdvertiseData ==
== AdvertiseData ==
Line 840: Line 1,028:
The salt used with hashing below is the same regardless of the AdvertiseData.
The salt used with hashing below is the same regardless of the AdvertiseData.


Later the server also sets the AdvertiseData to {0x10-byte Uuid previously used below}.
Immediately after a socket connection is setup, the AdvertiseData is set to size=0. Later when the server recreates the network it also sets the AdvertiseData to {0x10-byte Uuid previously used below}.


The Uuid used below is generated with <code>nn::util::GenerateUuid</code>.
The Uuid used below is generated with <code>nn::util::GenerateUuid</code>.
Line 998: Line 1,186:
| 0x8 || 0x20 || Data from [[SPL_services|GenerateRandomBytes]].
| 0x8 || 0x20 || Data from [[SPL_services|GenerateRandomBytes]].
|-
|-
| 0x28 || 0x10 || CMAC ([[SPL_services|ComputeCmac]]) over the above 0x20-bytes, followed by 0x20-bytes from state (the first state u8 here is set to 0x1).
| 0x28 || 0x10 || CMAC ([[SPL_services|ComputeCmac]]) over the above 0x20-bytes, followed by 0x20-bytes from state (zeros).
|}
|}


Line 1,007: Line 1,195:
! Offset || Size || Description
! Offset || Size || Description
|-
|-
| 0x8 || 0x10 || Ctr for [[SPL_services|ComputeCtr]] with the below data.
| 0x8 || 0x10 || Ctr for [[SPL_services|ComputeCtr]] with the below data (generated with [[SPL_services|GenerateRandomBytes]]).
|-
|-
| 0x18 || 0x70 || Data encrypted with [[SPL_services|ComputeCtr]].
| 0x18 || 0x70 || Data encrypted with [[SPL_services|ComputeCtr]].
Line 1,020: Line 1,208:
! Offset || Size || Description
! Offset || Size || Description
|-
|-
| 0x0 || 0x28 || Passphrase for key derivation.
| 0x0 || 0x28 || Passphrase for key derivation (generated with [[SPL_services|GenerateRandomBytes]]).
|-
|-
| 0x28 || 0x20 || Must match the random data previously sent by the client.
| 0x28 || 0x20 || Must match the random data previously sent by the client.
|-
|-
| 0x48 || 0x20 ||  
| 0x48 || 0x20 || Data from [[SPL_services|GenerateRandomBytes]].
|-
|-
| 0x68 || 0x8 ||  
| 0x68 || 0x8 || Zeros
|}
|}


Line 1,069: Line 1,257:
! Offset || Size || Description
! Offset || Size || Description
|-
|-
| 0x0 || 0x14 ||  
| 0x0 || 0x20 || Must match the 0x20-bytes which is used for [[#KeyExchange|KeyExchange]] response_plaintext+0x48.
|}
|}


Line 1,096: Line 1,284:
| 0x0 || 0x20 || Must match the random data previously sent by the client.
| 0x0 || 0x20 || Must match the random data previously sent by the client.
|-
|-
| 0x20 || 0x20 || Copied into state by the client.
| 0x20 || 0x20 || Data from [[SPL_services|GenerateRandomBytes]]. Copied into state by the client.
|}
|}


Line 1,139: Line 1,327:
! DataMessageId (client/server) || Description
! DataMessageId (client/server) || Description
|-
|-
| 0x10 ||  
| 0x10 || [[#RequestServerProfile|RequestServerProfile]]
|-
|-
| 0x11 || [[#ClientProfileNetwork|ClientProfileNetwork]]
| 0x11 || [[#SendClientProfile|SendClientProfile]]
|-
|-
| 0x12 ||  
| 0x12 || [[#GetList|GetList]]
|-
|-
| 0x20 || [[#AcceptDecline|AcceptDecline]]
| 0x20 || [[#AcceptDecline|AcceptDecline]]
|-
|-
| 0xE0 || [[#PrepareTransferDisconnect|PrepareTransferDisconnect]]
| 0x80 || [[#Transfer|Transfer]]
|-
| 0x90 || [21.0.1+]
|-
| 0x91 || [-21.0.0]
|-
| 0xE0 || [20.0.0+] [[#PrepareTransferDisconnect2|PrepareTransferDisconnect2]]
|-
| 0xE1 || [20.0.0+] [[#PrepareTransfer2|PrepareTransfer2]]
|-
| 0xE2 || [20.0.0+] [[#Transfer2|Transfer2]]
|-
|-
| 0xE1 || [[#PrepareTransfer|PrepareTransfer]]
| 0xF0 || [[#SynchronizeState|SynchronizeState]]
|-
|-
| 0xE2 || [[#TransferNext|TransferNext]]
| 0xFF || [[#Suspend|Suspend]]
|}
|}


==== 0x10 ====
==== RequestServerProfile ====
This is sent by the client following the last vfunc call in [[#ConnectByServerIdAsync|ConnectByServerIdAsync]] where the bool ret is checked, with both true/false blocks. This follows [[#ClientProfileNetwork|ClientProfileNetwork]].
This is sent by the client following the last vfunc call in [[#ConnectByServerIdAsync|ConnectByServerIdAsync]] where the bool ret is checked, with both true/false blocks. This follows [[#SendClientProfile|SendClientProfile]].


The request only contains the header.
The request only contains the header.


Response payload:
Response payload (0x140-byte struct ([20.0.0+] 0x148-byte), which has the following layout):
 
[20.0.0+]:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,165: Line 1,365:
! Offset || Size || Description
! Offset || Size || Description
|-
|-
| 0x0 || 0x148 ||  
| 0x0 || 0x1 || Value 0x1.
|-
| 0x1 || 0x7 || Zeros
|-
| 0x8 || 0x4 || SystemVersion (same form as [[#SendClientProfile|SendClientProfile]])
|-
| 0xC || 0x10 || Uuid (from <code>nn::util::GenerateUuid()</code>)
|-
| 0x1C || 0x100 || [[#ServerProfile|ServerProfile]]
|-
| 0x11C || 0x4 || ApplicationIdsCount (arraycount from [[#Cmd1110]])
|-
| 0x120 || 0x8 || ApplicationId (first ApplicationId from the input array)
|-
| 0x128 || 0x8 || [[Network|transfer_event_id]] from the [[#PrepareAsync|/start]] request, then used with the remaining network requests.
|-
| 0x130 || 0x4 || Value 0x1.
|-
| 0x134 || 0x10 || Zeros with savedata.
|-
| 0x144 || 0x4 || [[Settings_services#GetProductModel|ProductModel]]
|}
|}


When the aforementioned vfunc returned true, the client only uses the response data for verifying that various fields match state. Otherwise when false the client uses this data.
With "nn::migration::device::" this is a 0x398-byte struct.


==== ClientProfileNetwork ====
When the aforementioned vfunc returned true, the client only uses the response data for verifying that various fields match state. Otherwise when false the client uses this data, in which case:
* The ProductModel must be within [[Settings_services#ProductModel|range]] for Nintendo Switch / Nintendo Switch 2.
* Calls the same func as [[#SendClientProfile|SendClientProfile]] with the above SystemVersion and ProductModel, returning an error if false is returned.
* The u8 at +0x0 must match 0x1.
* The Uuid must be non-zero.
* The ApplicationIdsCount must be 0x1-0x800.
* The ApplicationId must match state.
* Copies data into state, etc.
 
==== SendClientProfile ====
Request payload:
Request payload:


Line 1,179: Line 1,408:
| 0x0 || 0x100 || [[#ClientProfile|ClientProfile]]
| 0x0 || 0x100 || [[#ClientProfile|ClientProfile]]
|-
|-
| 0x100 || 0x4 ||  
| 0x100 || 0x4 || [20.0.0+] [[Settings_services#GetProductModel|ProductModel]]
|-
|-
| 0x100 || 0x4 ||  
| 0x104 || 0x4 || [20.0.0+] [[System_Version_Title|SystemVersion]]. u32 in the form: <code>(Major<<16) | (Minor<<8) | Micro</code>
|}
|}
With "nn::migration::device::" this is a 0x218-byte struct.


The response only contains the header.
The response only contains the header.


==== 0x12 ====
When the vfunc ret at the end of [[#WaitConnectionAsync|WaitConnectionAsync]] is true, the server just validates that the first 0x104-bytes of the above request match state. Otherwise when that vfunc ret is false:
This is sent by the client following DataMessageId 0x10, where the vfunc mentioned there returned false.
* The ProductModel must be within [[Settings_services#ProductModel|range]] for Nintendo Switch / Nintendo Switch 2.
* Calls a func with the request SystemVersion and ProductModel, returning an error if false is returned.
** If <ProductModel is within the range for Nintendo Switch>: return SystemVersion == <SystemVersion value loaded for the server system>.
** Otherwise: return SystemVersion > <min_sysver> && <ProductModel is within the range for Nintendo Switch 2>.
* Then the ClientProfile/ProductModel are copied into state, etc.
 
==== GetList ====
This is sent by the client following [[#RequestServerProfile|RequestServerProfile]], where the vfunc mentioned there returned false.


The request only contains the header.
The request only contains the header.


Response payload:
Initial response payload, after the header:
 
{| class="wikitable" border="1"
|-
! Offset || Size || Description
|-
| 0x0 || 0x4 || Data size.
|}
 
The remaining messages contain the data payload with the above size.
 
This is used for transferring the contents of a fs file (server reads from it, client writes to a file).
 
With savedata this is an array where each entry is 0x50-bytes, where each entry has the structure (converted from [[Filesystem_services#SaveDataInfo|SaveDataInfo]]):


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,197: Line 1,448:
! Offset || Size || Description
! Offset || Size || Description
|-
|-
| 0x0 || 0xC ||  
| 0x0 || 0x1 || Type: 0 = SaveDataType System, 1 = SaveDataType Account, 2 = SaveDataType Device. >=3 is invalid.
|-
| 0x1 || 0x1 || IsAccountIdValid (whether loading the AccountId was successful when UserId is non-zero)
|-
| 0x4 || 0x4 || Output from sdk func <code>nn::fs::QuerySaveDataRequiredSizeForImport</code> (on NX the input PlatformIdForSaveDataSizeCalculation is 1).
|-
| 0x8 || 0x8 || SaveDataId
|-
| 0x10 || 0x10 || UserId
|-
| 0x20 || 0x8 || AccountId ([[NS_services#IManagerForSystemService|IManagerForSystemService]] GetAccountId with the UserId, when loaded successfully where UserId is set)
|-
| 0x28 || 0x8 || ApplicationId (for System, SystemSaveDataId)
|-
| 0x30 || 0x4 || [[NS_services#IApplicationVersionInterface|LaunchRequiredVersion]] (from GetLaunchRequiredVersion, for Account/Device)
|-
|-
| 0xC || Varies ||  
| 0x38 || 0x18 || [[OLSC_services#UserSaveDataProperty|UserSaveDataProperty]], from GetUserSaveDataProperty. Only with Account. If IsAccountIdValid is not set this is cleared instead.
|}
|}
[[Filesystem_services|GetSaveDataCommitId]] is also used when initializing the above entry, however the output is unused besides returning the Result on failure.


==== AcceptDecline ====
==== AcceptDecline ====
Line 1,221: Line 1,488:
|-
|-
| 0x0 || 0x10 || Input from [[#DeclineAsync2|DeclineAsync2]] (zeros with Accept).
| 0x0 || 0x10 || Input from [[#DeclineAsync2|DeclineAsync2]] (zeros with Accept).
|-
| 0x10 || 0x8 || Zeros
|}
|}


Line 1,236: Line 1,501:
The response only contains the header.
The response only contains the header.


=== PrepareTransferDisconnect ===
==== Transfer ====
The client sends this via [[#TransferNextAsync|TransferNextAsync]] if a state flag is not set, prior to temporarily disconnecting from the network for using network request [[Network|transfer_events/%lld/save_datas/%lld/generate_key_seed_package]] (this usually not used since the state flag is already set?).
The client sends this via [[#TransferNextAsync|TransferNextAsync]]. With [20.0.0+] PrepareTransfer2/etc are used instead, for savedata.
 
The request only contains the header, which besides the DataMessageId contains:
 
{| class="wikitable" border="1"
|-
! Offset || Size || Description
|-
| 0x4 || 0x4 || s32, must not be negative. Index in the fs data used by [[#GetList|GetList]].
|}
 
The initial response payload message following the header contains:
 
{| class="wikitable" border="1"
|-
! Offset || Size || Description
|-
| 0x0 || 0x8 ||
|-
| 0x8 || 0x8 ||
|}
 
The remaining messages contain the transfer payload.
 
The transfer payload is the output from exporting savedata from [[Filesystem_services|ISaveDataTransferManager]], which the client then imports.
 
==== 0x90 ====
This replaced DataMessageId 0x91 with [21.0.1+]. This is used by "nn::migration::device::IServer" ProcessTransferAsync.
 
The request only contains the header, which besides the DataMessageId contains:
 
{| class="wikitable" border="1"
|-
! Offset || Size || Description
|-
| 0x4 || 0x4 || s32, must not be negative.
|}
 
The initial response following the header contains:
 
{| class="wikitable" border="1"
|-
! Offset || Size || Description
|-
| 0x0 || 0x8 || Data size.
|}
 
The remaining messages contain the data payload with the above size.
 
This is used for transferring the contents of a fs file. (Album related)
 
==== 0x91 ====
This was replaced by DataMessageId 0x90 with [21.0.1+]. This is used by "nn::migration::device::IServer" ProcessTransferAsync.
 
The request only contains the header, which besides the DataMessageId contains:
 
{| class="wikitable" border="1"
|-
! Offset || Size || Description
|-
| 0x8 || 0x4 || s32
|-
| 0xC || 0x4 || s32
|}
 
Initial response data, after the DataMessageId:
 
{| class="wikitable" border="1"
|-
! Offset || Size || Description
|-
| 0x4 || 0x4 || Data size.
|}
 
The remaining messages contain the data payload with the above size.
 
This is used for transferring the contents of a fs file. (Album related)
 
==== PrepareTransferDisconnect2 ====
The client sends this via [[#TransferNextAsync|TransferNextAsync]] if a state flag is not set, prior to temporarily disconnecting from the network for using network request [[Network|transfer_events/%lld/save_datas/%lld/generate_key_seed_package]].


The request only contains the 0x4-byte header. The u8 at header+0x1 (after DataMessageId) is set to <code>statefield==other_statefield</code>.
The request only contains the 0x4-byte header. The bool at header+0x1 (after DataMessageId) is set to <code>statefield==other_statefield</code>. This bool indicates whether the previously mentioned network request is used, with false indicating the request is used.


The response only contains the 0x4-byte header.
The response only contains the 0x4-byte header.


=== PrepareTransfer ===
==== PrepareTransfer2 ====
The client sends this via [[#TransferNextAsync|TransferNextAsync]] (after [[#PrepareTransferDisconnect|PrepareTransferDisconnect]] if using it was enabled).
The client sends this via [[#TransferNextAsync|TransferNextAsync]] (after [[#PrepareTransferDisconnect2|PrepareTransferDisconnect2]] if using it was enabled).
 
The request only contains the header, which besides the DataMessageId contains:


The request only contains the header.
{| class="wikitable" border="1"
|-
! Offset || Size || Description
|-
| 0x4 || 0x4 || s32, must not be negative. Index in the fs data used by [[#GetList|GetList]].
|}


Response payload:
Response payload:
Line 1,254: Line 1,605:
! Offset || Size || Description
! Offset || Size || Description
|-
|-
| 0x0 || 0x20 ||  
| 0x0 || 0x20 || Below struct.
|-
|-
| 0x20 || 0x200 || [[Filesystem_services|LocalKeySeedPackage]]
| 0x20 || 0x200 || [[Filesystem_services|LocalKeySeedPackage]]
|}
|}


=== TransferNext ===
The 0x20-byte struct has the following layout:
The client sends this via [[#TransferNextAsync|TransferNextAsync]], after [[#PrepareTransfer|PrepareTransfer]].
 
{| class="wikitable" border="1"
|-
! Offset || Size || Description
|-
| 0x8 || 0x8 || InitialDataVersion2 size
|-
| 0x18 || 0x8 || Size
|}
 
==== Transfer2 ====
The client sends this via [[#TransferNextAsync|TransferNextAsync]], after [[#PrepareTransfer2|PrepareTransfer2]].


The request only contains the header.
The request only contains the header.


Response payload:
The initial response payload message following the header contains:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,270: Line 1,632:
! Offset || Size || Description
! Offset || Size || Description
|-
|-
| 0x0 || 0x8 || Zeros?
| 0x0 || 0x8 || Zeros
|}
 
The remaining messages contain the transfer payload.
 
The transfer payload is the output from exporting savedata from [[Filesystem_services|ISaveDataTransferManagerWithDivision]], which the client then imports. This begins with the [[Filesystem_services|InitialDataVersion2]] (with size from [[#PrepareTransfer2|PrepareTransfer2]]), with the rest being the savedata-export.
 
==== SynchronizeState ====
The client sends this via [[#ConnectByServerIdAsync|ConnectByServerIdAsync]], [[#CompleteAsync_2|CompleteAsync]], and DebugSynchronizeState*.
 
Request payload:
 
{| class="wikitable" border="1"
|-
! Offset || Size || Description
|-
|-
| 0x8 || Rest of message || Transfer payload.
| 0x0 || 0x4 || See below.
|}
|}


With savedata the transfer payload is the output from exporting savedata from [[Filesystem_services|ISaveDataTransferManagerWithDivision]] (see [[#Notes]]), which the client then imports.
{| class="wikitable" border="1"
|-
! Value || Description
|-
| 1 || Can be used by [[#ConnectByServerIdAsync|ConnectByServerIdAsync]].
|-
| 3 || [[#DebugSynchronizeStateInFinalization0Async|InFinalization0]]
|-
| 4 || [[#DebugSynchronizeStateInFinalization1Async|InFinalization1]]
|-
| 5 || [[#DebugSynchronizeStateFinalizedAsync|Finalized]]
|}
 
The response only contains the header.
 
==== Suspend ====
The client sends this via [[#SuspendAsync|SuspendAsync]]
 
The request only contains the header.
 
The response only contains the header.
 
The server returns an error following handling Suspend.


=== Error ===
=== Error ===
Line 1,294: Line 1,692:
Savedata transfer ("nn::migration::savedata::IServer"/"nn::migration::savedata::IClient") requires that accounts are linked to the same network-account. acc:su [[NS_services#IManagerForSystemService|IManagerForSystemService]] GetAccountId is used here. The server stores this Id into state, and also stores it in the [[LDN_services|ldn]] AdvertiseData. The client also verifies the Id in AdvertiseData against the Id from GetAccountId for the account(s) being used, during ScanServersAsync.
Savedata transfer ("nn::migration::savedata::IServer"/"nn::migration::savedata::IClient") requires that accounts are linked to the same network-account. acc:su [[NS_services#IManagerForSystemService|IManagerForSystemService]] GetAccountId is used here. The server stores this Id into state, and also stores it in the [[LDN_services|ldn]] AdvertiseData. The client also verifies the Id in AdvertiseData against the Id from GetAccountId for the account(s) being used, during ScanServersAsync.


[20.0.0+] [[Filesystem_services#ISaveDataTransferManagerWithDivision|ISaveDataTransferManagerWithDivision]] is now used instead of ISaveDataTransferManager. Besides [[Filesystem_services|SetKeySeedPackage]] being used now as needed, [[Filesystem_services|SetLocalKeySeedPackage]] is now used by the relevant functionality in [[#IClient_2|IClient]] TransferNextAsync.
[20.0.0+] [[Filesystem_services#ISaveDataTransferManagerWithDivision|ISaveDataTransferManagerWithDivision]] is now used (from "nn::migration::savedata::*") instead of ISaveDataTransferManager. Besides [[Filesystem_services|SetKeySeedPackage]] being used now as needed, [[Filesystem_services|SetLocalKeySeedPackage]] is now used by the relevant functionality in [[#IClient_2|IClient]] TransferNextAsync.


[S2] System Settings now has an additional menu once send-savedata is selected, for selecting whether to send to Nintendo Switch or Nintendo Switch 2. If Switch 2 is selected, and there's a S1 receiving, the S2 will display a message regarding wrong target system. The receiving system also displays an error. Note that savedata for S2-only applications is only listed with the dest-S2 option (S1-applications are also listed).
[S2] System Settings now has an additional menu once send-savedata is selected, for selecting whether to send to Nintendo Switch or Nintendo Switch 2. If Switch 2 is selected, and there's a S1 receiving, the S2 will display a message regarding wrong target system. The receiving system also displays an error. Note that savedata for S2-only applications is only listed with the dest-S2 option (S1-applications are also listed).


[[Category:Services]]
[[Category:Services]]