mirror of
https://github.com/EmulatorJS/EmulatorJS.git
synced 2024-09-20 02:46:23 +00:00
store roms/bios/core in browser
This commit is contained in:
parent
94fcc7f108
commit
a9f7505113
4 changed files with 169 additions and 40 deletions
|
@ -24,7 +24,7 @@ class EJS_GameManager {
|
||||||
//this.FS.writeFile("/home/web_user/retroarch/userdata/retroarch.cfg", this.getRetroArchCfg());
|
//this.FS.writeFile("/home/web_user/retroarch/userdata/retroarch.cfg", this.getRetroArchCfg());
|
||||||
|
|
||||||
this.initShaders();
|
this.initShaders();
|
||||||
window.RA.nonblock = true; //lets see if this works
|
//window.RA.nonblock = true; //lets see if this works
|
||||||
}
|
}
|
||||||
mkdir(path) {
|
mkdir(path) {
|
||||||
try {
|
try {
|
||||||
|
|
121
src/emulator.js
121
src/emulator.js
|
@ -1,4 +1,20 @@
|
||||||
class EmulatorJS {
|
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) {
|
createElement(type) {
|
||||||
return document.createElement(type);
|
return document.createElement(type);
|
||||||
}
|
}
|
||||||
|
@ -38,14 +54,12 @@ class EmulatorJS {
|
||||||
cb({
|
cb({
|
||||||
data: data,
|
data: data,
|
||||||
headers: {
|
headers: {
|
||||||
"content-length": xhr.getResponseHeader('content-length'),
|
"content-length": xhr.getResponseHeader('content-length')
|
||||||
"content-type": xhr.getResponseHeader('content-type'),
|
|
||||||
"last-modified": xhr.getResponseHeader('last-modified')
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xhr.responseType = opts.responseType;
|
if (opts.responseType) xhr.responseType = opts.responseType;
|
||||||
xhr.onerror = () => cb(-1);
|
xhr.onerror = () => cb(-1);
|
||||||
xhr.open(opts.method, path, true);
|
xhr.open(opts.method, path, true);
|
||||||
xhr.send();
|
xhr.send();
|
||||||
|
@ -90,6 +104,11 @@ class EmulatorJS {
|
||||||
this.canvas.classList.add('ejs_canvas');
|
this.canvas.classList.add('ejs_canvas');
|
||||||
this.bindListeners();
|
this.bindListeners();
|
||||||
this.fullscreen = false;
|
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");
|
this.game.classList.add("ejs_game");
|
||||||
|
@ -279,13 +298,8 @@ class EmulatorJS {
|
||||||
}
|
}
|
||||||
downloadGameCore() {
|
downloadGameCore() {
|
||||||
this.textElem.innerText = this.localization("Download Game Core");
|
this.textElem.innerText = this.localization("Download Game Core");
|
||||||
this.downloadFile('cores/'+this.getCore()+'-wasm.data', (res) => {
|
const gotCore = (data) => {
|
||||||
if (res === -1) {
|
this.checkCompression(new Uint8Array(data), this.localization("Decompress Game Core")).then((data) => {
|
||||||
this.textElem.innerText = "Error";
|
|
||||||
this.textElem.style.color = "red";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.checkCompression(new Uint8Array(res.data), this.localization("Decompress Game Core")).then((data) => {
|
|
||||||
//console.log(data);
|
//console.log(data);
|
||||||
let js, wasm;
|
let js, wasm;
|
||||||
for (let k in data) {
|
for (let k in data) {
|
||||||
|
@ -297,10 +311,27 @@ class EmulatorJS {
|
||||||
}
|
}
|
||||||
this.initGameCore(js, wasm);
|
this.initGameCore(js, wasm);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
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) => {
|
}, (progress) => {
|
||||||
this.textElem.innerText = this.localization("Download Game Core") + progress;
|
this.textElem.innerText = this.localization("Download Game Core") + progress;
|
||||||
}, false, {responseType: "arraybuffer", method: "GET"});
|
}, false, {responseType: "arraybuffer", method: "GET"});
|
||||||
|
})
|
||||||
}
|
}
|
||||||
initGameCore(js, wasm) {
|
initGameCore(js, wasm) {
|
||||||
this.initModule(wasm);
|
this.initModule(wasm);
|
||||||
|
@ -360,13 +391,8 @@ class EmulatorJS {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.textElem.innerText = this.localization("Download Game BIOS");
|
this.textElem.innerText = this.localization("Download Game BIOS");
|
||||||
this.downloadFile(this.config.biosUrl, (res) => {
|
const gotBios = (data) => {
|
||||||
if (res === -1) {
|
this.checkCompression(new Uint8Array(data), this.localization("Decompress Game BIOS")).then((data) => {
|
||||||
this.textElem.innerText = "Error";
|
|
||||||
this.textElem.style.color = "red";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.checkCompression(new Uint8Array(res.data), this.localization("Decompress Game BIOS")).then((data) => {
|
|
||||||
for (const k in data) {
|
for (const k in data) {
|
||||||
if (k === "!!notCompressedData") {
|
if (k === "!!notCompressedData") {
|
||||||
FS.writeFile(this.config.biosUrl.split('/').pop().split("#")[0].split("?")[0], data[k]);
|
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]);
|
FS.writeFile(k.split('/').pop(), data[k]);
|
||||||
}
|
}
|
||||||
this.startGame();
|
this.startGame();
|
||||||
});
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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) => {
|
}, (progress) => {
|
||||||
this.textElem.innerText = this.localization("Download Game Data") + progress;
|
this.textElem.innerText = this.localization("Download Game BIOS") + progress;
|
||||||
}, true, {responseType: "arraybuffer", method: "GET"});
|
}, true, {responseType: "arraybuffer", method: "GET"});
|
||||||
|
})
|
||||||
|
}, null, true, {method: "HEAD"})
|
||||||
|
|
||||||
}
|
}
|
||||||
downloadRom() {
|
downloadRom() {
|
||||||
this.gameManager = new window.EJS_GameManager(this.Module);
|
this.gameManager = new window.EJS_GameManager(this.Module);
|
||||||
|
|
||||||
this.textElem.innerText = this.localization("Download Game Data");
|
this.textElem.innerText = this.localization("Download Game Data");
|
||||||
this.downloadFile(this.config.gameUrl, (res) => {
|
const gotGameData = (data) => {
|
||||||
if (res === -1) {
|
this.checkCompression(new Uint8Array(data), this.localization("Decompress Game Data")).then((data) => {
|
||||||
this.textElem.innerText = "Error";
|
|
||||||
this.textElem.style.color = "red";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.checkCompression(new Uint8Array(res.data), this.localization("Decompress Game Data")).then((data) => {
|
|
||||||
for (const k in data) {
|
for (const k in data) {
|
||||||
if (k === "!!notCompressedData") {
|
if (k === "!!notCompressedData") {
|
||||||
this.fileName = this.config.gameUrl.split('/').pop().split("#")[0].split("?")[0];
|
this.fileName = this.config.gameUrl.split('/').pop().split("#")[0].split("?")[0];
|
||||||
|
@ -410,9 +452,29 @@ class EmulatorJS {
|
||||||
}
|
}
|
||||||
this.downloadBios();
|
this.downloadBios();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
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) => {
|
}, (progress) => {
|
||||||
this.textElem.innerText = this.localization("Download Game Data") + progress;
|
this.textElem.innerText = this.localization("Download Game Data") + progress;
|
||||||
}, true, {responseType: "arraybuffer", method: "GET"});
|
}, true, {responseType: "arraybuffer", method: "GET"});
|
||||||
|
})
|
||||||
|
}, null, true, {method: "HEAD"})
|
||||||
}
|
}
|
||||||
initModule(wasmData) {
|
initModule(wasmData) {
|
||||||
window.Module = {
|
window.Module = {
|
||||||
|
@ -440,9 +502,6 @@ class EmulatorJS {
|
||||||
if (fileName.endsWith(".wasm")) {
|
if (fileName.endsWith(".wasm")) {
|
||||||
return URL.createObjectURL(new Blob([wasmData], {type: "application/wasm"}));
|
return URL.createObjectURL(new Blob([wasmData], {type: "application/wasm"}));
|
||||||
}
|
}
|
||||||
},
|
|
||||||
'readAsync': function(a, b, c) {
|
|
||||||
console.log(a, b, c)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.Module = window.Module;
|
this.Module = window.Module;
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
await loadScript('emulator.js');
|
await loadScript('emulator.js');
|
||||||
await loadScript('nipplejs.js');
|
await loadScript('nipplejs.js');
|
||||||
await loadScript('shaders.js');
|
await loadScript('shaders.js');
|
||||||
|
await loadScript('storage.js');
|
||||||
await loadScript('gamepad.js');
|
await loadScript('gamepad.js');
|
||||||
await loadScript('GameManager.js');
|
await loadScript('GameManager.js');
|
||||||
await loadStyle('css/main.css');
|
await loadStyle('css/main.css');
|
||||||
|
|
69
src/storage.js
Normal file
69
src/storage.js
Normal file
|
@ -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;
|
Loading…
Reference in a new issue