Skip to content

Commit

Permalink
Add bigFile option to Switch.file()
Browse files Browse the repository at this point in the history
  • Loading branch information
TooTallNate committed Jul 16, 2024
1 parent 720d68f commit b0aadd7
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/rich-seahorses-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"nxjs-runtime": patch
---

Add `bigFile` option to `Switch.file()`
23 changes: 23 additions & 0 deletions docs/content/runtime/concepts/file-system.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,26 @@ await writer.close();

> If the file path provided to `Switch.file()` does not yet exist, then a new file will be created
> (as well as any necessary parent directories).
### Big files

If you need to write files larger than 4gb, can create a "big file" by passing
the `bigFile: true` option to `Switch.file()`:

```typescript
const file = Switch.file('sdmc:/test.txt', {
bigFile: true
});

const writer = file.writable.getWriter();
// … write more than 4gb worth of data to the file …
await writer.close();
```

> [!NOTE]
> This works even on FAT32 partitions, which normally have a 4gb limit.
> How "big files" work under the hood is that a directory is created with the
> "archive" bit set, which causes the directory to be treated as a file
> containing the directory's concatenated contents. This is all handled
> transparently by the operating system, so your application code can treat
> it as if it were a normal file.
1 change: 1 addition & 0 deletions packages/runtime/src/$.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ export interface Init {
fopen(path: string, mode: string): Promise<FileHandle>;
fread(f: FileHandle, buf: ArrayBuffer): Promise<number | null>;
fwrite(f: FileHandle, data: ArrayBuffer): Promise<void>;
fsCreateBigFile(path: string): void;
mkdirSync(path: string, mode: number): number;
readDirSync(path: string): string[] | null;
readFile(path: string): Promise<ArrayBuffer | null>;
Expand Down
17 changes: 16 additions & 1 deletion packages/runtime/src/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,19 @@ export function stat(path: PathLike) {
return $.stat(pathToString(path));
}

/**
* Options object for the {@link File | `Switch.file()`} function.
*/
export interface FsFileOptions {
type?: string;

/**
* Create a "big file", which is a directory with the "archive" bit set.
* This will cause HOS to treat the directory as if it were a file
* containing the directory's concatenated contents, allowing you to
* write file contents larger than 4GB.
*/
bigFile?: boolean;
}

/**
Expand All @@ -139,15 +150,19 @@ export function file(path: PathLike, opts?: FsFileOptions) {

export class FsFile extends File {
constructor(path: PathLike, opts?: FsFileOptions) {
const { bigFile, ...rest } = opts ?? {};
super([], pathToString(path), {
type: 'text/plain;charset=utf-8',
...opts,
...rest,
});
Object.defineProperty(this, 'lastModified', {
get(): number {
return (statSync(this.name)?.mtime ?? 0) * 1000;
},
});
if (bigFile) {
$.fsCreateBigFile(this.name);
}
}

get size() {
Expand Down
32 changes: 32 additions & 0 deletions source/fs.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "fs.h"
#include "async.h"
#include "error.h"
#include <dirent.h>
#include <errno.h>
#include <stdbool.h>
Expand Down Expand Up @@ -685,11 +686,42 @@ JSValue nx_remove_sync(JSContext *ctx, JSValueConst this_val, int argc,
return JS_UNDEFINED;
}

JSValue nx_fs_create_big_file(JSContext *ctx, JSValueConst this_val, int argc,
JSValueConst *argv) {
const char *path = JS_ToCString(ctx, argv[0]);
if (!path)
return JS_EXCEPTION;

char *protocol = js_strdup(ctx, path);
char *end_of_protocol = strstr(protocol, ":/");
end_of_protocol[0] = '\0';
char *name = end_of_protocol + 1;

FsFileSystem *fs = fsdevGetDeviceFileSystem(protocol);
if (!fs) {
js_free(ctx, protocol);
JS_FreeCString(ctx, path);
return JS_ThrowTypeError(ctx, "Invalid protocol: %s", protocol);
}

Result rc = fsFsCreateFile(fs, name, 0, FsCreateOption_BigFile);
if (R_FAILED(rc)) {
js_free(ctx, protocol);
JS_FreeCString(ctx, path);
return nx_throw_libnx_error(ctx, rc, "fsFsCreateFile()");
}

js_free(ctx, protocol);
JS_FreeCString(ctx, path);
return JS_UNDEFINED;
}

static const JSCFunctionListEntry function_list[] = {
JS_CFUNC_DEF("fclose", 1, nx_fclose),
JS_CFUNC_DEF("fopen", 1, nx_fopen),
JS_CFUNC_DEF("fread", 1, nx_fread),
JS_CFUNC_DEF("fwrite", 1, nx_fwrite),
JS_CFUNC_DEF("fsCreateBigFile", 1, nx_fs_create_big_file),
JS_CFUNC_DEF("mkdirSync", 1, nx_mkdir_sync),
JS_CFUNC_DEF("readDirSync", 1, nx_readdir_sync),
JS_CFUNC_DEF("readFile", 2, nx_read_file),
Expand Down

0 comments on commit b0aadd7

Please sign in to comment.