2020-07-27 02:01:57 +00:00
|
|
|
const fs = require("fs");
|
|
|
|
const path = require("path");
|
2020-07-28 13:51:56 +00:00
|
|
|
const { error } = require("console");
|
2020-07-26 16:06:09 +00:00
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
const homeDir = require("os").homedir();
|
|
|
|
const macDir = path.join(homeDir, "macintosh.js");
|
|
|
|
const macintoshCopyPath = path.join(__dirname, "user_files");
|
|
|
|
|
2020-07-28 13:51:56 +00:00
|
|
|
// Set by config
|
|
|
|
let userDataPath;
|
|
|
|
|
|
|
|
function getUserDataDiskPath() {
|
|
|
|
return path.join(userDataPath, 'disk');
|
|
|
|
}
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
function cleanupCopyPath() {
|
|
|
|
try {
|
|
|
|
if (fs.existsSync(macintoshCopyPath)) {
|
|
|
|
fs.rmdirSync(macintoshCopyPath, { recursive: true });
|
|
|
|
}
|
2020-07-14 19:45:04 +00:00
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
fs.mkdirSync(macintoshCopyPath);
|
|
|
|
} catch (error) {
|
|
|
|
console.error(`cleanupCopyPath: Failed to remove`, error);
|
2020-07-14 19:45:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-28 13:51:56 +00:00
|
|
|
function getUserDataDiskImage() {
|
|
|
|
if (!userDataPath) {
|
|
|
|
console.error(`getUserDataDiskImage: userDataPath not set`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const diskImageUserPath = getUserDataDiskPath();
|
|
|
|
const diskImagePath = path.join(__dirname, 'disk');
|
|
|
|
|
|
|
|
// If there's a disk image, move it over
|
|
|
|
if (fs.existsSync(diskImageUserPath)) {
|
|
|
|
// Delete a possible basilisk disk image
|
|
|
|
if (fs.existsSync(diskImagePath)) {
|
|
|
|
console.log(`Disk image ${diskImageUserPath} exists, deleting ${diskImagePath}`);
|
|
|
|
fs.unlinkSync(diskImagePath);
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.renameSync(diskImageUserPath, diskImagePath);
|
|
|
|
} else {
|
|
|
|
console.log(`getUserDataDiskImage: No image in user data dir, not doing anything`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-28 22:08:07 +00:00
|
|
|
// Taken a given path, it'll look at all the files in there,
|
|
|
|
// copy them over to the basilisk folder, and then add them
|
|
|
|
// to MEMFS
|
|
|
|
function copyFilesAtPath(module, sourcePath) {
|
|
|
|
try {
|
|
|
|
const absoluteSourcePath = path.join(macDir, sourcePath);
|
|
|
|
const absoluteTargetPath = path.join(macintoshCopyPath, sourcePath);
|
|
|
|
const targetPath = `/macintosh.js${sourcePath ? `/${sourcePath}` : ""}`;
|
|
|
|
const files = fs.readdirSync(absoluteSourcePath).filter((v) => {
|
|
|
|
// Remove hidden, iso, and img files
|
|
|
|
return !v.startsWith('.') && !v.endsWith(".iso") && !v.endsWith(".img");
|
|
|
|
});
|
|
|
|
|
|
|
|
(files || []).forEach((fileName) => {
|
|
|
|
try {
|
|
|
|
// If not, let's move on
|
|
|
|
const fileSourcePath = path.join(absoluteSourcePath, fileName);
|
|
|
|
const copyPath = path.join(absoluteTargetPath, fileName);
|
|
|
|
const relativeSourcePath = `${
|
|
|
|
sourcePath ? `${sourcePath}/` : ""
|
|
|
|
}${fileName}`;
|
|
|
|
const fileUrl = `user_files/${relativeSourcePath}`;
|
|
|
|
|
|
|
|
// Check if directory
|
|
|
|
if (fs.statSync(fileSourcePath).isDirectory()) {
|
|
|
|
if (!fs.existsSync(copyPath)) {
|
|
|
|
fs.mkdirSync(copyPath);
|
2020-07-27 05:21:19 +00:00
|
|
|
}
|
|
|
|
|
2020-07-28 22:08:07 +00:00
|
|
|
try {
|
|
|
|
const virtualDirPath = `${targetPath}/${fileName}`;
|
|
|
|
module.FS.mkdir(virtualDirPath);
|
|
|
|
} catch (error) {
|
|
|
|
console.log(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
copyFilesAtPath(module, relativeSourcePath);
|
|
|
|
return;
|
2020-07-27 05:21:19 +00:00
|
|
|
}
|
|
|
|
|
2020-07-28 22:08:07 +00:00
|
|
|
// We copy the files over and then add them as preload
|
|
|
|
console.log(`copyFilesAtPath: Adding ${fileName}`);
|
|
|
|
fs.copyFileSync(fileSourcePath, copyPath);
|
|
|
|
|
|
|
|
module.FS_createPreloadedFile(
|
|
|
|
targetPath,
|
|
|
|
fileName,
|
|
|
|
fileUrl,
|
|
|
|
true,
|
|
|
|
true
|
|
|
|
);
|
|
|
|
} catch (error) {
|
|
|
|
postMessage("showMessageBoxSync", {
|
|
|
|
type: "error",
|
|
|
|
title: "Could not transfer file",
|
|
|
|
message: `We tried to transfer ${fileName} to the virtual machine, but failed. The error was: ${error}`,
|
|
|
|
});
|
|
|
|
|
|
|
|
console.error(`copyFilesAtPath: Failed to preload ${fileName}`, error);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
postMessage("showMessageBoxSync", {
|
|
|
|
type: "error",
|
|
|
|
title: "Could not transfer files",
|
|
|
|
message: `We tried to transfer files to the virtual machine, but failed. The error was: ${error}`,
|
|
|
|
});
|
|
|
|
|
|
|
|
console.error(`copyFilesAtPath: Failed to copyFilesAtPath`, error);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function addAutoloader(module) {
|
2020-07-27 05:21:19 +00:00
|
|
|
const loadDatafiles = function () {
|
|
|
|
module.autoloadFiles.forEach((filepath) => {
|
2020-07-27 22:07:47 +00:00
|
|
|
const parent = `/`;
|
|
|
|
const name = path.basename(filepath);
|
|
|
|
|
|
|
|
console.log(`Adding preload file`, { parent, name, url: filepath });
|
|
|
|
|
2020-07-27 22:09:56 +00:00
|
|
|
module.FS_createPreloadedFile(parent, name, filepath, true, true);
|
2020-07-14 19:45:04 +00:00
|
|
|
});
|
2020-07-27 05:21:19 +00:00
|
|
|
|
|
|
|
// If the user has a macintosh.js dir, we'll copy over user
|
|
|
|
// data
|
|
|
|
if (!fs.existsSync(macDir)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-28 22:08:07 +00:00
|
|
|
// Load user files
|
|
|
|
copyFilesAtPath(module, "");
|
2020-07-14 19:45:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if (module.autoloadFiles) {
|
|
|
|
module.preRun = module.preRun || [];
|
|
|
|
module.preRun.unshift(loadDatafiles);
|
|
|
|
}
|
|
|
|
|
|
|
|
return module;
|
|
|
|
}
|
|
|
|
|
|
|
|
function addCustomAsyncInit(module) {
|
|
|
|
if (module.asyncInit) {
|
|
|
|
module.preRun = module.preRun || [];
|
|
|
|
module.preRun.push(function waitForCustomAsyncInit() {
|
2020-07-27 02:01:57 +00:00
|
|
|
module.addRunDependency("__moduleAsyncInit");
|
2020-07-14 19:45:04 +00:00
|
|
|
|
|
|
|
module.asyncInit(module, function asyncInitCallback() {
|
2020-07-27 02:01:57 +00:00
|
|
|
module.removeRunDependency("__moduleAsyncInit");
|
2020-07-14 19:45:04 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
function writeSafely(filePath, fileData) {
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
fs.writeFile(filePath, fileData, (error) => {
|
|
|
|
if (error) {
|
2020-07-27 17:33:55 +00:00
|
|
|
postMessage("showMessageBoxSync", {
|
2020-07-27 22:07:47 +00:00
|
|
|
type: "error",
|
|
|
|
title: "Could not save files",
|
|
|
|
message: `We tried to save files from the virtual machine, but failed. The error was: ${error}`,
|
2020-07-27 17:33:55 +00:00
|
|
|
});
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
console.error(`Disk save: Encountered error for ${filePath}`, error);
|
|
|
|
} else {
|
|
|
|
console.log(`Disk save: Finished writing ${filePath}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-07-27 22:07:47 +00:00
|
|
|
function getPrefs(userImages = []) {
|
|
|
|
try {
|
|
|
|
const prefsTemplatePath = path.join(__dirname, "prefs_template");
|
|
|
|
const prefsPath = path.join(__dirname, "prefs");
|
|
|
|
let prefs = fs.readFileSync(prefsTemplatePath, { encoding: "utf-8" });
|
|
|
|
|
2020-07-28 04:25:17 +00:00
|
|
|
// Replace line endings, just in case
|
|
|
|
prefs = prefs.replaceAll('\r\n', '\n');
|
|
|
|
|
2020-07-27 22:07:47 +00:00
|
|
|
if (userImages && userImages.length > 0) {
|
|
|
|
console.log(`getPrefs: Found ${userImages.length} user images`);
|
|
|
|
userImages.forEach((file) => {
|
|
|
|
if (file.endsWith(".iso")) {
|
|
|
|
prefs += `\ncdrom ${file}`;
|
|
|
|
} else if (file.endsWith(".img")) {
|
|
|
|
prefs += `\ndisk ${file}`;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
prefs += `\n`;
|
|
|
|
|
|
|
|
fs.writeFileSync(prefsPath, prefs);
|
|
|
|
} catch (error) {
|
|
|
|
console.error(`getPrefs: Failed to set prefs`, error);
|
|
|
|
}
|
|
|
|
|
|
|
|
return "prefs";
|
|
|
|
}
|
|
|
|
|
2020-07-27 22:09:56 +00:00
|
|
|
function isMacDirFileOfType(extension = "", v = "") {
|
2020-07-27 22:07:47 +00:00
|
|
|
const isType = v.endsWith(`.${extension}`);
|
|
|
|
const isMatch = isType && fs.statSync(path.join(macDir, v)).isFile();
|
|
|
|
|
|
|
|
console.log(`isMacDirFileOfType: ${v} is file and ${extension}: ${isMatch}`);
|
|
|
|
return isMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
function copyUserImages() {
|
|
|
|
const result = [];
|
|
|
|
|
|
|
|
try {
|
|
|
|
// No need if the macDir doesn't exist
|
|
|
|
if (!fs.existsSync(macDir)) {
|
|
|
|
console.log(`autoMountImageFiles: ${macDir} does not exist, exit`);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
const macDirFiles = fs.readdirSync(macDir);
|
2020-07-27 22:09:56 +00:00
|
|
|
const imgFiles = macDirFiles.filter((v) => isMacDirFileOfType("img", v));
|
|
|
|
const isoFiles = macDirFiles.filter((v) => isMacDirFileOfType("iso", v));
|
2020-07-27 22:07:47 +00:00
|
|
|
const isoImgFiles = [...isoFiles, ...imgFiles];
|
|
|
|
|
|
|
|
console.log(`copyUserImages: iso and img files`, isoImgFiles);
|
|
|
|
|
|
|
|
isoImgFiles.forEach((fileName, i) => {
|
|
|
|
const sourcePath = path.join(macDir, fileName);
|
2020-07-27 22:09:56 +00:00
|
|
|
const sanitizedFileName = `user_image_${i}_${fileName.replace(
|
|
|
|
/[^\w\s\.]/gi,
|
|
|
|
""
|
|
|
|
)}`;
|
2020-07-27 22:07:47 +00:00
|
|
|
const targetPath = path.join(__dirname, sanitizedFileName);
|
|
|
|
|
|
|
|
if (fs.existsSync(targetPath)) {
|
|
|
|
const sourceStat = fs.statSync(sourcePath);
|
|
|
|
const targetStat = fs.statSync(targetPath);
|
|
|
|
|
|
|
|
// Copy if the length is different
|
|
|
|
if (sourceStat.size !== targetStat.size) {
|
|
|
|
fs.copyFileSync(sourcePath, targetPath);
|
|
|
|
} else {
|
|
|
|
console.log(
|
|
|
|
`autoMountImageFiles: ${sourcePath} already exists in ${targetPath}, not copying`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fs.copyFileSync(sourcePath, targetPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log(`Copied over ${targetPath}`);
|
|
|
|
result.push(sanitizedFileName);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Delete all old files
|
|
|
|
const imagesCopyFiles = fs.readdirSync(__dirname);
|
|
|
|
imagesCopyFiles.forEach((v) => {
|
2020-07-27 22:09:56 +00:00
|
|
|
if (v.startsWith("user_image_") && !result.includes(v)) {
|
2020-07-27 22:07:47 +00:00
|
|
|
fs.unlinkSync(path.join(__dirname, v));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
console.error(`copyUserImages: Encountered error`, error);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getAutoLoadFiles(userImages = []) {
|
|
|
|
const autoLoadFiles = ["disk", "rom", "prefs", ...userImages];
|
|
|
|
return autoLoadFiles;
|
|
|
|
}
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
async function saveFilesInPath(folderPath) {
|
|
|
|
const entries = (Module.FS.readdir(folderPath) || []).filter(
|
|
|
|
(v) => !v.startsWith(".")
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!entries || entries.length === 0) return;
|
|
|
|
|
|
|
|
// Ensure directory
|
|
|
|
const targetDir = path.join(homeDir, folderPath);
|
|
|
|
if (!fs.existsSync(targetDir)) {
|
|
|
|
fs.mkdirSync(targetDir);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const file of entries) {
|
|
|
|
try {
|
|
|
|
const fileSourcePath = `${folderPath}/${file}`;
|
|
|
|
const stat = Module.FS.analyzePath(fileSourcePath);
|
|
|
|
|
|
|
|
if (stat && stat.object && stat.object.isFolder) {
|
|
|
|
// This is a folder, step into
|
|
|
|
await saveFilesInPath(fileSourcePath);
|
|
|
|
} else if (stat && stat.object && stat.object.contents) {
|
|
|
|
const fileData = stat.object.contents;
|
|
|
|
const filePath = path.join(targetDir, file);
|
|
|
|
|
|
|
|
await writeSafely(filePath, fileData);
|
|
|
|
} else {
|
|
|
|
console.log(
|
|
|
|
`Disk save: Object at ${fileSourcePath} is something, but we don't know what`,
|
|
|
|
stat
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} catch (error) {
|
2020-07-27 17:33:55 +00:00
|
|
|
postMessage("showMessageBoxSync", {
|
2020-07-27 22:07:47 +00:00
|
|
|
type: "error",
|
|
|
|
title: "Could not safe file",
|
|
|
|
message: `We tried to save the file "${file}" from the virtual machine, but failed. The error was: ${error}`,
|
2020-07-27 17:33:55 +00:00
|
|
|
});
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
console.error(`Disk save: Could not write ${file}`, error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let InputBufferAddresses = {
|
2020-07-14 19:45:04 +00:00
|
|
|
globalLockAddr: 0,
|
|
|
|
mouseMoveFlagAddr: 1,
|
|
|
|
mouseMoveXDeltaAddr: 2,
|
|
|
|
mouseMoveYDeltaAddr: 3,
|
|
|
|
mouseButtonStateAddr: 4,
|
|
|
|
keyEventFlagAddr: 5,
|
|
|
|
keyCodeAddr: 6,
|
|
|
|
keyStateAddr: 7,
|
|
|
|
};
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
let LockStates = {
|
2020-07-14 19:45:04 +00:00
|
|
|
READY_FOR_UI_THREAD: 0,
|
|
|
|
UI_THREAD_LOCK: 1,
|
|
|
|
READY_FOR_EMUL_THREAD: 2,
|
|
|
|
EMUL_THREAD_LOCK: 3,
|
|
|
|
};
|
|
|
|
|
|
|
|
var Module = null;
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
self.onmessage = async function (msg) {
|
2020-07-27 02:01:57 +00:00
|
|
|
console.log("Worker message received", msg.data);
|
2020-07-26 16:06:09 +00:00
|
|
|
|
|
|
|
// If it's a config object, start the show
|
|
|
|
if (msg && msg.data && msg.data.SCREEN_WIDTH) {
|
2020-07-27 02:01:57 +00:00
|
|
|
console.log("Start emulator worker");
|
|
|
|
startEmulator(
|
|
|
|
Object.assign({}, msg.data, { singleThreadedEmscripten: true })
|
|
|
|
);
|
2020-07-26 16:06:09 +00:00
|
|
|
}
|
|
|
|
|
2020-07-27 02:01:57 +00:00
|
|
|
if (msg && msg.data === "disk_save") {
|
|
|
|
const diskData = Module.FS.readFile("/disk");
|
2020-07-28 13:51:56 +00:00
|
|
|
const diskPath = getUserDataDiskPath();
|
|
|
|
const basiliskDiskPath = path.join(__dirname, 'disk');
|
2020-07-26 16:06:09 +00:00
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
// I wish we could do this with promises, but OOM crashes kill that idea
|
|
|
|
try {
|
|
|
|
console.log(`Trying to save disk`);
|
|
|
|
fs.writeFileSync(diskPath, diskData);
|
2020-07-26 16:06:09 +00:00
|
|
|
console.log(`Finished writing disk`);
|
2020-07-27 05:21:19 +00:00
|
|
|
} catch (error) {
|
|
|
|
console.error(`Failed to write disk`, error);
|
|
|
|
}
|
2020-07-26 16:06:09 +00:00
|
|
|
|
2020-07-28 13:51:56 +00:00
|
|
|
try {
|
|
|
|
if (fs.existsSync(basiliskDiskPath) && !(Module && Module.isDevMode)) {
|
|
|
|
fs.unlinkSync(basiliskDiskPath);
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error(`Failed to delete ${basiliskDiskPath}`);
|
|
|
|
}
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
// Now, user files
|
|
|
|
console.log(`Saving user files`);
|
|
|
|
await saveFilesInPath("/macintosh.js");
|
2020-07-27 02:01:57 +00:00
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
// Clean up old copy dir
|
|
|
|
cleanupCopyPath();
|
|
|
|
|
|
|
|
postMessage({ type: "disk_saved" });
|
2020-07-26 16:06:09 +00:00
|
|
|
}
|
2020-07-14 19:45:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
function startEmulator(parentConfig) {
|
2020-07-28 13:51:56 +00:00
|
|
|
userDataPath = parentConfig.userDataPath;
|
|
|
|
|
|
|
|
getUserDataDiskImage();
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
let screenBufferView = new Uint8Array(
|
2020-07-14 19:45:04 +00:00
|
|
|
parentConfig.screenBuffer,
|
|
|
|
0,
|
|
|
|
parentConfig.screenBufferSize
|
|
|
|
);
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
let videoModeBufferView = new Int32Array(
|
2020-07-14 19:45:04 +00:00
|
|
|
parentConfig.videoModeBuffer,
|
|
|
|
0,
|
|
|
|
parentConfig.videoModeBufferSize
|
|
|
|
);
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
let inputBufferView = new Int32Array(
|
2020-07-14 19:45:04 +00:00
|
|
|
parentConfig.inputBuffer,
|
|
|
|
0,
|
|
|
|
parentConfig.inputBufferSize
|
|
|
|
);
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
let nextAudioChunkIndex = 0;
|
|
|
|
let audioDataBufferView = new Uint8Array(
|
2020-07-14 19:45:04 +00:00
|
|
|
parentConfig.audioDataBuffer,
|
|
|
|
0,
|
|
|
|
parentConfig.audioDataBufferSize
|
|
|
|
);
|
|
|
|
|
|
|
|
function waitForTwoStateLock(bufferView, lockIndex) {
|
|
|
|
if (Atomics.load(bufferView, lockIndex) === LockStates.UI_THREAD_LOCK) {
|
|
|
|
while (
|
|
|
|
Atomics.compareExchange(
|
|
|
|
bufferView,
|
|
|
|
lockIndex,
|
|
|
|
LockStates.UI_THREAD_LOCK,
|
|
|
|
LockStates.EMUL_THREAD_LOCK
|
|
|
|
) !== LockStates.UI_THREAD_LOCK
|
|
|
|
) {
|
|
|
|
// spin
|
|
|
|
// TODO use wait and wake
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// already unlocked
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function releaseTwoStateLock(bufferView, lockIndex) {
|
|
|
|
Atomics.store(bufferView, lockIndex, LockStates.UI_THREAD_LOCK); // unlock
|
|
|
|
}
|
|
|
|
|
|
|
|
function tryToAcquireCyclicalLock(bufferView, lockIndex) {
|
2020-07-27 05:21:19 +00:00
|
|
|
let res = Atomics.compareExchange(
|
2020-07-14 19:45:04 +00:00
|
|
|
bufferView,
|
|
|
|
lockIndex,
|
|
|
|
LockStates.READY_FOR_EMUL_THREAD,
|
|
|
|
LockStates.EMUL_THREAD_LOCK
|
|
|
|
);
|
|
|
|
if (res === LockStates.READY_FOR_EMUL_THREAD) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
function releaseCyclicalLock(bufferView, lockIndex) {
|
|
|
|
Atomics.store(bufferView, lockIndex, LockStates.READY_FOR_UI_THREAD); // unlock
|
|
|
|
}
|
|
|
|
|
|
|
|
function acquireInputLock() {
|
|
|
|
return tryToAcquireCyclicalLock(
|
|
|
|
inputBufferView,
|
|
|
|
InputBufferAddresses.globalLockAddr
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function releaseInputLock() {
|
|
|
|
// reset
|
|
|
|
inputBufferView[InputBufferAddresses.mouseMoveFlagAddr] = 0;
|
|
|
|
inputBufferView[InputBufferAddresses.mouseMoveXDeltaAddr] = 0;
|
|
|
|
inputBufferView[InputBufferAddresses.mouseMoveYDeltaAddr] = 0;
|
|
|
|
inputBufferView[InputBufferAddresses.mouseButtonStateAddr] = 0;
|
|
|
|
inputBufferView[InputBufferAddresses.keyEventFlagAddr] = 0;
|
|
|
|
inputBufferView[InputBufferAddresses.keyCodeAddr] = 0;
|
|
|
|
inputBufferView[InputBufferAddresses.keyStateAddr] = 0;
|
|
|
|
|
|
|
|
releaseCyclicalLock(inputBufferView, InputBufferAddresses.globalLockAddr);
|
|
|
|
}
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
let AudioConfig = null;
|
|
|
|
let AudioBufferQueue = [];
|
2020-07-27 22:07:47 +00:00
|
|
|
const userImages = copyUserImages();
|
2020-07-14 19:45:04 +00:00
|
|
|
|
|
|
|
Module = {
|
2020-07-27 22:07:47 +00:00
|
|
|
autoloadFiles: getAutoLoadFiles(userImages),
|
2020-07-14 19:45:04 +00:00
|
|
|
|
2020-07-27 22:07:47 +00:00
|
|
|
userImages: userImages,
|
|
|
|
|
|
|
|
arguments: ["--config", getPrefs(userImages)],
|
2020-07-14 19:45:04 +00:00
|
|
|
canvas: null,
|
|
|
|
|
|
|
|
blit: function blit(bufPtr, width, height, depth, usingPalette) {
|
|
|
|
videoModeBufferView[0] = width;
|
|
|
|
videoModeBufferView[1] = height;
|
|
|
|
videoModeBufferView[2] = depth;
|
|
|
|
videoModeBufferView[3] = usingPalette;
|
2020-07-27 05:21:19 +00:00
|
|
|
let length = width * height * (depth === 32 ? 4 : 1); // 32bpp or 8bpp
|
|
|
|
for (let i = 0; i < length; i++) {
|
2020-07-14 19:45:04 +00:00
|
|
|
screenBufferView[i] = Module.HEAPU8[bufPtr + i];
|
|
|
|
}
|
|
|
|
// releaseTwoStateLock(videoModeBufferView, 9);
|
|
|
|
},
|
|
|
|
|
|
|
|
openAudio: function openAudio(
|
|
|
|
sampleRate,
|
|
|
|
sampleSize,
|
|
|
|
channels,
|
|
|
|
framesPerBuffer
|
|
|
|
) {
|
|
|
|
AudioConfig = {
|
|
|
|
sampleRate: sampleRate,
|
|
|
|
sampleSize: sampleSize,
|
|
|
|
channels: channels,
|
|
|
|
framesPerBuffer: framesPerBuffer,
|
|
|
|
};
|
|
|
|
console.log(AudioConfig);
|
|
|
|
},
|
|
|
|
|
|
|
|
enqueueAudio: function enqueueAudio(bufPtr, nbytes, type) {
|
2020-07-27 05:21:19 +00:00
|
|
|
let newAudio = Module.HEAPU8.slice(bufPtr, bufPtr + nbytes);
|
|
|
|
let writingChunkIndex = nextAudioChunkIndex;
|
|
|
|
let writingChunkAddr =
|
2020-07-14 19:45:04 +00:00
|
|
|
writingChunkIndex * parentConfig.audioBlockChunkSize;
|
|
|
|
|
|
|
|
if (audioDataBufferView[writingChunkAddr] === LockStates.UI_THREAD_LOCK) {
|
|
|
|
console.warn(
|
2020-07-27 02:01:57 +00:00
|
|
|
"worker tried to write audio data to UI-thread-locked chunk",
|
2020-07-14 19:45:04 +00:00
|
|
|
writingChunkIndex
|
|
|
|
);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-27 05:21:19 +00:00
|
|
|
let nextNextChunkIndex = writingChunkIndex + 1;
|
2020-07-14 19:45:04 +00:00
|
|
|
if (
|
|
|
|
nextNextChunkIndex * parentConfig.audioBlockChunkSize >
|
|
|
|
audioDataBufferView.length - 1
|
|
|
|
) {
|
|
|
|
nextNextChunkIndex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
audioDataBufferView[writingChunkAddr + 1] = nextNextChunkIndex;
|
|
|
|
audioDataBufferView.set(newAudio, writingChunkAddr + 2);
|
|
|
|
audioDataBufferView[writingChunkAddr] = LockStates.UI_THREAD_LOCK;
|
|
|
|
|
|
|
|
nextAudioChunkIndex = nextNextChunkIndex;
|
|
|
|
return nbytes;
|
|
|
|
},
|
|
|
|
|
|
|
|
debugPointer: function debugPointer(ptr) {
|
2020-07-27 02:01:57 +00:00
|
|
|
console.log("debugPointer", ptr);
|
2020-07-14 19:45:04 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
acquireInputLock: acquireInputLock,
|
|
|
|
|
|
|
|
InputBufferAddresses: InputBufferAddresses,
|
|
|
|
|
|
|
|
getInputValue: function getInputValue(addr) {
|
|
|
|
return inputBufferView[addr];
|
|
|
|
},
|
|
|
|
|
|
|
|
totalDependencies: 0,
|
2020-07-27 02:01:57 +00:00
|
|
|
monitorRunDependencies: function (left) {
|
2020-07-14 19:45:04 +00:00
|
|
|
this.totalDependencies = Math.max(this.totalDependencies, left);
|
|
|
|
|
|
|
|
if (left == 0) {
|
2020-07-27 02:01:57 +00:00
|
|
|
postMessage({ type: "emulator_ready" });
|
2020-07-14 19:45:04 +00:00
|
|
|
} else {
|
|
|
|
postMessage({
|
2020-07-27 02:01:57 +00:00
|
|
|
type: "emulator_loading",
|
2020-07-14 19:45:04 +00:00
|
|
|
completion: (this.totalDependencies - left) / this.totalDependencies,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-07-26 16:06:09 +00:00
|
|
|
print: (message) => {
|
2020-07-27 02:01:57 +00:00
|
|
|
console.log(message);
|
|
|
|
|
2020-07-26 16:06:09 +00:00
|
|
|
postMessage({
|
2020-07-27 02:01:57 +00:00
|
|
|
type: "TTY",
|
|
|
|
data: message,
|
2020-07-26 16:06:09 +00:00
|
|
|
});
|
|
|
|
},
|
2020-07-14 19:45:04 +00:00
|
|
|
|
|
|
|
printErr: console.warn.bind(console),
|
|
|
|
|
|
|
|
releaseInputLock: releaseInputLock,
|
|
|
|
};
|
|
|
|
|
|
|
|
addAutoloader(Module);
|
|
|
|
addCustomAsyncInit(Module);
|
|
|
|
|
|
|
|
if (parentConfig.singleThreadedEmscripten) {
|
2020-07-27 02:01:57 +00:00
|
|
|
importScripts("BasiliskII.js");
|
2020-07-14 19:45:04 +00:00
|
|
|
}
|
|
|
|
}
|