mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 16:46:16 +00:00
Prepare filesystem support
This commit is contained in:
7
page/package-lock.json
generated
7
page/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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),
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user