Skip to content

Commit

Permalink
Support string / URL initialization parameter to `new Switch.Applicat…
Browse files Browse the repository at this point in the history
…ion()`
  • Loading branch information
TooTallNate committed Dec 1, 2024
1 parent e4c39c4 commit 41fcbbb
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changeset/slimy-goats-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nx.js/runtime": patch
---

Support string / URL initialization parameter to `new Switch.Application()`
2 changes: 1 addition & 1 deletion packages/runtime/src/$.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export interface Init {
// ns.c
nsInitialize(): () => void;
nsAppInit(c: ClassOf<Application>): void;
nsAppNew(id: BigInt | ArrayBuffer | null): Application;
nsAppNew(id: string | bigint | ArrayBuffer | null): Application;
nsAppNext(index: number): bigint | null;

// software-keyboard.c
Expand Down
36 changes: 29 additions & 7 deletions packages/runtime/src/switch/ns.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { $ } from '../$';
import { URL } from '../polyfills/url';
import { SaveData, SaveDataCreationInfoWithNacp } from './savedata';
import { inspect } from './inspect';
import { readFileSync } from '../fs';
import { FunctionPrototypeWithIteratorHelpers, proto, stub } from '../utils';
import {
FunctionPrototypeWithIteratorHelpers,
pathToString,
proto,
stub,
} from '../utils';
import type { Profile } from './profile';

let init = false;
Expand Down Expand Up @@ -75,6 +80,20 @@ export class Application {
* @param id The ID of the installed application.
*/
constructor(id: bigint);
/**
* Creates an `Application` instance from the string path
* containing the contents of a `.nro` homebrew application.
*
* @example
*
* ```typescript
* const app = new Switch.Application('sdmc:/hbmenu.nro');
* console.log(app.name);
* ```
*
* @param path The path of the `.nro` file.
*/
constructor(path: string | URL);
/**
* Creates an `Application` instance from an `ArrayBuffer`
* containing the contents of a `.nro` homebrew application.
Expand All @@ -90,9 +109,12 @@ export class Application {
* @param nro The contents of the `.nro` file.
*/
constructor(nro: ArrayBuffer);
constructor(v: bigint | ArrayBuffer) {
constructor(v: string | bigint | ArrayBuffer | URL) {
_init();
return proto($.nsAppNew(v), Application);
return proto(
$.nsAppNew(v instanceof URL ? pathToString(v) : v),
Application,
);
}

/**
Expand Down Expand Up @@ -167,11 +189,11 @@ export class Application {
static get self(): Application {
if (!self) {
_init();
let nro: ArrayBuffer | null = null;
let p: string | null = null;
if ($.argv.length) {
nro = readFileSync($.argv[0]);
p = $.argv[0];
}
self = proto($.nsAppNew(nro), Application);
self = proto($.nsAppNew(p), Application);
}
return self;
}
Expand Down
44 changes: 44 additions & 0 deletions source/ns.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "ns.h"
#include "applet.h"
#include "error.h"
#include <errno.h>

static JSClassID nx_app_class_id;

Expand Down Expand Up @@ -51,6 +52,49 @@ static JSValue nx_ns_app_new(JSContext *ctx, JSValueConst this_val, int argc,
if (JS_ToBigUint64(ctx, &application_id, argv[0])) {
return JS_EXCEPTION;
}
} else if (JS_IsString(argv[0])) {
// Get app ID from opening the path specified at the string value, which
// contains an NRO file.
const char *path = JS_ToCString(ctx, argv[0]);
if (!path) {
return JS_EXCEPTION;
}
FILE *file = fopen(path, "rb");
JS_FreeCString(ctx, path);

if (file == NULL) {
return nx_throw_errno_error(ctx, errno, "fopen()");
}

// Seek to offset 0x18 and read a u32 from the NRO file, which
// contains the offset of the asset header.
u32 asset_header_offset;
fseek(file, 0x18, SEEK_SET);
fread(&asset_header_offset, sizeof(u32), 1, file);

// Seek the file to the asset header offset and allocate buffer
uint8_t *asset_header = malloc(0x28); // Size needed for header info
fseek(file, asset_header_offset, SEEK_SET);
fread(asset_header, 0x28, 1, file);

u32 icon_section_offset = *(u32 *)(asset_header + 0x8);
u32 icon_section_size = *(u32 *)(asset_header + 0x10);
u32 nacp_section_offset = *(u32 *)(asset_header + 0x18);
u32 nacp_section_size = *(u32 *)(asset_header + 0x20);

// Seek to the icon section offset and read the icon data
fseek(file, asset_header_offset + icon_section_offset, SEEK_SET);
fread(&data->data.icon, icon_section_size, 1, file);

// Seek to the nacp section offset and read the nacp data
fseek(file, asset_header_offset + nacp_section_offset, SEEK_SET);
fread(&data->data.nacp, nacp_section_size, 1, file);

free(asset_header);
fclose(file);

data->icon_size = icon_section_size;
loaded = true;
} else {
// Get app ID from parsing the array buffer which contains an NRO file.
// This is the case when running the NRO through hbmenu or a forwarder.
Expand Down

0 comments on commit 41fcbbb

Please sign in to comment.