More support for saving entire machines
This commit is contained in:
parent
0a31a64d0c
commit
6bffc69b25
8 changed files with 348 additions and 255 deletions
|
|
@ -259,23 +259,20 @@ Component.addMachineResource = function(idMachine, sName, data)
|
|||
{
|
||||
Component.assert(Component.machines[idMachine]);
|
||||
if (Component.machines[idMachine]) {
|
||||
if (!Component.machines[idMachine][sName]) {
|
||||
Component.machines[idMachine][sName] = [];
|
||||
}
|
||||
Component.machines[idMachine][sName].push(data);
|
||||
Component.assert(Component.machines[idMachine][sName] === undefined);
|
||||
Component.machines[idMachine][sName] = data;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Component.getMachineResources(idMachine, sName)
|
||||
* Component.getMachineResources(idMachine)
|
||||
*
|
||||
* @param {string} idMachine
|
||||
* @param {string} sName (name of the resource)
|
||||
* @return {Array|undefined}
|
||||
* @return {Object|undefined}
|
||||
*/
|
||||
Component.getMachineResources = function(idMachine, sName)
|
||||
Component.getMachineResources = function(idMachine)
|
||||
{
|
||||
return Component.machines[idMachine] && Component.machines[idMachine][sName];
|
||||
return Component.machines[idMachine];
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ function embedMachine(sName, sVersion, idMachine, sXMLFile, sXSLFile, sParms)
|
|||
displayError(sXML);
|
||||
return;
|
||||
}
|
||||
Component.addMachineResource(idMachine, 'xml', sXML);
|
||||
Component.addMachineResource(idMachine, sXMLFile, sXML);
|
||||
/*
|
||||
* Non-COMPILED kludge to extract the version number from the stylesheet path in the machine XML file;
|
||||
* we don't need this code in COMPILED (non-DEBUG) releases, because APPVERSION is hard-coded into them.
|
||||
|
|
@ -395,7 +395,7 @@ function embedMachine(sName, sVersion, idMachine, sXMLFile, sXSLFile, sParms)
|
|||
displayError(sXSL);
|
||||
return;
|
||||
}
|
||||
Component.addMachineResource(idMachine, 'xsl', sXSL);
|
||||
Component.addMachineResource(idMachine, sXSLFile, sXSL);
|
||||
/*
|
||||
* The <machine> template in components.xsl now generates a "machine div" that makes
|
||||
* the div we required the caller of embedMachine() to provide redundant, so instead
|
||||
|
|
@ -501,27 +501,102 @@ function embedPC(idMachine, sXMLFile, sXSLFile, sParms)
|
|||
}
|
||||
|
||||
/**
|
||||
* savePC(idMachine)
|
||||
* savePC(idMachine, sPCJSFile)
|
||||
*
|
||||
* @param {string} idMachine
|
||||
* @param {string} [sPCJSFile]
|
||||
* @return {boolean} true if successful, false if error
|
||||
*/
|
||||
function savePC(idMachine)
|
||||
function savePC(idMachine, sPCJSFile)
|
||||
{
|
||||
var cmp = Component.getComponentByType("Computer", idMachine);
|
||||
var dbg = Component.getComponentByType("Debugger", idMachine);
|
||||
if (cmp) {
|
||||
var sPCJSFile = "/versions/pcjs/" + XMLVERSION + "/pc" + (dbg? "-dbg" : "") + ".js";
|
||||
var sPCJSCode = web.loadResource(sPCJSFile);
|
||||
/*
|
||||
* Next, we need to enumerate the FDC drives and "dump" the disks in them....
|
||||
*/
|
||||
if (!sPCJSFile) sPCJSFile = "/versions/pcjs/" + (XMLVERSION || APPVERSION) + "/pc" + (dbg? "-dbg" : "") + ".js";
|
||||
web.loadResource(sPCJSFile, true, null, null, downloadPC, [idMachine, cmp, dbg]);
|
||||
return true;
|
||||
}
|
||||
web.alertUser("Unable to identify machine '" + idMachine + "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* downloadPC(sURL, sPCJS, nErrorCode, aMachineInfo)
|
||||
*
|
||||
* @param {string} sURL
|
||||
* @param {string} sPCJS
|
||||
* @param {number} nErrorCode
|
||||
* @param {string} aMachineInfo ([0] = idMachine, [1] = Computer component, [2] = Debugger component, if any)
|
||||
*/
|
||||
function downloadPC(sURL, sPCJS, nErrorCode, aMachineInfo)
|
||||
{
|
||||
/*
|
||||
* sPCJS is supposed to contain the entire PCjs script, which has been wrapped with:
|
||||
*
|
||||
* (function(){...
|
||||
*
|
||||
* at the top and:
|
||||
*
|
||||
* ...})();
|
||||
*
|
||||
* at the bottom, thanks to the following Closure Compiler option:
|
||||
*
|
||||
* --output_wrapper "(function(){%output%})();"
|
||||
*
|
||||
* Immediately inside that wrapping, we want to embed all the specified machine's resources, using:
|
||||
*
|
||||
* var resources = {"xml": "...", "xsl": "...", ...};
|
||||
*
|
||||
* Note that the "resources" variable has been added to our externs.js, to prevent it from being renamed
|
||||
* by the Closure Compiler.
|
||||
*/
|
||||
if (sPCJS) {
|
||||
var idMachine = aMachineInfo[0];
|
||||
var matchScript = sPCJS.match(/^(\s*\(function\(\)\{)([\s\S]*)(}\)\(\);\s*)$/);
|
||||
if (matchScript) {
|
||||
var resources = Component.getMachineResources(idMachine), resourcesNew = {};
|
||||
for (var name in resources) {
|
||||
var data = resources[name];
|
||||
if (name == "xml") {
|
||||
/*
|
||||
* Look through this resource for <disk> entries whose paths do not appear as one of the other
|
||||
* machine resources, and remove those entries.
|
||||
*/
|
||||
var matchDisk, reDisk = /[ \t]*<disk [^>]*path=(['"])(.*?)\1.*?<\/disk>\n?/g;
|
||||
while (matchDisk = reDisk.exec(resources[name])) {
|
||||
var path = matchDisk[2];
|
||||
if (path) {
|
||||
if (resources[path]) {
|
||||
console.log("saving disk: '" + path);
|
||||
} else {
|
||||
data = data.replace(matchDisk[0], "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log("saving resource: '" + name + "' (" + data.length + " bytes)");
|
||||
resourcesNew[name] = data;
|
||||
}
|
||||
|
||||
var sResources = JSON.stringify(resourcesNew);
|
||||
sPCJS = matchScript[1] + "var resources=" + sResources + ";" + matchScript[2] + matchScript[3];
|
||||
console.log("saving machine: '" + idMachine + "' (" + sPCJS.length + " bytes)");
|
||||
|
||||
var uri = "data:application/octet-stream;base64," + btoa(sPCJS);
|
||||
var link = document.createElement('a');
|
||||
if (typeof link.download == 'string') {
|
||||
link.href = uri;
|
||||
link.download = str.getBaseName(sURL, true) + ".json";
|
||||
document.body.appendChild(link); // Firefox requires the link to be in the body (?)
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
} else {
|
||||
window.open(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the Closure Compiler from renaming functions we want to export, by adding them
|
||||
* as (named) properties of a global object.
|
||||
|
|
|
|||
|
|
@ -33,5 +33,6 @@
|
|||
"use strict";
|
||||
|
||||
var global;
|
||||
var resources;
|
||||
|
||||
// var webkitAudioContext;
|
||||
|
|
|
|||
|
|
@ -190,7 +190,20 @@ web.notice = function(s, fPrintOnly, id)
|
|||
*/
|
||||
web.loadResource = function(sURL, fAsync, data, componentNotify, fnNotify, pNotify)
|
||||
{
|
||||
fAsync = !!fAsync; // ensure that fAsync is a valid boolean (Internet Explorer xmlHTTP functions insist on it)
|
||||
var nErrorCode = 0;
|
||||
var sURLData = null;
|
||||
|
||||
if (resources && (sURLData = resources[sURL])) {
|
||||
if (fnNotify) {
|
||||
if (!componentNotify) {
|
||||
fnNotify(sURL, sURLData, nErrorCode, pNotify);
|
||||
} else {
|
||||
fnNotify.call(componentNotify, sURL, sURLData, nErrorCode, pNotify);
|
||||
}
|
||||
}
|
||||
return [nErrorCode, sURLData];
|
||||
}
|
||||
|
||||
if (NODE) {
|
||||
/*
|
||||
* We don't even need to load Component, because we can't use any of the code below
|
||||
|
|
@ -201,8 +214,7 @@ web.loadResource = function(sURL, fAsync, data, componentNotify, fnNotify, pNoti
|
|||
var net = require("./netlib");
|
||||
return net.loadResource(sURL, fAsync, data, componentNotify, fnNotify, pNotify);
|
||||
}
|
||||
var nErrorCode = 0;
|
||||
var sURLData = null;
|
||||
|
||||
var xmlHTTP = (window.XMLHttpRequest? new window.XMLHttpRequest() : new window.ActiveXObject("Microsoft.XMLHTTP"));
|
||||
if (fAsync) {
|
||||
xmlHTTP.onreadystatechange = function() {
|
||||
|
|
@ -238,6 +250,7 @@ web.loadResource = function(sURL, fAsync, data, componentNotify, fnNotify, pNoti
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (data) {
|
||||
var sData = "";
|
||||
for (var p in data) {
|
||||
|
|
@ -247,14 +260,15 @@ web.loadResource = function(sURL, fAsync, data, componentNotify, fnNotify, pNoti
|
|||
}
|
||||
sData = sData.replace(/%20/g, '+');
|
||||
if (MAXDEBUG) web.log("web.loadResource(POST " + sURL + "): " + sData.length + " bytes");
|
||||
xmlHTTP.open("POST", sURL, fAsync);
|
||||
xmlHTTP.open("POST", sURL, !!fAsync); // ensure that fAsync is a valid boolean (Internet Explorer xmlHTTP functions insist on it)
|
||||
xmlHTTP.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xmlHTTP.send(sData);
|
||||
} else {
|
||||
if (MAXDEBUG) web.log("web.loadResource(GET " + sURL + ")");
|
||||
xmlHTTP.open("GET", sURL, fAsync);
|
||||
xmlHTTP.open("GET", sURL, !!fAsync); // ensure that fAsync is a valid boolean (Internet Explorer xmlHTTP functions insist on it)
|
||||
xmlHTTP.send();
|
||||
}
|
||||
|
||||
var response = [];
|
||||
if (!fAsync) {
|
||||
sURLData = xmlHTTP.responseText;
|
||||
|
|
|
|||
Loading…
Reference in a new issue