Roughly got save files working.
This commit is contained in:
@ -5,6 +5,7 @@
|
||||
<title>Emulator Window</title>
|
||||
<link href="/emulator.css" rel="stylesheet" type="text/css" />
|
||||
<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="/jszip.min.js" type="text/javascript"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -3,6 +3,11 @@ class HomeplayEmulator {
|
||||
scriptElement = null;
|
||||
initInterval = null;
|
||||
oldGetRetroArchCfg = null;
|
||||
gameStarted = false;
|
||||
|
||||
saveUpdates = false;
|
||||
saveInterval = null;
|
||||
saveLock = false;
|
||||
|
||||
constructor() {
|
||||
// Set up the EJS stuff
|
||||
@ -50,6 +55,23 @@ class HomeplayEmulator {
|
||||
this.initInterval = setInterval(() => {
|
||||
this.send({ message: 'iframe_loaded' });
|
||||
}, 1000);
|
||||
|
||||
// Check for save updates
|
||||
this.saveInterval = setInterval(async () => {
|
||||
if(this.saveLock) return;
|
||||
this.saveLock = true;
|
||||
try {
|
||||
this.saveUpdates = await this.hasSaveUpdates();
|
||||
if(this.saveUpdates) {
|
||||
console.log('Saving');
|
||||
await this.save();
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('Error checking for save updates', e);
|
||||
} finally {
|
||||
this.saveLock = false;
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
send = data => {
|
||||
@ -86,6 +108,11 @@ class HomeplayEmulator {
|
||||
|
||||
onEJSGameStart = () => {
|
||||
console.log('EJS Game Start');
|
||||
|
||||
setTimeout(() => {
|
||||
window.EJS_emulator.gameManager.saveSaveFiles();
|
||||
this.gameStarted = true;
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
onEJSGameEnd = () => {
|
||||
@ -127,7 +154,7 @@ class HomeplayEmulator {
|
||||
}
|
||||
}
|
||||
|
||||
md5File = async path => {
|
||||
getFileMD5 = 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));
|
||||
@ -155,21 +182,47 @@ class HomeplayEmulator {
|
||||
// Return back as RA config string
|
||||
return Object.entries(raConfig).map(([k, v]) => `${k} = ${v}`).join('\n');
|
||||
}
|
||||
|
||||
getSaveFiles = () => {
|
||||
return [ 'rom.srm' ];
|
||||
}
|
||||
|
||||
// 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);
|
||||
hasSaveUpdates = async () => {
|
||||
if(!window.EJS_emulator) return false;
|
||||
if(!window.EJS_emulator.gameManager) return false;
|
||||
if(!this.gameStarted) return false;
|
||||
|
||||
// send({
|
||||
// message: ''
|
||||
// })
|
||||
// }
|
||||
const saveFiles = this.getSaveFiles();
|
||||
const hashes = await Promise.all(saveFiles.map(f => this.getFileMD5(`/homeplay/saves/${f}`)));
|
||||
|
||||
window.EJS_emulator.gameManager.saveSaveFiles();
|
||||
|
||||
const newHashes = await Promise.all(saveFiles.map(f => this.getFileMD5(`/homeplay/saves/${f}`)));
|
||||
const changedFiles = saveFiles.filter((f, i) => hashes[i] !== newHashes[i]);
|
||||
|
||||
return !!changedFiles.length;
|
||||
}
|
||||
|
||||
save = async () => {
|
||||
// Check if updates
|
||||
if(!this.hasSaveUpdates) return;
|
||||
|
||||
// Get save file contents
|
||||
const saveFiles = this.getSaveFiles();
|
||||
|
||||
// Now we need to create a zip archive. EJS provides a nice ZIP library.
|
||||
const zip = new JSZip();
|
||||
|
||||
await Promise.all(saveFiles.map(async f => {
|
||||
const data = window.EJS_emulator.gameManager.FS.readFile(`/homeplay/saves/${f}`);
|
||||
zip.file(f, data);
|
||||
}));
|
||||
|
||||
this.send({
|
||||
message: 'save',
|
||||
data: await zip.generateAsync({ type:"base64" })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
window.homeplay = new HomeplayEmulator();
|
13
public/jszip.min.js
vendored
Normal file
13
public/jszip.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -27,7 +27,8 @@ type ReceiveEmulatorMessage = (
|
||||
{ message: 'start' } |
|
||||
{ message: 'ready' } |
|
||||
{ message: 'save_state' } |
|
||||
{ message: 'load_state' }
|
||||
{ message: 'load_state' } |
|
||||
{ message: 'save', data:string }
|
||||
);
|
||||
|
||||
export type EmulatorProps = {
|
||||
@ -68,6 +69,14 @@ export const Emulator:React.FC<EmulatorProps> = props => {
|
||||
});
|
||||
break;
|
||||
|
||||
case 'save':
|
||||
// Download save data
|
||||
const a = document.createElement('a');
|
||||
a.href = `data:application/zip;base64,${msg.data}`;
|
||||
a.download = `${props.gameId}.zip`;
|
||||
a.click();
|
||||
break;
|
||||
|
||||
case 'start':
|
||||
case 'ready':
|
||||
case 'load_state':
|
||||
|
Reference in New Issue
Block a user