diff --git a/CODESTYLE.md b/CODESTYLE.md index 393e0a03..7f836bf8 100644 --- a/CODESTYLE.md +++ b/CODESTYLE.md @@ -22,5 +22,10 @@ ## Functions `modulenameFunctionName` +### Submodule function naming +Singletons use names like smInitialize/smExit + +Objects use names like tmemCreate/tmemClose + ## Macros `LIKE_THIS` diff --git a/README.md b/README.md index e7f8cad4..5bf08dbe 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,3 @@ Based on libctru. # Install instructions See [Switchbrew](http://switchbrew.org/index.php?title=Setting_up_Development_Environment). - -# Icon - -`nx/default_icon.jpg` is based on the icon by [Sweet Farm from the Noun Project](https://thenounproject.com/term/nintendo-switch/694750/). diff --git a/nx/data/default_font.bin b/nx/data/default_font.bin index e3ae20b3..758460e4 100644 Binary files a/nx/data/default_font.bin and b/nx/data/default_font.bin differ diff --git a/nx/default_icon.jpg b/nx/default_icon.jpg index e93c1d7e..859caf4a 100644 Binary files a/nx/default_icon.jpg and b/nx/default_icon.jpg differ diff --git a/nx/include/switch.h b/nx/include/switch.h index d1e80af8..34ccb71a 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -12,11 +12,14 @@ extern "C" { #include "switch/types.h" #include "switch/result.h" +#include "switch/nro.h" + #include "switch/kernel/svc.h" #include "switch/kernel/tmem.h" #include "switch/kernel/shmem.h" #include "switch/kernel/mutex.h" #include "switch/kernel/rwlock.h" +#include "switch/kernel/condvar.h" #include "switch/kernel/thread.h" #include "switch/kernel/virtmem.h" #include "switch/kernel/detect.h" @@ -35,12 +38,14 @@ extern "C" { #include "switch/services/audout.h" #include "switch/services/bsd.h" #include "switch/services/fatal.h" +#include "switch/services/time.h" #include "switch/services/usb.h" #include "switch/services/hid.h" #include "switch/services/irs.h" #include "switch/services/vi.h" #include "switch/services/nv.h" #include "switch/services/pm.h" +#include "switch/services/set.h" #include "switch/display/gfx.h" #include "switch/display/binder.h" @@ -65,6 +70,7 @@ extern "C" { #include "switch/runtime/devices/console.h" #include "switch/runtime/devices/usb_comms.h" #include "switch/runtime/devices/fs_dev.h" +#include "switch/runtime/devices/romfs_dev.h" #ifdef __cplusplus } diff --git a/nx/include/switch/display/gfx.h b/nx/include/switch/display/gfx.h index ff53312d..0e70d9e0 100644 --- a/nx/include/switch/display/gfx.h +++ b/nx/include/switch/display/gfx.h @@ -14,7 +14,7 @@ /// Same as \ref RGBA8 except with alpha=0xff. #define RGBA8_MAXALPHA(r,g,b) RGBA8(r,g,b,0xff) -/// GfxMode set by \ref gfxSetMode. The default is GfxMode_LinearDouble. Note that the text-console (see console.h) sets this to GfxMode_TiledSingle. +/// GfxMode set by \ref gfxSetMode. The default is GfxMode_LinearDouble. Note that the text-console (see console.h) sets this to GfxMode_TiledDouble. typedef enum { GfxMode_TiledSingle, ///< Single-buffering with raw tiled (block-linear) framebuffer. @@ -22,6 +22,8 @@ typedef enum GfxMode_LinearDouble ///< Double-buffering with linear framebuffer, which is transferred to the actual framebuffer by \ref gfxFlushBuffers(). } GfxMode; +/// Framebuffer pixel-format is RGBA8888, there's no known way to change this. + /** * @brief Initializes the graphics subsystem. * @warning Do not use \ref viInitialize when using this function. @@ -83,6 +85,12 @@ size_t gfxGetFramebufferSize(void); /// Sets the \ref GfxMode. void gfxSetMode(GfxMode mode); +/// Controls whether a vertical-flip is done when determining the pixel-offset within the actual framebuffer. By default this is enabled. +void gfxSetDrawFlip(bool flip); + +/// Configures transform. See the NATIVE_WINDOW_TRANSFORM_* enums in buffer_producer.h. The default is NATIVE_WINDOW_TRANSFORM_FLIP_V. +void gfxConfigureTransform(u32 transform); + /// Flushes the framebuffer in the data cache. When \ref GfxMode is GfxMode_LinearDouble, this also transfers the linear-framebuffer to the actual framebuffer. void gfxFlushBuffers(void); @@ -94,10 +102,11 @@ static inline u32 gfxGetFramebufferDisplayOffset(u32 x, u32 y) { extern size_t g_gfx_framebuf_aligned_width; extern size_t g_gfx_framebuf_display_height; + extern bool g_gfx_drawflip; //if (x >= g_gfx_framebuf_width || y >= g_gfx_framebuf_display_height) return (gfxGetFramebufferSize()-4)/4;//Return the last pixel-offset in the buffer, the data located here is not displayed due to alignment. (Disabled for perf) - y = g_gfx_framebuf_display_height-1-y; + if (g_gfx_drawflip) y = g_gfx_framebuf_display_height-1-y; tmp_pos = ((y & 127) / 16) + (x/16*8) + ((y/16/8)*(g_gfx_framebuf_aligned_width/16*8)); tmp_pos *= 16*16 * 4; diff --git a/nx/include/switch/nro.h b/nx/include/switch/nro.h new file mode 100644 index 00000000..53e7c270 --- /dev/null +++ b/nx/include/switch/nro.h @@ -0,0 +1,54 @@ +/** + * @file nro.h + * @brief NRO headers. + * @copyright libnx Authors + */ + +#pragma once + +#define NROHEADER_MAGIC 0x304f524e + +#define NROASSETHEADER_MAGIC 0x54455341 +#define NROASSETHEADER_VERSION 0 + +/// Entry for each segment in the codebin. +typedef struct { + u32 file_off; + u32 size; +} NroSegment; + +/// Offset 0x0 in the NRO. +typedef struct { + u32 unused; + u32 mod_offset; + u8 padding[8]; +} NroStart; + +/// This follows NroStart, the actual nro-header. +typedef struct { + u32 magic; + u32 unk1; + u32 size; + u32 unk2; + NroSegment segments[3]; + u32 bss_size; + u32 unk3; + u8 build_id[0x20]; + u8 padding[0x20]; +} NroHeader; + +/// Custom asset section. +typedef struct { + u64 offset; + u64 size; +} NroAssetSection; + +/// Custom asset header. +typedef struct { + u32 magic; + u32 version; + NroAssetSection icon; + NroAssetSection nacp; + NroAssetSection romfs; +} NroAssetHeader; + diff --git a/nx/include/switch/result.h b/nx/include/switch/result.h index 3827152d..3299aa6e 100644 --- a/nx/include/switch/result.h +++ b/nx/include/switch/result.h @@ -64,6 +64,7 @@ enum { LibnxError_JitUnavailable, LibnxError_WeirdKernel, LibnxError_IncompatSysVer, + LibnxError_InitFail_Time, }; /// libnx nvidia error codes diff --git a/nx/include/switch/runtime/devices/console.h b/nx/include/switch/runtime/devices/console.h index 7c7f3726..0d16a6aa 100644 --- a/nx/include/switch/runtime/devices/console.h +++ b/nx/include/switch/runtime/devices/console.h @@ -37,7 +37,7 @@ typedef bool(*ConsolePrint)(void* con, int c); /// A font struct for the console. typedef struct ConsoleFont { - u8* gfx; ///< A pointer to the font graphics + u16* gfx; ///< A pointer to the font graphics u16 asciiOffset; ///< Offset to the first valid character in the font table u16 numChars; ///< Number of characters in the font graphics }ConsoleFont; @@ -51,18 +51,18 @@ typedef struct ConsoleFont * { * //Font: * { - * (u8*)default_font_bin, //font gfx + * (u16*)default_font_bin, //font gfx * 0, //first ascii character in the set * 128, //number of characters in the font set * }, * 0,0, //cursorX cursorY * 0,0, //prevcursorX prevcursorY - * 160, //console width - * 90, //console height + * 80, //console width + * 45, //console height * 0, //window x * 0, //window y - * 160, //window width - * 90, //window height + * 80, //window width + * 45, //window height * 3, //tab size * 0, //font character offset * 0, //print callback @@ -75,6 +75,7 @@ typedef struct PrintConsole ConsoleFont font; ///< Font of the console u32 *frameBuffer; ///< Framebuffer address + u32 *frameBuffer2; ///< Framebuffer address int cursorX; ///< Current X location of the cursor (as a tile offset by default) int cursorY; ///< Current Y location of the cursor (as a tile offset by default) diff --git a/nx/include/switch/runtime/devices/fs_dev.h b/nx/include/switch/runtime/devices/fs_dev.h index 76164d40..de3e05c3 100644 --- a/nx/include/switch/runtime/devices/fs_dev.h +++ b/nx/include/switch/runtime/devices/fs_dev.h @@ -38,3 +38,6 @@ int fsdevUnmountDevice(const char *name); /// Uses fsFsCommit() with the specified device. This must be used after any savedata-write operations(not just file-write). /// This is not used automatically at device unmount. Result fsdevCommitDevice(const char *name); + +/// Returns the FsFileSystem for the default device (SD card), if mounted. Used internally by romfs_dev. +FsFileSystem* fsdevGetDefaultFileSystem(void); diff --git a/nx/include/switch/runtime/devices/romfs_dev.h b/nx/include/switch/runtime/devices/romfs_dev.h index d6dee80c..f23ab5ab 100644 --- a/nx/include/switch/runtime/devices/romfs_dev.h +++ b/nx/include/switch/runtime/devices/romfs_dev.h @@ -66,7 +66,7 @@ static inline Result romfsInit(void) /** * @brief Mounts RomFS from an open file. - * @param file Handle of the RomFS file. + * @param file FsFile of the RomFS image. * @param offset Offset of the RomFS within the file. * @param mount Output mount handle */ @@ -76,6 +76,18 @@ static inline Result romfsInitFromFile(FsFile file, u64 offset) return romfsMountFromFile(file, offset, NULL); } +/** + * @brief Mounts RomFS from an open storage. + * @param storage FsStorage of the RomFS image. + * @param offset Offset of the RomFS within the storage. + * @param mount Output mount handle + */ +Result romfsMountFromStorage(FsStorage storage, u64 offset, struct romfs_mount **mount); +static inline Result romfsInitFromStorage(FsStorage storage, u64 offset) +{ + return romfsMountFromStorage(storage, offset, NULL); +} + /// Bind the RomFS mount Result romfsBind(struct romfs_mount *mount); diff --git a/nx/include/switch/services/applet.h b/nx/include/switch/services/applet.h index ef217b76..74d32f85 100644 --- a/nx/include/switch/services/applet.h +++ b/nx/include/switch/services/applet.h @@ -51,6 +51,8 @@ Result appletGetAppletResourceUserId(u64 *out); void appletNotifyRunning(u8 *out); Result appletCreateManagedDisplayLayer(u64 *out); +Result appletGetDesiredLanguage(u64 *LanguageCode); + /** * @brief Controls whether screenshot-capture is allowed. * @param val 0 = disable, 1 = enable. diff --git a/nx/include/switch/services/fs.h b/nx/include/switch/services/fs.h index 2cd41499..4ea337eb 100644 --- a/nx/include/switch/services/fs.h +++ b/nx/include/switch/services/fs.h @@ -1,6 +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. * @author plutoo * @author yellows8 * @copyright libnx Authors @@ -91,8 +92,11 @@ void fsExit(void); Service* fsGetServiceSession(void); +/// Do not call this directly, see fs_dev.h. Result fsMountSdcard(FsFileSystem* out); + Result fsMountSaveData(FsFileSystem* out, u8 inval, FsSave *save); +Result fsOpenDataStorageByCurrentProcess(FsStorage* out); // todo: Rest of commands here /// FsFileSystem can be mounted with fs_dev for use with stdio, see fs_dev.h. @@ -130,4 +134,6 @@ Result fsDirRead(FsDir* d, u64 inval, size_t* total_entries, size_t max_entries, Result fsDirGetEntryCount(FsDir* d, u64* count); void fsDirClose(FsDir* d); -// todo: IStorage +// IStorage +Result fsStorageRead(FsStorage* s, u64 off, void* buf, size_t len); +void fsStorageClose(FsStorage* s); diff --git a/nx/include/switch/services/hid.h b/nx/include/switch/services/hid.h index d29a58ad..2c0edde7 100644 --- a/nx/include/switch/services/hid.h +++ b/nx/include/switch/services/hid.h @@ -328,6 +328,16 @@ typedef struct JoystickPosition s32 dy; } JoystickPosition; +typedef struct MousePosition +{ + u32 x; + u32 y; + u32 velocityX; + u32 velocityY; + u32 scrollVelocityX; + u32 scrollVelocityY; +} MousePosition; + #define JOYSTICK_MAX (0x8000) #define JOYSTICK_MIN (-0x8000) @@ -399,12 +409,7 @@ typedef struct HidMouseEntry { u64 timestamp; u64 timestamp_2; - u32 x; - u32 y; - u32 velocityX; - u32 velocityY; - u32 scrollVelocityX; - u32 scrollVelocityY; + MousePosition position; u64 buttons; } HidMouseEntry; static_assert(sizeof(HidMouseEntry) == 0x30, "Hid mouse entry structure has incorrect size"); @@ -552,6 +557,7 @@ u64 hidKeysUp(HidControllerID id); u64 hidMouseButtonsHeld(void); u64 hidMouseButtonsDown(void); u64 hidMouseButtonsUp(void); +void hidMouseRead(MousePosition *pos); bool hidKeyboardModifierHeld(HidKeyboardModifier modifier); bool hidKeyboardModifierDown(HidKeyboardModifier modifier); diff --git a/nx/include/switch/services/set.h b/nx/include/switch/services/set.h new file mode 100644 index 00000000..9d314ffb --- /dev/null +++ b/nx/include/switch/services/set.h @@ -0,0 +1,67 @@ +/** + * @file set.h + * @brief Settings services IPC wrapper. + * @author plutoo + * @author yellows8 + * @copyright libnx Authors + */ +#include "../result.h" + +typedef enum { + ColorSetId_Light=0, + ColorSetId_Dark=1 +} ColorSetId; + +/// IDs for Language. +typedef enum +{ + SetLanguage_JA = 0, ///< Japanese + SetLanguage_ENUS = 1, ///< US English ("AmericanEnglish") + SetLanguage_FR = 2, ///< French + SetLanguage_DE = 3, ///< German + SetLanguage_IT = 4, ///< Italian + SetLanguage_ES = 5, ///< Spanish + SetLanguage_ZHCN = 6, ///< Simplified Chinese ("Chinese") + SetLanguage_KO = 7, ///< Korean + SetLanguage_NL = 8, ///< Dutch + SetLanguage_PT = 9, ///< Portuguese + SetLanguage_RU = 10, ///< Russian + SetLanguage_ZHTW = 11, ///< Traditional Chinese ("Taiwanese") + SetLanguage_ENGB = 12, ///< GB English ("BritishEnglish") + SetLanguage_FRCA = 13, ///< CA French ("CanadianFrench") + SetLanguage_ES419 = 14, ///< "LatinAmericanSpanish" +} SetLanguage; + +Result setInitialize(void); +void setExit(void); + +/// Converts LanguageCode to Language. +Result setMakeLanguage(u64 LanguageCode, s32 *Language); + +/// Converts Language to LanguageCode. +Result setMakeLanguageCode(s32 Language, u64 *LanguageCode); + +/// Gets the current system LanguageCode. +/// Normally this should be used instead of \ref setGetLanguageCode. +/// LanguageCode is a string, see here: http://switchbrew.org/index.php?title=Settings_services#LanguageCode +Result setGetSystemLanguage(u64 *LanguageCode); + +/// Gets the current LanguageCode, \ref setGetSystemLanguage should be used instead normally. +Result setGetLanguageCode(u64 *LanguageCode); + +/// Gets available LanguageCodes. +/// On system-version <4.0.0, max_entries is set to the output from \ref setGetAvailableLanguageCodeCount if max_entries is larger than that. +Result setGetAvailableLanguageCodes(s32 *total_entries, u64 *LanguageCodes, size_t max_entries); + +/// Gets total available LanguageCodes. +/// Output total is overridden with value 0 if the total is <0. +Result setGetAvailableLanguageCodeCount(s32 *total); + +/// Gets the RegionCode. +Result setGetRegionCode(s32 *RegionCode); + +Result setsysInitialize(void); +void setsysExit(void); + +/// Gets the current system theme. +Result setsysGetColorSetId(ColorSetId* out); diff --git a/nx/include/switch/services/time.h b/nx/include/switch/services/time.h new file mode 100644 index 00000000..0ef21a09 --- /dev/null +++ b/nx/include/switch/services/time.h @@ -0,0 +1,33 @@ +/** + * @file time.h + * @brief Time services IPC wrapper. + * @author yellows8 + * @copyright libnx Authors + */ +#pragma once + +#include "../types.h" +#include "../services/sm.h" + +/// Time clock type. +typedef enum { + TimeType_UserSystemClock, + TimeType_NetworkSystemClock, + TimeType_LocalSystemClock, + TimeType_Default = TimeType_NetworkSystemClock, +} TimeType; + +Result timeInitialize(void); +void timeExit(void); + +Service* timeGetSessionService(void); + +Result timeGetCurrentTime(TimeType type, u64 *timestamp); + +/** + * @brief Sets the time for the specified clock. + * @param[in] type Clock to use. + * @param[in] timestamp POSIX UTC timestamp. + * @return Result code. + */ +Result timeSetCurrentTime(TimeType type, u64 timestamp); diff --git a/nx/source/display/gfx.c b/nx/source/display/gfx.c index 4f8dcb3d..4c0e96bc 100644 --- a/nx/source/display/gfx.c +++ b/nx/source/display/gfx.c @@ -34,12 +34,16 @@ 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; size_t g_gfx_singleframebuf_size=0; size_t g_gfx_singleframebuf_linear_size=0; +bool g_gfx_drawflip = true; + static AppletHookCookie g_gfx_autoresolution_applethookcookie; static bool g_gfx_autoresolution_enabled; @@ -55,7 +59,6 @@ extern nvioctl_fence g_nvgfx_nvhostgpu_gpfifo_fence; //static Result _gfxGetDisplayResolution(u64 *width, u64 *height); -// TODO: Let the user configure some of this? static BqQueueBufferInput g_gfxQueueBufferData = { .timestamp = 0x0, .isAutoTimestamp = 0x1, @@ -135,7 +138,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, 0, 0x300, &g_gfxCurrentProducerBuffer, fence); + rc = bqDequeueBuffer(async, g_gfx_framebuf_width, g_gfx_framebuf_height, g_gfxPixelFormat, 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); @@ -178,6 +181,10 @@ static Result _gfxInit(ViServiceType servicetype, const char *DisplayName, u32 L g_gfxFramebufSize = 0; g_gfxMode = GfxMode_LinearDouble; + 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)); @@ -538,6 +545,18 @@ void gfxSetMode(GfxMode mode) { g_gfxMode = mode; } +void gfxSetDrawFlip(bool flip) { + g_gfx_drawflip = flip; +} + +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/condvar.c b/nx/source/kernel/condvar.c index 6e474b18..d3be0ed2 100644 --- a/nx/source/kernel/condvar.c +++ b/nx/source/kernel/condvar.c @@ -13,7 +13,7 @@ void condvarInit(CondVar* c, Mutex* m) { Result condvarWaitTimeout(CondVar* c, u64 timeout) { Result rc; - rc = svcWaitProcessWideKeyAtomic(&c->tag, (u32*) c->mutex, getThreadVars()->handle, timeout); + rc = svcWaitProcessWideKeyAtomic((u32*) c->mutex, &c->tag, getThreadVars()->handle, timeout); // On timeout, we need to acquire it manually. if (rc == 0xEA01) diff --git a/nx/source/runtime/devices/console.c b/nx/source/runtime/devices/console.c index 271714d6..f70584f9 100644 --- a/nx/source/runtime/devices/console.c +++ b/nx/source/runtime/devices/console.c @@ -42,19 +42,20 @@ PrintConsole defaultConsole = { //Font: { - (u8*)default_font_bin, //font gfx + (u16*)default_font_bin, //font gfx 0, //first ascii character in the set 256 //number of characters in the font set }, (u32*)NULL, + (u32*)NULL, 0,0, //cursorX cursorY 0,0, //prevcursorX prevcursorY - 160, //console width - 90, //console height + 80, //console width + 45, //console height 0, //window x 0, //window y - 160, //window width - 90, //window height + 80, //window width + 45, //window height 3, //tab size 7, // foreground color 0, // background color @@ -123,6 +124,8 @@ static void consoleCls(char mode) { } } gfxFlushBuffers(); + gfxSwapBuffers(); + gfxWaitForVsync(); } //--------------------------------------------------------------------------------- static void consoleClearLine(char mode) { @@ -176,6 +179,8 @@ static void consoleClearLine(char mode) { } } gfxFlushBuffers(); + gfxSwapBuffers(); + gfxWaitForVsync(); } @@ -547,13 +552,16 @@ PrintConsole* consoleInit(PrintConsole* console) { console->consoleInitialised = 1; - gfxSetMode(GfxMode_TiledSingle); + gfxSetMode(GfxMode_TiledDouble); + + console->frameBuffer = (u32*)gfxGetFramebuffer(NULL, NULL); + gfxSwapBuffers(); + console->frameBuffer2 = (u32*)gfxGetFramebuffer(NULL, NULL); + gfxFlushBuffers(); + gfxSwapBuffers(); gfxWaitForVsync(); - console->frameBuffer = (u32*)gfxGetFramebuffer(NULL, NULL); - - consoleCls('2'); return currentConsole; @@ -615,15 +623,18 @@ static void newRow(void) { int i,j; u32 x, y; - x = currentConsole->windowX * 8; - y = currentConsole->windowY * 8; + x = currentConsole->windowX * 16; + y = currentConsole->windowY * 16; - for (i=0; iwindowWidth*8; i++) { + for (i=0; iwindowWidth*16; i++) { u32 *from; u32 *to; - for (j=0;j<(currentConsole->windowHeight-1)*8;j++) { + for (j=0;j<(currentConsole->windowHeight-1)*16;j++) { to = ¤tConsole->frameBuffer[gfxGetFramebufferDisplayOffset(x + i, y + j)]; - from = ¤tConsole->frameBuffer[gfxGetFramebufferDisplayOffset(x + i, y + 8 + j)]; + from = ¤tConsole->frameBuffer[gfxGetFramebufferDisplayOffset(x + i, y + 16 + j)]; + *to = *from; + to = ¤tConsole->frameBuffer2[gfxGetFramebufferDisplayOffset(x + i, y + j)]; + from = ¤tConsole->frameBuffer2[gfxGetFramebufferDisplayOffset(x + i, y + 16 + j)]; *to = *from; } } @@ -637,7 +648,7 @@ void consoleDrawChar(int c) { c -= currentConsole->font.asciiOffset; if ( c < 0 || c > currentConsole->font.numChars ) return; - u8 *fontdata = currentConsole->font.gfx + (8 * c); + u16 *fontdata = currentConsole->font.gfx + (16 * c); int writingColor = currentConsole->fg; int screenColor = currentConsole->bg; @@ -657,25 +668,37 @@ void consoleDrawChar(int c) { u32 bg = colorTable[screenColor]; u32 fg = colorTable[writingColor]; - u64 bval = *((u64*)fontdata); + u128 *tmp = (u128*)fontdata; - if (currentConsole->flags & CONSOLE_UNDERLINE) bval |= 0xffUL << 7*8; + u128 bvaltop = tmp[0]; + u128 bvalbtm = tmp[1]; - if (currentConsole->flags & CONSOLE_CROSSED_OUT) bval |= 0xff << 3*8; + if (currentConsole->flags & CONSOLE_UNDERLINE) bvalbtm |= (u128)0xffffULL << 7*16; - u8 mask = 0x80; + if (currentConsole->flags & CONSOLE_CROSSED_OUT) bvaltop |= (u128)0xffffULL << 7*16; + + u16 mask = 0x8000; int i, j; - int x = (currentConsole->cursorX + currentConsole->windowX) * 8; - int y = ((currentConsole->cursorY + currentConsole->windowY) *8 ); + int x = (currentConsole->cursorX + currentConsole->windowX) * 16; + int y = ((currentConsole->cursorY + currentConsole->windowY) *16 ); u32 *screen; - for (i=0;i<8;i++) { + for (i=0;i<16;i++) { for (j=0;j<8;j++) { - screen = ¤tConsole->frameBuffer[gfxGetFramebufferDisplayOffset(x + i, y + j)]; - if (bval >> (8*j) & mask) { *screen = fg; }else{ *screen = bg; } + uint32_t screenOffset = gfxGetFramebufferDisplayOffset(x + i, y + j); + screen = ¤tConsole->frameBuffer[screenOffset]; + if (bvaltop >> (16*j) & mask) { *screen = fg; }else{ *screen = bg; } + screen = ¤tConsole->frameBuffer2[screenOffset]; + if (bvaltop >> (16*j) & mask) { *screen = fg; }else{ *screen = bg; } + + screenOffset = gfxGetFramebufferDisplayOffset(x + i, y + j + 8); + screen = ¤tConsole->frameBuffer[screenOffset]; + if (bvalbtm >> (16*j) & mask) { *screen = fg; }else{ *screen = bg; } + screen = ¤tConsole->frameBuffer2[screenOffset]; + if (bvalbtm >> (16*j) & mask) { *screen = fg; }else{ *screen = bg; } } mask >>= 1; } @@ -730,6 +753,8 @@ void consolePrintChar(int c) { case 13: currentConsole->cursorX = 0; gfxFlushBuffers(); + gfxSwapBuffers(); + gfxWaitForVsync(); break; default: consoleDrawChar(c); diff --git a/nx/source/runtime/devices/fs_dev.c b/nx/source/runtime/devices/fs_dev.c index f5e99590..e4d787ef 100644 --- a/nx/source/runtime/devices/fs_dev.c +++ b/nx/source/runtime/devices/fs_dev.c @@ -469,6 +469,13 @@ Result fsdevExit(void) return 0; } +FsFileSystem* fsdevGetDefaultFileSystem(void) +{ + if(fsdev_fsdevice_default==-1) return NULL; + + return &fsdev_fsdevices[fsdev_fsdevice_default].fs; +} + /*! Open a file * * @param[in,out] r newlib reentrancy struct diff --git a/nx/source/runtime/devices/romfs_dev.c b/nx/source/runtime/devices/romfs_dev.c index fd2a415d..5e7a7899 100644 --- a/nx/source/runtime/devices/romfs_dev.c +++ b/nx/source/runtime/devices/romfs_dev.c @@ -9,14 +9,17 @@ #include #include "runtime/devices/romfs_dev.h" +#include "runtime/devices/fs_dev.h" #include "runtime/util/utf.h" #include "services/fs.h" - -/// WARNING: This is not ready to be used. +#include "runtime/env.h" +#include "nro.h" typedef struct romfs_mount { + bool fd_type; FsFile fd; + FsStorage fd_storage; time_t mtime; u64 offset; romfs_header header; @@ -30,7 +33,6 @@ extern int __system_argc; extern char** __system_argv; static char __component[PATH_MAX+1]; -//static uint16_t __utf16path[PATH_MAX+1]; #define romFS_root(m) ((romfs_dir*)(m)->dirTable) #define romFS_dir(m,x) ((romfs_dir*) ((u8*)(m)->dirTable + (x))) @@ -39,16 +41,25 @@ static char __component[PATH_MAX+1]; #define romFS_dir_mode (S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH) #define romFS_file_mode (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH) -static ssize_t _romfs_read(romfs_mount *mount, u64 offset, void* buffer, u32 size) +static ssize_t _romfs_read(romfs_mount *mount, u64 offset, void* buffer, u64 size) { u64 pos = mount->offset + offset; size_t read = 0; - Result rc = fsFileRead(&mount->fd, pos, buffer, size, &read); + Result rc = 0; + if(!mount->fd_type) + { + rc = fsFileRead(&mount->fd, pos, buffer, size, &read); + } + else + { + rc = fsStorageRead(&mount->fd_storage, pos, buffer, size); + read = size; + } if (R_FAILED(rc)) return -1; return read; } -static bool _romfs_read_chk(romfs_mount *mount, u64 offset, void* buffer, u32 size) +static bool _romfs_read_chk(romfs_mount *mount, u64 offset, void* buffer, u64 size) { return _romfs_read(mount, offset, buffer, size) == size; } @@ -104,26 +115,8 @@ static devoptab_t romFS_devoptab = //----------------------------------------------------------------------------- -// File header -/*#define _3DSX_MAGIC 0x58534433 // '3DSX' -typedef struct -{ - u32 magic; - u16 headerSize, relocHdrSize; - u32 formatVer; - u32 flags; - - // Sizes of the code, rodata and data segments + - // size of the BSS section (uninitialized latter half of the data segment) - u32 codeSegSize, rodataSegSize, dataSegSize, bssSize; - // offset and size of smdh - u32 smdhOffset, smdhSize; - // offset to filesystem - u32 fsOffset; -} _3DSX_Header;*/ - static Result romfsMountCommon(romfs_mount *mount); -//static void romfsInitMtime(romfs_mount *mount, FS_ArchiveID archId, FS_Path archPath, FS_Path filePath); +static void romfsInitMtime(romfs_mount *mount); __attribute__((weak)) const char* __romfs_path = NULL; @@ -173,9 +166,19 @@ Result romfsMount(struct romfs_mount **p) if(mount == NULL) return 99; - if (/*envIsHomebrew()*/1)//TODO: How to handle? + if (!envIsNso()) { - // RomFS appended to a 3DSX file + // RomFS embedded in a NRO + + mount->fd_type = 0; + + FsFileSystem *sdfs = fsdevGetDefaultFileSystem(); + if(sdfs==NULL) + { + romfs_free(mount); + return 1; + } + const char* filename = __romfs_path; if (__system_argc > 0 && __system_argv[0]) filename = __system_argv[0]; @@ -187,69 +190,57 @@ Result romfsMount(struct romfs_mount **p) if (strncmp(filename, "sdmc:/", 6) == 0) filename += 5; - /*else if (strncmp(filename, "3dslink:/", 9) == 0) + else if (strncmp(filename, "nxlink:/", 8) == 0) { - strncpy(__component, "/3ds", PATH_MAX); - strncat(__component, filename+8, PATH_MAX); + strncpy(__component, "/switch", PATH_MAX); + strncat(__component, filename+7, PATH_MAX); __component[PATH_MAX] = 0; filename = __component; - }*/ + } else { romfs_free(mount); return 2; } - //TODO - /*ssize_t units = utf8_to_utf16(__utf16path, (const uint8_t*)filename, PATH_MAX); - if (units < 0) - { - romfs_free(mount); - return 3; - } - if (units >= PATH_MAX) - { - romfs_free(mount); - return 4; - } - __utf16path[units] = 0; - - FS_Path archPath = { PATH_EMPTY, 1, (u8*)"" }; - FS_Path filePath = { PATH_UTF16, (units+1)*2, (u8*)__utf16path }; - - Result rc = FSUSER_OpenFileDirectly(&mount->fd, ARCHIVE_SDMC, archPath, filePath, FS_OPEN_READ, 0); + Result rc = fsFsOpenFile(sdfs, filename, FS_OPEN_READ, &mount->fd); if (R_FAILED(rc)) { romfs_free(mount); return rc; } - //romfsInitMtime(mount, ARCHIVE_SDMC, archPath, filePath); + romfsInitMtime(mount); - _3DSX_Header hdr; - if (!_romfs_read_chk(mount, 0, &hdr, sizeof(hdr))) goto _fail0; - if (hdr.magic != _3DSX_MAGIC) goto _fail0; - if (hdr.headerSize < sizeof(hdr)) goto _fail0; - mount->offset = hdr.fsOffset; - if (!mount->offset) goto _fail0;*/ + NroHeader hdr; + NroAssetHeader asset_header; + + if (!_romfs_read_chk(mount, sizeof(NroStart), &hdr, sizeof(hdr))) goto _fail0; + if (hdr.magic != NROHEADER_MAGIC) goto _fail0; + if (!_romfs_read_chk(mount, hdr.size, &asset_header, sizeof(asset_header))) goto _fail0; + + if (asset_header.magic != NROASSETHEADER_MAGIC + || asset_header.version > NROASSETHEADER_VERSION + || asset_header.romfs.offset == 0 + || asset_header.romfs.size == 0) + goto _fail0; + + mount->offset = hdr.size + asset_header.romfs.offset; } - else//TODO + else { // Regular RomFS - /*u8 zeros[0xC]; - memset(zeros, 0, sizeof(zeros)); - FS_Path archPath = { PATH_EMPTY, 1, (u8*)"" }; - FS_Path filePath = { PATH_BINARY, sizeof(zeros), zeros }; + mount->fd_type = 1; - Result rc = FSUSER_OpenFileDirectly(&mount->fd, ARCHIVE_ROMFS, archPath, filePath, FS_OPEN_READ, 0); + Result rc = fsOpenDataStorageByCurrentProcess(&mount->fd_storage); if (R_FAILED(rc)) { romfs_free(mount); return rc; } - //romfsInitMtime(mount, ARCHIVE_ROMFS, archPath, filePath);*/ + romfsInitMtime(mount); } Result ret = romfsMountCommon(mount); @@ -258,8 +249,9 @@ Result romfsMount(struct romfs_mount **p) return ret; -//_fail0: - fsFileClose(&mount->fd); +_fail0: + if(!mount->fd_type)fsFileClose(&mount->fd); + if(mount->fd_type)fsStorageClose(&mount->fd_storage); romfs_free(mount); return 10; } @@ -270,6 +262,7 @@ Result romfsMountFromFile(FsFile file, u64 offset, struct romfs_mount **p) if(mount == NULL) return 99; + mount->fd_type = 0; mount->fd = file; mount->offset = offset; @@ -280,6 +273,23 @@ Result romfsMountFromFile(FsFile file, u64 offset, struct romfs_mount **p) return ret; } +Result romfsMountFromStorage(FsStorage storage, u64 offset, struct romfs_mount **p) +{ + romfs_mount *mount = romfs_alloc(); + if(mount == NULL) + return 99; + + mount->fd_type = 1; + mount->fd_storage = storage; + mount->offset = offset; + + Result ret = romfsMountCommon(mount); + if(R_SUCCEEDED(ret) && p) + *p = mount; + + return ret; +} + Result romfsMountCommon(romfs_mount *mount) { if (_romfs_read(mount, 0, &mount->header, sizeof(mount->header)) != sizeof(mount->header)) @@ -318,35 +328,16 @@ Result romfsMountCommon(romfs_mount *mount) return 0; fail: - fsFileClose(&mount->fd); + if(!mount->fd_type)fsFileClose(&mount->fd); + if(mount->fd_type)fsStorageClose(&mount->fd_storage); romfs_free(mount); return 10; } -/*static void romfsInitMtime(romfs_mount *mount, FS_ArchiveID archId, FS_Path archPath, FS_Path filePath) +static void romfsInitMtime(romfs_mount *mount) { - u64 mtime; - FS_Archive arch; - Result rc; - mount->mtime = time(NULL); - rc = FSUSER_OpenArchive(&arch, archId, archPath); - if (R_FAILED(rc)) - return; - - rc = FSUSER_ControlArchive(arch, ARCHIVE_ACTION_GET_TIMESTAMP, - (void*)filePath.data, filePath.size, - &mtime, sizeof(mtime)); - FSUSER_CloseArchive(arch); - if (R_FAILED(rc)) - return;*/ - - /* convert from milliseconds to seconds */ - //mtime /= 1000; - /* convert from 2000-based timestamp to UNIX timestamp */ - /*mtime += 946684800; - mount->mtime = mtime; -}*/ +} Result romfsBind(struct romfs_mount *mount) { @@ -368,7 +359,8 @@ Result romfsUnmount(struct romfs_mount *mount) if(mount) { // unmount specific - fsFileClose(&mount->fd); + if(!mount->fd_type)fsFileClose(&mount->fd); + if(mount->fd_type)fsStorageClose(&mount->fd_storage); romfs_free(mount); } else @@ -376,7 +368,8 @@ Result romfsUnmount(struct romfs_mount *mount) // unmount everything while(romfs_mount_list) { - fsFileClose(&romfs_mount_list->fd); + if(!romfs_mount_list->fd_type)fsFileClose(&romfs_mount_list->fd); + if(romfs_mount_list->fd_type)fsStorageClose(&romfs_mount_list->fd_storage); romfs_free(romfs_mount_list); } } @@ -483,7 +476,7 @@ static int navigateToDir(romfs_mount *mount, romfs_dir** ppDir, const char** pPa } } - *ppDir = searchForDir(mount, *ppDir, (uint8_t*)component, strlen(component)+1); + *ppDir = searchForDir(mount, *ppDir, (uint8_t*)component, strlen(component)); if (!*ppDir) return EEXIST; } @@ -551,7 +544,7 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, if (r->_errno != 0) return -1; - romfs_file* file = searchForFile(fileobj->mount, curDir, (uint8_t*)path, strlen(path)+1); + romfs_file* file = searchForFile(fileobj->mount, curDir, (uint8_t*)path, strlen(path)); if (!file) { if(flags & O_CREAT) @@ -567,7 +560,7 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, } fileobj->file = file; - fileobj->offset = (u64)fileobj->mount->header.fileDataOff + file->dataOff; + fileobj->offset = fileobj->mount->header.fileDataOff + file->dataOff; fileobj->pos = 0; return 0; @@ -669,7 +662,7 @@ int romfs_stat(struct _reent *r, const char *path, struct stat *st) if(r->_errno != 0) return -1; - romfs_dir* dir = searchForDir(mount, curDir, (uint8_t*)path, strlen(path)+1); + romfs_dir* dir = searchForDir(mount, curDir, (uint8_t*)path, strlen(path)); if(dir) { memset(st, 0, sizeof(*st)); @@ -684,7 +677,7 @@ int romfs_stat(struct _reent *r, const char *path, struct stat *st) return 0; } - romfs_file* file = searchForFile(mount, curDir, (uint8_t*)path, strlen(path)+1); + romfs_file* file = searchForFile(mount, curDir, (uint8_t*)path, strlen(path)); if(file) { memset(st, 0, sizeof(*st)); diff --git a/nx/source/runtime/init.c b/nx/source/runtime/init.c index d3224465..d034c67b 100644 --- a/nx/source/runtime/init.c +++ b/nx/source/runtime/init.c @@ -4,6 +4,7 @@ #include "services/fatal.h" #include "services/fs.h" #include "services/hid.h" +#include "services/time.h" #include "services/applet.h" #include "runtime/devices/fs_dev.h" @@ -101,6 +102,10 @@ void __attribute__((weak)) __appInit(void) fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_HID)); } + rc = timeInitialize(); + if (R_FAILED(rc)) + fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_Time)); + rc = fsInitialize(); if (R_FAILED(rc)) fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); @@ -113,6 +118,7 @@ void __attribute__((weak)) __appExit(void) // Cleanup default services. fsdevExit(); fsExit(); + timeExit(); hidExit(); appletExit(); smExit(); diff --git a/nx/source/runtime/newlib.c b/nx/source/runtime/newlib.c index dccc888e..08238cff 100644 --- a/nx/source/runtime/newlib.c +++ b/nx/source/runtime/newlib.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -8,6 +9,7 @@ #include "runtime/env.h" #include "kernel/mutex.h" #include "services/fatal.h" +#include "services/time.h" #include "result.h" void __attribute__((weak)) NORETURN __libnx_exit(int rc); @@ -16,6 +18,9 @@ extern const u8 __tdata_lma[]; extern const u8 __tdata_lma_end[]; extern u8 __tls_start[]; +/// TimeType passed to timeGetCurrentTime() by __libnx_gtod(). +__attribute__((weak)) TimeType __nx_time_type = TimeType_Default; + static struct _reent* __libnx_get_reent(void) { ThreadVars* tv = getThreadVars(); if (tv->magic != THREADVARS_MAGIC) @@ -23,9 +28,41 @@ static struct _reent* __libnx_get_reent(void) { return tv->reent; } +//TODO: timeGetCurrentTime() returns UTC time. How to handle timezones? + +int __libnx_gtod(struct _reent *ptr, struct timeval *tp, struct timezone *tz) { + if (tp != NULL) { + u64 now=0; + Result rc=0; + + rc = timeGetCurrentTime(__nx_time_type, &now); + if (R_FAILED(rc)) { + ptr->_errno = EINVAL; + return -1; + } + + tp->tv_sec = now; + tp->tv_usec = now*1000000;//timeGetCurrentTime() only returns seconds. + } + + if (tz != NULL) { + tz->tz_minuteswest = 0; + tz->tz_dsttime = 0; + } + + return 0; +} + +int usleep(useconds_t useconds) +{ + svcSleepThread(useconds * 1000ull); + return 0; +} + void newlibSetup(void) { // Register newlib syscalls __syscalls.exit = __libnx_exit; + __syscalls.gettod_r = __libnx_gtod; __syscalls.getreent = __libnx_get_reent; // Register locking syscalls diff --git a/nx/source/services/applet.c b/nx/source/services/applet.c index de8f972f..56d4fae3 100644 --- a/nx/source/services/applet.c +++ b/nx/source/services/applet.c @@ -503,6 +503,45 @@ Result appletGetAppletResourceUserId(u64 *out) { return 0; } +Result appletGetDesiredLanguage(u64 *LanguageCode) { + IpcCommand c; + ipcInitialize(&c); + + if (!serviceIsActive(&g_appletSrv) || __nx_applet_type!=AppletType_Application) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 21; + + Result rc = serviceIpcDispatch(&g_appletIFunctions); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u64 LanguageCode; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && LanguageCode) { + *LanguageCode = resp->LanguageCode; + } + } + + return rc; +} + void appletNotifyRunning(u8 *out) { IpcCommand c; ipcInitialize(&c); diff --git a/nx/source/services/fs.c b/nx/source/services/fs.c index f23ec654..013a5779 100644 --- a/nx/source/services/fs.c +++ b/nx/source/services/fs.c @@ -131,6 +131,41 @@ Result fsMountSaveData(FsFileSystem* out, u8 inval, FsSave *save) { return rc; } +Result fsOpenDataStorageByCurrentProcess(FsStorage* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 200; + + 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)) { + out->h = r.Handles[0]; + } + } + + return rc; +} + // Wrapper(s) for fsMountSaveData. Result fsMount_SaveData(FsFileSystem* out, u64 titleID, u128 userID) { FsSave save; @@ -595,7 +630,7 @@ Result fsFsGetTotalSpace(FsFileSystem* fs, const char* path, u64* out) { } void fsFsClose(FsFileSystem* fs) { - svcCloseHandle(fs->h); + if(fs->h != INVALID_HANDLE) svcCloseHandle(fs->h); } // IFile implementation @@ -778,12 +813,12 @@ Result fsFileGetSize(FsFile* f, u64* out) { } void fsFileClose(FsFile* f) { - svcCloseHandle(f->h); + if(f->h != INVALID_HANDLE) svcCloseHandle(f->h); } // IDirectory implementation void fsDirClose(FsDir* d) { - svcCloseHandle(d->h); + if(d->h != INVALID_HANDLE) svcCloseHandle(d->h); } Result fsDirRead(FsDir* d, u64 inval, size_t* total_entries, size_t max_entries, FsDirectoryEntry *buf) { @@ -858,3 +893,44 @@ Result fsDirGetEntryCount(FsDir* d, u64* count) { return rc; } +// IStorage implementation +Result fsStorageRead(FsStorage* s, u64 off, void* buf, size_t len) { + IpcCommand c; + ipcInitialize(&c); + ipcAddRecvBuffer(&c, buf, len, 1); + + struct { + u64 magic; + u64 cmd_id; + u64 offset; + u64 read_size; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + raw->offset = off; + raw->read_size = len; + + Result rc = ipcDispatch(s->h); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +void fsStorageClose(FsStorage* s) { + if(s->h != INVALID_HANDLE) svcCloseHandle(s->h); +} + diff --git a/nx/source/services/hid.c b/nx/source/services/hid.c index 2d60daca..cad32029 100644 --- a/nx/source/services/hid.c +++ b/nx/source/services/hid.c @@ -267,6 +267,12 @@ u64 hidMouseButtonsUp(void) { return tmp; } +void hidMouseRead(MousePosition *pos) { + rwlockReadLock(&g_hidLock); + *pos = g_mouseEntry.position; + rwlockReadUnlock(&g_hidLock); +} + bool hidKeyboardModifierHeld(HidKeyboardModifier modifier) { rwlockReadLock(&g_hidLock); bool tmp = g_keyboardModHeld & modifier; diff --git a/nx/source/services/set.c b/nx/source/services/set.c new file mode 100644 index 00000000..ff207aed --- /dev/null +++ b/nx/source/services/set.c @@ -0,0 +1,337 @@ +/** + * @file set.h + * @brief Settings services IPC wrapper. + * @author plutoo + * @author yellows8 + * @copyright libnx Authors + */ +#include "types.h" +#include "result.h" +#include "ipc.h" +#include "kernel/detect.h" +#include "services/set.h" +#include "services/sm.h" +#include "services/applet.h" + +static Service g_setSrv; +static Service g_setsysSrv; + +static bool g_setLanguageCodesInitialized; +static u64 g_setLanguageCodes[0x40]; +static s32 g_setLanguageCodesTotal; + +static Result _setMakeLanguageCode(s32 Language, u64 *LanguageCode); + +Result setInitialize(void) +{ + if (serviceIsActive(&g_setSrv)) + return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized); + + g_setLanguageCodesInitialized = 0; + + return smGetService(&g_setSrv, "set"); +} + +void setExit(void) +{ + serviceClose(&g_setSrv); +} + +Result setsysInitialize(void) +{ + if (serviceIsActive(&g_setsysSrv)) + return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized); + + return smGetService(&g_setsysSrv, "set:sys"); +} + +void setsysExit(void) +{ + serviceClose(&g_setsysSrv); +} + +static Result setInitializeLanguageCodesCache(void) { + if (g_setLanguageCodesInitialized) return 0; + Result rc = 0; + + rc = setGetAvailableLanguageCodes(&g_setLanguageCodesTotal, g_setLanguageCodes, sizeof(g_setLanguageCodes)/sizeof(u64)); + if (R_FAILED(rc)) return rc; + + if (g_setLanguageCodesTotal < 0) g_setLanguageCodesTotal = 0; + + g_setLanguageCodesInitialized = 1; + + return rc; +} + +Result setMakeLanguage(u64 LanguageCode, s32 *Language) { + Result rc = setInitializeLanguageCodesCache(); + if (R_FAILED(rc)) return rc; + + s32 i; + rc = MAKERESULT(Module_Libnx, LibnxError_BadInput); + for (i=0; i= g_setLanguageCodesTotal) { + if (!kernelAbove400()) return MAKERESULT(Module_Libnx, LibnxError_BadInput); + return _setMakeLanguageCode(Language, LanguageCode); + } + + *LanguageCode = g_setLanguageCodes[Language]; + + return rc; +} + +Result setGetSystemLanguage(u64 *LanguageCode) { + //This is disabled because the returned LanguageCode can differ from the system language, for example ja instead of {English}. + /*Result rc = appletGetDesiredLanguage(LanguageCode); + if (R_SUCCEEDED(rc)) return rc;*/ + + return setGetLanguageCode(LanguageCode); +} + +Result setGetLanguageCode(u64 *LanguageCode) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + + Result rc = serviceIpcDispatch(&g_setSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u64 LanguageCode; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && LanguageCode) *LanguageCode = resp->LanguageCode; + } + + return rc; +} + +Result setGetAvailableLanguageCodes(s32 *total_entries, u64 *LanguageCodes, size_t max_entries) { + IpcCommand c; + ipcInitialize(&c); + + Result rc=0; + bool new_cmd = kernelAbove400(); + + if (!new_cmd) {//On system-version <4.0.0 the sysmodule will close the session if max_entries is too large. + s32 tmptotal = 0; + + rc = setGetAvailableLanguageCodeCount(&tmptotal); + if (R_FAILED(rc)) return rc; + + if (max_entries > (size_t)tmptotal) max_entries = (size_t)tmptotal; + } + + size_t bufsize = max_entries*sizeof(u64); + + if (!new_cmd) { + ipcAddRecvStatic(&c, LanguageCodes, bufsize, 0); + } + else { + ipcAddRecvBuffer(&c, LanguageCodes, bufsize, 0); + } + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = new_cmd ? 5 : 1; + + rc = serviceIpcDispatch(&g_setSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + s32 total_entries; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && total_entries) *total_entries = resp->total_entries; + } + + return rc; +} + +static Result _setMakeLanguageCode(s32 Language, u64 *LanguageCode) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + s32 Language; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 2; + raw->Language = Language; + + Result rc = serviceIpcDispatch(&g_setSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u64 LanguageCode; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && LanguageCode) *LanguageCode = resp->LanguageCode; + } + + return rc; +} + +Result setGetAvailableLanguageCodeCount(s32 *total) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = kernelAbove400() ? 6 : 3; + + Result rc = serviceIpcDispatch(&g_setSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + s32 total; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && total) { + *total = resp->total; + if (*total < 0) *total = 0; + } + } + + return rc; +} + +Result setGetRegionCode(s32 *RegionCode) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 4; + + Result rc = serviceIpcDispatch(&g_setSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + s32 RegionCode; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && RegionCode) *RegionCode = resp->RegionCode; + } + + return rc; +} + +Result setsysGetColorSetId(ColorSetId* out) +{ + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 23; + + Result rc = serviceIpcDispatch(&g_setsysSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u32 color_set; + } *resp = r.Raw; + + *out = resp->color_set; + rc = resp->result; + } + + return rc; + +} diff --git a/nx/source/services/time.c b/nx/source/services/time.c new file mode 100644 index 00000000..67cc89c0 --- /dev/null +++ b/nx/source/services/time.c @@ -0,0 +1,189 @@ +#include +#include "types.h" +#include "result.h" +#include "ipc.h" +#include "services/time.h" +#include "services/sm.h" + +static Service g_timeSrv; +static Service g_timeUserSystemClock; +static Service g_timeNetworkSystemClock; +static Service g_timeTimeZoneService; +static Service g_timeLocalSystemClock; + +static Result _timeGetSession(Service* srv_out, u64 cmd_id); + +Result timeInitialize(void) +{ + if (serviceIsActive(&g_timeSrv)) + return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized); + + Result rc; + + rc = smGetService(&g_timeSrv, "time:s"); + if (R_FAILED(rc)) + rc = smGetService(&g_timeSrv, "time:u"); + + if (R_FAILED(rc)) + return rc; + + rc = _timeGetSession(&g_timeUserSystemClock, 0); + + if (R_SUCCEEDED(rc)) + rc = _timeGetSession(&g_timeNetworkSystemClock, 1); + + if (R_SUCCEEDED(rc)) + rc = _timeGetSession(&g_timeTimeZoneService, 3); + + if (R_SUCCEEDED(rc)) + rc = _timeGetSession(&g_timeLocalSystemClock, 4); + + if (R_FAILED(rc)) + timeExit(); + + return rc; +} + +void timeExit(void) +{ + if (!serviceIsActive(&g_timeSrv)) + return; + + serviceClose(&g_timeLocalSystemClock); + serviceClose(&g_timeTimeZoneService); + serviceClose(&g_timeNetworkSystemClock); + serviceClose(&g_timeUserSystemClock); + serviceClose(&g_timeSrv); +} + +Service* timeGetSessionService(void) { + return &g_timeSrv; +} + +static Result _timeGetSession(Service* srv_out, u64 cmd_id) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + + Result rc = serviceIpcDispatch(&g_timeSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreate(srv_out, r.Handles[0]); + } + } + + return rc; +} + +static Service* _timeGetClockSession(TimeType type) { + if (type==TimeType_UserSystemClock) { + return &g_timeUserSystemClock; + } + else if (type==TimeType_NetworkSystemClock) { + return &g_timeNetworkSystemClock; + } + else if (type==TimeType_LocalSystemClock) { + return &g_timeLocalSystemClock; + } + else { + return NULL; + } +} + +Result timeGetCurrentTime(TimeType type, u64 *timestamp) { + Service *srv = _timeGetClockSession(type); + + if (srv==NULL) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u64 timestamp; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && timestamp) *timestamp = resp->timestamp; + } + + return rc; +} + +Result timeSetCurrentTime(TimeType type, u64 timestamp) { + Service *srv = _timeGetClockSession(type); + + if (srv==NULL) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 timestamp; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 1; + raw->timestamp = timestamp; + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + diff --git a/nx/switch_rules b/nx/switch_rules index cb1bfe7d..c5a570fa 100644 --- a/nx/switch_rules +++ b/nx/switch_rules @@ -4,7 +4,7 @@ endif include $(DEVKITPRO)/devkitA64/base_rules -PORTLIBS := $(PORTLIBS_PATH)/switch $(PORTLIBS_PATH)/armv8-a +PORTLIBS := $(PORTLIBS_PATH)/switch LIBNX ?= $(DEVKITPRO)/libnx