Prepare filesystem support

This commit is contained in:
momo5502
2025-04-29 15:48:18 +02:00
parent eb279847ed
commit 29e6072159
6 changed files with 81 additions and 52 deletions

View File

@@ -9,6 +9,7 @@
"version": "0.0.0",
"dependencies": {
"@fontsource/inter": "^5.2.5",
"@irori/idbfs": "^0.5.0",
"@radix-ui/react-checkbox": "^1.2.3",
"@radix-ui/react-dialog": "^1.1.11",
"@radix-ui/react-dropdown-menu": "^2.1.12",
@@ -1137,6 +1138,12 @@
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@irori/idbfs": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@irori/idbfs/-/idbfs-0.5.0.tgz",
"integrity": "sha512-IHZU8mA9I89YfR4canqTYO51mv7+326fhWKFF5eUJo9tmECoPh0dBLrjpOzh7yhjYO8rr9EGzFI9moJsYtJ/GA==",
"license": "Unlicense"
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",

View File

@@ -11,6 +11,7 @@
},
"dependencies": {
"@fontsource/inter": "^5.2.5",
"@irori/idbfs": "^0.5.0",
"@radix-ui/react-checkbox": "^1.2.3",
"@radix-ui/react-dialog": "^1.1.11",
"@radix-ui/react-dropdown-menu": "^2.1.12",

View File

@@ -7,7 +7,7 @@ onmessage = async (event) => {
const data = event.data;
if (data.message == "run") {
const payload = data.data;
runEmulation(payload.filesystem, payload.file, payload.options);
runEmulation(payload.file, payload.options);
} else if (data.message == "event") {
const payload = data.data;
msgQueue.push(payload);
@@ -49,22 +49,17 @@ function getMessageFromQueue() {
return msgQueue.shift();
}
function runEmulation(filesystem, file, options) {
function runEmulation(file, options) {
const mainArguments = [...options, "-e", "./root", file];
globalThis.Module = {
arguments: [...options, "-e", "./root", file],
arguments: mainArguments,
noInitialRun: true,
onRuntimeInitialized: function () {
filesystem.forEach((e) => {
if (e.name.endsWith("/")) {
FS.mkdir(e.name.slice(0, -1));
} else {
const dirs = e.name.split("/");
const file = dirs.pop();
const buffer = new Uint8Array(e.data);
if (FS.analyzePath(e.name).exists) {
FS.unlink(e.name);
}
FS.createDataFile("/" + dirs.join("/"), file, buffer, true, true);
}
FS.mkdir("/root");
FS.mount(IDBFS, {}, "/root");
FS.syncfs(true, function (err) {
Module.callMain(mainArguments);
});
},
print: logLine,

View File

@@ -4,7 +4,7 @@ import { Output } from "@/components/output";
import { Separator } from "@/components/ui/separator";
import { Emulator, UserFile, EmulationState } from "./emulator";
import { getFilesystem } from "./filesystem";
import { setupFilesystem } from "./filesystem";
import "./App.css";
import {
@@ -103,11 +103,11 @@ export function Playground() {
logLine("Starting emulation...");
const fs = await getFilesystem((current, total, file) => {
await setupFilesystem((current, total, file) => {
logLine(`Processing filesystem (${current}/${total}): ${file}`);
});
const new_emulator = new Emulator(fs, logLines, (_) => forceUpdate());
const new_emulator = new Emulator(logLines, (_) => forceUpdate());
new_emulator.onTerminate().then(() => setEmulator(null));
setEmulator(new_emulator);

View File

@@ -5,6 +5,8 @@ import * as flatbuffers from "flatbuffers";
import * as fbDebugger from "@/fb/debugger";
import * as fbDebuggerEvent from "@/fb/debugger/event";
import { storeFile } from "./filesystem";
type LogHandler = (lines: string[]) => void;
export interface UserFile {
@@ -50,7 +52,6 @@ function decodeEvent(data: string) {
type StateChangeHandler = (state: EmulationState) => void;
export class Emulator {
filesystem: FileEntry[];
logHandler: LogHandler;
stateChangeHandler: StateChangeHandler;
terminatePromise: Promise<number | null>;
@@ -59,12 +60,7 @@ export class Emulator {
worker: Worker;
state: EmulationState = EmulationState.Stopped;
constructor(
filesystem: FileEntry[],
logHandler: LogHandler,
stateChangeHandler: StateChangeHandler,
) {
this.filesystem = filesystem;
constructor(logHandler: LogHandler, stateChangeHandler: StateChangeHandler) {
this.logHandler = logHandler;
this.stateChangeHandler = stateChangeHandler;
this.terminateResolve = () => {};
@@ -81,7 +77,7 @@ export class Emulator {
this.worker.onmessage = this._onMessage.bind(this);
}
start(
async start(
settings: Settings = createDefaultSettings(),
userFile: UserFile | null = null,
) {
@@ -90,17 +86,14 @@ export class Emulator {
const filename = userFile.name.split("/").pop()?.split("\\").pop();
const canonicalName = filename?.toLowerCase();
file = "c:/" + canonicalName;
this.filesystem.push({
name: "root/filesys/c/" + canonicalName,
data: userFile.data,
});
await storeFile("root/filesys/c/" + canonicalName, userFile.data);
}
this._setState(EmulationState.Running);
this.worker.postMessage({
message: "run",
data: {
filesystem: this.filesystem,
file,
options: translateSettings(settings),
},

View File

@@ -1,4 +1,5 @@
import { parseZipFile, FileEntry, ProgressHandler } from "./zip-file";
import idbfsModule, { MainModule } from "@irori/idbfs";
function openDatabase(): Promise<IDBDatabase> {
return new Promise((resolve, reject) => {
@@ -59,23 +60,6 @@ async function getData(id: string) {
});
}
async function cacheAndUseData(
id: string,
asyncFunction: () => Promise<FileEntry[]>,
) {
try {
let data = (await getData(id)) as FileEntry[];
if (!data) {
data = await asyncFunction();
await saveData(id, data);
}
return data;
} catch (error) {
console.error("Error:", error);
throw error;
}
}
function fetchFilesystemZip() {
return fetch("./root.zip?1", {
method: "GET",
@@ -90,8 +74,57 @@ async function fetchFilesystem(progressHandler: ProgressHandler) {
return await parseZipFile(filesys, progressHandler);
}
export function getFilesystem(progressHandler: ProgressHandler) {
return cacheAndUseData("emulator-filesystem-2", () =>
fetchFilesystem(progressHandler),
);
function synchronizeIDBFS(idbfs: MainModule, populate: boolean) {
return new Promise<void>((resolve, reject) => {
idbfs.FS.syncfs(populate, function (err: any) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
async function initializeIDBFS() {
const idbfs = await idbfsModule();
idbfs.FS.mkdir("/root");
idbfs.FS.mount(idbfs.IDBFS, {}, "/root");
await synchronizeIDBFS(idbfs, true);
return idbfs;
}
export async function setupFilesystem(progressHandler: ProgressHandler) {
const idbfs = await initializeIDBFS();
if (idbfs.FS.analyzePath("/root/api-set.bin", false).exists) {
return;
}
const filesystem = await fetchFilesystem(progressHandler);
filesystem.forEach((e) => {
if (idbfs.FS.analyzePath("/" + e.name, false).exists) {
return;
}
if (e.name.endsWith("/")) {
idbfs.FS.mkdir("/" + e.name.slice(0, -1));
} else {
const buffer = new Uint8Array(e.data);
idbfs.FS.writeFile("/" + e.name, buffer);
}
});
await synchronizeIDBFS(idbfs, false);
}
export async function storeFile(file: string, data: ArrayBuffer) {
const idbfs = await initializeIDBFS();
const buffer = new Uint8Array(data);
idbfs.FS.writeFile(file, buffer);
await synchronizeIDBFS(idbfs, false);
}