Keep track of disk sides (#87)

Disk side information was being dropped and thus not displayable in the UI. This plumbs the value through various formats and adds some light testing.

Also fixes an issue where URL encoded hashes were not properly interpreted.
This commit is contained in:
Will Scullin 2021-10-02 11:45:09 -07:00 committed by GitHub
parent 52f9c3e99e
commit 2daef8040f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 100 additions and 28 deletions

View File

@ -165,7 +165,7 @@ const PHASE_DELTA = [
export interface Callbacks {
driveLight: (drive: DriveNumber, on: boolean) => void;
dirty: (drive: DriveNumber, dirty: boolean) => void;
label: (drive: DriveNumber, name: string) => void;
label: (drive: DriveNumber, name?: string, side?: string) => void;
}
/** Common information for Nibble and WOZ disks. */
@ -177,6 +177,8 @@ interface BaseDrive {
volume: byte,
/** Displayed disk name */
name: string,
/** (Optional) Disk side (Front/Back, A/B) */
side?: string,
/** Quarter track position of read/write head. */
track: byte,
/** Position of the head on the track. */
@ -222,6 +224,7 @@ interface DriveState {
encoding: typeof ENCODING_BITSTREAM | typeof ENCODING_NIBBLE
volume: byte,
name: string,
side?: string,
tracks: memory[],
track: byte,
head: byte,
@ -247,6 +250,7 @@ function getDriveState(drive: Drive): DriveState {
encoding: drive.encoding,
volume: drive.volume,
name: drive.name,
side: drive.side,
tracks: [],
track: drive.track,
head: drive.head,
@ -279,6 +283,7 @@ function setDriveState(state: DriveState) {
encoding: ENCODING_NIBBLE,
volume: state.volume,
name: state.name,
side: state.side,
tracks: [],
track: state.track,
head: state.head,
@ -295,6 +300,7 @@ function setDriveState(state: DriveState) {
encoding: ENCODING_BITSTREAM,
volume: state.volume,
name: state.name,
side: state.side,
track: state.track,
head: state.head,
phase: state.phase,
@ -752,9 +758,10 @@ export default class DiskII implements Card {
for (const d of DRIVE_NUMBERS) {
const idx = d - 1;
this.drives[idx] = setDriveState(state.drives[idx]);
this.callbacks.label(d, state.drives[idx].name);
const { name, side, dirty } = state.drives[idx];
this.callbacks.label(d, name, side);
this.callbacks.driveLight(d, this.on);
this.callbacks.dirty(d, this.drives[idx].dirty);
this.callbacks.dirty(d, dirty);
}
this.cur = this.drives[this.drive - 1];
}
@ -800,7 +807,7 @@ export default class DiskII implements Card {
const cur = this.drives[drive - 1];
Object.assign(cur, disk);
this.updateDirty(drive, false);
this.callbacks.label(drive, disk.name);
this.callbacks.label(drive, disk.name, disk.side);
return true;
}
}
@ -858,9 +865,10 @@ export default class DiskII implements Card {
const disk = createDisk(fmt, options);
if (disk) {
const cur = this.drives[drive - 1];
const { name, side } = cur;
Object.assign(cur, disk);
this.updateDirty(drive, true);
this.callbacks.label(drive, name);
this.callbacks.label(drive, name, side);
return true;
}
@ -880,8 +888,9 @@ export default class DiskII implements Card {
if (disk) {
const cur = this.drives[drive - 1];
Object.assign(cur, disk);
const { name, side } = cur;
this.updateDirty(drive, true);
this.callbacks.label(drive, disk.name);
this.callbacks.label(drive, name, side);
}
}
break;

View File

@ -46,6 +46,7 @@ export function createDiskFromJsonDisk(disk: JSONDisk): FloppyDisk | null {
const fmt = disk.type;
const readOnly = disk.readOnly;
const name = disk.name;
const side = disk.disk;
if (includes(NIBBLE_FORMATS, fmt)) {
let trackData: memory[][];
@ -53,11 +54,11 @@ export function createDiskFromJsonDisk(disk: JSONDisk): FloppyDisk | null {
trackData = [];
for (let t = 0; t < disk.data.length; t++) {
trackData[t] = [];
if (fmt == 'nib') {
trackData[t][0] = base64_decode(disk.data[t] as string);
if (disk.type === 'nib') {
trackData[t][0] = base64_decode(disk.data[t]);
} else {
for (let s = 0; s < disk.data[t].length; s++) {
trackData[t][s] = base64_decode(disk.data[t][s] as string);
trackData[t][s] = base64_decode(disk.data[t][s]);
}
}
}
@ -71,6 +72,7 @@ export function createDiskFromJsonDisk(disk: JSONDisk): FloppyDisk | null {
volume,
readOnly,
name,
side,
data: trackData
} as DiskOptions;

View File

@ -18,11 +18,12 @@ import { NibbleDisk, DiskOptions, ENCODING_NIBBLE } from './types';
* @returns A nibblized disk
*/
export default function createDiskFromDOS13(options: DiskOptions) {
const { data, name, rawData, volume, readOnly } = options;
const { data, name, side, rawData, volume, readOnly } = options;
const disk: NibbleDisk = {
format: 'd13',
encoding: ENCODING_NIBBLE,
name,
side,
volume,
readOnly,
tracks: []

View File

@ -20,11 +20,12 @@ import { NibbleDisk, DiskOptions, ENCODING_NIBBLE } from './types';
* @returns A nibblized disk
*/
export default function createDiskFromDOS(options: DiskOptions): NibbleDisk {
const { data, name, rawData, volume, readOnly } = options;
const { data, name, side, rawData, volume, readOnly } = options;
const disk: NibbleDisk = {
format: 'dsk',
encoding: ENCODING_NIBBLE,
name,
side,
volume,
readOnly,
tracks: [],

View File

@ -18,11 +18,12 @@ import { memory } from '../types';
* @returns A nibblized disk
*/
export default function createDiskFromNibble(options: DiskOptions): NibbleDisk {
const { data, name, rawData, volume, readOnly } = options;
const { data, name, side, rawData, volume, readOnly } = options;
const disk: NibbleDisk = {
format: 'nib',
encoding: ENCODING_NIBBLE,
name,
side,
volume: volume || 254,
readOnly: readOnly || false,
tracks: []

View File

@ -20,11 +20,12 @@ import { NibbleDisk, DiskOptions, ENCODING_NIBBLE } from './types';
* @returns A nibblized disk
*/
export default function createDiskFromProDOS(options: DiskOptions) {
const { data, name, rawData, volume, readOnly } = options;
const { data, name, side, rawData, volume, readOnly } = options;
const disk: NibbleDisk = {
format: 'nib',
encoding: ENCODING_NIBBLE,
name,
side,
volume: volume || 254,
tracks: [],
readOnly: readOnly || false,

View File

@ -21,6 +21,7 @@ export type DriveNumber = MemberOf<typeof DRIVE_NUMBERS>;
export interface DiskOptions {
name: string
side?: string
volume: byte
readOnly: boolean
data?: memory[][]
@ -35,6 +36,7 @@ export interface DiskOptions {
export interface Disk {
name: string
side?: string
readOnly: boolean
}
@ -97,19 +99,29 @@ export type DiskFormat = MemberOf<typeof DISK_FORMATS>;
export class JSONDiskBase {
type: DiskFormat
name: string
disk?: number
disk?: string
category?: string
writeProtected?: boolean
volume: byte
readOnly: boolean
volume?: byte
readOnly?: boolean
gamepad?: GamepadConfiguration
}
/**
* JSON Disk format with base64 encoded tracks
* JSON Disk format with base64 encoded tracks with sectors
*/
export interface Base64JSONDisk extends JSONDiskBase {
type: Exclude<DiskFormat, 'nib'>
encoding: 'base64'
data: string[][]
}
/**
* JSON Disk format with base64 encoded nibblized tracks
*/
export interface Base64JSONNibbleDisk extends JSONDiskBase {
type: 'nib'
encoding: 'base64'
data: string[]
}
@ -119,6 +131,7 @@ export interface Base64JSONDisk extends JSONDiskBase {
*/
export interface BinaryJSONDisk extends JSONDiskBase {
type: DiskFormat
encoding: 'binary'
data: memory[][]
}
@ -127,7 +140,7 @@ export interface BinaryJSONDisk extends JSONDiskBase {
* General JSON Disk format
*/
export type JSONDisk = Base64JSONDisk | BinaryJSONDisk;
export type JSONDisk = Base64JSONDisk | Base64JSONNibbleDisk | BinaryJSONDisk;
/**
* Process Disk message payloads for worker

View File

@ -297,13 +297,16 @@ export default function createDiskFromWoz(options: DiskOptions): WozDisk {
debug(chunks);
const { meta, tmap, trks } = chunks;
const disk: WozDisk = {
encoding: ENCODING_BITSTREAM,
trackMap: chunks.tmap?.trackMap || [],
tracks: chunks.trks?.tracks || [],
rawTracks: chunks.trks?.rawTracks || [],
trackMap: tmap?.trackMap || [],
tracks: trks?.tracks || [],
rawTracks: trks?.rawTracks || [],
readOnly: true, //chunks.info.writeProtected === 1;
name: chunks.meta?.values['title'] || options.name
name: meta?.values['title'] || options.name,
side: meta?.values['side_name'] || meta?.values['side'],
};
return disk;

View File

@ -844,7 +844,8 @@ function gup(name: string) {
/** Returns the URL fragment. */
function hup() {
const regex = new RegExp('#(.*)');
const results = regex.exec(window.location.hash);
const hash = decodeURIComponent(window.location.hash);
const results = regex.exec(hash);
if (!results)
return '';
else

View File

@ -14,11 +14,11 @@ export default class DriveLights implements Callbacks {
// document.querySelector('#disksave' + drive).disabled = !dirty;
}
public label(drive: DriveNumber, label?: string) {
public label(drive: DriveNumber, label?: string, side?: string) {
const labelElement =
document.querySelector('#disk-label' + drive)! as HTMLElement;
if (label) {
labelElement.innerText = label;
labelElement.innerText = label + (side ? ` - ${side}` : '');
}
return labelElement.innerText;
}

View File

@ -0,0 +1,17 @@
import { createDiskFromJsonDisk } from 'js/formats/create_disk';
import { testDisk } from './testdata/json';
describe('createDiskFromJsonDisk', () => {
it('parses a JSON disk', () => {
const disk = createDiskFromJsonDisk(testDisk);
expect(disk).toEqual({
encoding: 'nibble',
format: 'dsk',
name: 'Test Disk',
readOnly: undefined,
side: 'Front',
volume: 254,
tracks: expect.any(Array)
});
});
});

22
test/js/formats/testdata/json.ts vendored Normal file
View File

@ -0,0 +1,22 @@
import { base64_encode } from 'js/base64';
import { JSONDisk } from 'js/formats/types';
export const testDisk: JSONDisk = {
name: 'Test Disk',
disk: 'Front',
category: 'Test',
type: 'dsk',
encoding: 'base64',
data: []
};
const sector = new Uint8Array(256);
sector.fill(0);
for (let idx = 0; idx < 35; idx++) {
const track: string[] = [];
for (let jdx = 0; jdx < 16; jdx++) {
track.push(base64_encode(sector));
}
testDisk.data.push(track);
}

View File

@ -108,8 +108,8 @@ const mockTRKS2 = [
* META structures
*/
const mockMETA1 = 'title\tMock Woz 1';
const mockMETA2 = 'title\tMock Woz 2';
const mockMETA1 = 'title\tMock Woz 1\t';
const mockMETA2 = 'title\tMock Woz 2\nside_name\tB';
/**
* Woz Version 1

View File

@ -61,6 +61,7 @@ describe('woz', () => {
const disk = createDiskFromWoz(options) as WozDisk;
expect(disk).toEqual({
name: 'Mock Woz 2',
side: 'B',
readOnly: true,
encoding: ENCODING_BITSTREAM,
trackMap: mockTMAP,