Boot Sector
{{ floppyDisk.bootSectorInfo }}
- Root Dir
+ Directory Entries
{{ floppyDisk.rootDirEntries }}
- Data
- Offset: {{ floppyDisk.dataOffset }}
-
+
diff --git a/src/floppy/disk.ts b/src/floppy/disk.ts
index 4f156a7..04c9b04 100644
--- a/src/floppy/disk.ts
+++ b/src/floppy/disk.ts
@@ -48,6 +48,7 @@ export interface IStandardDirEntry {
firstCluster: number
clusterChain: number[]
data: ArrayBuffer
+ subDirEntries: TDirEntry[]
}
export interface ILongFileNameDirEntry {
@@ -62,17 +63,17 @@ export interface IUnusedDirEntry {
type: 'unused-entry'
}
-export type DirEntry = IStandardDirEntry | ILongFileNameDirEntry | IFinalDirEntry | IUnusedDirEntry
+export type TDirEntry = IStandardDirEntry | ILongFileNameDirEntry | IFinalDirEntry | IUnusedDirEntry
export class FloppyDisk {
- private readonly _buffer = new ArrayBuffer(0)
+ buffer = new ArrayBuffer(0)
private _bootSector: IBootSectorInfo | null = null
constructor(buffer: ArrayBuffer) {
- this._buffer = buffer
+ this.buffer = buffer
}
- private _rootDirEntries: DirEntry[] | null = null
+ private _rootDirEntries: TDirEntry[] | null = null
get rootDirEntries() {
if (this._rootDirEntries === null) {
@@ -81,59 +82,23 @@ export class FloppyDisk {
const end = offset + size
- if (this._buffer.byteLength >= end) {
+ if (this.buffer.byteLength >= end) {
this._rootDirEntries = decodeDirectoryEntries(
- new DataView(this._buffer, this.rootDirOffset, this.rootDirSize),
+ new DataView(this.buffer, this.rootDirOffset, this.rootDirSize),
+ this,
)
}
-
- if (this._rootDirEntries === null) {
- return null
- }
-
- for (const entry of this._rootDirEntries) {
- if (entry.type === 'standard-entry' && (entry.size > 0 || entry.attributes.directory)) {
- entry.clusterChain = this.clusterChain(entry.firstCluster)
-
- const dataSize = entry.clusterChain.length * this.bytesPerCluster
- if (dataSize === 0) {
- continue
- }
-
- const data = new Uint8Array(dataSize)
- for (let i = 0; i < entry.clusterChain.length; i++) {
- const offset = (entry.clusterChain[i] - 2) * this.bytesPerCluster
- const view = new Uint8Array(
- this._buffer,
- this.dataOffset + offset,
- this.bytesPerCluster,
- )
- data.set(view, i * this.bytesPerCluster)
- }
- console.log(
- entry.name.trim() + (entry.extension.trim() ? '.' + entry.extension.trim() : ''),
- )
- entry.data = data.slice(0, entry.size > 0 ? entry.size : data.byteLength)
- if (
- !entry.attributes.directory &&
- (['BAT', 'TXT', 'BAS'].includes(entry.extension) || entry.name.trim() === 'LICENSE')
- ) {
- const decoder = new TextDecoder()
- console.log(decoder.decode(entry.data))
- }
- }
- }
}
return this._rootDirEntries
}
get bootSectorInfo() {
- if (this._buffer.byteLength < 512) {
+ if (this.buffer.byteLength < 512) {
return null
}
return (this._bootSector =
- this._bootSector ?? decodeBootSector(new DataView(this._buffer, 0, 512)))
+ this._bootSector ?? decodeBootSector(new DataView(this.buffer, 0, 512)))
}
get tableSize() {
@@ -198,7 +163,7 @@ export class FloppyDisk {
const entityOffset = tableOffset % this.bytesPerSector
const table = new DataView(
- this._buffer,
+ this.buffer,
tableSector * this.bytesPerSector,
2 * this.bytesPerSector,
)
@@ -297,7 +262,7 @@ function decodeBootSector(data: DataView): IBootSectorInfo | null {
}
}
-function decodeDirectoryEntry(data: DataView): DirEntry | null {
+function decodeDirectoryEntry(data: DataView, fd: FloppyDisk): TDirEntry | null {
if (data.byteLength < 32) {
return null
}
@@ -313,10 +278,10 @@ function decodeDirectoryEntry(data: DataView): DirEntry | null {
return { type: 'long-filename-entry' }
} else {
const asciiDecoder = new TextDecoder('ascii')
- const name = asciiDecoder.decode(data.buffer.slice(data.byteOffset, data.byteOffset + 8))
- const extension = asciiDecoder.decode(
- data.buffer.slice(data.byteOffset + 8, data.byteOffset + 11),
- )
+ const name = asciiDecoder.decode(data.buffer.slice(data.byteOffset, data.byteOffset + 8)).trim()
+ const extension = asciiDecoder
+ .decode(data.buffer.slice(data.byteOffset + 8, data.byteOffset + 11))
+ .trim()
const attributeByte = data.getUint8(11)
@@ -332,28 +297,57 @@ function decodeDirectoryEntry(data: DataView): DirEntry | null {
const firstCluster = (data.getUint16(20, true) << 16) | data.getUint16(26, true)
const size = data.getUint32(28, true)
- return {
+ const entry: IStandardDirEntry = {
type: 'standard-entry',
name,
extension,
attributes,
firstCluster,
clusterChain: [],
- data: new ArrayBuffer(0),
+ data: new Uint8Array(0),
size,
+ subDirEntries: [],
}
+
+ if (
+ entry.size > 0 ||
+ (entry.attributes.directory && entry.name !== '.' && entry.name !== '..')
+ ) {
+ entry.clusterChain = fd.clusterChain(entry.firstCluster)
+
+ const dataSize = entry.clusterChain.length * fd.bytesPerCluster
+
+ if (dataSize !== 0) {
+ const entryData = new Uint8Array(dataSize)
+ for (let i = 0; i < entry.clusterChain.length; i++) {
+ const offset = (entry.clusterChain[i] - 2) * fd.bytesPerCluster
+ const view = new Uint8Array(fd.buffer, fd.dataOffset + offset, fd.bytesPerCluster)
+ entryData.set(view, i * fd.bytesPerCluster)
+ }
+
+ const resizedData = entryData.slice(0, entry.size > 0 ? entry.size : entryData.byteLength)
+
+ if (entry.attributes.directory) {
+ entry.subDirEntries = decodeDirectoryEntries(new DataView(resizedData.buffer), fd)
+ } else {
+ entry.data = resizedData
+ }
+ }
+ }
+
+ return entry
}
}
-function decodeDirectoryEntries(data: DataView): DirEntry[] {
- const entries: DirEntry[] = []
+function decodeDirectoryEntries(data: DataView, fd: FloppyDisk): TDirEntry[] {
+ const entries: TDirEntry[] = []
let offset = 0
while (data.byteLength - offset >= 32) {
const dirData = new DataView(data.buffer, data.byteOffset + offset, 32)
offset += 32
- const entry = decodeDirectoryEntry(dirData)
+ const entry = decodeDirectoryEntry(dirData, fd)
if (entry === null) {
break
}