A FAT12 Disk Image Explorer in JavaScript

The JavaScript code below, when run, presents a file upload button. When the user selects a valid FAT12 disk image and uploads it, the contents of the disk’s root directory will be displayed. The user can then click the Download button next to a displayed file to download a copy of that file to their local system. Other buttons make it possible to navigate down into subdirectories and, from there, back up to parent directories.

To see the code in action, copy it into an .html file and open that file in a web browser that runs JavaScript. Or, for an online version, visit https://thiscouldbebetter.neocities.org/fat12diskimage.html.

To test it out, you’ll somehow need to generate a disk image file, which typically use the .img extension. After some struggles trying to do this, I eventually ended up using a product called OSFMount, by PassMark Software, which is available at the URL http://www.osforensics.com/tools/mount-disk-images.html. As near as I can determine from the website, I think certain versions may be free for non-commercial use, though I’m not sure that it won’t start nagging me after some trial period expires.

Anyway, to generate an .img file, I started up OSFMount, clicked the “Mount new…” button, selected the “Empty RAM drive” radio button, specified a disk size of one megabyte, and clicked OK to dismiss the dialog. I then used the Windows Command prompt to format the newly mounted drive by running “format E: /FS:FAT”, responding to the prompts when necessary. (If you try it yourself, please remember to always be extra careful with the “format” command.) Then, back in OSFMount, I right-clicked the mounted drive and selected the “Save to image file…” item from the context menu.

<html>
<body>

<div id="divMain"></div>

<script>

// main

function main()
{
	var divMain = document.getElementById("divMain");

	var labelDiskImageFile = document.createElement("label");
	labelDiskImageFile.innerHTML = "Disk Image File:";
	divMain.appendChild(labelDiskImageFile);

	var inputFile = document.createElement("input");
	inputFile.type = "file";
	inputFile.onchange = handleEventInputFileChanged;
	divMain.appendChild(inputFile);
}

function handleEventInputFileChanged(event)
{
	var inputFile = event.target;
	var fileToLoad = inputFile.files[0];
	FileHelper.loadFileAsBinaryString
	(
		fileToLoad,
		handleEventInputFileChanged_2
	);
}

function handleEventInputFileChanged_2(fileAsBinaryString)
{
	var fileAsBytes = ByteHelper.binaryStringToBytes
	(
		fileAsBinaryString
	);

	var filesystemFAT12 = FilesystemFAT12.fromBytes
	(
		fileAsBytes
	);

	Globals.Instance.initialize(filesystemFAT12);
}

// classes

function Base64Encoder()
{
	// do nothing
}
{
	// static methods

	Base64Encoder.encodeBytes = function(bytesToEncode)
	{
		// Encode each three sets of eight bits (octets, or bytes)
		// as four sets of six bits (sextets, or Base 64 digits)

		var returnString = "";

		var bytesPerSet = 3;
		var base64DigitsPerSet = 4;

		var base64DigitsAsString = 
			"ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
			+ "abcdefghijklmnopqrstuvwxyz"
			+ "0123456789"
			+ "+/";

		var numberOfBytesToEncode = bytesToEncode.length;
		var numberOfFullSets = Math.floor(numberOfBytesToEncode / bytesPerSet);
		var numberOfBytesInFullSets = numberOfFullSets * bytesPerSet;
		var numberOfBytesLeftAtEnd = numberOfBytesToEncode - numberOfBytesInFullSets;

		for (var s = 0; s < numberOfFullSets; s++)
		{
			var b = s * bytesPerSet;

			var valueToEncode = 
				(bytesToEncode[b] << 16)
				| (bytesToEncode[b + 1] << 8)
				| (bytesToEncode[b + 2]);

			returnString += base64DigitsAsString[((valueToEncode & 0xFC0000) >>> 18)];
			returnString += base64DigitsAsString[((valueToEncode & 0x03F000) >>> 12)];
			returnString += base64DigitsAsString[((valueToEncode & 0x000FC0) >>> 6)];
			returnString += base64DigitsAsString[((valueToEncode & 0x00003F))];
		}	

		var b = numberOfFullSets * bytesPerSet;

		if (numberOfBytesLeftAtEnd == 1)
		{
			var valueToEncode = (bytesToEncode[b] << 16);

			returnString += base64DigitsAsString[((valueToEncode & 0xFC0000) >>> 18)];
			returnString += base64DigitsAsString[((valueToEncode & 0x03F000) >>> 12)];
			returnString += "==";
		}		
		else if (numberOfBytesLeftAtEnd == 2)
		{
			var valueToEncode = 
				(bytesToEncode[b] << 16)
				| (bytesToEncode[b + 1] << 8);

			returnString += base64DigitsAsString[((valueToEncode & 0xFC0000) >>> 18)];
			returnString += base64DigitsAsString[((valueToEncode & 0x03F000) >>> 12)];
			returnString += base64DigitsAsString[((valueToEncode & 0x000FC0) >>> 6)];
			returnString += "=";
		}

		return returnString;
	}
}

