Skip to content

Commit

Permalink
Various Emscripten frontend fixes (#199)
Browse files Browse the repository at this point in the history
* Prevent browsers from blocking various shortcuts

* Fix O_TRUNC and localstorage empty files, add unlink and rmdir

* Clean up setattr size implementation

* Improve write performance for large files

* Add more function keys for good measure (Opera uses F10...)

* Remove existence check from unlink (already done by lookup)

* Inline shortcut event handler

* Remove unused code in CompositeStorage set/remove methods

* Increase minimum vfs buffer size
  • Loading branch information
AliceLR committed Oct 1, 2019
1 parent 3953b7e commit 0552ae2
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 11 deletions.
21 changes: 21 additions & 0 deletions arch/emscripten/web/src/index.js
Expand Up @@ -77,6 +77,27 @@ window.MzxrunInitialize = function(options) {
e.preventDefault();
}, false);

/* Disable the default functions for several common MegaZeux shortcuts.
* FIXME: Opera defaults for left alt and F3 can't be disabled this way (as of 63).
*/
document.body.addEventListener('keydown', event => {
let key = event.key.toUpperCase();
if ((event.altKey && (
key == 'C' // Select char
|| key == 'D' // Delete file/directory
|| key == 'N' // New directory
|| key == 'R' // Rename file/directory
))
|| key == 'F1' // Help
|| key == 'F2' // Settings
|| key == 'F3' // Save, Load World
|| key == 'F4' // Load save
|| key == 'F9' // Quicksave
|| key == 'F10' // Quickload Save

) event.preventDefault()
})

try {
if (!options.path) throw "Missing option: path!";
if (!options.files) throw "Missing option: files!";
Expand Down
44 changes: 42 additions & 2 deletions arch/emscripten/web/src/storage.js
Expand Up @@ -60,6 +60,12 @@ class InMemoryStorage {
this.map[key] = value;
return true;
}

remove(key) {
if (this.readonly) return false;
delete this.map[key];
return true;
}
}

class CompositeStorage {
Expand Down Expand Up @@ -98,13 +104,20 @@ class CompositeStorage {
}

set(key, value) {
let promise = Promise.resolve(false);
for (var p = this.providers.length - 1; p >= 0; p--) {
let provider = this.providers[p];
if (provider.set(key, value)) return true;
}
return false;
}

remove(key) {
for (var p = this.providers.length - 1; p >= 0; p--) {
let provider = this.providers[p];
if (provider.remove(key)) return true;
}
return false;
}
}

class AsyncStorageWrapper extends InMemoryStorage {
Expand Down Expand Up @@ -139,6 +152,15 @@ class AsyncStorageWrapper extends InMemoryStorage {
return false;
}
}

remove(key) {
if (super.remove(key)) {
this.parent.remove(key);
return true;
} else {
return false;
}
}
}

