More support for saving entire machines

This commit is contained in:
Jeff Parsons 2016-02-16 18:11:15 -08:00
commit 6bffc69b25
8 changed files with 348 additions and 255 deletions

View file

@ -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];
};
/**

View file

@ -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.

View file

@ -33,5 +33,6 @@
"use strict";
var global;
var resources;
// var webkitAudioContext;

View file

@ -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;