lalit / VirtualXPLoader.js
Studytime171's picture
Upload 10 files
d9f8c7b verified
(function (global) {
/**
* Emulator constructor
* @param {object} options
*/
function Halfix(options) {
this.options = options || {};
this.canvas = this.options["canvas"] || null;
this.ctx = this.canvas ? this.canvas.getContext("2d") : null;
this.total_memory = 256;
this.fast = options["fast"] || false;
this.winnt_hack = options["winnt_hack"] || false;
this.reportSpeed = options["reportSpeed"] || function (n) { };
console.log(options.reportSpeed);
this.paused = false;
/** @type {ImageData} */
this.image_data = null;
this.config = this.buildConfiguration();
this.onprogress = options["onprogress"] || function (a, b, c) { };
}
var _cache = [];
/**
* @param {string} name
* @returns {string|null} Value of the parameter or null
*/
Halfix.prototype.getParameterByName = function (name) {
var opt = this.options[name];
if (!opt) return null;
// Check if we have a special kind of object here
var index = _cache.length;
if (opt instanceof ArrayBuffer) {
_cache.push(opt);
// Return that we have this in the cache
return "ab!" + index;
} else if (opt instanceof File) {
_cache.push(opt);
return "file!" + index;
}
return opt;
};
/**
* @param {string} name
* @param {boolean} defaultValue
* @returns {boolean} Value of parameter as a boolean
*/
Halfix.prototype.getBooleanByName = function (name, defaultValue) {
var res = this.getParameterByName(name);
if (res === null) return defaultValue | 0;
else return !!res | 0;
};
/**
* @param {string} name
* @param {number} defaultValue
* @returns {number} Value of parameter as an integer
*/
Halfix.prototype.getIntegerByName = function (name, defaultValue) {
var res = this.getParameterByName(name);
if (res === null) return defaultValue;
else return parseInt(res) | 0;
};
/**
* @param {string} name
* @param {number} defaultValue
* @returns {number} Value of parameter as a double
*/
Halfix.prototype.getDoubleByName = function (name, defaultValue) {
var res = this.getParameterByName(name);
if (res === null) return defaultValue;
else return +parseFloat(res);
};
/**
* Build drive configuration
* @param {array} config
* @param {string} drvid A drive ID (a/b/c/d)
* @param {number} primary Primary? (0=yes, 1=no)
* @param {number} master Master? (0=yes, 1=no)
*/
Halfix.prototype.buildDrive = function (config, drvid, primary, master) {
var hd = this.getParameterByName("hd" + drvid),
cd = this.getParameterByName("cd" + drvid);
if (!hd && !cd)
return;
config.push("[ata" + primary + "-" + master + "]");
if (hd) {
if (hd !== "none") {
config.push("file=" + hd);
config.push("inserted=1");
}
config.push("type=hd");
} else {
if (cd !== "none") {
config.push("file=" + cd);
config.push("inserted=1");
}
config.push("type=cd");
}
};
/**
* @param {string} f name of file
* @param {number} a current position
* @param {number} b end
*/
Halfix.prototype.updateNetworkProgress = function (f, a, b) {
this.onprogress(f, a, b);
};
/**
* @param {number} total Total bytes loaded
*/
Halfix.prototype.updateTotalBytes = function (total) {
};
Halfix.prototype.handleSavestate = function () {
// TODO
};
/**
* Build floppy configuration
*/
Halfix.prototype.buildFloppy = function (config, drvid) {
var fd = this.getParameterByName("fd" + drvid);
if (!fd)
return;
config.push("[fd" + drvid + "]");
config.push("file=" + fd);
config.push("inserted=1");
}
/**
* From the given URL parameters, we build a .conf file for Halfix to consume.
*/
Halfix.prototype.buildConfiguration = function () {
var config = [];
/*
bios_path: getParameterByName("bios") || "bios.bin",
bios: null,
vgabios_path: getParameterByName("vgabios") || "vgabios.bin",
vgabios: null,
hd: [getParameterByName("hda"), getParameterByName("hdb"), getParameterByName("hdc"), getParameterByName("hdd")],
cd: [getParameterByName("cda"), getParameterByName("cdb"), getParameterByName("cdc"), getParameterByName("cdd")],
pci: getBooleanByName("pcienabled"),
apic: getBooleanByName("apicenabled"),
acpi: getBooleanByName("apicenabled"),
now: getParameterByName("now") ? parseFloat(getParameterByName("now")) : 1563602400,
mem: getParameterByName("mem") ? parseInt(getParameterByName("mem")) : 32,
vgamem: getParameterByName("vgamem") ? parseInt(getParameterByName("vgamem")) : 32,
fd: [getParameterByName("fda"), getParameterByName("fdb")],
boot: getParameterByName("boot") || "chf" // HDA, FDC, CDROM
*/
config.push("bios=" + (this.getParameterByName("bios") || "bios.bin"));
config.push("vgabios=" + (this.getParameterByName("vgabios") || "vgabios.bin"));
config.push("pci=" + this.getBooleanByName("pcienabled", true));
config.push("apic=" + this.getBooleanByName("apicenabled", true));
config.push("acpi=" + this.getBooleanByName("acpienabled", true));
config.push("pcivga=" + this.getBooleanByName("pcivga", false));
config.push("now=" + this.getDoubleByName("now", new Date().getTime()));
var floppyRequired = (!!this.getParameterByName("fda") || !!this.getParameterByName("fdb")) | 0;
console.log(floppyRequired);
config.push("floppy=" + floppyRequired);
var mem = this.getIntegerByName("mem", 32),
vgamem = this.getIntegerByName("vgamem", 4);
function roundUp(v) {
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
this.total_memory = roundUp(mem + 32 + vgamem) * 1024 * 1024 | 0;
//Module["TOTAL_MEMORY"] = roundUp(mem + 32 + vgamem) * 1024 * 1024 | 0;
config.push("memory=" + mem + "M");
config.push("vgamemory=" + vgamem + "M");
this.buildDrive(config, "a", 0, "master");
this.buildDrive(config, "b", 0, "slave");
this.buildDrive(config, "c", 1, "master");
this.buildDrive(config, "d", 1, "slave");
this.buildFloppy(config, "a");
this.buildFloppy(config, "b");
config.push("[boot]");
var bootOrder = this.getParameterByName("boot") || "hcf";
config.push("a=" + bootOrder[0] + "d");
config.push("b=" + bootOrder[1] + "d");
config.push("c=" + bootOrder[2] + "d");
config.push("[cpu]");
config.push("cpuid_limit_winnt=" + (this.winnt_hack ? "1" : "0"));
config.push(""); // Trailing empty line
return config.join("\n");
}
Halfix.prototype["send_ctrlaltdel"] = function () {
send_ctrlaltdel(1);
send_ctrlaltdel(0);
};
function loadFiles(paths, cb, gz) {
var resultCounter = paths.length | 0,
results = [];
inLoading = true;
for (var i = 0; i < paths.length; i = i + 1 | 0) {
(function () {
// Save some state information inside the closure.
var xhr = new XMLHttpRequest(),
idx = i,
lastProgress = 0;
var path = paths[i] + (gz ? ".gz" : "");
xhr.open("GET", paths[i] + (gz ? ".gz" : ""));
xhr.onprogress = function (e) {
if (e.lengthComputable) {
_halfix.updateNetworkProgress(path, e.loaded, e.total);
lastProgress = now;
}
};
xhr.responseType = "arraybuffer";
xhr.onload = function () {
if (!gz)
results[idx] = new Uint8Array(xhr.response);
else
results[idx] = pako.inflate(new Uint8Array(xhr.response));
resultCounter = resultCounter - 1 | 0;
_halfix.updateTotalBytes(xhr.response.byteLength | 0);
if (resultCounter === 0) {
cb(null, results);
inLoading = false;
// If we have requested a savestate, then create it now.
_halfix.handleSavestate();
}
};
xhr.onerror = function (e) {
alert("Unable to load file");
cb(e, null);
};
xhr.send();
})();
}
}
Halfix.prototype.updateScreen = function () {
if (this.ctx)
this.ctx.putImageData(this.image_data, 0, 0);
};
var _loaded = false;
// ========================================================================
// Public API
// ========================================================================
/**
* Get configuration file
* @returns {string} Configuration text
*/
Halfix.prototype["getConfiguration"] = function () {
return this.config;
};
/** @type {Halfix} */
var _halfix = null;
var init_cb = null;
/**
* Load and initialize emulator instance
* @param {function(Error, object)} cb Callback
*/
Halfix.prototype["init"] = function (cb) {
// Only one instance can be loaded at a time, unfortunately
if (_loaded) cb(new Error("Already initialized"), null);
_loaded = true;
// Save our Halfix instance for later
_halfix = this;
// Set up our module instance
global["Module"]["canvas"] = this.canvas;
global["Module"]["TOTAL_MEMORY"] = this.total_memory;
init_cb = cb;
// Load emulator
var script = document.createElement("script");
script.src = this.getParameterByName("emulator") || "VirtualXP.js";
document.head.appendChild(script);
};
var savestate_files = {};
function u8tostr(u) {
var str = "";
for (var i = 0; i < u.length; i = i + 1 | 0)str += String.fromCharCode(u[i]);
return str;
}
/**
* Load savestate from directory
* @param {string} statepath
* @param {function} cb
*/
Halfix.prototype["loadStateXHR"] = function (statepath, cb) {
loadFiles([
statepath + "/state.bin",
statepath + "/ram",
statepath + "/vram",
statepath + "/diskinfo.json"], function (err, data) {
if (err) throw err;
savestate_files["/state.bin"] = data[0];
savestate_files["/ram"] = data[1];
savestate_files["/vram"] = data[2];
savestate_files["/diskinfo.json"] = JSON.parse(u8tostr(data[3]));
wrap("emscripten_load_state")();
delete data[3]; // try to get this gc'ed
cb();
}, true);
};
/**
* Pause the emulator
* @param {boolean} paused
*/
Halfix.prototype["pause"] = function (paused) {
this.paused = paused;
};
/**
* Send a fullscreen request to the brower.
*/
Halfix.prototype["fullscreen"] = function () {
Module["requestFullscreen"]();
};
var cyclebase = 0;
function run_again(me, x) {
if (requests_in_progress !== 0) {
setTimeout(function () { run_again(me, x); }, x)
} else
me["run"]();
}
Halfix.prototype["run"] = function () {
if (this.paused) return;
try {
var x = run();
var temp;
var elapsed = (temp = new Date().getTime()) - now;
if (elapsed >= 1000) {
var curcycles = cycles();
this.reportSpeed(((curcycles - cyclebase) / (elapsed) / (1000)).toFixed(2));
//console.log(((curcycles - cyclebase) / (elapsed) / (1000)).toFixed(2));
//$("speed").innerHTML = ((curcycles - cyclebase) / (elapsed) / (1000)).toFixed(2);
cyclebase = curcycles;
now = temp;
}
var me = this;
setTimeout(function () {
run_again(me, x);
}, x);
} catch (e) {
$("error").innerHTML = "Exception thrown -- see JavaScript console";
$("messages").innerHTML = e.toString() + "<br />" + e.stack;
failed = true;
console.log(e);
throw e;
}
};
// ========================================================================
// Emscripten support code
// ========================================================================
var dynCall_vii, send_ctrlaltdel;
var cycles, now;
function run_wrapper2() {
wrap("emscripten_init")();
now = new Date().getTime();
cycles = wrap("emscripten_get_cycles");
get_now = wrap("emscripten_get_now");
dynCall_vii = wrap("emscripten_dyncall_vii");
run = wrap("emscripten_run");
send_ctrlaltdel = wrap("display_send_ctrl_alt_del");
wrap("emscripten_set_fast")(_halfix.fast);
init_cb();
}
// Initialize module instance
global["Module"] = {};
// Set up some stuff that we might find helpful
const SAVE_LOGS = true;
var arr = [];
Module["printErr"] = function (ln) { if (SAVE_LOGS) arr.push(ln); };
function save(filename, data) {
var blob = new Blob([data], { type: 'text/csv' });
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename);
}
else {
var elem = window.document.createElement('a');
elem.href = window.URL.createObjectURL(blob);
elem.download = filename;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
}
}
function saveLog() {
save("test.txt", arr.join("\n"));
}
window["saveLog"] = saveLog;
Module["print"] = function (ln) { console.log(ln); };
global["update_screen"] = function () {
_halfix.updateScreen();
};
var requests_in_progress = 0;
/**
* @param {number} lenptr Pointer to length (type: int*)
* @param {number} dataptr Pointer to allocated data (type: void**)
* @param {number} Pointer to path (type: char*)
*/
global["load_file_xhr"] = function (lenptr, dataptr, path) {
var pathstr = readstr(path);
var cb = function (err, data) {
if (err) throw err;
var destination = Module["_emscripten_alloc"](data.length, 4096);
memcpy(destination, data);
i32[lenptr >> 2] = data.length;
i32[dataptr >> 2] = destination;
requests_in_progress = requests_in_progress - 1 | 0;
if (requests_in_progress === 0) run_wrapper2();
};
// Increment requests in progress by one
requests_in_progress = requests_in_progress + 1 | 0;
if (pathstr.indexOf("!") !== -1) {
// If the user specified an arraybuffer or a file
var pathparts = pathstr.split("!");
var data = _cache[parseInt(pathparts[1])];
/** @type {WholeFileLoader} */
var driver = new xhr_replacements[pathparts[0]](data);
driver.load(cb);
} else loadFiles([pathstr], function (err, datas) {
cb(err, datas[0]);
}, false);
};
/**
* @param {number} fbptr Pointer to framebuffer information
* @param {number} x The width of the window
* @param {number} y The height of the window
*/
global["update_size"] = function (fbptr, x, y) {
if (x == 0 || y == 0) return; // Don't do anything if x or y is zero (VGA resizing sometimes gives weird sizes)
Module["canvas"].width = x;
Module["canvas"].height = y;
_halfix.image_data = new ImageData(new Uint8ClampedArray(Module["HEAPU8"].buffer, fbptr, (x * y) << 2), x, y);
};
Module["onRuntimeInitialized"] = function () {
// Initialize runtime
u8 = Module["HEAPU8"];
u16 = Module["HEAPU16"];
i32 = Module["HEAP32"];
// Get pointer to configuration
var pc = Module["_emscripten_get_pc_config"]();
var cfg = _halfix.getConfiguration();
var area = alloc(cfg.length + 1);
strcpy(area, cfg);
Module["_parse_cfg"](pc, area);
var fast = _halfix.getBooleanByName("fast", false);
Module["_emscripten_set_fast"](fast);
// The story continues in global["load_file_xhr"], which loads the BIOS/VGA BIOS files
// Run some primitive garbage collection
gc();
};
global["drives"] = [];
global["drive_init"] = function (info_ptr, path, id) {
var p = readstr(path), image;
if (p.indexOf("!") !== -1) {
var chunks = p.split("!");
image = new image_backends[chunks[0]](_cache[parseInt(chunks[1]) | 0]);
} else
image = new XHRImage();
requests_in_progress = requests_in_progress + 1 | 0;
image.init(p, function (err, data) {
if (err) throw err;
var dataptr = alloc(data.length), strptr = alloc(p.length + 1);
memcpy(dataptr, data);
strcpy(strptr, p);
wrap("drive_emscripten_init")(info_ptr, strptr, dataptr, id);
gc();
global["drives"][id] = image;
requests_in_progress = requests_in_progress - 1 | 0;
if (requests_in_progress === 0) run_wrapper2();
});
};
// ========================================================================
// Data reading functions
// ========================================================================
/**
* @constructor
*/
function WholeFileLoader() { }
/**
* Load a file
* @param {function} cb Callback
*/
WholeFileLoader.prototype.load = function (cb) {
throw new Error("requires implementation");
};
/**
* @extends WholeFileLoader
* @constructor
* @param {ArrayBuffer} data
*/
function ArrayBufferLoader(data) {
this.data = data;
}
ArrayBufferLoader.prototype = new WholeFileLoader();
ArrayBufferLoader.prototype.load = function (cb) {
cb(null, new Uint8Array(this.data));
};
/**
* @extends WholeFileLoader
* @constructor
* @param {File} file
*/
function FileReaderLoader(file) {
this.file = file;
}
FileReaderLoader.prototype = new WholeFileLoader();
FileReaderLoader.prototype.load = function (cb) {
var fr = new FileReader();
fr.onload = function () {
cb(null, new Uint8Array(fr.result));
};
fr.onerror = function (e) {
cb(e, null);
};
fr.onabort = function () {
cb(new Error("filereader aborted"), null);
};
fr.readAsArrayBuffer(this.file);
};
// see load_file_xhr
var xhr_replacements = {
"file": FileReaderLoader,
"ab": ArrayBufferLoader
};
/**
* Generic hard drive image. All you have to do is fill in
* @constructor
*/
function HardDriveImage() {
/** @type {Uint8Array[]} */
this.blocks = [];
/** @type {string[]} */
this.request_queue = [];
/** @type {number[]} */
this.request_queue_ids = [];
/** @type {number} */
this.cb = 0;
/** @type {number} */
this.arg1 = 0;
}
/**
* Adds a block to the cache. Called on IDE writes, typically
*
* @param {number} id
* @param {number} offset Offset in memory to read from
* @param {number} length
*/
HardDriveImage.prototype["addCache"] = function (id, offset, length) {
this.blocks[id] = u8.slice(offset, length + offset | 0);
};
/**
* Reads a section of a block from the cache
*
* See src/drive.c: drive_read_block_internal
*
* @param {number} id Block ID
* @param {number} buffer Position in the buffer
* @param {number} offset Offset in the block to read from
* @param {number} length Number of bytes to read
*/
HardDriveImage.prototype["readCache"] = function (id, buffer, offset, length) {
id = id | 0;
if (!this.blocks[id]) {
//printElt.value += "[JSError] readCache(id=0x" + id.toString(16) + ", buffer=0x" + buffer.toString(16) + ", length=0x" + buffer.toString(16) + ")\n";
return 1; // No block here with that data.
}
var buf = this.blocks[id].subarray(offset, length + offset | 0);
if (buf.length > length) throw new Error("Block too long");
u8.set(this.blocks[id].subarray(offset, length + offset | 0), buffer);
return 0;
};
/**
* Writes a section of a block with some data
*
* See src/drive.c: drive_write_block_internal
*
* @param {number} id Block ID
* @param {number} buffer Position in the buffer
* @param {number} offset Offset in the block to read from
* @param {number} length Number of bytes to read
*/
HardDriveImage.prototype["writeCache"] = function (id, buffer, offset, length) {
id = id | 0;
if (!this.blocks[id]) {
//printElt.value += "[JSError] writeCache(id=0x" + id.toString(16) + ", buffer=0x" + buffer.toString(16) + ", length=0x" + buffer.toString(16) + ")\n";
return 1; // No block here with that data.
}
var buf = u8.subarray(buffer, length + buffer | 0);
if (buf.length > length) throw new Error("Block too long");
this.blocks[id].set(buf, offset);
return 0;
};
/**
* Queues a memory read. Useful for multi-block reads
*
* @param {number} str Pointer to URL
* @param {number} id Block ID to store it in
*/
HardDriveImage.prototype["readQueue"] = function (str, id) {
this.request_queue.push(readstr(str));
this.request_queue_ids.push(id);
};
/**
* Runs all requests simultaneously.
*
* @param {number} cb Callback pointer
* @param {number} arg1 Callback argument
*/
HardDriveImage.prototype["flushReadQueue"] = function (cb, arg1) {
/** @type {HardDriveImage} */
var me = this;
this.cb = cb;
this.arg1 = arg1;
this.load(this.request_queue, function (err, data) {
if (err) throw err;
var rql = me.request_queue.length;
for (var i = 0; i < rql; i = i + 1 | 0)
me.blocks[me.request_queue_ids[i]] = data[i];
// Empty request queue
me.request_queue = [];
me.request_queue_ids = [];
me.callback(0);
}, true);
};
/**
* Call an Emscripten callback
* @type {number} res
*/
HardDriveImage.prototype.callback = function (res) {
fptr_vii(this.cb | 0, this.arg1 | 0, res | 0);
};
/**
* @param {string[]} paths
* @param {function(object,Uint8Array[])} cb
*
* Note that all Uint8Arrays passed back to cb MUST be BLOCK_SIZE bytes long
*/
HardDriveImage.prototype.load = function (reqs, cb) {
throw new Error("implement me");
};
/**
* Initialize hard drive image
* @param {string} arg
* @param {function(Uint8Array)} cb
*/
HardDriveImage.prototype.init = function (arg, cb) {
throw new Error("implement me");
};
/**
* Convert a URL (i.e. os2/blk0000005a.bin) into a number (i.e. 0x5a)
* @param {string} str
* @return {number}
*/
function _url_to_blkid(str) {
var parts = str.match(/blk([0-9a-f]{8})\.bin/);
return parseInt(parts[1], 16) >>> 0;
}
/**
* Create an "info.dat" file
* @param {number} size
* @param {number} blksize
* @returns {Uint8Array} The data that would have been contained in info.dat
*/
function _construct_info(size, blksize) {
var i32 = new Int32Array(2);
i32[0] = size;
i32[1] = blksize;
return new Uint8Array(i32.buffer);
}
/**
* ArrayBuffer-backed image
* @param {ArrayBuffer} ab
* @constructor
* @extends HardDriveImage
*/
function ArrayBufferImage(ab) {
this.data = new Uint8Array(ab);
}
ArrayBufferImage.prototype = new HardDriveImage();
/**
* @param {string[]} paths
* @param {function(object,Uint8Array[])} cb
*/
ArrayBufferImage.prototype.load = function (reqs, cb) {
var data = [];
for (var i = 0; i < reqs.length; i = i + 1 | 0) {
// note to self: Math.log(256*1024)/Math.log(2) === 18
var blockoffs = (_url_to_blkid(i) << 18) >>> 0;
data[i] = this.data.slice(blockoffs, (blockoffs + (256 << 10)) >>> 0);
}
setTimeout(function () {
cb(null, data);
}, 0);
};
ArrayBufferImage.prototype.init = function (arg, cb) {
var data = _construct_info(this.data.byteLength, 256 << 10);
setTimeout(function () {
cb(null, data);
}, 0);
};
/**
* File API-backed image
* @param {File} f
* @constructor
* @extends HardDriveImage
*/
function FileImage(f) {
this.file = finalResponse;
}
FileImage.prototype = new HardDriveImage();
FileImage.prototype.load = function (reqs, cb) {
console.log(reqs);
// Ensure that loads are in order and consecutive, which speeds things up
var blockBase = _url_to_blkid(reqs[0]);
for (var i = 1; i < reqs.length; i = i + 1 | 0)
if ((_url_to_blkid(reqs[i]) - i | 0) !== blockBase) throw new Error("non-consecutive reads");
var blocks = reqs.length;
/** @type {File} */
var fileslice = this.file.slice((blockBase << 18) >>> 0, ((blockBase + blocks) << 18) >>> 0);
var fr = new FileReader();
fr.onload = function () {
var arr = [];
for (var i = 0; i < reqs.length; i = i + 1 | 0) {
// Slice a 256 KB chunk of the file
arr.push(new Uint8Array(fr.result.slice(i << 18, (i + 1) << 18)));
}
cb(null, arr);
};
fr.onerror = function (e) {
cb(e, null);
};
fr.onabort = function () {
cb(new Error("filereader aborted"), null);
};
fr.readAsArrayBuffer(fileslice);
};
FileImage.prototype.init = function (arg, cb) {
var data = _construct_info(this.file.size, 256 << 10);
setTimeout(function () {
cb(null, data);
}, 0);
};
/**
* XHR-backed image
* @constructor
* @extends HardDriveImage
*/
function XHRImage() {
}
XHRImage.prototype = new HardDriveImage();
XHRImage.prototype.load = function (reqs, cb) {
loadFiles(reqs, cb, true);
};
XHRImage.prototype.init = function (arg, cb) {
loadFiles([join_path(arg, "info.dat")], function (err, data) {
if (err) throw err;
cb(null, data[0]);
});
};
var image_backends = {
"file": FileImage,
"ab": ArrayBufferImage
};
// ========================================================================
// Useful functions
// ========================================================================
var _allocs = [];
/**
* Allocates a patch of memory in Emscripten. Returns the address pointer
* @param {number} size
* @return {number} Address
*/
function alloc(size) {
var n = Module["_malloc"](size);
_allocs.push(n);
return n;
}
var u8 = null, u16 = null, i32 = null;
/**
* Copy a string into memory
* @param {number} dest
* @param {string} src
*/
function strcpy(dest, src) {
var srclen = src.length | 0;
for (var i = 0; i < srclen; i = i + 1 | 0)
u8[i + dest | 0] = src.charCodeAt(i);
u8[dest + srclen | 0] = 0; // End with NULL terminator
}
/**
* Copy a Uint8Array into memory
* @param {number} dest
* @param {Uint8Array} src
*/
function memcpy(dest, src) {
var srclen = src.length | 0;
for (var i = 0; i < srclen; i = i + 1 | 0)
u8[i + dest | 0] = src[i | 0];
}
/**
* Frees every single patch of memory we have reserved with alloc()
*/
function gc() {
var free = Module["_free"];
for (var i = 0; i < _allocs.length; i = i + 1 | 0) free(_allocs[i]);
_allocs = [];
}
/**
* Call an Emscripten function pointer with the signature void func(int, int);
* @param {number} cb The function pointer itself
* @param {number} cb_ptr The first argument
* @param {number} arg2 The second argument
*/
function fptr_vii(cb, cb_ptr, arg2) {
dynCall_vii(cb, cb_ptr, arg2);
}
/**
* Copy a string into JavaScript
* @param {number} src
*/
function readstr(src) {
var str = "";
while (u8[src] !== 0) {
str += String.fromCharCode(u8[src]);
src = src + 1 | 0;
}
return str;
}
// Returns a pointer to an Emscripten-compiled function
function wrap(nm) {
return Module["_" + nm];
}
/**
* Join two fragments of a path together
* @param {string} a The first part of the path
* @param {string} b The second part of the path
*/
function join_path(a, b) {
if (b.charAt(0) !== "/")
b = "/" + b;
if (a.charAt(a.length - 1 | 0) === "/")
a = a.substring(0, a.length - 1 | 0);
return a + b; //normalize_path(a + b);
}
// Some more savestate-related functions
/**
* Load file from file cache
* @param {number} pathstr Pointer to path string
* @param {number} addr Address to load the data
*/
window["loadFile"] = function (pathstr, addr) {
var path = readstr(pathstr);
var data = savestate_files[path];
if (!data) throw new Error("ENOENT: " + path);
memcpy(addr, data);
return addr;
};
/**
* Load file from file cache and allocate a buffer to store it in.
* It is the responsibility of the caller to free the memory.
* @param {number} pathstr Pointer to path string
* @param {number} addr Address to load the data
*/
window["loadFile2"] = function (pathstr, addr) {
var path = readstr(pathstr);
var data = savestate_files[path];
if (!data) throw new Error("ENOENT: " + path);
var len = data.length;
var addr = alloc(len);
_allocs.pop();
memcpy(addr, data);
console.log(path, data, addr);
return addr;
};
if (typeof module !== "undefined" && module["exports"])
module["exports"] = Halfix;
else
global["Halfix"] = Halfix;
})(typeof window !== "undefined" ? window : global);