diff --git a/nx/include/switch/services/async.h b/nx/include/switch/services/async.h index 98f68488..ae441f44 100644 --- a/nx/include/switch/services/async.h +++ b/nx/include/switch/services/async.h @@ -64,6 +64,7 @@ Result asyncValueCancel(AsyncValue *a); /** * @brief Gets the \ref ErrorContext. + * @note Only available on [4.0.0+]. * @param a \ref AsyncValue * @param[out] context \ref ErrorContext */ @@ -104,6 +105,7 @@ Result asyncResultCancel(AsyncResult *a); /** * @brief Gets the \ref ErrorContext. + * @note Only available on [4.0.0+]. * @param a \ref AsyncResult * @param[out] context \ref ErrorContext */ diff --git a/nx/include/switch/services/ns.h b/nx/include/switch/services/ns.h index 3268fdcb..163460bc 100644 --- a/nx/include/switch/services/ns.h +++ b/nx/include/switch/services/ns.h @@ -51,6 +51,12 @@ typedef struct { Service s; ///< IRequestServerStopper } NsRequestServerStopper; +/// ProgressAsyncResult +typedef struct { + Service s; ///< IProgressAsyncResult + Event event; ///< Event with autoclear=false. +} NsProgressAsyncResult; + /// SystemUpdateControl typedef struct { Service s; ///< ISystemUpdateControl @@ -120,7 +126,7 @@ typedef struct { u64 end_timestamp; ///< POSIX timestamp for the promotion end. s64 remaining_time; ///< Remaining time until the promotion ends, in nanoseconds ({end_timestamp - current_time} converted to nanoseconds). u8 unk_x18[0x4]; ///< Not set, left at zero. - u8 flags; ///< Flags. Bit0: whether the PromotionInfo is valid (including bit1). Bit1 clear: remaining_time is set. + u8 flags; ///< Flags. Bit0: whether the PromotionInfo is valid (including bit1). Bit1 clear: remaining_time is set. u8 pad[3]; ///< Padding. } NsPromotionInfo; @@ -572,6 +578,25 @@ Result nsIsApplicationUpdateRequested(u64 application_id, bool *flag, u32 *out); */ Result nsWithdrawApplicationUpdateRequest(u64 application_id); +/** + * @brief RequestVerifyAddOnContentsRights + * @note Only available on [3.0.0+]. + * @param[out] a \ref NsProgressAsyncResult + * @param[in] application_id ApplicationId. + */ +Result nsRequestVerifyAddOnContentsRights(NsProgressAsyncResult *a, u64 application_id); + +/** + * @brief RequestVerifyApplication + * @note On pre-5.0.0 this uses cmd RequestVerifyApplicationDeprecated, otherwise cmd RequestVerifyApplication is used. + * @param[out] a \ref NsProgressAsyncResult. The data available with \ref nsProgressAsyncResultGetProgress is basically the same as \ref NsSystemUpdateProgress. + * @param[in] application_id ApplicationId. + * @param[in] unk Unknown. A default value of 0x7 can be used (which is what qlaunch uses). Only used on [5.0.0+]. + * @param buffer 0x1000-byte aligned buffer for TransferMemory. This buffer must not be accessed until the async operation finishes. + * @param[in] size 0x1000-byte aligned buffer size for TransferMemory. qlaunch uses size 0x100000. + */ +Result nsRequestVerifyApplication(NsProgressAsyncResult *a, u64 application_id, u32 unk, void* buffer, size_t size); + /** * @brief IsAnyApplicationEntityInstalled * @note Only available on [2.0.0+]. @@ -881,6 +906,61 @@ void nsRequestServerStopperClose(NsRequestServerStopper *r); ///@} +///@name IProgressAsyncResult +///@{ + +/** + * @brief Close a \ref NsProgressAsyncResult. + * @note When the object is initialized, this uses \ref nsProgressAsyncResultCancel then \ref nsProgressAsyncResultWait with timeout=U64_MAX. + * @param a \ref NsProgressAsyncResult + */ +void nsProgressAsyncResultClose(NsProgressAsyncResult *a); + +/** + * @brief Waits for the async operation to finish using the specified timeout. + * @param a \ref NsProgressAsyncResult + * @param[in] timeout Timeout in nanoseconds. U64_MAX for no timeout. + */ +Result nsProgressAsyncResultWait(NsProgressAsyncResult *a, u64 timeout); + +/** + * @brief Gets the Result. + * @note Prior to using the cmd, this uses \ref nsProgressAsyncResultWait with timeout=U64_MAX. + * @param a \ref NsProgressAsyncResult + */ +Result nsProgressAsyncResultGet(NsProgressAsyncResult *a); + +/** + * @brief Cancels the async operation. + * @note Used automatically by \ref nsProgressAsyncResultClose. + * @param a \ref NsProgressAsyncResult + */ +Result nsProgressAsyncResultCancel(NsProgressAsyncResult *a); + +/** + * @brief Gets the progress. + * @param a \ref NsProgressAsyncResult + * @param[out] buffer Output buffer. + * @param[in] size Output buffer size. + */ +Result nsProgressAsyncResultGetProgress(NsProgressAsyncResult *a, void* buffer, size_t size); + +/** + * @brief GetDetailResult + * @param a \ref NsProgressAsyncResult + */ +Result nsProgressAsyncResultGetDetailResult(NsProgressAsyncResult *a); + +/** + * @brief Gets the \ref ErrorContext. + * @note Only available on [4.0.0+]. + * @param a \ref NsProgressAsyncResult + * @param[out] context \ref ErrorContext + */ +Result nsProgressAsyncResultGetErrorContext(NsProgressAsyncResult *a, ErrorContext *context); + +///@} + ///@name ns:vm ///@{ diff --git a/nx/source/services/ns.c b/nx/source/services/ns.c index abc2ca6b..177575c7 100644 --- a/nx/source/services/ns.c +++ b/nx/source/services/ns.c @@ -622,6 +622,86 @@ Result nsWithdrawApplicationUpdateRequest(u64 application_id) { return _nsCmdInU64(&g_nsAppManSrv, application_id, 907); } +static Result _nsRequestVerifyApplicationDeprecated(NsProgressAsyncResult *a, u64 application_id, TransferMemory *tmem) { + const struct { + u64 application_id; + u64 size; + } in = { application_id, tmem->size }; + + memset(a, 0, sizeof(*a)); + Handle event = INVALID_HANDLE; + Result rc = serviceDispatchIn(&g_nsAppManSrv, 1000, in, + .in_num_handles = 1, + .in_handles = { tmem->handle }, + .out_num_objects = 1, + .out_objects = &a->s, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = &event, + ); + + if (R_SUCCEEDED(rc)) + eventLoadRemote(&a->event, event, false); + return rc; +} + +static Result _nsRequestVerifyApplication(NsProgressAsyncResult *a, u64 application_id, u32 unk, TransferMemory *tmem) { // [5.0.0+] + const struct { + u32 unk; + u32 pad; + u64 application_id; + u64 size; + } in = { unk, 0, application_id, tmem->size }; + + memset(a, 0, sizeof(*a)); + Handle event = INVALID_HANDLE; + Result rc = serviceDispatchIn(&g_nsAppManSrv, 1003, in, + .in_num_handles = 1, + .in_handles = { tmem->handle }, + .out_num_objects = 1, + .out_objects = &a->s, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = &event, + ); + + if (R_SUCCEEDED(rc)) + eventLoadRemote(&a->event, event, false); + return rc; +} + +Result nsRequestVerifyAddOnContentsRights(NsProgressAsyncResult *a, u64 application_id) { + if (hosversionBefore(3,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + memset(a, 0, sizeof(*a)); + Handle event = INVALID_HANDLE; + Result rc = serviceDispatchIn(&g_nsAppManSrv, 1002, application_id, + .out_num_objects = 1, + .out_objects = &a->s, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = &event, + ); + + if (R_SUCCEEDED(rc)) + eventLoadRemote(&a->event, event, false); + return rc; +} + +Result nsRequestVerifyApplication(NsProgressAsyncResult *a, u64 application_id, u32 unk, void* buffer, size_t size) { + Result rc=0; + TransferMemory tmem={0}; + + rc = tmemCreateFromMemory(&tmem, buffer, size, Perm_None); + if (R_SUCCEEDED(rc)) { + if (hosversionBefore(5,0,0)) + rc = _nsRequestVerifyApplicationDeprecated(a, application_id, &tmem); + else + rc = _nsRequestVerifyApplication(a, application_id, unk, &tmem); + } + tmemClose(&tmem); + + return rc; +} + Result nsIsAnyApplicationEntityInstalled(u64 application_id, bool *out) { if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); @@ -1058,6 +1138,70 @@ void nsRequestServerStopperClose(NsRequestServerStopper *r) { serviceClose(&r->s); } +// IProgressAsyncResult + +void nsProgressAsyncResultClose(NsProgressAsyncResult *a) { + if (serviceIsActive(&a->s)) { + nsProgressAsyncResultCancel(a); // Official sw ignores rc from this prior to waiting on the event. + nsProgressAsyncResultWait(a, U64_MAX); + } + + serviceClose(&a->s); + eventClose(&a->event); +} + +Result nsProgressAsyncResultWait(NsProgressAsyncResult *a, u64 timeout) { + if (!serviceIsActive(&a->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return eventWait(&a->event, timeout); +} + +Result nsProgressAsyncResultGet(NsProgressAsyncResult *a) { + if (!serviceIsActive(&a->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + Result rc = nsProgressAsyncResultWait(a, U64_MAX); + if (R_SUCCEEDED(rc)) rc = _nsCmdNoIO(&a->s, 0); + return rc; +} + +Result nsProgressAsyncResultCancel(NsProgressAsyncResult *a) { + if (!serviceIsActive(&a->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _nsCmdNoIO(&a->s, 1); +} + +Result nsProgressAsyncResultGetProgress(NsProgressAsyncResult *a, void* buffer, size_t size) { + if (!serviceIsActive(&a->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return serviceDispatch(&a->s, 2, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { buffer, size } }, + ); +} + +Result nsProgressAsyncResultGetDetailResult(NsProgressAsyncResult *a) { + if (!serviceIsActive(&a->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _nsCmdNoIO(&a->s, 3); +} + +Result nsProgressAsyncResultGetErrorContext(NsProgressAsyncResult *a, ErrorContext *context) { + if (!serviceIsActive(&a->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return serviceDispatch(&a->s, 4, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { context, sizeof(*context) } }, + ); +} + // ns:vm NX_GENERATE_SERVICE_GUARD(nsvm);