Sub directories
This commit is contained in:
parent
a6503ae65b
commit
5fdbd4ceb1
@ -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>
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function decodeDirectoryEntries(data: DataView): DirEntry[] {
|
||||
const entries: DirEntry[] = []
|
||||
return entry
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user