├── .gitignore ├── LICENSE ├── README.md ├── examples ├── accounts │ ├── account_ex.nim │ └── nim.cfg └── helloworld │ ├── helloworld.nim │ └── nim.cfg ├── libnx.nimble ├── libnxGen.cfg └── src └── libnx ├── account.nim ├── app.nim ├── console.nim ├── ext └── integer128.nim ├── graphics.nim ├── input.nim ├── nim.cfg ├── results.nim ├── service.nim └── utils.nim /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | # Swap 3 | [._]*.s[a-v][a-z] 4 | [._]*.sw[a-p] 5 | [._]s[a-rt-v][a-z] 6 | [._]ss[a-gi-z] 7 | [._]sw[a-p] 8 | 9 | # Session 10 | Session.vim 11 | 12 | # Temporary 13 | .netrwhist 14 | *~ 15 | # Auto-generated tag files 16 | tags 17 | # Persistent undo 18 | [._]*.un~ 19 | 20 | build 21 | *.elf 22 | wrapper 23 | 24 | nimcache/ 25 | nimcache*/ 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nim-libnx 2 | Libnx ported to the Nim programming language. You will need a Nim compiler with Nintendo switch support which can be found in the latest devel branch of the Nim compiler. 3 | 4 | You also must have DevkitPro and switch (libnx) libraries for [Mac and Linux](https://github.com/devkitPro/pacman/releases) or [Windows](https://github.com/devkitPro/installer/releases) installed. 5 | 6 | From dkp-pacman, the switch libraries can be installed with: 7 | 8 | ``` 9 | dkp-pacman -Syu 10 | dkp-pacman -S switch-dev 11 | ## When it asks for installation options, choose the default which will install everything 12 | ``` 13 | 14 | The DEVKITPRO environment variable must also exist and point to a directory with the following structure: 15 | 16 | - `DEVKITPRO/libnx/lib` 17 | - `DEVKITPRO/libnx/include` 18 | 19 | OR you must specify a valid libnx path and/or devkitpro path to the `switch_build` utility: 20 | 21 | ```bash 22 | switch_build --libnxPath:"C:\devkitPro\libnx" --author:"Joey" --version:"1.0.0" .\examples\accounts\account_ex.nim 23 | # OR 24 | switch_build --devkitProPath:"C:\devkitPro" --author:"Joey" --version:"1.0.0" .\examples\accounts\account_ex.nim 25 | ``` 26 | 27 | ## Install 28 | 29 | Simply run 30 | 31 | ```bash 32 | # Because of a bug in nimble right now, you must install this first! 33 | nimble install nimgen@#head 34 | nimble install 35 | ``` 36 | 37 | To compile examples: 38 | 39 | ```bash 40 | nimble buildExamples 41 | ``` 42 | 43 | PRs are welcome for porting other examples or any other changes! 44 | -------------------------------------------------------------------------------- /examples/accounts/account_ex.nim: -------------------------------------------------------------------------------- 1 | import sets, strutils 2 | import libnx/[graphics, console, account, input, app] 3 | import libnx/ext/integer128 4 | 5 | mainFunction: 6 | graphics.initDefault() 7 | console.init() 8 | 9 | printAt (5, 2), "Account info:" 10 | 11 | withAccountService: 12 | try: 13 | let 14 | user = getActiveUser() 15 | userID = user.id 16 | 17 | print "UserID: 0x" & userID.toHex() 18 | print "Username: " & user.username 19 | print "MiiID: " & $user.miiID 20 | print "IconID: " & $user.iconID 21 | except AccountUserNotSelectedError: 22 | print "No user currently selected!" 23 | except AccountError: 24 | let msg = getCurrentExceptionMsg() 25 | print msg 26 | 27 | try: 28 | let users = listAllUsers() 29 | print "" 30 | print "There are $# users:" % $users.len() 31 | for user in users: 32 | print "User: " & user.username 33 | except AccountUserListError: 34 | let msg = getCurrentExceptionMsg() 35 | print msg 36 | 37 | 38 | mainLoop: 39 | let keysDown = keysDown(Controller.P1_AUTO) 40 | 41 | if keysDown.len() > 0: 42 | print keysDown 43 | 44 | if ControllerKey.Plus in keysDown: 45 | break 46 | -------------------------------------------------------------------------------- /examples/accounts/nim.cfg: -------------------------------------------------------------------------------- 1 | --path="../../src" 2 | -------------------------------------------------------------------------------- /examples/helloworld/helloworld.nim: -------------------------------------------------------------------------------- 1 | import sets 2 | import libnx/[graphics, console, app, input] 3 | 4 | 5 | mainFunction: 6 | initDefault() 7 | console.init() 8 | 9 | printAt (17, 20), "HELLO FROM NIM" 10 | mainLoop: 11 | let keysDown = keysDown(Controller.P1_AUTO) 12 | 13 | if keysDown.len() > 0: 14 | echo keysDown 15 | 16 | if ControllerKey.Plus in keysDown: 17 | break 18 | -------------------------------------------------------------------------------- /examples/helloworld/nim.cfg: -------------------------------------------------------------------------------- 1 | --path="../../src" 2 | -------------------------------------------------------------------------------- /libnx.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.2.2" 4 | author = "Joey Payne" 5 | description = "Nintendo Switch library libnx for Nim." 6 | license = "The Unlicense" 7 | 8 | srcDir = "src" 9 | 10 | import distros 11 | 12 | var prefix = "" 13 | var username = getEnv("USER") 14 | if detectOs(Windows): 15 | prefix = "cmd /c " 16 | username = getEnv("USERNAME") 17 | 18 | # Deps 19 | requires "nim >= 0.18.1", "nimgen#dc9943a22c9c8f6a5a6a92f0055e1de4dfaf87d2" 20 | requires "switch_build >= 0.1.3" 21 | 22 | task setup, "Download and generate bindings": 23 | echo "Building libnx..." 24 | exec prefix & "nimgen libnxGen.cfg" 25 | 26 | task buildExamples, "Build switch examples": 27 | if detectOs(Windows): 28 | let devkitPath = getEnv("DEVKITPRO") 29 | if devkitPath == "" or not dirExists(devkitPath): 30 | echo "You must set the DEVKITPRO environment variable to something valid!" 31 | else: 32 | exec prefix & "switch_build --libnxPath=\"" & devkitPath & "/libnx/\" --author=\"" & username & "\" --version=\"1.0.0\" examples/helloworld/helloworld.nim" 33 | exec prefix & "switch_build --libnxPath=\"" & devkitPath & "/libnx/\" --author=\"" & username & "\" --version=\"1.0.0\" examples/accounts/account_ex.nim" 34 | else: 35 | exec prefix & "switch_build --libnxPath=\"" & thisDir() & "/src/libnx/wrapper/nx/\" --author=\"" & username & "\" --version=\"1.0.0\" examples/helloworld/helloworld.nim" 36 | exec prefix & "switch_build --libnxPath=\"" & thisDir() & "/src/libnx/wrapper/nx/\" --author=\"" & username & "\" --version=\"1.0.0\" examples/accounts/account_ex.nim" 37 | 38 | before install: 39 | setupTask() 40 | 41 | task test, "Run tests": 42 | discard 43 | # no tests because code needs to run on the Switch :( 44 | # exec "nim c -r tests/test.nim" 45 | -------------------------------------------------------------------------------- /libnxGen.cfg: -------------------------------------------------------------------------------- 1 | [n.global] 2 | output="src/libnx/wrapper" 3 | c_compiler="aarch64-none-elf-gcc" 4 | cpp_compiler="aarch64-none-elf-g++" 5 | filter=lock 6 | 7 | [n.include] 8 | "${output}/nx/include" 9 | "${output}/nx/include/switch" 10 | "${output}/nx/include/switch/arm" 11 | "${output}/nx/include/switch/audio" 12 | "${output}/nx/include/switch/display" 13 | "${output}/nx/include/switch/kernel" 14 | "${output}/nx/include/switch/nvidia" 15 | "${output}/nx/include/switch/runtime" 16 | "${output}/nx/include/switch/runtime/util" 17 | "${output}/nx/include/switch/runtime/devices" 18 | "${output}/nx/include/switch/services" 19 | "${DEVKITPRO}/devkitA64/aarch64-none-elf/include/" 20 | 21 | [n.exclude] 22 | "${output}/nim.cfg" 23 | "${output}/config.nims" 24 | 25 | [svc.h] 26 | search = "PACKED" 27 | replace = "" 28 | 29 | search.noreturn = "NORETURN" 30 | replace.noreturn = "" 31 | 32 | defines=true 33 | 34 | [result.h] 35 | defines = true 36 | 37 | [thread_context.h] 38 | preprocess = false 39 | 40 | [hid.h] 41 | defines = true 42 | search.static_assert = "static_assert" 43 | replace.static_assert = "// static_assert" 44 | search.touch = "touchPosition" 45 | replace.touch = "TouchPosition" 46 | 47 | [ipc.h] 48 | defines = true 49 | 50 | [romfs_dev.h] 51 | search = "romfs_" 52 | replace = "Romfs_" 53 | 54 | [gfx.h] 55 | defines = true 56 | 57 | [ioctl.h] 58 | defines = true 59 | search = "_NV_" 60 | replace = "UNV_" 61 | 62 | search.nv = "__nv_" 63 | replace.nv = "DUnv_" 64 | 65 | [n.wildcard] 66 | wildcard.1 = "console.h" 67 | rename = "$replace(console=cons)" 68 | 69 | wildcard.2 = "*.h" 70 | 71 | search.u8 = "u8" 72 | replace.u8 = "uint8" 73 | 74 | search.u16 = "u16" 75 | replace.u16 = "uint16" 76 | 77 | search.u32 = "u32" 78 | replace.u32 = "uint32" 79 | 80 | search.u64 = "u64" 81 | replace.u64 = "uint64" 82 | 83 | wildcard.3 = "*.nim" 84 | 85 | search.bool = "_Bool" 86 | replace.bool = "bool" 87 | 88 | search.lib = "src/libnx/wrapper" 89 | replace.lib = "libnx/wrapper" 90 | 91 | search.kimport = "../kernel/" 92 | replace.kimport = "libnx/wrapper/" 93 | 94 | search.resimport = "../result" 95 | replace.resimport = "libnx/wrapper/result" 96 | 97 | search.armimport = "../arm/" 98 | replace.armimport = "libnx/wrapper/" 99 | 100 | search.lock = "_LOCK" 101 | replace.lock = "LOCK" 102 | 103 | search.cdecl = "stdcall" 104 | replace.cdecl = "cdecl" 105 | 106 | [n.prepare] 107 | git = "https://github.com/switchbrew/libnx" 108 | execute-lin = """cd ${output}; make;""" 109 | execute-mac = """cd ${output}; make;""" 110 | 111 | [n.post] 112 | reset=true 113 | 114 | [src/libnx/wrapper/nim.cfg] 115 | create = """ 116 | --path:"../" 117 | --path:"../../" 118 | """ 119 | noprocess=true 120 | 121 | [src/libnx/wrapper/config.nims] 122 | create = """ 123 | switch("passC", "-I" & thisDir() & "/nx/include") 124 | switch("passL", "-specs=" & thisDir() & "/nx/switch.specs -L" & thisDir() & "/nx/lib -lnx") 125 | """ 126 | noprocess=true 127 | 128 | [switch.h] 129 | preprocess = true 130 | defines = true 131 | recurse = true 132 | 133 | 134 | [types.nim] 135 | search.o = " uint8*" 136 | prepend.o = """ 137 | u8* = uint8 138 | u16* = uint16 139 | u32* = uint32 140 | u64* = uint64 141 | int8_t* = int8 142 | int16_t* = int16 143 | int32_t* = int32 144 | int64_t* = int64 145 | ssize_t* = int 146 | """ 147 | 148 | search.s128 = " s128* = __int128_t" 149 | replace.s128 = "" 150 | 151 | search.u128 = " u128* = __uint128_t" 152 | replace.u128 = "" 153 | 154 | search.uint8 = "uint8* = uint8_t" 155 | replace.uint8 = "uint8_t* = uint8" 156 | 157 | search.uint16 = "uint16* = uint16_t" 158 | replace.uint16 = "uint16_t* = uint16" 159 | 160 | search.uint32 = "uint32* = uint32_t" 161 | replace.uint32 = "uint32_t* = uint32" 162 | 163 | search.uint64 = "uint64* = uint64_t" 164 | replace.uint64 = "uint64_t* = uint64" 165 | 166 | search.import = "type\n" 167 | prepend.import = """ 168 | import libnx/ext/integer128 169 | template BIT*(n): auto = (1.uint shl n) 170 | """ 171 | 172 | [thread_context.nim] 173 | search.timport = "../types" 174 | replace.timport = "libnx/wrapper/types" 175 | 176 | prepend.o = """ 177 | import libnx/ext/integer128 178 | """ 179 | 180 | search.cpuall = "RegisterGroup_CpuAll =" 181 | comment.cpuall = 1 182 | 183 | search.fpugprs = "RegisterGroup_FpuGprs =" 184 | prepend.fpugprs = "RegisterGroup_CpuAll = BIT(0) or BIT(1), ## /< All CPU registers." 185 | 186 | search.fpuall = "RegisterGroup_FpuGprs or RegisterGroup_FpuSprs" 187 | replace.fpuall = "BIT(2) or BIT(3)" 188 | 189 | search.groupall = "RegisterGroup_CpuAll or RegisterGroup_FpuAll" 190 | replace.groupall = "BIT(0) or BIT(1) or BIT(2) or BIT(3)" 191 | 192 | [svc.nim] 193 | search.timport = "../types" 194 | replace.timport = "libnx/wrapper/types" 195 | 196 | search.permx = "Perm_X = BIT(2), ## /< Execute permission." 197 | replace.permx = "Perm_Rw = Perm_R.int or Perm_W.int, ## /< Read/write permissions." 198 | 199 | search.permrw = "Perm_Rw = Perm_R or Perm_W, ## /< Read/write permissions." 200 | replace.permrw = "Perm_X = BIT(2), ## /< Execute permission." 201 | 202 | search.permrx = "Perm_Rx = Perm_R or Perm_X, ## /< Read/execute permissions." 203 | replace.permrx = "Perm_Rx = Perm_R.int or Perm_X.int, ## /< Read/execute permissions." 204 | 205 | [shmem.nim] 206 | search.import = "type\n" 207 | prepend.import = """ 208 | import libnx/wrapper/svc 209 | """ 210 | 211 | [nacp.nim] 212 | search.t = "type" 213 | prepend.t = """ 214 | import libnx/wrapper/types 215 | """ 216 | 217 | [lock.nim] 218 | search.flock_t = " __lock_t" 219 | replace.flock_t = " DUlock_t" 220 | 221 | search.nlock_t = "= __lock_t" 222 | replace.nlock_t = "= DUlock_t" 223 | 224 | search.import = "type" 225 | prepend.import = """ 226 | import libnx/wrapper/types 227 | """ 228 | 229 | search.proc_under = "proc __" 230 | replace.proc_under = "proc DU" 231 | 232 | [ipc.nim] 233 | search.o = "UINT32_MAX" 234 | replace.o = "uint32.high" 235 | 236 | search.import = "import\n" 237 | append.import = """ 238 | libnx/wrapper/types, 239 | """ 240 | 241 | [audin.nim] 242 | search.o = "import " 243 | prepend.o = """ 244 | import libnx/wrapper/types 245 | """ 246 | 247 | [audout.nim] 248 | search.o = "import " 249 | prepend.o = """ 250 | import libnx/wrapper/types 251 | """ 252 | 253 | [hid.nim] 254 | search.servimport = "../services/" 255 | replace.servimport = "libnx/wrapper/" 256 | search.timport = "../types" 257 | replace.timport = "libnx/wrapper/types" 258 | search.o = """ KEY_JOYCON_RIGHT = BIT(0), KEY_JOYCON_DOWN = BIT(1), KEY_JOYCON_UP = BIT(2), KEY_JOYCON_LEFT = BIT( 259 | 3),""" 260 | replace.o = "" 261 | 262 | search.key_left = "KEY_LEFT = KEY_DLEFT or KEY_LSTICK_LEFT or KEY_RSTICK_LEFT, ## /< D-Pad Left or Sticks Left" 263 | replace.key_left = "" 264 | search.key_rstick_up = " KEY_RSTICK_UP" 265 | prepend.key_rstick_up = """ 266 | KEY_LEFT = KEY_DLEFT.int or KEY_LSTICK_LEFT.int or KEY_RSTICK_LEFT.int, ## /< D-Pad Left or Sticks Left 267 | """ 268 | 269 | search.key_right = " KEY_RIGHT = KEY_DRIGHT or KEY_LSTICK_RIGHT or KEY_RSTICK_RIGHT ## /< D-Pad Right or Sticks Right" 270 | replace.key_right = "" 271 | search.key_rstick_down = " KEY_RSTICK_DOWN" 272 | prepend.key_rstick_down = """ 273 | KEY_RIGHT = KEY_DRIGHT.int or KEY_LSTICK_RIGHT.int or KEY_RSTICK_RIGHT.int, ## /< D-Pad Right or Sticks Right 274 | """ 275 | 276 | search.key_up = " KEY_UP = KEY_DUP or KEY_LSTICK_UP or KEY_RSTICK_UP, ## /< D-Pad Up or Sticks Up" 277 | replace.key_up = "" 278 | search.key_rstick_right = " KEY_RSTICK_RIGHT" 279 | prepend.key_rstick_right = """ 280 | KEY_UP = KEY_DUP.int or KEY_LSTICK_UP.int or KEY_RSTICK_UP.int, ## /< D-Pad Up or Sticks Up 281 | """ 282 | 283 | search.key_down = " KEY_DOWN = KEY_DDOWN or KEY_LSTICK_DOWN or KEY_RSTICK_DOWN, ## /< D-Pad Down or Sticks Down" 284 | replace.key_down = "" 285 | search.key_sl = " KEY_SL" 286 | prepend.key_sl = """ 287 | KEY_DOWN = KEY_DDOWN.int or KEY_LSTICK_DOWN.int or KEY_RSTICK_DOWN.int, ## /< D-Pad Down or Sticks Down 288 | """ 289 | 290 | [nifm.nim] 291 | search.o = "import libnx" 292 | prepend.o = """ 293 | import libnx/wrapper/types 294 | """ 295 | 296 | [set.nim] 297 | search.o = "import libnx" 298 | prepend.o = """ 299 | import libnx/wrapper/types 300 | """ 301 | 302 | [parcel.nim] 303 | search.o = "import libnx" 304 | prepend.o = """ 305 | import libnx/wrapper/types 306 | """ 307 | 308 | [fs_dev.nim] 309 | search.o = "import libnx" 310 | prepend.o = """ 311 | import libnx/wrapper/types 312 | """ 313 | 314 | [buffer_producer.nim] 315 | search.o = "import libnx" 316 | prepend.o = """ 317 | import libnx/wrapper/types 318 | import libnx/wrapper/binder 319 | """ 320 | 321 | [nxlink.nim] 322 | search.o = "var __nxlink" 323 | replace.o = "var DUnxlink" 324 | 325 | [fs.nim] 326 | search.o = "import libnx" 327 | prepend.o = """ 328 | import libnx/ext/integer128 329 | """ 330 | 331 | [acc.nim] 332 | prepend.o = """ 333 | import libnx/ext/integer128 334 | """ 335 | 336 | [fence.nim] 337 | prepend.o = """ 338 | import libnx/wrapper/types 339 | """ 340 | 341 | [cons.nim] 342 | search.o = "type\n" 343 | prepend.o = """ 344 | 345 | template CONSOLE_ESC*(x: string): string = "\x1b[" & $x 346 | # Colors 347 | template CONSOLE_RESET*: string = CONSOLE_ESC("0m") 348 | template CONSOLE_BLACK*: string = CONSOLE_ESC("30m") 349 | template CONSOLE_RED*: string = CONSOLE_ESC("31;1m") 350 | template CONSOLE_GREEN*: string = CONSOLE_ESC("32;1m") 351 | template CONSOLE_YELLOW*: string = CONSOLE_ESC("33;1m") 352 | template CONSOLE_BLUE*: string = CONSOLE_ESC("34;1m") 353 | template CONSOLE_MAGENTA*: string = CONSOLE_ESC("35;1m") 354 | template CONSOLE_CYAN*: string = CONSOLE_ESC("36;1m") 355 | template CONSOLE_WHITE*: string = CONSOLE_ESC("37;1m") 356 | 357 | # Styles 358 | template CONSOLE_COLOR_BOLD*: int = BIT(0).int 359 | template CONSOLE_COLOR_FAINT*: int = BIT(1).int 360 | template CONSOLE_ITALIC*: int = BIT(2).int 361 | template CONSOLE_UNDERLINE*: int = BIT(3).int 362 | template CONSOLE_BLINK_SLOW*: int = BIT(4).int 363 | template CONSOLE_BLINK_FAST*: int = BIT(5).int 364 | template CONSOLE_COLOR_REVERSE*: int = BIT(6).int 365 | template CONSOLE_CONCEAL*: int = BIT(7).int 366 | template CONSOLE_CROSSED_OUT*: int = BIT(8).int 367 | 368 | """ 369 | 370 | search.threedmoo = "debugDevice_3DMOO" 371 | replace.threedmoo = "debugDevice_3DMOO*" 372 | 373 | [gfx.nim] 374 | search.timport = "../types" 375 | replace.timport = "libnx/wrapper/types" 376 | 377 | search.fimport = "../nvidia/fence" 378 | replace.fimport = "libnx/wrapper/fence" 379 | 380 | [romfs_dev.nim] 381 | search.timport = "../types" 382 | replace.timport = "libnx/wrapper/types" 383 | search.servimport = "../services/" 384 | replace.servimport = "libnx/wrapper/" 385 | search.o = "../libnx" 386 | replace.o = "libnx" 387 | 388 | [n.sourcefile] 389 | "${output}/*.nim" 390 | -------------------------------------------------------------------------------- /src/libnx/account.nim: -------------------------------------------------------------------------------- 1 | import strutils 2 | import libnx/wrapper/acc 3 | import libnx/results 4 | import libnx/wrapper/types 5 | import libnx/ext/integer128 6 | import libnx/service 7 | import libnx/utils 8 | 9 | type 10 | AccountError* = object of Exception 11 | AccountInitError* = object of AccountError 12 | AccountImageSizeError* = object of AccountError 13 | AccountUserProfileError* = object of AccountError 14 | AccountUserCountError* = object of AccountError 15 | AccountUserListError* = object of AccountError 16 | AccountUserNotSelectedError* = object of AccountError 17 | AccountUserDataError* = object of AccountError 18 | AccountActiveUserError* = object of AccountError 19 | AccountImageLoadError* = object of AccountError 20 | 21 | User* = ref object 22 | id*: u128 23 | selected*: bool 24 | username*: string 25 | lastEdited*: uint64 26 | iconID*: uint32 27 | iconBgColorID*: uint8 28 | miiID*: array[0x10, uint8] 29 | 30 | Profile* = ref object 31 | service*: Service 32 | 33 | AccountImage* = ref object 34 | data: seq[uint8] 35 | imageSize: int 36 | 37 | var enabled = false 38 | 39 | 40 | proc getService*(): Service = 41 | let serv = accountGetService()[] 42 | result = newService(serv) 43 | 44 | 45 | proc init() = 46 | ## Initializes the account service. Should not 47 | ## be used in client code 48 | let service = getService() 49 | if service.isActive: 50 | return 51 | 52 | let code = accountInitialize().newResult 53 | if code.failed: 54 | raiseEx( 55 | AccountInitError, 56 | "Error, account api could not be initialized", code 57 | ) 58 | enabled = true 59 | 60 | 61 | proc exit() = 62 | ## Exits and closes the Account service 63 | let service = getService() 64 | if not service.isActive: 65 | return 66 | accountExit() 67 | enabled = false 68 | 69 | 70 | proc close*(profile: AccountProfile) = 71 | accountProfileClose(profile.unsafeAddr) 72 | 73 | 74 | template withAccountService*(code: untyped): typed = 75 | init() 76 | code 77 | exit() 78 | 79 | 80 | proc ensureEnabled() = 81 | if not enabled: 82 | raiseEx(AccountError, "Use withAccountService to access account functions.") 83 | 84 | 85 | proc getImageSize*(profile: AccountProfile): int = 86 | ensureEnabled() 87 | var res = 0.csize 88 | result = 0 89 | let code = accountProfileGetImageSize(profile.unsafeAddr, res.addr).newResult 90 | if code.failed: 91 | raiseEx(AccountImageSizeError, "Error, could not get image size", code) 92 | result = res.int 93 | 94 | 95 | proc imageSize*(user: User): int = 96 | ensureEnabled() 97 | var prof: AccountProfile 98 | var size: csize 99 | let res = accountProfileGetImageSize(prof.addr, size.addr).newResult 100 | 101 | if res.failed: 102 | raiseEx(AccountImageSizeError, "Error, could not get image size", res) 103 | 104 | result = size.int 105 | 106 | 107 | proc getProfileHelper(userID: u128): AccountProfile = 108 | let res = accountGetProfile(result.addr, userID).newResult 109 | 110 | if res.failed: 111 | raiseEx(AccountUserProfileError, "Error, could not get user profile", res) 112 | 113 | 114 | proc loadImage*(user: User): AccountImage = 115 | ensureEnabled() 116 | result = new(AccountImage) 117 | let imSize = user.imageSize() 118 | var size: csize 119 | 120 | var prof = getProfileHelper(user.id) 121 | 122 | result.data = newSeq[uint8](imSize) 123 | 124 | let res = accountProfileLoadImage( 125 | prof.addr, result.data[0].addr, 126 | imSize, result.imageSize.addr 127 | ).newResult 128 | 129 | if res.failed: 130 | prof.close() 131 | raiseEx(AccountImageLoadError, "Error, could not load image", res) 132 | 133 | prof.close() 134 | 135 | 136 | proc userID*(user: User): string = 137 | $user.id 138 | 139 | 140 | proc getUser*(userID: u128): User = 141 | ensureEnabled() 142 | result = new(User) 143 | 144 | result.id = userID 145 | result.selected = false 146 | 147 | var 148 | prof: AccountProfile = getProfileHelper(userID) 149 | userData: AccountUserData 150 | profBase: AccountProfileBase 151 | 152 | let res = accountProfileGet(prof.addr, userData.addr, profBase.addr).newResult 153 | 154 | if res.failed: 155 | raiseEx(AccountUserDataError, "Error, could not get user data", res) 156 | 157 | result.username = profBase.username.join("") 158 | result.lastEdited = profBase.lastEditTimestamp 159 | result.iconID = userData.iconID 160 | result.iconBgColorID = userData.iconBackgroundColorID 161 | result.miiID = userData.miiID 162 | 163 | prof.close() 164 | 165 | 166 | proc getActiveUser*(): User = 167 | ensureEnabled() 168 | result = new(User) 169 | var 170 | userID: u128 = newUInt128(0,0) 171 | selected: bool 172 | 173 | var res = accountGetActiveUser(userID.addr, selected.addr).newResult 174 | 175 | if res.failed: 176 | raiseEx(AccountActiveUserError, "Error, could not get active user ID", res) 177 | 178 | if not selected: 179 | raiseEx(AccountUserNotSelectedError, "No user currently selected!") 180 | 181 | result.id = userID 182 | result.selected = selected 183 | 184 | var 185 | prof: AccountProfile = getProfileHelper(userID) 186 | userData: AccountUserData 187 | profBase: AccountProfileBase 188 | 189 | res = accountProfileGet(prof.addr, userData.addr, profBase.addr).newResult 190 | 191 | if res.failed: 192 | raiseEx(AccountUserDataError, "Error, could not get user data", res) 193 | 194 | result.username = profBase.username.join("") 195 | result.lastEdited = profBase.lastEditTimestamp 196 | result.iconID = userData.iconID 197 | result.iconBgColorID = userData.iconBackgroundColorID 198 | result.miiID = userData.miiID 199 | 200 | prof.close() 201 | 202 | 203 | proc getProfile*(user: User): Profile = 204 | ensureEnabled() 205 | result = new(Profile) 206 | var prof: AccountProfile 207 | 208 | let res = accountGetProfile(prof.addr, user.id).newResult 209 | if res.failed: 210 | raiseEx( 211 | AccountUserProfileError, 212 | "Error, could not get account profile", res 213 | ) 214 | 215 | result.service = newService(prof.s) 216 | prof.close() 217 | 218 | 219 | proc getUserCount*(): int32 = 220 | ## Gets the number of users on the switch 221 | ensureEnabled() 222 | var count: int32 223 | let res = accountGetUserCount(count.addr).newResult 224 | if res.failed: 225 | raiseEx( 226 | AccountUserCountError, 227 | "Error, could not get user count", res 228 | ) 229 | result = count 230 | 231 | 232 | proc listAllUsers*(): seq[User] = 233 | ## Gets a list of all users currently on the switch 234 | ensureEnabled() 235 | result = @[] 236 | 237 | var 238 | userIDs: array[ACC_USER_LIST_SIZE, u128] 239 | usersReturned: csize 240 | 241 | let res = accountListAllUsers( 242 | userIDs[0].addr, 243 | ACC_USER_LIST_SIZE, 244 | usersReturned.addr 245 | ).newResult 246 | 247 | if res.failed: 248 | raiseEx( 249 | AccountUserListError, 250 | "Error, could not list users", res 251 | ) 252 | 253 | for i in 0 ..< usersReturned.int: 254 | result.add(getUser(userIDs[i])) 255 | -------------------------------------------------------------------------------- /src/libnx/app.nim: -------------------------------------------------------------------------------- 1 | import libnx/graphics, libnx/input, libnx/wrapper/applet 2 | 3 | template mainFunction*(code: untyped): untyped = 4 | proc main() = 5 | try: 6 | code 7 | except: 8 | let e = getCurrentException() 9 | echo "" 10 | echo e.getStackTrace() 11 | echo getCurrentExceptionMsg() 12 | echo "" 13 | echo "Your program has crashed. Press + on Controller 1 to exit safely." 14 | echo "" 15 | 16 | while appletMainLoop(): 17 | scanInput() 18 | 19 | let keysDown = keysDown(Controller.P1_AUTO) 20 | 21 | if ControllerKey.Plus in keysDown: 22 | break 23 | 24 | flushBuffers() 25 | swapBuffers() 26 | waitForVSync() 27 | try: 28 | graphics.exit() 29 | quit(0) 30 | except: 31 | quit(0) 32 | 33 | main() 34 | 35 | template mainLoop*(code: untyped): untyped = 36 | while appletMainLoop(): 37 | scanInput() 38 | 39 | code 40 | 41 | flushBuffers() 42 | swapBuffers() 43 | waitForVSync() 44 | 45 | graphics.exit() 46 | -------------------------------------------------------------------------------- /src/libnx/console.nim: -------------------------------------------------------------------------------- 1 | import macros, strutils, sets 2 | import libnx/wrapper/cons 3 | import libnx/utils 4 | 5 | 6 | type 7 | PrintCallback* = proc (con: Console; c: char): bool 8 | 9 | Font* = ref ConsoleFont 10 | 11 | Style* {.pure.} = enum 12 | Bold = CONSOLE_COLOR_BOLD(), 13 | Faint = CONSOLE_COLOR_FAINT(), 14 | Italic = CONSOLE_ITALIC(), 15 | Underline = CONSOLE_UNDERLINE(), 16 | BlinkSlow = CONSOLE_BLINK_SLOW(), 17 | BlinkFast = CONSOLE_BLINK_FAST(), 18 | ColorReverse = CONSOLE_COLOR_REVERSE(), 19 | Conceal = CONSOLE_CONCEAL(), 20 | CrossedOut = CONSOLE_CROSSED_OUT() 21 | 22 | Console* = ref object 23 | pcon: ptr PrintConsole 24 | font*: ConsoleFont 25 | frameBuffer*: ptr UncheckedArray[uint32] 26 | frameBuffer2*: ptr UncheckedArray[uint32] 27 | cursorX*: int 28 | cursorY*: int 29 | prevCursorX*: int 30 | prevCursorY*: int 31 | width*: int 32 | height*: int 33 | windowX*: int 34 | windowY*: int 35 | windowWidth*: int 36 | windowHeight*: int 37 | tabSize*: int 38 | fg*: int 39 | bg*: int 40 | flags*: HashSet[Style] 41 | printCharCallback*: PrintCallback 42 | initialised*: bool 43 | 44 | DebugDevice* {.size: sizeof(cint), pure.} = enum 45 | Null, Service, Console, ThreeDMOO 46 | 47 | var currentConsole {.threadvar.}: Console 48 | 49 | proc toConsole(pconsole: ptr PrintConsole): Console = 50 | result = new(Console) 51 | result.pcon = pconsole 52 | result.font = pconsole.font 53 | result.frameBuffer = cast[ptr UncheckedArray[uint32]](pconsole.frameBuffer) 54 | result.frameBuffer2 = cast[ptr UncheckedArray[uint32]](pconsole.frameBuffer2) 55 | result.cursorX = pconsole.cursorX 56 | result.cursorY = pconsole.cursorY 57 | result.prevCursorX = pconsole.prevCursorX 58 | result.prevCursorY = pconsole.prevCursorY 59 | result.width = pconsole.consoleWidth 60 | result.height = pconsole.consoleHeight 61 | result.windowX = pconsole.windowX 62 | result.windowY = pconsole.windowY 63 | result.windowWidth = pconsole.windowWidth 64 | result.windowHeight = pconsole.windowHeight 65 | result.tabSize = pconsole.tabSize 66 | result.fg = pconsole.fg 67 | result.bg = pconsole.bg 68 | result.flags = initSet[Style]() 69 | 70 | result.pcon.PrintChar = proc (con: pointer, c: cint): bool {.cdecl.} = 71 | let console = cast[ptr PrintConsole](con).toConsole() 72 | if not console.printCharCallback.isNil: 73 | return console.printCharCallback(console, c.char) 74 | 75 | result.initialised = pconsole.consoleInitialised 76 | 77 | proc setFont*(console: Console; font: Font) = 78 | var f = font 79 | consoleSetFont(console.pcon, f[].addr) 80 | console.font = console.pcon.font 81 | 82 | proc setWindow*(console: Console, x, y, width, height: int) = 83 | console.pcon.consoleSetWindow(x.cint, y.cint, width.cint, height.cint) 84 | console.width = console.pcon.consoleWidth 85 | console.height = console.pcon.consoleHeight 86 | console.windowX = console.pcon.windowX.int 87 | console.windowY = console.pcon.windowY.int 88 | 89 | proc getDefault*(): Console = 90 | ## Gets the default console with default values 91 | let pcon = consoleGetDefault() 92 | result = pcon.toConsole() 93 | 94 | proc select*(console: Console): Console = 95 | currentConsole = consoleInit(console.pcon).toConsole() 96 | return currentConsole 97 | 98 | proc init*(console: Console): Console {.discardable.} = 99 | currentConsole = consoleInit(console.pcon).toConsole() 100 | return currentConsole 101 | 102 | proc init*(): Console {.discardable.} = 103 | currentConsole = consoleInit(nil).toConsole() 104 | return currentConsole 105 | 106 | proc debugInit*(device: DebugDevice) = 107 | if device == ThreeDMOO: 108 | consoleDebugInit(debugDevice_3DMOO) 109 | else: 110 | consoleDebugInit(debugDevice(device)) 111 | 112 | proc clear*() = 113 | consoleClear() 114 | 115 | proc printAt*(pos: tuple[row: int, col: int], args: varargs[string, `$`]) = 116 | echo CONSOLE_ESC("2K"), (CONSOLE_ESC("$#;$#H") % [$pos.row, $pos.col]), args.join("") 117 | currentConsole.pcon.cursorX = pos.col.int32 118 | currentConsole.pcon.cursorY = pos.row.int32 119 | currentConsole.cursorX = pos.col 120 | currentConsole.cursorY = pos.row 121 | 122 | proc print*(args: varargs[string, `$`]) = 123 | ## Will print at the previously set printPos using ``printAt`` 124 | ## and then increment the row by one 125 | let 126 | pcon = currentConsole.pcon 127 | printPos = (pcon.cursorY.int+1, pcon.cursorX.int) 128 | printAt printPos, args 129 | -------------------------------------------------------------------------------- /src/libnx/ext/integer128.nim: -------------------------------------------------------------------------------- 1 | import strutils 2 | # Capacity of 42 because it seems 3 | # reasonable that the number will fit. 4 | # If it doesn't, nim will just allocate 5 | # more memory for us anyway 6 | const STRING_CAPACITY = 42 7 | 8 | type 9 | s128* {.importc: "__int128_t"} = object 10 | high: int64 11 | low: uint64 12 | 13 | u128* {.importc: "__uint128_t"} = object 14 | high, low: uint64 15 | 16 | helperInt128 = object 17 | hi: int64 18 | lo: uint64 19 | 20 | helperUInt128 = object 21 | hi: uint64 22 | lo: uint64 23 | 24 | proc toHelper(val: s128): helperInt128 = 25 | (cast[ptr helperInt128](unsafeAddr val))[] 26 | 27 | proc lo*(val: s128): uint64 = 28 | val.toHelper().lo 29 | 30 | proc hi*(val: s128): int64 = 31 | val.toHelper().hi 32 | 33 | proc toInt128(val: helperInt128): s128 = 34 | (cast[ptr s128](unsafeAddr val))[] 35 | 36 | proc toHelper(val: u128): helperUInt128 = 37 | (cast[ptr helperUInt128](unsafeAddr val))[] 38 | 39 | proc lo*(val: u128): uint64 = 40 | val.toHelper().lo 41 | 42 | proc hi*(val: u128): uint64 = 43 | val.toHelper().hi 44 | 45 | proc toUInt128(val: helperUInt128): u128 = 46 | (cast[ptr u128](unsafeAddr val))[] 47 | 48 | proc newInt128*(hi: int64, lo: uint64): s128 = 49 | let r = helperInt128(hi: hi, lo: lo) 50 | return r.toInt128() 51 | 52 | proc newUInt128*(hi: uint64, lo: uint64): u128 = 53 | let r = helperUInt128(hi: hi, lo: lo) 54 | return r.toUInt128() 55 | 56 | proc toUInt128(val: s128): u128 = 57 | newUInt128(val.hi.uint64, val.lo) 58 | 59 | converter intToInt128*(val: int): s128 = 60 | newInt128(0, val.uint64) 61 | 62 | converter intToUInt128*(val: int): u128 = 63 | newUInt128(0, val.uint64) 64 | 65 | proc `<`*(val1: u128, val2: u128): bool = 66 | let lhs = val1.toHelper() 67 | let rhs = val2.toHelper() 68 | 69 | if (lhs.hi < rhs.hi): 70 | return true 71 | if (rhs.hi < lhs.hi): 72 | return false 73 | if (lhs.lo < rhs.lo): 74 | return true 75 | return false 76 | 77 | proc `<`*(val1: s128, val2: s128): bool = 78 | let lhs = val1.toHelper() 79 | let rhs = val2.toHelper() 80 | 81 | if (lhs.hi < rhs.hi): 82 | return true 83 | if (rhs.hi < lhs.hi): 84 | return false 85 | if (lhs.lo < rhs.lo): 86 | return true 87 | return false 88 | 89 | proc `>=`*(val1: s128, val2: s128): bool = 90 | let lhs = val1.toHelper() 91 | let rhs = val2.toHelper() 92 | 93 | if lhs.hi == rhs.hi and lhs.lo == rhs.lo: 94 | return true 95 | 96 | if lhs.hi > rhs.hi: 97 | return true 98 | if rhs.hi > lhs.hi: 99 | return false 100 | 101 | if lhs.lo > rhs.lo: 102 | return true 103 | return false 104 | 105 | proc `>=`*(val1: u128, val2: u128): bool = 106 | let lhs = val1.toHelper() 107 | let rhs = val2.toHelper() 108 | 109 | if lhs.hi == rhs.hi and lhs.lo == rhs.lo: 110 | return true 111 | 112 | if lhs.hi > rhs.hi: 113 | return true 114 | if rhs.hi > lhs.hi: 115 | return false 116 | 117 | if lhs.lo > rhs.lo: 118 | return true 119 | return false 120 | 121 | proc `>`*(val1: s128, val2: s128): bool = 122 | let lhs = val1.toHelper() 123 | let rhs = val2.toHelper() 124 | 125 | if (lhs.hi < rhs.hi): 126 | return true 127 | if (rhs.hi < lhs.hi): 128 | return false 129 | if (lhs.lo < rhs.lo): 130 | return true 131 | return false 132 | 133 | proc `>`*(val1: u128, val2: u128): bool = 134 | let lhs = val1.toHelper() 135 | let rhs = val2.toHelper() 136 | 137 | if (lhs.hi < rhs.hi): 138 | return true 139 | if (rhs.hi < lhs.hi): 140 | return false 141 | if (lhs.lo < rhs.lo): 142 | return true 143 | return false 144 | 145 | proc `<=`*(val1: s128, val2: s128): bool = 146 | let lhs = val1.toHelper() 147 | let rhs = val2.toHelper() 148 | 149 | if lhs.hi == rhs.hi and lhs.lo == rhs.lo: 150 | return true 151 | 152 | if lhs.hi < rhs.hi: 153 | return true 154 | if rhs.hi < lhs.hi: 155 | return false 156 | if lhs.lo < rhs.lo: 157 | return true 158 | return false 159 | 160 | proc `<=`*(val1: u128, val2: u128): bool = 161 | let lhs = val1.toHelper() 162 | let rhs = val2.toHelper() 163 | 164 | if lhs.hi == rhs.hi and lhs.lo == rhs.lo: 165 | return true 166 | 167 | if lhs.hi < rhs.hi: 168 | return true 169 | if rhs.hi < lhs.hi: 170 | return false 171 | if lhs.lo < rhs.lo: 172 | return true 173 | return false 174 | 175 | proc `!=`*(val1: s128, val2: s128): bool = 176 | let lhs = val1.toHelper() 177 | let rhs = val2.toHelper() 178 | return (lhs.hi != rhs.hi) or (lhs.lo != rhs.lo) 179 | 180 | proc `!=`*(val1: u128, val2: u128): bool = 181 | let lhs = val1.toHelper() 182 | let rhs = val2.toHelper() 183 | return (lhs.hi != rhs.hi) or (lhs.lo != rhs.lo) 184 | 185 | proc `==`*(val1: s128, val2: s128): bool = 186 | let lhs = val1.tohelper() 187 | let rhs = val2.tohelper() 188 | if lhs.hi != rhs.hi: 189 | return false 190 | if lhs.lo != rhs.lo: 191 | return false 192 | return true 193 | 194 | proc `==`*(val1: u128, val2: u128): bool = 195 | let lhs = val1.tohelper() 196 | let rhs = val2.tohelper() 197 | if lhs.hi != rhs.hi: 198 | return false 199 | if lhs.lo != rhs.lo: 200 | return false 201 | return true 202 | 203 | proc `+`*(val1, val2: s128): s128 = 204 | let lhs = val1.toHelper() 205 | let rhs = val2.toHelper() 206 | 207 | var res = result.toHelper() 208 | 209 | res.hi = lhs.hi + rhs.hi 210 | 211 | res.lo = lhs.lo + rhs.lo 212 | if res.lo < rhs.lo: 213 | res.hi.inc() 214 | result = res.toInt128() 215 | 216 | proc `+`*(val1, val2: u128): u128 = 217 | let lhs = val1.toHelper() 218 | let rhs = val2.toHelper() 219 | 220 | var res = result.toHelper() 221 | 222 | res.hi = lhs.hi + rhs.hi 223 | 224 | res.lo = lhs.lo + rhs.lo 225 | if res.lo < rhs.lo: 226 | res.hi.inc() 227 | result = res.toUInt128() 228 | 229 | proc `-`*(val: s128): s128 = 230 | var res = val.toHelper() 231 | res.hi = not res.hi 232 | res.lo = not res.lo 233 | res.lo += 1 234 | if res.lo == 0: 235 | res.hi += 1 236 | 237 | result = res.toInt128() 238 | 239 | proc `-`*(val: u128): s128 = 240 | var res = newInt128(val.hi.int64, val.lo).toHelper() 241 | 242 | res.hi = not res.hi 243 | res.lo = not res.lo 244 | res.lo += 1 245 | if res.lo == 0: 246 | res.hi += 1 247 | 248 | result = res.toInt128() 249 | 250 | proc `-`*(val1, val2: s128): s128 = 251 | let lhs = val1.toHelper() 252 | let rhs = val2.toHelper() 253 | 254 | var res = result.toHelper() 255 | 256 | res.hi = lhs.hi - rhs.hi 257 | res.lo = lhs.lo - rhs.lo 258 | if res.lo > lhs.lo: 259 | res.hi.dec() 260 | 261 | result = res.toInt128() 262 | 263 | proc `-`*(val1, val2: u128): s128 = 264 | let lhs = val1.toHelper() 265 | let rhs = val2.toHelper() 266 | 267 | var res = result.toHelper() 268 | 269 | res.hi = (lhs.hi - rhs.hi).int64 270 | res.lo = lhs.lo - rhs.lo 271 | if res.lo > lhs.lo: 272 | res.hi.dec() 273 | 274 | result = res.toInt128() 275 | 276 | proc `$`*(val: s128): string = 277 | var v = val 278 | 279 | if v == 0: 280 | return "0" 281 | 282 | var str = newStringOfCap(STRING_CAPACITY) 283 | 284 | var 285 | p10 = 1.s128 286 | temp: s128 287 | num_digits = 0 288 | going = 1 289 | digit = 0 290 | 291 | if v < 0: 292 | str.add('-') 293 | v = -v 294 | 295 | while (p10 <= v and going > 0): 296 | p10 = p10 + p10 297 | if p10 < 0: 298 | going = 0 299 | temp = p10 300 | p10 = p10 + p10 301 | if p10 < 0: 302 | going = 0 303 | p10 = p10 + p10 304 | if p10 < 0: 305 | going = 0 306 | p10 = p10 + temp 307 | if p10 < 0: 308 | going = 0 309 | num_digits.inc 310 | 311 | while num_digits > 0: 312 | num_digits -= 1 313 | p10 = 1 314 | for i in 0..= p10): 323 | v = v - p10 324 | digit.inc 325 | str.add(char(48 + digit)) 326 | 327 | result = str 328 | 329 | proc toHex*(val: u128): string = 330 | result = val.hi.toHex & val.lo.toHex 331 | 332 | proc `$`*(val: u128): string = 333 | var v = val 334 | 335 | if v == 0: 336 | return "0" 337 | 338 | var str = newStringOfCap(STRING_CAPACITY) 339 | 340 | var 341 | p10 = 1.u128 342 | temp: u128 343 | num_digits = 0 344 | going = 1 345 | digit = 0 346 | 347 | while (p10 <= v and going > 0): 348 | p10 = p10 + p10 349 | if p10 < 0: 350 | going = 0 351 | temp = p10 352 | p10 = p10 + p10 353 | if p10 < 0: 354 | going = 0 355 | p10 = p10 + p10 356 | if p10 < 0: 357 | going = 0 358 | p10 = p10 + temp 359 | if p10 < 0: 360 | going = 0 361 | num_digits.inc 362 | 363 | while num_digits > 0: 364 | num_digits -= 1 365 | p10 = 1 366 | for i in 0..= p10): 375 | v = (v - p10).toUInt128() 376 | digit.inc 377 | str.add(char(48 + digit)) 378 | 379 | result = str 380 | -------------------------------------------------------------------------------- /src/libnx/graphics.nim: -------------------------------------------------------------------------------- 1 | import strutils 2 | import 3 | libnx/wrapper/types, 4 | libnx/wrapper/gfx, 5 | libnx/results, 6 | libnx/utils 7 | 8 | type 9 | GraphicsError* = object of Exception 10 | GraphicsInitError* = object of GraphicsError 11 | InitResolutionError* = object of GraphicsError 12 | CropBoundsError* = object of GraphicsError 13 | 14 | RGBA8* = ref object 15 | red*: int 16 | green*: int 17 | blue*: int 18 | alpha*: int 19 | 20 | Framebuffer* = ref object 21 | width*: uint32 22 | height*: uint32 23 | data*: Buffer[uint8] 24 | 25 | BufferTransform* {.pure, size: sizeof(cint).} = enum 26 | FlipHorizontal = 0x1 27 | FlipVertical = 0x2 28 | Rotate180 = 0x3 29 | Rotate90 = 0x4 30 | Rotate270 = 0x7 31 | 32 | ## / Converts red, green, blue, and alpha components to packed RGBA8. 33 | proc packed*(rgba: RGBA8): int = 34 | gfx.RGBA8(rgba.red, rgba.green, rgba.blue, rgba.alpha) 35 | 36 | ## / Same as \ref RGBA8 except with alpha=0xff. 37 | proc maxAlpha*(r, g, b: int): RGBA8 = 38 | RGBA8(red:r, green:g, blue:b, alpha:0xFF) 39 | 40 | ## / GfxMode set by \ref gfxSetMode. The default is GfxMode_LinearDouble. Note that the 41 | ## text-console (see console.h) sets this to GfxMode_TiledDouble. 42 | type 43 | GfxMode* {.size: sizeof(cint), pure.} = enum 44 | TiledSingle, ## /< Single-buffering with raw tiled (block-linear) framebuffer. 45 | TiledDouble, ## /< Double-buffering with raw tiled (block-linear) framebuffer. 46 | LinearDouble ## /< Double-buffering with linear framebuffer, which is 47 | ## transferred to the actual framebuffer by \ref gfxFlushBuffers(). 48 | 49 | var enabled = false 50 | 51 | ## / Framebuffer pixel-format is RGBA8888, there's no known way to change this. 52 | ## * 53 | ## @brief Initializes the graphics subsystem. 54 | ## @warning Do not use \ref viInitialize when using this function. 55 | ## 56 | proc initDefault*() = 57 | if not enabled: 58 | let code = gfxInitDefault().newResult 59 | if code.failed: 60 | raiseEx( 61 | GraphicsInitError, 62 | "Error, graphics could not be initialized", code 63 | ) 64 | enabled = true 65 | 66 | ## * 67 | ## @brief Uninitializes the graphics subsystem. 68 | ## @warning Do not use \ref viExit when using this function. 69 | ## 70 | proc exit*() = 71 | # XXX Important!!! This deallocHeap call must be here 72 | # in order for the switch not to crash. It must be run right 73 | # before gfxExit() to leave a clean slate in the OS. This call 74 | # will disable any further allocations and free all consumed 75 | # memory on the heap. 76 | if enabled: 77 | deallocHeap(runFinalizers = true, allowGcAfterwards = false) 78 | gfxExit() 79 | enabled = false 80 | 81 | ## / Get the framebuffer width/height without crop. 82 | proc getFramebufferResolution*(): tuple[width: uint32, height: uint32] = 83 | var 84 | width: uint32 85 | height: uint32 86 | gfxGetFramebufferResolution(width.addr, height.addr) 87 | return (width: width, height: height) 88 | 89 | ## * 90 | ## @brief Sets the resolution to be used when initializing the graphics subsystem. 91 | ## @param[in] width Horizontal resolution, in pixels. 92 | ## @param[in] height Vertical resolution, in pixels. 93 | ## @note The default resolution is 720p. 94 | ## @note This can only be used before calling \ref gfxInitDefault, this will use \ref 95 | ## fatalSimple otherwise. If the input is 0, the default resolution will be used during 96 | ## \ref gfxInitDefault. This sets the maximum resolution for the framebuffer, used 97 | ## during \ref gfxInitDefault. This is also used as the current resolution when crop 98 | ## isn't set. The width/height are reset to the default when \ref gfxExit is used. 99 | ## @note Normally you should only use this when you need a maximum resolution larger 100 | ## than the default, see above. 101 | ## @note The width and height are aligned to 4. 102 | ## 103 | proc initResolution*(width: uint32; height: uint32) = 104 | if not enabled: 105 | gfxInitResolution(width, height) 106 | else: 107 | raiseEx(InitResolutionError, "Cannot init resolution after graphics.initDefault!") 108 | 109 | ## / Wrapper for \ref gfxInitResolution with resolution=1080p. Use this if you want to 110 | ## support 1080p or >720p in docked-mode. 111 | proc initResolution1080p*() = 112 | if not enabled: 113 | gfxInitResolutionDefault() 114 | else: 115 | raiseEx(InitResolutionError, "Cannot init resolution after graphics.initDefault!") 116 | 117 | proc cropBoundsValid(left, top, right, bottom: uint32, width, height: uint32): bool = 118 | 119 | if left < right and top > bottom: 120 | if left <= width and right <= width: 121 | if top <= height and bottom <= height: 122 | return true 123 | return false 124 | 125 | ## / Configure framebuffer crop, by default crop is all-zero. Use all-zero input to 126 | ## reset to default. \ref gfxExit resets this to the default. 127 | ## / When the input is invalid this returns without changing the crop data, this 128 | ## includes the input values being larger than the framebuf width/height. 129 | ## / This will update the display width/height returned by \ref gfxGetFramebuffer, with 130 | ## that width/height being reset to the default when required. 131 | ## / \ref gfxGetFramebufferDisplayOffset uses absolute x/y, it will not adjust for 132 | ## non-zero crop left/top. 133 | ## / The new crop config will not take affect with double-buffering disabled. When used 134 | ## during frame-drawing, this should be called before \ref gfxGetFramebuffer. 135 | ## / The right and bottom params are aligned to 4. 136 | proc setCrop*(left, top, right, bottom: uint32) = 137 | let (width, height) = getFramebufferResolution() 138 | if not cropBoundsValid(left, top, right, bottom, width, height): 139 | raiseEx( 140 | CropBoundsError, 141 | "The crop bounds are outside the frame buffer limits of w: $#, h:$#" % 142 | [$width, $height] 143 | ) 144 | gfxConfigureCrop(left.s32, top.s32, right.s32, bottom.s32) 145 | 146 | proc resetCrop*() = 147 | setCrop(0, 0, 0, 0) 148 | 149 | ## / Wrapper for \ref gfxConfigureCrop. Use this to set the resolution, within the 150 | ## bounds of the maximum resolution. Use all-zero input to reset to default. 151 | proc setCropResolution*(width, height: uint32) = 152 | setCrop(0, 0, width, height) 153 | 154 | ## / If enabled, \ref gfxConfigureResolution will be used with the input resolution for 155 | ## the current OperationMode. Then \ref gfxConfigureResolution will automatically be 156 | ## used with the specified resolution each time OperationMode changes. 157 | proc setAutoResolution*(enable: bool, handheldWidth, 158 | handheldHeight, dockedWidth, 159 | dockedHeight: uint32) = 160 | gfxConfigureAutoResolution( 161 | enable, handheldWidth.s32, handheldHeight.s32, dockedWidth.s32, dockedHeight.s32 162 | ) 163 | 164 | ## / Wrapper for \ref gfxConfigureAutoResolution. handheld_resolution=720p, 165 | ## docked_resolution={all-zero for using current maximum resolution}. 166 | proc setAutoResolutionDefault*(enable: bool) = 167 | gfxConfigureAutoResolutionDefault(enable) 168 | 169 | ## / Waits for vertical sync. 170 | proc waitForVsync*() = gfxWaitForVSync() 171 | 172 | ## / Swaps the framebuffers (for double-buffering). 173 | proc swapBuffers*() = gfxSwapBuffers() 174 | 175 | ## / Use this to get the actual byte-size of the framebuffer for use with memset/etc. 176 | proc getFramebufferSize*(): int = gfxGetFramebufferSize().int 177 | 178 | ## / Get the current framebuffer address, with optional output ptrs for the display 179 | ## framebuffer width/height. The display width/height is adjusted by \ref 180 | ## gfxConfigureCrop and \ref gfxConfigureResolution. 181 | proc getFramebuffer*(): Framebuffer = 182 | result = new(Framebuffer) 183 | let size = getFramebufferSize() 184 | let frameBuf = gfxGetFrameBuffer( 185 | result.width.addr, 186 | result.height.addr 187 | ) 188 | let arr = cast[ptr UncheckedArray[uint8]](frameBuf) 189 | result.data = Buffer[uint8](len: size, data: arr) 190 | 191 | ## / Sets the \ref GfxMode. 192 | proc setMode*(mode: GfxMode) = gfxSetMode(gfx.GfxMode(mode)) 193 | 194 | ## / Configures transform. See the NATIVE_WINDOW_TRANSFORM_* enums in buffer_producer.h. 195 | ## The default is NATIVE_WINDOW_TRANSFORM_FLIP_V. 196 | proc configureTransform*(transform: BufferTransform) = 197 | gfxConfigureTransform(transform.uint32) 198 | 199 | ## / Flushes the framebuffer in the data cache. When \ref GfxMode is 200 | ## GfxMode_LinearDouble, this also transfers the linear-framebuffer to the actual 201 | ## framebuffer. 202 | proc flushBuffers*() = gfxFlushBuffers() 203 | 204 | ## / Use this to get the pixel-offset in the framebuffer. Returned value is in pixels, 205 | ## not bytes. 206 | ## / This implements tegra blocklinear, with hard-coded constants etc. 207 | ## / Do not use this when \ref GfxMode is GfxMode_LinearDouble. 208 | proc getFramebufferDisplayOffset*(x: uint32; y: uint32): uint32 = 209 | gfxGetFrameBufferDisplayOffset(x, y) 210 | -------------------------------------------------------------------------------- /src/libnx/input.nim: -------------------------------------------------------------------------------- 1 | import macros, strutils, sets 2 | import libnx/wrapper/hid 3 | from libnx/wrapper/types import BIT 4 | import libnx/utils 5 | import libnx/results 6 | import libnx/service 7 | import libnx/results 8 | 9 | niceify(HidMouseButton, "Hid:MOUSE_") 10 | niceify(HidKeyboardModifier, "Hid:KBD_MOD_") 11 | niceify(HidKeyboardScancode, "HidKeyboardScancode:KeyboardKey:KBD_:") 12 | niceify(HidControllerType, "Hid:TYPE_") 13 | niceify(HidControllerLayoutType, "Hid:LAYOUT_") 14 | niceify(HidControllerColorDescription, "Hid:COLORS_") 15 | niceify(HidControllerKeys, "HidControllerKeys:ControllerKey:KEY_:") 16 | niceify(HidControllerJoystick, "Hid:JOYSTICK_") 17 | niceify(HidControllerID, "HidControllerID:Controller:CONTROLLER_:") 18 | 19 | export TouchPosition, JoystickPosition, MousePosition 20 | export JOYSTICK_MAX, JOYSTICK_MIN 21 | 22 | type 23 | InputError* = object of Exception 24 | ControllerSelectError* = object of InputError 25 | VibrationError* = object of InputError 26 | VibrationInitError* = object of InputError 27 | ControllerMergeError* = object of InputError 28 | 29 | VibrationDeviceInfo* = HidVibrationDeviceInfo 30 | 31 | VibrationValue* = HidVibrationValue 32 | 33 | VibrationDevice* = uint32 34 | 35 | JoyconMode {.pure.} = enum 36 | Single, Dual 37 | 38 | proc init*(): Result = hidInitialize().newResult 39 | proc exit*() = hidExit() 40 | proc reset*() = hidReset() 41 | 42 | 43 | proc getSessionService*(): Service = 44 | newService(hidGetSessionService()[]) 45 | 46 | 47 | proc getSharedMemory*(): ptr HidSharedMemory = 48 | result = cast[ptr HidSharedMemory](hidGetSharedMemAddr()) 49 | 50 | 51 | proc `layout=`*(controller: Controller, layoutType: ControllerLayoutType) = 52 | hidSetControllerLayout( 53 | HidControllerID(controller), 54 | HidControllerLayoutType(layoutType) 55 | ) 56 | 57 | 58 | proc layout*(controller: Controller): ControllerLayoutType = 59 | hidGetControllerLayout(HidControllerID(controller)).ControllerLayoutType 60 | 61 | 62 | proc scanInput*() = 63 | ## Call this once per frame to make the rest of the procs 64 | ## in here poll controller data 65 | hidScanInput() 66 | 67 | 68 | proc keysHeld*(controller: Controller): HashSet[ControllerKey] = 69 | ## Gets the keys held by the `controller` 70 | result = initSet[ControllerKey]() 71 | 72 | var raw = hidKeysHeld(HidControllerID(controller)) 73 | for i in 0 ..< ControllerKey.size: 74 | let bit = raw and 0x1 75 | if bit == 1: 76 | result.incl(ControllerKey(BIT(i))) 77 | raw = raw shr 1 78 | 79 | 80 | proc keysDown*(controller: Controller): HashSet[ControllerKey] = 81 | ## Gets the keys that are pressed at the moment by the `controller` 82 | result = initSet[ControllerKey]() 83 | 84 | var raw = hidKeysDown(HidControllerID(controller)) 85 | for i in 0 ..< ControllerKey.size: 86 | let bit = raw and 0x1 87 | if bit == 1: 88 | result.incl(ControllerKey(BIT(i))) 89 | raw = raw shr 1 90 | 91 | 92 | proc keysUp*(controller: Controller): HashSet[ControllerKey] = 93 | ## Gets the keys that are not pressed at the moment by the `controller` 94 | result = initSet[ControllerKey]() 95 | 96 | var raw = hidKeysUp(HidControllerID(controller)) 97 | for i in 0 ..< ControllerKey.size: 98 | let bit = raw and 0x1 99 | if bit == 1: 100 | result.incl(ControllerKey(BIT(i))) 101 | raw = raw shr 1 102 | 103 | 104 | proc mouseButtonsHeld*(): HashSet[MouseButton] = 105 | result = initSet[MouseButton]() 106 | 107 | var raw = hidMouseButtonsHeld() 108 | for i in 0 ..< MouseButton.size: 109 | let bit = raw and 0x1 110 | if bit == 1: 111 | result.incl(MouseButton(BIT(i))) 112 | raw = raw shr 1 113 | 114 | 115 | proc mouseButtonsDown*(): HashSet[MouseButton] = 116 | result = initSet[MouseButton]() 117 | 118 | var raw = hidMouseButtonsDown() 119 | for i in 0 ..< MouseButton.size: 120 | let bit = raw and 0x1 121 | if bit == 1: 122 | result.incl(MouseButton(BIT(i))) 123 | raw = raw shr 1 124 | 125 | proc mouseButtonsUp*(): HashSet[MouseButton] = 126 | result = initSet[MouseButton]() 127 | 128 | var raw = hidMouseButtonsUp() 129 | for i in 0 ..< MouseButton.size: 130 | let bit = raw and 0x1 131 | if bit == 1: 132 | result.incl(MouseButton(BIT(i))) 133 | raw = raw shr 1 134 | 135 | 136 | proc readMouse*(): MousePosition = 137 | hidMouseRead(result.addr) 138 | 139 | 140 | proc keyboardModifierHeld*(modifier: KeyboardModifier): bool = 141 | hidKeyboardModifierHeld(modifier.HidKeyboardModifier) 142 | 143 | 144 | proc keyboardModifierDown*(modifier: KeyboardModifier): bool = 145 | hidKeyboardModifierDown(modifier.HidKeyboardModifier) 146 | 147 | 148 | proc keyboardModifierUp*(modifier: KeyboardModifier): bool = 149 | hidKeyboardModifierUp(modifier.HidKeyboardModifier) 150 | 151 | 152 | proc keyboardHeld*(key: KeyboardKey): bool = 153 | hidKeyboardHeld(key.HidKeyboardScancode) 154 | 155 | 156 | proc keyboardDown*(key: KeyboardKey): bool = 157 | hidKeyboardDown(key.HidKeyboardScancode) 158 | 159 | 160 | proc keyboardUp*(key: KeyboardKey): bool = 161 | hidKeyboardUp(key.HidKeyboardScancode) 162 | 163 | 164 | proc keyboardKeysDown*(): HashSet[KeyboardKey] = 165 | ## Get all keyboard keys currently down, excluding modifiers 166 | result = initSet[KeyboardKey]() 167 | 168 | for i in KeyboardKey.low..KeyboardKey.high: 169 | let key = KeyboardKey(i) 170 | if keyboardDown(key): 171 | result.incl(key) 172 | 173 | 174 | proc keyboardKeysUp*(): HashSet[KeyboardKey] = 175 | ## Get all keyboard keys currently up, excluding modifiers 176 | result = initSet[KeyboardKey]() 177 | 178 | for i in KeyboardKey.low..KeyboardKey.high: 179 | let key = KeyboardKey(i) 180 | if keyboardUp(key): 181 | result.incl(key) 182 | 183 | 184 | proc keyboardKeysHeld*(): HashSet[KeyboardKey] = 185 | ## Get all keyboard keys currently held, excluding modifiers 186 | result = initSet[KeyboardKey]() 187 | 188 | for i in KeyboardKey.low..KeyboardKey.high: 189 | let key = KeyboardKey(i) 190 | if keyboardHeld(key): 191 | result.incl(key) 192 | 193 | 194 | proc keyboardModifiersHeld*(): HashSet[KeyboardModifier] = 195 | ## Get all keyboard modifiers currently held 196 | result = initSet[KeyboardModifier]() 197 | 198 | for i in 0..KeyboardModifier.size: 199 | let key = KeyboardModifier(BIT(i)) 200 | if keyboardModifierHeld(key): 201 | result.incl(key) 202 | 203 | 204 | proc keyboardModifiersDown*(): HashSet[KeyboardModifier] = 205 | ## Get all keyboard modifiers currently down 206 | result = initSet[KeyboardModifier]() 207 | 208 | for i in 0..KeyboardModifier.size: 209 | let key = KeyboardModifier(BIT(i)) 210 | if keyboardModifierDown(key): 211 | result.incl(key) 212 | 213 | proc keyboardModifiersUp*(): HashSet[KeyboardModifier] = 214 | ## Get all keyboard modifiers currently up 215 | result = initSet[KeyboardModifier]() 216 | 217 | for i in 0..KeyboardModifier.size: 218 | let key = KeyboardModifier(BIT(i)) 219 | if keyboardModifierUp(key): 220 | result.incl(key) 221 | 222 | 223 | proc touchCount*(): uint32 = 224 | hidTouchCount() 225 | 226 | 227 | proc readTouch*(pointId: uint32): TouchPosition = 228 | hidTouchRead(result.addr, pointId) 229 | 230 | 231 | proc readJoystick*(id: Controller, stick: ControllerJoystick): JoystickPosition = 232 | hidJoystickRead(result.addr, id.HidControllerID, stick.HidControllerJoystick) 233 | 234 | 235 | proc isHandheldMode*(): bool = 236 | ## / This can be used to check what CONTROLLER_P1_AUTO uses. 237 | ## / Returns 0 when CONTROLLER_PLAYER_1 is connected, otherwise returns 1 for 238 | ## handheld-mode. 239 | hidGetHandheldMode() 240 | 241 | 242 | proc `joyconMode=`*(id: Controller, mode: JoyconMode) = 243 | ## Sets the joycon mode to either single or dual 244 | if id notin {Controller.Player1 .. Controller.Player8}: 245 | raiseEx(ControllerSelectError, "Must be controller 1-8") 246 | 247 | var rc: Result 248 | 249 | case mode: 250 | of JoyconMode.Single: 251 | rc = hidSetNpadJoyAssignmentModeSingleByDefault(id.HidControllerId).newResult 252 | of JoyconMode.Dual: 253 | rc = hidSetNpadJoyAssignmentModeDual(id.HidControllerId).newResult 254 | 255 | if rc.failed: 256 | raiseEx(ControllerMergeError, "Could not set joycon mode to " & $mode, rc) 257 | 258 | 259 | proc mergeJoycons*(id1: Controller; id2: Controller) = 260 | ## / Merge two single joy-cons into a dual-mode controller. Use this after \ref 261 | ## hidSetNpadJoyAssignmentModeDual, when \ref hidSetNpadJoyAssignmentModeSingleByDefault 262 | ## was previously used (this includes using this manually at application exit). 263 | let rc = hidMergeSingleJoyAsDualJoy( 264 | id1.HidControllerID, 265 | id2.HidControllerID 266 | ).newResult 267 | 268 | if rc.failed: 269 | raiseEx(ControllerMergeError, "Could not merge joycons", rc) 270 | 271 | 272 | proc initializeVibrationDevices*( 273 | controller: Controller, 274 | controllerTypes: openArray[ControllerType] 275 | ): seq[VibrationDevice] = 276 | result = @[] 277 | 278 | for ctype in controllerTypes: 279 | var devs: array[2, VibrationDevice] 280 | var numDevices = 2 281 | 282 | if ctype == ControllerType.JoyconLeft or ctype == ControllerType.JoyconRight: 283 | numDevices = 1 284 | 285 | let rc = hidInitializeVibrationDevices( 286 | devs[0].addr, numDevices, HidControllerID(controller), 287 | HidControllerType(ctype) 288 | ).newResult 289 | 290 | if rc.failed: 291 | raiseEx( 292 | VibrationInitError, 293 | "Could not init vibration for controller $# with type $#" % 294 | [$controller, $ctype] 295 | ) 296 | for i in 0 ..< numDevices: 297 | result.add(devs[i]) 298 | 299 | 300 | proc info*(device: VibrationDevice): VibrationDeviceInfo = 301 | ## / Gets HidVibrationDeviceInfo for the specified VibrationDeviceHandle. 302 | let rc = hidGetVibrationDeviceInfo(device.unsafeAddr, result.addr).newResult 303 | 304 | if rc.failed: 305 | raiseEx(VibrationError, "Could not get vibration device info", rc) 306 | 307 | proc `vibration=`*(device: VibrationDevice, value: VibrationValue) = 308 | ## / Send the value to the specified handle. 309 | let rc = hidSendVibrationValue(device.unsafeAddr, value.unsafeAddr).newResult 310 | 311 | if rc.failed: 312 | raiseEx(VibrationError, "Could not send vibration value", rc) 313 | 314 | proc vibration*(device: VibrationDevice): VibrationValue = 315 | ## / Gets the current HidVibrationValue for the specified VibrationDeviceHandle. 316 | let rc = hidGetActualVibrationValue(device.unsafeAddr, result.addr).newResult 317 | 318 | if rc.failed: 319 | raiseEx(VibrationError, "Could not get vibration value", rc) 320 | 321 | proc permitVibration*(flag: bool) = 322 | ## / Sets whether vibration is allowed, this also affects the config displayed by 323 | ## System Settings. 324 | let rc = hidPermitVibration(flag).newResult 325 | 326 | if rc.failed: 327 | raiseEx(VibrationError, "Unable to permit vibration", rc) 328 | 329 | proc isVibrationPermitted*(): bool = 330 | ## / Gets whether vibration is allowed. 331 | let rc = hidIsVibrationPermitted(result.addr).newResult 332 | if rc.failed: 333 | raiseEx(VibrationError, "Error getting vibration information", rc) 334 | 335 | proc sendVibrationValues*( 336 | devices: openArray[VibrationDevice], 337 | values: openArray[VibrationValue]) = 338 | ## / Send VibrationValues[index] to VibrationDeviceHandles[index], where count is the 339 | ## number of entries in the VibrationDeviceHandles/VibrationValues arrays. 340 | let sizeH = len(devices) 341 | let sizeV = len(values) 342 | 343 | if sizeH != sizeV: 344 | raiseEx( 345 | VibrationError, 346 | "Vibration arrays must be the same size. handles $# != values $#" % 347 | [$sizeH, $sizeV] 348 | ) 349 | 350 | let rc = hidSendVibrationValues( 351 | devices[0].unsafeAddr, values[0].unsafeAddr, sizeH 352 | ).newResult 353 | 354 | if rc.failed: 355 | raiseEx(VibrationError, "Unable to send vibration error", rc) 356 | -------------------------------------------------------------------------------- /src/libnx/nim.cfg: -------------------------------------------------------------------------------- 1 | --path="../" 2 | -------------------------------------------------------------------------------- /src/libnx/results.nim: -------------------------------------------------------------------------------- 1 | import strutils 2 | import 3 | libnx/wrapper/result, 4 | libnx/wrapper/types 5 | 6 | type 7 | 8 | Module {.pure.} = enum 9 | Success = 0, 10 | Kernel = 1, 11 | Libnx = 345, 12 | LibnxNvidia = 348 13 | 14 | Result* = ref object 15 | code*: uint32 16 | module*: string 17 | kind*: Module 18 | error*: string 19 | 20 | KernelError* {.pure.} = enum 21 | Timeout = 117 22 | 23 | LibnxError* {.pure.} = enum 24 | BadReloc=1, 25 | OutOfMemory, 26 | AlreadyMapped, 27 | BadGetInfo_Stack, 28 | BadGetInfo_Heap, 29 | BadQueryMemory, 30 | AlreadyInitialized, 31 | NotInitialized, 32 | NotFound, 33 | IoError, 34 | BadInput, 35 | BadReent, 36 | BufferProducerError, 37 | HandleTooEarly, 38 | HeapAllocFailed, 39 | TooManyOverrides, 40 | ParcelError, 41 | BadGfxInit, 42 | BadGfxEventWait, 43 | BadGfxQueueBuffer, 44 | BadGfxDequeueBuffer, 45 | AppletCmdidNotFound, 46 | BadAppletReceiveMessage, 47 | BadAppletNotifyRunning, 48 | BadAppletGetCurrentFocusState, 49 | BadAppletGetOperationMode, 50 | BadAppletGetPerformanceMode, 51 | BadUsbCommsRead, 52 | BadUsbCommsWrite, 53 | InitFail_SM, 54 | InitFail_AM, 55 | InitFail_HID, 56 | InitFail_FS, 57 | BadGetInfo_Rng, 58 | JitUnavailable, 59 | WeirdKernel, 60 | IncompatSysVer, 61 | InitFail_Time, 62 | TooManyDevOpTabs 63 | 64 | LibnxNvidiaError* {.pure.} = enum 65 | Unknown=1, 66 | NotImplemented, #///< Maps to Nvidia: 1 67 | NotSupported, #///< Maps to Nvidia: 2 68 | NotInitialized, #///< Maps to Nvidia: 3 69 | BadParameter, #///< Maps to Nvidia: 4 70 | Timeout, #///< Maps to Nvidia: 5 71 | InsufficientMemory, #///< Maps to Nvidia: 6 72 | ReadOnlyAttribute, #///< Maps to Nvidia: 7 73 | InvalidState, #///< Maps to Nvidia: 8 74 | InvalidAddress, #///< Maps to Nvidia: 9 75 | InvalidSize, #///< Maps to Nvidia: 10 76 | BadValue, #///< Maps to Nvidia: 11 77 | AlreadyAllocated, #///< Maps to Nvidia: 13 78 | Busy, #///< Maps to Nvidia: 14 79 | ResourceError, #///< Maps to Nvidia: 15 80 | CountMismatch, #///< Maps to Nvidia: 16 81 | SharedMemoryTooSmall, #///< Maps to Nvidia: 0x1000 82 | FileOperationFailed, #///< Maps to Nvidia: 0x30003 83 | IoctlFailed #///< Maps to Nvidia: 0x3000F 84 | 85 | 86 | proc succeeded*(res: Result): bool = res.code.R_SUCCEEDED 87 | proc failed*(res: Result): bool = res.code.R_FAILED 88 | 89 | proc newResult*(code: uint32): Result = 90 | ## Create a result from a libnx error code for friendlier syntax. 91 | result = new(Result) 92 | 93 | result.code = code 94 | 95 | let moduleCode = code.R_MODULE 96 | let module = Module(moduleCode) 97 | 98 | result.kind = module 99 | 100 | let descCode = code.R_DESCRIPTION 101 | 102 | var error = "Unknown error: module: $# description: $#" % 103 | [$moduleCode, $descCode] 104 | try: 105 | case module 106 | of Module.Success: 107 | return 108 | of Module.Kernel: 109 | error = $KernelError(descCode) 110 | of Module.Libnx: 111 | error = $LibnxError(descCode) 112 | of Module.LibnxNvidia: 113 | error = $LibnxNvidiaError(descCode) 114 | except: 115 | echo "Converting to result failed: " & getCurrentExceptionMsg() 116 | echo "Code: " & $code 117 | echo "ModuleCode: " & $moduleCode 118 | echo "Module: " & $module 119 | echo "DescCode: " & $descCode 120 | 121 | result.module = $module 122 | result.error = error 123 | -------------------------------------------------------------------------------- /src/libnx/service.nim: -------------------------------------------------------------------------------- 1 | import 2 | libnx/results, 3 | libnx/wrapper/sm 4 | 5 | from libnx/wrapper/types import Handle 6 | 7 | type 8 | Service* = ref object 9 | serv: sm.Service 10 | 11 | proc getSmService*(serv: Service): sm.Service = 12 | serv.serv 13 | 14 | proc newService*(serv: sm.Service): Service = 15 | result = Service(serv: serv) 16 | 17 | proc isOverride*(service: Service): bool = 18 | ## * 19 | ## @brief Returns whether a service is overriden in the homebrew environment. 20 | ## @param[in] s Service object. 21 | ## @return true if overriden. 22 | ## 23 | serviceIsOverride(service.serv.addr) 24 | 25 | proc isActive*(service: Service): bool = 26 | ## * 27 | ## @brief Returns whether a service has been initialized. 28 | ## @param[in] s Service object. 29 | ## @return true if initialized. 30 | ## 31 | serviceIsActive(service.serv.addr) 32 | 33 | proc isDomain*(service: Service): bool = 34 | ## * 35 | ## @brief Returns whether a service is a domain. 36 | ## @param[in] s Service object. 37 | ## @return true if a domain. 38 | ## 39 | serviceIsDomain(service.serv.addr) 40 | 41 | proc isDomainSubservice*(service: Service): bool = 42 | ## * 43 | ## @brief Returns whether a service is a domain subservice. 44 | ## @param[in] s Service object. 45 | ## @return true if a domain subservice. 46 | ## 47 | serviceIsDomainSubservice(service.serv.addr) 48 | 49 | proc objectId*(service: Service): uint32 = 50 | ## * 51 | ## @brief For a domain/domain subservice, return the associated object ID. 52 | ## @param[in] s Service object, necessarily a domain or domain subservice. 53 | ## @return The object ID. 54 | ## 55 | serviceGetObjectId(service.serv.addr) 56 | 57 | proc close*(service: Service; objectId: uint32): Result = 58 | ## * 59 | ## @brief Closes a domain object by ID. 60 | ## @param[in] s Service object, necessarily a domain or domain subservice. 61 | ## @param object_id ID of the object to close. 62 | ## @return Result code. 63 | ## 64 | serviceCloseObjectById(service.serv.addr, objectId).newResult 65 | 66 | 67 | proc ipcDispatch*(service: Service): Result = 68 | ## * 69 | ## @brief Dispatches an IPC request to a service. 70 | ## @param[in] s Service object. 71 | ## @return Result code. 72 | ## 73 | serviceIpcDispatch(service.serv.addr).newResult 74 | 75 | proc createService*(handle: Handle): Service = 76 | ## * 77 | ## @brief Creates a service object from an IPC session handle. 78 | ## @param[out] s Service object. 79 | ## @param[in] h IPC session handle. 80 | ## 81 | result = new(Service) 82 | serviceCreate(result.serv.addr, handle) 83 | 84 | proc createDomainSubservice*(parent: Service; objectId: uint32): Service = 85 | ## * 86 | ## @brief Creates a domain subservice object from a parent service. 87 | ## @param[out] s Service object. 88 | ## @param[in] parent Parent service, necessarily a domain or domain subservice. 89 | ## @param[in] object_id Object ID for this subservice. 90 | ## 91 | result = new(Service) 92 | serviceCreateDomainSubservice(result.serv.addr, parent.serv.addr, objectId) 93 | 94 | proc convertToDomain*(service: Service): Result = 95 | ## * 96 | ## @brief Converts a regular service to a domain. 97 | ## @param[in] s Service object. 98 | ## @return Result code. 99 | ## 100 | serviceConvertToDomain(service.serv.addr).newResult 101 | 102 | proc close*(service: Service) = 103 | ## * 104 | ## @brief Closes a service. 105 | ## @param[in] s Service object. 106 | ## 107 | serviceClose(service.serv.addr) 108 | -------------------------------------------------------------------------------- /src/libnx/utils.nim: -------------------------------------------------------------------------------- 1 | import macros, strutils, math, hashes 2 | import libnx/results 3 | 4 | 5 | proc hash*(obj: SomeOrdinal): Hash = 6 | ## Allow hashing of enums with holes 7 | result = cast[int](obj) 8 | 9 | proc size*(enumTy: typedesc): int = 10 | # Returns the number of items in a bit set enum 11 | log2(float64(enumTy.high)).int + 1 12 | 13 | template recursive(node, action: untyped): untyped {.dirty.} = 14 | ## recursively iterate over AST nodes and perform an 15 | ## action on them 16 | proc helper(child: NimNode): NimNode {.gensym.}= 17 | action 18 | result = child.copy() 19 | for c in child.children: 20 | if child.kind == nnkCall and c.kind == nnkDotExpr: 21 | # ignore dot expressions that are also calls 22 | continue 23 | result.add helper(c) 24 | discard helper(node) 25 | 26 | macro niceify*(enumType: typed, replaceNameField: string): untyped = 27 | ## 28 | ## niceify(HidMouseButton, "Hid:MOUSE_") transforms this: 29 | ## 30 | ## HidMouseButton* {.size: sizeof(cint)} = enum 31 | ## MOUSE_LEFT = BIT(0), MOUSE_RIGHT = BIT(1), MOUSE_MIDDLE = BIT(2), 32 | ## MOUSE_FORWARD = BIT(3), MOUSE_BACK = BIT(4) 33 | ## 34 | ## into this: 35 | ## 36 | ## MouseButton {.size: 4, pure.} = enum 37 | ## Left = MOUSE_LEFT, Right = MOUSE_RIGHT, Middle = MOUSE_MIDDLE, 38 | ## Forward = MOUSE_FORWARD, 39 | ## Back = MOUSE_BACK 40 | ## 41 | ## If any fields begin with a number, the first letter of the name after 42 | ## the colon will be used in addition. (128 -> M128 in this case) 43 | 44 | let replaceSplit = replaceNameField.strVal.split(":") 45 | var 46 | replaceName: string 47 | replaceField: string 48 | replaceNameWith = "" 49 | replaceFieldWith = "" 50 | 51 | if replaceSplit.len == 2: 52 | replaceName = replaceSplit[0] 53 | replaceField = replaceSplit[1] 54 | else: 55 | replaceName = replaceSplit[0] 56 | replaceNameWith = replaceSplit[1] 57 | replaceField = replaceSplit[2] 58 | replaceFieldWith = replaceSplit[3] 59 | 60 | var node = enumType.getImpl().copy() 61 | recursive(node): 62 | if child.kind == nnkPragmaExpr: 63 | child[0] = postFix( 64 | ident(child[0].strVal.replace($replaceName, replaceNameWith)), 65 | "*" 66 | ) 67 | 68 | if child.kind == nnkPragma: 69 | child.add(ident("pure")) 70 | 71 | if child.kind == nnkEnumFieldDef: 72 | let 73 | lhs = child[0] 74 | rhs = child[1] 75 | name = lhs.strVal 76 | var 77 | newName = name.replace($replaceField, replaceFieldWith) 78 | .normalize().capitalizeAscii() 79 | firstChar = newName[0] 80 | 81 | if firstChar in Digits: 82 | newName = replaceField[0] & newName 83 | 84 | child[0] = ident(newName) 85 | child[1] = ident(name) 86 | 87 | return newNimNode(nnkTypeSection).add(node) 88 | 89 | type 90 | BufferError* = object of Exception 91 | BufferIndexError* = object of BufferError 92 | 93 | Buffer*[T] = ref object 94 | len*: int 95 | data*: ptr UncheckedArray[T] 96 | 97 | template raiseEx*(ty: untyped, message: string, rc: Result): untyped = 98 | ## Raise an exception with a result error 99 | raise newException(ty, message & ": " & rc.error) 100 | 101 | template raiseEx*(ty: untyped, message: string): untyped = 102 | ## Raise an exception 103 | raise newException(ty, message) 104 | 105 | proc `[]`[T](buff: Buffer[T], index: int): T = 106 | if index > (buff.len - 1): 107 | raiseEx( 108 | BufferIndexError, 109 | "Index: $# is greater than buffer length: $#!" % [$index, $buff.len] 110 | ) 111 | 112 | result = buff.data[][index] 113 | 114 | proc `[]=`[T](buff: Buffer[T], index: int, value: T) = 115 | if index > (buff.len - 1): 116 | raiseEx( 117 | BufferIndexError, 118 | "Index: $# is greater than buffer length: $#!" % [$index, $buff.len] 119 | ) 120 | 121 | buff.data[][index] = value 122 | --------------------------------------------------------------------------------