From 67af341594cd9d2721a0c19f6c068038b1ca9786 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Tue, 13 Feb 2018 17:50:21 -0500 Subject: [PATCH 01/11] Fixed romfs_dir struct for the parent field and fixed romfs_dev.h formatting. --- nx/include/switch/runtime/devices/romfs_dev.h | 53 ++++++++++--------- nx/source/runtime/devices/romfs_dev.c | 10 ++-- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/nx/include/switch/runtime/devices/romfs_dev.h b/nx/include/switch/runtime/devices/romfs_dev.h index 22ed18f5..d6dee80c 100644 --- a/nx/include/switch/runtime/devices/romfs_dev.h +++ b/nx/include/switch/runtime/devices/romfs_dev.h @@ -16,39 +16,40 @@ /// RomFS header. typedef struct { - u64 headerSize; ///< Size of the header. - u64 dirHashTableOff; ///< Offset of the directory hash table. - u64 dirHashTableSize; ///< Size of the directory hash table. - u64 dirTableOff; ///< Offset of the directory table. - u64 dirTableSize; ///< Size of the directory table. - u64 fileHashTableOff; ///< Offset of the file hash table. - u64 fileHashTableSize; ///< Size of the file hash table. - u64 fileTableOff; ///< Offset of the file table. - u64 fileTableSize; ///< Size of the file table. - u64 fileDataOff; ///< Offset of the file data. + u64 headerSize; ///< Size of the header. + u64 dirHashTableOff; ///< Offset of the directory hash table. + u64 dirHashTableSize; ///< Size of the directory hash table. + u64 dirTableOff; ///< Offset of the directory table. + u64 dirTableSize; ///< Size of the directory table. + u64 fileHashTableOff; ///< Offset of the file hash table. + u64 fileHashTableSize; ///< Size of the file hash table. + u64 fileTableOff; ///< Offset of the file table. + u64 fileTableSize; ///< Size of the file table. + u64 fileDataOff; ///< Offset of the file data. } romfs_header; /// RomFS directory. typedef struct { - u32 sibling; ///< Offset of the next sibling directory. - u32 childDir; ///< Offset of the first child directory. - u32 childFile; ///< Offset of the first file. - u32 nextHash; ///< Directory hash table pointer. - u32 nameLen; ///< Name length. - uint8_t name[]; ///< Name. (UTF-8) + u32 parent; ///< Offset of the parent directory. + u32 sibling; ///< Offset of the next sibling directory. + u32 childDir; ///< Offset of the first child directory. + u32 childFile; ///< Offset of the first file. + u32 nextHash; ///< Directory hash table pointer. + u32 nameLen; ///< Name length. + uint8_t name[]; ///< Name. (UTF-8) } romfs_dir; /// RomFS file. typedef struct { - u32 parent; ///< Offset of the parent directory. - u32 sibling; ///< Offset of the next sibling file. - u64 dataOff; ///< Offset of the file's data. - u64 dataSize; ///< Length of the file's data. - u32 nextHash; ///< File hash table pointer. - u32 nameLen; ///< Name length. - uint8_t name[]; ///< Name. (UTF-8) + u32 parent; ///< Offset of the parent directory. + u32 sibling; ///< Offset of the next sibling file. + u64 dataOff; ///< Offset of the file's data. + u64 dataSize; ///< Length of the file's data. + u32 nextHash; ///< File hash table pointer. + u32 nameLen; ///< Name length. + uint8_t name[]; ///< Name. (UTF-8) } romfs_file; struct romfs_mount; @@ -60,7 +61,7 @@ struct romfs_mount; Result romfsMount(struct romfs_mount **mount); static inline Result romfsInit(void) { - return romfsMount(NULL); + return romfsMount(NULL); } /** @@ -72,7 +73,7 @@ static inline Result romfsInit(void) Result romfsMountFromFile(FsFile file, u64 offset, struct romfs_mount **mount); static inline Result romfsInitFromFile(FsFile file, u64 offset) { - return romfsMountFromFile(file, offset, NULL); + return romfsMountFromFile(file, offset, NULL); } /// Bind the RomFS mount @@ -82,6 +83,6 @@ Result romfsBind(struct romfs_mount *mount); Result romfsUnmount(struct romfs_mount *mount); static inline Result romfsExit(void) { - return romfsUnmount(NULL); + return romfsUnmount(NULL); } diff --git a/nx/source/runtime/devices/romfs_dev.c b/nx/source/runtime/devices/romfs_dev.c index d58f046a..fd2a415d 100644 --- a/nx/source/runtime/devices/romfs_dev.c +++ b/nx/source/runtime/devices/romfs_dev.c @@ -294,7 +294,7 @@ Result romfsMountCommon(romfs_mount *mount) mount->dirTable = malloc(mount->header.dirTableSize); if (!mount->dirTable) goto fail; - if (!_romfs_read_chk(mount, mount->header.dirTableOff + 4, mount->dirTable, mount->header.dirTableSize)) + if (!_romfs_read_chk(mount, mount->header.dirTableOff, mount->dirTable, mount->header.dirTableSize)) goto fail; mount->fileHashTable = (u32*)malloc(mount->header.fileHashTableSize); @@ -411,7 +411,7 @@ static romfs_dir* searchForDir(romfs_mount *mount, romfs_dir* parent, const uint for (curOff = mount->dirHashTable[hash]; curOff != romFS_none; curOff = curDir->nextHash) { curDir = romFS_dir(mount, curOff); - //if (curDir->parent != parentOff) continue;//TODO: How to handle parent here? + if (curDir->parent != parentOff) continue; if (curDir->nameLen != namelen) continue; if (memcmp(curDir->name, name, namelen) != 0) continue; return curDir; @@ -478,7 +478,7 @@ static int navigateToDir(romfs_mount *mount, romfs_dir** ppDir, const char** pPa if (!component[1]) continue; if (component[1]=='.' && !component[2]) { - //*ppDir = romFS_dir(mount, (*ppDir)->parent);//TODO: How to handle parent here? + *ppDir = romFS_dir(mount, (*ppDir)->parent); continue; } } @@ -762,10 +762,10 @@ int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct s else if(iter->state == 1) { /* '..' entry */ - //romfs_dir* dir = romFS_dir(iter->mount, iter->dir->parent);//TODO: How to handle parent here? + romfs_dir* dir = romFS_dir(iter->mount, iter->dir->parent); memset(filestat, 0, sizeof(*filestat)); - //filestat->st_ino = dir_inode(iter->mount, dir); + filestat->st_ino = dir_inode(iter->mount, dir); filestat->st_mode = romFS_dir_mode; strcpy(filename, ".."); From 3f90f3b64c1d1ca026577ca0fd3cf9ac5cb2f949 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Fri, 16 Feb 2018 20:13:47 -0500 Subject: [PATCH 02/11] Align width/height to 4 in gfxInitResolution() and gfxConfigureCrop(). Updated the image-transfer code in gfxFlushBuffers() for removing the width misalignment handling. --- nx/include/switch/gfx/gfx.h | 2 ++ nx/source/gfx/gfx.c | 24 ++++++++---------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/nx/include/switch/gfx/gfx.h b/nx/include/switch/gfx/gfx.h index f8968b6c..f1c93e64 100644 --- a/nx/include/switch/gfx/gfx.h +++ b/nx/include/switch/gfx/gfx.h @@ -41,6 +41,7 @@ void gfxExit(void); * @note The default resolution is 720p. * @note This can only be used before calling \ref gfxInitDefault, this will use \ref fatalSimple otherwise. If the input is 0, the default resolution will be used during \ref gfxInitDefault. This sets the maximum resolution for the framebuffer, used during \ref gfxInitDefault. This is also used as the current resolution when crop isn't set. The width/height are reset to the default when \ref gfxExit is used. * @note Normally you should only use this when you need a maximum resolution larger than the default, see above. + * @note The width and height are aligned to 4. */ void gfxInitResolution(u32 width, u32 height); @@ -52,6 +53,7 @@ void gfxInitResolutionDefault(void); /// This will update the display width/height returned by \ref gfxGetFramebuffer, with that width/height being reset to the default when required. /// \ref gfxGetFramebufferDisplayOffset uses absolute x/y, it will not adjust for non-zero crop left/top. /// The new crop config will not take affect with double-buffering disabled. When used during frame-drawing, this should be called before \ref gfxGetFramebuffer. +/// The right and bottom params are aligned to 4. void gfxConfigureCrop(s32 left, s32 top, s32 right, s32 bottom); /// Wrapper for \ref gfxConfigureCrop. Use this to set the resolution, within the bounds of the maximum resolution. Use all-zero input to reset to default. diff --git a/nx/source/gfx/gfx.c b/nx/source/gfx/gfx.c index 8f7165a6..23aeda92 100644 --- a/nx/source/gfx/gfx.c +++ b/nx/source/gfx/gfx.c @@ -397,8 +397,8 @@ void gfxExit(void) void gfxInitResolution(u32 width, u32 height) { if (g_gfxInitialized) fatalSimple(MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized)); - g_gfx_framebuf_width = width; - g_gfx_framebuf_height = height; + g_gfx_framebuf_width = (width+3) & ~3; + g_gfx_framebuf_height = (height+3) & ~3; } void gfxInitResolutionDefault(void) { @@ -412,6 +412,10 @@ void gfxConfigureCrop(s32 left, s32 top, s32 right, s32 bottom) { } if (left < 0 || top < 0 || right < 0 || bottom < 0) return; + + right = (right+3) & ~3; + bottom = (bottom+3) & ~3; + if (right < left || bottom < top) return; if (left > g_gfx_framebuf_width || top > g_gfx_framebuf_height) return; if (right > g_gfx_framebuf_width || bottom > g_gfx_framebuf_height) return; @@ -540,26 +544,14 @@ void gfxFlushBuffers(void) { u32 *actual_framebuf = (u32*)&g_gfxFramebuf[g_gfxCurrentBuffer*g_gfx_singleframebuf_size]; if (g_gfxMode == GfxMode_LinearDouble) { - //TODO: Implement block-linear here without re-calculating the entire offset with gfxGetFramebufferDisplayOffset(). - - size_t x, y, j, tmpoff; + size_t x, y; size_t width = g_gfx_framebuf_display_width; size_t height = g_gfx_framebuf_display_height; u32 *in_framebuf = (u32*)g_gfxFramebufLinear; for (y=0; y= width) - break; - actual_framebuf[tmpoff+j] = in_framebuf[y * width + x+j]; - } - } - else { - *((u128*)&actual_framebuf[tmpoff]) = *((u128*)&in_framebuf[y * width + x]); - } + *((u128*)&actual_framebuf[gfxGetFramebufferDisplayOffset(x, y)]) = *((u128*)&in_framebuf[y * width + x]); } } } From 3c6b8bb6ddcf5a0c3f61e557bce818448619769d Mon Sep 17 00:00:00 2001 From: yellows8 Date: Fri, 16 Feb 2018 22:40:53 -0500 Subject: [PATCH 03/11] Removed envGetArgc() and the related code, since it was removed from wiki-ABI. Updated a comment in argv.c. --- nx/include/switch/runtime/env.h | 2 -- nx/source/runtime/argv.c | 2 +- nx/source/runtime/env.c | 6 ------ 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/nx/include/switch/runtime/env.h b/nx/include/switch/runtime/env.h index 66c0d0b9..af2e0d57 100644 --- a/nx/include/switch/runtime/env.h +++ b/nx/include/switch/runtime/env.h @@ -60,8 +60,6 @@ u64 envGetHeapOverrideSize(void); /// Returns true if the environment has an argv array. bool envHasArgv(void); -/// Returns the number of arguments in the argv array. -u64 envGetArgc(void); /// Returns the pointer to the argv array. void* envGetArgv(void); diff --git a/nx/source/runtime/argv.c b/nx/source/runtime/argv.c index 4786ac6f..412b07b0 100644 --- a/nx/source/runtime/argv.c +++ b/nx/source/runtime/argv.c @@ -36,7 +36,7 @@ void argvSetup(void) __system_argc = 0; __system_argv = NULL; - // TODO: Use envHasArgv() here for the NRO case. + // TODO: Use envHasArgv()/envGetArgv() here for the NRO case. if (envIsNso()) { memset(&meminfo, 0, sizeof(meminfo)); diff --git a/nx/source/runtime/env.c b/nx/source/runtime/env.c index fc5b7f0a..6ceb6e31 100644 --- a/nx/source/runtime/env.c +++ b/nx/source/runtime/env.c @@ -11,7 +11,6 @@ static Handle g_mainThreadHandle = INVALID_HANDLE; static LoaderReturnFn g_loaderRetAddr = NULL; static void* g_overrideHeapAddr = NULL; static u64 g_overrideHeapSize = 0; -static u64 g_overrideArgc = 0; static void* g_overrideArgv = NULL; static u64 g_syscallHints[2]; static Handle g_processHandle = INVALID_HANDLE; @@ -69,7 +68,6 @@ void envSetup(void* ctx, Handle main_thread, LoaderReturnFn saved_lr) break; case EntryType_Argv: - g_overrideArgc = ent->Value[0]; g_overrideArgv = (void*) ent->Value[1]; break; @@ -129,10 +127,6 @@ bool envHasArgv(void) { return g_overrideArgv != NULL; } -u64 envGetArgc(void) { - return g_overrideArgc; -} - void* envGetArgv(void) { return g_overrideArgv; } From c22655e7a2e02775940900c3c153c6a9e9a38b5f Mon Sep 17 00:00:00 2001 From: yellows8 Date: Fri, 16 Feb 2018 22:54:15 -0500 Subject: [PATCH 04/11] Moved the applet GetEventHandle code outside of the regularapp block since appletMainLoop() uses this event. --- nx/source/services/applet.c | 43 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/nx/source/services/applet.c b/nx/source/services/applet.c index f016f48a..de8f972f 100644 --- a/nx/source/services/applet.c +++ b/nx/source/services/applet.c @@ -157,36 +157,35 @@ Result appletInitialize(void) if (R_SUCCEEDED(rc)) rc = _appletGetSession(&g_appletProxySession, &g_appletIDebugFunctions, 1000); - if (R_SUCCEEDED(rc) && (__nx_applet_type == AppletType_Application)) - { - // ICommonStateGetter::GetEventHandle + // ICommonStateGetter::GetEventHandle + if (R_SUCCEEDED(rc)) rc = _appletGetHandle(&g_appletICommonStateGetter, &g_appletMessageEventHandle, 0); - if (R_SUCCEEDED(rc)) { - do { - svcWaitSynchronizationSingle(g_appletMessageEventHandle, U64_MAX); + if (R_SUCCEEDED(rc) && (__nx_applet_type == AppletType_Application)) + { + do { + svcWaitSynchronizationSingle(g_appletMessageEventHandle, U64_MAX); - u32 msg; - rc = _appletReceiveMessage(&msg); + u32 msg; + rc = _appletReceiveMessage(&msg); - if (R_FAILED(rc)) - { - if ((rc & 0x3fffff) == 0x680) - continue; - - break; - } - - if (msg != 0xF) + if (R_FAILED(rc)) + { + if ((rc & 0x3fffff) == 0x680) continue; - rc = _appletGetCurrentFocusState(&g_appletFocusState); + break; + } - if (R_FAILED(rc)) - break; + if (msg != 0xF) + continue; - } while(g_appletFocusState!=1); - } + rc = _appletGetCurrentFocusState(&g_appletFocusState); + + if (R_FAILED(rc)) + break; + + } while(g_appletFocusState!=1); if (R_SUCCEEDED(rc)) rc = _appletAcquireForegroundRights(); From 0e40828c19096e7c8e8267396c42b5902097c67e Mon Sep 17 00:00:00 2001 From: Mike H Date: Sat, 17 Feb 2018 16:40:59 +0000 Subject: [PATCH 05/11] Initial audio support (#43) Initial audio support --- nx/include/switch.h | 1 + nx/include/switch/services/audout.h | 61 ++++ nx/source/services/audout.c | 445 ++++++++++++++++++++++++++++ 3 files changed, 507 insertions(+) create mode 100644 nx/include/switch/services/audout.h create mode 100644 nx/source/services/audout.c diff --git a/nx/include/switch.h b/nx/include/switch.h index 706160a3..006fa1f0 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -32,6 +32,7 @@ extern "C" { #include "switch/services/acc.h" #include "switch/services/apm.h" #include "switch/services/applet.h" +#include "switch/services/audout.h" #include "switch/services/bsd.h" #include "switch/services/fatal.h" #include "switch/services/usb.h" diff --git a/nx/include/switch/services/audout.h b/nx/include/switch/services/audout.h new file mode 100644 index 00000000..1f8a20a5 --- /dev/null +++ b/nx/include/switch/services/audout.h @@ -0,0 +1,61 @@ +/** + * @file audout.h + * @brief Audio output service. + * @author hexkyz + * @copyright libnx Authors + */ +#pragma once + +#include "../types.h" + +typedef enum { + PcmFormat_Invalid = 0, + PcmFormat_INT8 = 1, + PcmFormat_INT16 = 2, + PcmFormat_INT24 = 3, + PcmFormat_INT32 = 4, + PcmFormat_FLOAT = 5, + PcmFormat_ADPCM = 6, +} PcmFormat; + +typedef enum { + AudioOutState_Started = 0, + AudioOutState_Stopped = 1, +} AudioOutState; + +/// Audio output buffer format +typedef struct AudioOutBuffer AudioOutBuffer; + +struct AudioOutBuffer +{ + AudioOutBuffer* next; ///< Next buffer. + void* buffer; ///< Sample buffer. + u64 buffer_size; ///< Sample buffer size. + u64 data_size; ///< Size of data inside the buffer. + u64 data_offset; ///< Offset of data inside the buffer. +}; + +Result audoutInitialize(void); +void audoutExit(void); + +Result audoutListAudioOuts(char *DeviceNames, u32 *DeviceNamesCount); +Result audoutOpenAudioOut(const char *DeviceNameIn, char *DeviceNameOut, u32 SampleRateIn, u32 ChannelCountIn, u32 *SampleRateOut, u32 *ChannelCountOut, PcmFormat *Format, AudioOutState *State); +Result audoutGetAudioOutState(AudioOutState *State); +Result audoutStartAudioOut(void); +Result audoutStopAudioOut(void); +Result audoutAppendAudioOutBuffer(AudioOutBuffer *Buffer); +Result audoutGetReleasedAudioOutBuffer(AudioOutBuffer *Buffer, u32 *ReleasedBuffersCount); +Result audoutContainsAudioOutBuffer(AudioOutBuffer *Buffer, bool *ContainsBuffer); + +/** + * @brief Submits an audio sample data buffer for playing. + * @param source AudioOutBuffer containing the source sample data to be played. + * @param released AudioOutBuffer to receive the last played buffer. + */ +void audoutPlayBuffer(AudioOutBuffer *source, AudioOutBuffer *released); + +/// These return the state associated with the currently active audio output device. +u32 audoutGetSampleRate(void); ///< Supported sample rate (48000Hz). +u32 audoutGetChannelCount(void); ///< Supported channel count (2 channels). +PcmFormat audoutGetPcmFormat(void); ///< Supported PCM format (INT16). +AudioOutState audoutGetDeviceState(void); ///< Initial device state (stopped). diff --git a/nx/source/services/audout.c b/nx/source/services/audout.c new file mode 100644 index 00000000..9ca8d613 --- /dev/null +++ b/nx/source/services/audout.c @@ -0,0 +1,445 @@ +#include +#include "types.h" +#include "result.h" +#include "ipc.h" +#include "services/audout.h" +#include "services/sm.h" + +#define DEVICE_NAME_LENGTH 0x100 +#define DEFAULT_SAMPLE_RATE 0xBB80 +#define DEFAULT_CHANNEL_COUNT 0x00020000 + +static Service g_audoutSrv; +static Service g_audoutIAudioOut; + +static Handle g_audoutBufferEventHandle = INVALID_HANDLE; + +static u32 g_sampleRate = 0; +static u32 g_channelCount = 0; +static PcmFormat g_pcmFormat = PcmFormat_Invalid; +static AudioOutState g_deviceState = AudioOutState_Stopped; + +static Result _audoutRegisterBufferEvent(Handle *BufferEvent); + +Result audoutInitialize(void) +{ + if (serviceIsActive(&g_audoutSrv)) + return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized); + + Result rc = 0; + rc = smGetService(&g_audoutSrv, "audout:u"); + + // Setup the default device + if (R_SUCCEEDED(rc)) + { + // Passing an empty device name will open the default "DeviceOut" + char DeviceNameIn[DEVICE_NAME_LENGTH] = {0}; + char DeviceNameOut[DEVICE_NAME_LENGTH] = {0}; + + // Open audio output device + rc = audoutOpenAudioOut(DeviceNameIn, DeviceNameOut, DEFAULT_SAMPLE_RATE, DEFAULT_CHANNEL_COUNT, &g_sampleRate, &g_channelCount, &g_pcmFormat, &g_deviceState); + } + + // Register global handle for buffer events + if (R_SUCCEEDED(rc)) + rc = _audoutRegisterBufferEvent(&g_audoutBufferEventHandle); + + if (R_FAILED(rc)) + audoutExit(); + + return rc; +} + +void audoutExit(void) +{ + if (g_audoutBufferEventHandle != INVALID_HANDLE) { + svcCloseHandle(g_audoutBufferEventHandle); + g_audoutBufferEventHandle = INVALID_HANDLE; + } + + g_sampleRate = 0; + g_channelCount = 0; + g_pcmFormat = PcmFormat_Invalid; + g_deviceState = AudioOutState_Stopped; + + serviceClose(&g_audoutIAudioOut); + serviceClose(&g_audoutSrv); +} + +u32 audoutGetSampleRate(void) { + return g_sampleRate; +} + +u32 audoutGetChannelCount(void) { + return g_channelCount; +} + +PcmFormat audoutGetPcmFormat(void) { + return g_pcmFormat; +} + +AudioOutState audoutGetDeviceState(void) { + return g_deviceState; +} + +void audoutPlayBuffer(AudioOutBuffer *source, AudioOutBuffer *released) { + // Try to push the supplied buffer to the audio output device + Result do_append = audoutAppendAudioOutBuffer(source); + + if (R_SUCCEEDED(do_append)) + { + // Wait on the buffer event handle + Result do_wait = svcWaitSynchronizationSingle(g_audoutBufferEventHandle, U64_MAX); + + if (R_SUCCEEDED(do_wait)) + { + svcResetSignal(g_audoutBufferEventHandle); + + u32 released_count = 0; + Result do_release = audoutGetReleasedAudioOutBuffer(released, &released_count); + + // Ensure that all buffers are released and return the last one only + while (R_SUCCEEDED(do_release) && (released_count > 0)) + do_release = audoutGetReleasedAudioOutBuffer(released, &released_count); + } + } +} + +Result audoutListAudioOuts(char *DeviceNames, u32 *DeviceNamesCount) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + ipcAddRecvBuffer(&c, DeviceNames, DEVICE_NAME_LENGTH, 0); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + + Result rc = serviceIpcDispatch(&g_audoutSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u32 DeviceNamesCount; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && DeviceNamesCount) + *DeviceNamesCount = resp->DeviceNamesCount; + } + + return rc; +} + +Result audoutOpenAudioOut(const char *DeviceNameIn, char *DeviceNameOut, u32 SampleRateIn, u32 ChannelCountIn, u32 *SampleRateOut, u32 *ChannelCountOut, PcmFormat *Format, AudioOutState *State) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 sample_rate; + u32 channel_count; + u64 client_pid; + } *raw; + + ipcSendPid(&c); + ipcSendHandleCopy(&c, CUR_PROCESS_HANDLE); + ipcAddSendBuffer(&c, DeviceNameIn, DEVICE_NAME_LENGTH, 0); + ipcAddRecvBuffer(&c, DeviceNameOut, DEVICE_NAME_LENGTH, 0); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 1; + raw->sample_rate = SampleRateIn; + raw->channel_count = ChannelCountIn; + raw->client_pid = 0; + + Result rc = serviceIpcDispatch(&g_audoutSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u32 sample_rate; + u32 channel_count; + u32 pcm_format; + u32 state; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreate(&g_audoutIAudioOut, r.Handles[0]); + + if (SampleRateOut) + *SampleRateOut = resp->sample_rate; + + if (ChannelCountOut) + *ChannelCountOut = resp->channel_count; + + if (Format) + *Format = resp->pcm_format; + + if (State) + *State = resp->state; + } + } + + return rc; +} + +Result audoutGetAudioOutState(AudioOutState *State) { + 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_audoutIAudioOut); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u32 state; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && State) + *State = resp->state; + } + + return rc; +} + +Result audoutStartAudioOut(void) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 1; + + Result rc = serviceIpcDispatch(&g_audoutIAudioOut); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result audoutStopAudioOut(void) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 2; + + Result rc = serviceIpcDispatch(&g_audoutIAudioOut); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result audoutAppendAudioOutBuffer(AudioOutBuffer *Buffer) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 tag; + } *raw; + + ipcAddSendBuffer(&c, Buffer, sizeof(AudioOutBuffer), 0); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 3; + raw->tag = (u64)&Buffer; + + Result rc = serviceIpcDispatch(&g_audoutIAudioOut); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _audoutRegisterBufferEvent(Handle *BufferEvent) { + 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_audoutIAudioOut); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && BufferEvent) + *BufferEvent = r.Handles[0]; + } + + return rc; +} + +Result audoutGetReleasedAudioOutBuffer(AudioOutBuffer *Buffer, u32 *ReleasedBuffersCount) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + ipcAddRecvBuffer(&c, Buffer, sizeof(AudioOutBuffer), 0); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 5; + + Result rc = serviceIpcDispatch(&g_audoutIAudioOut); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u32 released_buffers_count; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && ReleasedBuffersCount) + *ReleasedBuffersCount = resp->released_buffers_count; + } + + return rc; +} + +Result audoutContainsAudioOutBuffer(AudioOutBuffer *Buffer, bool *ContainsBuffer) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 tag; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 6; + raw->tag = (u64)&Buffer; + + Result rc = serviceIpcDispatch(&g_audoutIAudioOut); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u32 contains_buffer; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && ContainsBuffer) + *ContainsBuffer = (resp->contains_buffer & 0x01); + } + + return rc; +} From a1462c8b0b7111692ba42780d6bb6da734e1d0a0 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Sat, 17 Feb 2018 19:35:04 -0500 Subject: [PATCH 06/11] Fixed size of bufferProducerGraphicBuffer and removed the union for it. Removed an invalid comment from gfx.c, since bufferProducerRequestBuffer() is used after bufferProducerGraphicBufferInit(). --- nx/include/switch/gfx/buffer_producer.h | 68 ++++++++++++------------- nx/source/gfx/gfx.c | 1 - 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/nx/include/switch/gfx/buffer_producer.h b/nx/include/switch/gfx/buffer_producer.h index 1d6e30bb..e6a8a750 100644 --- a/nx/include/switch/gfx/buffer_producer.h +++ b/nx/include/switch/gfx/buffer_producer.h @@ -45,42 +45,38 @@ typedef struct { u32 numFds; u32 numInts; - union { - u32 rawdata[0x144>>2];//Actual size is numFds*4 + numInts*4. - - struct { - u32 unk_x0; - u32 nvmap_handle0; - u32 unk_x8; - u32 unk_xc; - u32 unk_x10; - u32 unk_x14; - u32 unk_x18; - u32 unk_x1c; - u32 unk_x20; - u32 width_unk0; - u32 buffer_size0; - u32 unk_x2c; - u32 unk_x30; - u32 width_unk1; - u32 height_unk; - u32 flags; - u32 unk_x40; - u32 unk_x44; - u32 byte_stride; - u32 nvmap_handle1; - u32 buffer_offset; - u32 unk_x54; - u32 unk_x58; - u32 unk_x5c; - u32 unk_x60; - u32 unk_x64; - u32 unk_x68; - u32 buffer_size1; - u32 unk_x70[0x33];//Normally all-zero. - u64 timestamp;//Unknown, some timestamp perhaps? - } data; - }; + struct {//Actual size is numFds*4 + numInts*4. + u32 unk_x0; + u32 nvmap_handle0; + u32 unk_x8; + u32 unk_xc; + u32 unk_x10; + u32 unk_x14; + u32 unk_x18; + u32 unk_x1c; + u32 unk_x20; + u32 width_unk0; + u32 buffer_size0; + u32 unk_x2c; + u32 unk_x30; + u32 width_unk1; + u32 height_unk; + u32 flags; + u32 unk_x40; + u32 unk_x44; + u32 byte_stride; + u32 nvmap_handle1; + u32 buffer_offset; + u32 unk_x54; + u32 unk_x58; + u32 unk_x5c; + u32 unk_x60; + u32 unk_x64; + u32 unk_x68; + u32 buffer_size1; + u32 unk_x70[0x33];//Normally all-zero. + u64 timestamp; + } PACKED data; } PACKED bufferProducerGraphicBuffer; //From Android window.h. diff --git a/nx/source/gfx/gfx.c b/nx/source/gfx/gfx.c index 23aeda92..a70ae5fe 100644 --- a/nx/source/gfx/gfx.c +++ b/nx/source/gfx/gfx.c @@ -78,7 +78,6 @@ static bufferProducerQueueBufferInput g_gfxQueueBufferData = { }; //Some of this struct is based on tegra_dc_ext_flip_windowattr. -//TODO: How much of this struct do official apps really set? Most of it seems to be used as-is from the bufferProducerRequestBuffer() output. static bufferProducerGraphicBuffer g_gfx_BufferInitData = { .magic = 0x47424652,//"RFBG"/'GBFR' .format = 0x1, From 20cd940095a4c24a4c91674dc4f0a9a0e6b442be Mon Sep 17 00:00:00 2001 From: yellows8 Date: Sat, 17 Feb 2018 21:25:21 -0500 Subject: [PATCH 07/11] Implemented arg parsing for NRO env. When args aren't available, set __system_argv to {ptr to NULL} instead of NULL. --- nx/source/runtime/argv.c | 153 ++++++++++++++++++++++----------------- 1 file changed, 85 insertions(+), 68 deletions(-) diff --git a/nx/source/runtime/argv.c b/nx/source/runtime/argv.c index 412b07b0..72975c54 100644 --- a/nx/source/runtime/argv.c +++ b/nx/source/runtime/argv.c @@ -13,6 +13,8 @@ extern char* fake_heap_end; extern u32 __argdata__; +static char* g_argv_empty = NULL; + void argvSetup(void) { Result rc=0; @@ -21,22 +23,20 @@ void argvSetup(void) u8 *argdata = (u8*)&__argdata__; u32 *arg32 = (u32*)argdata; - u64 argdata_allocsize; - u64 argdata_strsize; - u32 argvptr_pos; + u64 argdata_allocsize=0; + u64 argdata_strsize=0; + u32 argvptr_pos=0; u32 max_argv; u32 argi; u32 arglen=0; bool quote_flag=0; bool end_flag=0; - char *args; + char *args = NULL; char *argstart; char *argstorage; __system_argc = 0; - __system_argv = NULL; - - // TODO: Use envHasArgv()/envGetArgv() here for the NRO case. + __system_argv = &g_argv_empty; if (envIsNso()) { memset(&meminfo, 0, sizeof(meminfo)); @@ -57,67 +57,84 @@ void argvSetup(void) argvptr_pos = 0x20 + argdata_strsize+1; if (argvptr_pos >= argdata_allocsize) return; - argstorage = (char*)&argdata[argvptr_pos]; - - argvptr_pos += (argdata_strsize+1 + 0x9) & ~0x7; - if (argvptr_pos >= argdata_allocsize) return; - - max_argv = (argdata_allocsize - argvptr_pos) >> 3; - if (max_argv < 2) return; - - __system_argv = (char**)&argdata[argvptr_pos]; - - argstart = NULL; - - for(argi=0; argi= max_argv) break; - } - } - } - - if (arglen && __system_argc < max_argv) { - strncpy(argstorage, argstart, arglen); - argstorage[arglen] = 0; - __system_argv[__system_argc] = argstorage; - __system_argc++; - } - - __system_argv[__system_argc] = NULL; } + else if (envHasArgv()) { + args = envGetArgv(); + argdata_allocsize = 0x9000;//Use the same size as NSO. + argdata_strsize = strlen(args); + argdata = (u8*)fake_heap_start; + + if (argdata_strsize==0) return; + + if (fake_heap_end - fake_heap_start < argdata_allocsize) return; + + fake_heap_start += argdata_allocsize; + + argvptr_pos = 0; + + memset(argdata, 0, argdata_allocsize); + } + + argstorage = (char*)&argdata[argvptr_pos]; + + argvptr_pos += (argdata_strsize+1 + 0x9) & ~0x7; + if (argvptr_pos >= argdata_allocsize) return; + + max_argv = (argdata_allocsize - argvptr_pos) >> 3; + if (max_argv < 2) return; + + __system_argv = (char**)&argdata[argvptr_pos]; + + argstart = NULL; + + for(argi=0; argi= max_argv) break; + } + } + } + + if (arglen && __system_argc < max_argv) { + strncpy(argstorage, argstart, arglen); + argstorage[arglen] = 0; + __system_argv[__system_argc] = argstorage; + __system_argc++; + } + + __system_argv[__system_argc] = NULL; } From 3acc66ee4752fd60f3b1379746269a8ef74f0cd7 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Sun, 18 Feb 2018 12:18:35 -0500 Subject: [PATCH 08/11] Updated README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dcd51e22..cab25611 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Based on libctru. * Install [devkitA64](https://sourceforge.net/projects/devkitpro/files/devkitA64/) to `$DEVKITPRO/devkitA64`. * `make install` -See also the [switch-tools](https://github.com/switchbrew/switch-tools) repo if you want to manually build those. +See also the [switch-tools](https://github.com/switchbrew/switch-tools) repo if you want to manually build those - devkitA64 includes switch-tools. See also [switch-examples](https://github.com/switchbrew/switch-examples). # Icon From 8b0b2407f96d00bc53d9ed1f2067c9296ced75d0 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Sun, 18 Feb 2018 22:14:03 -0500 Subject: [PATCH 09/11] Updated README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cab25611..190805ab 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Based on libctru. # Install instructions * Install [devkitA64](https://sourceforge.net/projects/devkitpro/files/devkitA64/) to `$DEVKITPRO/devkitA64`. -* `make install` +* Install libnx with the [latest](https://github.com/switchbrew/libnx/releases/latest) release. If building from source, use: `make install` See also the [switch-tools](https://github.com/switchbrew/switch-tools) repo if you want to manually build those - devkitA64 includes switch-tools. See also [switch-examples](https://github.com/switchbrew/switch-examples). From 43124d2b958ccce878c7514662d5c94e29f2be65 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Mon, 19 Feb 2018 02:11:57 -0500 Subject: [PATCH 10/11] Updated README. --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 190805ab..e7f8cad4 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,7 @@ Based on libctru. [![Build status](https://doozer.io/badge/switchbrew/libnx/buildstatus/master)](https://doozer.io/switchbrew/libnx) # Install instructions -* Install [devkitA64](https://sourceforge.net/projects/devkitpro/files/devkitA64/) to `$DEVKITPRO/devkitA64`. -* Install libnx with the [latest](https://github.com/switchbrew/libnx/releases/latest) release. If building from source, use: `make install` - -See also the [switch-tools](https://github.com/switchbrew/switch-tools) repo if you want to manually build those - devkitA64 includes switch-tools. See also [switch-examples](https://github.com/switchbrew/switch-examples). +See [Switchbrew](http://switchbrew.org/index.php?title=Setting_up_Development_Environment). # Icon From 9f8a33ba11ee4e4ceeaa0f3e7e1a8d145ba1af0f Mon Sep 17 00:00:00 2001 From: yellows8 Date: Mon, 19 Feb 2018 02:29:52 -0500 Subject: [PATCH 11/11] Added default_icon.jpg to Makefile dist-src. --- nx/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nx/Makefile b/nx/Makefile index 8911dfa5..5e1822ff 100644 --- a/nx/Makefile +++ b/nx/Makefile @@ -97,7 +97,7 @@ dist-bin: all @tar --exclude=*~ -cjf libnx-$(VERSION).tar.bz2 include lib default_icon.jpg switch_rules switch.ld switch.specs dist-src: - @tar --exclude=*~ -cjf libnx-src-$(VERSION).tar.bz2 include source data Makefile switch_rules switch.ld switch.specs + @tar --exclude=*~ -cjf libnx-src-$(VERSION).tar.bz2 include source data Makefile default_icon.jpg switch_rules switch.ld switch.specs dist: dist-src dist-bin