Sub directories
This commit is contained in:
parent
a6503ae65b
commit
5fdbd4ceb1
@ -13,11 +13,22 @@ const floppyDisk = computed(() => {
|
|||||||
<section v-if="floppyDisk.bootSectorInfo">
|
<section v-if="floppyDisk.bootSectorInfo">
|
||||||
<h3>Boot Sector</h3>
|
<h3>Boot Sector</h3>
|
||||||
<pre>{{ floppyDisk.bootSectorInfo }}</pre>
|
<pre>{{ floppyDisk.bootSectorInfo }}</pre>
|
||||||
<h3>Root Dir</h3>
|
<h3>Directory Entries</h3>
|
||||||
<pre>{{ floppyDisk.rootDirEntries }}</pre>
|
<pre>{{ floppyDisk.rootDirEntries }}</pre>
|
||||||
<h3>Data</h3>
|
|
||||||
<pre>Offset: {{ floppyDisk.dataOffset }}</pre>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</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
|
firstCluster: number
|
||||||
clusterChain: number[]
|
clusterChain: number[]
|
||||||
data: ArrayBuffer
|
data: ArrayBuffer
|
||||||
|
subDirEntries: TDirEntry[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILongFileNameDirEntry {
|
export interface ILongFileNameDirEntry {
|
||||||
@ -62,17 +63,17 @@ export interface IUnusedDirEntry {
|
|||||||
type: 'unused-entry'
|
type: 'unused-entry'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DirEntry = IStandardDirEntry | ILongFileNameDirEntry | IFinalDirEntry | IUnusedDirEntry
|
export type TDirEntry = IStandardDirEntry | ILongFileNameDirEntry | IFinalDirEntry | IUnusedDirEntry
|
||||||
|
|
||||||
export class FloppyDisk {
|
export class FloppyDisk {
|
||||||
private readonly _buffer = new ArrayBuffer(0)
|
buffer = new ArrayBuffer(0)
|
||||||
private _bootSector: IBootSectorInfo | null = null
|
private _bootSector: IBootSectorInfo | null = null
|
||||||
|
|
||||||
constructor(buffer: ArrayBuffer) {
|
constructor(buffer: ArrayBuffer) {
|
||||||
this._buffer = buffer
|
this.buffer = buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
private _rootDirEntries: DirEntry[] | null = null
|
private _rootDirEntries: TDirEntry[] | null = null
|
||||||
|
|
||||||
get rootDirEntries() {
|
get rootDirEntries() {
|
||||||
if (this._rootDirEntries === null) {
|
if (this._rootDirEntries === null) {
|
||||||
@ -81,59 +82,23 @@ export class FloppyDisk {
|
|||||||
|
|
||||||
const end = offset + size
|
const end = offset + size
|
||||||
|
|
||||||
if (this._buffer.byteLength >= end) {
|
if (this.buffer.byteLength >= end) {
|
||||||
this._rootDirEntries = decodeDirectoryEntries(
|
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
|
return this._rootDirEntries
|
||||||
}
|
}
|
||||||
|
|
||||||
get bootSectorInfo() {
|
get bootSectorInfo() {
|
||||||
if (this._buffer.byteLength < 512) {
|
if (this.buffer.byteLength < 512) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return (this._bootSector =
|
return (this._bootSector =
|
||||||
this._bootSector ?? decodeBootSector(new DataView(this._buffer, 0, 512)))
|
this._bootSector ?? decodeBootSector(new DataView(this.buffer, 0, 512)))
|
||||||
}
|
}
|
||||||
|
|
||||||
get tableSize() {
|
get tableSize() {
|
||||||
@ -198,7 +163,7 @@ export class FloppyDisk {
|
|||||||
const entityOffset = tableOffset % this.bytesPerSector
|
const entityOffset = tableOffset % this.bytesPerSector
|
||||||
|
|
||||||
const table = new DataView(
|
const table = new DataView(
|
||||||
this._buffer,
|
this.buffer,
|
||||||
tableSector * this.bytesPerSector,
|
tableSector * this.bytesPerSector,
|
||||||
2 * 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) {
|
if (data.byteLength < 32) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -313,10 +278,10 @@ function decodeDirectoryEntry(data: DataView): DirEntry | null {
|
|||||||
return { type: 'long-filename-entry' }
|
return { type: 'long-filename-entry' }
|
||||||
} else {
|
} else {
|
||||||
const asciiDecoder = new TextDecoder('ascii')
|
const asciiDecoder = new TextDecoder('ascii')
|
||||||
const name = asciiDecoder.decode(data.buffer.slice(data.byteOffset, data.byteOffset + 8))
|
const name = asciiDecoder.decode(data.buffer.slice(data.byteOffset, data.byteOffset + 8)).trim()
|
||||||
const extension = asciiDecoder.decode(
|
const extension = asciiDecoder
|
||||||
data.buffer.slice(data.byteOffset + 8, data.byteOffset + 11),
|
.decode(data.buffer.slice(data.byteOffset + 8, data.byteOffset + 11))
|
||||||
)
|
.trim()
|
||||||
|
|
||||||
const attributeByte = data.getUint8(11)
|
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 firstCluster = (data.getUint16(20, true) << 16) | data.getUint16(26, true)
|
||||||
const size = data.getUint32(28, true)
|
const size = data.getUint32(28, true)
|
||||||
|
|
||||||
return {
|
const entry: IStandardDirEntry = {
|
||||||
type: 'standard-entry',
|
type: 'standard-entry',
|
||||||
name,
|
name,
|
||||||
extension,
|
extension,
|
||||||
attributes,
|
attributes,
|
||||||
firstCluster,
|
firstCluster,
|
||||||
clusterChain: [],
|
clusterChain: [],
|
||||||
data: new ArrayBuffer(0),
|
data: new Uint8Array(0),
|
||||||
size,
|
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[] {
|
return entry
|
||||||
const entries: DirEntry[] = []
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeDirectoryEntries(data: DataView, fd: FloppyDisk): TDirEntry[] {
|
||||||
|
const entries: TDirEntry[] = []
|
||||||
|
|
||||||
let offset = 0
|
let offset = 0
|
||||||
while (data.byteLength - offset >= 32) {
|
while (data.byteLength - offset >= 32) {
|
||||||
const dirData = new DataView(data.buffer, data.byteOffset + offset, 32)
|
const dirData = new DataView(data.buffer, data.byteOffset + offset, 32)
|
||||||
offset += 32
|
offset += 32
|
||||||
|
|
||||||
const entry = decodeDirectoryEntry(dirData)
|
const entry = decodeDirectoryEntry(dirData, fd)
|
||||||
if (entry === null) {
|
if (entry === null) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user