Sub directories

This commit is contained in:
Jordan Goulder 2025-01-17 14:43:09 -05:00
parent a6503ae65b
commit 5fdbd4ceb1
2 changed files with 65 additions and 60 deletions

View File

@ -13,11 +13,22 @@ const floppyDisk = computed(() => {
<section v-if="floppyDisk.bootSectorInfo">
<h3>Boot Sector</h3>
<pre>{{ floppyDisk.bootSectorInfo }}</pre>
<h3>Root Dir</h3>
<h3>Directory Entries</h3>
<pre>{{ floppyDisk.rootDirEntries }}</pre>
<h3>Data</h3>
<pre>Offset: {{ floppyDisk.dataOffset }}</pre>
</section>
</template>
<style scoped></style>
<style scoped>
pre {
width: 100%;
padding: 0.5em 1em 10em;
box-sizing: border-box;
overflow: scroll;
min-height: 10rem;
height: 20rem;
max-height: 20rem;
background: rgba(255, 255, 255, 0.15);
color: #c0c0c0;
border: 1px solid rgba(255, 255, 255, 0.3);
}
</style>

View File

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