Cleaned up the Emulator JS
This commit is contained in:
19
public/emulator.css
Normal file
19
public/emulator.css
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
body,html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ejs_menu_bar {
|
||||||
|
display: none !important;
|
||||||
|
}
|
@ -2,184 +2,14 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>Test</title>
|
<title>Emulator Window</title>
|
||||||
|
<link href="/emulator.css" rel="stylesheet" type="text/css" />
|
||||||
<style type="text/css">
|
|
||||||
body,html {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.game {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ejs_menu_bar {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js" integrity="sha512-a+SUDuwNzXDvz4XrIcXHuCf089/iJAoN4lmrXJg18XnduKK6YlDHNRalv4yd1N40OKI80tFidF+rqTFKGPoWFQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js" integrity="sha512-a+SUDuwNzXDvz4XrIcXHuCf089/iJAoN4lmrXJg18XnduKK6YlDHNRalv4yd1N40OKI80tFidF+rqTFKGPoWFQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="game" id="game">
|
<div class="game" id="game">
|
||||||
</div>
|
</div>
|
||||||
|
<script src="/emulator.js" type="text/javascript"></script>
|
||||||
<script type="text/javascript" language="javascript">
|
|
||||||
let scriptElement = null;
|
|
||||||
|
|
||||||
let initInterval = setInterval(() => {
|
|
||||||
send({ message: 'iframe_loaded' });
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
EJS_player = "#game";
|
|
||||||
EJS_pathtodata = "https://cdn.emulatorjs.org/stable/data/";
|
|
||||||
EJS_language = "en-US";
|
|
||||||
EJS_volume = 0;
|
|
||||||
EJS_startOnLoaded = true;
|
|
||||||
EJS_backgroundColor = '#000';
|
|
||||||
|
|
||||||
EJS_Buttons = {
|
|
||||||
playPause: false,
|
|
||||||
restart: false,
|
|
||||||
mute: false,
|
|
||||||
settings: false,
|
|
||||||
fullscreen: false,
|
|
||||||
saveState: false,
|
|
||||||
loadState: false,
|
|
||||||
screenRecord: false,
|
|
||||||
gamepad: false,
|
|
||||||
cheat: false,
|
|
||||||
volume: false,
|
|
||||||
saveSavFiles: false,
|
|
||||||
loadSavFiles: false,
|
|
||||||
quickSave: false,
|
|
||||||
quickLoad: false,
|
|
||||||
screenshot: false,
|
|
||||||
cacheManager: false,
|
|
||||||
exitEmulation: false
|
|
||||||
}
|
|
||||||
|
|
||||||
EJS_defaultOptions = {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const emulatorInit = data => {
|
|
||||||
EJS_gameUrl = `/api/v1/rom?id=${data.gameId}`;
|
|
||||||
EJS_core = data.core;
|
|
||||||
EJS_gameName = data.gameName;
|
|
||||||
EJS_externalFiles = {
|
|
||||||
'/homeplay/saves/': `/api/v1/save?id=${data.gameId}`
|
|
||||||
}
|
|
||||||
|
|
||||||
scriptElement = document.createElement('script');
|
|
||||||
scriptElement.src = 'https://cdn.emulatorjs.org/stable/data/loader.js';
|
|
||||||
document.body.appendChild(scriptElement);
|
|
||||||
|
|
||||||
clearInterval(initInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
const md5File = async path => {
|
|
||||||
const buffer = EJS_emulator.gameManager.FS.readFile(path);
|
|
||||||
const hash = await crypto.subtle.digest('SHA-256', buffer);
|
|
||||||
const hashArray = Array.from(new Uint8Array(hash));
|
|
||||||
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
||||||
return hashHex;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emulatorSave = async () => {
|
|
||||||
// First, we get the currently saved MD5 hash of all files in the save
|
|
||||||
// directory
|
|
||||||
const contents = EJS_emulator.gameManager.FS.readdir('/homeplay/saves').filter(f => !f.startsWith('.'));
|
|
||||||
const hashes = await Promise.all(contents.map(f => md5File(`/homeplay/saves/${f}`)));
|
|
||||||
EJS_emulator.gameManager.saveSaveFiles();
|
|
||||||
const newHashes = await Promise.all(contents.map(f => md5File(`/homeplay/saves/${f}`)));
|
|
||||||
const changedFiles = contents.filter((f, i) => hashes[i] !== newHashes[i]);
|
|
||||||
console.log('Changed files', changedFiles);
|
|
||||||
|
|
||||||
send({
|
|
||||||
message: ''
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const send = data => {
|
|
||||||
window.parent.postMessage(data, '*');
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onmessage = (e) => {
|
|
||||||
const { data } = e;
|
|
||||||
|
|
||||||
if(!data) {
|
|
||||||
console.error('No data');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { message } = data;
|
|
||||||
switch(message) {
|
|
||||||
case 'init':
|
|
||||||
emulatorInit(data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'volume':
|
|
||||||
EJS_emulator.setVolume(data.volume);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'save':
|
|
||||||
emulatorSave();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
console.error('Unknown message', message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
EJS_ready = () => {
|
|
||||||
// Modify the EJS stuff
|
|
||||||
const oldGetRetroArchCfg = window.EJS_GameManager.prototype.getRetroArchCfg;
|
|
||||||
const oldMountFileSystems = window.EJS_GameManager.prototype.mountFileSystems;
|
|
||||||
|
|
||||||
EJS_GameManager.prototype.getRetroArchCfg = function() {
|
|
||||||
const ejsConfig = oldGetRetroArchCfg.call(this);
|
|
||||||
|
|
||||||
// Parse back into key value pair
|
|
||||||
const raConfig = ejsConfig.split('\n').reduce((acc, line) => {
|
|
||||||
if(!line.length) return acc;
|
|
||||||
const [key, value] = line.split('=');
|
|
||||||
acc[key.trim()] = value.trim();
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
// Add new options
|
|
||||||
raConfig['savefiles_in_content_dir'] = false;
|
|
||||||
raConfig['sort_savefiles_by_content_enable'] = false;
|
|
||||||
raConfig['sort_savefiles_enable'] = false;
|
|
||||||
raConfig['savefile_directory'] = '/homeplay/saves';
|
|
||||||
|
|
||||||
// Return back as RA config string
|
|
||||||
return Object.entries(raConfig).map(([k, v]) => `${k} = ${v}`).join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
send({ message: 'ready' });
|
|
||||||
}
|
|
||||||
|
|
||||||
EJS_onSaveState = () => {
|
|
||||||
send({ message: 'save_state' });
|
|
||||||
}
|
|
||||||
|
|
||||||
EJS_onLoadState = () => {
|
|
||||||
send({ message: 'load_state' });
|
|
||||||
}
|
|
||||||
|
|
||||||
EJS_onGameStart = () => {
|
|
||||||
send({ message: 'start' });
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
175
public/emulator.js
Normal file
175
public/emulator.js
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
// Init EJS stuff
|
||||||
|
class HomeplayEmulator {
|
||||||
|
scriptElement = null;
|
||||||
|
initInterval = null;
|
||||||
|
oldGetRetroArchCfg = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// Set up the EJS stuff
|
||||||
|
window.EJS_player = "#game";
|
||||||
|
window.EJS_pathtodata = "https://cdn.emulatorjs.org/stable/data/";
|
||||||
|
window.EJS_language = "en-US";
|
||||||
|
window.EJS_volume = 0;
|
||||||
|
window.EJS_startOnLoaded = true;
|
||||||
|
window.EJS_backgroundColor = '#000';
|
||||||
|
|
||||||
|
window.EJS_Buttons = {
|
||||||
|
playPause: false,
|
||||||
|
restart: false,
|
||||||
|
mute: false,
|
||||||
|
settings: false,
|
||||||
|
fullscreen: false,
|
||||||
|
saveState: false,
|
||||||
|
loadState: false,
|
||||||
|
screenRecord: false,
|
||||||
|
gamepad: false,
|
||||||
|
cheat: false,
|
||||||
|
volume: false,
|
||||||
|
saveSavFiles: false,
|
||||||
|
loadSavFiles: false,
|
||||||
|
quickSave: false,
|
||||||
|
quickLoad: false,
|
||||||
|
screenshot: false,
|
||||||
|
cacheManager: false,
|
||||||
|
exitEmulation: false
|
||||||
|
}
|
||||||
|
|
||||||
|
window.EJS_defaultOptions = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bind events
|
||||||
|
window.EJS_ready = this.onEJSReady;
|
||||||
|
window.EJS_onGameStart = this.onEJSGameStart;
|
||||||
|
window.EJS_onGameEnd = this.onEJSGameEnd;
|
||||||
|
window.EJS_onSaveState = this.onEJSSaveState;
|
||||||
|
window.EJS_onLoadState = this.onEJSLoadState;
|
||||||
|
window.onmessage = this.onMessage;
|
||||||
|
|
||||||
|
// Begin the init interval
|
||||||
|
this.initInterval = setInterval(() => {
|
||||||
|
this.send({ message: 'iframe_loaded' });
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
send = data => {
|
||||||
|
window.parent.postMessage(data, '*');
|
||||||
|
}
|
||||||
|
|
||||||
|
init = data => {
|
||||||
|
window.EJS_gameUrl = `/api/v1/rom?id=${data.gameId}`;
|
||||||
|
window.EJS_core = data.core;
|
||||||
|
window.EJS_gameName = data.gameName;
|
||||||
|
window.EJS_externalFiles = {
|
||||||
|
'/homeplay/saves/': `/api/v1/save?id=${data.gameId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scriptElement = document.createElement('script');
|
||||||
|
this.scriptElement.src = 'https://cdn.emulatorjs.org/stable/data/loader.js';
|
||||||
|
document.body.appendChild(this.scriptElement);
|
||||||
|
|
||||||
|
clearInterval(this.initInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEJSReady = () => {
|
||||||
|
// Modify the EJS stuff
|
||||||
|
this.oldGetRetroArchCfg = window.EJS_GameManager.prototype.getRetroArchCfg;
|
||||||
|
const raConfig = (...a) => this.getRetroArchCfg(...a);
|
||||||
|
window.EJS_GameManager.prototype.getRetroArchCfg = function() {
|
||||||
|
return raConfig(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.send({ message: 'ready' });
|
||||||
|
|
||||||
|
console.log('EJS Ready');
|
||||||
|
}
|
||||||
|
|
||||||
|
onEJSGameStart = () => {
|
||||||
|
console.log('EJS Game Start');
|
||||||
|
}
|
||||||
|
|
||||||
|
onEJSGameEnd = () => {
|
||||||
|
console.log('EJS Game End');
|
||||||
|
}
|
||||||
|
|
||||||
|
onEJSSaveState = () => {
|
||||||
|
console.log('EJS Save State');
|
||||||
|
}
|
||||||
|
|
||||||
|
onEJSLoadState = () => {
|
||||||
|
console.log('EJS Load State');;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessage = e => {
|
||||||
|
const { data } = e;
|
||||||
|
|
||||||
|
if(!data) {
|
||||||
|
console.error('No data');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { message } = data;
|
||||||
|
switch(message) {
|
||||||
|
case 'init':
|
||||||
|
this.init(data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'volume':
|
||||||
|
window.EJS_volume = data.volume;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'save':
|
||||||
|
window.EJS_emulator.saveSaveFiles();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.error('Unknown message', message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
md5File = async path => {
|
||||||
|
const buffer = window.EJS_emulator.gameManager.FS.readFile(path);
|
||||||
|
const hash = await crypto.subtle.digest('SHA-256', buffer);
|
||||||
|
const hashArray = Array.from(new Uint8Array(hash));
|
||||||
|
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
||||||
|
return hashHex;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRetroArchCfg = (gameManager) => {
|
||||||
|
const ejsConfig = this.oldGetRetroArchCfg.call(gameManager);
|
||||||
|
|
||||||
|
// Parse back into key value pair
|
||||||
|
const raConfig = ejsConfig.split('\n').reduce((acc, line) => {
|
||||||
|
if(!line.length) return acc;
|
||||||
|
const [key, value] = line.split('=');
|
||||||
|
acc[key.trim()] = value.trim();
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// Add new options
|
||||||
|
raConfig['savefiles_in_content_dir'] = false;
|
||||||
|
raConfig['sort_savefiles_by_content_enable'] = false;
|
||||||
|
raConfig['sort_savefiles_enable'] = false;
|
||||||
|
raConfig['savefile_directory'] = '/homeplay/saves';
|
||||||
|
|
||||||
|
// Return back as RA config string
|
||||||
|
return Object.entries(raConfig).map(([k, v]) => `${k} = ${v}`).join('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// const emulatorSave = async () => {
|
||||||
|
// // First, we get the currently saved MD5 hash of all files in the save
|
||||||
|
// // directory
|
||||||
|
// const contents = EJS_emulator.gameManager.FS.readdir('/homeplay/saves').filter(f => !f.startsWith('.'));
|
||||||
|
// const hashes = await Promise.all(contents.map(f => md5File(`/homeplay/saves/${f}`)));
|
||||||
|
// EJS_emulator.gameManager.saveSaveFiles();
|
||||||
|
// const newHashes = await Promise.all(contents.map(f => md5File(`/homeplay/saves/${f}`)));
|
||||||
|
// const changedFiles = contents.filter((f, i) => hashes[i] !== newHashes[i]);
|
||||||
|
// console.log('Changed files', changedFiles);
|
||||||
|
|
||||||
|
// send({
|
||||||
|
// message: ''
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
window.homeplay = new HomeplayEmulator();
|
@ -1,4 +1,3 @@
|
|||||||
import { LanguageProvider } from '@/providers/LanguageProvider';
|
|
||||||
import { Html, Head, Main, NextScript, DocumentProps } from 'next/document';
|
import { Html, Head, Main, NextScript, DocumentProps } from 'next/document';
|
||||||
|
|
||||||
const RootDocument:React.FC<DocumentProps> = props => {
|
const RootDocument:React.FC<DocumentProps> = props => {
|
||||||
|
Reference in New Issue
Block a user