diff --git a/src/GameManager.js b/src/GameManager.js index a96bfb2..c039521 100644 --- a/src/GameManager.js +++ b/src/GameManager.js @@ -24,7 +24,7 @@ class EJS_GameManager { //this.FS.writeFile("/home/web_user/retroarch/userdata/retroarch.cfg", this.getRetroArchCfg()); this.initShaders(); - window.RA.nonblock = true; //lets see if this works + //window.RA.nonblock = true; //lets see if this works } mkdir(path) { try { diff --git a/src/emulator.js b/src/emulator.js index c971f77..4746c9b 100644 --- a/src/emulator.js +++ b/src/emulator.js @@ -1,4 +1,20 @@ class EmulatorJS { + version = { + a5200: 1, + beetle_vb: 1, + desmume2015: 1, + fbalpha2012_cps1: 1, + fbalpha2012_cps2: 1, + fceumm: 1, + gambatte: 1, + mame2003: 1, + mednafen_psx_hw: 1, + melonds: 1, + mgba: 1, + mupen64plus_next: 1, + nestopia: 1, + snes9x: 1 + } createElement(type) { return document.createElement(type); } @@ -38,14 +54,12 @@ class EmulatorJS { cb({ data: data, headers: { - "content-length": xhr.getResponseHeader('content-length'), - "content-type": xhr.getResponseHeader('content-type'), - "last-modified": xhr.getResponseHeader('last-modified') + "content-length": xhr.getResponseHeader('content-length') } }); } } - xhr.responseType = opts.responseType; + if (opts.responseType) xhr.responseType = opts.responseType; xhr.onerror = () => cb(-1); xhr.open(opts.method, path, true); xhr.send(); @@ -90,6 +104,11 @@ class EmulatorJS { this.canvas.classList.add('ejs_canvas'); this.bindListeners(); this.fullscreen = false; + this.storage = { + rom: new window.EJS_STORAGE("EmulatorJS-roms", "rom"), + bios: new window.EJS_STORAGE("EmulatorJS-bios", "bios"), + core: new window.EJS_STORAGE("EmulatorJS-core", "core") + } this.game.classList.add("ejs_game"); @@ -279,13 +298,8 @@ class EmulatorJS { } downloadGameCore() { this.textElem.innerText = this.localization("Download Game Core"); - this.downloadFile('cores/'+this.getCore()+'-wasm.data', (res) => { - if (res === -1) { - this.textElem.innerText = "Error"; - this.textElem.style.color = "red"; - return; - } - this.checkCompression(new Uint8Array(res.data), this.localization("Decompress Game Core")).then((data) => { + const gotCore = (data) => { + this.checkCompression(new Uint8Array(data), this.localization("Decompress Game Core")).then((data) => { //console.log(data); let js, wasm; for (let k in data) { @@ -297,10 +311,27 @@ class EmulatorJS { } this.initGameCore(js, wasm); }); - }, (progress) => { - this.textElem.innerText = this.localization("Download Game Core") + progress; - }, false, {responseType: "arraybuffer", method: "GET"}); - + } + this.storage.core.get(this.getCore()+'-wasm.data').then((result) => { + if (result && result.version === this.version[this.getCore()] && !this.debug) { + gotCore(result.data); + return; + } + this.downloadFile('cores/'+this.getCore()+'-wasm.data', (res) => { + if (res === -1) { + this.textElem.innerText = "Error"; + this.textElem.style.color = "red"; + return; + } + gotCore(res.data); + this.storage.core.put(this.getCore()+'-wasm.data', { + version: this.version[this.getCore()], + data: res.data + }); + }, (progress) => { + this.textElem.innerText = this.localization("Download Game Core") + progress; + }, false, {responseType: "arraybuffer", method: "GET"}); + }) } initGameCore(js, wasm) { this.initModule(wasm); @@ -360,13 +391,8 @@ class EmulatorJS { return; } this.textElem.innerText = this.localization("Download Game BIOS"); - this.downloadFile(this.config.biosUrl, (res) => { - if (res === -1) { - this.textElem.innerText = "Error"; - this.textElem.style.color = "red"; - return; - } - this.checkCompression(new Uint8Array(res.data), this.localization("Decompress Game BIOS")).then((data) => { + const gotBios = (data) => { + this.checkCompression(new Uint8Array(data), this.localization("Decompress Game BIOS")).then((data) => { for (const k in data) { if (k === "!!notCompressedData") { FS.writeFile(this.config.biosUrl.split('/').pop().split("#")[0].split("?")[0], data[k]); @@ -377,23 +403,39 @@ class EmulatorJS { FS.writeFile(k.split('/').pop(), data[k]); } this.startGame(); - }); - }, (progress) => { - this.textElem.innerText = this.localization("Download Game Data") + progress; - }, true, {responseType: "arraybuffer", method: "GET"}); + }) + } + + this.downloadFile(this.config.biosUrl, (res) => { + this.storage.bios.get(this.config.biosUrl.split("/").pop()).then((result) => { + if (result && result['content-length'] === res.headers['content-length'] && !this.debug) { + gotBios(result.data); + return; + } + this.downloadFile(this.config.biosUrl, (res) => { + if (res === -1) { + this.textElem.innerText = "Error"; + this.textElem.style.color = "red"; + return; + } + gotBios(res.data); + this.storage.bios.put(this.config.biosUrl.split("/").pop(), { + "content-length": res.headers['content-length'], + data: res.data + }) + }, (progress) => { + this.textElem.innerText = this.localization("Download Game BIOS") + progress; + }, true, {responseType: "arraybuffer", method: "GET"}); + }) + }, null, true, {method: "HEAD"}) } downloadRom() { this.gameManager = new window.EJS_GameManager(this.Module); this.textElem.innerText = this.localization("Download Game Data"); - this.downloadFile(this.config.gameUrl, (res) => { - if (res === -1) { - this.textElem.innerText = "Error"; - this.textElem.style.color = "red"; - return; - } - this.checkCompression(new Uint8Array(res.data), this.localization("Decompress Game Data")).then((data) => { + const gotGameData = (data) => { + this.checkCompression(new Uint8Array(data), this.localization("Decompress Game Data")).then((data) => { for (const k in data) { if (k === "!!notCompressedData") { this.fileName = this.config.gameUrl.split('/').pop().split("#")[0].split("?")[0]; @@ -410,9 +452,29 @@ class EmulatorJS { } this.downloadBios(); }); - }, (progress) => { - this.textElem.innerText = this.localization("Download Game Data") + progress; - }, true, {responseType: "arraybuffer", method: "GET"}); + } + this.downloadFile(this.config.gameUrl, (res) => { + this.storage.rom.get(this.config.gameUrl.split("/").pop()).then((result) => { + if (result && result['content-length'] === res.headers['content-length'] && !this.debug) { + gotGameData(result.data); + return; + } + this.downloadFile(this.config.gameUrl, (res) => { + if (res === -1) { + this.textElem.innerText = "Error"; + this.textElem.style.color = "red"; + return; + } + gotGameData(res.data); + this.storage.rom.put(this.config.gameUrl.split("/").pop(), { + "content-length": res.headers['content-length'], + data: res.data + }) + }, (progress) => { + this.textElem.innerText = this.localization("Download Game Data") + progress; + }, true, {responseType: "arraybuffer", method: "GET"}); + }) + }, null, true, {method: "HEAD"}) } initModule(wasmData) { window.Module = { @@ -440,9 +502,6 @@ class EmulatorJS { if (fileName.endsWith(".wasm")) { return URL.createObjectURL(new Blob([wasmData], {type: "application/wasm"})); } - }, - 'readAsync': function(a, b, c) { - console.log(a, b, c) } }; this.Module = window.Module; diff --git a/src/loader.js b/src/loader.js index c8d8e00..42f8540 100644 --- a/src/loader.js +++ b/src/loader.js @@ -37,6 +37,7 @@ await loadScript('emulator.js'); await loadScript('nipplejs.js'); await loadScript('shaders.js'); + await loadScript('storage.js'); await loadScript('gamepad.js'); await loadScript('GameManager.js'); await loadStyle('css/main.css'); diff --git a/src/storage.js b/src/storage.js new file mode 100644 index 0000000..8f00ecf --- /dev/null +++ b/src/storage.js @@ -0,0 +1,69 @@ +class EJS_STORAGE { + constructor(dbName, storeName) { + this.dbName = dbName; + this.storeName = storeName; + } + get(key) { + return new Promise((resolve, reject) => { + let openRequest = indexedDB.open(this.dbName, 1); + openRequest.onerror = () => {}; + openRequest.onsuccess = () => { + let db = openRequest.result; + let transaction = db.transaction([this.storeName], "readwrite"); + let objectStore = transaction.objectStore(this.storeName); + let request = objectStore.get(key); + request.onsuccess = async (e) => { + resolve(request.result); + }; + request.onerror = () => resolve(); + }; + openRequest.onupgradeneeded = () => { + let db = openRequest.result; + if (! db.objectStoreNames.contains(this.storeName)) { + db.createObjectStore(this.storeName); + }; + }; + }); + } + put(key, data) { + return new Promise((resolve, reject) => { + let openRequest = indexedDB.open(this.dbName, 1); + openRequest.onerror = () => {}; + openRequest.onsuccess = () => { + let db = openRequest.result; + let transaction = db.transaction([this.storeName], "readwrite"); + let objectStore = transaction.objectStore(this.storeName); + let request = objectStore.put(data, key); + request.onerror = () => resolve(); + request.onsuccess = () => resolve(); + }; + openRequest.onupgradeneeded = () => { + let db = openRequest.result; + if (! db.objectStoreNames.contains(this.storeName)) { + db.createObjectStore(this.storeName); + }; + }; + }) + } + remove(key) { + return new Promise((resolve, reject) => { + let openRequest = indexedDB.open(this.dbName, 1); + openRequest.onerror = () => {}; + openRequest.onsuccess = () => { + let db = openRequest.result; + let transaction = db.transaction([this.storeName], "readwrite"); + let objectStore = transaction.objectStore(this.storeName); + let request2 = objectStore.delete(key); + request2.onsuccess = () => resolve(); + request2.onerror = () => {}; + }; + openRequest.onupgradeneeded = () => { + let db = openRequest.result; + if (! db.objectStoreNames.contains(this.storeName)) { + db.createObjectStore(this.storeName); + }; + }; + }); + } +} +window.EJS_STORAGE = EJS_STORAGE;