Make accounts, service, and result more Nim friendly

This commit is contained in:
Joey Yakimowich-Payne 2018-06-24 12:01:35 +09:00
commit 83a79afb3d
8 changed files with 435 additions and 15 deletions

View file

@ -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()

View file

@ -0,0 +1 @@
--path="../../src"

View file

@ -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: "<stdio.h>".}
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()

158
src/libnx/account.nim Normal file
View file

@ -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()

1
src/libnx/nim.cfg Normal file
View file

@ -0,0 +1 @@
--path="../"

127
src/libnx/results.nim Normal file
View file

@ -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

105
src/libnx/service.nim Normal file
View file

@ -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)

13
src/libnx/utils.nim Normal file
View file

@ -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()