class EJS_GameManager { constructor(Module, EJS) { this.EJS = EJS; this.Module = Module; this.FS = this.Module.FS; this.functions = { restart: this.Module.cwrap('system_restart', '', []), getStateInfo: this.Module.cwrap('get_state_info', 'string', []), //these names are dumb saveStateInfo: this.Module.cwrap('save_state_info', 'null', []), loadState: this.Module.cwrap('load_state', 'number', ['string', 'number']), screenshot: this.Module.cwrap('cmd_take_screenshot', '', []), simulateInput: this.Module.cwrap('simulate_input', 'null', ['number', 'number', 'number']), toggleMainLoop: this.Module.cwrap('toggleMainLoop', 'null', ['number']), getCoreOptions: this.Module.cwrap('get_core_options', 'string', []), setVariable: this.Module.cwrap('ejs_set_variable', 'null', ['string', 'string']), setCheat: this.Module.cwrap('set_cheat', 'null', ['number', 'number', 'string']), resetCheat: this.Module.cwrap('reset_cheat', 'null', []), toggleShader: this.Module.cwrap('shader_enable', 'null', ['number']), getDiskCount: this.Module.cwrap('get_disk_count', 'number', []), getCurrentDisk: this.Module.cwrap('get_current_disk', 'number', []), setCurrentDisk: this.Module.cwrap('set_current_disk', 'null', ['number']), getSaveFilePath: this.Module.cwrap('save_file_path', 'string', []), saveSaveFiles: this.Module.cwrap('cmd_savefiles', '', []), supportsStates: this.Module.cwrap('supports_states', 'number', []), loadSaveFiles: this.Module.cwrap('refresh_save_files', 'null', []), toggleFastForward: this.Module.cwrap('toggle_fastforward', 'null', ['number']), setFastForwardRatio: this.Module.cwrap('set_ff_ratio', 'null', ['number']), toggleRewind: this.Module.cwrap('toggle_rewind', 'null', ['number']), setRewindGranularity: this.Module.cwrap('set_rewind_granularity', 'null', ['number']), toggleSlowMotion: this.Module.cwrap('toggle_slow_motion', 'null', ['number']), setSlowMotionRatio: this.Module.cwrap('set_sm_ratio', 'null', ['number']), getFrameNum: this.Module.cwrap('get_current_frame_count', 'number', ['']) } this.writeFile("/home/web_user/retroarch/userdata/config/Beetle PSX HW/Beetle PSX HW.opt", 'beetle_psx_hw_renderer = "software"\n'); this.writeFile("/home/web_user/retroarch/userdata/config/MAME 2003 (0.78)/MAME 2003 (0.78).opt", 'mame2003_skip_disclaimer = "enabled"\nmame2003_skip_warnings = "enabled"\n'); this.mkdir("/data"); this.mkdir("/data/saves"); this.writeFile("/home/web_user/retroarch/userdata/retroarch.cfg", this.getRetroArchCfg()); this.FS.mount(IDBFS, {}, '/data/saves'); this.FS.syncfs(true, () => {}); this.initShaders(); this.EJS.addEventListener(window, "beforeunload", () => { this.saveSaveFiles(); this.FS.syncfs(() => {}); }) } loadExternalFiles() { return new Promise(async (resolve, reject) => { if (this.EJS.config.externalFiles && this.EJS.config.externalFiles.constructor.name === 'Object') { for (const key in this.EJS.config.externalFiles) { await new Promise(done => { this.EJS.downloadFile(this.EJS.config.externalFiles[key], async (res) => { if (res === -1) { if (this.EJS.debug) console.warn("Failed to fetch file from '" + this.EJS.config.externalFiles[key] + "'. Make sure the file exists."); return done(); } let path = key; if (key.trim().endsWith("/")) { const invalidCharacters = /[#<$+%>!`&*'|{}/\\?"=@:^\r\n]/ig; let name = this.EJS.config.externalFiles[key].split("/").pop().split("#")[0].split("?")[0].replace(invalidCharacters, "").trim(); if (!name) return done(); const files = await this.EJS.checkCompression(new Uint8Array(res.data), this.EJS.localization("Decompress Game Assets")); if (files["!!notCompressedData"]) { path += name; } else { for (const k in files) { this.writeFile(path+k, files[k]); } return done(); } } try { this.writeFile(path, res.data); } catch(e) { if (this.EJS.debug) console.warn("Failed to write file to '" + path + "'. Make sure there are no conflicting files."); } done(); }, null, true, {responseType: "arraybuffer", method: "GET"}); }) } } resolve(); }); } writeFile(path, data) { const parts = path.split("/"); let current = "/"; for (let i=0; i { const stateInfo = (await this.getStateInfo()).split('|') let state; let size = stateInfo[0] >> 0; if (size > 0) { state = new Uint8Array(size); let start = stateInfo[1] >> 0; for (let i=0; i { let a; let b = setInterval(() => { a = this.functions.getStateInfo(); if (a) { clearInterval(b); resolve(a); } }, 50) }); } loadState(state) { try { this.FS.unlink('game.state'); } catch(e){} this.FS.writeFile('/game.state', state); this.clearEJSResetTimer(); this.functions.loadState("game.state", 0); setTimeout(() => { try { this.FS.unlink('game.state'); } catch(e){} }, 5000) } screenshot() { this.functions.screenshot(); return this.FS.readFile('screenshot.png'); } quickSave(slot) { if (!slot) slot = 1; (async () => { let name = slot + '-quick.state'; try { this.FS.unlink(name); } catch (e) {} let data = await this.getState(); this.FS.writeFile('/'+name, data); })(); } quickLoad(slot) { if (!slot) slot = 1; (async () => { let name = slot + '-quick.state'; this.clearEJSResetTimer(); this.functions.loadState(name, 0); })(); } simulateInput(player, index, value) { if (this.EJS.isNetplay) { this.EJS.netplay.simulateInput(player, index, value); return; } if ([24, 25, 26, 27, 28, 29].includes(index)) { if (index === 24 && value === 1) { const slot = this.EJS.settings['save-state-slot'] ? this.EJS.settings['save-state-slot'] : "1"; this.quickSave(slot); this.EJS.displayMessage(this.EJS.localization("SAVED STATE TO SLOT")+" "+slot); } if (index === 25 && value === 1) { const slot = this.EJS.settings['save-state-slot'] ? this.EJS.settings['save-state-slot'] : "1"; this.quickLoad(slot); this.EJS.displayMessage(this.EJS.localization("LOADED STATE FROM SLOT")+" "+slot); } if (index === 26 && value === 1) { let newSlot; try { newSlot = parseFloat(this.EJS.settings['save-state-slot'] ? this.EJS.settings['save-state-slot'] : "1") + 1; } catch(e) { newSlot = 1; } if (newSlot > 9) newSlot = 1; this.EJS.displayMessage(this.EJS.localization("SET SAVE STATE SLOT TO")+" "+newSlot); this.EJS.changeSettingOption('save-state-slot', newSlot.toString()); } if (index === 27) { this.functions.toggleFastForward(this.EJS.isFastForward ? !value : value); } if (index === 29) { this.functions.toggleSlowMotion(this.EJS.isSlowMotion ? !value : value); } if (index === 28) { if (this.EJS.rewindEnabled) { this.functions.toggleRewind(value); } } return; } this.functions.simulateInput(player, index, value); } getFileNames() { if (this.EJS.getCore() === "picodrive") { return ["bin", "gen", "smd", "md", "32x", "cue", "iso", "sms", "68k", "chd"]; } else { return ["toc", "ccd", "exe", "pbp", "chd", "img", "bin", "iso"]; } } createCueFile(fileNames) { try { if (fileNames.length > 1) { fileNames = fileNames.filter((item) => { return this.getFileNames().includes(item.split(".").pop().toLowerCase()); }) fileNames = fileNames.sort((a, b) => { if (isNaN(a.charAt()) || isNaN(b.charAt())) throw new Error("Incorrect file name format"); return (parseInt(a.charAt()) > parseInt(b.charAt())) ? 1 : -1; }) } } catch(e) { if (fileNames.length > 1) { console.warn("Could not auto-create cue file(s)."); return null; } } for (let i=0; i 1) { let contents = ""; for (let i=0; i { this.EJS.downloadFile('cores/ppsspp-assets.zip', (res) => { this.EJS.checkCompression(new Uint8Array(res.data), this.EJS.localization("Decompress Game Data")).then((pspassets) => { if (pspassets === -1) { this.EJS.textElem.innerText = this.localization('Network Error'); this.EJS.textElem.style.color = "red"; return; } this.mkdir("/PPSSPP"); for (const file in pspassets) { const data = pspassets[file]; const path = "/PPSSPP/"+file; const paths = path.split("/"); let cp = ""; for (let i=0; i {}); } supportsStates() { return !!this.functions.supportsStates(); } getSaveFile() { this.saveSaveFiles(); const exists = FS.analyzePath(this.getSaveFilePath()).exists; return (exists ? FS.readFile(this.getSaveFilePath()) : null); } loadSaveFiles() { this.clearEJSResetTimer(); this.functions.loadSaveFiles(); } setFastForwardRatio(ratio) { this.functions.setFastForwardRatio(ratio); } toggleFastForward(active) { this.functions.toggleFastForward(active); } setSlowMotionRatio(ratio) { this.functions.setSlowMotionRatio(ratio); } toggleSlowMotion(active) { this.functions.toggleSlowMotion(active); } setRewindGranularity(value) { this.functions.setRewindGranularity(value); } getFrameNum() { return this.functions.getFrameNum(); } } window.EJS_GameManager = EJS_GameManager;