Disk in current drive can now be saved as an IMG file, and groundwork has been laid for saving an entire machine

This commit is contained in:
Jeff Parsons 2016-02-12 14:14:56 -08:00
commit 9c2ec7a20b
17 changed files with 1684 additions and 1549 deletions

View file

@ -1198,6 +1198,9 @@ Disk.prototype.doneLoad = function(sURL, sDiskData, nErrorCode)
if (DEBUG && this.messageEnabled()) {
this.printMessage('doneLoad("' + this.sDiskPath + '")');
}
Component.addMachineResource(this.controller.idMachine, sURL, sDiskData);
try {
/*
* The following code was a hack to turn on write-protection for a disk image if there was
@ -1839,55 +1842,22 @@ Disk.prototype.getClusterEntry = function(dir, iCluster, iByte)
*
* @this {Disk}
* @param {number} pba (physical block address)
* @return {Object} sector
* @return {Object|null} sector
*/
Disk.prototype.getSector = function(pba)
{
var nSectorsPerCylinder = this.nHeads * this.nSectors;
var iCylinder = (pba / nSectorsPerCylinder) | 0;
this.assert(iCylinder < this.nCylinders);
var nSectorsRemaining = (pba % nSectorsPerCylinder);
var iHead = (nSectorsRemaining / this.nSectors) | 0;
/*
* PBA numbers are 0-based, but the sector numbers in CHS addressing are 1-based, so add one to iSector
*/
var iSector = (nSectorsRemaining % this.nSectors) + 1;
return this.seek(iCylinder, iHead, iSector);
};
/**
* updateSector(file, pba, off)
*
* Like getSector(), this must convert a PBA into CHS values; consider factoring that conversion code out.
*
* @this {Disk}
* @param {Object} file
* @param {number} pba (physical block address from the file's apba)
* @param {number} off (file offset corresponding to the given pba of the given file)
* @return {boolean} true if successfully updated, false if not
*/
Disk.prototype.updateSector = function(file, pba, off)
{
var nSectorsPerCylinder = this.nHeads * this.nSectors;
var iCylinder = (pba / nSectorsPerCylinder) | 0;
var nSectorsRemaining = (pba % nSectorsPerCylinder);
var iHead = (nSectorsRemaining / this.nSectors) | 0;
var iSector = (nSectorsRemaining % this.nSectors);
var cylinder, head, sector;
if ((cylinder = this.aDiskData[iCylinder]) && (head = cylinder[iHead]) && (sector = head[iSector])) {
this.assert(sector['sector'] == iSector +1);
if (sector['file']) {
if (DEBUG && this.messageEnabled()) {
this.printMessage('"' + sector['file'].sPath + '" cross-linked at offset ' + sector['file'].offFile + ' with "' + file.sPath + '" at offset ' + off);
}
return false;
}
sector['file'] = file;
sector.offFile = off;
return true;
if (iCylinder < this.nCylinders) {
var nSectorsRemaining = (pba % nSectorsPerCylinder);
var iHead = (nSectorsRemaining / this.nSectors) | 0;
/*
* PBA numbers are 0-based, but the sector numbers in CHS addressing are 1-based, so add one to iSector
*/
var iSector = (nSectorsRemaining % this.nSectors) + 1;
return this.seek(iCylinder, iHead, iSector);
}
if (DEBUG && this.messageEnabled()) this.printMessage("unable to map PBA " + pba + " to CHS");
return false;
return null;
};
/**
@ -1943,6 +1913,41 @@ Disk.prototype.getSectorString = function(sector, off, len)
return s;
};
/**
* updateSector(file, pba, off)
*
* Like getSector(), this must convert a PBA into CHS values; consider factoring that conversion code out.
*
* @this {Disk}
* @param {Object} file
* @param {number} pba (physical block address from the file's apba)
* @param {number} off (file offset corresponding to the given pba of the given file)
* @return {boolean} true if successfully updated, false if not
*/
Disk.prototype.updateSector = function(file, pba, off)
{
var nSectorsPerCylinder = this.nHeads * this.nSectors;
var iCylinder = (pba / nSectorsPerCylinder) | 0;
var nSectorsRemaining = (pba % nSectorsPerCylinder);
var iHead = (nSectorsRemaining / this.nSectors) | 0;
var iSector = (nSectorsRemaining % this.nSectors);
var cylinder, head, sector;
if ((cylinder = this.aDiskData[iCylinder]) && (head = cylinder[iHead]) && (sector = head[iSector])) {
this.assert(sector['sector'] == iSector +1);
if (sector['file']) {
if (DEBUG && this.messageEnabled()) {
this.printMessage('"' + sector['file'].sPath + '" cross-linked at offset ' + sector['file'].offFile + ' with "' + file.sPath + '" at offset ' + off);
}
return false;
}
sector['file'] = file;
sector.offFile = off;
return true;
}
if (DEBUG && this.messageEnabled()) this.printMessage("unable to map PBA " + pba + " to CHS");
return false;
};
/**
* initSector(sector, iCylinder, iHead, iSector, cbSector, dwPattern)
*
@ -2497,6 +2502,7 @@ Disk.prototype.write = function(sector, ibSector, b)
var dwPattern = sector['pattern'];
var idw = ibSector >> 2;
var nShift = (ibSector & 0x3) << 3;
/*
* Ensure every byte up to the specified byte is properly initialized.
*/
@ -2520,6 +2526,26 @@ Disk.prototype.write = function(sector, ibSector, b)
return null;
};
/**
* encodeAsBase64()
*
* @this {Disk}
* @return {string}
*/
Disk.prototype.encodeAsBase64 = function()
{
/*
* Gross, but simple; more importantly, it works -- at least for disks of typical floppy magnitude.
*/
var s = "", pba = 0, sector;
while ((sector = this.getSector(pba++))) {
for (var off = 0, len = sector['length']; off < len; off++) {
s += String.fromCharCode(this.getSectorData(sector, off, 1));
}
}
return btoa(s);
};
/**
* save()
*

View file

@ -467,6 +467,37 @@ FDC.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
};
return true;
case "saveDrive":
this.bindings[sBinding] = control;
control.onclick = function onClickLoadDrive(event) {
var controlDrives = fdc.bindings["listDrives"];
if (controlDrives && controlDrives.options && fdc.aDrives) {
var iDriveSelected = str.parseInt(controlDrives.value, 10);
var drive = fdc.aDrives[iDriveSelected];
if (drive) {
if (drive.disk) {
if (DEBUG) fdc.println("saving disk " + drive.disk.sDiskPath + "...");
var uri = "data:application/octet-stream;base64," + drive.disk.encodeAsBase64();
var link = document.createElement('a');
if (typeof link.download == 'string') {
link.href = uri;
link.download = drive.disk.sDiskFile.replace(".json", ".img");
document.body.appendChild(link); // Firefox requires the link to be in the body (?)
link.click();
document.body.removeChild(link);
} else {
window.open(uri);
}
} else {
fdc.notice("No disk loaded in drive");
}
} else {
fdc.notice("No drive selected");
}
}
};
return true;
case "mountDrive":
if (this.fLocalDisks) {
this.bindings[sBinding] = control;
@ -1229,10 +1260,10 @@ FDC.prototype.loadSelectedDrive = function(sDisketteName, sDiskettePath, file)
sDiskettePath = window.prompt("Enter the URL of a remote disk image.", "") || "";
if (!sDiskettePath) return;
sDisketteName = str.getBaseName(sDiskettePath);
this.println("Attempting to load " + sDiskettePath + " as \"" + sDisketteName + "\"");
if (DEBUG) this.println("Attempting to load " + sDiskettePath + " as \"" + sDisketteName + "\"");
}
this.println("loading disk " + sDiskettePath + "...");
if (DEBUG) this.println("loading disk " + sDiskettePath + "...");
while (this.loadDiskette(iDrive, sDisketteName, sDiskettePath, false, file)) {
if (!window.confirm("Click OK to reload the original disk.\n(WARNING: All disk changes will be discarded)")) {

View file

@ -225,6 +225,9 @@ ROM.prototype.doneLoad = function(sURL, sROMData, nErrorCode)
this.notice("Unable to load system ROM (error " + nErrorCode + ": " + sURL + ")");
return;
}
Component.addMachineResource(this.idMachine, sURL, sROMData);
if (sROMData.charAt(0) == "[" || sROMData.charAt(0) == "{") {
try {
/*

View file

@ -3724,6 +3724,9 @@ Video.prototype.doneLoad = function(sURL, sFontData, nErrorCode)
this.notice("Unable to load font ROM (error " + nErrorCode + ": " + sURL + ")");
return;
}
Component.addMachineResource(this.idMachine, sURL, sFontData);
try {
/*
* The most likely source of any exception will be right here, where we're parsing the JSON-encoded data.

View file

@ -229,6 +229,7 @@
<xsl:when test="$url != ''"><div class="{$APPCLASS}-reference">[<a href="{$url}">XML</a>]</div></xsl:when>
<xsl:otherwise/>
</xsl:choose>
<xsl:if test="$MACHINECLASS = 'pc'"><div class="{$APPCLASS}-reference" style="padding-left:8px">[<a href="#" onclick="savePC('{$machine}');">Save Machine</a>]</div></xsl:if>
<div class="{$APPCLASS}-copyright">
<a href="http://{$SITEHOST}" target="_blank">PCjs</a> v<xsl:value-of select="$APPVERSION"/> © 2012-2016 by <a href="http://twitter.com/jeffpar" target="_blank">@jeffpar</a>
</div>