diff --git a/nx/external/bsd/include/netinet/in.h b/nx/external/bsd/include/netinet/in.h index 0f624398..2e66e057 100644 --- a/nx/external/bsd/include/netinet/in.h +++ b/nx/external/bsd/include/netinet/in.h @@ -588,37 +588,37 @@ struct group_source_req { * Unspecified */ #define IN6_IS_ADDR_UNSPECIFIED(a) \ - ((a)->__u6_addr.__u6_addr32[0] == 0 && \ - (a)->__u6_addr.__u6_addr32[1] == 0 && \ - (a)->__u6_addr.__u6_addr32[2] == 0 && \ - (a)->__u6_addr.__u6_addr32[3] == 0) + ((a)->__u6_addr32[0] == 0 && \ + (a)->__u6_addr32[1] == 0 && \ + (a)->__u6_addr32[2] == 0 && \ + (a)->__u6_addr32[3] == 0) /* * Loopback */ #define IN6_IS_ADDR_LOOPBACK(a) \ - ((a)->__u6_addr.__u6_addr32[0] == 0 && \ - (a)->__u6_addr.__u6_addr32[1] == 0 && \ - (a)->__u6_addr.__u6_addr32[2] == 0 && \ - (a)->__u6_addr.__u6_addr32[3] == ntohl(1)) + ((a)->__u6_addr32[0] == 0 && \ + (a)->__u6_addr32[1] == 0 && \ + (a)->__u6_addr32[2] == 0 && \ + (a)->__u6_addr32[3] == ntohl(1)) /* * IPv4 compatible */ #define IN6_IS_ADDR_V4COMPAT(a) \ - ((a)->__u6_addr.__u6_addr32[0] == 0 && \ - (a)->__u6_addr.__u6_addr32[1] == 0 && \ - (a)->__u6_addr.__u6_addr32[2] == 0 && \ - (a)->__u6_addr.__u6_addr32[3] != 0 && \ - (a)->__u6_addr.__u6_addr32[3] != ntohl(1)) + ((a)->__u6_addr32[0] == 0 && \ + (a)->__u6_addr32[1] == 0 && \ + (a)->__u6_addr32[2] == 0 && \ + (a)->__u6_addr32[3] != 0 && \ + (a)->__u6_addr32[3] != ntohl(1)) /* * Mapped */ #define IN6_IS_ADDR_V4MAPPED(a) \ - ((a)->__u6_addr.__u6_addr32[0] == 0 && \ - (a)->__u6_addr.__u6_addr32[1] == 0 && \ - (a)->__u6_addr.__u6_addr32[2] == ntohl(0x0000ffff)) + ((a)->__u6_addr32[0] == 0 && \ + (a)->__u6_addr32[1] == 0 && \ + (a)->__u6_addr32[2] == ntohl(0x0000ffff)) #define __IPV6_ADDR_SCOPE_NODELOCAL 0x01 #define __IPV6_ADDR_SCOPE_INTFACELOCAL 0x01 diff --git a/nx/include/switch.h b/nx/include/switch.h index 7dda99f6..3a965f26 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -78,6 +78,7 @@ extern "C" { #include "switch/nvidia/cmds/3d_clear.h" #include "switch/runtime/env.h" +#include "switch/runtime/nxlink.h" #include "switch/runtime/util/utf.h" diff --git a/nx/include/switch/kernel/detect.h b/nx/include/switch/kernel/detect.h index 2c5ee370..6d86a09e 100644 --- a/nx/include/switch/kernel/detect.h +++ b/nx/include/switch/kernel/detect.h @@ -13,5 +13,7 @@ bool kernelAbove200(void); bool kernelAbove300(void); /// Returns true if the kernel version is equal to or above 4.0.0. bool kernelAbove400(void); +/// Returns true if the kernel version is equal to or above 5.0.0. +bool kernelAbove500(void); /// Returns true if the process has a debugger attached. bool detectDebugger(void); diff --git a/nx/include/switch/kernel/ipc.h b/nx/include/switch/kernel/ipc.h index 87514790..36290f06 100644 --- a/nx/include/switch/kernel/ipc.h +++ b/nx/include/switch/kernel/ipc.h @@ -395,7 +395,7 @@ static inline Result ipcParse(IpcParsedCommand* r) { r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36)); r->BufferSizes[i] = desc->Size; - r->BufferTypes[i] = packed & 3; + r->BufferTypes[i] = (BufferType) (packed & 3); if (i < num_bufs_send) r->BufferDirections[i] = BufferDirection_Send; diff --git a/nx/include/switch/nvidia/cmds/common.h b/nx/include/switch/nvidia/cmds/common.h deleted file mode 100644 index e69de29b..00000000 diff --git a/nx/include/switch/runtime/devices/usb_comms.h b/nx/include/switch/runtime/devices/usb_comms.h index 7c2c3efb..5c11d8d3 100644 --- a/nx/include/switch/runtime/devices/usb_comms.h +++ b/nx/include/switch/runtime/devices/usb_comms.h @@ -11,5 +11,21 @@ Result usbCommsInitialize(void); void usbCommsExit(void); +/// Same as usbCommsInitialize, except this can be used after usbCommsInitialize (or instead of usbCommsInitialize), for creating new interface(s). +/// bInterface* are the values for the same fields in usb.h \ref usb_interface_descriptor. \ref usbCommsInitialize uses USB_CLASS_VENDOR_SPEC for all of these internally. +Result usbCommsInitializeEx(u32 *interface, u8 bInterfaceClass, u8 bInterfaceSubClass, u8 bInterfaceProtocol); + +/// Shutdown the specified interface. If no interfaces are remaining, this then uses \ref usbCommsExit internally. +void usbCommsExitEx(u32 interface); + +/// Read data with the default interface. size_t usbCommsRead(void* buffer, size_t size); + +/// Write data with the default interface. size_t usbCommsWrite(const void* buffer, size_t size); + +/// Same as usbCommsRead except with the specified interface. +size_t usbCommsReadEx(void* buffer, size_t size, u32 interface); + +/// Same as usbCommsWrite except with the specified interface. +size_t usbCommsWriteEx(const void* buffer, size_t size, u32 interface); diff --git a/nx/include/switch/runtime/env.h b/nx/include/switch/runtime/env.h index af2e0d57..b21db618 100644 --- a/nx/include/switch/runtime/env.h +++ b/nx/include/switch/runtime/env.h @@ -85,3 +85,6 @@ Result envSetNextLoad(const char* path, const char* argv); /// Returns true if the environment supports envSetNextLoad. bool envHasNextLoad(void); + +/// Returns the Result from the last NRO. +Result envGetLastLoadResult(void); diff --git a/nx/include/switch/runtime/nxlink.h b/nx/include/switch/runtime/nxlink.h new file mode 100644 index 00000000..0f178811 --- /dev/null +++ b/nx/include/switch/runtime/nxlink.h @@ -0,0 +1,8 @@ +#pragma once + +struct in_addr; + +extern struct in_addr __nxlink_host; + +#define NXLINK_SERVER_PORT 28280 +#define NXLINK_CLIENT_PORT 28771 diff --git a/nx/include/switch/services/acc.h b/nx/include/switch/services/acc.h index 481320d7..35480055 100644 --- a/nx/include/switch/services/acc.h +++ b/nx/include/switch/services/acc.h @@ -8,9 +8,45 @@ #include "../types.h" #include "sm.h" +typedef struct { + Service s; +} AccountProfile; + +typedef struct +{ + u32 unk_x0; + u32 iconID; ///< Icon ID. 0 = Mii, the rest are character icon IDs. + u8 iconBackgroundColorID; ///< Profile icon background color ID + u8 unk_x9[0x7]; + u8 miiID[0x10]; ///< Some ID related to the Mii? All zeros when a character icon is used. + u8 unk_x20[0x60]; ///< Usually zeros? +} PACKED AccountUserData; + +typedef struct +{ + u128 userID; + u64 lastEditTimestamp; ///< POSIX UTC timestamp, for the last account edit. + char username[0x20]; ///< UTF-8 Username. +} PACKED AccountProfileBase; + Result accountInitialize(void); void accountExit(void); Service* accountGetService(void); /// Get the userID for the currently active user. The output userID is only valid when the output account_selected==1, otherwise no user is currently selected. +/// An user is only selected when the user-account selection applet was used to select an user at least once before. Result accountGetActiveUser(u128 *userID, bool *account_selected); + +/// Get an AccountProfile for the specified userID. +Result accountGetProfile(AccountProfile* out, u128 userID); + +/// Get \ref AccountUserData and \ref AccountProfileBase for the specified profile, userdata is optional (can be NULL). +Result accountProfileGet(AccountProfile* profile, AccountUserData* userdata, AccountProfileBase* profilebase); + +/// Get the icon image size. +Result accountProfileGetImageSize(AccountProfile* profile, size_t* image_size); + +/// Load the JPEG profile icon, valid for both Miis and character icons. The output image_size is the same as the one from \ref accountProfileGetImageSize. +Result accountProfileLoadImage(AccountProfile* profile, void* buf, size_t len, size_t* image_size); + +void accountProfileClose(AccountProfile* profile); diff --git a/nx/include/switch/services/fs.h b/nx/include/switch/services/fs.h index 4ea337eb..56e1ac22 100644 --- a/nx/include/switch/services/fs.h +++ b/nx/include/switch/services/fs.h @@ -1,7 +1,7 @@ /** * @file fs.h * @brief Filesystem (fsp-srv) service IPC wrapper. - * Normally applications should just use standard stdio not FS-serv directly. However this can be used if obtaining a FsFileSystem, FsFile, or FsStorage, for mounting with fs_dev/romfs_dev. + * Normally applications should just use standard stdio not FS-serv directly. However this can be used if obtaining a FsFileSystem, FsFile, or FsStorage, for mounting with fs_dev/romfs_dev, etc. * @author plutoo * @author yellows8 * @copyright libnx Authors @@ -14,31 +14,32 @@ #define FS_MAX_PATH 0x301 -/// For use with fsMountSaveData(). -#define FS_MOUNTSAVEDATA_INVAL_DEFAULT 0x1 - /// For use with FsSave. #define FS_SAVEDATA_CURRENT_TITLEID 0 -/// For use with FsSave. +/// For use with \ref FsSave and \ref FsSaveDataInfo. #define FS_SAVEDATA_USERID_COMMONSAVE 0 typedef struct { - Handle h; + Service s; } FsFileSystem; typedef struct { - Handle h; + Service s; } FsFile; typedef struct { - Handle h; + Service s; } FsDir; typedef struct { - Handle h; + Service s; } FsStorage; +typedef struct { + Service s; +} FsSaveDataIterator; + /// Directory entry. typedef struct { @@ -55,12 +56,25 @@ typedef struct u64 titleID; ///< titleID of the savedata to access when accessing other titles' savedata via SaveData, otherwise FS_SAVEDATA_CURRENT_TITLEID. u128 userID; ///< userID of the user-specific savedata to access, otherwise FS_SAVEDATA_USERID_COMMONSAVE. See account.h. u64 saveID; ///< saveID, 0 for SaveData. - u64 ContentStorageId; ///< ContentStorageId? See FsContentStorageId. + u64 SaveDataType; ///< See \ref FsSaveDataType. u64 unk_x28; ///< 0 for SystemSaveData/SaveData. u64 unk_x30; ///< 0 for SystemSaveData/SaveData. u64 unk_x38; ///< 0 for SystemSaveData/SaveData. } PACKED FsSave; +typedef struct +{ + u64 saveID_unk; + u8 SaveDataSpaceId; ///< See \ref FsSaveDataSpaceId. + u8 SaveDataType; ///< See \ref FsSaveDataType. + u8 pad[6]; + u128 userID; ///< See userID for \ref FsSave. + u64 saveID; ///< See saveID for \ref FsSave. + u64 titleID; ///< titleID for FsSaveDataType_SaveData. + u64 size; ///< Raw saveimage size. + u8 unk_x38[0x28]; ///< Unknown. Usually zeros? +} PACKED FsSaveDataInfo; + typedef enum { ENTRYTYPE_DIR = 0, ENTRYTYPE_FILE = 1 @@ -80,6 +94,16 @@ typedef enum FS_DIROPEN_FILE = BIT(1), ///< Enable reading file entries. } FsDirectoryFlags; +typedef enum +{ + FsStorageId_None = 0, + FsStorageId_Host = 1, + FsStorageId_GameCard = 2, + FsStorageId_NandSystem = 3, + FsStorageId_NandUser = 4, + FsStorageId_SdCard = 5, +} FsStorageId; + typedef enum { FS_CONTENTSTORAGEID_NandSystem = 0, @@ -87,6 +111,26 @@ typedef enum FS_CONTENTSTORAGEID_SdCard = 2, } FsContentStorageId; +typedef enum +{ + FsSaveDataSpaceId_NandSystem = 0, + FsSaveDataSpaceId_NandUser = 1, + FsSaveDataSpaceId_SdCard = 2, + FsSaveDataSpaceId_TemporaryStorage = 3, + + FsSaveDataSpaceId_All = -1, ///< Pseudo value for fsOpenSaveDataIterator(). +} FsSaveDataSpaceId; + +typedef enum +{ + FsSaveDataType_SystemSaveData = 0, + FsSaveDataType_SaveData = 1, + FsSaveDataType_BcatDeliveryCacheStorage = 2, + FsSaveDataType_DeviceSaveData = 3, + FsSaveDataType_TemporaryStorage = 4, ///< [3.0.0+] + FsSaveDataType_CacheStorage = 5, ///< [3.0.0+] +} FsSaveDataType; + Result fsInitialize(void); void fsExit(void); @@ -96,6 +140,8 @@ Service* fsGetServiceSession(void); Result fsMountSdcard(FsFileSystem* out); Result fsMountSaveData(FsFileSystem* out, u8 inval, FsSave *save); +Result fsMountSystemSaveData(FsFileSystem* out, u8 inval, FsSave *save); +Result fsOpenSaveDataIterator(FsSaveDataIterator* out, s32 SaveDataSpaceId); Result fsOpenDataStorageByCurrentProcess(FsStorage* out); // todo: Rest of commands here @@ -105,6 +151,10 @@ Result fsOpenDataStorageByCurrentProcess(FsStorage* out); /// See FsSave for titleID and userID. Result fsMount_SaveData(FsFileSystem* out, u64 titleID, u128 userID); +/// Wrapper for fsMountSystemSaveData. +/// WARNING: You can brick when writing to SystemSaveData, if the data is corrupted etc. +Result fsMount_SystemSaveData(FsFileSystem* out, u64 saveID); + // IFileSystem Result fsFsCreateFile(FsFileSystem* fs, const char* path, size_t size, int flags); Result fsFsDeleteFile(FsFileSystem* fs, const char* path); @@ -137,3 +187,9 @@ void fsDirClose(FsDir* d); // IStorage Result fsStorageRead(FsStorage* s, u64 off, void* buf, size_t len); void fsStorageClose(FsStorage* s); + +// ISaveDataInfoReader + +/// Read FsSaveDataInfo data into the buf array. +Result fsSaveDataIteratorRead(FsSaveDataIterator *s, FsSaveDataInfo* buf, size_t max_entries, size_t* total_entries); +void fsSaveDataIteratorClose(FsSaveDataIterator *s); diff --git a/nx/include/switch/services/hid.h b/nx/include/switch/services/hid.h index cb53f2be..29a3124e 100644 --- a/nx/include/switch/services/hid.h +++ b/nx/include/switch/services/hid.h @@ -310,7 +310,7 @@ typedef enum CONTROLLER_PLAYER_8 = 7, CONTROLLER_HANDHELD = 8, CONTROLLER_UNKNOWN = 9, - CONTROLLER_P1_AUTO = 10, /// Not an actual HID-sysmodule ID. Only for hidKeys*(). Automatically uses CONTROLLER_PLAYER_1 when connected, otherwise uses CONTROLLER_HANDHELD. + CONTROLLER_P1_AUTO = 10, /// Not an actual HID-sysmodule ID. Only for hidKeys*()/hidJoystickRead(). Automatically uses CONTROLLER_PLAYER_1 when connected, otherwise uses CONTROLLER_HANDHELD. } HidControllerID; typedef struct touchPosition @@ -581,17 +581,23 @@ void hidTouchRead(touchPosition *pos, u32 point_id); void hidJoystickRead(JoystickPosition *pos, HidControllerID id, HidControllerJoystick stick); +/// This can be used to check what CONTROLLER_P1_AUTO uses. +/// Returns 0 when CONTROLLER_PLAYER_1 is connected, otherwise returns 1 for handheld-mode. +bool hidGetHandheldMode(void); + /// Use this if you want to use a single joy-con as a dedicated CONTROLLER_PLAYER_*. /// When used, both joy-cons in a pair should be used with this (CONTROLLER_PLAYER_1 and CONTROLLER_PLAYER_2 for example). /// id must be CONTROLLER_PLAYER_*. Result hidSetNpadJoyAssignmentModeSingleByDefault(HidControllerID id); +/// Use this if you want to use a pair of joy-cons as a single CONTROLLER_PLAYER_*. Only necessary if you want to use this mode in your application after \ref hidSetNpadJoyAssignmentModeSingleByDefault was used with this pair of joy-cons. /// Used automatically during app startup/exit for all controllers. /// When used, both joy-cons in a pair should be used with this (CONTROLLER_PLAYER_1 and CONTROLLER_PLAYER_2 for example). /// id must be CONTROLLER_PLAYER_*. Result hidSetNpadJoyAssignmentModeDual(HidControllerID id); -Result hidInitializeVibrationDevices(u32 *VibrationDeviceHandles, size_t total_handles, HidControllerID id, HidControllerLayoutType type); +Result hidInitializeVibrationDevices(u32 *VibrationDeviceHandles, size_t total_handles, HidControllerID id, HidControllerType type); +/// Send the VibrationValue to the specified VibrationDeviceHandle. Result hidSendVibrationValue(u32 *VibrationDeviceHandle, HidVibrationValue *VibrationValue); /// Sets whether vibration is allowed, this also affects the config displayed by System Settings. @@ -599,3 +605,6 @@ Result hidPermitVibration(bool flag); /// Gets whether vibration is allowed. Result hidIsVibrationPermitted(bool *flag); + +/// Send VibrationValues[index] to VibrationDeviceHandles[index], where count is the number of entries in the VibrationDeviceHandles/VibrationValues arrays. +Result hidSendVibrationValues(u32 *VibrationDeviceHandles, HidVibrationValue *VibrationValues, size_t count); diff --git a/nx/include/switch/services/pm.h b/nx/include/switch/services/pm.h index 6a1c8e3c..4312c49a 100644 --- a/nx/include/switch/services/pm.h +++ b/nx/include/switch/services/pm.h @@ -2,6 +2,7 @@ * @file pm.h * @brief Process management (pm*) service IPC wrapper. * @author plutoo + * @author yellows8 * @copyright libnx Authors */ #pragma once @@ -10,8 +11,13 @@ Result pmdmntInitialize(void); void pmdmntExit(void); +Result pmshellInitialize(void); +void pmshellExit(void); + Result pmdmntStartProcess(u64 pid); Result pmdmntGetTitlePid(u64* pid_out, u64 title_id); Result pmdmntEnableDebugForTitleId(Handle* handle_out, u64 title_id); Result pmdmntGetApplicationPid(u64* pid_out); Result pmdmntEnableDebugForApplication(Handle* handle_out); + +Result pmshellLaunchProcess(u32 launch_flags, u64 titleID, u64 storageID, u64 *pid); diff --git a/nx/source/display/gfx.c b/nx/source/display/gfx.c index 31305786..2ae5f139 100644 --- a/nx/source/display/gfx.c +++ b/nx/source/display/gfx.c @@ -34,8 +34,6 @@ static GfxMode g_gfxMode = GfxMode_LinearDouble; static u8 *g_gfxFramebufLinear; -static s32 g_gfxPixelFormat = 0; - size_t g_gfx_framebuf_width=0, g_gfx_framebuf_aligned_width=0; size_t g_gfx_framebuf_height=0, g_gfx_framebuf_aligned_height=0; size_t g_gfx_framebuf_display_width=0, g_gfx_framebuf_display_height=0; @@ -138,7 +136,7 @@ static Result _gfxDequeueBuffer(void) { memcpy(&tmp_fence, fence, sizeof(BqFence));//Offical sw waits on the fence from the previous DequeueBuffer call. Using the fence from the current DequeueBuffer call results in nvgfxEventWait() failing. - rc = bqDequeueBuffer(async, g_gfx_framebuf_width, g_gfx_framebuf_height, g_gfxPixelFormat, 0x300, &g_gfxCurrentProducerBuffer, fence); + rc = bqDequeueBuffer(async, g_gfx_framebuf_width, g_gfx_framebuf_height, 0, 0x300, &g_gfxCurrentProducerBuffer, fence); //Only run nvgfxEventWait when the fence is valid and the id is not NO_FENCE. if (R_SUCCEEDED(rc) && tmp_fence.is_valid && tmp_fence.nv_fences[0].id!=0xffffffff) rc = nvgfxEventWait(tmp_fence.nv_fences[0].id, tmp_fence.nv_fences[0].value, -1); @@ -183,7 +181,6 @@ static Result _gfxInit(ViServiceType servicetype, const char *DisplayName, u32 L g_gfx_drawflip = true; g_gfxQueueBufferData.transform = NATIVE_WINDOW_TRANSFORM_FLIP_V; - g_gfxPixelFormat = 0; memset(g_gfx_ProducerSlotsRequested, 0, sizeof(g_gfx_ProducerSlotsRequested)); memset(&g_gfx_DequeueBuffer_fence, 0, sizeof(g_gfx_DequeueBuffer_fence)); @@ -536,10 +533,6 @@ void gfxConfigureTransform(u32 transform) { g_gfxQueueBufferData.transform = transform; } -/*void gfxSetPixelFormat(s32 format) { - g_gfxPixelFormat = format; -}*/ - void gfxFlushBuffers(void) { u32 *actual_framebuf = (u32*)&g_gfxFramebuf[g_gfxCurrentBuffer*g_gfx_singleframebuf_size]; diff --git a/nx/source/kernel/detect.c b/nx/source/kernel/detect.c index d0ff6074..e52e59a2 100644 --- a/nx/source/kernel/detect.c +++ b/nx/source/kernel/detect.c @@ -1,24 +1,41 @@ // Copyright 2017 plutoo #include "types.h" #include "kernel/detect.h" +#include "kernel/mutex.h" #include "kernel/svc.h" static bool g_IsAbove200; static bool g_IsAbove300; static bool g_IsAbove400; +static bool g_IsAbove500; static bool g_HasCached = 0; +static Mutex g_Mutex; static void _CacheValues(void) { - // This is actually thread safe, might cache twice but that's fine. - if (!g_HasCached) - { - u64 tmp; - g_IsAbove200 = (svcGetInfo(&tmp, 12, INVALID_HANDLE, 0) != 0xF001); - g_IsAbove300 = (svcGetInfo(&tmp, 18, INVALID_HANDLE, 0) != 0xF001); - g_IsAbove400 = (svcGetInfo(&tmp, 19, INVALID_HANDLE, 0) != 0xF001); - g_HasCached = true; + if (g_HasCached) + return; + + mutexLock(&g_Mutex); + + if (g_HasCached) { + mutexUnlock(&g_Mutex); + return; } + + u64 tmp; + g_IsAbove200 = (svcGetInfo(&tmp, 12, INVALID_HANDLE, 0) != 0xF001); + g_IsAbove300 = (svcGetInfo(&tmp, 18, INVALID_HANDLE, 0) != 0xF001); + g_IsAbove400 = (svcGetInfo(&tmp, 19, INVALID_HANDLE, 0) != 0xF001); + g_IsAbove500 = (svcGetInfo(&tmp, 20, INVALID_HANDLE, 0) != 0xF001); + + g_IsAbove400 |= g_IsAbove500; + g_IsAbove300 |= g_IsAbove400; + g_IsAbove200 |= g_IsAbove300; + + g_HasCached = true; + + mutexUnlock(&g_Mutex); } bool kernelAbove200(void) { @@ -36,6 +53,11 @@ bool kernelAbove400(void) { return g_IsAbove400; } +bool kernelAbove500(void) { + _CacheValues(); + return g_IsAbove500; +} + bool detectDebugger(void) { u64 tmp; svcGetInfo(&tmp, 8, 0, 0); diff --git a/nx/source/runtime/argv.c b/nx/source/runtime/argv.c index 13c89c68..6796621b 100644 --- a/nx/source/runtime/argv.c +++ b/nx/source/runtime/argv.c @@ -1,5 +1,8 @@ #include #include +#include +#include + #include "result.h" #include "runtime/env.h" #include "kernel/svc.h" @@ -7,6 +10,7 @@ // System globals we define here int __system_argc; char** __system_argv; +struct in_addr __nxlink_host; extern char* fake_heap_start; extern char* fake_heap_end; @@ -15,6 +19,8 @@ extern u32 __argdata__; static char* g_argv_empty = NULL; +void nxlinkSetup(void); + void argvSetup(void) { Result rc=0; @@ -136,6 +142,12 @@ void argvSetup(void) __system_argc++; } + + // Check for nxlink parameters + nxlinkSetup(); + __system_argv[__system_argc] = NULL; + + } diff --git a/nx/source/runtime/devices/socket.c b/nx/source/runtime/devices/socket.c index a7c807fb..b5a3a840 100644 --- a/nx/source/runtime/devices/socket.c +++ b/nx/source/runtime/devices/socket.c @@ -553,7 +553,7 @@ int ioctl(int fd, int request, ...) { if(flags == -1) return -1; flags = *(int *)data != 0 ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK); - return fcntl(fd, F_SETFL, 0); + return fcntl(fd, F_SETFL, flags); } case BIOCSETF: case BIOCSETWF: @@ -584,9 +584,35 @@ int ioctl(int fd, int request, ...) { } } +#define O_NONBLOCK_NX 0x800 + +#define ALL_NX (O_NONBLOCK_NX) +#define ALL_FLAGS (O_NONBLOCK) + +static int from_nx(int flags) { + int newflags = 0; + + if(flags & O_NONBLOCK_NX) + newflags |= O_NONBLOCK; + /* add other flag translations here */ + + return newflags; +} + +static int to_nx(int flags) { + int newflags = 0; + + if(flags & O_NONBLOCK) + newflags |= O_NONBLOCK_NX; + /* add other flag translations here */ + + return newflags; +} + + int fcntl(int fd, int cmd, ...) { va_list args; - int flags; + int flags=0; /* bsd:u/s only supports F_GETFL and F_SETFL with the O_NONBLOCK flag (or 0). @@ -595,14 +621,30 @@ int fcntl(int fd, int cmd, ...) { */ if(cmd != F_GETFL && cmd != F_SETFL) return EOPNOTSUPP; - va_start(args, cmd); - flags = va_arg(args, int); - va_end(args); + + if (cmd == F_SETFL) { + + va_start(args, cmd); + flags = va_arg(args, int); + va_end(args); + + if (flags & ~ALL_FLAGS) { + errno = EINVAL; + return -1; + } + flags = to_nx(flags); + } fd = _socketGetFd(fd); if(fd == -1) return -1; - return _socketParseBsdResult(NULL, bsdFcntl(fd, cmd, flags)); + + flags = _socketParseBsdResult(NULL, bsdFcntl(fd, cmd, flags)); + + if (flags & ~ALL_NX) { + /* report unknown flags here */ + } + return from_nx(flags); } int shutdown(int sockfd, int how) { @@ -664,7 +706,7 @@ int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, s /***********************************************************************************************************************/ -// Adapted from ctrulib +// Adapted from libctru static int _socketInetAtonDetail(int *outBase, size_t *outNumBytes, const char *cp, struct in_addr *inp) { int base; u32 val; @@ -749,7 +791,7 @@ static int _socketInetAtonDetail(int *outBase, size_t *outNumBytes, const char * return 1; } -// Adapted from ctrulib +// Adapted from libctru static const char *inet_ntop4(const void *src, char *dst, socklen_t size) { const u8 *ip = src; diff --git a/nx/source/runtime/devices/usb_comms.c b/nx/source/runtime/devices/usb_comms.c index 409ee04b..a0ec7d36 100644 --- a/nx/source/runtime/devices/usb_comms.c +++ b/nx/source/runtime/devices/usb_comms.c @@ -2,97 +2,173 @@ #include #include "types.h" #include "result.h" +#include "kernel/rwlock.h" #include "services/fatal.h" #include "services/usb.h" #include "runtime/devices/usb_comms.h" +#define TOTAL_INTERFACES 4 + +typedef struct { + RwLock lock, lock_in, lock_out; + bool initialized; + + UsbDsInterface* interface; + UsbDsEndpoint *endpoint_in, *endpoint_out; + + u8 *endpoint_in_buffer, *endpoint_out_buffer; +} usbCommsInterface; + static bool g_usbCommsInitialized = false; -static UsbDsInterface* interface = NULL; -static UsbDsEndpoint *g_usbComms_endpoint_in = NULL, *g_usbComms_endpoint_out = NULL; +static usbCommsInterface g_usbCommsInterfaces[TOTAL_INTERFACES]; -static u8 *g_usbComms_endpoint_in_buffer = NULL, *g_usbComms_endpoint_out_buffer = NULL; +static RwLock g_usbCommsLock; -static Result _usbCommsInit(void); +static Result _usbCommsInterfaceInit(usbCommsInterface *interface, u8 bInterfaceClass, u8 bInterfaceSubClass, u8 bInterfaceProtocol); -static Result _usbCommsWrite(const void* buffer, size_t size, size_t *transferredSize); +static Result _usbCommsWrite(usbCommsInterface *interface, const void* buffer, size_t size, size_t *transferredSize); + +Result usbCommsInitializeEx(u32 *interface, u8 bInterfaceClass, u8 bInterfaceSubClass, u8 bInterfaceProtocol) +{ + bool found=0; + usbCommsInterface *inter = NULL; + + rwlockWriteLock(&g_usbCommsLock); + + if (g_usbCommsInitialized && interface==NULL) { + rwlockWriteUnlock(&g_usbCommsLock); + return 0; + } + + Result rc=0; + u32 i = 0; + + if (!g_usbCommsInitialized) rc = usbDsInitialize(UsbComplexId_Default, NULL); + + if (R_SUCCEEDED(rc)) { + for(i=0; ilock); + if (!inter->initialized) found=1; + rwlockReadUnlock(&inter->lock); + + if (found) break; + } + + if (!found) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); + } + + if (R_SUCCEEDED(rc)) { + rwlockWriteLock(&inter->lock); + rwlockWriteLock(&inter->lock_in); + rwlockWriteLock(&inter->lock_out); + rc = _usbCommsInterfaceInit(inter, bInterfaceClass, bInterfaceSubClass, bInterfaceProtocol); + rwlockWriteUnlock(&inter->lock_out); + rwlockWriteUnlock(&inter->lock_in); + rwlockWriteUnlock(&inter->lock); + } + + if (R_FAILED(rc)) { + usbCommsExit(); + } + + if (R_SUCCEEDED(rc)) g_usbCommsInitialized=true; + + if (R_SUCCEEDED(rc) && interface) *interface = i; + + rwlockWriteUnlock(&g_usbCommsLock); + + return rc; +} Result usbCommsInitialize(void) { - if (g_usbCommsInitialized) return 0; + return usbCommsInitializeEx(NULL, USB_CLASS_VENDOR_SPEC, USB_CLASS_VENDOR_SPEC, USB_CLASS_VENDOR_SPEC); +} - Result ret=0; - - ret = usbDsInitialize(UsbComplexId_Default, NULL); - - if (R_SUCCEEDED(ret)) { - //The buffer for PostBufferAsync commands must be 0x1000-byte aligned. - g_usbComms_endpoint_in_buffer = memalign(0x1000, 0x1000); - if (g_usbComms_endpoint_in_buffer==NULL) ret = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); - - if (R_SUCCEEDED(ret)) { - g_usbComms_endpoint_out_buffer = memalign(0x1000, 0x1000); - if (g_usbComms_endpoint_out_buffer==NULL) ret = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); - } - - if (R_SUCCEEDED(ret)) { - memset(g_usbComms_endpoint_in_buffer, 0, 0x1000); - memset(g_usbComms_endpoint_out_buffer, 0, 0x1000); - ret = _usbCommsInit(); - - if (ret != 0) { - ret += 2000<<9; - } - } - - if (R_FAILED(ret)) { - usbDsExit(); - - free(g_usbComms_endpoint_in_buffer); - g_usbComms_endpoint_in_buffer = NULL; - - free(g_usbComms_endpoint_out_buffer); - g_usbComms_endpoint_out_buffer = NULL; - } - } - else { - ret += 1000<<9; +static void _usbCommsInterfaceExit(usbCommsInterface *interface) +{ + rwlockWriteLock(&interface->lock); + if (!interface->initialized) { + rwlockWriteUnlock(&interface->lock); + return; } - if (R_SUCCEEDED(ret)) g_usbCommsInitialized=true; + rwlockWriteLock(&interface->lock_in); + rwlockWriteLock(&interface->lock_out); - return ret; + interface->initialized = 0; + + usbDsInterface_DisableInterface(interface->interface); + usbDsEndpoint_Close(interface->endpoint_in); + usbDsEndpoint_Close(interface->endpoint_out); + usbDsInterface_Close(interface->interface); + + interface->endpoint_in = NULL; + interface->endpoint_out = NULL; + interface->interface = NULL; + + free(interface->endpoint_in_buffer); + free(interface->endpoint_out_buffer); + interface->endpoint_in_buffer = NULL; + interface->endpoint_out_buffer = NULL; + + rwlockWriteUnlock(&interface->lock_out); + rwlockWriteUnlock(&interface->lock_in); + + rwlockWriteUnlock(&interface->lock); +} + +void usbCommsExitEx(u32 interface) +{ + u32 i; + bool found=0; + if (interface>=TOTAL_INTERFACES) return; + + _usbCommsInterfaceExit(&g_usbCommsInterfaces[interface]); + + for (i=0; iinitialized = 1; + + //The buffer for PostBufferAsync commands must be 0x1000-byte aligned. + interface->endpoint_in_buffer = memalign(0x1000, 0x1000); + if (interface->endpoint_in_buffer==NULL) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); + + if (R_SUCCEEDED(rc)) { + interface->endpoint_out_buffer = memalign(0x1000, 0x1000); + if (interface->endpoint_out_buffer==NULL) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); + } + + if (R_SUCCEEDED(rc)) { + memset(interface->endpoint_in_buffer, 0, 0x1000); + memset(interface->endpoint_out_buffer, 0, 0x1000); + } + + if (R_FAILED(rc)) return rc; + //Setup interface. - ret = usbDsGetDsInterface(&interface, &interface_descriptor, "usb"); - if (R_FAILED(ret)) return ret; + rc = usbDsGetDsInterface(&interface->interface, &interface_descriptor, "usb"); + if (R_FAILED(rc)) return rc; //Setup endpoints. - ret = usbDsInterface_GetDsEndpoint(interface, &g_usbComms_endpoint_in, &endpoint_descriptor_in);//device->host - if (R_FAILED(ret)) return ret; + rc = usbDsInterface_GetDsEndpoint(interface->interface, &interface->endpoint_in, &endpoint_descriptor_in);//device->host + if (R_FAILED(rc)) return rc; - ret = usbDsInterface_GetDsEndpoint(interface, &g_usbComms_endpoint_out, &endpoint_descriptor_out);//host->device - if (R_FAILED(ret)) return ret; + rc = usbDsInterface_GetDsEndpoint(interface->interface, &interface->endpoint_out, &endpoint_descriptor_out);//host->device + if (R_FAILED(rc)) return rc; - ret = usbDsInterface_EnableInterface(interface); - if (R_FAILED(ret)) return ret; + rc = usbDsInterface_EnableInterface(interface->interface); + if (R_FAILED(rc)) return rc; - return ret; + return rc; } -static Result _usbCommsRead(void* buffer, size_t size, size_t *transferredSize) +static Result _usbCommsRead(usbCommsInterface *interface, void* buffer, size_t size, size_t *transferredSize) { - Result ret=0; + Result rc=0; u32 urbId=0; u8 *bufptr = (u8*)buffer; u8 *transfer_buffer = NULL; @@ -141,15 +235,15 @@ static Result _usbCommsRead(void* buffer, size_t size, size_t *transferredSize) usbDsReportData reportdata; //Makes sure endpoints are ready for data-transfer / wait for init if needed. - ret = usbDsWaitReady(); - if (R_FAILED(ret)) return ret; + rc = usbDsWaitReady(); + if (R_FAILED(rc)) return rc; while(size) { if(((u64)bufptr) & 0xfff)//When bufptr isn't page-aligned copy the data into g_usbComms_endpoint_in_buffer and transfer that, otherwise use the bufptr directly. { - transfer_buffer = g_usbComms_endpoint_out_buffer; - memset(g_usbComms_endpoint_out_buffer, 0, 0x1000); + transfer_buffer = interface->endpoint_out_buffer; + memset(interface->endpoint_out_buffer, 0, 0x1000); chunksize = 0x1000; chunksize-= ((u64)bufptr) & 0xfff;//After this transfer, bufptr will be page-aligned(if size is large enough for another transfer). @@ -166,18 +260,18 @@ static Result _usbCommsRead(void* buffer, size_t size, size_t *transferredSize) } //Start a host->device transfer. - ret = usbDsEndpoint_PostBufferAsync(g_usbComms_endpoint_out, transfer_buffer, chunksize, &urbId); - if (R_FAILED(ret)) return ret; + rc = usbDsEndpoint_PostBufferAsync(interface->endpoint_out, transfer_buffer, chunksize, &urbId); + if (R_FAILED(rc)) return rc; //Wait for the transfer to finish. - svcWaitSynchronizationSingle(g_usbComms_endpoint_out->CompletionEvent, U64_MAX); - svcClearEvent(g_usbComms_endpoint_out->CompletionEvent); + svcWaitSynchronizationSingle(interface->endpoint_out->CompletionEvent, U64_MAX); + svcClearEvent(interface->endpoint_out->CompletionEvent); - ret = usbDsEndpoint_GetReportData(g_usbComms_endpoint_out, &reportdata); - if (R_FAILED(ret)) return ret; + rc = usbDsEndpoint_GetReportData(interface->endpoint_out, &reportdata); + if (R_FAILED(rc)) return rc; - ret = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize); - if (R_FAILED(ret)) return ret; + rc = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize); + if (R_FAILED(rc)) return rc; if (tmp_transferredSize > chunksize) tmp_transferredSize = chunksize; total_transferredSize+= (size_t)tmp_transferredSize; @@ -191,12 +285,12 @@ static Result _usbCommsRead(void* buffer, size_t size, size_t *transferredSize) if (transferredSize) *transferredSize = total_transferredSize; - return ret; + return rc; } -static Result _usbCommsWrite(const void* buffer, size_t size, size_t *transferredSize) +static Result _usbCommsWrite(usbCommsInterface *interface, const void* buffer, size_t size, size_t *transferredSize) { - Result ret=0; + Result rc=0; u32 urbId=0; u32 chunksize=0; u8 *bufptr = (u8*)buffer; @@ -206,21 +300,21 @@ static Result _usbCommsWrite(const void* buffer, size_t size, size_t *transferre usbDsReportData reportdata; //Makes sure endpoints are ready for data-transfer / wait for init if needed. - ret = usbDsWaitReady(); - if (R_FAILED(ret)) return ret; + rc = usbDsWaitReady(); + if (R_FAILED(rc)) return rc; while(size) { if(((u64)bufptr) & 0xfff)//When bufptr isn't page-aligned copy the data into g_usbComms_endpoint_in_buffer and transfer that, otherwise use the bufptr directly. { - transfer_buffer = g_usbComms_endpoint_in_buffer; - memset(g_usbComms_endpoint_in_buffer, 0, 0x1000); + transfer_buffer = interface->endpoint_in_buffer; + memset(interface->endpoint_in_buffer, 0, 0x1000); chunksize = 0x1000; chunksize-= ((u64)bufptr) & 0xfff;//After this transfer, bufptr will be page-aligned(if size is large enough for another transfer). if (sizeendpoint_in_buffer, bufptr, chunksize); } else { @@ -229,18 +323,18 @@ static Result _usbCommsWrite(const void* buffer, size_t size, size_t *transferre } //Start a device->host transfer. - ret = usbDsEndpoint_PostBufferAsync(g_usbComms_endpoint_in, transfer_buffer, chunksize, &urbId); - if(R_FAILED(ret))return ret; + rc = usbDsEndpoint_PostBufferAsync(interface->endpoint_in, transfer_buffer, chunksize, &urbId); + if(R_FAILED(rc))return rc; //Wait for the transfer to finish. - svcWaitSynchronizationSingle(g_usbComms_endpoint_in->CompletionEvent, U64_MAX); - svcClearEvent(g_usbComms_endpoint_in->CompletionEvent); + svcWaitSynchronizationSingle(interface->endpoint_in->CompletionEvent, U64_MAX); + svcClearEvent(interface->endpoint_in->CompletionEvent); - ret = usbDsEndpoint_GetReportData(g_usbComms_endpoint_in, &reportdata); - if (R_FAILED(ret)) return ret; + rc = usbDsEndpoint_GetReportData(interface->endpoint_in, &reportdata); + if (R_FAILED(rc)) return rc; - ret = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize); - if (R_FAILED(ret)) return ret; + rc = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize); + if (R_FAILED(rc)) return rc; if (tmp_transferredSize > chunksize) tmp_transferredSize = chunksize; @@ -254,38 +348,80 @@ static Result _usbCommsWrite(const void* buffer, size_t size, size_t *transferre if (transferredSize) *transferredSize = total_transferredSize; - return ret; + return rc; +} + +size_t usbCommsReadEx(void* buffer, size_t size, u32 interface) +{ + size_t transferredSize=0; + u32 state=0; + Result rc, rc2; + usbCommsInterface *inter = &g_usbCommsInterfaces[interface]; + bool initialized; + + if (interface>=TOTAL_INTERFACES) return 0; + + rwlockReadLock(&inter->lock); + initialized = inter->initialized; + rwlockReadUnlock(&inter->lock); + if (!initialized) return 0; + + rwlockWriteLock(&inter->lock_out); + rc = _usbCommsRead(inter, buffer, size, &transferredSize); + rwlockWriteUnlock(&inter->lock_out); + if (R_FAILED(rc)) { + rc2 = usbDsGetState(&state); + if (R_SUCCEEDED(rc2)) { + if (state!=5) { + rwlockWriteLock(&inter->lock_out); + rc = _usbCommsRead(&g_usbCommsInterfaces[interface], buffer, size, &transferredSize); //If state changed during transfer, try again. usbDsWaitReady() will be called from this. + rwlockWriteUnlock(&inter->lock_out); + } + } + if (R_FAILED(rc)) fatalSimple(MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsRead)); + } + return transferredSize; } size_t usbCommsRead(void* buffer, size_t size) +{ + return usbCommsReadEx(buffer, size, 0); +} + +size_t usbCommsWriteEx(const void* buffer, size_t size, u32 interface) { size_t transferredSize=0; u32 state=0; - Result ret, ret2; - ret = _usbCommsRead(buffer, size, &transferredSize); - if (R_FAILED(ret)) { - ret2 = usbDsGetState(&state); - if (R_SUCCEEDED(ret2)) { - if (state!=5) ret = _usbCommsRead(buffer, size, &transferredSize); //If state changed during transfer, try again. usbDsWaitReady() will be called from this. + Result rc, rc2; + usbCommsInterface *inter = &g_usbCommsInterfaces[interface]; + bool initialized; + + if (interface>=TOTAL_INTERFACES) return 0; + + rwlockReadLock(&inter->lock); + initialized = inter->initialized; + rwlockReadUnlock(&inter->lock); + if (!initialized) return 0; + + rwlockWriteLock(&inter->lock_in); + rc = _usbCommsWrite(&g_usbCommsInterfaces[interface], buffer, size, &transferredSize); + rwlockWriteUnlock(&inter->lock_in); + if (R_FAILED(rc)) { + rc2 = usbDsGetState(&state); + if (R_SUCCEEDED(rc2)) { + if (state!=5) { + rwlockWriteLock(&inter->lock_in); + rc = _usbCommsWrite(&g_usbCommsInterfaces[interface], buffer, size, &transferredSize); //If state changed during transfer, try again. usbDsWaitReady() will be called from this. + rwlockWriteUnlock(&inter->lock_in); + } } - if (R_FAILED(ret))fatalSimple(MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsRead)); + if (R_FAILED(rc)) fatalSimple(MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsWrite)); } return transferredSize; } size_t usbCommsWrite(const void* buffer, size_t size) { - size_t transferredSize=0; - u32 state=0; - Result ret, ret2; - ret = _usbCommsWrite(buffer, size, &transferredSize); - if (R_FAILED(ret)) { - ret2 = usbDsGetState(&state); - if (R_SUCCEEDED(ret2)) { - if (state!=5) ret = _usbCommsWrite(buffer, size, &transferredSize); //If state changed during transfer, try again. usbDsWaitReady() will be called from this. - } - if (R_FAILED(ret))fatalSimple(MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsWrite)); - } - return transferredSize; + return usbCommsWriteEx(buffer, size, 0); } diff --git a/nx/source/runtime/env.c b/nx/source/runtime/env.c index 6ceb6e31..1cc7eb44 100644 --- a/nx/source/runtime/env.c +++ b/nx/source/runtime/env.c @@ -16,6 +16,7 @@ static u64 g_syscallHints[2]; static Handle g_processHandle = INVALID_HANDLE; static char* g_nextLoadPath = NULL; static char* g_nextLoadArgv = NULL; +static Result g_lastLoadResult = 0; extern __attribute__((weak)) u32 __nx_applet_type; @@ -84,6 +85,10 @@ void envSetup(void* ctx, Handle main_thread, LoaderReturnFn saved_lr) g_processHandle = ent->Value[0]; break; + case EntryType_LastLoadResult: + g_lastLoadResult = ent->Value[0]; + break; + default: if (ent->Flags & EntryFlag_IsMandatory) { @@ -164,3 +169,7 @@ Result envSetNextLoad(const char* path, const char* argv) bool envHasNextLoad(void) { return g_nextLoadPath != NULL; } + +Result envGetLastLoadResult(void) { + return g_lastLoadResult; +} diff --git a/nx/source/runtime/nxlink.c b/nx/source/runtime/nxlink.c new file mode 100644 index 00000000..9e42090c --- /dev/null +++ b/nx/source/runtime/nxlink.c @@ -0,0 +1,23 @@ +#include +#include +#include + + +// System globals we define here +extern int __system_argc; +extern char** __system_argv; + +struct in_addr __nxlink_host; + + +void nxlinkSetup(void) +{ + if ( __system_argc > 1 && + strlen(__system_argv[__system_argc - 1]) == 16 && + strncmp(&__system_argv[__system_argc - 1][8], "_NXLINK_", 8) == 0 ) + { + __system_argc--; + __nxlink_host.s_addr = strtoul(__system_argv[__system_argc], NULL, 16); + } + __system_argv[__system_argc] = NULL; +} diff --git a/nx/source/services/acc.c b/nx/source/services/acc.c index 49050b62..8c5d04b8 100644 --- a/nx/source/services/acc.c +++ b/nx/source/services/acc.c @@ -1,3 +1,5 @@ +#include + #include "types.h" #include "arm/atomics.h" #include "services/acc.h" @@ -8,12 +10,17 @@ static u64 g_refCnt; Result accountInitialize(void) { + Result rc=0; + atomicIncrement64(&g_refCnt); if (serviceIsActive(&g_accSrv)) return 0; - return smGetService(&g_accSrv, "acc:u1"); + rc = smGetService(&g_accSrv, "acc:u1"); + if (R_FAILED(rc)) rc = smGetService(&g_accSrv, "acc:u0"); + + return rc; } void accountExit(void) @@ -67,3 +74,153 @@ Result accountGetActiveUser(u128 *userID, bool *account_selected) return rc; } +Result accountGetProfile(AccountProfile* out, u128 userID) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u128 userID; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 5; + raw->userID = userID; + + Result rc = serviceIpcDispatch(&g_accSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreate(&out->s, r.Handles[0]); + } + } + + return rc; +} + +//IProfile implementation +Result accountProfileGet(AccountProfile* profile, AccountUserData* userdata, AccountProfileBase* profilebase) { + IpcCommand c; + ipcInitialize(&c); + if (userdata) ipcAddRecvStatic(&c, userdata, sizeof(AccountUserData), 0); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = userdata==NULL ? 1 : 0; + + Result rc = serviceIpcDispatch(&profile->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + AccountProfileBase profilebase; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && profilebase) memcpy(profilebase, &resp->profilebase, sizeof(AccountProfileBase)); + } + + return rc; +} + +Result accountProfileGetImageSize(AccountProfile* profile, size_t* image_size) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 10; + + Result rc = serviceIpcDispatch(&profile->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u32 image_size; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + *image_size = resp->image_size; + } + } + + return rc; +} + +Result accountProfileLoadImage(AccountProfile* profile, void* buf, size_t len, size_t* image_size) { + IpcCommand c; + ipcInitialize(&c); + ipcAddRecvBuffer(&c, buf, len, 0); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 11; + + Result rc = serviceIpcDispatch(&profile->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u32 image_size; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + *image_size = resp->image_size; + } + } + + return rc; +} + +void accountProfileClose(AccountProfile* profile) { + serviceClose(&profile->s); +} + diff --git a/nx/source/services/fs.c b/nx/source/services/fs.c index 22634b72..85fcb5be 100644 --- a/nx/source/services/fs.c +++ b/nx/source/services/fs.c @@ -92,7 +92,7 @@ Result fsMountSdcard(FsFileSystem* out) { rc = resp->result; if (R_SUCCEEDED(rc)) { - out->h = r.Handles[0]; + serviceCreate(&out->s, r.Handles[0]); } } @@ -131,7 +131,96 @@ Result fsMountSaveData(FsFileSystem* out, u8 inval, FsSave *save) { rc = resp->result; if (R_SUCCEEDED(rc)) { - out->h = r.Handles[0]; + serviceCreate(&out->s, r.Handles[0]); + } + } + + return rc; +} + +Result fsMountSystemSaveData(FsFileSystem* out, u8 inval, FsSave *save) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 inval;//Actually u8. + FsSave save; + } PACKED *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 52; + raw->inval = (u64)inval; + memcpy(&raw->save, save, sizeof(FsSave)); + + Result rc = serviceIpcDispatch(&g_fsSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreate(&out->s, r.Handles[0]); + } + } + + return rc; +} + +Result fsOpenSaveDataIterator(FsSaveDataIterator* out, s32 SaveDataSpaceId) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + struct { + u64 magic; + u64 cmd_id; + u8 SaveDataSpaceId; + } *raw2; + + if (SaveDataSpaceId == FsSaveDataSpaceId_All) { + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 60; + } + else { + raw2 = ipcPrepareHeader(&c, sizeof(*raw2)); + + raw2->magic = SFCI_MAGIC; + raw2->cmd_id = 61; + raw2->SaveDataSpaceId = SaveDataSpaceId; + } + + Result rc = serviceIpcDispatch(&g_fsSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreate(&out->s, r.Handles[0]); } } @@ -166,7 +255,7 @@ Result fsOpenDataStorageByCurrentProcess(FsStorage* out) { rc = resp->result; if (R_SUCCEEDED(rc)) { - out->h = r.Handles[0]; + serviceCreate(&out->s, r.Handles[0]); } } @@ -180,9 +269,19 @@ Result fsMount_SaveData(FsFileSystem* out, u64 titleID, u128 userID) { memset(&save, 0, sizeof(save)); save.titleID = titleID; save.userID = userID; - save.ContentStorageId = FS_CONTENTSTORAGEID_NandUser; + save.SaveDataType = FsSaveDataType_SaveData; - return fsMountSaveData(out, FS_MOUNTSAVEDATA_INVAL_DEFAULT, &save); + return fsMountSaveData(out, FsSaveDataSpaceId_NandUser, &save); +} + +Result fsMount_SystemSaveData(FsFileSystem* out, u64 saveID) { + FsSave save; + + memset(&save, 0, sizeof(save)); + save.saveID = saveID; + save.SaveDataType = FsSaveDataType_SystemSaveData; + + return fsMountSystemSaveData(out, FsSaveDataSpaceId_NandSystem, &save); } // IFileSystem impl @@ -207,7 +306,7 @@ Result fsFsCreateFile(FsFileSystem* fs, const char* path, size_t size, int flags raw->size = size; raw->flags = flags; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -239,7 +338,7 @@ Result fsFsDeleteFile(FsFileSystem* fs, const char* path) { raw->magic = SFCI_MAGIC; raw->cmd_id = 1; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -271,7 +370,7 @@ Result fsFsCreateDirectory(FsFileSystem* fs, const char* path) { raw->magic = SFCI_MAGIC; raw->cmd_id = 2; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -303,7 +402,7 @@ Result fsFsDeleteDirectory(FsFileSystem* fs, const char* path) { raw->magic = SFCI_MAGIC; raw->cmd_id = 3; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -335,7 +434,7 @@ Result fsFsDeleteDirectoryRecursively(FsFileSystem* fs, const char* path) { raw->magic = SFCI_MAGIC; raw->cmd_id = 4; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -368,7 +467,7 @@ Result fsFsRenameFile(FsFileSystem* fs, const char* path0, const char* path1) { raw->magic = SFCI_MAGIC; raw->cmd_id = 5; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -401,7 +500,7 @@ Result fsFsRenameDirectory(FsFileSystem* fs, const char* path0, const char* path raw->magic = SFCI_MAGIC; raw->cmd_id = 6; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -433,7 +532,7 @@ Result fsFsGetEntryType(FsFileSystem* fs, const char* path, FsEntryType* out) { raw->magic = SFCI_MAGIC; raw->cmd_id = 7; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -472,7 +571,7 @@ Result fsFsOpenFile(FsFileSystem* fs, const char* path, int flags, FsFile* out) raw->cmd_id = 8; raw->flags = flags; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -486,7 +585,7 @@ Result fsFsOpenFile(FsFileSystem* fs, const char* path, int flags, FsFile* out) rc = resp->result; if (R_SUCCEEDED(rc)) { - out->h = r.Handles[0]; + serviceCreate(&out->s, r.Handles[0]); } } @@ -510,7 +609,7 @@ Result fsFsOpenDirectory(FsFileSystem* fs, const char* path, int flags, FsDir* o raw->cmd_id = 9; raw->flags = flags; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -524,7 +623,7 @@ Result fsFsOpenDirectory(FsFileSystem* fs, const char* path, int flags, FsDir* o rc = resp->result; if (R_SUCCEEDED(rc)) { - out->h = r.Handles[0]; + serviceCreate(&out->s, r.Handles[0]); } } @@ -545,7 +644,7 @@ Result fsFsCommit(FsFileSystem* fs) { raw->magic = SFCI_MAGIC; raw->cmd_id = 10; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -577,7 +676,7 @@ Result fsFsGetFreeSpace(FsFileSystem* fs, const char* path, u64* out) { raw->magic = SFCI_MAGIC; raw->cmd_id = 11; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -614,7 +713,7 @@ Result fsFsGetTotalSpace(FsFileSystem* fs, const char* path, u64* out) { raw->magic = SFCI_MAGIC; raw->cmd_id = 12; - Result rc = ipcDispatch(fs->h); + Result rc = serviceIpcDispatch(&fs->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -637,7 +736,7 @@ Result fsFsGetTotalSpace(FsFileSystem* fs, const char* path, u64* out) { } void fsFsClose(FsFileSystem* fs) { - if(fs->h != INVALID_HANDLE) svcCloseHandle(fs->h); + serviceClose(&fs->s); } // IFile implementation @@ -662,7 +761,7 @@ Result fsFileRead(FsFile* f, u64 off, void* buf, size_t len, size_t* out) { raw->offset = off; raw->read_size = len; - Result rc = ipcDispatch(f->h); + Result rc = serviceIpcDispatch(&f->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -705,7 +804,7 @@ Result fsFileWrite(FsFile* f, u64 off, const void* buf, size_t len) { raw->offset = off; raw->write_size = len; - Result rc = ipcDispatch(f->h); + Result rc = serviceIpcDispatch(&f->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -736,7 +835,7 @@ Result fsFileFlush(FsFile* f) { raw->magic = SFCI_MAGIC; raw->cmd_id = 2; - Result rc = ipcDispatch(f->h); + Result rc = serviceIpcDispatch(&f->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -769,7 +868,7 @@ Result fsFileSetSize(FsFile* f, u64 sz) { raw->cmd_id = 3; raw->size = sz; - Result rc = ipcDispatch(f->h); + Result rc = serviceIpcDispatch(&f->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -800,7 +899,7 @@ Result fsFileGetSize(FsFile* f, u64* out) { raw->magic = SFCI_MAGIC; raw->cmd_id = 4; - Result rc = ipcDispatch(f->h); + Result rc = serviceIpcDispatch(&f->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -820,12 +919,12 @@ Result fsFileGetSize(FsFile* f, u64* out) { } void fsFileClose(FsFile* f) { - if(f->h != INVALID_HANDLE) svcCloseHandle(f->h); + serviceClose(&f->s); } // IDirectory implementation void fsDirClose(FsDir* d) { - if(d->h != INVALID_HANDLE) svcCloseHandle(d->h); + serviceClose(&d->s); } Result fsDirRead(FsDir* d, u64 inval, size_t* total_entries, size_t max_entries, FsDirectoryEntry *buf) { @@ -845,7 +944,7 @@ Result fsDirRead(FsDir* d, u64 inval, size_t* total_entries, size_t max_entries, raw->cmd_id = 0; raw->inval = inval; - Result rc = ipcDispatch(d->h); + Result rc = serviceIpcDispatch(&d->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -881,7 +980,7 @@ Result fsDirGetEntryCount(FsDir* d, u64* count) { raw->magic = SFCI_MAGIC; raw->cmd_id = 1; - Result rc = ipcDispatch(d->h); + Result rc = serviceIpcDispatch(&d->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -920,7 +1019,7 @@ Result fsStorageRead(FsStorage* s, u64 off, void* buf, size_t len) { raw->offset = off; raw->read_size = len; - Result rc = ipcDispatch(s->h); + Result rc = serviceIpcDispatch(&s->s); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; @@ -938,6 +1037,48 @@ Result fsStorageRead(FsStorage* s, u64 off, void* buf, size_t len) { } void fsStorageClose(FsStorage* s) { - if(s->h != INVALID_HANDLE) svcCloseHandle(s->h); + serviceClose(&s->s); +} + +// ISaveDataInfoReader +Result fsSaveDataIteratorRead(FsSaveDataIterator *s, FsSaveDataInfo* buf, size_t max_entries, size_t* total_entries) { + IpcCommand c; + ipcInitialize(&c); + ipcAddRecvBuffer(&c, buf, sizeof(FsSaveDataInfo)*max_entries, 0); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + + Result rc = serviceIpcDispatch(&s->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u64 total_entries; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + if (total_entries) *total_entries = resp->total_entries; + } + } + + return rc; +} + +void fsSaveDataIteratorClose(FsSaveDataIterator* s) { + serviceClose(&s->s); } diff --git a/nx/source/services/hid.c b/nx/source/services/hid.c index 293b2562..9980dc9d 100644 --- a/nx/source/services/hid.c +++ b/nx/source/services/hid.c @@ -375,6 +375,10 @@ void hidJoystickRead(JoystickPosition *pos, HidControllerID id, HidControllerJoy } } +bool hidGetHandheldMode(void) { + return g_controllerP1AutoID == CONTROLLER_HANDHELD; +} + static Result _hidSetDualModeAll(void) { Result rc; int i; @@ -731,26 +735,87 @@ Result hidIsVibrationPermitted(bool *flag) { return rc; } -Result hidInitializeVibrationDevices(u32 *VibrationDeviceHandles, size_t total_handles, HidControllerID id, HidControllerLayoutType type) { +Result hidSendVibrationValues(u32 *VibrationDeviceHandles, HidVibrationValue *VibrationValues, size_t count) { + Result rc; + u64 AppletResourceUserId; + + rc = appletGetAppletResourceUserId(&AppletResourceUserId); + if (R_FAILED(rc)) + return rc; + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 AppletResourceUserId; + } *raw; + + ipcAddSendStatic(&c, VibrationDeviceHandles, sizeof(u32)*count, 0); + ipcAddSendStatic(&c, VibrationValues, sizeof(HidVibrationValue)*count, 0); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 206; + raw->AppletResourceUserId = AppletResourceUserId; + + rc = serviceIpcDispatch(&g_hidSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result hidInitializeVibrationDevices(u32 *VibrationDeviceHandles, size_t total_handles, HidControllerID id, HidControllerType type) { Result rc=0; Service srv; u32 tmp_type = type & 0xff; + u32 tmp_id = id; size_t i; if (total_handles == 0 || total_handles > 2) return MAKERESULT(Module_Libnx, LibnxError_BadInput); - if (tmp_type < 5) { - if (tmp_type == 4) tmp_type |= 0x010000; - tmp_type+= 3; + if (tmp_id == CONTROLLER_HANDHELD) + tmp_id = 0x20; + + if (tmp_type & LAYOUT_PROCONTROLLER) { + tmp_type = 3; } - else { - if (tmp_type == 5) tmp_type = 0x20; - if (tmp_type == 6) tmp_type = 0x21; + else if (tmp_type & TYPE_HANDHELD) { + tmp_type = 4; + } + else if (tmp_type & TYPE_JOYCON_PAIR) { + tmp_type = 5; + } + else if (tmp_type & TYPE_JOYCON_LEFT) { + tmp_type = 6; + } + else if (tmp_type & TYPE_JOYCON_RIGHT) { + tmp_type = 7; + tmp_type |= 0x010000; + } + //The HidControllerID enum doesn't have bit29/bit30 checked by official sw, for tmp_type 0x20/0x21. + else if (tmp_type & BIT(29)) { + tmp_type = 0x20; + } + else if (tmp_type & BIT(30)) { + tmp_type = 0x21; } - //TODO: Is type correct? - VibrationDeviceHandles[0] = tmp_type | (id & 0xff)<<8; + VibrationDeviceHandles[0] = tmp_type | (tmp_id & 0xff)<<8; if (total_handles > 1) { tmp_type &= 0xff; diff --git a/nx/source/services/pm.c b/nx/source/services/pm.c index 073a6802..f7321de9 100644 --- a/nx/source/services/pm.c +++ b/nx/source/services/pm.c @@ -6,12 +6,12 @@ #include "services/pm.h" #include "services/sm.h" -static Service g_pmdmntSrv; -static u64 g_refCnt; +static Service g_pmdmntSrv, g_pmshellSrv; +static u64 g_pmdmntRefCnt, g_pmshellRefCnt; Result pmdmntInitialize(void) { - atomicIncrement64(&g_refCnt); + atomicIncrement64(&g_pmdmntRefCnt); if (serviceIsActive(&g_pmdmntSrv)) return 0; @@ -21,11 +21,28 @@ Result pmdmntInitialize(void) void pmdmntExit(void) { - if (atomicDecrement64(&g_refCnt) == 0) { + if (atomicDecrement64(&g_pmdmntRefCnt) == 0) { serviceClose(&g_pmdmntSrv); } } +Result pmshellInitialize(void) +{ + atomicIncrement64(&g_pmshellRefCnt); + + if (serviceIsActive(&g_pmshellSrv)) + return 0; + + return smGetService(&g_pmshellSrv, "pm:shell"); +} + +void pmshellExit(void) +{ + if (atomicDecrement64(&g_pmshellRefCnt) == 0) { + serviceClose(&g_pmshellSrv); + } +} + Result pmdmntStartProcess(u64 pid) { IpcCommand c; ipcInitialize(&c); @@ -204,3 +221,43 @@ Result pmdmntEnableDebugForApplication(Handle* handle_out) { return rc; } + +Result pmshellLaunchProcess(u32 launch_flags, u64 titleID, u64 storageID, u64 *pid) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 launch_flags; + u64 titleID; + u64 storageID; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + raw->launch_flags = launch_flags; + raw->titleID = titleID; + raw->storageID = storageID; + + Result rc = serviceIpcDispatch(&g_pmshellSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u64 pid; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && pid) *pid = resp->pid; + } + + return rc; +} diff --git a/nx/switch_rules b/nx/switch_rules index c5a570fa..dd83def4 100644 --- a/nx/switch_rules +++ b/nx/switch_rules @@ -5,6 +5,7 @@ endif include $(DEVKITPRO)/devkitA64/base_rules PORTLIBS := $(PORTLIBS_PATH)/switch +PATH := $(PORTLIBS)/bin:$(PATH) LIBNX ?= $(DEVKITPRO)/libnx