function BitStream(bytes, seekToEnd)
{
	this.bytes = bytes;
	this.byteOffset = 0;
	this.bitOffsetWithinByte = 0;

	if (seekToEnd == true)
	{
		this.byteOffset = this.bytes.length;
	}

	this.bitOffsetWithinByteReversed =
		BitStream.BitsPerByte 
		- this.bitOffsetWithinByte
		- 1;
}
{
	// constants

	BitStream.BitsPerByte = 8;

	// instance methods

	BitStream.prototype.bitOffsetAdvance = function(numberOfBitsToAdvance)
	{
		var bitsPerByte = BitStream.BitsPerByte;

		var numberOfBytesToAdvance = Math.floor
		(
			numberOfBitsToAdvance / bitsPerByte
		);

		this.byteOffset += numberOfBytesToAdvance;

		this.bitOffsetWithinByte +=
			numberOfBitsToAdvance 
			- (numberOfBytesToAdvance * bitsPerByte);

		if (this.bitOffsetWithinByte >= bitsPerByte)
		{
			this.byteOffset++;
			this.bitOffsetWithinByte -= bitsPerByte;
		}

		this.bitOffsetWithinByteReversed =
			BitStream.BitsPerByte 
			- this.bitOffsetWithinByte
			- 1;		
	}

	BitStream.prototype.byteOffsetAdvanceIfNecessary = function()
	{
		if (this.byteOffset >= this.bytes.length)
		{
			this.bytes.push(0);	
		}
	}

	BitStream.prototype.readBit = function()
	{
		var byteCurrent = this.bytes[this.byteOffset];
		var bitValue = (byteCurrent >> this.bitOffsetWithinByteReversed) & 1;
		this.bitOffsetAdvance(1);
		return bitValue;
	}

	BitStream.prototype.readByte = function()
	{
		return this.readBitsAsInteger(BitStream.BitsPerByte);
	}

	BitStream.prototype.readBytes = function(numberOfBytesToRead)
	{
		var returnValues = [];

		for (var i = 0; i < numberOfBytesToRead; i++)
		{
			returnValues.push(this.readByte());
		}

		return returnValues;
	}

	BitStream.prototype.readFAT12Entry = function()
	{
		// hack

		var returnValue;

		if (this.bitOffsetWithinByte == 0)
		{
			returnValue = 
				this.bytes[this.byteOffset]
				| ((this.bytes[this.byteOffset + 1] & 0xF) << 8);
		
		}
		else
		{
			returnValue = 
				((this.bytes[this.byteOffset + 1] & 0xF) << 8)
				| (this.bytes[this.byteOffset]);
		}

		this.readInteger(12);

		return returnValue;
	}

	BitStream.prototype.readInteger = function(numberOfBitsInInteger)
	{
		var returnValue = 0;

		for (var i = 0; i < numberOfBitsInInteger; i++)
		{
			var iReversed = numberOfBitsInInteger - i - 1;
			var bitValue = this.readBit();
			var bitValueInPlace = (bitValue << iReversed);
			returnValue += bitValueInPlace;
		}

		return returnValue;	
	}

	BitStream.prototype.writeBit = function(bitToWrite)
	{
		this.byteOffsetAdvanceIfNecessary();
		var byteCurrent = this.bytes[this.byteOffset];
		var bitValueInPlace = (bitToWrite << this.bitOffsetWithinByteReversed);
		this.bytes[this.byteOffset] += bitValueInPlace;
		this.bitOffsetAdvance(1);
	}

	BitStream.prototype.writeBits = function(bitsToWrite)
	{
		for (var i = 0; i < bitsToWrite.length; i++)
		{
			this.writebit(bitsToWrite[i]);
		}
	}

	BitStream.prototype.writeIntegerUsingBitWidth = function(integerToWrite, numberOfBitsInInteger)
	{
		for (var i = 0; i < numberOfBitsInInteger; i++)
		{
			var iReversed = numberOfBitsInInteger - i - 1;

			//var bitValue = (integerToWrite >>> iReversed) & 1;

			// hack 
			// Can't simply shift right by iReversed
			// for bit widths greater than 32,
			// because (n >>> 32 == n)!

			var temp = integerToWrite;

			for (var j = 0; j < iReversed; j++)
			{
				temp = temp >>> 1;
			}

			var bitValue = temp & 1;

			this.writeBit(bitValue);
		}	
	}
}