class BrowserBackedStorage {
Expand All @@ -153,7 +175,7 @@ class BrowserBackedStorage {

get(key) {
const result = this.storage.getItem(this.prefix + key);
if (result) {
if (result !== null) {
return result.split(",").map(s => parseInt(s));
} else {
return null;
Expand All @@ -175,6 +197,11 @@ class BrowserBackedStorage {
this.storage.setItem(this.prefix + key, value.join(","));
return true;
}

remove(key) {
this.storage.removeItem(this.prefix + key);
return true;
}
}

class IndexedDbBackedAsyncStorage {
Expand Down Expand Up @@ -247,6 +274,19 @@ class IndexedDbBackedAsyncStorage {
}
});
}

remove(key) {
const transaction = this.database.transaction(["files"], "readwrite");
return new Promise((resolve, reject) => {
const request = transaction.objectStore("files").delete(key);
request.onsuccess = event => {
resolve(true);
}
request.onerror = event => {
resolve(false);
}
});
}
}

export function createBrowserBackedStorage(storage, dbName) {
Expand Down
59 changes: 50 additions & 9 deletions arch/emscripten/web/src/storage_emscripten.js
Expand Up @@ -21,6 +21,7 @@
const EPERM = 1;
const ENOENT = 2;
const EINVAL = 22;
const ENOTEMPTY = 39;
const O_CREAT = 0x40;
const O_TRUNC = 0x200;
const S_IFDIR = 0x4000;
Expand All @@ -30,7 +31,7 @@ const S_IFMT = 0xF000;
function vfs_get_type(vfs, path) {
if (path.length == 0) return "dir";
let contents = vfs.get(path);
if (contents) {
if (contents !== null) {
return "file";
} else {
const list = vfs.list(a => a.startsWith(path));
Expand All @@ -39,8 +40,15 @@ function vfs_get_type(vfs, path) {
return "empty";
}

function vfs_next_power_of_two(n) {
var i = 4096;
while (i < n) i *= 2;
return i;
}

function vfs_expand_array(array, newLength) {
if (newLength <= array.length) return array;
newLength = vfs_next_power_of_two(newLength);

var newArrayBuffer = new ArrayBuffer(newLength);
var newArray = new Uint8Array(newArrayBuffer);
Expand Down Expand Up @@ -109,6 +117,18 @@ export function wrapStorageForEmscripten(vfs) {

node.node_ops.setattr = (n, attr) => {
if (attr.mode !== undefined) n.mode = attr.mode;
if (attr.size !== undefined) {
// Used to implement O_TRUNC by the Emscripten FS API.
// console.log("FS setattr size " + n.vfs_path + " " + attr.size);
let old_data = vfs.get(n.vfs_path);
let new_data = new Uint8Array(attr.size);
if (attr.size > 0 && old_data) {
// Note: not sure sizes > 0 will reach here from the FS API...
new_data.set(old_data.slice(0, attr.size));
}
if (!vfs.set(n.vfs_path, new_data))
throw new FS.ErrnoError(EPERM);
}
}

node.stream_ops.llseek = (stream, offset, whence) => {
Expand All @@ -119,7 +139,7 @@ export function wrapStorageForEmscripten(vfs) {
return offset + stream.position;
case 2:
if (stream.vfs_data)
return offset + stream.vfs_data.length;
return offset + stream.vfs_data_length;
else
return offset;
default:
Expand All @@ -141,7 +161,23 @@ export function wrapStorageForEmscripten(vfs) {
return wrap.createNode(parent, '/' + node.vfs_path + name, mode, dev);
};
node.node_ops.rename = (oldNode, newDir, newName) => {
throw "FS TODO rename " + newName;
console.log("FS FIXME rename " + newName);
throw new FS.ErrnoError(EPERM);
};
node.node_ops.unlink = (parent, name) => {
// console.log("FS unlink " + name);
if (!vfs.remove(node.vfs_path + name))
throw new FS.ErrnoError(EPERM);
};
node.node_ops.rmdir = (parent, name) => {
// console.log("FS rmdir " + name);
const path = node.vfs_path + name;
const list = vfs.list(a => a.startsWith(path));
for (var i = 0; i < list.length; i++) {
var entry = list[i].substring(path.length);
if (entry.length != 0)
throw new FS.ErrnoError(ENOTEMPTY);
}
};
node.node_ops.readdir = (node) => {
const path = node.vfs_path;
Expand Down Expand Up @@ -183,18 +219,20 @@ export function wrapStorageForEmscripten(vfs) {
}
}
}
stream.vfs_data_length = stream.vfs_data.length;
}

node.stream_ops.close = (stream) => {
// console.log("FS close " + node.vfs_path);
if (stream.vfs_data && stream.vfs_modified) {
vfs.set(node.vfs_path, stream.vfs_data);
let length = Math.min(stream.vfs_data.length, stream.vfs_data_length);
vfs.set(node.vfs_path, stream.vfs_data.subarray(0, length));
}
}

node.stream_ops.read = (stream, buffer, bufOffset, dataLength, dataOffset) => {
// console.log("FS read " + node.vfs_path + " " + dataOffset + " " + dataLength);
const size = Math.min(dataLength, stream.vfs_data.length - dataOffset);
const size = Math.min(dataLength, stream.vfs_data_length - dataOffset);
if (size > 8 && buffer.set && stream.vfs_data.subarray) {
buffer.set(stream.vfs_data.subarray(dataOffset, dataOffset + size), bufOffset);
} else {
Expand All @@ -208,7 +246,7 @@ export function wrapStorageForEmscripten(vfs) {
node.stream_ops.write = (stream, buffer, bufOffset, dataLength, dataOffset) => {
// console.log("FS write " + node.vfs_path + " " + dataOffset + " " + dataLength);
if (dataLength <= 0) return 0;
stream.vfs_data = vfs_expand_array(stream.vfs_data, Math.max(stream.vfs_data.length, dataOffset + dataLength))
stream.vfs_data = vfs_expand_array(stream.vfs_data, dataOffset + dataLength);
const size = dataLength;
if (size > 8 && stream.vfs_data.set && buffer.subarray) {
stream.vfs_data.set(buffer.subarray(bufOffset, bufOffset + size), dataOffset);
Expand All @@ -217,18 +255,21 @@ export function wrapStorageForEmscripten(vfs) {
stream.vfs_data[dataOffset + i] = buffer[bufOffset + i];
}
}
stream.vfs_data_length = Math.max(dataOffset + dataLength, stream.vfs_data_length);
stream.vfs_modified = true;
return dataLength;
}

node.stream_ops.allocate = (stream, offset, length) => {
const oldLength = stream.vfs_data.length;
stream.vfs_data = vfs_expand_array(stream.vfs_data, offset + length);
if (oldLength != stream.vfs_data.length) stream.vfs_modified = true;
if (offset + length > stream.vfs_data_length) {
stream.vfs_data_length = offset + length;
stream.vfs_modified = true;
}
}
}
return node;
}
};
return wrap;
}
}

0 comments on commit 0552ae2

Please sign in to comment.