diff --git a/web/worker/cdimage.ts b/web/worker/cdimage.ts index ccde71a..1a502a3 100644 --- a/web/worker/cdimage.ts +++ b/web/worker/cdimage.ts @@ -1,10 +1,12 @@ importScripts('text-decoder.js'); class ISO9660FileSystem { - private pvd: PVD; + static async create(sectorReader: CDImageReader): Promise { + let pvd = new PVD(await sectorReader.readSector(0x10)); + return new ISO9660FileSystem(sectorReader, pvd); + } - constructor(private sectorReader: CDImageReader) { - this.pvd = new PVD(sectorReader.readSector(0x10)); + private constructor(private sectorReader: CDImageReader, private pvd: PVD) { if (this.pvd.type !== 1) throw('PVD not found'); } @@ -17,16 +19,16 @@ class ISO9660FileSystem { return this.pvd.rootDirEnt(); } - getDirEnt(name: string, parent: DirEnt): DirEnt { + async getDirEnt(name: string, parent: DirEnt): Promise { name = name.toLowerCase(); - for (let e of this.readDir(parent)) { + for (let e of await this.readDir(parent)) { if (e.name.toLowerCase() === name) return e; } return null; } - readDir(dirent: DirEnt): DirEnt[] { + async readDir(dirent: DirEnt): Promise { let sector = dirent.sector; let position = 0; let length = dirent.size; @@ -34,7 +36,7 @@ class ISO9660FileSystem { let buf: ArrayBuffer; while (position < length) { if (position === 0) - buf = this.sectorReader.readSector(sector); + buf = await this.sectorReader.readSector(sector); let child = new DirEnt(buf, position); if (child.length === 0) { // Padded end of sector @@ -54,7 +56,7 @@ class ISO9660FileSystem { return entries; } - readFile(dirent: DirEnt): Uint8Array[] { + readFile(dirent: DirEnt): Promise { return this.sectorReader.readSequentialSectors(dirent.sector, dirent.size); } } @@ -101,23 +103,41 @@ class DirEnt { } interface CDImageReader { - readSector(sector: number): ArrayBuffer; - readSequentialSectors(startSector: number, length: number): Uint8Array[]; + readSector(sector: number): Promise; + readSequentialSectors(startSector: number, length: number): Promise; maxTrack(): number; - extractTrack(track: number): Blob; + extractTrack(track: number): Promise; +} + +function readAsArrayBuffer(blob: Blob): Promise { + return new Promise((resolve, reject) => { + let reader = new FileReader(); + reader.onload = () => { resolve(reader.result); }; + reader.onerror = () => { reject(reader.error); }; + reader.readAsArrayBuffer(blob); + }); +} + +function readAsText(blob: Blob): Promise { + return new Promise((resolve, reject) => { + let reader = new FileReader(); + reader.onload = () => { resolve(reader.result); }; + reader.onerror = () => { reject(reader.error); }; + reader.readAsText(blob); + }); } class ImageReaderBase { constructor(public image: File) {} - readSequential(startOffset: number, - bytesToRead: number, - blockSize: number, - sectorSize: number, - sectorOffset: number): Uint8Array[] { + async readSequential(startOffset: number, + bytesToRead: number, + blockSize: number, + sectorSize: number, + sectorOffset: number): Promise { let sectors = Math.ceil(bytesToRead / sectorSize); let blob = this.image.slice(startOffset, startOffset + sectors * blockSize); - let buf = new FileReaderSync().readAsArrayBuffer(blob); + let buf = await readAsArrayBuffer(blob); let bufs: Uint8Array[] = []; for (let i = 0; i < sectors; i++) { bufs.push(new Uint8Array(buf, i * blockSize + sectorOffset, Math.min(bytesToRead, sectorSize))); @@ -130,23 +150,28 @@ class ImageReaderBase { class ImgCueReader extends ImageReaderBase implements CDImageReader { private tracks: Array<{ type: string; index: string[]; }>; - constructor(img: File, cue: File) { - super(img); - this.parseCue(cue); + static async create(img: File, cue: File): Promise { + let reader = new ImgCueReader(img); + await reader.parseCue(cue); + return reader; } - readSector(sector: number): ArrayBuffer { + private constructor(img: File) { + super(img); + } + + readSector(sector: number): Promise { let start = sector * 2352 + 16; let end = start + 2048; - return new FileReaderSync().readAsArrayBuffer(this.image.slice(start, end)); + return readAsArrayBuffer(this.image.slice(start, end)); } - readSequentialSectors(startSector: number, length: number): Uint8Array[] { + readSequentialSectors(startSector: number, length: number): Promise { return this.readSequential(startSector * 2352, length, 2352, 2048, 16); } - private parseCue(cueFile: File) { - let lines = new FileReaderSync().readAsText(cueFile).split('\n'); + private async parseCue(cueFile: File) { + let lines = (await readAsText(cueFile)).split('\n'); this.tracks = []; let currentTrack: number = null; for (let line of lines) { @@ -170,7 +195,7 @@ class ImgCueReader extends ImageReaderBase implements CDImageReader { return this.tracks.length - 1; } - extractTrack(track: number): Blob { + async extractTrack(track: number): Promise { if (!this.tracks[track] || this.tracks[track].type !== 'AUDIO') return; @@ -197,13 +222,18 @@ enum MdsTrackMode { Audio = 0xa9, Mode1 = 0xaa }; class MdfMdsReader extends ImageReaderBase implements CDImageReader { private tracks: Array<{ mode: number; sectorSize: number; offset: number; sectors: number; }>; - constructor(mdf: File, mds: File) { - super(mdf); - this.parseMds(mds); + static async create(mdf: File, mds: File): Promise { + let reader = new MdfMdsReader(mdf); + await reader.parseMds(mds); + return reader; } - private parseMds(mdsFile: File) { - let buf = new FileReaderSync().readAsArrayBuffer(mdsFile); + private constructor(mdf: File) { + super(mdf); + } + + private async parseMds(mdsFile: File) { + let buf = await readAsArrayBuffer(mdsFile); let signature = new TextDecoder().decode(new DataView(buf, 0, 16)); if (signature !== 'MEDIA DESCRIPTOR') @@ -228,13 +258,13 @@ class MdfMdsReader extends ImageReaderBase implements CDImageReader { throw 'track 1 is not mode1'; } - readSector(sector: number): ArrayBuffer { + readSector(sector: number): Promise { let start = sector * this.tracks[1].sectorSize + 16; let end = start + 2048; - return new FileReaderSync().readAsArrayBuffer(this.image.slice(start, end)); + return readAsArrayBuffer(this.image.slice(start, end)); } - readSequentialSectors(startSector: number, length: number): Uint8Array[] { + readSequentialSectors(startSector: number, length: number): Promise { let track = this.tracks[1]; return this.readSequential(track.offset + startSector * track.sectorSize, length, track.sectorSize, 2048, 16); } @@ -243,12 +273,12 @@ class MdfMdsReader extends ImageReaderBase implements CDImageReader { return this.tracks.length - 1; } - extractTrack(track: number): Blob { + async extractTrack(track: number): Promise { if (!this.tracks[track] || this.tracks[track].mode !== MdsTrackMode.Audio) return; let size = this.tracks[track].sectors * 2352; - let chunks = this.readSequential(this.tracks[track].offset, size, this.tracks[track].sectorSize, 2352, 0); + let chunks = await this.readSequential(this.tracks[track].offset, size, this.tracks[track].sectorSize, 2352, 0); return new Blob([createWaveHeader(size)].concat(chunks), {type : 'audio/wav'}); } } diff --git a/web/worker/imagereader-worker.ts b/web/worker/imagereader-worker.ts index c27fcc6..c7481da 100644 --- a/web/worker/imagereader-worker.ts +++ b/web/worker/imagereader-worker.ts @@ -11,10 +11,10 @@ class Installer { addEventListener('message', this.onMessage.bind(this)); } - private onMessage(evt: MessageEvent) { + private async onMessage(evt: MessageEvent) { switch (evt.data.command) { case 'setFile': - this.setFile(evt.data.file); + await this.setFile(evt.data.file); let imgName = this.imgFile && this.imgFile.name; let cueName = this.cueFile && this.cueFile.name; postMessage({command: 'readyState', img: imgName, cue: cueName}); @@ -32,7 +32,7 @@ class Installer { } } - private setFile(file: File) { + private async setFile(file: File) { let name = file.name.toLowerCase(); if (name.endsWith('.img') || name.endsWith('.mdf')) this.imgFile = file; @@ -40,9 +40,9 @@ class Installer { this.cueFile = file; if (this.imgFile && this.cueFile) { if (this.cueFile.name.endsWith('.cue')) - this.imageReader = new ImgCueReader(this.imgFile, this.cueFile); + this.imageReader = await ImgCueReader.create(this.imgFile, this.cueFile); else - this.imageReader = new MdfMdsReader(this.imgFile, this.cueFile); + this.imageReader = await MdfMdsReader.create(this.imgFile, this.cueFile); } } @@ -50,22 +50,22 @@ class Installer { return !!this.imageReader; } - private extractFiles() { - let isofs = new ISO9660FileSystem(this.imageReader); + private async extractFiles() { + let isofs = await ISO9660FileSystem.create(this.imageReader); // this.walk(isofs, isofs.rootDir(), '/'); - let gamedata = isofs.getDirEnt('gamedata', isofs.rootDir()); + let gamedata = await isofs.getDirEnt('gamedata', isofs.rootDir()); if (!gamedata) { postMessage({command: 'error', message: 'インストールできません。GAMEDATAフォルダが見つかりません。'}); return; } let files: any = {}; let transfers = []; - for (let e of isofs.readDir(gamedata)) { + for (let e of await isofs.readDir(gamedata)) { if (e.name.toLowerCase().endsWith('.ald')) { let buffer = new ArrayBuffer(e.size); let uint8 = new Uint8Array(buffer); let ptr = 0; - for (let buf of isofs.readFile(e)) { + for (let buf of await isofs.readFile(e)) { uint8.set(buf, ptr); ptr += buf.byteLength; } @@ -80,8 +80,8 @@ class Installer { files}, transfers); } - private walk(isofs: ISO9660FileSystem, dir: DirEnt, dirname: string) { - for (let e of isofs.readDir(dir)) { + private async walk(isofs: ISO9660FileSystem, dir: DirEnt, dirname: string) { + for (let e of await isofs.readDir(dir)) { if (e.name !== '\0' && e.name !== '\x01') { console.log(dirname + e.name); if (e.isDirectory) @@ -90,8 +90,8 @@ class Installer { } } - private getTrack(track: number) { - let blob = this.imageReader.extractTrack(track); + private async getTrack(track: number) { + let blob = await this.imageReader.extractTrack(track); postMessage({command: 'complete', track, wav: blob}); } }