function ByteHelper()
{}
{
	ByteHelper.binaryStringToBytes = function(binaryString)
	{
		var returnValues = [];

		for (var i = 0; i < binaryString.length; i++)
		{
			var byteValue = binaryString.charCodeAt(i);
			returnValues.push(byteValue);
		}

		return returnValues;
	}
}

function ByteStream(bytes)
{
	this.bytes = bytes;
	this.byteIndexCurrent = 0;
}
{
	ByteStream.prototype.hasMoreBytes = function()
	{
		return (this.byteIndexCurrent < this.bytes.length);
	}

	ByteStream.prototype.readByte = function()
	{
		var returnValue = this.bytes[this.byteIndexCurrent];
		this.byteIndexCurrent++;
		return returnValue;
	}

	ByteStream.prototype.readBytes = function(numberOfBytesToRead)
	{	
		var returnValues = [];

		for (var i = 0; i < numberOfBytesToRead; i++)
		{
			var byte = this.readByte();
			returnValues.push(byte);
		}

		return returnValues;
	}

	ByteStream.prototype.readIntegerLE = function(numberOfBytesToRead)
	{
		// little-endian

		var returnValue = 0;

		for (var i = 0; i < numberOfBytesToRead; i++)
		{
			var byte = this.readByte();
			returnValue |= (byte << (i * 8));
		}

		return returnValue;		
	}

	ByteStream.prototype.readString = function(lengthOfString)
	{	
		var returnValue = "";

		var bytes = this.readBytes(lengthOfString);

		for (var i = 0; i < bytes.length; i++)
		{
			var byte = bytes[i];
			returnValue += String.fromCharCode(byte);	
		}

		return returnValue;
	}
}

function FileHelper()
{
	// static class
}
{
	FileHelper.destroyClickedElement = function(event)
	{
		event.target.parentElement.removeChild(event.target);
	}

	FileHelper.loadFileAsBinaryString = function(fileToLoad, callback)
	{
		var fileReader = new FileReader();

		fileReader.onloadend = function(fileLoadedEvent)
		{
			if (fileLoadedEvent.target.readyState == FileReader.DONE)
			{
				var bytesFromFile = fileLoadedEvent.target.result;
			}

			callback(bytesFromFile);
		}
		fileReader.readAsBinaryString(fileToLoad);
	}

	FileHelper.saveBytesToFile = function(bytesToSave, filenameToSaveTo)
	{
		var numberOfBytes = bytesToSave.length;
		var bytesAsArrayBuffer = new ArrayBuffer(numberOfBytes);
		var bytesAsUIntArray = new Uint8Array(bytesAsArrayBuffer);
		for (var i = 0; i < numberOfBytes; i++) 
		{
			bytesAsUIntArray[i] = bytesToSave[i];
		}

		var bytesAsBlob = new Blob
		(
			[ bytesAsArrayBuffer ], 
			{type:'application/type'}
		);

		var downloadLink = document.createElement("a");
		var url = (window.webkitURL != null ? window.webkitURL : window.URL);
		downloadLink.href = url.createObjectURL(bytesAsBlob);
		downloadLink.download = filenameToSaveTo;
		downloadLink.click();
	}

	FileHelper.saveTextAsFile = function(textToWrite, fileNameToSaveAs)
	{
		var textFileAsBlob = new Blob([textToWrite], {type:'text/plain'});

		var downloadLink = document.createElement("a");
		downloadLink.download = fileNameToSaveAs;
		downloadLink.innerHTML = "Download File";
		if (window.webkitURL != null)
		{
			// Chrome allows the link to be clicked
			// without actually adding it to the DOM.
			downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
		}
		else
		{
			// Firefox requires the link to be added to the DOM
			// before it can be clicked.
			downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
			downloadLink.onclick = FileHelper.destroyClickedElement;
			downloadLink.style.display = "none";
			document.body.appendChild(downloadLink);
		}
	
		downloadLink.click();
	}
}

