mirror of
https://github.com/kichikuou/xsystem35-sdl2.git
synced 2026-10-05 21:28:22 +03:00
Add shell
This commit is contained in:
+1
-1
@@ -13,7 +13,7 @@ LFLAGS := $(EMFLAGS)
|
||||
LFLAGS += -s EXPORTED_FUNCTIONS="['_main']"
|
||||
LFLAGS += --preload-file gamedata@/
|
||||
|
||||
xsystem35.html: xsystem35.bc emterpretify_whitelist src/xsystem35
|
||||
docs/xsystem35.js: xsystem35.bc emterpretify_whitelist src/xsystem35
|
||||
$(CC) $< $(LFLAGS) $(shell grep '^ *-s' emterpretify_whitelist) -o $@
|
||||
|
||||
xsystem35.bc:
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>xsystem35 for web</title>
|
||||
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/pure/0.6.0/pure-min.css">
|
||||
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<style>
|
||||
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
|
||||
textarea.emscripten { font-family: monospace; width: 80%; }
|
||||
div.emscripten { text-align: center; }
|
||||
div.emscripten_border { border: 1px solid black; }
|
||||
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
|
||||
canvas.emscripten { border: 0px none; }
|
||||
.pixelated { image-rendering: pixelated; image-rendering: -moz-crisp-edges; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="contents">
|
||||
<div class="files">
|
||||
<ul>
|
||||
<li id="imgReady" class="notready"><i class="fa fa-file"></i> <code>KICHIKUOU.img</code></li>
|
||||
<li id="cueReady" class="notready"><i class="fa fa-file"></i> <code>KICHIKUOU.cue</code></li>
|
||||
</ul>
|
||||
<input type="file" id="fileselect" multiple/>
|
||||
</div>
|
||||
|
||||
<audio id="audio" controls></audio>
|
||||
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="emscripten" id="status">Downloading...</div>
|
||||
<div class="emscripten">
|
||||
<progress value="0" max="100" id="progress" hidden=1></progress>
|
||||
</div>
|
||||
<div class="emscripten_border">
|
||||
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="emscripten">
|
||||
<label for="smoothing">
|
||||
<input type="checkbox" id="smoothing" checked onclick="document.getElementById('canvas').classList.toggle('pixelated')">Smoothing
|
||||
</label>
|
||||
<input type="checkbox" id="resize">Resize canvas
|
||||
<input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer
|
||||
|
||||
<input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked,
|
||||
document.getElementById('resize').checked)">
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<script>
|
||||
var Module = {};
|
||||
</script>
|
||||
<script src="shell.js"></script>
|
||||
<script src="xsystem35.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,280 @@
|
||||
class ISO9660FileSystem {
|
||||
private pvd: PVD;
|
||||
|
||||
constructor(private sectorReader: CDImageReader) {
|
||||
this.pvd = new PVD(sectorReader.readSector(0x10));
|
||||
if (this.pvd.type != 1)
|
||||
throw('PVD not found');
|
||||
}
|
||||
|
||||
volumeLabel(): string {
|
||||
return this.pvd.volumeLabel();
|
||||
}
|
||||
|
||||
rootDir(): DirEnt {
|
||||
return this.pvd.rootDirEnt();
|
||||
}
|
||||
|
||||
getDirEnt(name:string, parent:DirEnt): DirEnt {
|
||||
name = name.toLowerCase();
|
||||
for (var 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[] = [];
|
||||
while (position < length) {
|
||||
if (position == 0)
|
||||
var buf = this.sectorReader.readSector(sector);
|
||||
var child = new DirEnt(buf, position);
|
||||
if (child.length == 0) {
|
||||
// Padded end of sector
|
||||
position = 2048;
|
||||
} else {
|
||||
entries.push(child);
|
||||
position += child.length;
|
||||
}
|
||||
if (position > 2048)
|
||||
throw('dirent across sector boundary');
|
||||
if (position == 2048) {
|
||||
sector++;
|
||||
position = 0;
|
||||
length -= 2048;
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
readFile(dirent:DirEnt, callback: (data:Uint8Array[]) => void) {
|
||||
this.sectorReader.readSequentialSectors(dirent.sector, dirent.size, callback);
|
||||
}
|
||||
}
|
||||
|
||||
class PVD {
|
||||
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');
|
||||
return decoder.decode(new DataView(this.buf, 40, 32)).trim();
|
||||
}
|
||||
rootDirEnt(): DirEnt {
|
||||
return new DirEnt(this.buf, 156);
|
||||
}
|
||||
}
|
||||
|
||||
class DirEnt {
|
||||
private view:DataView;
|
||||
constructor(private buf:ArrayBuffer, private offset:number) {
|
||||
this.view = new DataView(buf, offset);
|
||||
}
|
||||
get length(): number {
|
||||
return this.view.getUint8(0);
|
||||
}
|
||||
get sector(): number {
|
||||
return this.view.getUint32(2, true);
|
||||
}
|
||||
get size(): number {
|
||||
return this.view.getUint32(10, true);
|
||||
}
|
||||
get isDirectory(): boolean {
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
interface CDImageReader {
|
||||
readSector(sector:number): ArrayBuffer;
|
||||
readSequentialSectors(startSector: number, length: number, callback:(data:Uint8Array[])=>void): void;
|
||||
maxTrack(): number;
|
||||
extractTrack(track:number): Blob;
|
||||
}
|
||||
|
||||
class ImageReaderBase {
|
||||
constructor(public image:File) {}
|
||||
|
||||
readSequential(startOffset:number,
|
||||
bytesToRead:number,
|
||||
blockSize:number,
|
||||
sectorSize:number,
|
||||
sectorOffset:number,
|
||||
callback:(data:Uint8Array[])=>void) {
|
||||
var sectors = Math.ceil(bytesToRead / sectorSize);
|
||||
var chunk = 256;
|
||||
var reader = new FileReaderSync();
|
||||
while (sectors > 0) {
|
||||
var n = Math.min(chunk, sectors);
|
||||
var blob = this.image.slice(startOffset, startOffset + n * blockSize);
|
||||
var buf = reader.readAsArrayBuffer(blob);
|
||||
var bufs:Uint8Array[] = [];
|
||||
for (var i = 0; i < n; i++) {
|
||||
bufs.push(new Uint8Array(buf, i * blockSize + sectorOffset, Math.min(bytesToRead, sectorSize)));
|
||||
bytesToRead -= sectorSize;
|
||||
}
|
||||
callback(bufs);
|
||||
sectors -= n;
|
||||
startOffset += n * blockSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImgCueReader extends ImageReaderBase implements CDImageReader {
|
||||
private tracks:{ type:string; index:string[]; }[];
|
||||
|
||||
constructor(img:File, cue:File) {
|
||||
super(img);
|
||||
this.parseCue(cue);
|
||||
}
|
||||
|
||||
readSector(sector:number): ArrayBuffer {
|
||||
var start = sector * 2352 + 16;
|
||||
var end = start + 2048;
|
||||
return new FileReaderSync().readAsArrayBuffer(this.image.slice(start, end));
|
||||
}
|
||||
|
||||
readSequentialSectors(startSector: number, length: number, callback:(data:Uint8Array[])=>void) {
|
||||
this.readSequential(startSector * 2352, length, 2352, 2048, 16, callback);
|
||||
}
|
||||
|
||||
private parseCue(cueFile:File) {
|
||||
var lines = new FileReaderSync().readAsText(cueFile).split('\n');
|
||||
this.tracks = [];
|
||||
var currentTrack:number = null;
|
||||
for (var line of lines) {
|
||||
var fields = line.trim().split(/\s+/);
|
||||
switch (fields[0]) {
|
||||
case 'TRACK':
|
||||
currentTrack = Number(fields[1]);
|
||||
this.tracks[currentTrack] = {type:fields[2], index:[]};
|
||||
break;
|
||||
case 'INDEX':
|
||||
if (currentTrack)
|
||||
this.tracks[currentTrack].index[Number(fields[1])] = fields[2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maxTrack():number {
|
||||
return this.tracks.length - 1;
|
||||
}
|
||||
|
||||
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];
|
||||
end = this.indexToSector(index) * 2352;
|
||||
} else {
|
||||
end = this.image.size;
|
||||
}
|
||||
var 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];
|
||||
}
|
||||
}
|
||||
|
||||
enum MdsTrackMode { Audio = 0xa9, Mode1 = 0xaa };
|
||||
|
||||
class MdfMdsReader extends ImageReaderBase implements CDImageReader {
|
||||
private tracks:{ mode:number; sectorSize:number; offset:number; sectors:number; }[];
|
||||
|
||||
constructor(mdf:File, mds:File) {
|
||||
super(mdf);
|
||||
this.parseMds(mds);
|
||||
}
|
||||
|
||||
private parseMds(mdsFile:File) {
|
||||
var buf = new FileReaderSync().readAsArrayBuffer(mdsFile);
|
||||
|
||||
var 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);
|
||||
|
||||
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);
|
||||
if (track < 100)
|
||||
this.tracks[track] = {mode:mode, sectorSize:sectorSize, offset:offset, sectors:sectors};
|
||||
}
|
||||
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;
|
||||
return new FileReaderSync().readAsArrayBuffer(this.image.slice(start, end));
|
||||
}
|
||||
|
||||
readSequentialSectors(startSector: number, length: number, callback:(data:Uint8Array[])=>void) {
|
||||
var track = this.tracks[1];
|
||||
this.readSequential(track.offset + startSector * track.sectorSize, length, track.sectorSize, 2048, 16, callback);
|
||||
}
|
||||
|
||||
maxTrack():number {
|
||||
return this.tracks.length - 1;
|
||||
}
|
||||
|
||||
extractTrack(track:number): Blob {
|
||||
if (!this.tracks[track] || this.tracks[track].mode != MdsTrackMode.Audio)
|
||||
return;
|
||||
|
||||
var size = this.tracks[track].sectors * 2352;
|
||||
var chunks: any[] = [createWaveHeader(size)];
|
||||
|
||||
this.readSequential(this.tracks[track].offset, size, this.tracks[track].sectorSize, 2352, 0,
|
||||
buf => chunks.push(buf));
|
||||
|
||||
return new Blob(chunks);
|
||||
}
|
||||
}
|
||||
|
||||
function createWaveHeader(size:number):ArrayBuffer {
|
||||
var buf = new ArrayBuffer(44);
|
||||
var view = new DataView(buf);
|
||||
view.setUint32(0, 0x52494646, false); // 'RIFF'
|
||||
view.setUint32(4, size + 36, true); // filesize - 8
|
||||
view.setUint32(8, 0x57415645, false); // 'WAVE'
|
||||
view.setUint32(12, 0x666D7420, false); // 'fmt '
|
||||
view.setUint32(16, 16, true); // size of fmt chunk
|
||||
view.setUint16(20, 1, true); // PCM format
|
||||
view.setUint16(22, 2, true); // stereo
|
||||
view.setUint32(24, 44100, true); // sampling rate
|
||||
view.setUint32(28, 176400, true); // bytes/sec
|
||||
view.setUint16(32, 4, true); // block size
|
||||
view.setUint16(34, 16, true); // bit/sample
|
||||
view.setUint32(36, 0x64617461, false); // 'data'
|
||||
view.setUint32(40, size, true); // data size
|
||||
return buf;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
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 onMessage(evt: MessageEvent) {
|
||||
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});
|
||||
break;
|
||||
case 'extractFiles':
|
||||
if (this.ready())
|
||||
this.extractFiles();
|
||||
break;
|
||||
case 'getTrack':
|
||||
if (this.ready())
|
||||
this.getTrack(evt.data.track);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private setFile(file:File) {
|
||||
var 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 = new ImgCueReader(this.imgFile, this.cueFile);
|
||||
else
|
||||
this.imageReader = new MdfMdsReader(this.imgFile, this.cueFile);
|
||||
}
|
||||
}
|
||||
|
||||
private ready(): boolean {
|
||||
return !!this.imageReader;
|
||||
}
|
||||
|
||||
private extractFiles() {
|
||||
var isofs = new ISO9660FileSystem(this.imageReader);
|
||||
//this.walk(isofs, isofs.rootDir(), '/');
|
||||
var gamedata = isofs.getDirEnt('gamedata', isofs.rootDir()) ||
|
||||
isofs.getDirEnt('mugen', isofs.rootDir());
|
||||
if (!gamedata) {
|
||||
postMessage({command:'error', message:'インストールできません。GAMEDATAフォルダが見つかりません。'});
|
||||
return;
|
||||
}
|
||||
var files: any = {};
|
||||
var transfers = [];
|
||||
for (var e of isofs.readDir(gamedata)) {
|
||||
if (e.name.toLowerCase().endsWith('.dat')) {
|
||||
var buffer = new ArrayBuffer(e.size);
|
||||
var uint8 = new Uint8Array(buffer);
|
||||
var ptr = 0;
|
||||
isofs.readFile(e, (bufs) => {
|
||||
for (var buf of bufs) {
|
||||
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: files}, transfers);
|
||||
}
|
||||
|
||||
private walk(isofs:ISO9660FileSystem, dir:DirEnt, dirname:string) {
|
||||
for (var 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 + '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getTrack(track:number) {
|
||||
var blob = this.imageReader.extractTrack(track);
|
||||
postMessage({command:'complete', track:track, wav:blob});
|
||||
}
|
||||
}
|
||||
|
||||
var installer = new Installer();
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "xsystem35-shell-worker",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@types/text-encoding": "0.0.30"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"outDir": "../docs",
|
||||
"lib": ["es6", "webworker"],
|
||||
"noImplicitAny": true,
|
||||
"sourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "xsystem35-shell",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@types/emscripten": "0.0.29"
|
||||
}
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
var $: (selector:string)=>HTMLElement = document.querySelector.bind(document);
|
||||
|
||||
declare namespace Module {
|
||||
var noInitialRun: boolean;
|
||||
|
||||
// Undocumented methods / attributes
|
||||
var canvas: HTMLElement;
|
||||
function setStatus(status: string): void;
|
||||
function monitorRunDependencies(left: number): void;
|
||||
function callMain(): void;
|
||||
|
||||
var cdPlayer: CDPlayer;
|
||||
};
|
||||
|
||||
declare namespace FS {
|
||||
function writeFile(path: string, data: ArrayBufferView | string, opts?: {encoding?: string; flags?: string}): void;
|
||||
};
|
||||
|
||||
class Sys3Shell {
|
||||
private imageLoader: ImageLoader;
|
||||
status: HTMLElement = document.getElementById('status');
|
||||
progress: HTMLProgressElement = <HTMLProgressElement>document.getElementById('progress');
|
||||
|
||||
constructor() {
|
||||
this.imageLoader = new ImageLoader(this);
|
||||
this.setStatus('Downloading...');
|
||||
window.onerror = () => {
|
||||
this.setStatus('Exception thrown, see JavaScript console');
|
||||
this.setStatus = (text: string) => {
|
||||
if (text) Module.printErr('[post-exception status] ' + text);
|
||||
};
|
||||
};
|
||||
|
||||
// Initialize the Module object
|
||||
Module.print = Module.printErr = console.log.bind(console);
|
||||
Module.canvas = document.getElementById('canvas');
|
||||
Module.noInitialRun = true;
|
||||
Module.setStatus = this.setStatus.bind(this);
|
||||
Module.monitorRunDependencies = this.monitorRunDependencies.bind(this);
|
||||
|
||||
Module.cdPlayer = new CDPlayer(this.imageLoader);
|
||||
}
|
||||
|
||||
setStatus(text: string) {
|
||||
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
|
||||
var now = Date.now();
|
||||
if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon
|
||||
if (m) {
|
||||
text = m[1];
|
||||
this.progress.value = parseInt(m[2])*100;
|
||||
this.progress.max = parseInt(m[4])*100;
|
||||
this.progress.hidden = false;
|
||||
} else {
|
||||
this.progress.value = null;
|
||||
this.progress.max = null;
|
||||
this.progress.hidden = true;
|
||||
}
|
||||
this.status.innerHTML = text;
|
||||
}
|
||||
|
||||
totalDependencies: number = 0;
|
||||
monitorRunDependencies(left: number) {
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
this.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
|
||||
}
|
||||
}
|
||||
|
||||
class CDPlayer {
|
||||
audio = <HTMLAudioElement>$('audio');
|
||||
private blobs: Blob[];
|
||||
|
||||
constructor(private imageLoader:ImageLoader) {
|
||||
this.blobs = [];
|
||||
}
|
||||
|
||||
play(track:number, loop:number) {
|
||||
if (this.blobs[track]) {
|
||||
this.startPlayback(this.blobs[track]);
|
||||
return;
|
||||
}
|
||||
this.imageLoader.getCDDA(track, (blob) => {
|
||||
this.blobs[track] = blob;
|
||||
this.startPlayback(blob);
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.audio.pause();
|
||||
}
|
||||
|
||||
private startPlayback(blob:Blob) {
|
||||
this.audio.setAttribute('src', URL.createObjectURL(blob));
|
||||
this.audio.load();
|
||||
this.audio.play();
|
||||
}
|
||||
}
|
||||
|
||||
class ImageLoader {
|
||||
private worker: Worker;
|
||||
private cddaCallback: (wab:Blob)=>void;
|
||||
|
||||
constructor(private shell:Sys3Shell) {
|
||||
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: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 code').textContent = imgName;
|
||||
}
|
||||
if (cueName) {
|
||||
$('#cueReady').classList.remove('notready');
|
||||
$('#cueReady code').textContent = cueName;
|
||||
}
|
||||
}
|
||||
|
||||
private populateFiles(files:any, volumeLabel:string) {
|
||||
for (var name in files) {
|
||||
var data: ArrayBufferView = new Uint8Array(files[name]);
|
||||
FS.writeFile(name, data, { encoding: 'binary' });
|
||||
}
|
||||
Module.callMain();
|
||||
}
|
||||
|
||||
private handleFileSelect(evt:Event) {
|
||||
var input = <HTMLInputElement>evt.target;
|
||||
var files = input.files;
|
||||
for (var i = 0; i < files.length; i++)
|
||||
this.setFile(files[i]);
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
private handleDragOver(evt:DragEvent) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
evt.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
private handleDrop(evt:DragEvent) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
var files = evt.dataTransfer.files;
|
||||
for (var i = 0; i < files.length; i++)
|
||||
this.setFile(files[i]);
|
||||
}
|
||||
|
||||
private setFile(file:File) {
|
||||
this.send({command:'setFile', file: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;
|
||||
}
|
||||
}
|
||||
|
||||
private onWorkerError(evt: Event) {
|
||||
console.log('worker error', evt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var shell = new Sys3Shell();
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"outDir": "../docs",
|
||||
"noImplicitAny": true,
|
||||
"sourceMap": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user