From 123760e9ac3b9b52b109f30570ce17241a0f3d50 Mon Sep 17 00:00:00 2001 From: Ethan O'Brien <77750390+ethanaobrien@users.noreply.github.com> Date: Mon, 3 Jul 2023 12:03:00 -0500 Subject: [PATCH] Keep states in browser --- data/GameManager.js | 12 +++++---- data/emulator.js | 62 +++++++++++++++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/data/GameManager.js b/data/GameManager.js index 984286f..0bb7c54 100644 --- a/data/GameManager.js +++ b/data/GameManager.js @@ -82,16 +82,18 @@ class EJS_GameManager { this.FS.writeFile('/game.state', state); this.functions.loadState("game.state", 0); setTimeout(() => { - this.FS.unlink('game.state'); + try { + this.FS.unlink('game.state'); + } catch(e){} }, 5000) } screenshot() { this.functions.screenshot(); return this.FS.readFile('screenshot.png'); } - quickSave() { + quickSave(slot) { + if (!slot) slot = 1; (async () => { - let slot = 0; let name = slot + '-quick.state'; try { this.FS.unlink(name); @@ -100,9 +102,9 @@ class EJS_GameManager { this.FS.writeFile('/'+name, data); })(); } - quickLoad() { + quickLoad(slot) { + if (!slot) slot = 1; (async () => { - let slot = 0; let name = slot + '-quick.state'; this.functions.loadState(name, 0); })(); diff --git a/data/emulator.js b/data/emulator.js index f12b1a4..a9c3bf5 100644 --- a/data/emulator.js +++ b/data/emulator.js @@ -175,7 +175,8 @@ class EmulatorJS { 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") + core: new window.EJS_STORAGE("EmulatorJS-core", "core"), + states: new window.EJS_STORAGE("EmulatorJS-states", "states") } this.game.classList.add("ejs_game"); @@ -508,7 +509,7 @@ class EmulatorJS { getBaseFileName() { //Only once game and core is loaded if (!this.started) return null; - if (this.config.gameName) { + if (typeof this.config.gameName === "string") { const invalidCharacters = /[#<$+%>!`&*'|{}/\\?"=@:^\r\n]/ig; const name = this.config.gameName.replace(invalidCharacters, "").trim(); if (name) return name; @@ -517,6 +518,9 @@ class EmulatorJS { parts.splice(parts.length-1, 1); return parts.join("."); } + saveInBrowserSupported() { + return !!window.indexedDB && (typeof this.config.gameName === "string" || !this.config.gameUrl.startsWith("blob:")); + } downloadStartState() { if (typeof this.config.loadState !== "string") { this.startGame(); @@ -798,11 +802,13 @@ class EmulatorJS { hideMenu(); }); const qSave = addButton("Quick Save", false, () => { - this.gameManager.quickSave(); + const slot = this.settings['save-state-slot'] ? this.settings['save-state-slot'] : "1"; + this.gameManager.quickSave(slot); hideMenu(); }); const qLoad = addButton("Quick Load", false, () => { - this.gameManager.quickLoad(); + const slot = this.settings['save-state-slot'] ? this.settings['save-state-slot'] : "1"; + this.gameManager.quickLoad(slot); hideMenu(); }); addButton("EmulatorJS", false, () => { @@ -989,19 +995,29 @@ class EmulatorJS { }); if (called > 0) return; if (stateUrl) URL.revokeObjectURL(stateUrl); - const blob = new Blob([state]); - stateUrl = URL.createObjectURL(blob); - const a = this.createElement("a"); - a.href = stateUrl; - a.download = this.getBaseFileName()+".state"; - a.click(); + if (this.settings['save-state-location'] === "browser" && this.saveInBrowserSupported()) { + this.storage.states.put(this.getBaseFileName()+".state", state); + } else { + const blob = new Blob([state]); + stateUrl = URL.createObjectURL(blob); + const a = this.createElement("a"); + a.href = stateUrl; + a.download = this.getBaseFileName()+".state"; + a.click(); + } }); const loadState = addButton("Load State", '', async () => { const called = this.callEvent("load"); if (called > 0) return; - const file = await this.selectFile(); - const state = new Uint8Array(await file.arrayBuffer()); - this.gameManager.loadState(state); + if (this.settings['save-state-location'] === "browser" && this.saveInBrowserSupported()) { + this.storage.states.get(this.getBaseFileName()+".state").then(e => { + this.gameManager.loadState(e); + }) + } else { + const file = await this.selectFile(); + const state = new Uint8Array(await file.arrayBuffer()); + this.gameManager.loadState(state); + } }); const controlMenu = addButton("Control Settings", '', () => { this.controlMenu.style.display = ""; @@ -2136,6 +2152,7 @@ class EmulatorJS { } menuOptionChanged(option, value) { this.saveSettings(); + console.log(option, value); if (option === "shader") { try { this.Module.FS.unlink("/shader/shader.glslp"); @@ -2289,12 +2306,6 @@ class EmulatorJS { addToMenu(this.localization("Disk"), "disk", diskLabels, this.gameManager.getCurrentDisk().toString()); } - - addToMenu(this.localization('FPS'), 'fps', { - 'show': this.localization("show"), - 'hide': this.localization("hide") - }, 'hide'); - if (window.EJS_SHADERS) { addToMenu(this.localization('Shaders'), 'shader', { 'disabled': this.localization("Disabled"), @@ -2307,6 +2318,19 @@ class EmulatorJS { }, 'disabled'); } + addToMenu(this.localization('FPS'), 'fps', { + 'show': this.localization("show"), + 'hide': this.localization("hide") + }, 'hide'); + + if (this.saveInBrowserSupported()) { + addToMenu(this.localization('Save State Slot'), 'save-state-slot', ["1", "2", "3", "4", "5", "6", "7", "8", "9"], "1"); + addToMenu(this.localization('Save State Location'), 'save-state-location', { + 'download': this.localization("Download"), + 'browser': this.localization("Keep in Browser") + }, 'download'); + } + this.gameManager.getCoreOptions().split('\n').forEach((line, index) => { let option = line.split('; '); let name = option[0];