js-dos 8.4.0 → 8.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.deployment.md +29 -14
- package/README.md +38 -25
- package/dist/emulators/emulators.js +1 -1
- package/dist/emulators/emulators.js.map +1 -1
- package/dist/emulators/sockdrive.js.map +1 -1
- package/dist/emulators/types/dos/dosbox/ts/audio-worklet.d.ts +1 -0
- package/dist/emulators/types/dos/dosbox/ts/direct.d.ts +1 -0
- package/dist/emulators/types/emulators.d.ts +4 -0
- package/dist/emulators/types/impl/emulators-impl.d.ts +3 -0
- package/dist/emulators/types/impl/modules.d.ts +5 -1
- package/dist/emulators/types/protocol/mini-lz4.d.ts +3 -0
- package/dist/emulators/types/sockdrive/sockdrive-store.d.ts +36 -0
- package/dist/emulators/types/sockdrive/sockdrive.d.ts +29 -0
- package/dist/emulators/wdosbox-x-dbg.js +49 -0
- package/dist/emulators/wdosbox-x-dbg.wasm +0 -0
- package/dist/emulators/wdosbox-x-jspi.js +2 -2
- package/dist/emulators/wdosbox-x-jspi.js.symbols +10252 -9988
- package/dist/emulators/wdosbox-x-jspi.wasm +0 -0
- package/dist/emulators/wdosbox-x.js +2 -2
- package/dist/emulators/wdosbox-x.js.symbols +10317 -10053
- package/dist/emulators/wdosbox-x.wasm +0 -0
- package/dist/emulators/wdosbox.js +1 -1
- package/dist/emulators/wdosbox.js.symbols +1616 -1617
- package/dist/emulators/wdosbox.wasm +0 -0
- package/dist/js-dos.js +2 -2
- package/dist/js-dos.js.map +1 -1
- package/package.json +2 -2
- package/release.zip +0 -0
- package/scripts/deploy-dz-nightly.sh +14 -0
- package/scripts/deploy-dz.sh +14 -0
|
@@ -1 +1,2 @@
|
|
|
1
1
|
export declare function createAudioPort(): Promise<MessagePort | undefined>;
|
|
2
|
+
export declare const audioWorkletCode = "\nclass SampleRing {\n constructor(capacity) {\n this.samples = new Float32Array(capacity);\n this.readPosition = 0;\n this.writePosition = 0;\n this.samplesCount = 0;\n }\n\n clear() {\n this.readPosition = 0;\n this.writePosition = 0;\n this.samplesCount = 0;\n }\n\n length() {\n return this.samplesCount;\n }\n\n push(sample) {\n if (this.samplesCount === this.samples.length) {\n this.discard(1);\n }\n this.samples[this.writePosition] = sample;\n this.writePosition = (this.writePosition + 1) % this.samples.length;\n this.samplesCount++;\n }\n\n pushArray(samples) {\n for (let i = 0; i < samples.length; ++i) {\n this.push(samples[i]);\n }\n }\n\n peek(offset) {\n return this.samples[(this.readPosition + offset) % this.samples.length];\n }\n\n shift() {\n const sample = this.peek(0);\n this.discard(1);\n return sample;\n }\n\n discard(count) {\n count = Math.min(count, this.samplesCount);\n this.readPosition = (this.readPosition + count) % this.samples.length;\n this.samplesCount -= count;\n }\n}\n\nclass Processor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.outputRate = sampleRate;\n this.sourceRate = sampleRate;\n this.targetFrames = Math.max(1024, Math.round(this.outputRate * 2048 / 44100));\n this.maxFrames = this.targetFrames * 4;\n this.frameSize = 512;\n this.hopSize = this.frameSize / 2;\n this.searchSize = 64;\n this.sourceQueue = new SampleRing(this.maxFrames * 4);\n this.queue = new SampleRing(this.maxFrames);\n this.outputQueue = new SampleRing(this.frameSize * 4);\n this.tail = new Float32Array(this.hopSize);\n this.sourcePosition = 0;\n this.tempo = 1;\n this.analysisDebt = 0;\n this.started = false;\n this.haveTail = false;\n this.lastOutput = 0;\n\n this.port.onmessage = (event) => {\n const message = event.data;\n if (message instanceof Float32Array) {\n this.sourceQueue.pushArray(message);\n this.resampleInput();\n } else if (message && message.type === \"init\") {\n this.initialize(message.sourceRate);\n }\n };\n }\n\n initialize(sourceRate) {\n this.sourceRate = sourceRate > 0 ? sourceRate : this.outputRate;\n this.sourceQueue.clear();\n this.queue.clear();\n this.outputQueue.clear();\n this.sourcePosition = 0;\n this.tempo = 1;\n this.analysisDebt = 0;\n this.started = false;\n this.haveTail = false;\n this.lastOutput = 0;\n }\n\n resampleInput() {\n const step = this.sourceRate / this.outputRate;\n while (this.sourcePosition + 1 < this.sourceQueue.length()) {\n const index = Math.floor(this.sourcePosition);\n const fraction = this.sourcePosition - index;\n const first = this.sourceQueue.peek(index);\n const second = this.sourceQueue.peek(index + 1);\n this.queue.push(first + (second - first) * fraction);\n this.sourcePosition += step;\n }\n\n const consumed = Math.floor(this.sourcePosition);\n if (consumed > 0) {\n this.sourceQueue.discard(consumed);\n this.sourcePosition -= consumed;\n }\n }\n\n prepareFirstFrame() {\n if (this.queue.length() < this.frameSize) {\n return false;\n }\n for (let i = 0; i < this.hopSize; ++i) {\n this.outputQueue.push(this.queue.peek(i) * (i + 1) / this.hopSize);\n this.tail[i] = this.queue.peek(this.hopSize + i);\n }\n this.haveTail = true;\n return true;\n }\n\n updateTempo() {\n const error = (this.queue.length() - this.targetFrames) / this.targetFrames;\n const desired = Math.max(0.95, Math.min(1.05, 1 + error * 0.1));\n const change = Math.max(-0.002, Math.min(0.002, desired - this.tempo));\n this.tempo += change;\n }\n\n findBestFrame(expected) {\n const minimum = Math.max(1, expected - this.searchSize);\n const maximum = Math.min(\n expected + this.searchSize,\n this.queue.length() - this.frameSize,\n );\n if (maximum < minimum) {\n return -1;\n }\n\n let bestPosition = Math.min(expected, maximum);\n let bestScore = Number.POSITIVE_INFINITY;\n for (let position = minimum; position <= maximum; ++position) {\n let difference = 0;\n for (let i = 0; i < this.hopSize; i += 4) {\n const delta = this.tail[i] - this.queue.peek(position + i);\n difference += delta * delta;\n }\n const distancePenalty = Math.abs(position - expected) * 0.002;\n const score = difference + distancePenalty;\n if (score < bestScore) {\n bestScore = score;\n bestPosition = position;\n }\n }\n return bestPosition;\n }\n\n generateFrame() {\n if (!this.haveTail) {\n return this.prepareFirstFrame();\n }\n\n this.updateTempo();\n const idealAdvance = this.hopSize * this.tempo;\n const expected = Math.max(1, Math.round(idealAdvance + this.analysisDebt));\n const position = this.findBestFrame(expected);\n if (position < 0) {\n return false;\n }\n this.analysisDebt += idealAdvance - position;\n this.analysisDebt = Math.max(-this.searchSize, Math.min(this.searchSize, this.analysisDebt));\n\n this.queue.discard(position);\n for (let i = 0; i < this.hopSize; ++i) {\n const mix = i / this.hopSize;\n const sample = this.tail[i] * (1 - mix) + this.queue.peek(i) * mix;\n this.outputQueue.push(sample);\n this.tail[i] = this.queue.peek(this.hopSize + i);\n }\n return true;\n }\n\n stopForUnderflow() {\n if (this.haveTail) {\n this.queue.discard(this.hopSize);\n }\n this.started = false;\n this.haveTail = false;\n this.outputQueue.clear();\n this.tempo = 1;\n this.analysisDebt = 0;\n }\n\n writeTo(output) {\n if (!this.started && this.queue.length() >= this.targetFrames) {\n this.started = true;\n }\n\n while (this.started && this.outputQueue.length() < output.length) {\n if (!this.generateFrame()) {\n break;\n }\n }\n\n let written = 0;\n while (written < output.length && this.outputQueue.length() > 0) {\n this.lastOutput = this.outputQueue.shift();\n output[written++] = this.lastOutput;\n }\n\n if (written < output.length) {\n const missing = output.length - written;\n for (let i = 0; i < missing; ++i) {\n output[written + i] = this.lastOutput * (1 - (i + 1) / missing);\n }\n this.lastOutput = 0;\n if (this.started) {\n this.stopForUnderflow();\n }\n }\n }\n\n bufferedFrames() {\n return this.queue.length() + this.outputQueue.length();\n }\n\n process(inputs, outputs) {\n const channels = outputs[0];\n this.writeTo(channels[0]);\n for (let channel = 1; channel < channels.length; ++channel) {\n channels[channel].set(channels[0]);\n }\n return true;\n }\n}\n\nregisterProcessor(\"jsdos-audio\", Processor);\n";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { WasmModule } from "../../../impl/modules";
|
|
2
2
|
import { TransportLayer, Net } from "../../../protocol/protocol";
|
|
3
|
+
import "../../../sockdrive/sockdrive";
|
|
3
4
|
export declare function dosDirect(wasmModule: WasmModule, sessionId: string, canvas?: OffscreenCanvas, audioWorklet?: boolean, net?: Net): Promise<TransportLayer>;
|
|
@@ -15,6 +15,8 @@ export interface BackendOptions {
|
|
|
15
15
|
canvas?: OffscreenCanvas;
|
|
16
16
|
audioWorklet?: boolean;
|
|
17
17
|
sockdrivePreload?: "all" | "default" | "none";
|
|
18
|
+
sockdriveOpfsRoot?: string;
|
|
19
|
+
mcpServerPort?: number;
|
|
18
20
|
net?: Net;
|
|
19
21
|
}
|
|
20
22
|
export type InitBundleEntry = Uint8Array;
|
|
@@ -37,8 +39,10 @@ export interface Emulators {
|
|
|
37
39
|
dosboxWorker: (init: InitFs, options?: BackendOptions) => Promise<CommandInterface>;
|
|
38
40
|
dosboxXNode: (init: InitFs, options?: BackendOptions) => Promise<CommandInterface>;
|
|
39
41
|
dosboxXDirect: (init: InitFs, options?: BackendOptions) => Promise<CommandInterface>;
|
|
42
|
+
dosboxXJspiDirect: (init: InitFs, options?: BackendOptions) => Promise<CommandInterface>;
|
|
40
43
|
dosboxXWorker: (init: InitFs, options?: BackendOptions) => Promise<CommandInterface>;
|
|
41
44
|
dosboxXJspiWorker: (init: InitFs, options?: BackendOptions) => Promise<CommandInterface>;
|
|
45
|
+
dosboxXDebugWorker: (init: InitFs, options?: BackendOptions) => Promise<CommandInterface>;
|
|
42
46
|
backend: (init: InitFs, transportLayer: TransportLayer, options?: BackendOptions) => Promise<CommandInterface>;
|
|
43
47
|
}
|
|
44
48
|
export interface CommandInterface {
|
|
@@ -9,6 +9,7 @@ declare class EmulatorsImpl implements Emulators {
|
|
|
9
9
|
wdosboxJs: string;
|
|
10
10
|
wdosboxxJs: string;
|
|
11
11
|
wdosboxxJsJspi: string;
|
|
12
|
+
wdosboxxJsDebug: string;
|
|
12
13
|
private wasmModulesPromise?;
|
|
13
14
|
bundle(): Promise<DosBundle>;
|
|
14
15
|
bundleConfig(bundle: InitBundleEntry): Promise<DosConfig | null>;
|
|
@@ -18,8 +19,10 @@ declare class EmulatorsImpl implements Emulators {
|
|
|
18
19
|
dosboxWorker(init: InitFs, options?: BackendOptions): Promise<CommandInterface>;
|
|
19
20
|
dosboxXNode(init: InitFs, options?: BackendOptions): Promise<CommandInterface>;
|
|
20
21
|
dosboxXDirect(init: InitFs, options?: BackendOptions): Promise<CommandInterface>;
|
|
22
|
+
dosboxXJspiDirect(init: InitFs, options?: BackendOptions): Promise<CommandInterface>;
|
|
21
23
|
dosboxXWorker(init: InitFs, options?: BackendOptions): Promise<CommandInterface>;
|
|
22
24
|
dosboxXJspiWorker(init: InitFs, options?: BackendOptions): Promise<CommandInterface>;
|
|
25
|
+
dosboxXDebugWorker(init: InitFs, options?: BackendOptions): Promise<CommandInterface>;
|
|
23
26
|
backend(init: InitFs, transportLayer: TransportLayer, options?: BackendOptions): Promise<CommandInterface>;
|
|
24
27
|
wasmModules(): Promise<IWasmModules>;
|
|
25
28
|
dosDirect(init: InitFs): Promise<CommandInterface>;
|
|
@@ -6,6 +6,7 @@ export interface IWasmModules {
|
|
|
6
6
|
dosbox: () => Promise<WasmModule>;
|
|
7
7
|
dosboxx: () => Promise<WasmModule>;
|
|
8
8
|
dosboxxJspi: () => Promise<WasmModule>;
|
|
9
|
+
dosboxxDebug: () => Promise<WasmModule>;
|
|
9
10
|
}
|
|
10
11
|
interface Globals {
|
|
11
12
|
exports: {
|
|
@@ -30,16 +31,19 @@ export declare class WasmModulesImpl implements IWasmModules {
|
|
|
30
31
|
private wdosboxJs;
|
|
31
32
|
private wdosboxxJs;
|
|
32
33
|
private wdosboxxJsJspi;
|
|
34
|
+
private wdosboxxJsDebug;
|
|
33
35
|
private libzipPromise?;
|
|
34
36
|
private dosboxPromise?;
|
|
35
37
|
private dosboxxPromise?;
|
|
36
38
|
private dosboxxJspiPromise?;
|
|
39
|
+
private dosboxxDebugPromise?;
|
|
37
40
|
wasmSupported: boolean;
|
|
38
|
-
constructor(pathPrefix: string, pathSuffix: string, wdosboxJs: string, wdosboxxJs: string, wdosboxxJsJspi: string);
|
|
41
|
+
constructor(pathPrefix: string, pathSuffix: string, wdosboxJs: string, wdosboxxJs: string, wdosboxxJsJspi: string, wdosboxxJsDebug: string);
|
|
39
42
|
libzip(): Promise<WasmModule>;
|
|
40
43
|
dosbox(): Promise<WasmModule>;
|
|
41
44
|
dosboxx(): Promise<WasmModule>;
|
|
42
45
|
dosboxxJspi(): Promise<WasmModule>;
|
|
46
|
+
dosboxxDebug(): Promise<WasmModule>;
|
|
43
47
|
private loadModule;
|
|
44
48
|
}
|
|
45
49
|
export declare function loadWasmModule(url: string, moduleName: string, onprogress: (stage: string, total: number, loaded: number) => void): Promise<WasmModule>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface Store {
|
|
2
|
+
keys: () => Promise<number[]>;
|
|
3
|
+
put: (key: number, data: Uint8Array) => Promise<void>;
|
|
4
|
+
getSync: (key: number) => Uint8Array | null;
|
|
5
|
+
getAsync: (key: number) => Promise<Uint8Array | null>;
|
|
6
|
+
}
|
|
7
|
+
export declare class NoStore implements Store {
|
|
8
|
+
store: Map<number, Uint8Array>;
|
|
9
|
+
put(key: number, data: Uint8Array): Promise<void>;
|
|
10
|
+
getSync(key: number): Uint8Array | null;
|
|
11
|
+
getAsync(_: number): Promise<Uint8Array | null>;
|
|
12
|
+
keys(): Promise<number[]>;
|
|
13
|
+
}
|
|
14
|
+
export declare class OpfsStore implements Store {
|
|
15
|
+
private dir;
|
|
16
|
+
private index;
|
|
17
|
+
private syncMode;
|
|
18
|
+
private blockHandle;
|
|
19
|
+
private metaHandle;
|
|
20
|
+
private blockSize;
|
|
21
|
+
private metaSize;
|
|
22
|
+
private blockLength;
|
|
23
|
+
private metaEntry;
|
|
24
|
+
private asyncOpPromise;
|
|
25
|
+
private constructor();
|
|
26
|
+
static create(url: string, blockLength: number, opfsRoot: string): Promise<OpfsStore>;
|
|
27
|
+
private init;
|
|
28
|
+
private loadIndex;
|
|
29
|
+
keys(): Promise<number[]>;
|
|
30
|
+
put(key: number, data: Uint8Array): Promise<void>;
|
|
31
|
+
getSync(key: number): Uint8Array | null;
|
|
32
|
+
getAsync(key: number): Promise<Uint8Array | null>;
|
|
33
|
+
}
|
|
34
|
+
export declare function getStore(url: string, blockLength: number, opfsRoot: string): Promise<Store>;
|
|
35
|
+
export declare function readUint32(container: Uint8Array, offset: number): number;
|
|
36
|
+
export declare function writeUint32(container: Uint8Array, value: number, offset: number): number;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
interface DriveInfo {
|
|
2
|
+
ahead_read: number;
|
|
3
|
+
range_count: number;
|
|
4
|
+
dropped_ranges: number[];
|
|
5
|
+
preload_ranges: number[] | "_";
|
|
6
|
+
small_ranges: number[];
|
|
7
|
+
cylinders: number;
|
|
8
|
+
heads: number;
|
|
9
|
+
sectors: number;
|
|
10
|
+
sector_size: number;
|
|
11
|
+
size: number;
|
|
12
|
+
name: string;
|
|
13
|
+
url: string;
|
|
14
|
+
preloadSizeInBytes: number;
|
|
15
|
+
sizeInBytes: number;
|
|
16
|
+
readInBytes: number;
|
|
17
|
+
writeInBytes: number;
|
|
18
|
+
}
|
|
19
|
+
export interface Drive {
|
|
20
|
+
info: DriveInfo;
|
|
21
|
+
range(sector: number): number;
|
|
22
|
+
readRangeAsync(range: number): void;
|
|
23
|
+
ready(): void;
|
|
24
|
+
write(sector: number, buffer: Uint8Array): void;
|
|
25
|
+
persist(): Promise<Uint8Array | null>;
|
|
26
|
+
}
|
|
27
|
+
export declare function sockdrive(url: string, persistedSectors: Uint8Array | null, preloadMode: "all" | "default" | "none", _onNewRange: (range: number, buffer: Uint8Array) => void, opfsRoot: string): Promise<Drive>;
|
|
28
|
+
export declare function traverseSockdriveChanges(encoded: Uint8Array, callback: (url: string, changes: Uint8Array) => void): boolean;
|
|
29
|
+
export {};
|