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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long