From d8a50a46cf3dd25f2259d03617338a9a5648945e Mon Sep 17 00:00:00 2001 From: HookedBehemoth Date: Wed, 19 Feb 2020 23:32:01 +0100 Subject: [PATCH] add caps:c (#375) --- nx/include/switch.h | 1 + nx/include/switch/services/caps.h | 22 +- nx/include/switch/services/capsa.h | 32 ++- nx/include/switch/services/capsc.h | 333 ++++++++++++++++++++++++++++ nx/source/services/capsa.c | 60 ++++- nx/source/services/capsc.c | 345 +++++++++++++++++++++++++++++ 6 files changed, 763 insertions(+), 30 deletions(-) create mode 100644 nx/include/switch/services/capsc.h create mode 100644 nx/source/services/capsc.c diff --git a/nx/include/switch.h b/nx/include/switch.h index b6123a46..b51d25d5 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -93,6 +93,7 @@ extern "C" { #include "switch/services/psc.h" #include "switch/services/caps.h" #include "switch/services/capsa.h" +#include "switch/services/capsc.h" #include "switch/services/capsdc.h" #include "switch/services/capsu.h" #include "switch/services/capssc.h" diff --git a/nx/include/switch/services/caps.h b/nx/include/switch/services/caps.h index d10e55c7..79047cb0 100644 --- a/nx/include/switch/services/caps.h +++ b/nx/include/switch/services/caps.h @@ -88,9 +88,8 @@ typedef struct { CapsAlbumFileDateTime datetime; ///< \ref CapsAlbumFileDateTime u8 storage; ///< \ref CapsAlbumStorage u8 content; ///< \ref CapsAlbumFileContents - u32 pad_x12; ///< Set to 0 by official software - u16 pad_x16; ///< Set to 0 by official software -} PACKED CapsAlbumFileId; + u8 pad_x12[0x6]; ///< padding +} CapsAlbumFileId; /// AlbumEntry typedef struct { @@ -104,14 +103,17 @@ typedef struct { u8 data[0x20]; ///< Data. struct { - u8 unk_x0[0x20]; ///< Unknown. + u8 unk_x0[0x20]; ///< aes256 with random key over \ref AlbumEntry. } v0; ///< Pre-7.0.0 struct { u64 size; ///< size of the entry - u64 application_id; ///< ApplicationId + u64 hash; ///< aes256 with hardcoded key over \ref AlbumEntry. CapsAlbumFileDateTime datetime; ///< \ref CapsAlbumFileDateTime - u8 unk_x18[0x8]; ///< Unknown. + u8 storage; ///< \ref CapsAlbumStorage + u8 content; ///< \ref CapsAlbumFileContents + u8 pad_x1a[0x5]; ///< padding + u8 unk_x1f; ///< Set to 1 by official software } v1; ///< [7.0.0+] }; } CapsApplicationAlbumEntry; @@ -162,12 +164,6 @@ typedef struct { CapsAlbumContentsUsage usages[16]; ///< \ref CapsAlbumContentsUsage } CapsAlbumUsage16; -/// OverlayThumbnailData -typedef struct { - CapsAlbumFileId file_id; ///< \ref CapsAlbumFileId - u64 size; ///< Size. -} CapsOverlayThumbnailData; - /// UserIdList typedef struct { AccountUid uids[ACC_USER_LIST_SIZE]; ///< \ref AccountUid @@ -201,7 +197,7 @@ typedef enum { /// AlbumCache typedef struct { u64 count; ///< Count - u8 unk_x8[8]; ///< Unknown + u64 unk_x8; ///< Unknown } CapsAlbumCache; /// Gets the ShimLibraryVersion. diff --git a/nx/include/switch/services/capsa.h b/nx/include/switch/services/capsa.h index 7dce12b8..7a5517bf 100644 --- a/nx/include/switch/services/capsa.h +++ b/nx/include/switch/services/capsa.h @@ -116,6 +116,15 @@ Result capsaLoadAlbumScreenShotImage(u64 *width, u64 *height, const CapsAlbumFil */ Result capsaLoadAlbumScreenShotThumbnailImage(u64 *width, u64 *height, const CapsAlbumFileId *file_id, void* image, u64 image_size, void* workbuf, u64 workbuf_size); +/** + * @brief Load an \ref CapsAlbumEntry from a \ref CapsApplicationAlbumEntry and an ApplicationId. + * @note Only available on [2.0.0+]. + * @param[out] entry \ref CapsAlbumEntry + * @param[in] application_entry \ref CapsApplicationAlbumEntry + * @param[in] application_id ApplicationId + */ +Result capsaGetAlbumEntryFromApplicationAlbumEntry(CapsAlbumEntry *entry, const CapsApplicationAlbumEntry *application_entry, u64 application_id); + /** * @brief Load the ScreenShotImage for the specified AlbumFile. * @note Only available on [3.0.0+]. @@ -178,9 +187,10 @@ Result capsaGetAlbumMountResult(CapsAlbumStorage storage); * @brief Returns the AlbumUsage for a specified \ref CapsAlbumStorage. * @note Only available on [4.0.0+]. * @param[in] storage \ref CapsAlbumStorage + * @param[in] flags \ref CapsAlbumFileContentsFlag * @param[out] out \ref CapsAlbumUsage16 */ -Result capsaGetAlbumUsage16(CapsAlbumStorage storage, CapsAlbumUsage16 *out); +Result capsaGetAlbumUsage16(CapsAlbumStorage storage, u8 flags, CapsAlbumUsage16 *out); /** * @brief Returns the start and end of the Applet Id range. @@ -213,20 +223,22 @@ Result capsaGetAlbumFileListEx0(CapsAlbumStorage storage, u8 flags, u64 *out, Ca /** * @brief Returns the image from the last shown ScreenShot Overlay. - * @param[out] data \ref CapsOverlayThumbnailData + * @param[out] file_id \ref CapsAlbumFileId + * @param[out] out_size Size of the thumbnail image. Always 0x5100. * @param[out] image RGBA8 image output buffer. * @param[in] image_size Image buffer size, should be at least large enough for RGBA8 96×54. */ -Result capsaGetLastOverlayScreenShotThumbnail(CapsOverlayThumbnailData *data, void* image, u64 image_size); +Result capsaGetLastOverlayScreenShotThumbnail(CapsAlbumFileId *file_id, u64 *out_size, void* image, u64 image_size); /** * @brief Returns the image from the last shown Movie Overlay. * @note Only available on [4.0.0+]. - * @param[out] data \ref CapsOverlayThumbnailData + * @param[out] file_id \ref CapsAlbumFileId + * @param[out] out_size Size of the thumbnail image. Always 0x5100. * @param[out] image RGBA8 image output buffer. * @param[in] image_size Image buffer size, should be at least large enough for RGBA8 96×54. */ -Result capsaGetLastOverlayMovieThumbnail(CapsOverlayThumbnailData *data, void* image, u64 image_size); +Result capsaGetLastOverlayMovieThumbnail(CapsAlbumFileId *file_id, u64 *out_size, void* image, u64 image_size); /** * @brief Gets the currently set autosaving storage. @@ -305,6 +317,7 @@ Result capsaRefreshAlbumCache(CapsAlbumStorage storage); /** * @brief Gets the AlbumCache of the specified AlbumStorage. + * @note Stubbed on [4.0.0+]. * @note use \ref capsaGetAlbumCacheEx instead. * @param[in] storage \ref CapsAlbumStorage * @param[out] cache \ref CapsAlbumCache @@ -313,13 +326,20 @@ Result capsaGetAlbumCache(CapsAlbumStorage storage, CapsAlbumCache *cache); /** * @brief Gets the AlbumCache for the specified type of the specified AlbumStorage. - * @note Stubbed on [4.0.0+]. * @param[in] storage \ref CapsAlbumStorage * @param[in] contents \ref CapsAlbumFileContents * @param[out] cache \ref CapsAlbumCache */ Result capsaGetAlbumCacheEx(CapsAlbumStorage storage, CapsAlbumFileContents contents, CapsAlbumCache *cache); +/** + * @brief Load an \ref CapsAlbumEntry from a \ref CapsApplicationAlbumEntry and an AppletResourceUserId. + * @note Only available on [2.0.0+]. + * @param[out] entry \ref CapsAlbumEntry + * @param[in] application_entry \ref CapsApplicationAlbumEntry + */ +Result capsaGetAlbumEntryFromApplicationAlbumEntryAruid(CapsAlbumEntry *entry, const CapsApplicationAlbumEntry *application_entry); + /** * @brief Opens an AlbumMovieStream. * @note This opens IAlbumAccessorSession if not previously opened, it's closed during \ref capsaExit. diff --git a/nx/include/switch/services/capsc.h b/nx/include/switch/services/capsc.h new file mode 100644 index 00000000..c348022a --- /dev/null +++ b/nx/include/switch/services/capsc.h @@ -0,0 +1,333 @@ +/** + * @file capsc.h + * @brief Album Control (caps:c) service IPC wrapper. + * @author Behemoth + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "../sf/service.h" +#include "../services/caps.h" + +/// Initialize caps:c +Result capscInitialize(void); + +/// Exit caps:c. +void capscExit(void); + +/// Gets the Service for caps:c. +Service* capscGetServiceSession(void); + +/** + * @brief Notify the service that a storage is now available. + * @note This will result in capsrv mounting the image directory on that storage medium. + * @param[in] storage \ref CapsAlbumStorage + */ +Result capscNotifyAlbumStorageIsAvailable(CapsAlbumStorage storage); + +/** + * @brief Notify the service that a storage is now unavailable. + * @note This will result in capsrv unmounting the image directory on that storage medium. + * @param[in] storage \ref CapsAlbumStorage + */ +Result capscNotifyAlbumStorageIsUnAvailable(CapsAlbumStorage storage); + +/** + * @brief Register an applet for later usage. + * @note Called at application launch by the system. + * @note Will generate a random AES-256 key for this application for use on Shim-Version 0. + * @note Only available on [2.0.0+]. + * @param[in] appletResourceUserId AppletResourceUserId. + * @param[in] application_id ApplicationId. + */ +Result capscRegisterAppletResourceUserId(u64 appletResourceUserId, u64 application_id); + +/** + * @brief Unregister an applet. + * @note Called at application exit by the system. + * @note Only available on [2.0.0+]. + * @param[in] appletResourceUserId AppletResourceUserId. + * @param[in] application_id ApplicationId. + */ +Result capscUnregisterAppletResourceUserId(u64 appletResourceUserId, u64 application_id); + +/** + * @brief Get an ApplicationId that corresponds to an AppletResourceUserId. + * @note Returns value set by \ref capscRegisterAppletResourceUserId. + * @note Only available on [2.0.0+]. + * @param[out] application_id ApplicationId. + * @param[in] appletResourceUserId AppletResourceUserId. + */ +Result capscGetApplicationIdFromAruid(u64 *application_id, u64 aruid); + +/** + * @brief Checks whether an ApplicationId is registered. + * @note Only available on [2.0.0+]. + * @param[in] application_id ApplicationId. + */ +Result capscCheckApplicationIdRegistered(u64 application_id); + +/** + * @brief Generate an AlbumFileId based on parameters and current time. + * @param[in] application_id ApplicationId. + * @note Only available on [2.0.0+]. + * @param[in] contents \ref CapsAlbumFileContents + * @param[out] file_id \ref CapsAlbumFileId + */ +Result capscGenerateCurrentAlbumFileId(u64 application_id, CapsAlbumFileContents contents, CapsAlbumFileId *file_id); + +/** + * @brief Generate an ApplicationAlbumEntry based on parameters. + * @note Output will be different between Shim Version 0 and 1. + * @note Only available on [2.0.0+]. + * @param[out] appEntry \ref CapsApplicationAlbumEntry + * @param[in] entry \ref CapsAlbumEntry + * @param[in] application_id ApplicationId. + */ +Result capscGenerateApplicationAlbumEntry(CapsApplicationAlbumEntry *appEntry, const CapsAlbumEntry *entry, u64 application_id); + +/** + * @brief Save a jpeg image. + * @note Only available on [2.0.0-3.0.2]. + * @param[in] file_id \ref CapsAlbumFileId + * @param[in] buffer JPEG image buffer. + * @param[in] buffer_size Size of the JPEG image. + */ +Result capscSaveAlbumScreenShotFile(const CapsAlbumFileId *file_id, const void* buffer, u64 buffer_size); + +/** + * @brief Save a jpeg image. + * @note Only available on [4.0.0+]. + * @note Version 3 as of [9.1.0]. + * @param[in] file_id \ref CapsAlbumFileId + * @param[in] version Revision number. + * @param[in] makernote_offset Offset to makernote in JPEG buffer. + * @param[in] makernote_size Size of the makernote in JPEG buffer. + * @param[in] buffer JPEG image buffer. + * @param[in] buffer_size Size of the JPEG image. + */ +Result capscSaveAlbumScreenShotFileEx(const CapsAlbumFileId *file_id, u64 version, u64 makernote_offset, u64 makernote_size, const void* buffer, u64 buffer_size); + +/** + * @brief Sets thumbnail data for the last taken screenshot. + * @note 96×54 Image will get saved. + * @param[in] file_id \ref CapsAlbumFileId + * @param[in] image RGBA8 image buffer. + * @param[in] image_size size of the RGBA8 image buffer. + */ +Result capscSetOverlayScreenShotThumbnailData(const CapsAlbumFileId *file_id, const void* image, u64 image_size); + +/** + * @brief Sets thumbnail data for the last recorded movie. + * @note Only availabe on [4.0.0+]. + * @note 96×54 Image will get saved. + * @param[in] file_id \ref CapsAlbumFileId + * @param[in] image RGBA8 image buffer. + * @param[in] image_size size of the RGBA8 image buffer. + */ +Result capscSetOverlayMovieThumbnailData(const CapsAlbumFileId *file_id, const void* image, u64 image_size); + +/** + * @brief Opens an AlbumMovieReadStream. + * @note This opens IAlbumControlSession if not previously opened, it's closed during \ref capscExit. + * @note Up to 4 streams can be open at the same time. Multiple streams can be open at the same time for the same \ref CapsAlbumFileId. + * @note Only available on [4.0.0+]. + * @param[out] stream Stream handle. + * @param[in] entry \ref CapsAlbumFileId + */ +Result capscOpenAlbumMovieReadStream(u64 *stream, const CapsAlbumFileId *file_id); + +/** + * @brief Closes an AlbumMovieReadStream. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + */ +Result capscCloseAlbumMovieStream(u64 stream); + +/** + * @brief Gets the data size of an AlbumMovieReadStream. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + * @param[out] size Size of the actual MP4, without the JPEG at the end. + */ +Result capscGetAlbumMovieStreamSize(u64 stream, u64 *size); + +/** + * @brief Reads data from an AlbumMovieReadStream. + * @note offset(+size) must not be negative. offset and size must be aligned to 0x40000-bytes. + * @note When offset(+size) goes beyond the size from \ref capscGetAlbumMovieStreamSize, the regions of the buffer which goes beyond that are cleared to 0, and actual_size is still set to the input size. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + * @param[in] offset Offset. + * @param[out] Output data buffer. + * @param[in] size Data buffer size. + * @param[out] actual_size Actual read size. + */ +Result capscReadMovieDataFromAlbumMovieReadStream(u64 stream, u64 offset, void* buffer, size_t size, u64 *actual_size); + +/** + * @brief Gets the BrokenReason for an AlbumMovieReadStream. + * @note Official sw doesn't use this. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + */ +Result capscGetAlbumMovieReadStreamBrokenReason(u64 stream); + +/** + * @brief Gets the data size of an Image taken from an AlbumMovieReadStream. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + * @param[out] size Expected size of an Image. + */ +Result capscGetAlbumMovieReadStreamImageDataSize(u64 stream, u64 *size); + +/** + * @brief Reads data of an Image taken from an AlbumMovieReadStream. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + * @param[in] offset Offset. + * @param[out] buffer Output data buffer. + * @param[in] size Data buffer size. + * @param[out] actual_size Actual read size. + */ +Result capscReadImageDataFromAlbumMovieReadStream(u64 stream, u64 offset, void* buffer, size_t size, u64 *actual_size); + +/** + * @brief Gets the file attribute of an AlbumMovieReadStream. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + * @param[out] attr \ref CapsScreenShotAttribute + */ +Result capscReadFileAttributeFromAlbumMovieReadStream(u64 stream, CapsScreenShotAttribute *attribute); + +/** + * @brief Opens an AlbumMovieWriteStream. + * @note This opens IAlbumControlSession if not previously opened, it's closed during \ref capsaExit. + * @note Up to 2 streams can be open at the same time. + * @note Only available on [4.0.0+]. + * @param[out] stream Stream handle. + * @param[in] entry \ref CapsAlbumFileId + */ +Result capscOpenAlbumMovieWriteStream(u64 *stream, const CapsAlbumFileId *file_id); + +/** + * @brief Finish write to AlbumMovieWriteStream. + * @note Copies file from save to destination storage and deletes the temporary file. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + */ +Result capscFinishAlbumMovieWriteStream(u64 stream); + +/** + * @brief Closes a finished AlbumMovieWriteStream. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + */ +Result capscCommitAlbumMovieWriteStream(u64 stream); + +/** + * @brief Closes an AlbumMovieWriteStream in any state. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + */ +Result capscDiscardAlbumMovieWriteStream(u64 stream); + +/** + * @brief Closes an AlbumMovieWriteStream in any state without deleting the temporary file. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + */ +Result capscDiscardAlbumMovieWriteStreamNoDelete(u64 stream); + +/** + * @brief Closes a finished AlbumMovieWriteStream. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + * @param[out] entry \ref CapsAlbumEntry + */ +Result capscCommitAlbumMovieWriteStreamEx(u64 stream, CapsAlbumEntry *entry); + +/** + * @brief Start AlbumMovieWriteStream data section. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + */ +Result capscStartAlbumMovieWriteStreamDataSection(u64 stream); + +/** + * @brief End AlbumMovieWriteStream data section. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + */ +Result capscEndAlbumMovieWriteStreamDataSection(u64 stream); + +/** + * @brief Start AlbumMovieWriteStream meta section. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + */ +Result capscStartAlbumMovieWriteStreamMetaSection(u64 stream); + +/** + * @brief End AlbumMovieWriteStream meta section. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + */ +Result capscEndAlbumMovieWriteStreamMetaSection(u64 stream); + +/** + * @brief Reads data from an AlbumMovieWriteStream. + * @note offset(+size) must not be negative. offset and size must be aligned to 0x40000-bytes. + * @note When offset(+size) goes beyond the size from \ref capscGetAlbumMovieStreamSize, the regions of the buffer which goes beyond that are cleared to 0, and actual_size is still set to the input size. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + * @param[in] offset Offset. + * @param[out] buffer Output data buffer. + * @param[in] size Data buffer size. + * @param[out] actual_size Actual read size. + */ +Result capscReadDataFromAlbumMovieWriteStream(u64 stream, u64 offset, void* buffer, u64 size, u64 *actual_size); + +/** + * @brief Write data to an AlbumMovieWriteStream. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + * @param[in] offset Offset. + * @param[in] buffer Input data buffer. + * @param[in] size Data buffer size. + */ +Result capscWriteDataToAlbumMovieWriteStream(u64 stream, u64 offset, const void* buffer, u64 size); + +/** + * @brief Write meta data to an AlbumMovieWriteStream. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + * @param[in] offset Offset. + * @param[in] buffer Input data buffer. + * @param[in] size Data buffer size. + */ +Result capscWriteMetaToAlbumMovieWriteStream(u64 stream, u64 offset, const void* buffer, u64 size); + +/** + * @brief Gets the BrokenReason for an AlbumMovieWriteStream. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + */ +Result capscGetAlbumMovieWriteStreamBrokenReason(u64 stream); + +/** + * @brief Gets the data size of an AlbumMovieWriteStream. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + * @param[out] size Size of the data section. + */ +Result capscGetAlbumMovieWriteStreamDataSize(u64 stream, u64 *size); + +/** + * @brief Sets the data size of an AlbumMovieWriteStream. + * @note Must not be bigger than 2GiB. + * @note Only available on [4.0.0+]. + * @param[in] stream Stream handle. + * @param[out] size Size of the data section. + */ +Result capscSetAlbumMovieWriteStreamDataSize(u64 stream, u64 size); diff --git a/nx/source/services/capsa.c b/nx/source/services/capsa.c index bf571a1c..257e5512 100644 --- a/nx/source/services/capsa.c +++ b/nx/source/services/capsa.c @@ -112,6 +112,16 @@ Result capsaLoadAlbumScreenShotThumbnailImage(u64 *width, u64 *height, const Cap return _capsaLoadAlbumScreenshot(width, height, file_id, image, image_size, workbuf, workbuf_size, 10); } +Result capsaGetAlbumEntryFromApplicationAlbumEntry(CapsAlbumEntry *entry, const CapsApplicationAlbumEntry *application_entry, u64 application_id) { + if (hosversionBefore(2,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + const struct { + CapsApplicationAlbumEntry application_entry; + u64 application_id; + } in = { *application_entry, application_id }; + return serviceDispatchInOut(&g_capsaSrv, 11, in, *entry); +} + static Result _capsaLoadAlbumScreenshotEx(u64 *width, u64 *height, const CapsAlbumFileId *file_id, const CapsScreenShotDecodeOption *opts, void* image, u64 image_size, void* workbuf, u64 workbuf_size, u32 cmd_id) { if (hosversionBefore(3,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); @@ -183,11 +193,19 @@ Result capsaGetAlbumMountResult(CapsAlbumStorage storage) { return _capsaCmdInU8NoOut(&g_capsaSrv, storage, 16); } -Result capsaGetAlbumUsage16(CapsAlbumStorage storage, CapsAlbumUsage16 *out) { +Result capsaGetAlbumUsage16(CapsAlbumStorage storage, u8 flags, CapsAlbumUsage16 *out) { if (hosversionBefore(4,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); - u8 inval = storage; - return serviceDispatchInOut(&g_capsaSrv, 17, inval, *out); + struct { + u8 storage; + u8 pad_x1[7]; + u8 flags; + u8 pad_x9[7]; + } in = { storage, {0}, flags, {0} }; + return serviceDispatchIn(&g_capsaSrv, 17, in, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_FixedSize, }, + .buffers = { { out, sizeof(CapsAlbumUsage16) } }, + ); } Result capsaGetMinMaxAppletId(bool *success, u64* min, u64* max) { @@ -228,7 +246,7 @@ Result capsaGetAlbumFileListEx0(CapsAlbumStorage storage, u8 flags, u64 *out, Ca struct { u8 storage; u8 pad_x1[7]; - u8 contents; + u8 flags; u8 pad_x9[7]; } in = { storage, {0}, flags, {0} }; return serviceDispatchInOut(&g_capsaSrv, 101, in, *out, @@ -237,21 +255,30 @@ Result capsaGetAlbumFileListEx0(CapsAlbumStorage storage, u8 flags, u64 *out, Ca ); } -Result _capsaGetLastOverlayThumbnail(CapsOverlayThumbnailData *data, void* image, u64 image_size, u32 cmd_id) { - return serviceDispatchOut(&g_capsaSrv, cmd_id, *data, +Result _capsaGetLastOverlayThumbnail(CapsAlbumFileId *file_id, u64 *out_size, void* image, u64 image_size, u32 cmd_id) { + struct { + CapsAlbumFileId file_id; + u64 size; + } out; + Result rc = serviceDispatchOut(&g_capsaSrv, cmd_id, out, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out, }, .buffers = { { image, image_size }, }, ); + if (R_SUCCEEDED(rc)) { + if (file_id) *file_id = out.file_id; + if (out_size) *out_size = out.size; + } + return rc; } -Result capsaGetLastOverlayScreenShotThumbnail(CapsOverlayThumbnailData *data, void* image, u64 image_size) { - return _capsaGetLastOverlayThumbnail(data, image, image_size, 301); +Result capsaGetLastOverlayScreenShotThumbnail(CapsAlbumFileId *file_id, u64 *out_size, void* image, u64 image_size) { + return _capsaGetLastOverlayThumbnail(file_id, out_size, image, image_size, 301); } -Result capsaGetLastOverlayMovieThumbnail(CapsOverlayThumbnailData *data, void* image, u64 image_size) { +Result capsaGetLastOverlayMovieThumbnail(CapsAlbumFileId *file_id, u64 *out_size, void* image, u64 image_size) { if (hosversionBefore(4,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); - return _capsaGetLastOverlayThumbnail(data, image, image_size, 302); + return _capsaGetLastOverlayThumbnail(file_id, out_size, image, image_size, 302); } Result capsaGetAutoSavingStorage(CapsAlbumStorage *storage) { @@ -309,7 +336,8 @@ Result capsaRefreshAlbumCache(CapsAlbumStorage storage) { } Result capsaGetAlbumCache(CapsAlbumStorage storage, CapsAlbumCache *cache) { - return serviceDispatchInOut(&g_capsaSrv, 8012, storage, *cache); + u8 inval = storage; + return serviceDispatchInOut(&g_capsaSrv, 8012, inval, *cache); } Result capsaGetAlbumCacheEx(CapsAlbumStorage storage, CapsAlbumFileContents contents, CapsAlbumCache *cache) { @@ -322,6 +350,16 @@ Result capsaGetAlbumCacheEx(CapsAlbumStorage storage, CapsAlbumFileContents cont return serviceDispatchInOut(&g_capsaSrv, 8013, in, *cache); } +Result capsaGetAlbumEntryFromApplicationAlbumEntryAruid(CapsAlbumEntry *entry, const CapsApplicationAlbumEntry *application_entry) { + if (hosversionBefore(2,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + const struct { + CapsApplicationAlbumEntry application_entry; + u64 aruid; + } in = { *application_entry, appletGetAppletResourceUserId() }; + return serviceDispatchInOut(&g_capsaSrv, 8021, in, *entry, .in_send_pid = true); +} + static Result _capsaOpenAccessorSession(Service *srv_out) { u64 AppletResourceUserId = appletGetAppletResourceUserId(); diff --git a/nx/source/services/capsc.c b/nx/source/services/capsc.c new file mode 100644 index 00000000..508936f1 --- /dev/null +++ b/nx/source/services/capsc.c @@ -0,0 +1,345 @@ +#define NX_SERVICE_ASSUME_NON_DOMAIN +#include +#include +#include "service_guard.h" +#include "runtime/hosversion.h" +#include "services/applet.h" +#include "services/capsc.h" + +static Service g_capscSrv; +static Service g_capscControl; + +static Result _capscSetShimLibraryVersion(u64 version); + +NX_GENERATE_SERVICE_GUARD(capsc); + +Result _capscInitialize(void) { + Result rc=0; + + rc = smGetService(&g_capscSrv, "caps:c"); + + if (R_SUCCEEDED(rc) && hosversionAtLeast(7,0,0)) rc = _capscSetShimLibraryVersion(capsGetShimLibraryVersion()); + + return rc; +} + +void _capscCleanup(void) { + serviceClose(&g_capscControl); + serviceClose(&g_capscSrv); +} + +Service* capscGetServiceSession(void) { + return &g_capscSrv; +} + +static Result _capscSetShimLibraryVersion(u64 version) { + if (hosversionBefore(7,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + const struct { + u64 version; + u64 AppletResourceUserId; + } in = { version, appletGetAppletResourceUserId() }; + return serviceDispatchIn(&g_capscSrv, 33, in); +} + +static Result _capscCmdInU8NoOut(Service *srv, u32 cmd_id, u64 inval) { + return serviceDispatchIn(srv, cmd_id, inval); +} + +Result capscNotifyAlbumStorageIsAvailable(CapsAlbumStorage storage) { + return _capscCmdInU8NoOut(&g_capscSrv, 2001, storage); +} + +Result capscNotifyAlbumStorageIsUnAvailable(CapsAlbumStorage storage) { + return _capscCmdInU8NoOut(&g_capscSrv, 2002, storage); +} + +Result capscRegisterAppletResourceUserId(u64 appletResourceUserId, u64 application_id) { + if (hosversionBefore(2,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + const struct { + u64 appletResourceUserId; + u64 applicationId; + } in = { appletResourceUserId, application_id }; + return serviceDispatchIn(&g_capscSrv, 2011, in); +} + +Result capscUnregisterAppletResourceUserId(u64 appletResourceUserId, u64 application_id) { + if (hosversionBefore(2,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + const struct { + u64 appletResourceUserId; + u64 applicationId; + } in = { appletResourceUserId, application_id }; + return serviceDispatchIn(&g_capscSrv, 2012, in); +} + +Result capscGetApplicationIdFromAruid(u64 *application_id, u64 aruid) { + if (hosversionBefore(2,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + return serviceDispatchInOut(&g_capscSrv, 2013, aruid, *application_id); +} + +Result capscCheckApplicationIdRegistered(u64 application_id) { + if (hosversionBefore(2,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + return serviceDispatchIn(&g_capscSrv, 2014, application_id); +} + +Result capscGenerateCurrentAlbumFileId(u64 application_id, CapsAlbumFileContents contents, CapsAlbumFileId *file_id) { + if (hosversionBefore(2,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + const struct { + u8 type; + u64 applicationId; + } in = { contents, application_id }; + return serviceDispatchInOut(&g_capscSrv, 2101, in, *file_id); +} + +Result capscGenerateApplicationAlbumEntry(CapsApplicationAlbumEntry *appEntry, const CapsAlbumEntry *entry, u64 application_id) { + if (hosversionBefore(2,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + const struct { + CapsAlbumEntry entry; + u64 applicationId; + } in = { *entry, application_id }; + return serviceDispatchInOut(&g_capscSrv, 2102, in, *appEntry); +} + +Result capscSaveAlbumScreenShotFile(const CapsAlbumFileId *file_id, const void* buffer, u64 buffer_size) { + if (hosversionBefore(2,0,0) || hosversionAtLeast(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + return serviceDispatchIn(&g_capscSrv, 2201, file_id, + .buffer_attrs = { SfBufferAttr_HipcMapTransferAllowsNonSecure | SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { buffer, buffer_size }, }, + ); +} + +Result capscSaveAlbumScreenShotFileEx(const CapsAlbumFileId *file_id, u64 version, u64 makernote_offset, u64 makernote_size, const void* buffer, u64 buffer_size) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + const struct { + CapsAlbumFileId file_id; + u64 version; + u64 mn_offset; + u64 mn_size; + } in = { *file_id, version, makernote_offset, makernote_size }; + return serviceDispatchIn(&g_capscSrv, 2202, in, + .buffer_attrs = { SfBufferAttr_HipcMapTransferAllowsNonSecure | SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { buffer, buffer_size }, }, + ); +} + +static Result _capscSetOverlayThumbnailData(const CapsAlbumFileId *file_id, const void* image, u64 image_size, u32 cmd_id) { + return serviceDispatchIn(&g_capscSrv, cmd_id, *file_id, + .buffer_attrs = { SfBufferAttr_HipcMapTransferAllowsNonSecure | SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { image, image_size }, }, + ); +} + +Result capscSetOverlayScreenShotThumbnailData(const CapsAlbumFileId *file_id, const void* image, u64 image_size) { + if (hosversionBefore(2,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + return _capscSetOverlayThumbnailData(file_id, image, image_size, 2301); +} + +Result capscSetOverlayMovieThumbnailData(const CapsAlbumFileId *file_id, const void* image, u64 image_size) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + return _capscSetOverlayThumbnailData(file_id, image, image_size, 2302); +} + +static Result _capscOpenControlSession(Service *srv_out) { + u64 AppletResourceUserId = appletGetAppletResourceUserId(); + return serviceDispatchIn(&g_capscSrv, 60001, AppletResourceUserId, + .in_send_pid = true, + .out_num_objects = 1, + .out_objects = srv_out, + ); +} + +static Result _capscOpenAlbumMovieStream(const CapsAlbumFileId *file_id, u64 *stream, u32 cmd_id) { + return serviceDispatchInOut(&g_capscControl, cmd_id, *file_id, *stream); +} + +static Result _capscControlReadDataFromAlbumMovieStream(u64 stream, u64 offset, void* buffer, size_t size, u64 *actual_size, u32 cmd_id) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + if (!serviceIsActive(&g_capscControl)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + const struct { + u64 stream; + u64 offset; + } in = { stream, offset }; + return serviceDispatchInOut(&g_capscControl, cmd_id, in, *actual_size, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { buffer, size } }, + ); +} + +static Result _capscControlCmdInU64NoOut(u64 inval, u32 cmd_id) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + if (!serviceIsActive(&g_capscControl)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + return serviceDispatchIn(&g_capscControl, cmd_id, inval); +} + +Result capscOpenAlbumMovieReadStream(u64 *stream, const CapsAlbumFileId *file_id) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + Result rc=0; + if (!serviceIsActive(&g_capscControl)) rc = _capscOpenControlSession(&g_capscControl); + if (R_SUCCEEDED(rc)) rc = _capscOpenAlbumMovieStream(file_id, stream, 2001); + return rc; +} + +Result capscCloseAlbumMovieStream(u64 stream) { + return _capscControlCmdInU64NoOut(stream, 2002); +} + +Result capscGetAlbumMovieStreamSize(u64 stream, u64 *size) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + if (!serviceIsActive(&g_capscControl)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + return serviceDispatchInOut(&g_capscControl, 2003, stream, *size); +} + +Result capscReadMovieDataFromAlbumMovieReadStream(u64 stream, u64 offset, void* buffer, size_t size, u64 *actual_size) { + return _capscControlReadDataFromAlbumMovieStream(stream, offset, buffer, size, actual_size, 2004); +} + +Result capscGetAlbumMovieReadStreamBrokenReason(u64 stream) { + return _capscControlCmdInU64NoOut(stream, 2005); +} + +Result capscGetAlbumMovieReadStreamImageDataSize(u64 stream, u64 *size) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + if (!serviceIsActive(&g_capscControl)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + return serviceDispatchInOut(&g_capscControl, 2006, stream, *size); +} + +Result capscReadImageDataFromAlbumMovieReadStream(u64 stream, u64 offset, void* buffer, size_t size, u64 *actual_size) { + return _capscControlReadDataFromAlbumMovieStream(stream, offset, buffer, size, actual_size, 2007); +} + +Result capscReadFileAttributeFromAlbumMovieReadStream(u64 stream, CapsScreenShotAttribute *attribute) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + if (!serviceIsActive(&g_capscControl)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return serviceDispatchInOut(&g_capscControl, 2008, stream, *attribute); +} + +Result capscOpenAlbumMovieWriteStream(u64 *stream, const CapsAlbumFileId *file_id) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + Result rc=0; + + if (!serviceIsActive(&g_capscControl)) rc = _capscOpenControlSession(&g_capscControl); + + if (R_SUCCEEDED(rc)) rc = _capscOpenAlbumMovieStream(file_id, stream, 2401); + + return rc; +} + +Result capscFinishAlbumMovieWriteStream(u64 stream) { + return _capscControlCmdInU64NoOut(stream, 2402); +} + +Result capscCommitAlbumMovieWriteStream(u64 stream) { + return _capscControlCmdInU64NoOut(stream, 2403); +} + +Result capscDiscardAlbumMovieWriteStream(u64 stream) { + return _capscControlCmdInU64NoOut(stream, 2404); +} + +Result capscDiscardAlbumMovieWriteStreamNoDelete(u64 stream) { + return _capscControlCmdInU64NoOut(stream, 2405); +} + +Result capscCommitAlbumMovieWriteStreamEx(u64 stream, CapsAlbumEntry *entry) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + if (!serviceIsActive(&g_capscControl)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + return serviceDispatchInOut(&g_capscControl, 2406, stream, *entry); +} + +Result capscStartAlbumMovieWriteStreamDataSection(u64 stream) { + return _capscControlCmdInU64NoOut(stream, 2411); +} + +Result capscEndAlbumMovieWriteStreamDataSection(u64 stream) { + return _capscControlCmdInU64NoOut(stream, 2412); +} + +Result capscStartAlbumMovieWriteStreamMetaSection(u64 stream) { + return _capscControlCmdInU64NoOut(stream, 2413); +} + +Result capscEndAlbumMovieWriteStreamMetaSection(u64 stream) { + return _capscControlCmdInU64NoOut(stream, 2414); +} + +Result capscReadDataFromAlbumMovieWriteStream(u64 stream, u64 offset, void* buffer, u64 size, u64 *actual_size) { + return _capscControlReadDataFromAlbumMovieStream(stream, offset, buffer, size, actual_size ,2421); +} + +static Result _capscWriteToAlbumMovieWriteStream(u64 stream, u64 offset, const void* buffer, u64 size, u32 cmd_id) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + if (!serviceIsActive(&g_capscControl)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + const struct { + u64 stream; + u64 offset; + } in = { stream, offset }; + return serviceDispatchIn(&g_capscControl, cmd_id, in, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { buffer, size } }, + ); +} + +Result capscWriteDataToAlbumMovieWriteStream(u64 stream, u64 offset, const void* buffer, u64 size) { + return _capscWriteToAlbumMovieWriteStream(stream, offset, buffer, size, 2422); +} + +Result capscWriteMetaToAlbumMovieWriteStream(u64 stream, u64 offset, const void* buffer, u64 size) { + return _capscWriteToAlbumMovieWriteStream(stream, offset, buffer, size, 2424); +} + +Result capscGetAlbumMovieWriteStreamBrokenReason(u64 stream) { + return _capscControlCmdInU64NoOut(stream, 2431); +} + +Result capscGetAlbumMovieWriteStreamDataSize(u64 stream, u64 *size) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + if (!serviceIsActive(&g_capscControl)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return serviceDispatchInOut(&g_capscControl, 2433, stream, *size); +} + +Result capscSetAlbumMovieWriteStreamDataSize(u64 stream, u64 size) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + if (!serviceIsActive(&g_capscControl)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + const struct { + u64 stream; + u64 size; + } in = { stream, size }; + return serviceDispatchIn(&g_capscControl, 2434, in); +}