1
1
Fork 0
mirror of https://github.com/boxgaming/qbjs.git synced 2024-09-19 20:14:58 +00:00
qbjs/vfs.js
2022-10-27 09:04:15 -05:00

259 lines
7.5 KiB
JavaScript

let VFS = function() {
this.FILE = Symbol("FILE");
this.DIRECTORY = Symbol("DIRECTORY");
let _root = { toString: function() { return "/"; }};
this.rootDirectory = function() {
return _root;
};
this.reset = function() {
_root = { toString: function() { return "/"; }};
}
this.fullPath = function(node) {
if (node == _root) { return "/" };
var n = node;
var path = "";
while (n.name) {
path = "/" + n.name + path;
n = n.parent;
}
return path;
};
this.getParentPath = function(path) {
var parts = this.tokenizePath(path);
var path = "";
if (parts.length < 2) {
if (parts.isFullpath) { return "/"; }
else { return ""; }
}
for (var i=0; i < parts.length-1; i++) {
path += "/" + parts[i];
}
return path;
};
this.getFileName = function(path) {
var parts = this.tokenizePath(path);
return parts[parts.length-1];
};
this.createFile = function(path, parent) {
if (parent == undefined) { parent = _root; }
let fsnode = { type: this.FILE, name: path, data: new ArrayBuffer(0), parent: parent, toString: function() { return this.name; }};
parent[path] = fsnode;
return fsnode;
};
this.createDirectory = function(path, parent) {
if (parent == undefined) { parent = _root; }
let fsnode = { type: this.DIRECTORY, name: path, parent: parent, toString: function() { return this.name; }};
parent[path] = fsnode;
return fsnode;
};
this.getChildren = function(parent, type) {
if (parent == undefined) { parent = _root; }
let nodes = [];
for (var e in parent) {
if (type == undefined || (parent[e] && parent[e].type == type)) {
if ((parent[e].type == this.FILE || parent[e].type == this.DIRECTORY) && e != "parent") {
nodes.push(parent[e]);
}
}
}
nodes.sort();
return nodes;
};
this.writeText = function(file, text) {
file.data = appendBuffer(file.data, this.textToData(text));
};
this.textToData = function(text) {
let chars = [];
for (let i=0; i < text.length; i++) {
chars.push(text.charCodeAt(i));
}
return new Uint8Array(chars).buffer;
};
this.readLine = function(file, offset) {
if (offset == undefined) { offset = 0; }
if (offset >= file.data.byteLength) {
throw new Error("Input past end of file");
}
let view = new Uint8Array(file.data);
let c = null;
let str = "";
while (offset < file.data.byteLength && c != "\n") {
c = String.fromCharCode(view[offset]);
if (c != "\n") {
str += c;
}
offset++;
}
return str;
};
this.readText = function(file) {
let offset = 0;
let view = new Uint8Array(file.data);
let c = null;
let str = "";
while (offset < file.data.byteLength) {
c = String.fromCharCode(view[offset]);
str += c
offset++;
}
return str;
};
this.writeData = function(file, data, offset) {
if (offset == undefined) { offset = 0; }
let start = file.data.slice(0, offset);
let end = file.data.slice(offset + data.byteLength, file.data.byteLength);
file.data = start;
if (start.byteLength < offset) {
file.data = appendBuffer(file.data, new ArrayBuffer(offset - start.byteLength));
}
file.data = appendBuffer(file.data, data);
file.data = appendBuffer(file.data, end);
};
this.readData = function(file, offset, length) {
return file.data.slice(offset, offset + length);
};
this.getNode = function(path, parent) {
let parts = this.tokenizePath(path);
if (parts.isRoot) {
return _root;
}
if (parts.isFullpath) {
parent = _root;
}
let node = null;
for (let i=0; i < parts.length; i++) {
if (parts[i] == ".") {
// move along, nothing to see here
}
else if (parts[i] == "..") {
if (node.parent == undefined) {
node = _root;
}
else {
node = node.parent;
parent = node;
}
}
else {
node = parent[parts[i]];
parent = node;
if (node == undefined) {
return null;
}
}
}
return node;
};
this.getDataURL = async function(file) {
let blob = new Blob([file.data]);
let dataUrl = await new Promise(r => {let a=new FileReader(); a.onload=r; a.readAsDataURL(blob)}).then(e => e.target.result);
return dataUrl;
};
this.renameNode = function(node, newName) {
// TODO: move the file if the newName includes a path
var parent = node.parent;
parent[node.name] = undefined;
node.name = newName;
parent[node.name] = node;
};
this.removeFile = function(file, parent) {
if (typeof file == "string") {
file = this.getNode(file, parent);
}
if (file && file.type == this.FILE) {
let parent = file.parent;
delete parent[file.name];
}
else {
throw new Error("File not found");
}
};
this.removeDirectory = function(directory, parent) {
if (typeof directory == "string") {
directory = this.getNode(directory, parent);
}
if (directory && directory.type == this.DIRECTORY) {
var childNodes = this.getChildren(directory);
if (childNodes.length == 0) {
let parent = directory.parent;
delete parent[directory.name];
}
else {
// TODO: probably throw an exception
}
}
else {
// TODO: probably throw an exception
}
};
this.tokenizePath = function(path) {
path = path.replaceAll("\\","/");
let parts = path.split("/");
parts.isFullpath = false;
if (path.indexOf("/") == 0) {
parts.isFullpath = true;
}
if (parts[0].match(/[A-Z|a-z]:/)) {
parts.shift();
parts.isFullpath = true;
}
if (parts[0] == "") {
while (parts.length > 0 && parts[0] == "") {
parts.shift();
}
}
if (parts.isFullpath && parts.length == 0) {
parts.isRoot = true;
}
else {
parts.isRoot = false;
}
return parts;
};
function appendBuffer(buffer1, buffer2) {
let tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(new Uint8Array(buffer1), 0);
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
return tmp.buffer;
}
}
if (!ArrayBuffer.prototype.slice) {
ArrayBuffer.prototype.slice = function (start, end) {
let that = new Uint8Array(this);
if (end == undefined) end = that.length;
let result = new ArrayBuffer(end - start);
let resultArray = new Uint8Array(result);
for (let i = 0; i < resultArray.length; i++)
resultArray[i] = that[i + start];
return result;
}
}