nx.js
Concepts

Services

Interacting with system service modules via IPC

libnx, the core C library used by nx.js to interact with the Switch, contains a vast number of functions for interacting with various system services in a myriad of different ways. Many of these functions are higher level wrappers on top of the Switch's IPC communication mechanism, also known as "service modules".

It would not be reasonable for nx.js to attempt to expose all of these functions directly, so instead nx.js provides a low-level API for interacting with these system modules via IPC, which allows functionality that is not directly exposed by nx.js to be implemented in userland.

Warning

This is a low-level API which is not recommended to be used directly in most cases. You should look for an npm package which provides a higher-level API for the service you are trying to interact with.

Porting libnx functions to nx.js

Example with output value

Let's say you want your application to retrieve the current system region code. In libnx, this can be done using the setGetRegionCode() function, which is exposed by the set service module.

However, nx.js does not directly expose this function, so your app can use the Switch.Service class to interact with the set service module.

To do this correctly, you may need to reference the libnx source code to understand which command ID and parameters are required for the command you are attempting to port.

For example, the setGetRegionCode() function is implemented as follows:

libnx/source/services/set.c
Result setGetRegionCode(SetRegion *out) {
    s32 code=0;
    Result rc = _setCmdNoInOutU32(&g_setSrv, (u32*)&code, 4);
    if (R_SUCCEEDED(rc) && out) *out = code;
    return rc;
}

Based on this implementation, we can see that:

  • set is the named service module
  • The command ID is 4
  • There is no input data
  • The output data is a region code, which is a u32 value

Porting this function to nx.js would look like this:

src/main.ts
const setSrv = new Switch.Service('set');
 
function getRegionCode() {
	const code = new Uint32Array(1);
	setSrv.dispatchOut(4, code.buffer);
	return code[0];
}
 
console.log(getRegionCode());

Example with input value

Let's say you want to interact with a function which takes an input value, such as setsysSetRegionCode().

The setsysSetRegionCode() function is implemented as follows:

libnx/source/services/set.c
Result setsysSetRegionCode(SetRegion region) {
    return _setCmdInU32NoOut(&g_setsysSrv, region, 57);
}
  • set:sys is the named service module
  • The command ID is 57
  • The input data is a region code, which is a u32 value
  • There is no output data

Porting this function to nx.js would look like this:

src/main.ts
const setSysSrv = new Switch.Service('set:sys');
 
function setRegionCode(region: number) {
	setSysSrv.dispatchIn(57, new Uint32Array([region]).buffer);
}
 
const regionCode = 1; // SetRegion_USA
setRegionCode(regionCode);

Example with buffers

Some commands require use of input and/or output buffers, such as setGetAvailableLanguageCodes(). Let's take a look at how to port this function to nx.js.

The setGetAvailableLanguageCodes() function is implemented as follows (some parts are omitted for brevity):

libnx/source/services/set.c
Result setGetAvailableLanguageCodes(s32 *total_entries, u64 *LanguageCodes, size_t max_entries) {
    return serviceDispatchOut(&g_setSrv, 5, *total_entries,
        .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
        .buffers = { { LanguageCodes, max_entries*sizeof(u64) } },
    );
}
  • set is the named service module
  • The command ID is 5
  • There is no input data
  • The output data is a s32 number representing the number of entries that were returned
  • The output buffer is an array of u64 values, which represent the available language codes
Important

Additionally, looking at the Switchbrew wiki for LanguageCode, we can see that the language codes are represented as a u64 value, which is actually a NUL-terminated string.

Porting this function to nx.js would look like this:

src/main.ts
import { SfBufferAttr } from '@nx.js/constants';
 
const setSrv = new Switch.Service('set');
 
function getAvailableLanguageCodes() {
    const totalEntriesArr = new Int32Array(1);
    const languageCodesBuf = new ArrayBuffer(20 * 8);
    setSrv.dispatchOut(5, totalEntriesArr.buffer, {
        bufferAttrs: [SfBufferAttr.HipcMapAlias | SfBufferAttr.Out],
        buffers: [languageCodesBuf],
    });
 
    const decoder = new TextDecoder();
    const languageCodes: string[] = [];
 
    // Process the output buffer into the list of language codes as strings
    const totalEntries = totalEntriesArr[0];
    for (let i = 0; i < totalEntries; i++) {
        const data = languageCodesBuf.slice(i * 8, i * 8 + 8);
        const nul = new Uint8Array(data).indexOf(0);
        languageCodes.push(decoder.decode(data.slice(0, nul)));
    }
 
    return languageCodes;
}
 
console.log(getAvailableLanguageCodes());

Example with output service object

In some cases, the result of a command is itself a new Switch.Service instance.

One such example is the clkrstOpenSession() function:

libnx/source/services/clkrst.c
Result clkrstOpenSession(ClkrstSession* session_out, PcvModuleId module_id, u32 unk) {
    const struct {
        u32 module_id;
        u32 unk;
    } in = { module_id, unk };
    return serviceDispatchIn(&g_clkrstSrv, 0, in,
        .out_num_objects = 1,
        .out_objects = &session_out->s,
    );
}

In this case, you can create an "uninitialized" Switch.Service instance (by omitting the name from the constructor), and then pass the instance to the outObjects option in the dispatch function:

src/main.ts
const clkrstSrv = new Switch.Service('clkrst');
 
function openSession(moduleId: number) {
    const sessionSrv = new Switch.Service();
    const inArr = new Uint32Array([moduleId, 3]);
    clkrstSrv.dispatchIn(0, inArr.buffer, {
        outObjects: [sessionSrv],
    });
    return sessionSrv;
}
 
const moduleId = 0x40000001; // PcvModuleId_CpuBus
const session = openSession(moduleId);
// Dispatch additional commands to the session service instance

On this page