function FilesystemFAT12
(
	bytesPerSector,
	sectorsPerCluster,
	numberOfReservedSectors,
	numberOfFATs,
	rootDirectoryEntriesMax,
	sectorsTotal,
	sectorsPerFAT,
	sectorsPerTrack,
	numberOfHeads,
	volumeID,
	volumeLabel,

	fat,

	directoryRoot,

	bytes
)
{
	this.bytesPerSector = bytesPerSector;
	this.sectorsPerCluster = sectorsPerCluster;
	this.numberOfReservedSectors = numberOfReservedSectors;
	this.numberOfFATs = numberOfFATs;
	this.rootDirectoryEntriesMax = rootDirectoryEntriesMax;
	this.sectorsTotal = sectorsTotal;	
	this.sectorsPerFAT = sectorsPerFAT;
	this.sectorsPerTrack = sectorsPerTrack;
	this.numberOfHeads = numberOfHeads;
	this.volumeID = volumeID;
	this.volumeLabel = volumeLabel;

	this.fat = fat;

	this.directoryRoot = directoryRoot;

	this.bytes = bytes;

	this.offsetOfDataRegionInSectors = 
		this.numberOfReservedSectors
		+ 
		(
			this.numberOfFATs
			* this.sectorsPerFAT
		)
		+ 
		(
			this.rootDirectoryEntriesMax 
			* FilesystemFAT12.DirectoryEntrySizeInBytes
		)
		/ this.bytesPerSector
		- 2; // hack - Not sure why I have to subtract 2.

	this.directoryCurrent = this.directoryRoot;
}
{
	// constants

	FilesystemFAT12.DirectoryEntrySizeInBytes = 32;

	// static methods

	FilesystemFAT12.fromBytes = function(filesystemAsBytes)
	{
		// Based on descriptions of the FAT12 filesystem found at the URLs:
		// https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system
		// and
		// http://www.eit.lth.se/fileadmin/eit/courses/eitn50/Projekt1/FAT12Description.pdf

		var byteStream = new ByteStream(filesystemAsBytes);

		var descriptorOffsetInBytes = 11;
		var bytesIgnored = byteStream.readBytes(descriptorOffsetInBytes);

		var bytesPerSector = byteStream.readIntegerLE(2);
		var sectorsPerCluster = byteStream.readIntegerLE(1);
		var numberOfReservedSectors = byteStream.readIntegerLE(2);
		var numberOfFATs = byteStream.readIntegerLE(1);
		var rootDirectoryEntriesMax = byteStream.readIntegerLE(2);
		var sectorsTotal = byteStream.readIntegerLE(2);
		bytesIgnored = byteStream.readBytes(1);
		var sectorsPerFAT = byteStream.readIntegerLE(2);
		var sectorsPerTrack = byteStream.readIntegerLE(2);
		var numberOfHeads = byteStream.readIntegerLE(2);
		bytesIgnored = byteStream.readBytes(4);
		var sectorsTotalForFAT32 = byteStream.readIntegerLE(4); // ignore for FAT12
		bytesIgnored = byteStream.readBytes(2);
		var bootSignatureExtended = byteStream.readBytes(1);
		var volumeID = byteStream.readIntegerLE(4);
		var volumeLabel = byteStream.readString(11);
		var filesystemType = byteStream.readString(8);

		var byteOffsetOfFAT = numberOfReservedSectors * bytesPerSector;
		byteStream.byteIndexCurrent = byteOffsetOfFAT;
		var bytesPerFAT = sectorsPerFAT * bytesPerSector;
		var bytesForFAT = byteStream.readBytes(bytesPerFAT);

		var fat = FilesystemFAT12FileAllocationTable.fromBytes
		(
			bytesForFAT
		);

		var sectorOffsetOfDirectoryRoot = 
			numberOfReservedSectors
			+ (numberOfFATs * sectorsPerFAT);

		var byteOffsetOfDirectoryRoot = 
			sectorOffsetOfDirectoryRoot
			* bytesPerSector;

		var directoryRoot = FilesystemFAT12Directory.fromDirectoryParentEntryAndBytes
		(
			null, // directorParent
			null, // directoryEntryForSelf
			filesystemAsBytes.slice
			(
				byteOffsetOfDirectoryRoot,
				byteOffsetOfDirectoryRoot
					+ rootDirectoryEntriesMax 
					* FilesystemFAT12.DirectoryEntrySizeInBytes	
			)
		);

		var returnValue = new FilesystemFAT12
		(
			bytesPerSector,
			sectorsPerCluster,
			numberOfReservedSectors,
			numberOfFATs,
			rootDirectoryEntriesMax,
			sectorsTotal,
			sectorsPerFAT,
			sectorsPerTrack,
			numberOfHeads,
			volumeID,
			volumeLabel,

			fat,

			directoryRoot,

			filesystemAsBytes
		);

		return returnValue;	
	}	

	// instance methods

	// dom

	FilesystemFAT12.prototype.domElementUpdate = function()
	{
		if (this.domElement == null)
		{
			this.domElement = document.createElement("div");
		}

		this.domElement.innerHTML = "";

		this.domElement.appendChild
		(
			this.directoryCurrent.domElementUpdate()	
		);

		return this.domElement;
	}

	// string

	FilesystemFAT12.prototype.toString = function()
	{
		return JSON.stringify(this).split(",").join(",\n");
	}
}

