From 51b5280d616de785d20e92e7ce0ff12cf2176aa6 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sat, 25 Mar 2017 13:27:05 +0900 Subject: [PATCH] Read CD image in main thread --- web/{worker => shell}/cdimage.ts | 2 +- web/shell/loader.ts | 148 +++++++++++++------------- web/shell/package.json | 3 +- web/{worker => shell}/text-decoder.ts | 4 +- web/worker/imagereader-worker.ts | 99 ----------------- web/worker/package.json | 8 -- web/worker/tsconfig.json | 10 -- web/worker/tslint.json | 1 - 8 files changed, 80 insertions(+), 195 deletions(-) rename web/{worker => shell}/cdimage.ts (99%) rename web/{worker => shell}/text-decoder.ts (77%) delete mode 100644 web/worker/imagereader-worker.ts delete mode 100644 web/worker/package.json delete mode 100644 web/worker/tsconfig.json delete mode 120000 web/worker/tslint.json diff --git a/web/worker/cdimage.ts b/web/shell/cdimage.ts similarity index 99% rename from web/worker/cdimage.ts rename to web/shell/cdimage.ts index 1a502a3..0f65e59 100644 --- a/web/worker/cdimage.ts +++ b/web/shell/cdimage.ts @@ -1,4 +1,4 @@ -importScripts('text-decoder.js'); +/// class ISO9660FileSystem { static async create(sectorReader: CDImageReader): Promise { diff --git a/web/shell/loader.ts b/web/shell/loader.ts index 28a94d4..cfe8620 100644 --- a/web/shell/loader.ts +++ b/web/shell/loader.ts @@ -1,54 +1,21 @@ /// +/// namespace xsystem35 { export class ImageLoader { - private worker: Worker; - private cddaCallback: (wab: Blob) => void; + private imgFile: File; + private cueFile: File; + private imageReader: CDImageReader; constructor(private runMain: () => void) { - this.initWorker(); $('#fileselect').addEventListener('change', this.handleFileSelect.bind(this), false); document.body.ondragover = this.handleDragOver.bind(this); document.body.ondrop = this.handleDrop.bind(this); } - getCDDA(track: number, callback: (wav: Blob) => void) { - this.send({ command: 'getTrack', track }); - this.cddaCallback = callback; - } - - private initWorker() { - this.worker = new Worker('imagereader-worker.js'); - this.worker.addEventListener('message', this.onMessage.bind(this)); - this.worker.addEventListener('error', this.onWorkerError.bind(this)); - } - - private setReadyState(imgName: string, cueName: string) { - if (imgName) { - $('#imgReady').classList.remove('notready'); - $('#imgReady').textContent = imgName; - } - if (cueName) { - $('#cueReady').classList.remove('notready'); - $('#cueReady').textContent = cueName; - } - } - - private populateFiles(files: any, volumeLabel: string) { - let grGenerator = new GameResourceGenerator(); - for (let name in files) { - let data: ArrayBuffer = files[name]; - // Store contents in the emscripten heap, so that it can be mmap-ed without copying - let ptr = Module.getMemory(data.byteLength); - Module.HEAPU8.set(new Uint8Array(data), ptr); - FS.writeFile(name, Module.HEAPU8.subarray(ptr, ptr + data.byteLength), - { encoding: 'binary', canOwn: true }); - grGenerator.addFile(name); - } - FS.writeFile('xsystem35.gr', grGenerator.generate()); - FS.writeFile('.xsys35rc', xsystem35.xsys35rc); - - this.runMain(); + async getCDDA(track: number, callback: (wav: Blob) => void) { + let blob = await this.imageReader.extractTrack(track); + callback(blob); } private handleFileSelect(evt: Event) { @@ -73,42 +40,77 @@ namespace xsystem35 { this.setFile(files[i]); } - private setFile(file: File) { - this.send({ command: 'setFile', file }); - } - - private startInstall() { - this.send({ command: 'extractFiles' }); - } - - private send(msg: any) { - this.worker.postMessage(msg); - } - - private onMessage(evt: MessageEvent) { - switch (evt.data.command) { - case 'readyState': - this.setReadyState(evt.data.img, evt.data.cue); - if (evt.data.img && evt.data.cue) - this.startInstall(); - break; - case 'extractFiles': - this.populateFiles(evt.data.files, evt.data.volumeLabel); - break; - case 'complete': - this.cddaCallback(evt.data.wav); - this.cddaCallback = null; - break; - case 'error': - console.log(evt.data.message); - break; - default: - throw('unknown command: ' + evt.data.command); + private async setFile(file: File) { + let name = file.name.toLowerCase(); + if (name.endsWith('.img') || name.endsWith('.mdf')) { + this.imgFile = file; + $('#imgReady').classList.remove('notready'); + $('#imgReady').textContent = file.name; + } else if (name.endsWith('.cue') || name.endsWith('.mds')) { + this.cueFile = file; + $('#cueReady').classList.remove('notready'); + $('#cueReady').textContent = file.name; + } + if (this.imgFile && this.cueFile) { + if (this.cueFile.name.endsWith('.cue')) + this.imageReader = await ImgCueReader.create(this.imgFile, this.cueFile); + else + this.imageReader = await MdfMdsReader.create(this.imgFile, this.cueFile); + await this.installAndRun(); } } - private onWorkerError(evt: Event) { - console.log('worker error', evt); + private async extractFile(isofs: ISO9660FileSystem, entry: DirEnt): Promise { + let buffer = new ArrayBuffer(entry.size); + let uint8 = new Uint8Array(buffer); + let ptr = 0; + for (let buf of await isofs.readFile(entry)) { + uint8.set(buf, ptr); + ptr += buf.byteLength; + } + if (ptr !== entry.size) + throw ('expected ' + entry.size + ' bytes, but read ' + ptr + 'bytes'); + return buffer; + } + + private async installAndRun() { + let isofs = await ISO9660FileSystem.create(this.imageReader); + // this.walk(isofs, isofs.rootDir(), '/'); + let gamedata = await isofs.getDirEnt('gamedata', isofs.rootDir()); + if (!gamedata) { + this.setError('インストールできません。GAMEDATAフォルダが見つかりません。'); + return; + } + let grGenerator = new GameResourceGenerator(); + for (let e of await isofs.readDir(gamedata)) { + if (e.name.toLowerCase().endsWith('.ald')) { + let data = await this.extractFile(isofs, e); + // Store contents in the emscripten heap, so that it can be mmap-ed without copying + let ptr = Module.getMemory(data.byteLength); + Module.HEAPU8.set(new Uint8Array(data), ptr); + FS.writeFile(e.name, Module.HEAPU8.subarray(ptr, ptr + data.byteLength), + { encoding: 'binary', canOwn: true }); + grGenerator.addFile(e.name); + } + } + FS.writeFile('xsystem35.gr', grGenerator.generate()); + FS.writeFile('.xsys35rc', xsystem35.xsys35rc); + this.runMain(); + } + + private setError(msg: string) { + console.log(msg); + } + + // For debug + 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) + this.walk(isofs, e, dirname + e.name + '/'); + } + } } } diff --git a/web/shell/package.json b/web/shell/package.json index 81dcf6d..024e867 100644 --- a/web/shell/package.json +++ b/web/shell/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "description": "", "dependencies": { - "@types/emscripten": "0.0.29" + "@types/emscripten": "0.0.29", + "@types/text-encoding": "0.0.30" } } diff --git a/web/worker/text-decoder.ts b/web/shell/text-decoder.ts similarity index 77% rename from web/worker/text-decoder.ts rename to web/shell/text-decoder.ts index 3d22b65..7178d9d 100644 --- a/web/worker/text-decoder.ts +++ b/web/shell/text-decoder.ts @@ -14,5 +14,5 @@ class FallbackTextDecoder { } } -if (!(self).TextDecoder) - (self).TextDecoder = FallbackTextDecoder; +if (!(window).TextDecoder) + (window).TextDecoder = FallbackTextDecoder; diff --git a/web/worker/imagereader-worker.ts b/web/worker/imagereader-worker.ts deleted file mode 100644 index c7481da..0000000 --- a/web/worker/imagereader-worker.ts +++ /dev/null @@ -1,99 +0,0 @@ -importScripts('cdimage.js'); - -declare function postMessage(data: any, transfers: any): void; - -class Installer { - private imgFile: File; - private cueFile: File; - private imageReader: CDImageReader; - - constructor() { - addEventListener('message', this.onMessage.bind(this)); - } - - private async onMessage(evt: MessageEvent) { - switch (evt.data.command) { - case 'setFile': - 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}); - break; - case 'extractFiles': - if (this.ready()) - this.extractFiles(); - break; - case 'getTrack': - if (this.ready()) - this.getTrack(evt.data.track); - break; - default: - throw('unknown command: ' + evt.data.command); - } - } - - private async setFile(file: File) { - let name = file.name.toLowerCase(); - if (name.endsWith('.img') || name.endsWith('.mdf')) - this.imgFile = file; - else if (name.endsWith('.cue') || name.endsWith('.mds')) - this.cueFile = file; - if (this.imgFile && this.cueFile) { - if (this.cueFile.name.endsWith('.cue')) - this.imageReader = await ImgCueReader.create(this.imgFile, this.cueFile); - else - this.imageReader = await MdfMdsReader.create(this.imgFile, this.cueFile); - } - } - - private ready(): boolean { - return !!this.imageReader; - } - - private async extractFiles() { - let isofs = await ISO9660FileSystem.create(this.imageReader); - // this.walk(isofs, 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 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 await isofs.readFile(e)) { - uint8.set(buf, ptr); - ptr += buf.byteLength; - } - if (ptr !== e.size) - throw('expected ' + e.size + ' bytes, but read ' + ptr + 'bytes'); - files[e.name] = buffer; - transfers.push(buffer); - } - } - postMessage({command: 'extractFiles', - volumeLabel: isofs.volumeLabel(), - files}, transfers); - } - - 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) - this.walk(isofs, e, dirname + e.name + '/'); - } - } - } - - private async getTrack(track: number) { - let blob = await this.imageReader.extractTrack(track); - postMessage({command: 'complete', track, wav: blob}); - } -} - -let installer = new Installer(); diff --git a/web/worker/package.json b/web/worker/package.json deleted file mode 100644 index 4ee2ce7..0000000 --- a/web/worker/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "xsystem35-worker", - "version": "1.0.0", - "description": "", - "dependencies": { - "@types/text-encoding": "0.0.30" - } -} diff --git a/web/worker/tsconfig.json b/web/worker/tsconfig.json deleted file mode 100644 index ec80b37..0000000 --- a/web/worker/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "outDir": "../../docs", - "lib": ["es6", "webworker"], - "noImplicitAny": true, - "sourceMap": false - } -} \ No newline at end of file diff --git a/web/worker/tslint.json b/web/worker/tslint.json deleted file mode 120000 index b9c0b95..0000000 --- a/web/worker/tslint.json +++ /dev/null @@ -1 +0,0 @@ -../shell/tslint.json \ No newline at end of file