Skip to content

Commit

Permalink
Add Application#createSaveData() and Application#mountSaveData()
Browse files Browse the repository at this point in the history
  • Loading branch information
TooTallNate committed Jan 13, 2024
1 parent f3e075a commit 8faad62
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/moody-elephants-dress.md
@@ -0,0 +1,5 @@
---
'nxjs-runtime': patch
---

Add `Application#createSaveData()` and `Application#mountSaveData()`
7 changes: 7 additions & 0 deletions packages/runtime/src/$.ts
Expand Up @@ -3,6 +3,7 @@ import type {
IRSensor,
NetworkInfo,
Profile,
ProfileUid,
Stats,
Versions,
} from './switch';
Expand Down Expand Up @@ -119,6 +120,12 @@ export interface Init {
statSync(path: string): Stats | null;
writeFileSync(path: string, data: ArrayBuffer): void;

// fsdev.c
fsdevCommitDevice(name: string): void;
fsdevCreateSaveData(nacp: ArrayBuffer, uid: ProfileUid): void;
fsdevMountSaveData(name: string, nacp: ArrayBuffer, uid: ProfileUid): void;
fsdevUnmountDevice(name: string): void;

// image.c
imageInit(c: ClassOf<Image | ImageBitmap>): void;
imageNew(width?: number, height?: number): Image | ImageBitmap;
Expand Down
20 changes: 20 additions & 0 deletions packages/runtime/src/switch/fsdev.ts
@@ -0,0 +1,20 @@
import { $ } from '../$';

/**
* Represents a mounted filesystem device, such as the Save Data store for an Application / Profile pair.
*/
export class FsDev {
name: string;

constructor(name: string) {
this.name = name;
}

commit() {
$.fsdevCommitDevice(this.name);
}

unmount() {
$.fsdevUnmountDevice(this.name);
}
}
43 changes: 43 additions & 0 deletions packages/runtime/src/switch/ns.ts
@@ -1,5 +1,7 @@
import { $ } from '../$';
import { assertInternalConstructor, stub } from '../utils';
import { FsDev } from './fsdev';
import type { Profile } from './profile';

let init = false;

Expand Down Expand Up @@ -51,6 +53,47 @@ export class Application {
launch(): never {
stub();
}

/**
* Creates the Save Data for this {@link Application} for the provided user profile.
*
* @example
*
* ```typescript
* const profile = Switch.currentProfile({ required: true });
* app.createSaveData(profile);
* ```
*
* @param profile The {@link Profile} to create the save data for.
*/
createSaveData(profile: Profile) {
$.fsdevCreateSaveData(this.nacp, profile.uid);
}

/**
* Mounts the save data for this application such that filesystem operations may be used.
*
* @example
*
* ```typescript
* const profile = Switch.currentProfile({ required: true });
* const device = app.mountSaveData('save', profile);
*
* // Use the filesystem functions to do operations on the save mount
* console.log(Switch.readDirSync('save:/'));
*
* // Make sure to use `device.commit()` after any write operations
* Switch.writeFileSync('save:/state', 'your app state…');
* device.commit();
* ```
*
* @param name The name of the device mount for filesystem paths.
* @param profile The {@link Profile} which the save data belongs to.
*/
mountSaveData(name: string, profile: Profile) {
$.fsdevMountSaveData(name, this.nacp, profile.uid);
return new FsDev(name);
}
}
$.nsAppInit(Application);

Expand Down
139 changes: 139 additions & 0 deletions source/fsdev.c
@@ -0,0 +1,139 @@
#include "fsdev.h"

static JSValue nx_fsdev_create_save_data(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
AccountUid uid;
if (
JS_ToBigInt64(ctx, (s64 *)&uid.uid[0], JS_GetPropertyUint32(ctx, argv[1], 0)) ||
JS_ToBigInt64(ctx, (s64 *)&uid.uid[1], JS_GetPropertyUint32(ctx, argv[1], 1)))
{
return JS_EXCEPTION;
}

size_t nacp_size;
NacpStruct *nacp = (NacpStruct *)JS_GetArrayBuffer(ctx, &nacp_size, argv[0]);
if (nacp_size != sizeof(NacpStruct)) {
return JS_ThrowTypeError(ctx, "Invalid NACP buffer (got %ld bytes, expected %ld)", nacp_size, sizeof(NacpStruct));
}

FsSaveDataAttribute attr;
FsSaveDataCreationInfo info;
FsSaveDataMetaInfo meta;
memset(&attr, 0, sizeof(attr));
memset(&info, 0, sizeof(info));
memset(&meta, 0, sizeof(meta));

attr.application_id = nacp->save_data_owner_id;
attr.uid = uid;
attr.system_save_data_id = 0;
attr.save_data_type = FsSaveDataType_Account;
attr.save_data_rank = 0;
attr.save_data_index = 0;

info.journal_size = nacp->user_account_save_data_journal_size;
info.save_data_size = nacp->user_account_save_data_size;
info.available_size = 0x4000;
info.owner_id = nacp->save_data_owner_id;
info.flags = 0;
info.save_data_space_id = FsSaveDataSpaceId_User;

meta.size = 0x40060;
meta.type = FsSaveDataMetaType_Thumbnail;

Result rc = fsCreateSaveDataFileSystem(&attr, &info, &meta);
if (R_FAILED(rc))
{
JSValue err = JS_NewError(ctx);
u32 module = R_MODULE(rc);
u32 desc = R_DESCRIPTION(rc);
char message[256];
snprintf(message, 256, "fsCreateSaveDataFileSystem() failed (module: %u, description: %u)", module, desc);
JS_DefinePropertyValueStr(ctx, err, "message", JS_NewString(ctx, message), JS_PROP_C_W);
JS_SetPropertyStr(ctx, err, "module", JS_NewUint32(ctx, module));
JS_SetPropertyStr(ctx, err, "description", JS_NewUint32(ctx, desc));
JS_SetPropertyStr(ctx, err, "value", JS_NewUint32(ctx, R_VALUE(rc)));
return JS_Throw(ctx, err);
}

return JS_UNDEFINED;
}

static JSValue nx_fsdev_mount_save_data(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
AccountUid uid;
if (
JS_ToBigInt64(ctx, (s64 *)&uid.uid[0], JS_GetPropertyUint32(ctx, argv[2], 0)) ||
JS_ToBigInt64(ctx, (s64 *)&uid.uid[1], JS_GetPropertyUint32(ctx, argv[2], 1)))
{
return JS_EXCEPTION;
}
size_t nacp_size;
NacpStruct *nacp = (NacpStruct *)JS_GetArrayBuffer(ctx, &nacp_size, argv[1]);
if (nacp_size != sizeof(NacpStruct)) {
return JS_ThrowTypeError(ctx, "Invalid NACP buffer (got %ld bytes, expected %ld)", nacp_size, sizeof(NacpStruct));
}
const char *name = JS_ToCString(ctx, argv[0]);
if (!name)
{
return JS_EXCEPTION;
}
Result rc = fsdevMountSaveData(name, nacp->save_data_owner_id, uid);
JS_FreeCString(ctx, name);
if (R_FAILED(rc))
{
JSValue err = JS_NewError(ctx);
u32 module = R_MODULE(rc);
u32 desc = R_DESCRIPTION(rc);
char message[256];
snprintf(message, 256, "fsdevMountSaveData() failed (module: %u, description: %u)", module, desc);
JS_DefinePropertyValueStr(ctx, err, "message", JS_NewString(ctx, message), JS_PROP_C_W);
JS_SetPropertyStr(ctx, err, "module", JS_NewUint32(ctx, module));
JS_SetPropertyStr(ctx, err, "description", JS_NewUint32(ctx, desc));
JS_SetPropertyStr(ctx, err, "value", JS_NewUint32(ctx, R_VALUE(rc)));
//JSValue argv_arr = JS_NewArray(ctx);
//for (int i = 0; i < argc; i++) {
// JS_SetPropertyUint32(ctx, argv_arr, i, JS_DupValue(ctx, argv[i]));
//}
//JS_SetPropertyStr(ctx, err, "argv", argv_arr);
return JS_Throw(ctx, err);
}
return JS_UNDEFINED;
}

static JSValue nx_fsdev_commit_device(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
const char *name = JS_ToCString(ctx, argv[0]);
Result rc = fsdevCommitDevice(name);
JS_FreeCString(ctx, name);
if (R_FAILED(rc))
{
JS_ThrowInternalError(ctx, "fsdevCommitDevice() returned 0x%x", rc);
return JS_EXCEPTION;
}
return JS_UNDEFINED;
}

static JSValue nx_fsdev_unmount_device(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
const char *name = JS_ToCString(ctx, argv[0]);
Result rc = fsdevUnmountDevice(name);
JS_FreeCString(ctx, name);
if (R_FAILED(rc))
{
JS_ThrowInternalError(ctx, "fsdevUnmountDevice() returned 0x%x", rc);
return JS_EXCEPTION;
}
return JS_UNDEFINED;
}

static const JSCFunctionListEntry function_list[] = {
JS_CFUNC_DEF("fsdevCommitDevice", 0, nx_fsdev_commit_device),
JS_CFUNC_DEF("fsdevCreateSaveData", 0, nx_fsdev_create_save_data),
JS_CFUNC_DEF("fsdevMountSaveData", 0, nx_fsdev_mount_save_data),
JS_CFUNC_DEF("fsdevUnmountDevice", 1, nx_fsdev_unmount_device),
};

void nx_init_fsdev(JSContext *ctx, JSValueConst init_obj)
{
JS_SetPropertyFunctionList(ctx, init_obj, function_list, countof(function_list));
}
4 changes: 4 additions & 0 deletions source/fsdev.h
@@ -0,0 +1,4 @@
#pragma once
#include "types.h"

void nx_init_fsdev(JSContext *ctx, JSValueConst init_obj);
2 changes: 2 additions & 0 deletions source/main.c
Expand Up @@ -18,6 +18,7 @@
#include "canvas.h"
#include "font.h"
#include "fs.h"
#include "fsdev.h"
#include "irs.h"
#include "nifm.h"
#include "ns.h"
Expand Down Expand Up @@ -546,6 +547,7 @@ int main(int argc, char *argv[])
nx_init_error(ctx, init_obj);
nx_init_font(ctx, init_obj);
nx_init_fs(ctx, init_obj);
nx_init_fsdev(ctx, init_obj);
nx_init_image(ctx, init_obj);
nx_init_irs(ctx, init_obj);
nx_init_nifm(ctx, init_obj);
Expand Down

0 comments on commit 8faad62

Please sign in to comment.