diff --git a/web/shell/audio.ts b/web/shell/audio.ts index 11f5bad..043e1bb 100644 --- a/web/shell/audio.ts +++ b/web/shell/audio.ts @@ -2,113 +2,6 @@ /// namespace xsystem35 { - abstract class PCMSound { - protected context: AudioContext; - protected gain: GainNode; - protected duration_: number; - protected startTime: number; - - constructor(protected dst: AudioNode) { - this.context = dst.context; - this.gain = this.context.createGain(); - this.gain.connect(dst); - } - abstract start(loop: number): void; - abstract stop(): void; - setGain(gain: number) { - this.gain.gain.value = gain; - } - fadeout(msec: number) { - this.gain.gain.linearRampToValueAtTime(0, this.context.currentTime + msec / 1000); - } - getPosition(): number { - if (!this.startTime) - return 0; - return this.context.currentTime - this.startTime; - } - isPlaying(): boolean { - return !!this.startTime; - } - get duration(): number { - return this.duration_; - } - } - - class PCMSoundSimple extends PCMSound { - private node: AudioBufferSourceNode; - - constructor(dst: AudioNode, buf: AudioBuffer) { - super(dst); - this.node = this.context.createBufferSource(); - this.node.buffer = buf; - this.node.connect(this.gain); - this.node.onended = this.onended.bind(this); - this.duration_ = buf.duration; - } - - start(loop: number) { - if (loop == 0) - this.node.loop = true; - else if (loop != 1) - console.warn('Unsupported PCM loop count ' + loop); - this.node.start(); - this.startTime = this.context.currentTime; - } - - stop() { - if (this.startTime) { - this.node.stop(); - this.startTime = null; - } - } - - private onended() { - this.startTime = null; - } - } - - class PCMSoundMixLR extends PCMSound { - private lsrc: AudioBufferSourceNode; - private rsrc: AudioBufferSourceNode; - private endCount = 0; - - constructor(dst: AudioNode, lbuf: AudioBuffer, rbuf: AudioBuffer) { - super(dst); - this.lsrc = this.context.createBufferSource(); - this.rsrc = this.context.createBufferSource(); - this.lsrc.buffer = lbuf; - this.rsrc.buffer = rbuf; - var merger = this.context.createChannelMerger(2); - merger.connect(this.gain); - this.lsrc.connect(merger, 0, 0); - this.rsrc.connect(merger, 0, 1); - this.lsrc.onended = this.rsrc.onended = this.onended.bind(this); - this.duration_ = Math.max(lbuf.duration, rbuf.duration); - } - - start(loop: number) { - if (loop != 1) - console.warn('PCMSoundMixLR: loop is not supported ' + loop); - this.lsrc.start(); - this.rsrc.start(); - this.startTime = this.context.currentTime; - } - - stop() { - if (this.startTime) { - this.lsrc.stop(); - this.rsrc.stop(); - this.startTime = null; - } - } - - private onended() { - this.endCount++; - if (this.endCount == 2) - this.startTime = null; - } - } - declare var webkitAudioContext: any; export class AudioManager { private context: AudioContext; @@ -134,8 +27,8 @@ namespace xsystem35 { } private removeUserGestureRestriction() { - var hanlder = () => { - var src = this.context.createBufferSource(); + let hanlder = () => { + let src = this.context.createBufferSource(); src.buffer = this.context.createBuffer(1, 1, 22050); src.connect(this.context.destination); src.start(); @@ -146,11 +39,11 @@ namespace xsystem35 { } private load(no: number): Promise { - var buf = this.getWave(no); + let buf = this.getWave(no); if (!buf) return Promise.reject('Failed to open wave ' + no); - var decoded: Promise; + let decoded: Promise; if (this.isSafari) { decoded = new Promise((resolve, reject) => { this.context.decodeAudioData(buf, resolve, reject); @@ -165,12 +58,12 @@ namespace xsystem35 { } private getWave(no: number): ArrayBuffer { - var dfile = _ald_getdata(2 /* DRIFILE_WAVE */, no - 1); + let dfile = _ald_getdata(2 /* DRIFILE_WAVE */, no - 1); if (!dfile) return null; - var ptr = Module.getValue(dfile + 8, '*'); - var size = Module.getValue(dfile, 'i32'); - var buf = Module.HEAPU8.buffer.slice(ptr, ptr + size); + let ptr = Module.getValue(dfile + 8, '*'); + let size = Module.getValue(dfile, 'i32'); + let buf = Module.HEAPU8.buffer.slice(ptr, ptr + size); _ald_freedata(dfile); return buf; } @@ -196,7 +89,7 @@ namespace xsystem35 { this.slots[slot] = new PCMSoundMixLR(this.masterGain, this.buffers[noL], this.buffers[noR]); return resume(); } - var ps = []; + let ps = []; if (!this.buffers[noL]) ps.push(this.load(noL)); if (!this.buffers[noR]) ps.push(this.load(noR)); Promise.all(ps).then(() => { @@ -258,4 +151,114 @@ namespace xsystem35 { this.masterGain.gain.value = evt.detail; } } -} \ No newline at end of file + + abstract class PCMSound { + protected context: AudioContext; + protected gain: GainNode; + protected startTime: number; + + constructor(protected dst: AudioNode) { + this.context = dst.context; + this.gain = this.context.createGain(); + this.gain.connect(dst); + } + abstract start(loop: number): void; + abstract stop(): void; + setGain(gain: number) { + this.gain.gain.value = gain; + } + fadeout(msec: number) { + this.gain.gain.linearRampToValueAtTime(0, this.context.currentTime + msec / 1000); + } + getPosition(): number { + if (!this.startTime) + return 0; + return this.context.currentTime - this.startTime; + } + isPlaying(): boolean { + return !!this.startTime; + } + abstract get duration(): number; + } + + class PCMSoundSimple extends PCMSound { + private node: AudioBufferSourceNode; + + constructor(dst: AudioNode, buf: AudioBuffer) { + super(dst); + this.node = this.context.createBufferSource(); + this.node.buffer = buf; + this.node.connect(this.gain); + this.node.onended = this.onended.bind(this); + } + + start(loop: number) { + if (loop === 0) + this.node.loop = true; + else if (loop !== 1) + console.warn('Unsupported PCM loop count ' + loop); + this.node.start(); + this.startTime = this.context.currentTime; + } + + stop() { + if (this.startTime) { + this.node.stop(); + this.startTime = null; + } + } + + get duration() { + return this.node.buffer.duration; + } + + private onended() { + this.startTime = null; + } + } + + class PCMSoundMixLR extends PCMSound { + private lsrc: AudioBufferSourceNode; + private rsrc: AudioBufferSourceNode; + private endCount = 0; + + constructor(dst: AudioNode, lbuf: AudioBuffer, rbuf: AudioBuffer) { + super(dst); + this.lsrc = this.context.createBufferSource(); + this.rsrc = this.context.createBufferSource(); + this.lsrc.buffer = lbuf; + this.rsrc.buffer = rbuf; + let merger = this.context.createChannelMerger(2); + merger.connect(this.gain); + this.lsrc.connect(merger, 0, 0); + this.rsrc.connect(merger, 0, 1); + this.lsrc.onended = this.rsrc.onended = this.onended.bind(this); + } + + start(loop: number) { + if (loop !== 1) + console.warn('PCMSoundMixLR: loop is not supported ' + loop); + this.lsrc.start(); + this.rsrc.start(); + this.startTime = this.context.currentTime; + } + + stop() { + if (this.startTime) { + this.lsrc.stop(); + this.rsrc.stop(); + this.startTime = null; + } + } + + get duration() { + return Math.max(this.lsrc.buffer.duration, this.rsrc.buffer.duration); + } + + private onended() { + this.endCount++; + if (this.endCount === 2) + this.startTime = null; + } + } +} diff --git a/web/shell/cdda.ts b/web/shell/cdda.ts index 14fefef..009bb96 100644 --- a/web/shell/cdda.ts +++ b/web/shell/cdda.ts @@ -13,7 +13,7 @@ namespace xsystem35 { volumeControl.addEventListener(this.onVolumeChanged.bind(this)); this.audio.volume = volumeControl.volume(); this.waiting = false; - this.removeUserGestureRestriction() + this.removeUserGestureRestriction(); } play(track: number, loop: number) { @@ -37,13 +37,13 @@ namespace xsystem35 { getPosition(): number { if (!this.currentTrack) return 0; - var time = Math.round(this.audio.currentTime * 75); + let time = Math.round(this.audio.currentTime * 75); return this.currentTrack | time << 8; } private startPlayback(blob: Blob, loop: number) { this.audio.setAttribute('src', URL.createObjectURL(blob)); - this.audio.loop = (loop != 0); + this.audio.loop = (loop !== 0); this.audio.load(); this.audio.play(); this.waiting = false; @@ -54,7 +54,7 @@ namespace xsystem35 { } private removeUserGestureRestriction() { - var hanlder = () => { + let hanlder = () => { this.audio.load(); console.log('CDDA unlocked'); window.removeEventListener('touchend', hanlder); diff --git a/web/shell/loader.ts b/web/shell/loader.ts index ee9f4ec..28a94d4 100644 --- a/web/shell/loader.ts +++ b/web/shell/loader.ts @@ -5,7 +5,7 @@ namespace xsystem35 { private worker: Worker; private cddaCallback: (wab: Blob) => void; - constructor(private runMain: ()=>void) { + constructor(private runMain: () => void) { this.initWorker(); $('#fileselect').addEventListener('change', this.handleFileSelect.bind(this), false); document.body.ondragover = this.handleDragOver.bind(this); @@ -13,7 +13,7 @@ namespace xsystem35 { } getCDDA(track: number, callback: (wav: Blob) => void) { - this.send({ command: 'getTrack', track: track }); + this.send({ command: 'getTrack', track }); this.cddaCallback = callback; } @@ -35,13 +35,14 @@ namespace xsystem35 { } private populateFiles(files: any, volumeLabel: string) { - var grGenerator = new GameResourceGenerator(); - for (var name in files) { - var data: ArrayBuffer = files[name]; + 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 - var ptr = Module.getMemory(data.byteLength); + 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 }); + FS.writeFile(name, Module.HEAPU8.subarray(ptr, ptr + data.byteLength), + { encoding: 'binary', canOwn: true }); grGenerator.addFile(name); } FS.writeFile('xsystem35.gr', grGenerator.generate()); @@ -51,9 +52,9 @@ namespace xsystem35 { } private handleFileSelect(evt: Event) { - var input = evt.target; - var files = input.files; - for (var i = 0; i < files.length; i++) + let input = evt.target; + let files = input.files; + for (let i = 0; i < files.length; i++) this.setFile(files[i]); input.value = ''; } @@ -67,13 +68,13 @@ namespace xsystem35 { private handleDrop(evt: DragEvent) { evt.stopPropagation(); evt.preventDefault(); - var files = evt.dataTransfer.files; - for (var i = 0; i < files.length; i++) + let files = evt.dataTransfer.files; + for (let i = 0; i < files.length; i++) this.setFile(files[i]); } private setFile(file: File) { - this.send({ command: 'setFile', file: file }); + this.send({ command: 'setFile', file }); } private startInstall() { @@ -101,6 +102,8 @@ namespace xsystem35 { case 'error': console.log(evt.data.message); break; + default: + throw('unknown command: ' + evt.data.command); } } @@ -110,27 +113,28 @@ namespace xsystem35 { } class GameResourceGenerator { - static resourceType: { [ch: string]: string } = { s: 'Scenario', g: 'Graphics', w: 'Wave', d: 'Data', r: 'Resource', m: 'Midi' }; + static resourceType: { [ch: string]: string } = + { s: 'Scenario', g: 'Graphics', w: 'Wave', d: 'Data', r: 'Resource', m: 'Midi' }; private basename: string; private lines: string[] = []; addFile(name: string) { - var type = name.charAt(name.length - 6).toLowerCase(); - var id = name.charAt(name.length - 5); + let type = name.charAt(name.length - 6).toLowerCase(); + let id = name.charAt(name.length - 5); this.basename = name.slice(0, -6); this.lines.push(GameResourceGenerator.resourceType[type] + id.toUpperCase() + ' ' + name); } generate(): string { - for (var i = 0; i < 26; i++) { - var id = String.fromCharCode(65 + i); + for (let i = 0; i < 26; i++) { + let id = String.fromCharCode(65 + i); this.lines.push('Save' + id + ' save/' + this.basename + 's' + id.toLowerCase() + '.asd'); } return this.lines.join('\n') + '\n'; } isEmpty(): boolean { - return this.lines.length == 0; + return this.lines.length === 0; } } } diff --git a/web/shell/shell.ts b/web/shell/shell.ts index 494d073..e4f9b30 100644 --- a/web/shell/shell.ts +++ b/web/shell/shell.ts @@ -10,10 +10,10 @@ namespace xsystem35 { export const xsys35rc = [ 'font_device: ttf', 'ttfont_mincho: ' + Font.fname, - 'ttfont_gothic: ' + Font.fname, '' + 'ttfont_gothic: ' + Font.fname, '', ].join('\n'); - export var cdPlayer: CDPlayer; - export var audio: AudioManager; + export let cdPlayer: CDPlayer; + export let audio: AudioManager; class System35Shell { private imageLoader: ImageLoader; @@ -46,15 +46,15 @@ namespace xsystem35 { FS.mkdir('/save'); FS.mount(IDBFS, {}, '/save'); Module.addRunDependency('syncfs'); - FS.syncfs(true, (err) => { Module.removeRunDependency('syncfs') }); - } + FS.syncfs(true, (err) => { Module.removeRunDependency('syncfs'); }); + }, ]; this.volumeControl = new VolumeControl(); xsystem35.cdPlayer = new CDPlayer(this.imageLoader, this.volumeControl); this.zoom = new ZoomManager(); this.antialiasCheckbox = $('#antialias'); this.antialiasCheckbox.addEventListener('change', this.antialiasChanged.bind(this)); - this.antialiasCheckbox.checked = localStorage.getItem('antialias') != 'false'; + this.antialiasCheckbox.checked = localStorage.getItem('antialias') !== 'false'; xsystem35.audio = new AudioManager(this.volumeControl); } @@ -83,7 +83,7 @@ namespace xsystem35 { this.fsyncTimer = window.setTimeout(() => { FS.syncfs(false, (err) => { if (err) - console.log("FS.syncfs error: ", err); + console.log('FS.syncfs error: ', err); }); }, 100); } @@ -94,7 +94,7 @@ namespace xsystem35 { } private addRightClickEmulation() { - var emulatingRightClick = false; + let emulatingRightClick = false; document.body.addEventListener('touchstart', (e) => { if (e.target !== document.body) return; @@ -110,5 +110,5 @@ namespace xsystem35 { } } - export var shell = new System35Shell(); + export let shell = new System35Shell(); } diff --git a/web/shell/tslint.json b/web/shell/tslint.json new file mode 100644 index 0000000..5d99943 --- /dev/null +++ b/web/shell/tslint.json @@ -0,0 +1,27 @@ +{ + "extends": "tslint:recommended", + "rules": { + "curly": false, + "interface-name": false, + "max-classes-per-file": false, + "member-access": false, + "member-ordering": false, + "no-bitwise": false, + "no-empty": false, + "no-namespace": false, + "no-reference": false, + "only-arrow-functions": ["allow-declarations", "allow-named-functions"], + "prefer-for-of": false, + "quotemark": [true, "single", "avoid-escape"], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + + "no-console": false + } +} \ No newline at end of file diff --git a/web/shell/util.ts b/web/shell/util.ts index 537ab37..bcd938d 100644 --- a/web/shell/util.ts +++ b/web/shell/util.ts @@ -1,4 +1,4 @@ -var $: (selector:string)=>HTMLElement = document.querySelector.bind(document); +let $: (selector: string) => HTMLElement = document.querySelector.bind(document); // xsystem35 exported functions declare function _musfade_setvolval_all(vol: number): void; @@ -8,10 +8,10 @@ declare function _ald_getdata(type: number, no: number): number; declare function _ald_freedata(data: number): void; declare namespace Module { - var noInitialRun: boolean; + let noInitialRun: boolean; // Undocumented methods / attributes - var canvas: HTMLElement; + let canvas: HTMLElement; function setStatus(status: string): void; function monitorRunDependencies(left: number): void; function callMain(): void; @@ -19,9 +19,10 @@ declare namespace Module { } declare namespace FS { - function writeFile(path: string, data: ArrayBufferView | string, opts?: {encoding?: string; flags?: string; canOwn?: boolean}): void; + function writeFile(path: string, data: ArrayBufferView | string, + opts?: {encoding?: string; flags?: string; canOwn?: boolean}): void; } declare namespace EmterpreterAsync { - function handle(asyncOp: (resume:()=>void) => void): void; + function handle(asyncOp: (resume: () => void) => void): void; } diff --git a/web/shell/volume.ts b/web/shell/volume.ts index a3d12c8..afe97e7 100644 --- a/web/shell/volume.ts +++ b/web/shell/volume.ts @@ -23,7 +23,7 @@ namespace xsystem35 { } volume(): number { - return this.muted ? 0 : parseInt(this.slider.value) / 100; + return this.muted ? 0 : parseInt(this.slider.value, 10) / 100; } addEventListener(handler: (evt: CustomEvent) => any) { @@ -45,7 +45,7 @@ namespace xsystem35 { } private onSliderValueChanged(e: Event) { - this.vol = parseInt(this.slider.value) / 100; + this.vol = parseInt(this.slider.value, 10) / 100; if (this.vol > 0 && this.muted) { this.muted = false; this.icon.classList.remove('fa-volume-off'); @@ -59,7 +59,7 @@ namespace xsystem35 { } private dispatchEvent() { - var event = new CustomEvent('volumechange', { detail: this.volume() }); + let event = new CustomEvent('volumechange', { detail: this.volume() }); this.elem.dispatchEvent(event); } } diff --git a/web/shell/zoom.ts b/web/shell/zoom.ts index ef633fe..542e36b 100644 --- a/web/shell/zoom.ts +++ b/web/shell/zoom.ts @@ -13,21 +13,21 @@ namespace xsystem35 { this.zoomSelect.value = localStorage.getItem('zoom') || '1'; this.smoothingCheckbox = $('#smoothing'); this.smoothingCheckbox.addEventListener('change', this.handleSmoothing.bind(this)); - if (localStorage.getItem('smoothing') == 'false') { + if (localStorage.getItem('smoothing') === 'false') { this.smoothingCheckbox.checked = false; this.handleSmoothing(); } } handleZoom() { - var value = this.zoomSelect.value; + let value = this.zoomSelect.value; localStorage.setItem('zoom', value); - var contentsStyle = $('.contents').style; - if (value == 'fit') { + let contentsStyle = $('.contents').style; + if (value === 'fit') { contentsStyle.maxWidth = 'none'; contentsStyle.width = this.canvas.style.width = '100%'; } else { - var ratio = Number(value); + let ratio = Number(value); contentsStyle.maxWidth = 'none'; contentsStyle.width = this.canvas.style.width = this.canvas.width * ratio + 'px'; } diff --git a/web/worker/cdimage.ts b/web/worker/cdimage.ts index 6dc2ba0..ccde71a 100644 --- a/web/worker/cdimage.ts +++ b/web/worker/cdimage.ts @@ -5,7 +5,7 @@ class ISO9660FileSystem { constructor(private sectorReader: CDImageReader) { this.pvd = new PVD(sectorReader.readSector(0x10)); - if (this.pvd.type != 1) + if (this.pvd.type !== 1) throw('PVD not found'); } @@ -17,25 +17,26 @@ class ISO9660FileSystem { return this.pvd.rootDirEnt(); } - getDirEnt(name:string, parent:DirEnt): DirEnt { + getDirEnt(name: string, parent: DirEnt): DirEnt { name = name.toLowerCase(); - for (var e of this.readDir(parent)) { - if (e.name.toLowerCase() == name) + for (let e of this.readDir(parent)) { + if (e.name.toLowerCase() === name) return e; } return null; } - readDir(dirent:DirEnt): DirEnt[] { - var sector = dirent.sector; - var position = 0; - var length = dirent.size; - var entries:DirEnt[] = []; + readDir(dirent: DirEnt): DirEnt[] { + let sector = dirent.sector; + let position = 0; + let length = dirent.size; + let entries: DirEnt[] = []; + let buf: ArrayBuffer; while (position < length) { - if (position == 0) - var buf = this.sectorReader.readSector(sector); - var child = new DirEnt(buf, position); - if (child.length == 0) { + if (position === 0) + buf = this.sectorReader.readSector(sector); + let child = new DirEnt(buf, position); + if (child.length === 0) { // Padded end of sector position = 2048; } else { @@ -44,7 +45,7 @@ class ISO9660FileSystem { } if (position > 2048) throw('dirent across sector boundary'); - if (position == 2048) { + if (position === 2048) { sector++; position = 0; length -= 2048; @@ -53,21 +54,21 @@ class ISO9660FileSystem { return entries; } - readFile(dirent:DirEnt): Uint8Array[] { + readFile(dirent: DirEnt): Uint8Array[] { return this.sectorReader.readSequentialSectors(dirent.sector, dirent.size); } } class PVD { - private view:DataView; - constructor(private buf:ArrayBuffer) { + private view: DataView; + constructor(private buf: ArrayBuffer) { this.view = new DataView(buf); } get type(): number { return this.view.getUint8(0); } volumeLabel(): string { - var decoder = new TextDecoder('shift_jis'); + let decoder = new TextDecoder('shift_jis'); return decoder.decode(new DataView(this.buf, 40, 32)).trim(); } rootDirEnt(): DirEnt { @@ -76,8 +77,8 @@ class PVD { } class DirEnt { - private view:DataView; - constructor(private buf:ArrayBuffer, private offset:number) { + private view: DataView; + constructor(private buf: ArrayBuffer, private offset: number) { this.view = new DataView(buf, offset); } get length(): number { @@ -90,35 +91,35 @@ class DirEnt { return this.view.getUint32(10, true); } get isDirectory(): boolean { - return (this.view.getUint8(25) & 2) != 0; + return (this.view.getUint8(25) & 2) !== 0; } get name(): string { - var len = this.view.getUint8(32); - var decoder = new TextDecoder('shift_jis'); - return decoder.decode(new DataView(this.buf, this.offset+33, len)).split(';')[0]; + let len = this.view.getUint8(32); + let decoder = new TextDecoder('shift_jis'); + return decoder.decode(new DataView(this.buf, this.offset + 33, len)).split(';')[0]; } } interface CDImageReader { - readSector(sector:number): ArrayBuffer; + readSector(sector: number): ArrayBuffer; readSequentialSectors(startSector: number, length: number): Uint8Array[]; maxTrack(): number; - extractTrack(track:number): Blob; + extractTrack(track: number): Blob; } class ImageReaderBase { - constructor(public image:File) {} + constructor(public image: File) {} - readSequential(startOffset:number, - bytesToRead:number, - blockSize:number, - sectorSize:number, - sectorOffset:number): Uint8Array[] { - var sectors = Math.ceil(bytesToRead / sectorSize); - var blob = this.image.slice(startOffset, startOffset + sectors * blockSize); - var buf = new FileReaderSync().readAsArrayBuffer(blob); - var bufs:Uint8Array[] = []; - for (var i = 0; i < sectors; i++) { + readSequential(startOffset: number, + bytesToRead: number, + blockSize: number, + sectorSize: number, + sectorOffset: number): Uint8Array[] { + let sectors = Math.ceil(bytesToRead / sectorSize); + let blob = this.image.slice(startOffset, startOffset + sectors * blockSize); + let buf = new FileReaderSync().readAsArrayBuffer(blob); + let bufs: Uint8Array[] = []; + for (let i = 0; i < sectors; i++) { bufs.push(new Uint8Array(buf, i * blockSize + sectorOffset, Math.min(bytesToRead, sectorSize))); bytesToRead -= sectorSize; } @@ -127,16 +128,16 @@ class ImageReaderBase { } class ImgCueReader extends ImageReaderBase implements CDImageReader { - private tracks:{ type:string; index:string[]; }[]; + private tracks: Array<{ type: string; index: string[]; }>; - constructor(img:File, cue:File) { + constructor(img: File, cue: File) { super(img); this.parseCue(cue); } - readSector(sector:number): ArrayBuffer { - var start = sector * 2352 + 16; - var end = start + 2048; + readSector(sector: number): ArrayBuffer { + let start = sector * 2352 + 16; + let end = start + 2048; return new FileReaderSync().readAsArrayBuffer(this.image.slice(start, end)); } @@ -144,115 +145,117 @@ class ImgCueReader extends ImageReaderBase implements CDImageReader { return this.readSequential(startSector * 2352, length, 2352, 2048, 16); } - private parseCue(cueFile:File) { - var lines = new FileReaderSync().readAsText(cueFile).split('\n'); + private parseCue(cueFile: File) { + let lines = new FileReaderSync().readAsText(cueFile).split('\n'); this.tracks = []; - var currentTrack:number = null; - for (var line of lines) { - var fields = line.trim().split(/\s+/); + let currentTrack: number = null; + for (let line of lines) { + let fields = line.trim().split(/\s+/); switch (fields[0]) { case 'TRACK': currentTrack = Number(fields[1]); - this.tracks[currentTrack] = {type:fields[2], index:[]}; + this.tracks[currentTrack] = {type: fields[2], index: []}; break; case 'INDEX': if (currentTrack) this.tracks[currentTrack].index[Number(fields[1])] = fields[2]; break; + default: + ; // Do nothing } } } - maxTrack():number { + maxTrack(): number { return this.tracks.length - 1; } - extractTrack(track:number): Blob { - if (!this.tracks[track] || this.tracks[track].type != 'AUDIO') + extractTrack(track: number): Blob { + if (!this.tracks[track] || this.tracks[track].type !== 'AUDIO') return; - var start = this.indexToSector(this.tracks[track].index[1]) * 2352; - var end:number; - if (this.tracks[track+1]) { - var index = this.tracks[track+1].index[0] || this.tracks[track+1].index[1]; + let start = this.indexToSector(this.tracks[track].index[1]) * 2352; + let end: number; + if (this.tracks[track + 1]) { + let index = this.tracks[track + 1].index[0] || this.tracks[track + 1].index[1]; end = this.indexToSector(index) * 2352; } else { end = this.image.size; } - var size = end - start; + let size = end - start; return new Blob([createWaveHeader(size), this.image.slice(start, start + size)], {type : 'audio/wav'}); } - private indexToSector(index:string):number { - var msf = index.split(':').map(Number); - return msf[0]*60*75 + msf[1]*75 + msf[2]; + private indexToSector(index: string): number { + let msf = index.split(':').map(Number); + return msf[0] * 60 * 75 + msf[1] * 75 + msf[2]; } } enum MdsTrackMode { Audio = 0xa9, Mode1 = 0xaa }; class MdfMdsReader extends ImageReaderBase implements CDImageReader { - private tracks:{ mode:number; sectorSize:number; offset:number; sectors:number; }[]; + private tracks: Array<{ mode: number; sectorSize: number; offset: number; sectors: number; }>; - constructor(mdf:File, mds:File) { + constructor(mdf: File, mds: File) { super(mdf); this.parseMds(mds); } - private parseMds(mdsFile:File) { - var buf = new FileReaderSync().readAsArrayBuffer(mdsFile); + private parseMds(mdsFile: File) { + let buf = new FileReaderSync().readAsArrayBuffer(mdsFile); - var signature = new TextDecoder().decode(new DataView(buf, 0, 16)); - if (signature != 'MEDIA DESCRIPTOR') + let signature = new TextDecoder().decode(new DataView(buf, 0, 16)); + if (signature !== 'MEDIA DESCRIPTOR') throw mdsFile.name + ': not a mds file'; - var header = new DataView(buf, 0, 0x70); - var entries = header.getUint8(0x62); + let header = new DataView(buf, 0, 0x70); + let entries = header.getUint8(0x62); this.tracks = []; - for (var i = 0; i < entries; i++) { - var trackData = new DataView(buf, 0x70 + i * 0x50, 0x50); - var extraData = new DataView(buf, 0x70 + entries * 0x50 + i * 8, 8); - var mode = trackData.getUint8(0x00); - var track = trackData.getUint8(0x04); - var sectorSize = trackData.getUint16(0x10, true); - var offset = trackData.getUint32(0x28, true); // >4GB offset is not supported. - var sectors = extraData.getUint32(0x4, true); + for (let i = 0; i < entries; i++) { + let trackData = new DataView(buf, 0x70 + i * 0x50, 0x50); + let extraData = new DataView(buf, 0x70 + entries * 0x50 + i * 8, 8); + let mode = trackData.getUint8(0x00); + let track = trackData.getUint8(0x04); + let sectorSize = trackData.getUint16(0x10, true); + let offset = trackData.getUint32(0x28, true); // >4GB offset is not supported. + let sectors = extraData.getUint32(0x4, true); if (track < 100) - this.tracks[track] = {mode:mode, sectorSize:sectorSize, offset:offset, sectors:sectors}; + this.tracks[track] = {mode, sectorSize, offset, sectors}; } - if (this.tracks[1].mode != MdsTrackMode.Mode1) + if (this.tracks[1].mode !== MdsTrackMode.Mode1) throw 'track 1 is not mode1'; } - readSector(sector:number): ArrayBuffer { - var start = sector * this.tracks[1].sectorSize + 16; - var end = start + 2048; + readSector(sector: number): ArrayBuffer { + let start = sector * this.tracks[1].sectorSize + 16; + let end = start + 2048; return new FileReaderSync().readAsArrayBuffer(this.image.slice(start, end)); } readSequentialSectors(startSector: number, length: number): Uint8Array[] { - var track = this.tracks[1]; + let track = this.tracks[1]; return this.readSequential(track.offset + startSector * track.sectorSize, length, track.sectorSize, 2048, 16); } - maxTrack():number { + maxTrack(): number { return this.tracks.length - 1; } - extractTrack(track:number): Blob { - if (!this.tracks[track] || this.tracks[track].mode != MdsTrackMode.Audio) + extractTrack(track: number): Blob { + if (!this.tracks[track] || this.tracks[track].mode !== MdsTrackMode.Audio) return; - var size = this.tracks[track].sectors * 2352; - var chunks = this.readSequential(this.tracks[track].offset, size, this.tracks[track].sectorSize, 2352, 0); + let size = this.tracks[track].sectors * 2352; + let chunks = this.readSequential(this.tracks[track].offset, size, this.tracks[track].sectorSize, 2352, 0); return new Blob([createWaveHeader(size)].concat(chunks), {type : 'audio/wav'}); } } -function createWaveHeader(size:number):ArrayBuffer { - var buf = new ArrayBuffer(44); - var view = new DataView(buf); +function createWaveHeader(size: number): ArrayBuffer { + let buf = new ArrayBuffer(44); + let view = new DataView(buf); view.setUint32(0, 0x52494646, false); // 'RIFF' view.setUint32(4, size + 36, true); // filesize - 8 view.setUint32(8, 0x57415645, false); // 'WAVE' diff --git a/web/worker/imagereader-worker.ts b/web/worker/imagereader-worker.ts index 0d3be0a..c27fcc6 100644 --- a/web/worker/imagereader-worker.ts +++ b/web/worker/imagereader-worker.ts @@ -1,11 +1,11 @@ importScripts('cdimage.js'); -declare function postMessage(data: any, transfers:any): void; +declare function postMessage(data: any, transfers: any): void; class Installer { - private imgFile:File; - private cueFile:File; - private imageReader:CDImageReader; + private imgFile: File; + private cueFile: File; + private imageReader: CDImageReader; constructor() { addEventListener('message', this.onMessage.bind(this)); @@ -15,9 +15,9 @@ class Installer { switch (evt.data.command) { case 'setFile': this.setFile(evt.data.file); - var imgName = this.imgFile && this.imgFile.name; - var cueName = this.cueFile && this.cueFile.name; - postMessage({command:'readyState', img:imgName, cue:cueName}); + 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()) @@ -27,11 +27,13 @@ class Installer { if (this.ready()) this.getTrack(evt.data.track); break; + default: + throw('unknown command: ' + evt.data.command); } } - private setFile(file:File) { - var name = file.name.toLowerCase(); + private 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')) @@ -49,25 +51,25 @@ class Installer { } private extractFiles() { - var isofs = new ISO9660FileSystem(this.imageReader); - //this.walk(isofs, isofs.rootDir(), '/'); - var gamedata = isofs.getDirEnt('gamedata', isofs.rootDir()); + let isofs = new ISO9660FileSystem(this.imageReader); + // this.walk(isofs, isofs.rootDir(), '/'); + let gamedata = isofs.getDirEnt('gamedata', isofs.rootDir()); if (!gamedata) { - postMessage({command:'error', message:'インストールできません。GAMEDATAフォルダが見つかりません。'}); + postMessage({command: 'error', message: 'インストールできません。GAMEDATAフォルダが見つかりません。'}); return; } - var files: any = {}; - var transfers = []; - for (var e of isofs.readDir(gamedata)) { + let files: any = {}; + let transfers = []; + for (let e of isofs.readDir(gamedata)) { if (e.name.toLowerCase().endsWith('.ald')) { - var buffer = new ArrayBuffer(e.size); - var uint8 = new Uint8Array(buffer); - var ptr = 0; - for (var buf of isofs.readFile(e)) { + let buffer = new ArrayBuffer(e.size); + let uint8 = new Uint8Array(buffer); + let ptr = 0; + for (let buf of isofs.readFile(e)) { uint8.set(buf, ptr); ptr += buf.byteLength; } - if (ptr != e.size) + if (ptr !== e.size) throw('expected ' + e.size + ' bytes, but read ' + ptr + 'bytes'); files[e.name] = buffer; transfers.push(buffer); @@ -75,12 +77,12 @@ class Installer { } postMessage({command: 'extractFiles', volumeLabel: isofs.volumeLabel(), - files: files}, transfers); + files}, transfers); } - private walk(isofs:ISO9660FileSystem, dir:DirEnt, dirname:string) { - for (var e of isofs.readDir(dir)) { - if (e.name != '\0' && e.name != '\x01') { + private walk(isofs: ISO9660FileSystem, dir: DirEnt, dirname: string) { + for (let e of 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 + '/'); @@ -88,10 +90,10 @@ class Installer { } } - private getTrack(track:number) { - var blob = this.imageReader.extractTrack(track); - postMessage({command:'complete', track:track, wav:blob}); + private getTrack(track: number) { + let blob = this.imageReader.extractTrack(track); + postMessage({command: 'complete', track, wav: blob}); } } -var installer = new Installer(); +let installer = new Installer(); diff --git a/web/worker/text-decoder.ts b/web/worker/text-decoder.ts index c39a0a0..3d22b65 100644 --- a/web/worker/text-decoder.ts +++ b/web/worker/text-decoder.ts @@ -2,8 +2,8 @@ class FallbackTextDecoder { constructor(utfLabel?: string) {} decode(buffer: DataView): string { - var out = ''; - for (var i = 0; i < buffer.byteLength; i++) { + let out = ''; + for (let i = 0; i < buffer.byteLength; i++) { let c = buffer.getUint8(i); if (c < 128) out += String.fromCharCode(c); diff --git a/web/worker/tslint.json b/web/worker/tslint.json new file mode 120000 index 0000000..b9c0b95 --- /dev/null +++ b/web/worker/tslint.json @@ -0,0 +1 @@ +../shell/tslint.json \ No newline at end of file