function FilesystemFAT12Directory(directoryParent, directoryEntryForSelf, entries)
{
	this.directoryParent = directoryParent
	this.directoryEntryForSelf = directoryEntryForSelf;
	this.entries = entries;
}
{
	// static methods

	FilesystemFAT12Directory.fromDirectoryParentEntryAndBytes = function
	(
		directoryParent,
		directoryEntryForSelf, 
		directoryAsBytes
	)
	{
		var byteStream = new ByteStream(directoryAsBytes);

		var directoryEntries = [];
		var directoryEntrySizeInBytes = FilesystemFAT12.DirectoryEntrySizeInBytes;

		while (true)
		{
			var bytesForDirectoryEntry = byteStream.readBytes
			(
				directoryEntrySizeInBytes
			);

			var entryType = bytesForDirectoryEntry[0];

			if (entryType == 0x00) // no more entries
			{
				break;
			}
			else if (entryType == 0xE5) // freed entry
			{
				// do nothing
			}
			else
			{
				var attributesAsByte = bytesForDirectoryEntry[11];
				if (attributesAsByte == 0xF)
				{
					// long file name
					// todo
				}
				else
				{

					var directoryEntry = FilesystemFAT12DirectoryEntry.fromBytes
					(
						bytesForDirectoryEntry
					);

					if 
					(
						directoryEntry.shortFileName.indexOf(".") == 0
					)
					{
						// self (".") or parent ("..")
						// todo
					}
					else
					{
						directoryEntries.push
						(
							directoryEntry
						);
					}
				}
			}
			
		}

		var returnValue = new FilesystemFAT12Directory
		(			
			directoryParent,
			directoryEntryForSelf,
			directoryEntries
		);

		return returnValue;
	}

	// instance methods

	FilesystemFAT12Directory.prototype.directoryParentGoTo = function()
	{
		var filesystem = Globals.Instance.filesystem;
		filesystem.directoryCurrent = this.directoryParent;
		filesystem.domElementUpdate();
	}

	// dom

	FilesystemFAT12Directory.prototype.domElementUpdate = function()
	{
		if (this.domElement == null)
		{
			this.domElement = document.createElement("div");

			var divRoot = document.createElement("div");

			var labelName = document.createElement("label");
			labelName.innerHTML = 
				"Current Directory:"
				+ (
					this.directoryEntryForSelf == null 
					? "[root]"
					: this.directoryEntryForSelf.shortFileName
				);

			divRoot.appendChild(labelName);

			if (this.directoryEntryForSelf != null)
			{
				var buttonParent = document.createElement("button");
				buttonParent.innerHTML = "Up";
				buttonParent.onclick = this.directoryParentGoTo.bind(this);
				divRoot.appendChild(buttonParent);
			}

			this.domElement.appendChild(divRoot);

			for (var i = 0; i < this.entries.length; i++)
			{
				var entry = this.entries[i];
	
				this.domElement.appendChild
				(
					entry.domElementUpdate()
				);
			}
		}

		return this.domElement;		
	}
}

