--- name: libnx description: > Rules for touching libnx / Switch-homebrew code in this repo. Use when the task involves a libnx call or service (applet, hid/pad, fs, ns, ncm, es, set, nifm, psm, spl, svc*, hosversion, romfs), an nx type (Result, Service, Handle, NacpStruct, FsFileSystem, NcmContentStorage), the install backend or package stream, NRO/NSP/NCA/CNMT/ticket handling, applet-mode or sysmodule constraints, devkitPro/devkitA64 toolchain issues, or anything under src/install, src/platform, src/main_switch.cpp, vendor/libnx-ext. --- # libnx in pipensx Two rules carry almost all the weight: **read the header instead of recalling the signature**, and **know which of the two builds will see your call**. ## 1. The header on disk is the API, your memory is not libnx moves, and a plausible-looking `fooInitialize()` that does not exist costs a full Switch build to discover. Before writing any libnx call, read the declaration: ``` /opt/devkitpro/libnx/include/switch/services/*.h # 92 service headers /opt/devkitpro/libnx/include/switch/kernel/svc.h # svc*, and kernel/ for threads/events /opt/devkitpro/libnx/include/switch/{nacp,nro,result,types}.h grep -rn "ncmContentStorageWritePlaceHolder" /opt/devkitpro/libnx/include/ ``` What stock libnx lacks is vendored, not reinvented — check there before adding anything: `vendor/libnx-ext/libnx-ipcext/` (es, ns-ext, account-ext) and `vendor/libnx-ext/libnx-ext/`. Both are on the Switch target's include path only. For what a call *means* rather than what it takes, consult `switchbrew.org/wiki/`: per-command firmware gates, service ownership, error semantics, and the NCA/CNMT/ticket formats behind `package_stream.cpp`. The header will not tell you that `ncmContentStorageRevertToPlaceHolder` needs 2.0.0+ — the wiki will. ## 2. Most sources compile twice, and the second build is easy to forget `CORE_SOURCES`, `APP_SERVICE_SOURCES` and `UI_SOURCES` in `CMakeLists.txt` are linked into **Switch `pipensx`** (aarch64, real libnx). Only `src/main_switch.cpp`, `src/install/install_backend_switch.cpp`, `src/platform/switch_*` and the vendored ipcext are Switch-only. PC unit tests via `Makefile.pc` compile shared app/core code with `install_backend_pc.cpp` and the PC shim under `src/platform/pc/`. So a libnx call in shared code has to pick one of three existing patterns — do not invent a fourth: **a. `#ifdef __SWITCH__` with a PC fallback.** The default; 14 shared files already do it (`src/app/install_space.cpp`, `src/app/stream_ram_budget.cpp`, `src/app/update_service.cpp`, `src/ui/settings/settings_view.hpp`, `src/core/util.c`, `src/install/package_stream.cpp`, …). `__SWITCH__` is set by `target_compile_definitions(pipensx PRIVATE __SWITCH__)` and is absent for PC test builds. **b. Unconditional `#include `, resolved by the PC shim.** Only `src/app/installed_title_service.cpp` and `src/ui/common/ui_helpers.hpp` do this. `src/platform/pc/switch.h` (a hand-written 182-line stand-in) is first on the PC test include path and supplies stubs. **Adding a libnx symbol to one of these two files without adding a stub there leaves the Switch build green and breaks `make test`.** Stub behaviour is chosen for deterministic PC tests, not realism — read the header comment before adding to it. **c. Behind `src/install/install_backend.hpp`.** The heavy content-install surface (ncm, es, fs placeholders, CNMT) lives here, with `install_backend_switch.cpp` and `install_backend_pc.cpp` implementing both sides. New install-path work goes through this seam; both implementations get written, not just the console one. `Makefile.pc` builds core/app C only and never sees the shim — it relies purely on pattern (a). ## 3. House idioms - `Result rc = ...; if (R_FAILED(rc)) { ...close what you opened...; return; }` Every early return closes its own handles by hand — there is no RAII wrapper in this codebase, and adding one is a separate decision, not a drive-by. - Surface a failure as `0x%08x`, never as a bare decimal: `diagnostic_error("installed", "list", "result=0x%08x", rc)` (`src/core/util.h:41`), or the user-facing `"Unable to query SD storage (0x%08x)."` form. - Never `assert`/abort on a bad `Result`. This is homebrew running on someone's console — degrade and report. (Same reason `pipensx_utp` is built `NDEBUG`.) ## 4. Verify with both builds ``` make switch # Switch: build-switch/pipensx.nro make test # PC: compiles shared sources against the shim and install backend ``` A missing shim stub only shows up in the second. Run both before calling a libnx change done. ## 5. Constraints that live in prose, not headers Applet RAM budget, tile override, sysmodule tradeoffs — written up in `docs/plans/ESHOP_APPLET_PLAN.md`, `docs/plans/SYSMODULE_PLAN.md` and `docs/plans/PERF_PLAN.md`. Read them for the reasoning instead of re-deriving it, but treat them as historical (see `docs/plans/README.md`): they may describe code that has since changed. Confirm anything load-bearing against the source before acting on it.