diff --git a/nx/include/switch.h b/nx/include/switch.h index c4322d7b..81f14dcf 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -92,6 +92,7 @@ extern "C" { #include "switch/services/ncm.h" #include "switch/services/psc.h" #include "switch/services/caps.h" +#include "switch/services/capsa.h" #include "switch/services/capsu.h" #include "switch/services/capssc.h" #include "switch/services/capssu.h" diff --git a/nx/include/switch/runtime/devices/fs_dev.h b/nx/include/switch/runtime/devices/fs_dev.h index 0d36e609..78f7e009 100644 --- a/nx/include/switch/runtime/devices/fs_dev.h +++ b/nx/include/switch/runtime/devices/fs_dev.h @@ -38,6 +38,7 @@ Result fsdevMountSystemSaveData(const char *name, FsSaveDataSpaceId save_data_sp /// Mounts the input fs with the specified device name. fsdev will handle closing the fs when required, including when fsdevMountDevice() fails. /// Returns -1 when any errors occur. +/// Input device name string shouldn't exceed 31 characters, and shouldn't have a trailing colon. int fsdevMountDevice(const char *name, FsFileSystem fs); /// Unmounts the specified device. diff --git a/nx/include/switch/services/caps.h b/nx/include/switch/services/caps.h index bfffa09c..1fa9f5a6 100644 --- a/nx/include/switch/services/caps.h +++ b/nx/include/switch/services/caps.h @@ -22,6 +22,11 @@ typedef enum { AlbumReportOption_Enable = 1, ///< Display the screenshot-taken Overlay notification. } AlbumReportOption; +typedef enum { + CapsAlbumStorage_Nand = 0, ///< Nand + CapsAlbumStorage_Sd = 1, ///< Sd +} CapsAlbumStorage; + /// ContentType typedef enum { CapsContentType_Screenshot = 0, ///< Album screenshots. @@ -76,17 +81,18 @@ typedef struct { /// AlbumEntryId typedef struct { - u64 program_id; ///< ProgramId. + u64 application_id; ///< ApplicationId CapsAlbumFileDateTime datetime; ///< \ref CapsAlbumFileDateTime - u8 unk_x10; ///< Unknown. - u8 unk_x11; ///< Unknown. - u8 pad[6]; ///< Padding? -} CapsAlbumEntryId; + 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; /// AlbumEntry typedef struct { - u8 unk_x0[0x8]; ///< Unknown. - CapsAlbumEntryId id; ///< \ref CapsAlbumEntryId + u64 size; ///< Size. + CapsAlbumFileId file_id; ///< \ref CapsAlbumFileId } CapsAlbumEntry; /// ApplicationAlbumEntry @@ -99,10 +105,10 @@ typedef struct { } v0; ///< Pre-7.0.0 struct { - u8 unk_x0[0x8]; ///< Unknown. - u8 unk_x8[0x8]; ///< Unknown. - CapsAlbumFileDateTime datetime; ///< \ref CapsAlbumFileDateTime - u8 unk_x18[0x8]; ///< Unknown. + u64 size; ///< size of the entry + u64 application_id; ///< ApplicationId + CapsAlbumFileDateTime datetime; ///< \ref CapsAlbumFileDateTime + u8 unk_x18[0x8]; ///< Unknown. } v1; ///< [7.0.0+] }; } CapsApplicationAlbumEntry; @@ -120,6 +126,39 @@ typedef struct { u32 size; ///< UserData size. } CapsApplicationData; +/// AlbumFileContents +typedef enum { + CapsAlbumFileContents_ScreenShot = 0, + CapsAlbumFileContents_Movie = 1, + CapsAlbumFileContents_ExtraScreenShot = 2, + CapsAlbumFileContents_ExtraMovie = 3, +} CapsAlbumFileContents; + +typedef enum { + CapsAlbumContentsUsageFlag_HasGreaterUsage = BIT(0), ///< Indicates that there are additional files not captured by the count/size fields of CapsAlbumContentsUsage + CapsAlbumContentsUsageFlag_IsUnknownContents = BIT(1), ///< Indicates that the file is not a known content type +} CapsAlbumContentsUsageFlag; + +typedef struct { + s64 count; + s64 size; + u32 flags; + u8 file_contents; + u8 pad_x15[0x3]; +} CapsAlbumContentsUsage; + +typedef struct { + CapsAlbumContentsUsage usages[2]; +} CapsAlbumUsage2; + +typedef struct { + CapsAlbumContentsUsage usages[3]; +} CapsAlbumUsage3; + +typedef struct { + CapsAlbumContentsUsage usages[16]; +} CapsAlbumUsage16; + /// UserIdList typedef struct { AccountUid uids[ACC_USER_LIST_SIZE]; ///< \ref AccountUid diff --git a/nx/include/switch/services/capsa.h b/nx/include/switch/services/capsa.h new file mode 100644 index 00000000..2f5d458a --- /dev/null +++ b/nx/include/switch/services/capsa.h @@ -0,0 +1,48 @@ +/** + * @file capsa.h + * @brief Album Accessor (caps:a) service IPC wrapper. + * @author Behemoth + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "../sf/service.h" +#include "../services/caps.h" + +/// Initialize caps:a. +Result capsaInitialize(void); + +/// Exit caps:a. +void capsaExit(void); + +/// Gets the Service for caps:a. +Service* capsaGetServiceSession(void); + +Result capsaGetAlbumFileCount(CapsAlbumStorage storage, u64* count); +Result capsaGetAlbumFileList(CapsAlbumStorage storage, u64* count, CapsAlbumEntry* buffer, u64 size); +Result capsaLoadAlbumFile(const CapsAlbumFileId *file_id, u64 *out_size, void* workbuf, u64 workbuf_size); +Result capsaDeleteAlbumFile(const CapsAlbumFileId *file_id); +Result capsaStorageCopyAlbumFile(const CapsAlbumFileId *file_id, CapsAlbumStorage dst_storage); +Result capsaIsAlbumMounted(CapsAlbumStorage storage, bool* is_mounted); +Result capsaGetAlbumUsage(CapsAlbumStorage storage, CapsAlbumUsage2 *out); +Result capsaGetAlbumFileSize(const CapsAlbumFileId *file_id, u64* size); +Result capsaLoadAlbumFileThumbnail(const CapsAlbumFileId *file_id, u64 *out_size, void* workbuf, u64 workbuf_size); +/// Only available on [2.0.0+]. +Result capsaLoadAlbumScreenShotImage(u64* width, u64* height, const CapsAlbumFileId *file_id, void* workbuf, u64 workbuf_size, void* rawbuf, u64 rawbuf_size); +Result capsaLoadAlbumScreenShotThumbnailImage(u64* width, u64* height, const CapsAlbumFileId *file_id, void* workbuf, u64 workbuf_size, void* rawbuf, u64 rawbuf_size); +/// Only available on [3.0.0+]. +Result capsaLoadAlbumScreenShotImageEx(u64* width, u64* height, const CapsAlbumFileId *file_id, const CapsScreenShotDecodeOption *opts, void* workbuf, u64 workbuf_size, void* rawbuf, u64 rawbuf_size); +Result capsaLoadAlbumScreenShotThumbnailImageEx(u64* width, u64* height, const CapsAlbumFileId *file_id, const CapsScreenShotDecodeOption *opts, void* workbuf, u64 workbuf_size, void* rawbuf, u64 rawbuf_size); +/// Only available on [4.0.0+]. +Result capsaGetAlbumUsage3(CapsAlbumStorage storage, CapsAlbumUsage3 *out); +Result capsaGetAlbumMountResult(CapsAlbumStorage storage); +Result capsaGetAlbumUsage16(CapsAlbumStorage storage, CapsAlbumUsage16 *out); + +Result capsaGetAutoSavingStorage(CapsAlbumStorage* storage); +Result capsaGetRequiredStorageSpaceSizeToCopyAll(CapsAlbumStorage dst_storage, CapsAlbumStorage src_storage, u64* out); +/// Only available on [4.0.0+]. +Result capsaLoadAlbumScreenShotThumbnailImageEx1(const CapsAlbumFileId *file_id, const CapsScreenShotDecodeOption *opts, void* workbuf, u64 workbuf_size, void* rawbuf, u64 rawbuf_size, void* out, u64 out_size); + +Result capsaForceAlbumUnmounted(CapsAlbumStorage storage); +Result capsaResetAlbumMountStatus(CapsAlbumStorage storage); +Result capsaRefreshAlbumCache(CapsAlbumStorage storage); diff --git a/nx/include/switch/services/grc.h b/nx/include/switch/services/grc.h index 40fe6acb..58aa9f02 100644 --- a/nx/include/switch/services/grc.h +++ b/nx/include/switch/services/grc.h @@ -40,7 +40,7 @@ typedef struct { /// GameMovieId typedef struct { - CapsAlbumEntryId album_id; ///< \ref CapsAlbumEntryId + CapsAlbumFileId file_id; ///< \ref CapsAlbumFileId u8 reserved[0x28]; ///< Unused, always zero. } GrcGameMovieId; diff --git a/nx/include/switch/services/hid.h b/nx/include/switch/services/hid.h index 9b3eba0e..ed5725ce 100644 --- a/nx/include/switch/services/hid.h +++ b/nx/include/switch/services/hid.h @@ -760,6 +760,15 @@ u32 hidSixAxisSensorValuesRead(SixAxisSensorValues *values, HidControllerID id, /// Returns 0 when CONTROLLER_PLAYER_1 is connected, otherwise returns 1 for handheld-mode. bool hidGetHandheldMode(void); +/// SetSixAxisSensorFusionParameters. unk0 must be 0.0f-1.0f. +Result hidSetSixAxisSensorFusionParameters(u32 SixAxisSensorHandle, float unk0, float unk1); + +/// GetSixAxisSensorFusionParameters +Result hidGetSixAxisSensorFusionParameters(u32 SixAxisSensorHandle, float *unk0, float *unk1); + +/// ResetSixAxisSensorFusionParameters +Result hidResetSixAxisSensorFusionParameters(u32 SixAxisSensorHandle); + /// Sets which controller types are supported. This is automatically called with all types in \ref hidInitialize. Result hidSetSupportedNpadStyleSet(HidControllerType type); diff --git a/nx/source/services/capsa.c b/nx/source/services/capsa.c new file mode 100644 index 00000000..2d004a33 --- /dev/null +++ b/nx/source/services/capsa.c @@ -0,0 +1,193 @@ +#define NX_SERVICE_ASSUME_NON_DOMAIN +#include +#include +#include "service_guard.h" +#include "runtime/hosversion.h" +#include "services/applet.h" +#include "services/capsa.h" + +static Service g_capsaSrv; + +NX_GENERATE_SERVICE_GUARD(capsa); + +Result _capsaInitialize(void) { + return smGetService(&g_capsaSrv, "caps:a"); +} + +void _capsaCleanup(void) { + serviceClose(&g_capsaSrv); +} + +Service* capsaGetServiceSession(void) { + return &g_capsaSrv; +} + +static Result _capsaCmdInU8NoOut(Service* srv, u8 inval, u32 cmd_id) { + return serviceDispatchIn(srv, cmd_id, inval); +} + +Result capsaGetAlbumFileCount(CapsAlbumStorage storage, u64* count) { + u8 inval = storage; + return serviceDispatchInOut(&g_capsaSrv, 0, inval, *count); +} + +Result capsaGetAlbumFileList(CapsAlbumStorage storage, u64* count, CapsAlbumEntry* buffer, u64 buffer_size) { + u8 inval = storage; + return serviceDispatchInOut(&g_capsaSrv, 1, inval, *count, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { buffer, buffer_size } }, + ); +} + +Result capsaLoadAlbumFile(const CapsAlbumFileId *file_id, u64 *out_size, void* workbuf, u64 workbuf_size) { + return serviceDispatchInOut(&g_capsaSrv, 2, *file_id, *out_size, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { workbuf, workbuf_size } }, + ); +} + +Result capsaDeleteAlbumFile(const CapsAlbumFileId *file_id) { + return serviceDispatchIn(&g_capsaSrv, 3, *file_id); +} + +Result capsaStorageCopyAlbumFile(const CapsAlbumFileId* file_id, CapsAlbumStorage dst_storage) { + struct { + u8 storage; + u8 pad_x1[0x7]; + CapsAlbumFileId file_id; + } in = { dst_storage, {0}, *file_id }; + return serviceDispatchIn(&g_capsaSrv, 4, in); +} + +Result capsaIsAlbumMounted(CapsAlbumStorage storage, bool* is_mounted) { + u8 inval = storage; + return serviceDispatchInOut(&g_capsaSrv, 5, inval, *is_mounted); +} + +Result capsaGetAlbumUsage(CapsAlbumStorage storage, CapsAlbumUsage2 *out) { + u8 inval = storage; + return serviceDispatchInOut(&g_capsaSrv, 6, inval, *out); +} + +Result capsaGetAlbumFileSize(const CapsAlbumFileId *file_id, u64* size) { + return serviceDispatchInOut(&g_capsaSrv, 7, *file_id, *size); +} + +Result capsaLoadAlbumFileThumbnail(const CapsAlbumFileId *file_id, u64 *out_size, void* workbuf, u64 workbuf_size) { + return serviceDispatchInOut(&g_capsaSrv, 8, *file_id, *out_size, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { workbuf, workbuf_size } }, + ); +} + +static Result _capsaLoadAlbumScreenshot(u64* width, u64* height, const CapsAlbumFileId *file_id, void* workbuf, u64 workbuf_size, void* rawbuf, u64 rawbuf_size, u32 cmd_id) { + if (hosversionBefore(2,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + struct { + u64 width; + u64 height; + } out; + Result rc = serviceDispatchInOut(&g_capsaSrv, cmd_id, *file_id, out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_HipcMapTransferAllowsNonSecure, SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { rawbuf, rawbuf_size }, { workbuf, workbuf_size } }, + ); + *width = out.width; + *height = out.height; + return rc; +} + +Result capsaLoadAlbumScreenShotImage(u64* width, u64* height, const CapsAlbumFileId *file_id, void* workbuf, u64 workbuf_size, void* rawbuf, u64 rawbuf_size) { + return _capsaLoadAlbumScreenshot(width, height, file_id, workbuf, workbuf_size, rawbuf, rawbuf_size, 9); +} + +Result capsaLoadAlbumScreenShotThumbnailImage(u64* width, u64* height, const CapsAlbumFileId *file_id, void* workbuf, u64 workbuf_size, void* rawbuf, u64 rawbuf_size) { + return _capsaLoadAlbumScreenshot(width, height, file_id, workbuf, workbuf_size, rawbuf, rawbuf_size, 10); +} + +static Result _capsaLoadAlbumScreenshotEx(u64* width, u64* height, const CapsAlbumFileId *file_id, const CapsScreenShotDecodeOption *opts, void* workbuf, u64 workbuf_size, void* rawbuf, u64 rawbuf_size, u32 cmd_id) { + if (hosversionBefore(3,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + struct { + CapsAlbumFileId file_id; + CapsScreenShotDecodeOption opts; + } in = { *file_id, *opts }; + struct { + u64 width; + u64 height; + } out; + Result rc = serviceDispatchInOut(&g_capsaSrv, cmd_id, in, out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_HipcMapTransferAllowsNonSecure, SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { rawbuf, rawbuf_size }, { workbuf, workbuf_size } }, + ); + *width = out.width; + *height = out.height; + return rc; +} + +Result capsaLoadAlbumScreenShotImageEx(u64* width, u64* height, const CapsAlbumFileId *file_id, const CapsScreenShotDecodeOption *opts, void* workbuf, u64 workbuf_size, void* rawbuf, u64 rawbuf_size) { + return _capsaLoadAlbumScreenshotEx(width, height, file_id, opts, workbuf, workbuf_size, rawbuf, rawbuf_size, 12); +} + +Result capsaLoadAlbumScreenShotThumbnailImageEx(u64* width, u64* height, const CapsAlbumFileId *file_id, const CapsScreenShotDecodeOption *opts, void* workbuf, u64 workbuf_size, void* rawbuf, u64 rawbuf_size) { + return _capsaLoadAlbumScreenshotEx(width, height, file_id, opts, workbuf, workbuf_size, rawbuf, rawbuf_size, 13); +} + +Result capsaGetAlbumUsage3(CapsAlbumStorage storage, CapsAlbumUsage3 *out) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + u8 inval = storage; + return serviceDispatchInOut(&g_capsaSrv, 15, inval, *out); +} + +Result capsaGetAlbumMountResult(CapsAlbumStorage storage) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + return _capsaCmdInU8NoOut(&g_capsaSrv, storage, 16); +} + +Result capsaGetAlbumUsage16(CapsAlbumStorage storage, CapsAlbumUsage16 *out) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + u8 inval = storage; + return serviceDispatchInOut(&g_capsaSrv, 17, inval, *out); +} + +Result capsaGetAutoSavingStorage(CapsAlbumStorage* storage) { + u8 tmpval = 0; + Result rc = serviceDispatchOut(&g_capsaSrv, 401, tmpval); + *storage = tmpval; + return rc; +} + +Result capsaGetRequiredStorageSpaceSizeToCopyAll(CapsAlbumStorage dst_storage, CapsAlbumStorage src_storage, u64* out) { + struct { + u8 dest; + u8 src; + } in = { dst_storage, src_storage }; + return serviceDispatchInOut(&g_capsaSrv, 501, in, *out); +} + +Result capsaLoadAlbumScreenShotThumbnailImageEx1(const CapsAlbumFileId *file_id, const CapsScreenShotDecodeOption *opts, void* work_buffer, u64 work_buffer_size, void* raw_buffer, u64 raw_buffer_size, void* out, u64 out_size) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + struct { + CapsAlbumFileId file_id; + CapsScreenShotDecodeOption opts; + } in = { *file_id, *opts }; + return serviceDispatchIn(&g_capsaSrv, 1003, in, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_FixedSize, SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_HipcMapTransferAllowsNonSecure, SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { out, out_size }, { raw_buffer, raw_buffer_size }, { work_buffer, work_buffer_size } }, + ); +} + +Result capsaForceAlbumUnmounted(CapsAlbumStorage storage) { + return _capsaCmdInU8NoOut(&g_capsaSrv, storage, 8001); +} + +Result capsaResetAlbumMountStatus(CapsAlbumStorage storage) { + return _capsaCmdInU8NoOut(&g_capsaSrv, storage, 8002); +} + +Result capsaRefreshAlbumCache(CapsAlbumStorage storage) { + return _capsaCmdInU8NoOut(&g_capsaSrv, storage, 8011); +} diff --git a/nx/source/services/hid.c b/nx/source/services/hid.c index 0c5c0702..bab1f175 100644 --- a/nx/source/services/hid.c +++ b/nx/source/services/hid.c @@ -11,6 +11,7 @@ static Service g_hidSrv; static Service g_hidIAppletResource; +static Service g_hidIActiveVibrationDeviceList; static SharedMemory g_hidSharedmem; static HidTouchScreenEntry g_touchEntry; @@ -103,6 +104,8 @@ Result _hidInitialize(void) { } void _hidCleanup(void) { + serviceClose(&g_hidIActiveVibrationDeviceList); + hidFinalizeSevenSixAxisSensor(); hidSetNpadJoyHoldType(HidJoyHoldType_Default); @@ -755,6 +758,61 @@ static Result _hidGetSharedMemoryHandle(Service* srv, Handle* handle_out) { return _hidCmdGetHandle(srv, handle_out, 0); } +Result hidSetSixAxisSensorFusionParameters(u32 SixAxisSensorHandle, float unk0, float unk1) { + if (unk0 < 0.0f || unk0 > 1.0f) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + Result rc; + u64 AppletResourceUserId; + + rc = appletGetAppletResourceUserId(&AppletResourceUserId); + if (R_FAILED(rc)) + AppletResourceUserId = 0; + + const struct { + u32 SixAxisSensorHandle; + float unk0; + float unk1; + u32 pad; + u64 AppletResourceUserId; + } in = { SixAxisSensorHandle, unk0, unk1, 0, AppletResourceUserId }; + + return serviceDispatchIn(&g_hidSrv, 70, in, + .in_send_pid = true, + ); +} + +Result hidGetSixAxisSensorFusionParameters(u32 SixAxisSensorHandle, float *unk0, float *unk1) { + Result rc; + u64 AppletResourceUserId; + + rc = appletGetAppletResourceUserId(&AppletResourceUserId); + if (R_FAILED(rc)) + AppletResourceUserId = 0; + + const struct { + u32 SixAxisSensorHandle; + u32 pad; + u64 AppletResourceUserId; + } in = { SixAxisSensorHandle, 0, AppletResourceUserId }; + + struct { + float unk0; + float unk1; + } out; + + rc = serviceDispatchInOut(&g_hidSrv, 71, in, out, + .in_send_pid = true, + ); + if (R_SUCCEEDED(rc) && unk0) *unk0 = out.unk0; + if (R_SUCCEEDED(rc) && unk1) *unk1 = out.unk1; + return rc; +} + +Result hidResetSixAxisSensorFusionParameters(u32 SixAxisSensorHandle) { + return _hidCmdWithInputU32(SixAxisSensorHandle, 72); +} + Result hidSetSupportedNpadStyleSet(HidControllerType type) { return _hidCmdWithInputU32(type, 100); } @@ -1043,19 +1101,20 @@ static Result _hidGetDeviceHandles(u32 devicetype, u32 *DeviceHandles, s32 total Result hidInitializeVibrationDevices(u32 *VibrationDeviceHandles, s32 total_handles, HidControllerID id, HidControllerType type) { Result rc=0; - Service srv; s32 i; rc = _hidGetDeviceHandles(0, VibrationDeviceHandles, total_handles, id, type); if (R_FAILED(rc)) return rc; - for (i=0; i