Compare commits
4 Commits
17201bca9a
...
7cbb027ab1
Author | SHA1 | Date |
---|---|---|
Ian Flanigan | 7cbb027ab1 | |
dependabot[bot] | eab7de7db3 | |
dependabot[bot] | f4c5a0e024 | |
Ian Flanigan | 9d4a265daf |
|
@ -1,7 +1,5 @@
|
|||
import { explodeSector16, PO } from './format_utils';
|
||||
import { bytify } from '../util';
|
||||
import type { byte } from '../types';
|
||||
import { NibbleDisk, DiskOptions, ENCODING_NIBBLE } from './types';
|
||||
import { NibbleDisk, DiskOptions, ENCODING_NIBBLE, TrackSectorSource } from './types';
|
||||
import { ByteArrayArrayTrackSectorSource, ByteArrayByteSource, ByteTrackSectorSource, ProdosOrderedTrackSectorSource, TrackSector6x2NibbleTrackSource } from './sources';
|
||||
|
||||
/**
|
||||
* Returns a `Disk` object from ProDOS-ordered image data.
|
||||
|
@ -19,24 +17,23 @@ export default function createDiskFromProDOS(options: DiskOptions) {
|
|||
readOnly: readOnly || false,
|
||||
};
|
||||
|
||||
for (let physical_track = 0; physical_track < 35; physical_track++) {
|
||||
let track: byte[] = [];
|
||||
for (let physical_sector = 0; physical_sector < 16; physical_sector++) {
|
||||
const prodos_sector = PO[physical_sector];
|
||||
let sector;
|
||||
if (rawData) {
|
||||
const off = (16 * physical_track + prodos_sector) * 256;
|
||||
sector = new Uint8Array(rawData.slice(off, off + 256));
|
||||
} else if (data) {
|
||||
sector = data[physical_track][prodos_sector];
|
||||
} else {
|
||||
throw new Error('Requires data or rawData');
|
||||
}
|
||||
track = track.concat(
|
||||
explodeSector16(volume, physical_track, physical_sector, sector)
|
||||
);
|
||||
}
|
||||
disk.tracks[physical_track] = bytify(track);
|
||||
let trackSectorSource: TrackSectorSource;
|
||||
if (rawData) {
|
||||
trackSectorSource =
|
||||
new ByteTrackSectorSource(
|
||||
new ByteArrayByteSource(new Uint8Array(rawData)));
|
||||
} else if (data) {
|
||||
trackSectorSource = new ByteArrayArrayTrackSectorSource(data);
|
||||
} else {
|
||||
throw new Error('Requires data or rawData');
|
||||
}
|
||||
|
||||
const nibbleTrackSource =
|
||||
new TrackSector6x2NibbleTrackSource(
|
||||
new ProdosOrderedTrackSectorSource(trackSectorSource), volume);
|
||||
|
||||
for (let physical_track = 0; physical_track < nibbleTrackSource.numTracks(); physical_track++) {
|
||||
disk.tracks[physical_track] = nibbleTrackSource.read(physical_track);
|
||||
}
|
||||
|
||||
return disk;
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import type { byte, memory, MemberOf, word } from '../types';
|
||||
import type { byte, memory, MemberOf, word, ReadonlyUint8Array } from '../types';
|
||||
import type { GamepadConfiguration } from '../ui/types';
|
||||
import { InfoChunk } from './woz';
|
||||
|
||||
|
@ -8,6 +8,55 @@ export type SupportedSectors = MemberOf<typeof SUPPORTED_SECTORS>;
|
|||
export const DRIVE_NUMBERS = [1, 2] as const;
|
||||
export type DriveNumber = MemberOf<typeof DRIVE_NUMBERS>;
|
||||
|
||||
export interface Metadata {
|
||||
name: string;
|
||||
side?: string;
|
||||
}
|
||||
|
||||
export interface MetadataSource {
|
||||
getMetadata(): Metadata;
|
||||
}
|
||||
|
||||
export interface ByteSource {
|
||||
read(offset: number, length: number): Uint8Array;
|
||||
length(): number;
|
||||
}
|
||||
|
||||
export interface ByteSink {
|
||||
write(offset: number, data: ReadonlyUint8Array): void;
|
||||
}
|
||||
|
||||
export interface JsonSource {
|
||||
read(): string;
|
||||
}
|
||||
|
||||
export interface TrackSectorSource {
|
||||
/** Returns the logical sector data for the given physical sector. */
|
||||
read(track: byte, sector: byte): Uint8Array;
|
||||
numTracks(): byte;
|
||||
}
|
||||
|
||||
export interface TrackSectorSink {
|
||||
write(track: byte, sector: byte, data: ReadonlyUint8Array): void;
|
||||
}
|
||||
|
||||
export interface NibbleTrackSource {
|
||||
read(track: byte): Uint8Array;
|
||||
numTracks(): byte;
|
||||
}
|
||||
|
||||
export interface NibbleTrackSink {
|
||||
write(track: byte, data: Uint8Array): void;
|
||||
}
|
||||
|
||||
export interface BlockSource {
|
||||
read(block: word): Uint8Array;
|
||||
}
|
||||
|
||||
export interface BlockSink {
|
||||
write(block: word, data: Uint8Array): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments for the disk format processors.
|
||||
*/
|
||||
|
|
|
@ -67,12 +67,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
|
||||
"integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==",
|
||||
"version": "7.22.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/highlight": "^7.16.7"
|
||||
"@babel/highlight": "^7.22.13",
|
||||
"chalk": "^2.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
@ -116,13 +117,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.17.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.10.tgz",
|
||||
"integrity": "sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
|
||||
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.17.10",
|
||||
"@jridgewell/gen-mapping": "^0.1.0",
|
||||
"@babel/types": "^7.23.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jsesc": "^2.5.1"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -220,13 +222,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/helper-environment-visitor": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz",
|
||||
"integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.16.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
|
@ -241,25 +240,25 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/helper-function-name": {
|
||||
"version": "7.17.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz",
|
||||
"integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.16.7",
|
||||
"@babel/types": "^7.17.0"
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/types": "^7.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-hoist-variables": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz",
|
||||
"integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==",
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
|
||||
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.16.7"
|
||||
"@babel/types": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
@ -372,21 +371,30 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/helper-split-export-declaration": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz",
|
||||
"integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==",
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.16.7"
|
||||
"@babel/types": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
|
||||
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
|
||||
"integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
@ -425,13 +433,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/highlight": {
|
||||
"version": "7.17.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz",
|
||||
"integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
|
||||
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.16.7",
|
||||
"chalk": "^2.0.0",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -439,9 +447,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.17.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz",
|
||||
"integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
|
||||
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
|
@ -1416,33 +1424,33 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
|
||||
"integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.16.7",
|
||||
"@babel/parser": "^7.16.7",
|
||||
"@babel/types": "^7.16.7"
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.17.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.10.tgz",
|
||||
"integrity": "sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.16.7",
|
||||
"@babel/generator": "^7.17.10",
|
||||
"@babel/helper-environment-visitor": "^7.16.7",
|
||||
"@babel/helper-function-name": "^7.17.9",
|
||||
"@babel/helper-hoist-variables": "^7.16.7",
|
||||
"@babel/helper-split-export-declaration": "^7.16.7",
|
||||
"@babel/parser": "^7.17.10",
|
||||
"@babel/types": "^7.17.10",
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.0",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@babel/types": "^7.23.0",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
|
@ -1451,12 +1459,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.17.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.10.tgz",
|
||||
"integrity": "sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
|
||||
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.16.7",
|
||||
"@babel/helper-string-parser": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -2374,13 +2383,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
|
||||
"integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
|
@ -2414,20 +2424,6 @@
|
|||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
|
@ -4724,7 +4720,7 @@
|
|||
"node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/color-support": {
|
||||
|
@ -5612,7 +5608,7 @@
|
|||
"node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
|
@ -6925,7 +6921,7 @@
|
|||
"node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
|
@ -11179,9 +11175,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.25",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.25.tgz",
|
||||
"integrity": "sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw==",
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -14460,12 +14456,13 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@babel/code-frame": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
|
||||
"integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==",
|
||||
"version": "7.22.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.16.7"
|
||||
"@babel/highlight": "^7.22.13",
|
||||
"chalk": "^2.4.2"
|
||||
}
|
||||
},
|
||||
"@babel/compat-data": {
|
||||
|
@ -14499,13 +14496,14 @@
|
|||
}
|
||||
},
|
||||
"@babel/generator": {
|
||||
"version": "7.17.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.10.tgz",
|
||||
"integrity": "sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
|
||||
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.17.10",
|
||||
"@jridgewell/gen-mapping": "^0.1.0",
|
||||
"@babel/types": "^7.23.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jsesc": "^2.5.1"
|
||||
}
|
||||
},
|
||||
|
@ -14582,13 +14580,10 @@
|
|||
}
|
||||
},
|
||||
"@babel/helper-environment-visitor": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz",
|
||||
"integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.16.7"
|
||||
}
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-explode-assignable-expression": {
|
||||
"version": "7.13.0",
|
||||
|
@ -14600,22 +14595,22 @@
|
|||
}
|
||||
},
|
||||
"@babel/helper-function-name": {
|
||||
"version": "7.17.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz",
|
||||
"integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/template": "^7.16.7",
|
||||
"@babel/types": "^7.17.0"
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/types": "^7.23.0"
|
||||
}
|
||||
},
|
||||
"@babel/helper-hoist-variables": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz",
|
||||
"integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==",
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
|
||||
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.16.7"
|
||||
"@babel/types": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-member-expression-to-functions": {
|
||||
|
@ -14710,18 +14705,24 @@
|
|||
}
|
||||
},
|
||||
"@babel/helper-split-export-declaration": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz",
|
||||
"integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==",
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.16.7"
|
||||
"@babel/types": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-string-parser": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
|
||||
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-validator-identifier": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
|
||||
"integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-validator-option": {
|
||||
|
@ -14754,20 +14755,20 @@
|
|||
}
|
||||
},
|
||||
"@babel/highlight": {
|
||||
"version": "7.17.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz",
|
||||
"integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
|
||||
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.16.7",
|
||||
"chalk": "^2.0.0",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.17.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz",
|
||||
"integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
|
||||
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
|
||||
|
@ -15517,41 +15518,42 @@
|
|||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
|
||||
"integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.16.7",
|
||||
"@babel/parser": "^7.16.7",
|
||||
"@babel/types": "^7.16.7"
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.17.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.10.tgz",
|
||||
"integrity": "sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.16.7",
|
||||
"@babel/generator": "^7.17.10",
|
||||
"@babel/helper-environment-visitor": "^7.16.7",
|
||||
"@babel/helper-function-name": "^7.17.9",
|
||||
"@babel/helper-hoist-variables": "^7.16.7",
|
||||
"@babel/helper-split-export-declaration": "^7.16.7",
|
||||
"@babel/parser": "^7.17.10",
|
||||
"@babel/types": "^7.17.10",
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.0",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@babel/types": "^7.23.0",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.17.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.10.tgz",
|
||||
"integrity": "sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
|
||||
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.16.7",
|
||||
"@babel/helper-string-parser": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
|
@ -16217,13 +16219,14 @@
|
|||
}
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
|
||||
"integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/set-array": "^1.0.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/resolve-uri": {
|
||||
|
@ -16246,19 +16249,6 @@
|
|||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
|
@ -18068,7 +18058,7 @@
|
|||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"color-support": {
|
||||
|
@ -18748,7 +18738,7 @@
|
|||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true
|
||||
},
|
||||
"escodegen": {
|
||||
|
@ -19721,7 +19711,7 @@
|
|||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true
|
||||
},
|
||||
"has-property-descriptors": {
|
||||
|
@ -22857,9 +22847,9 @@
|
|||
}
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.25",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.25.tgz",
|
||||
"integrity": "sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw==",
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.6",
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
import '';
|
||||
|
||||
interface CustomMatchers<R = unknown> {
|
||||
equalsUint8Array(other: Uint8Array): R;
|
||||
}
|
||||
|
||||
declare global {
|
||||
namespace jest {
|
||||
interface Expect extends CustomMatchers {}
|
||||
interface Matchers<R> extends CustomMatchers<R> {}
|
||||
interface InverseAsymmetricMatchers extends CustomMatchers {}
|
||||
}
|
||||
}
|
||||
|
||||
function short(o: { toString(): string }): string {
|
||||
const result = o.toString();
|
||||
return result.length > 8 ? result.substring(0, 5) + '...' : result;
|
||||
}
|
||||
|
||||
function smallDiff(a: Uint8Array, b: Uint8Array): string {
|
||||
let result = '';
|
||||
|
||||
if (!(a instanceof Uint8Array)) {
|
||||
result += `${short(a)} is not a Uint8Array`;
|
||||
}
|
||||
if (!(b instanceof Uint8Array)) {
|
||||
result += `${short(b)} is not a Uint8Array`;
|
||||
}
|
||||
if (result.length) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (a.length !== b.length) {
|
||||
return `${short(a)} is not the same length as ${short(b)}: ${a.length} !== ${b.length}`;
|
||||
}
|
||||
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) {
|
||||
result += `first diff at ${i}:\n`;
|
||||
result += ` ${a.subarray(i, Math.min(i + 5, a.length)).toString()}\n`;
|
||||
result += ` ${b.subarray(i, Math.min(i + 5, b.length)).toString()}`;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 'no differences found';
|
||||
}
|
||||
|
||||
expect.extend({
|
||||
/**
|
||||
* Jest matcher for large Uint8Arrays
|
||||
*/
|
||||
equalsUint8Array(received: Uint8Array, other: Uint8Array) {
|
||||
const pass = received.length === other.length && received.every((value, i) => other[i] === value);
|
||||
if (pass) {
|
||||
return {
|
||||
message: () => 'expected arrays not to be equal',
|
||||
pass: true,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
message: () => `expected arrays to be equal: ${smallDiff(received, other)}`,
|
||||
pass: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,545 @@
|
|||
import './matchers';
|
||||
import { ByteArrayByteSource, ProdosOrderedTrackSectorSource, ByteTrackSectorSource, TrackSector5x3NibbleTrackSource, TrackSector6x2NibbleTrackSource, DosOrderedTrackSectorSource } from 'js/formats/sources';
|
||||
import { BYTES_BY_SECTOR_IMAGE, BYTES_BY_TRACK_IMAGE } from './testdata/16sector';
|
||||
import { BYTES_BY_SECTOR_IMAGE as S13_BYTES_BY_SECTOR_IMAGE, BYTES_BY_TRACK_IMAGE as S13_BYTES_BY_TRACK_IMAGE } from './testdata/13sector';
|
||||
import { D13O, DO, PO } from 'js/formats/format_utils';
|
||||
import { expectSequence, findBytes, skipGap } from './util';
|
||||
|
||||
describe('ProdosOrderedTrackSectorSource', () => {
|
||||
it('returns the correct logical sector for each physical sector', () => {
|
||||
const byteSource = new ByteArrayByteSource(BYTES_BY_SECTOR_IMAGE);
|
||||
const byteTrackSectorSource = new ByteTrackSectorSource(byteSource);
|
||||
const prodosTrackSectorSource = new ProdosOrderedTrackSectorSource(byteTrackSectorSource);
|
||||
for (let s = 0; s < 16; s++) {
|
||||
const sector = prodosTrackSectorSource.read(0, s);
|
||||
const expected = new Uint8Array(256).fill(PO[s]);
|
||||
expect(sector).equalsUint8Array(expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('DosOrderedByteTrackSectorSource', () => {
|
||||
it('returns the correct logical sector for each physical sector', () => {
|
||||
const byteSource = new ByteArrayByteSource(BYTES_BY_SECTOR_IMAGE);
|
||||
const byteTrackSectorSource = new ByteTrackSectorSource(byteSource);
|
||||
const dosTrackSectorSource = new DosOrderedTrackSectorSource(byteTrackSectorSource);
|
||||
for (let s = 0; s < 16; s++) {
|
||||
const sector = dosTrackSectorSource.read(0, s);
|
||||
const expected = new Uint8Array(256).fill(DO[s]);
|
||||
expect(sector).equalsUint8Array(expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('TrackSector6x2NibbleTrackSource', () => {
|
||||
const nibbleTrackSource = (image: Uint8Array = BYTES_BY_TRACK_IMAGE) => {
|
||||
const byteSource = new ByteArrayByteSource(image);
|
||||
const byteTrackSectorSource = new ByteTrackSectorSource(byteSource);
|
||||
const prodosTrackSectorSource = new ProdosOrderedTrackSectorSource(byteTrackSectorSource);
|
||||
return new TrackSector6x2NibbleTrackSource(prodosTrackSectorSource, 10);
|
||||
};
|
||||
|
||||
it('has correct number of tracks', () => {
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
expect(trackSectorNibbleTrackSource.numTracks()).toBe(35);
|
||||
});
|
||||
|
||||
it('has correct number of bytes in all tracks', () => {
|
||||
// Track 0 is slightly longer for some reason.
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
expect(trackSectorNibbleTrackSource.read(0).length).toEqual(6632);
|
||||
for (let i = 1; i < trackSectorNibbleTrackSource.numTracks(); i++) {
|
||||
expect(trackSectorNibbleTrackSource.read(i).length).toEqual(6602);
|
||||
}
|
||||
});
|
||||
|
||||
it('has correct GAP 1', () => {
|
||||
// From Beneith Apple DOS, GAP 1 should have 12-85 0xFF bytes
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
const track = trackSectorNibbleTrackSource.read(0);
|
||||
let numFF = 0;
|
||||
while (track[numFF] === 0xFF && numFF < 0x100) {
|
||||
numFF++;
|
||||
}
|
||||
expect(numFF).toBeGreaterThanOrEqual(40);
|
||||
expect(numFF).toBeLessThanOrEqual(128);
|
||||
});
|
||||
|
||||
it('has correct Address Field for track 0, sector 0', () => {
|
||||
// _Beneath Apple DOS_, TRACK FORMATTING, p. 3-12
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
const track = trackSectorNibbleTrackSource.read(0);
|
||||
let i = skipGap(track);
|
||||
// prologue
|
||||
i = expectSequence(track, i, [0xD5, 0xAA, 0x96]);
|
||||
// volume 10 = 0b00001010
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// track 0 = 0b00000000
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// sector 0 = 0b00000000
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// checksum = 0b00000101
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
});
|
||||
|
||||
it('has correct Data Field for track 0, sector 0 (BYTES_BY_TRACK)', () => {
|
||||
// _Beneath Apple DOS_, DATA FIELD ENCODING, pp. 3-13 to 3-21
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
const track = trackSectorNibbleTrackSource.read(0);
|
||||
// skip to the first address epilogue
|
||||
let i = findBytes(track, [0xDE, 0xAA, 0xEB]);
|
||||
expect(i).toBeGreaterThan(50);
|
||||
i = skipGap(track, i);
|
||||
// prologue
|
||||
i = expectSequence(track, i, [0xD5, 0xAA, 0xAD]);
|
||||
// data (all zeros, which is 0x96 with 6 and 2 encoding)
|
||||
for (let j = 0; j < 342; j++) {
|
||||
expect(track[i++]).toBe(0x96);
|
||||
}
|
||||
// checksum (also zero)
|
||||
expect(track[i++]).toBe(0x96);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
});
|
||||
|
||||
it('has correct Address Field for track 0, sector 1', () => {
|
||||
// _Beneath Apple DOS_, TRACK FORMATTING, p. 3-12
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
const track = trackSectorNibbleTrackSource.read(0);
|
||||
// first sector prologue
|
||||
let i = findBytes(track, [0xD5, 0xAA, 0x96]);
|
||||
|
||||
// second sector prologue
|
||||
i = findBytes(track, [0xD5, 0xAA, 0x96], i);
|
||||
// volume 10 = 0b00001010
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// track 0 = 0b00000000
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// sector 1 = 0b00000001
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
expect(track[i++]).toBe(0b10101011);
|
||||
// checksum = 0b00000101
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101011);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
});
|
||||
|
||||
it('has correct Data Field for track 0, sector 1 (BYTES_BY_SECTOR)', () => {
|
||||
// _Beneath Apple DOS_, DATA FIELD ENCODING, pp. 3-13 to 3-21
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource(BYTES_BY_SECTOR_IMAGE);
|
||||
const track = trackSectorNibbleTrackSource.read(0);
|
||||
// First data field prologue
|
||||
let i = findBytes(track, [0xD5, 0xAA, 0xAD]);
|
||||
// Second data field prologue
|
||||
i = findBytes(track, [0xD5, 0xAA, 0xAD], i);
|
||||
// Sector 1 is ProDOS sector 8.
|
||||
// In 6 x 2 encoding, the lowest 2 bits of all the bytes come first.
|
||||
// 0x07 is 0b00001000, so the lowest two bits are 0b00, reversed and
|
||||
// repeated would be 0b000000 (00 -> 0x96). Even though each byte is
|
||||
// XOR'd with the previous, they are all the same. This means there
|
||||
// are 86 0b00000000 (00 -> 0x96) bytes.
|
||||
for (let j = 0; j < 86; j++) {
|
||||
expect(track[i++]).toBe(0x96);
|
||||
}
|
||||
// Next we get 256 instances of the top bits, 0b000010. Again, with
|
||||
// the XOR, this means one 0b000010 XOR 0b000000 = 0b000010
|
||||
// (02 -> 0x9A) followed by 255 0b0000000 (00 -> 0x96).
|
||||
expect(track[i++]).toBe(0x9A);
|
||||
for (let j = 0; j < 255; j++) {
|
||||
expect(track[i++]).toBe(0x96);
|
||||
}
|
||||
// checksum 0b000010 XOR 0b000000 -> 9A
|
||||
expect(track[i++]).toBe(0x9A);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
});
|
||||
|
||||
it('has correct Address Field for track 1, sector 0', () => {
|
||||
// _Beneath Apple DOS_, TRACK FORMATTING, p. 3-12
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
const track = trackSectorNibbleTrackSource.read(1);
|
||||
let i = skipGap(track);
|
||||
// prologue
|
||||
i = expectSequence(track, i, [0xD5, 0xAA, 0x96]);
|
||||
// volume 10 = 0b00001010
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// track 1 = 0b00000001
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
expect(track[i++]).toBe(0b10101011);
|
||||
// sector 0 = 0b00000000
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// checksum = 0b00000100
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101011);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
});
|
||||
|
||||
it('has correct Data Field for track 1, sector 0 (BYTES_BY_TRACK)', () => {
|
||||
// _Beneath Apple DOS_, DATA FIELD ENCODING, pp. 3-13 to 3-21
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource(BYTES_BY_TRACK_IMAGE);
|
||||
const track = trackSectorNibbleTrackSource.read(1);
|
||||
let i = findBytes(track, [0xDE, 0xAA, 0xEB]);
|
||||
expect(i).toBeGreaterThan(50);
|
||||
i = skipGap(track, i);
|
||||
// prologue
|
||||
i = expectSequence(track, i, [0xD5, 0xAA, 0xAD]);
|
||||
// In 6 x 2 encoding, the lowest 2 bits of all the bytes come first.
|
||||
// This would normally mean 86 instances of 0b101010 (2A -> 0xE6),
|
||||
// but each byte is XOR'd with the previous. Since all of the bits
|
||||
// are the same, this means there are 85 0b000000 (00 -> 0x96).
|
||||
expect(track[i++]).toBe(0xE6);
|
||||
for (let j = 0; j < 85; j++) {
|
||||
expect(track[i++]).toBe(0x96);
|
||||
}
|
||||
// Next we get 256 instances of the top bits, 0b000000. Again, with
|
||||
// the XOR, this means one 0x101010 (2A -> 0xE6) followed by 255
|
||||
// 0b0000000 (00 -> 0x96).
|
||||
expect(track[i++]).toBe(0xE6);
|
||||
for (let j = 0; j < 255; j++) {
|
||||
expect(track[i++]).toBe(0x96);
|
||||
}
|
||||
// checksum (also zero)
|
||||
expect(track[i++]).toBe(0x96);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
});
|
||||
|
||||
it('has correct Address Fields for all tracks', () => {
|
||||
// _Beneath Apple DOS_, TRACK FORMATTING, p. 3-12
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource(BYTES_BY_TRACK_IMAGE);
|
||||
|
||||
for (let t = 0; t < trackSectorNibbleTrackSource.numTracks(); t++) {
|
||||
// We essentially seek through the track for the Address Fields
|
||||
const track = trackSectorNibbleTrackSource.read(t);
|
||||
let i = findBytes(track, [0xD5, 0xAA, 0x96]);
|
||||
for (let s = 0; s <= 15; s++) {
|
||||
// volume 10 = 0b00001010
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// convert track to 4x4 encoding
|
||||
const track4x4XX = ((t & 0b10101010) >> 1) | 0b10101010;
|
||||
const track4x4YY = (t & 0b01010101) | 0b10101010;
|
||||
expect(track[i++]).toBe(track4x4XX);
|
||||
expect(track[i++]).toBe(track4x4YY);
|
||||
// convert sector to 4x4 encoding
|
||||
const sector4x4XX = ((s & 0b10101010) >> 1) | 0b10101010;
|
||||
const sector4x4YY = (s & 0b01010101) | 0b10101010;
|
||||
expect(track[i++]).toBe(sector4x4XX);
|
||||
expect(track[i++]).toBe(sector4x4YY);
|
||||
// checksum
|
||||
expect(track[i++]).toBe(0b10101111 ^ track4x4XX ^ sector4x4XX);
|
||||
expect(track[i++]).toBe(0b10101010 ^ track4x4YY ^ sector4x4YY);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
// next sector
|
||||
i = findBytes(track, [0xD5, 0xAA, 0x96], i);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('TrackSector5x3NibbleTrackSource', () => {
|
||||
const nibbleTrackSource = (image: Uint8Array = S13_BYTES_BY_TRACK_IMAGE) => {
|
||||
const byteSource = new ByteArrayByteSource(image);
|
||||
const dos32TrackSectorSource = new ByteTrackSectorSource(byteSource, 13);
|
||||
return new TrackSector5x3NibbleTrackSource(dos32TrackSectorSource, 10);
|
||||
};
|
||||
|
||||
it('has correct number of tracks', () => {
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
expect(trackSectorNibbleTrackSource.numTracks()).toBe(35);
|
||||
});
|
||||
|
||||
it('has correct number of bytes in all tracks', () => {
|
||||
// Track 0 is slightly longer for some reason.
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
expect(trackSectorNibbleTrackSource.read(0).length).toEqual(6289);
|
||||
for (let i = 1; i < trackSectorNibbleTrackSource.numTracks(); i++) {
|
||||
expect(trackSectorNibbleTrackSource.read(i).length).toEqual(6265);
|
||||
}
|
||||
});
|
||||
|
||||
it('has correct GAP 1', () => {
|
||||
// From Beneith Apple DOS, GAP 1 should have 12-85 0xFF bytes
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
const track = trackSectorNibbleTrackSource.read(0);
|
||||
let numFF = 0;
|
||||
while (track[numFF] === 0xFF && numFF < 0x100) {
|
||||
numFF++;
|
||||
}
|
||||
expect(numFF).toBeGreaterThanOrEqual(40);
|
||||
expect(numFF).toBeLessThanOrEqual(128);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/no-identical-title
|
||||
it('has correct GAP 1', () => {
|
||||
// From Beneith Apple DOS, GAP 1 should have 12-85 0xFF bytes
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
const track = trackSectorNibbleTrackSource.read(0);
|
||||
let numFF = 0;
|
||||
while (track[numFF] === 0xFF && numFF < 0x100) {
|
||||
numFF++;
|
||||
}
|
||||
expect(numFF).toBeGreaterThanOrEqual(40);
|
||||
expect(numFF).toBeLessThanOrEqual(128);
|
||||
});
|
||||
|
||||
it('has correct Address Field for track 0, sector 0', () => {
|
||||
// _Beneath Apple DOS_, TRACK FORMATTING, p. 3-12
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
const track = trackSectorNibbleTrackSource.read(0);
|
||||
let i = skipGap(track);
|
||||
// prologue
|
||||
i = expectSequence(track, i, [0xD5, 0xAA, 0xB5]);
|
||||
// volume 10 = 0b00001010
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// track 0 = 0b00000000
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// sector 0 = 0b00000000
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// checksum = 0b00000101
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
});
|
||||
|
||||
it('has correct Data Field for track 0, sector 0 (BYTES_BY_TRACK)', () => {
|
||||
// _Beneath Apple DOS_, DATA FIELD ENCODING, pp. 3-13 to 3-21
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource(S13_BYTES_BY_TRACK_IMAGE);
|
||||
const track = trackSectorNibbleTrackSource.read(0);
|
||||
// skip to the first address epilogue
|
||||
let i = findBytes(track, [0xDE, 0xAA, 0xEB]);
|
||||
expect(i).toBeGreaterThan(50);
|
||||
i = skipGap(track, i);
|
||||
// prologue
|
||||
i = expectSequence(track, i, [0xD5, 0xAA, 0xAD]);
|
||||
// data (all zeros, which is 0xAB with 5 and 3 encoding)
|
||||
for (let j = 0; j < 410; j++) {
|
||||
expect(track[i++]).toBe(0xAB);
|
||||
}
|
||||
// checksum (also zero)
|
||||
expect(track[i++]).toBe(0xAB);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
});
|
||||
|
||||
it('has correct Address Field for track 0, sector 1', () => {
|
||||
// _Beneath Apple DOS_, TRACK FORMATTING, p. 3-12
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource(S13_BYTES_BY_TRACK_IMAGE);
|
||||
const track = trackSectorNibbleTrackSource.read(0);
|
||||
// first sector prologue
|
||||
let i = findBytes(track, [0xD5, 0xAA, 0xB5]);
|
||||
|
||||
// second sector prologue
|
||||
i = findBytes(track, [0xD5, 0xAA, 0xB5], i);
|
||||
// volume 10 = 0b00001010
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// track 0 = 0b00000000
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// sector A = 0b00001010
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// checksum = 0b00000101
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
});
|
||||
|
||||
it('has correct Data Field for track 0, disk sector 1 (BYTES_BY_SECTOR)', () => {
|
||||
// _Beneath Apple DOS_, DATA FIELD ENCODING, pp. 3-13 to 3-21
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource(S13_BYTES_BY_SECTOR_IMAGE);
|
||||
const track = trackSectorNibbleTrackSource.read(0);
|
||||
// First data field prologue
|
||||
let i = findBytes(track, [0xD5, 0xAA, 0xAD]);
|
||||
// Second data field prologue
|
||||
i = findBytes(track, [0xD5, 0xAA, 0xAD], i);
|
||||
|
||||
// Sector 1 is physical/DOS sector A.
|
||||
// In 5 x 3 encoding, the lowest 3 bits of all the bytes come first,
|
||||
// all mixed up in a crazy order. 0x0A is 0b00001010, so the lowest
|
||||
// 3 bits are 0b010. With mixing (see Figure 3.18), this becomes:
|
||||
// 0b01000, 0b01011, 0b01000
|
||||
// repeated. These chunks come in repeated blocks of 0x33 (51) bytes.
|
||||
//
|
||||
// Because 51 * 5 is 255, there is one odd byte that is treated
|
||||
// specially at the beginning.
|
||||
//
|
||||
// Lower 3 bits of last byte:
|
||||
// 0b00010 = 0b00010 (02 -> AE)
|
||||
expect(track[i++]).toBe(0xAE);
|
||||
//
|
||||
// Bottom 3 bits in block 1 (08 block):
|
||||
// 0b01000 XOR 0b00010 = 0b01010 (0A -> BE)
|
||||
// 0b01000 XOR 0b01000 = 0b00000 (00 -> AB) x 50
|
||||
expect(track[i++]).toBe(0xBE);
|
||||
for (let j = 0; j < 50; j++) {
|
||||
expect(track[i++]).toBe(0xAB);
|
||||
}
|
||||
//
|
||||
// Bottom 3 bits in block 2 (0B block):
|
||||
// 0b01011 XOR 0b01000 = 0b00011 (03 -> AF)
|
||||
// 0b01011 XOR 0b01011 = 0b00000 (00 -> AB) x 50
|
||||
expect(track[i++]).toBe(0xAF);
|
||||
for (let j = 0; j < 50; j++) {
|
||||
expect(track[i++]).toBe(0xAB);
|
||||
}
|
||||
//
|
||||
// Bottom 3 bits in block 1 (08 block):
|
||||
// 0b01000 XOR 0b01011 = 0b00011 (03 -> AF)
|
||||
// 0b01000 XOR 0b01000 = 0b00000 (00 -> AB) x 50
|
||||
expect(track[i++]).toBe(0xAF);
|
||||
for (let j = 0; j < 50; j++) {
|
||||
expect(track[i++]).toBe(0xAB);
|
||||
}
|
||||
// Upper 5 bits of 0x0A are 0x00001:
|
||||
// 0b00001 XOR 0b01000 = 0b01001 (09 -> BD)
|
||||
// 0b00001 XOR 0b00001 = 0b00000 (00 -> AB) x 255
|
||||
expect(track[i++]).toBe(0xBD);
|
||||
for (let j = 0; j < 255; j++) {
|
||||
expect(track[i++]).toBe(0xAB);
|
||||
}
|
||||
|
||||
// checksum 0b00001 (01 -> AD)
|
||||
expect(track[i++]).toBe(0xAD);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
});
|
||||
|
||||
it('has correct Address Field for track 1, sector 0', () => {
|
||||
// _Beneath Apple DOS_, TRACK FORMATTING, p. 3-12
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
const track = trackSectorNibbleTrackSource.read(1);
|
||||
let i = skipGap(track);
|
||||
// prologue
|
||||
i = expectSequence(track, i, [0xD5, 0xAA, 0xB5]);
|
||||
// volume 10 = 0b00001010
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// track 1 = 0b00000001
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
expect(track[i++]).toBe(0b10101011);
|
||||
// sector 0 = 0b00000000
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// checksum = 0b00000100
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101011);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
});
|
||||
|
||||
it('has correct Data Field for track 1, sector 0 (BYTES_BY_TRACK)', () => {
|
||||
// _Beneath Apple DOS_, DATA FIELD ENCODING, pp. 3-13 to 3-21
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource(S13_BYTES_BY_TRACK_IMAGE);
|
||||
const track = trackSectorNibbleTrackSource.read(1);
|
||||
let i = findBytes(track, [0xDE, 0xAA, 0xEB]);
|
||||
expect(i).toBeGreaterThan(50);
|
||||
i = skipGap(track, i);
|
||||
// prologue
|
||||
i = expectSequence(track, i, [0xD5, 0xAA, 0xAD]);
|
||||
|
||||
// Expect data to be all 1s (track number).
|
||||
|
||||
// In 5 x 3 encoding, the lowest 3 bits of all the bytes come first,
|
||||
// all mixed up in a crazy order. 0x01 is 0b00000001, so the lowest
|
||||
// 3 bits are 0b001. With mixing (see Figure 3.18), this becomes:
|
||||
// 0b00111, 0b00100, 0b00100
|
||||
// repeated. These chunks come in repeated blocks of 0x33 (51) bytes.
|
||||
//
|
||||
// Because 51 * 5 is 255, there is one odd byte that is treated
|
||||
// specially at the beginning.
|
||||
//
|
||||
// Lower 3 bits of last byte:
|
||||
// 0b00001 = 0b00001 (01 -> AD)
|
||||
expect(track[i++]).toBe(0xAD);
|
||||
//
|
||||
// Bottom 3 bits in block 1 (07 block):
|
||||
// 0b00111 XOR 0b00001 = 0b00110 (06 -> B7)
|
||||
// 0b00111 XOR 0b00111 = 0b00000 (00 -> AB) x 50
|
||||
expect(track[i++]).toBe(0xB7);
|
||||
for (let j = 0; j < 50; j++) {
|
||||
expect(track[i++]).toBe(0xAB);
|
||||
}
|
||||
//
|
||||
// Bottom 3 bits in block 2 (04 block):
|
||||
// 0b00111 XOR 0b00100 = 0b00011 (03 -> AF)
|
||||
// 0b00100 XOR 0b00100 = 0b00000 (00 -> AB) x 50
|
||||
expect(track[i++]).toBe(0xAF);
|
||||
for (let j = 0; j < 50; j++) {
|
||||
expect(track[i++]).toBe(0xAB);
|
||||
}
|
||||
//
|
||||
// Bottom 3 bits in block 1 (04 block):
|
||||
// 0b00100 XOR 0b00100 = 0b00011 (00 -> AB)
|
||||
// 0b00100 XOR 0b00100 = 0b00000 (00 -> AB) x 50
|
||||
expect(track[i++]).toBe(0xAB);
|
||||
for (let j = 0; j < 50; j++) {
|
||||
expect(track[i++]).toBe(0xAB);
|
||||
}
|
||||
// Upper 5 bits of 0x01 are 0x00000:
|
||||
// 0b00000 XOR 0b00100 = 0b00100 (04 -> B5)
|
||||
// 0b00000 XOR 0b00000 = 0b00000 (00 -> AB) x 255
|
||||
expect(track[i++]).toBe(0xB5);
|
||||
for (let j = 0; j < 255; j++) {
|
||||
expect(track[i++]).toBe(0xAB);
|
||||
}
|
||||
|
||||
// checksum 0b00000 (00 -> AB)
|
||||
expect(track[i++]).toBe(0xAB);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
});
|
||||
|
||||
it('has correct Address Fields for all tracks', () => {
|
||||
// _Beneath Apple DOS_, TRACK FORMATTING, p. 3-12
|
||||
const trackSectorNibbleTrackSource = nibbleTrackSource();
|
||||
|
||||
for (let t = 0; t < trackSectorNibbleTrackSource.numTracks(); t++) {
|
||||
// We essentially seek through the track for the Address Fields
|
||||
const track = trackSectorNibbleTrackSource.read(t);
|
||||
let i = findBytes(track, [0xD5, 0xAA, 0xB5]);
|
||||
for (let s = 0; s <= 12; s++) {
|
||||
// volume 10 = 0b00001010
|
||||
expect(track[i++]).toBe(0b10101111);
|
||||
expect(track[i++]).toBe(0b10101010);
|
||||
// convert track to 4x4 encoding
|
||||
const track4x4XX = ((t & 0b10101010) >> 1) | 0b10101010;
|
||||
const track4x4YY = (t & 0b01010101) | 0b10101010;
|
||||
expect(track[i++]).toBe(track4x4XX);
|
||||
expect(track[i++]).toBe(track4x4YY);
|
||||
// convert sector to 4x4 encoding
|
||||
const ss = D13O[s];
|
||||
const sector4x4XX = ((ss & 0b10101010) >> 1) | 0b10101010;
|
||||
const sector4x4YY = (ss & 0b01010101) | 0b10101010;
|
||||
expect(track[i++]).toBe(sector4x4XX);
|
||||
expect(track[i++]).toBe(sector4x4YY);
|
||||
// checksum
|
||||
expect(track[i++]).toBe(0b10101111 ^ track4x4XX ^ sector4x4XX);
|
||||
expect(track[i++]).toBe(0b10101010 ^ track4x4YY ^ sector4x4YY);
|
||||
// epilogue
|
||||
i = expectSequence(track, i, [0xDE, 0xAA, 0xEB]);
|
||||
// next sector
|
||||
i = findBytes(track, [0xD5, 0xAA, 0xB5], i);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,4 +1,5 @@
|
|||
import { memory } from 'js/types';
|
||||
import { concat } from 'js/util';
|
||||
|
||||
function generateBytesInOrder() {
|
||||
const data: memory[][] = [];
|
||||
|
@ -53,3 +54,25 @@ function generateBytesByTrack() {
|
|||
}
|
||||
|
||||
export const BYTES_BY_TRACK: memory[][] = generateBytesByTrack();
|
||||
|
||||
function toImage(disk: memory[][]) {
|
||||
const tracks: Uint8Array[] = [];
|
||||
for (let t = 0; t < disk.length; t++) {
|
||||
const track = concat(...disk[t]);
|
||||
tracks.push(track);
|
||||
}
|
||||
return concat(...tracks);
|
||||
}
|
||||
|
||||
export const BYTES_BY_SECTOR_IMAGE = toImage(BYTES_BY_SECTOR);
|
||||
export const BYTES_BY_TRACK_IMAGE = toImage(BYTES_BY_TRACK);
|
||||
|
||||
function randomImage() {
|
||||
const result = new Uint8Array(35 * 13 * 256);
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
result[i] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export const RANDOM_IMAGE = randomImage();
|
||||
|
|
Loading…
Reference in New Issue