apple2js/js/formats/sources.ts
Ian Flanigan 9d4a265daf Refactor storage types
Today, lots of information about how a file or JSON becomes a disk
image is embedded in the metadata for the image and/or disk. This
makes it hard to write back to the source when the in-memory disk
image changes.

This refactoring is an attempt to break out all of the bits of logic
into composable pieces.  While this is mestly concerned with reading
right now, the idea is that it will eventually allow configuring
writing as well.  The main goal is to allow round-tripping to the same
file on disk, but, in theory, it could also save to a different file
or the local database, too.

Note that this is a work in progress.
2022-06-05 23:30:34 +02:00

149 lines
4.3 KiB
TypeScript

import { byte } from 'js/types';
import { concat } from 'js/util';
import { D13O, DO, explodeSector13, explodeSector16, PO } from './format_utils';
import { ByteSource, Metadata, MetadataSource, NibbleTrackSource, TrackSectorSource } from './types';
export class FileHandleFileSource {
constructor(private readonly fileHandle: FileSystemFileHandle) { }
getFile(): Promise<File> {
return this.fileHandle.getFile();
}
}
export class FileHandleMetadataSource implements MetadataSource {
constructor(private readonly fileHandle: FileSystemFileHandle) { }
getMetadata(): Metadata {
return {
name: this.fileHandle.name,
};
}
}
export class FileByteArraySource {
constructor(private readonly file: File) { }
async getBytes() {
return new Uint8Array(await this.file.arrayBuffer());
}
}
export class ByteArrayByteSource implements ByteSource {
constructor(private readonly byteArray: Uint8Array) { }
read(offset: number, length: number): Uint8Array {
return this.byteArray.subarray(offset, offset + length);
}
length(): number {
return this.byteArray.length;
}
}
export class ByteArrayArrayTrackSectorSource implements TrackSectorSource {
constructor(
protected readonly data: Uint8Array[][]) { }
read(track: number, sector: number): Uint8Array {
return this.data[track][sector];
}
numTracks(): number {
return this.data.length;
}
}
export class ByteTrackSectorSource implements TrackSectorSource {
constructor(
protected readonly byteSource: ByteSource,
protected readonly sectors = 16) { }
read(track: number, sector: number): Uint8Array {
return this.byteSource.read((track * this.sectors + sector) * 256, 256);
}
numTracks(): number {
let tracks = this.byteSource.length() / (this.sectors * 256);
if (tracks !== Math.floor(tracks)) {
tracks = Math.floor(tracks + 1);
}
return tracks;
}
}
export class ProdosOrderedTrackSectorSource implements TrackSectorSource {
constructor(private readonly trackSectorSource: TrackSectorSource) { }
read(track: number, sector: number): Uint8Array {
return this.trackSectorSource.read(track, PO[sector]);
}
numTracks(): number {
return this.trackSectorSource.numTracks();
}
}
export class DosOrderedTrackSectorSource implements TrackSectorSource {
constructor(private readonly trackSectorSource: TrackSectorSource) { }
read(track: number, sector: number): Uint8Array {
return this.trackSectorSource.read(track, DO[sector]);
}
numTracks(): number {
return this.trackSectorSource.numTracks();
}
}
export class TrackSector6x2NibbleTrackSource implements NibbleTrackSource {
constructor(
private readonly trackSectorSource: TrackSectorSource,
private readonly volume: byte = 254) {
}
read(track: number): Uint8Array {
const sectors: byte[][] = [];
for (let sector = 0; sector < 16; sector++) {
const data = this.trackSectorSource.read(track, sector);
sectors.push(explodeSector16(this.volume, track, sector, data));
}
return concat(...sectors);
}
numTracks(): number {
return this.trackSectorSource.numTracks();
}
}
export class TrackSector5x3NibbleTrackSource implements NibbleTrackSource {
constructor(
private readonly trackSectorSource: TrackSectorSource,
private readonly volume: byte = 254) {
}
/*
* DOS 13-sector disks have the physical sectors skewed on the track. The skew
* between physical sectors is 10 (A), resulting in the following physical order:
*
* 0 A 7 4 1 B 8 5 2 C 9 6 3
*
* Note that because physical sector == logical sector, this works slightly
* differently from the DOS and ProDOS nibblizers.
*/
read(track: number): Uint8Array {
const sectors: byte[][] = [];
for (let sector = 0; sector < 13; sector++) {
const physical_sector = D13O[sector];
const data = this.trackSectorSource.read(track, physical_sector);
sectors.push(explodeSector13(this.volume, track, physical_sector, data));
}
return concat(...sectors);
}
numTracks(): number {
return this.trackSectorSource.numTracks();
}
}