Files
gaseous-server/gaseous-server/wwwroot/pages/dialogs/emulatorloadstate.html
Michael Green cebab38dd6 Provide ability to upload save states (#371)
This change provides the ability to upload save states.

When a state is downloaded, the state (savestate.state), screenshot
(screenshot.jpg), and a json file (rominfo.json) describing the save
state are zipped and downloaded.

The json file description is defined as follows:
```json
{
  "Name": "Super Mario Bros. (1985-09-13)(Nintendo)(JP-US).zip",
  "StateDateTime": "2024-06-24T05:34:38",
  "StateName": "",
  "MD5": "7d158dcd242e77ba249ac8342474aa77",
  "SHA1": "3d4b04dc78f9d998f17d9fe9ad982a83b5ed72df",
  "Type": "ROM"
}
```

| Attribute | Value |
| -------- | ------|
| Name | The name of the ROM that the state belongs to. This is merely a
convenience attribute. |
| StateDateTime | The date and time (in UTC) when the state was
initially saved. |
| StateName | The name of the state |
| MD5 | The MD5 hash of the ROM that the state belongs to. |
| SHA1 | The SHA1 hash of the ROM that the state belongs to. |
| Type | Whether the state belongs to a ROM or ROM Group |

If the zip is re-uploaded, the above json file will be used to
automatically match the saved state to the ROM that created it.

If a zip is uploaded without the above three files, the upload will
fail.

If a file is uploaded that is not a zip, it will be stored against the
currently running ROM and a warning will be displayed that Gaseous was
unable to verify that the state belongs to the ROM, and may not function
as expected.

Closes #336
2024-06-24 22:38:54 +10:00

195 lines
9.5 KiB
HTML

<div id="saved_states">
</div>
<div>
<span>Upload state file: </span><input type="file" id="stateFile" />
</div>
<script text="text/javascript">
document.getElementById('modal-heading').innerHTML = "Load saved state";
console.log(modalVariables);
var statesUrl = '/api/v1.1/StateManager/' + modalVariables.romId + '?IsMediaGroup=' + modalVariables.IsMediaGroup;
console.log(statesUrl);
function LoadStates() {
ajaxCall(
statesUrl,
'GET',
function (result) {
var statesBox = document.getElementById('saved_states');
statesBox.innerHTML = '';
document.getElementById('stateFile').value = '';
for (var i = 0; i < result.length; i++) {
var stateBox = document.createElement('div');
stateBox.id = 'stateBox_' + result[i].id;
stateBox.className = 'saved_state_box';
// screenshot panel
var stateImageBox = document.createElement('div');
stateImageBox.id = 'stateImageBox_' + result[i].id;
stateImageBox.className = 'saved_state_image_box';
if (result[i].hasScreenshot == true) {
var stateImage = document.createElement('img');
stateImage.className = 'saved_state_image_image';
stateImage.src = '/api/v1.1/StateManager/' + modalVariables.romId + '/' + result[i].id + '/Screenshot/image.png?IsMediaGroup=' + modalVariables.IsMediaGroup;
stateImageBox.appendChild(stateImage);
}
stateBox.appendChild(stateImageBox);
// main panel
var stateMainPanel = document.createElement('div');
stateMainPanel.id = 'stateMainPanel_' + result[i].id;
stateMainPanel.className = 'saved_state_main_box';
var stateName = document.createElement('input');
stateName.id = 'stateName_' + result[i].id;
stateName.type = 'text';
stateName.className = 'saved_state_name';
stateName.setAttribute('onblur', 'UpdateStateSave(' + result[i].id + ', ' + modalVariables.IsMediaGroup + ');');
if (result[i].name) {
stateName.value = result[i].name;
} else {
stateName.setAttribute('placeholder', "Untitled");
}
stateMainPanel.appendChild(stateName);
var stateTime = document.createElement('div');
stateTime.id = 'stateTime_' + result[i].id;
stateTime.className = 'saved_state_date';
stateTime.innerHTML = moment(result[i].saveTime).format("YYYY-MM-DD h:mm:ss a");
stateMainPanel.appendChild(stateTime);
var stateControls = document.createElement('div');
stateControls.id = 'stateControls_' + result[i].id;
stateControls.className = 'saved_state_controls';
var stateControlsLaunch = document.createElement('span');
stateControlsLaunch.id = 'stateControlsLaunch_' + result[i].id;
stateControlsLaunch.className = 'romstart';
var emulatorTarget = '/index.html?page=emulator&engine=@engine&core=@core&platformid=@platformid&gameid=@gameid&romid=@romid&mediagroup=@mediagroup&rompath=@rompath&stateid=' + result[i].id;
switch (getQueryString('page', 'string')) {
case 'emulator':
var mediagroupint = 0;
if (modalVariables.IsMediaGroup == true) {
mediagroupint = 1;
}
emulatorTarget = emulatorTarget.replaceAll('@engine', getQueryString('engine', 'string'));
emulatorTarget = emulatorTarget.replaceAll('@core', getQueryString('core', 'string'));
emulatorTarget = emulatorTarget.replaceAll('@platformid', getQueryString('platformid', 'string'));
emulatorTarget = emulatorTarget.replaceAll('@gameid', getQueryString('gameid', 'string'));
emulatorTarget = emulatorTarget.replaceAll('@romid', getQueryString('romid', 'string'));
emulatorTarget = emulatorTarget.replaceAll('@mediagroup', mediagroupint);
emulatorTarget = emulatorTarget.replaceAll('@rompath', getQueryString('rompath', 'string'));
stateControlsLaunch.setAttribute("onclick", 'window.location.replace("' + emulatorTarget + '")');
break;
case 'game':
console.log(modalVariables);
emulatorTarget = emulatorTarget.replaceAll('@engine', modalVariables.engine);
emulatorTarget = emulatorTarget.replaceAll('@core', modalVariables.core);
emulatorTarget = emulatorTarget.replaceAll('@platformid', modalVariables.platformid);
emulatorTarget = emulatorTarget.replaceAll('@gameid', modalVariables.gameid);
emulatorTarget = emulatorTarget.replaceAll('@romid', modalVariables.romId);
emulatorTarget = emulatorTarget.replaceAll('@mediagroup', modalVariables.mediagroup);
emulatorTarget = emulatorTarget.replaceAll('@rompath', modalVariables.rompath);
stateControlsLaunch.setAttribute("onclick", 'window.location.href = "' + emulatorTarget + '"');
break;
}
stateControlsLaunch.innerHTML = 'Launch';
stateControlsLaunch.style.float = 'right';
stateControls.appendChild(stateControlsLaunch);
var stateControlsDownload = document.createElement('a');
stateControlsDownload.id = 'stateControlsDownload_' + result[i].id;
stateControlsDownload.className = 'saved_state_buttonlink';
stateControlsDownload.href = '/api/v1.1/StateManager/' + modalVariables.romId + '/' + result[i].id + '/State/savestate.state?IsMediaGroup=' + modalVariables.IsMediaGroup;
stateControlsDownload.innerHTML = '<img src="/images/download.svg" class="banner_button_image" alt="Download" title="Download" />';
stateControls.appendChild(stateControlsDownload);
var stateControlsDelete = document.createElement('span');
stateControlsDelete.id = 'stateControlsDelete_' + result[i].id;
stateControlsDelete.className = 'saved_state_buttonlink';
stateControlsDelete.setAttribute('onclick', 'DeleteStateSave(' + result[i].id + ', ' + modalVariables.IsMediaGroup + ');');
stateControlsDelete.innerHTML = '<img src="/images/delete.svg" class="banner_button_image" alt="Delete" title="Delete" />';
stateControls.appendChild(stateControlsDelete);
stateMainPanel.appendChild(stateControls);
stateBox.appendChild(stateMainPanel);
statesBox.appendChild(stateBox);
}
}
);
}
LoadStates();
function DeleteStateSave(StateId, IsMediaGroup) {
ajaxCall(
'/api/v1.1/StateManager/' + modalVariables.romId + '/' + StateId + '?IsMediaGroup=' + IsMediaGroup,
'DELETE',
function (success) {
LoadStates();
},
function (error) {
LoadStates();
}
);
}
function UpdateStateSave(StateId, IsMediaGroup) {
var stateName = document.getElementById('stateName_' + StateId);
var model = {
"name": stateName.value
};
ajaxCall(
'/api/v1.1/StateManager/' + modalVariables.romId + '/' + StateId + '?IsMediaGroup=' + IsMediaGroup,
'PUT',
function (success) {
LoadStates();
},
function (error) {
LoadStates();
},
JSON.stringify(model)
);
}
document.getElementById('stateFile').addEventListener('change', function () {
let file = document.getElementById('stateFile').files[0];
let formData = new FormData();
formData.append('file', file);
console.log("Uploading state file");
fetch('/api/v1.1/StateManager/Upload?RomId=' + modalVariables.romId + '&IsMediaGroup=' + modalVariables.IsMediaGroup, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
UploadAlert(data);
LoadStates();
})
.catch(error => {
console.error("Error:", error);
UploadAlert(error);
LoadStates();
});
});
function UploadAlert(data) {
if (data.Management == "Managed") {
alert("State uploaded successfully.");
} else {
alert("State uploaded successfully, but it might not function correctly for this platform and ROM.");
}
}
</script>