From 83a79afb3df4474c7da1cd8f0ed294db1e789051 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 24 Jun 2018 12:01:35 +0900 Subject: [PATCH] Make accounts, service, and result more Nim friendly --- examples/accounts/account_ex.nim | 28 +++++ examples/accounts/account_ex.nim.cfg | 1 + examples/helloworld/helloworld.nim | 17 +-- src/libnx/account.nim | 158 +++++++++++++++++++++++++++ src/libnx/nim.cfg | 1 + src/libnx/results.nim | 127 +++++++++++++++++++++ src/libnx/service.nim | 105 ++++++++++++++++++ src/libnx/utils.nim | 13 +++ 8 files changed, 435 insertions(+), 15 deletions(-) create mode 100644 examples/accounts/account_ex.nim create mode 100644 examples/accounts/account_ex.nim.cfg create mode 100644 src/libnx/account.nim create mode 100644 src/libnx/nim.cfg create mode 100644 src/libnx/results.nim create mode 100644 src/libnx/service.nim create mode 100644 src/libnx/utils.nim diff --git a/examples/accounts/account_ex.nim b/examples/accounts/account_ex.nim new file mode 100644 index 0000000..cb1f105 --- /dev/null +++ b/examples/accounts/account_ex.nim @@ -0,0 +1,28 @@ +import libnx/wrapper/gfx +import libnx/wrapper/hid +import libnx/wrapper/console +import libnx/account +import libnx/utils + +proc main() = + gfxInitDefault() + discard consoleInit(nil) + + echo "\x1b[5;2H" & "Account info:" + + withAccountService: + try: + let user = getActiveUser() + echo "\x1b[6;2HUsername: " & user.username + echo "\x1b[7;2HMiiID: " & $user.miiID + echo "\x1b[8;2HIconID: " & $user.iconID + except AccountError: + echo "\x1b[6;2HNo user currently selected!" + + mainLoop: + let keysDown = hidKeysDown(CONTROLLER_P1_AUTO) + + if (keysDown and KEY_PLUS.uint64) > 0.uint64: + break + +main() diff --git a/examples/accounts/account_ex.nim.cfg b/examples/accounts/account_ex.nim.cfg new file mode 100644 index 0000000..e9b9a2e --- /dev/null +++ b/examples/accounts/account_ex.nim.cfg @@ -0,0 +1 @@ +--path="../../src" diff --git a/examples/helloworld/helloworld.nim b/examples/helloworld/helloworld.nim index 29723dd..7494893 100644 --- a/examples/helloworld/helloworld.nim +++ b/examples/helloworld/helloworld.nim @@ -1,30 +1,17 @@ import libnx/wrapper/gfx import libnx/wrapper/console import libnx/wrapper/hid -import libnx/wrapper/applet - -proc printf(formatstr: cstring) {.importc: "printf", varargs, - header: "".} +import libnx/utils proc main() = gfxInitDefault() - discard consoleInit(nil) - printf("\x1b[16;20HHello World From NIM!") echo "\x1b[17;20HHELLO FROM NIM" - while appletMainLoop(): - hidScanInput() - + mainLoop: let keysDown = hidKeysDown(CONTROLLER_P1_AUTO) if (keysDown and KEY_PLUS.uint64) > 0.uint64: break - gfxFlushBuffers() - gfxSwapBuffers() - gfxWaitForVsync() - - gfxExit() - main() diff --git a/src/libnx/account.nim b/src/libnx/account.nim new file mode 100644 index 0000000..282483d --- /dev/null +++ b/src/libnx/account.nim @@ -0,0 +1,158 @@ +import strutils +import libnx/wrapper/acc +import libnx/results +import libnx/wrapper/types +import libnx/service + +type + AccountError* = object of Exception + + User* = ref object + id*: u128 + selected*: bool + username*: string + lastEdited*: uint64 + iconID*: uint32 + iconBgColorID*: uint8 + miiID*: array[0x10, uint8] + + Profile* = ref object + service*: Service + + AccountImage* = ref object + data: seq[uint8] + imageSize: int + +var enabled = false + +template raiseEx(message: string) = + raise newException(AccountError, message) + +proc getService*(): Service = + let serv = accountGetService()[] + result = newService(serv) + +proc init() = + let service = getService() + if service.isActive: + return + + let code = accountInitialize().newResult + if code.failed: + raiseEx("Error, account api could not be initialized: " & code.description) + +proc exit() = + let service = getService() + if not service.isActive: + return + accountExit() + +proc close*(profile: AccountProfile) = + accountProfileClose(profile.unsafeAddr) + +template withAccountService*(code: untyped): typed = + enabled = true + init() + code + exit() + enabled = false + +proc ensureEnabled() = + if not enabled: + raiseEx("Use withAccountService to access account functions.") + +proc getImageSize*(profile: AccountProfile): int = + ensureEnabled() + var res = 0.csize + result = 0 + let code = accountProfileGetImageSize(profile.unsafeAddr, res.addr).newResult + if code.failed: + raiseEx("Error, image size could not be received: " & code.description) + result = res.int + +proc imageSize*(user: User): int = + var prof: AccountProfile + var size: csize + let res = accountProfileGetImageSize(prof.addr, size.addr).newResult + + if res.failed: + raiseEx("Error, could not get image size: " & res.description) + + result = size.int + +proc getProfileHelper(userID: u128): AccountProfile = + let res = accountGetProfile(result.addr, userID).newResult + + if res.failed: + raiseEx("Error, could not get user profile: " & res.description) + + +proc loadImage*(user: User): AccountImage = + ensureEnabled() + result = new(AccountImage) + let imSize = user.imageSize() + var size: csize + + var prof = getProfileHelper(user.id) + + result.data = newSeq[uint8](imSize) + + let res = accountProfileLoadImage( + prof.addr, result.data[0].addr, + imSize, result.imageSize.addr + ).newResult + + if res.failed: + prof.close() + raiseEx("Error, could not load image: " & res.description) + + prof.close() + +proc getActiveUser*(): User = + ensureEnabled() + result = new(User) + var + userID: u128 + selected: bool + + var res = accountGetActiveUser(userID.addr, selected.addr).newResult + + if res.failed: + raiseEx("Error, could not get active user ID: " & res.description) + + if not selected: + raiseEx("No user currently selected!") + + result.id = userID + result.selected = selected + + var + prof: AccountProfile = getProfileHelper(userID) + userData: AccountUserData + profBase: AccountProfileBase + + res = accountProfileGet(prof.addr, userData.addr, profBase.addr).newResult + + if res.failed: + raiseEx("Error, could not get user data: " & res.description) + + result.username = profBase.username.join("") + result.lastEdited = profBase.lastEditTimestamp + result.iconID = userData.iconID + result.iconBgColorID = userData.iconBackgroundColorID + result.miiID = userData.miiID + + prof.close() + + +proc getProfile*(user: User): Profile = + ensureEnabled() + result = new(Profile) + var prof: AccountProfile + + let res = accountGetProfile(prof.addr, user.id).newResult + if res.failed: + raiseEx("Error, could not get account profile: " & res.description) + + result.service = newService(prof.s) + prof.close() diff --git a/src/libnx/nim.cfg b/src/libnx/nim.cfg new file mode 100644 index 0000000..51df3ce --- /dev/null +++ b/src/libnx/nim.cfg @@ -0,0 +1 @@ +--path="../" diff --git a/src/libnx/results.nim b/src/libnx/results.nim new file mode 100644 index 0000000..74198c0 --- /dev/null +++ b/src/libnx/results.nim @@ -0,0 +1,127 @@ +import + libnx/wrapper/result, + libnx/wrapper/types + +type + + Module {.pure.} = enum + Invalid = 0, # This is just so the nim compiler is happy + Kernel = 1, + Libnx = 345, + LibnxNvidia = 348 + + Result* = ref object + code*: uint32 + module*: string + description*: string + case kind*: Module + of Module.Kernel: + kernelError*: KernelError + of Module.Libnx: + libnxError*: LibnxError + of Module.LibnxNvidia: + libnxNvidiaError*: LibnxNvidiaError + else: + discard + + KernelError* {.pure.} = enum + Timeout = 117 + + LibnxError* {.pure.} = enum + BadReloc=1, + OutOfMemory, + AlreadyMapped, + BadGetInfo_Stack, + BadGetInfo_Heap, + BadQueryMemory, + AlreadyInitialized, + NotInitialized, + NotFound, + IoError, + BadInput, + BadReent, + BufferProducerError, + HandleTooEarly, + HeapAllocFailed, + TooManyOverrides, + ParcelError, + BadGfxInit, + BadGfxEventWait, + BadGfxQueueBuffer, + BadGfxDequeueBuffer, + AppletCmdidNotFound, + BadAppletReceiveMessage, + BadAppletNotifyRunning, + BadAppletGetCurrentFocusState, + BadAppletGetOperationMode, + BadAppletGetPerformanceMode, + BadUsbCommsRead, + BadUsbCommsWrite, + InitFail_SM, + InitFail_AM, + InitFail_HID, + InitFail_FS, + BadGetInfo_Rng, + JitUnavailable, + WeirdKernel, + IncompatSysVer, + InitFail_Time, + TooManyDevOpTabs + + LibnxNvidiaError* {.pure.} = enum + Unknown=1, + NotImplemented, #///< Maps to Nvidia: 1 + NotSupported, #///< Maps to Nvidia: 2 + NotInitialized, #///< Maps to Nvidia: 3 + BadParameter, #///< Maps to Nvidia: 4 + Timeout, #///< Maps to Nvidia: 5 + InsufficientMemory, #///< Maps to Nvidia: 6 + ReadOnlyAttribute, #///< Maps to Nvidia: 7 + InvalidState, #///< Maps to Nvidia: 8 + InvalidAddress, #///< Maps to Nvidia: 9 + InvalidSize, #///< Maps to Nvidia: 10 + BadValue, #///< Maps to Nvidia: 11 + AlreadyAllocated, #///< Maps to Nvidia: 13 + Busy, #///< Maps to Nvidia: 14 + ResourceError, #///< Maps to Nvidia: 15 + CountMismatch, #///< Maps to Nvidia: 16 + SharedMemoryTooSmall, #///< Maps to Nvidia: 0x1000 + FileOperationFailed, #///< Maps to Nvidia: 0x30003 + IoctlFailed #///< Maps to Nvidia: 0x3000F + + +proc succeeded*(res: Result): bool = res.code.R_SUCCEEDED +proc failed*(res: Result): bool = res.code.R_FAILED + +proc newResult*(code: uint32): Result = + result = new(Result) + + result.code = code + if code.R_SUCCEEDED: + return + + let moduleCode = code.R_MODULE + let module = Module(moduleCode) + + result.kind = module + + let descCode = code.R_DESCRIPTION + let resCode = MAKERESULT(moduleCode, descCode) + + var description = "" + + case module + of Module.Invalid: + discard + of Module.Kernel: + result.kernelError = KernelError(resCode) + description = $result.kernelError + of Module.Libnx: + result.libnxError = LibnxError(resCode) + description = $result.libnxError + of Module.LibnxNvidia: + result.libnxNvidiaError = LibnxNvidiaError(resCode) + description = $result.libnxNvidiaError + + result.module = $module + result.description = description diff --git a/src/libnx/service.nim b/src/libnx/service.nim new file mode 100644 index 0000000..72b9dfe --- /dev/null +++ b/src/libnx/service.nim @@ -0,0 +1,105 @@ +import + libnx/results, + libnx/wrapper/sm + +from libnx/wrapper/types import Handle + +type + Service* = ref object + serv: sm.Service + + +proc newService*(serv: sm.Service): Service = + result = Service(serv: serv) + +## * +## @brief Returns whether a service is overriden in the homebrew environment. +## @param[in] s Service object. +## @return true if overriden. +## +proc isOverride*(service: Service): bool = + serviceIsOverride(service.serv.addr) + +## * +## @brief Returns whether a service has been initialized. +## @param[in] s Service object. +## @return true if initialized. +## +proc isActive*(service: Service): bool = + serviceIsActive(service.serv.addr) + +## * +## @brief Returns whether a service is a domain. +## @param[in] s Service object. +## @return true if a domain. +## +proc isDomain*(service: Service): bool = + serviceIsDomain(service.serv.addr) + +## * +## @brief Returns whether a service is a domain subservice. +## @param[in] s Service object. +## @return true if a domain subservice. +## +proc isDomainSubservice*(service: Service): bool = + serviceIsDomainSubservice(service.serv.addr) + +## * +## @brief For a domain/domain subservice, return the associated object ID. +## @param[in] s Service object, necessarily a domain or domain subservice. +## @return The object ID. +## +proc objectId*(service: Service): uint32 = + serviceGetObjectId(service.serv.addr) + +## * +## @brief Closes a domain object by ID. +## @param[in] s Service object, necessarily a domain or domain subservice. +## @param object_id ID of the object to close. +## @return Result code. +## +proc close*(service: Service; objectId: uint32): Result = + serviceCloseObjectById(service.serv.addr, objectId).newResult + + +## * +## @brief Dispatches an IPC request to a service. +## @param[in] s Service object. +## @return Result code. +## +proc ipcDispatch*(service: Service): Result = + serviceIpcDispatch(service.serv.addr).newResult + +## * +## @brief Creates a service object from an IPC session handle. +## @param[out] s Service object. +## @param[in] h IPC session handle. +## +proc createService*(handle: Handle): Service = + result = new(Service) + serviceCreate(result.serv.addr, handle) + +## * +## @brief Creates a domain subservice object from a parent service. +## @param[out] s Service object. +## @param[in] parent Parent service, necessarily a domain or domain subservice. +## @param[in] object_id Object ID for this subservice. +## +proc createDomainSubservice*(parent: Service; objectId: uint32): Service = + result = new(Service) + serviceCreateDomainSubservice(result.serv.addr, parent.serv.addr, objectId) + +## * +## @brief Converts a regular service to a domain. +## @param[in] s Service object. +## @return Result code. +## +proc convertToDomain*(service: Service): Result = + serviceConvertToDomain(service.serv.addr).newResult + +## * +## @brief Closes a service. +## @param[in] s Service object. +## +proc close*(service: Service) = + serviceClose(service.serv.addr) diff --git a/src/libnx/utils.nim b/src/libnx/utils.nim new file mode 100644 index 0000000..d6ae24e --- /dev/null +++ b/src/libnx/utils.nim @@ -0,0 +1,13 @@ +import libnx/wrapper/gfx, libnx/wrapper/hid, libnx/wrapper/applet + +template mainLoop*(code: untyped): untyped = + while appletMainLoop(): + hidScanInput() + + code + + gfxFlushBuffers() + gfxSwapBuffers() + gfxWaitForVSync() + + gfxExit()