function FilesystemFAT12DirectoryEntry
(
	shortFileName,
	shortFileExtension,
	attributes,
	datetimeCreated,
	dateAccessed,
	datetimeModified,
	clusterIndexFirst,
	sizeInBytes
)
{
	this.shortFileName = shortFileName;
	this.shortFileExtension = shortFileExtension;
	this.attributes = attributes;
	this.datetimeCreated = datetimeCreated;
	this.dateAccessed = dateAccessed;
	this.datetimeModified = datetimeModified;
	this.clusterIndexFirst = clusterIndexFirst;
	this.sizeInBytes = sizeInBytes;
}
{
	// static methods

	FilesystemFAT12DirectoryEntry.fromBytes = function(bytes)
	{
		var byteStream = new ByteStream(bytes);

		var shortFileName = byteStream.readString(8);
		var shortFileExtension = byteStream.readString(3);
		var attributesAsByte = byteStream.readByte();
		var reserved = byteStream.readBytes(2);
		var datetimeCreated = byteStream.readIntegerLE(4);
		var dateAccessed = byteStream.readIntegerLE(2);
		var bytesIgnored = byteStream.readBytes(2);
		var datetimeModified = byteStream.readIntegerLE(4);
		var clusterIndexFirst = byteStream.readIntegerLE(2);
		var sizeInBytes = byteStream.readIntegerLE(4);

		var attributes = FilesystemFAT12DirectoryEntryAttributes.fromByte
		(
			attributesAsByte
		);

		var returnValue = new FilesystemFAT12DirectoryEntry
		(
			shortFileName,
			shortFileExtension,
			attributes,
			datetimeCreated,
			dateAccessed,
			datetimeModified,
			clusterIndexFirst,
			sizeInBytes
		);

		return returnValue;
		
	}

	// instance methods

	FilesystemFAT12DirectoryEntry.prototype.contentBytesLoadUsingFilesystem = function
	(
		filesystem
	)
	{
		var contentBytes = [];

		var clusterIndexCurrent = this.clusterIndexFirst;

		var bytesPerCluster = 
			filesystem.sectorsPerCluster
			* filesystem.bytesPerSector;

		while 
		(
			clusterIndexCurrent != 0 // unused - file is empty?
			&& clusterIndexCurrent < 0xFF8
		)
		{
			var sectorOffsetOfCluster = 
				clusterIndexCurrent * filesystem.sectorsPerCluster
				+ filesystem.offsetOfDataRegionInSectors;

			var byteOffsetOfCluster = 
				sectorOffsetOfCluster
				* filesystem.bytesPerSector;

			var bytesFromCluster = 
			(
				filesystem.bytes.slice
				(
					byteOffsetOfCluster,
					byteOffsetOfCluster + bytesPerCluster
				)
			);

			contentBytes = contentBytes.concat
			(
				bytesFromCluster
			);

			clusterIndexCurrent = filesystem.fat.entries[clusterIndexCurrent];
		}

		return contentBytes;
	}

	FilesystemFAT12DirectoryEntry.prototype.downloadAsFile = function()
	{
		var filesystem = Globals.Instance.filesystem;

		var contentBytes = this.contentBytesLoadUsingFilesystem
		(
			filesystem
		);

		FileHelper.saveBytesToFile
		(
			contentBytes,
			this.fileNamePlusExtension()
		);
	}

	FilesystemFAT12DirectoryEntry.prototype.fileNamePlusExtension = function()
	{
		return this.shortFileName.trim() + "." + this.shortFileExtension;
	}


	FilesystemFAT12DirectoryEntry.prototype.openAsDirectory = function()
	{
		var filesystem = Globals.Instance.filesystem;

		var contentBytes = this.contentBytesLoadUsingFilesystem
		(
			filesystem
		);

		filesystem.directoryCurrent = FilesystemFAT12Directory.fromDirectoryParentEntryAndBytes
		(
			filesystem.directoryCurrent,
			this, // directoryEntryForSelf
			contentBytes
		);
	
		filesystem.domElementUpdate();
	}

	// dom

	FilesystemFAT12DirectoryEntry.prototype.domElementUpdate = function()
	{
		if (this.domElement == null)
		{
			this.domElement = document.createElement("div");

			var indent = "&nbsp;&nbsp;&nbsp;&nbsp;";

			if (this.attributes.isVolumeLabel == true)
			{
				this.domElement.innerHTML = 
					"Volume Label: " + this.shortFileName
			}
			else if (this.attributes.isSubdirectory == true)
			{
				var labelName = document.createElement("label");
				labelName.innerHTML = indent + this.shortFileName;
				this.domElement.appendChild(labelName);

				var buttonOpen = document.createElement("button");
				buttonOpen.innerHTML = "Open";
				buttonOpen.onclick = this.openAsDirectory.bind(this);
				this.domElement.appendChild(buttonOpen);
			}
			else
			{
				var fileNamePlusExtension = this.fileNamePlusExtension();

				var labelName = document.createElement("label");
				labelName.innerHTML = indent + fileNamePlusExtension;
				this.domElement.appendChild(labelName);

				var buttonDownload = document.createElement("button");
				buttonDownload.innerHTML = "Download";
				buttonDownload.onclick = this.downloadAsFile.bind(this);
				this.domElement.appendChild(buttonDownload);
			}
		}

		return this.domElement;
	}
}

function FilesystemFAT12DirectoryEntryAttributes
(
	isReadOnly,
	isHidden,
	isSystem,
	isVolumeLabel,
	isSubdirectory,
	isArchive
)
{
	this.isReadOnly = isReadOnly;
	this.isHidden = isHidden;
	this.isSystem = isSystem;
	this.isVolumeLabel = isVolumeLabel;
	this.isSubdirectory = isSubdirectory;
	this.isArchive = isArchive;
}
{
	FilesystemFAT12DirectoryEntryAttributes.fromByte = function(attributesAsByte)
	{
		var returnValue = new FilesystemFAT12DirectoryEntryAttributes
		(
			(((attributesAsByte >> 0) & 1) == 1),
			(((attributesAsByte >> 1) & 1) == 1),
			(((attributesAsByte >> 2) & 1) == 1),
			(((attributesAsByte >> 3) & 1) == 1),
			(((attributesAsByte >> 4) & 1) == 1),
			(((attributesAsByte >> 5) & 1) == 1)
		);

		return returnValue;
	}
}

function FilesystemFAT12FileAllocationTable(entries)
{
	this.entries = entries;
}
{
	// static methods

	FilesystemFAT12FileAllocationTable.fromBytes = function(fatAsBytes)
	{
		var bitsPerEntry = 12;
		var numberOfEntries = fatAsBytes.length * 8 / bitsPerEntry;

		var bitStream = new BitStream(fatAsBytes);

		var entries = [];
				
		for (var i = 0; i < numberOfEntries; i++)
		{
			var entry = bitStream.readFAT12Entry();
			entries.push(entry);
		}

		var returnValue = new FilesystemFAT12FileAllocationTable
		(
			entries
		);

		return returnValue;
	}
}

function Globals()
{
	// do nothing
}
{
	Globals.Instance = new Globals();
	
	Globals.prototype.initialize = function(filesystem)
	{
		this.filesystem = filesystem;

		var divMain = document.getElementById("divMain");
		divMain.appendChild
		(
			this.filesystem.domElementUpdate()
		);
	}
}

// run

main();

</script>
</body>
</html>
Advertisements
This entry was posted in Uncategorized and tagged , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s