mirror of
https://github.com/neon443/neon443.github.io.git
synced 2026-03-11 15:16:18 +00:00
1619 lines
56 KiB
JavaScript
Executable File
1619 lines
56 KiB
JavaScript
Executable File
var AUDIOBUFFSIZE = 1024;
|
|
|
|
class MyClass {
|
|
constructor() {
|
|
this.rom_name = '';
|
|
this.mobileMode = false;
|
|
this.iosMode = false;
|
|
this.iosVersion = 0;
|
|
this.audioInited = false;
|
|
this.allSaveStates = [];
|
|
this.loginModalOpened = false;
|
|
this.loadSavestateAfterBoot = false;
|
|
this.canvasSize = 640;
|
|
this.eepData = null;
|
|
this.sraData = null;
|
|
this.flaData = null;
|
|
this.dblist = [];
|
|
var Module = {};
|
|
Module['canvas'] = document.getElementById('canvas');
|
|
window['Module'] = Module;
|
|
document.getElementById('file-upload').addEventListener('change', this.uploadRom.bind(this));
|
|
document.getElementById('file-upload-eep').addEventListener('change', this.uploadEep.bind(this));
|
|
document.getElementById('file-upload-sra').addEventListener('change', this.uploadSra.bind(this));
|
|
document.getElementById('file-upload-fla').addEventListener('change', this.uploadFla.bind(this));
|
|
|
|
|
|
this.rivetsData = {
|
|
message: '',
|
|
beforeEmulatorStarted: true,
|
|
moduleInitializing: true,
|
|
showLogin: false,
|
|
currentFPS: 0,
|
|
audioSkipCount: 0,
|
|
n64SaveStates: [],
|
|
loggedIn: false,
|
|
noCloudSave: true,
|
|
password: '',
|
|
inputController: null,
|
|
remappings: null,
|
|
remapMode: '',
|
|
currKey: 0,
|
|
currJoy: 0,
|
|
chkUseJoypad: false,
|
|
remappingPlayer1: false,
|
|
hasRoms: false,
|
|
romList: [],
|
|
inputLoopStarted: false,
|
|
noLocalSave: true,
|
|
lblError: '',
|
|
chkAdvanced: false,
|
|
doubleSpeed: false,
|
|
showDoubleSpeed: false,
|
|
eepName: '',
|
|
sraName: '',
|
|
flaName: '',
|
|
swapSticks: false,
|
|
mouseMode: false,
|
|
useZasCMobile: false, //used for starcraft mobile
|
|
showFPS: true,
|
|
invert2P: false,
|
|
invert3P: false,
|
|
invert4P: false,
|
|
disableAudioSync: true,
|
|
hadNipple: false,
|
|
hadFullscreen: false,
|
|
forceAngry: false,
|
|
remapPlayer1: true,
|
|
remapOptions: false,
|
|
remapGameshark: false,
|
|
settingMobile: 'Auto',
|
|
iosShowWarning: false,
|
|
cheatName: '',
|
|
cheatAddress: '',
|
|
cheatValue: '',
|
|
cheats: [],
|
|
settings: {
|
|
CLOUDSAVEURL: "",
|
|
SHOWADVANCED: false,
|
|
SHOWOPTIONS: false
|
|
}
|
|
};
|
|
|
|
//comes from settings.js
|
|
this.rivetsData.settings = window["N64WASMSETTINGS"];
|
|
|
|
if (this.rivetsData.settings.CLOUDSAVEURL!="")
|
|
{
|
|
this.rivetsData.showLogin = true;
|
|
}
|
|
|
|
|
|
if (window["ROMLIST"].length > 0)
|
|
{
|
|
this.rivetsData.hasRoms = true;
|
|
window["ROMLIST"].forEach(rom => {
|
|
this.rivetsData.romList.push(rom);
|
|
});
|
|
}
|
|
|
|
rivets.formatters.ev = function (value, arg) {
|
|
return eval(value + arg);
|
|
}
|
|
rivets.formatters.ev_string = function (value, arg) {
|
|
let eval_string = "'" + value + "'" + arg;
|
|
return eval(eval_string);
|
|
}
|
|
|
|
rivets.bind(document.getElementById('topPanel'), { data: this.rivetsData });
|
|
rivets.bind(document.getElementById('bottomPanel'), { data: this.rivetsData });
|
|
rivets.bind(document.getElementById('loginModal'), { data: this.rivetsData });
|
|
rivets.bind(document.getElementById('buttonsModal'), { data: this.rivetsData });
|
|
rivets.bind(document.getElementById('lblError'), { data: this.rivetsData });
|
|
rivets.bind(document.getElementById('mobileBottomPanel'), { data: this.rivetsData });
|
|
rivets.bind(document.getElementById('mobileButtons'), { data: this.rivetsData });
|
|
|
|
|
|
|
|
this.setupDragDropRom();
|
|
this.detectMobile();
|
|
this.setupLogin();
|
|
this.createDB();
|
|
this.retrieveSettings();
|
|
|
|
$('#topPanel').show();
|
|
$('#lblErrorOuter').show();
|
|
|
|
}
|
|
|
|
setupInputController(){
|
|
this.rivetsData.inputController = new InputController();
|
|
|
|
|
|
//try to load keymappings from localstorage
|
|
try {
|
|
let keymappings = localStorage.getItem('n64wasm_mappings_v3');
|
|
if (keymappings) {
|
|
let keymappings_object = JSON.parse(keymappings);
|
|
|
|
for (let [key, value] of Object.entries(keymappings_object)) {
|
|
if (key in this.rivetsData.inputController.KeyMappings){
|
|
this.rivetsData.inputController.KeyMappings[key] = value;
|
|
}
|
|
}
|
|
}
|
|
} catch (error) { }
|
|
|
|
}
|
|
|
|
inputLoop(){
|
|
myClass.rivetsData.inputController.update();
|
|
if (myClass.rivetsData.beforeEmulatorStarted)
|
|
{
|
|
setTimeout(() => {
|
|
myClass.inputLoop();
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
|
|
processPrintStatement(text) {
|
|
console.log(text);
|
|
|
|
//emulator has started event
|
|
if (text.includes('mupen64plus: Starting R4300 emulator: Cached Interpreter')) {
|
|
console.log('detected emulator started');
|
|
|
|
if (myClass.loadSavestateAfterBoot)
|
|
{
|
|
setTimeout(() => {
|
|
myClass.loadCloud();
|
|
|
|
myClass.afterRun();
|
|
}, 500);
|
|
}
|
|
|
|
}
|
|
|
|
//backup sram event
|
|
if (text.includes('writing game.savememory')){
|
|
setTimeout(() => {
|
|
myClass.SaveSram();
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
detectMobile(){
|
|
let isIphone = navigator.userAgent.toLocaleLowerCase().includes('iphone');
|
|
let isIpad = navigator.userAgent.toLocaleLowerCase().includes('ipad');
|
|
if (isIphone || isIpad)
|
|
{
|
|
this.iosMode = true;
|
|
try {
|
|
let iosVersion = navigator.userAgent.substring(navigator.userAgent.indexOf("iPhone OS ") + 10);
|
|
iosVersion = iosVersion.substring(0, iosVersion.indexOf(' '));
|
|
iosVersion = iosVersion.substring(0, iosVersion.indexOf('_'));
|
|
this.iosVersion = parseInt(iosVersion);
|
|
} catch (err) { }
|
|
|
|
//don't need this anymore
|
|
// if (this.iosVersion > 15) {
|
|
// this.rivetsData.iosShowWarning = true;
|
|
// }
|
|
}
|
|
if (window.innerWidth < 600 || isIphone)
|
|
this.mobileMode = true;
|
|
else
|
|
this.mobileMode = false;
|
|
}
|
|
|
|
toggleDoubleSpeed(){
|
|
if (this.rivetsData.doubleSpeed)
|
|
{
|
|
this.rivetsData.doubleSpeed = false;
|
|
this.setDoubleSpeed(0)
|
|
}
|
|
else
|
|
{
|
|
this.rivetsData.doubleSpeed = true;
|
|
this.setDoubleSpeed(1)
|
|
}
|
|
}
|
|
|
|
async LoadEmulator(byteArray){
|
|
if (this.rom_name.toLocaleLowerCase().endsWith('.zip'))
|
|
{
|
|
this.rivetsData.lblError = 'Zip format not supported. Please uncompress first.'
|
|
this.rivetsData.beforeEmulatorStarted = false;
|
|
}
|
|
else
|
|
{
|
|
await this.writeAssets();
|
|
FS.writeFile('custom.v64',byteArray);
|
|
this.beforeRun();
|
|
this.WriteConfigFile();
|
|
this.initAudio(); //need to initAudio before next call for iOS to work
|
|
await this.LoadSram();
|
|
Module.callMain(['custom.v64']);
|
|
this.findInDatabase();
|
|
this.configureEmulator();
|
|
$('#canvasDiv').show();
|
|
this.rivetsData.beforeEmulatorStarted = false;
|
|
this.showToast = Module.cwrap('neil_toast_message', null, ['string']);
|
|
this.toggleFPSModule = Module.cwrap('toggleFPS', null, ['number']);
|
|
this.sendMobileControls = Module.cwrap('neil_send_mobile_controls', null, ['string','string','string']);
|
|
this.setRemainingAudio = Module.cwrap('neil_set_buffer_remaining', null, ['number']);
|
|
this.setDoubleSpeed = Module.cwrap('neil_set_double_speed', null, ['number']);
|
|
}
|
|
|
|
}
|
|
|
|
async writeAssets(){
|
|
|
|
let file = 'assets.zip';
|
|
let responseText = await this.downloadFile(file);
|
|
console.log(file,responseText.length);
|
|
FS.writeFile(
|
|
file, // file name
|
|
responseText
|
|
);
|
|
}
|
|
|
|
async downloadFile(url) {
|
|
return new Promise(function (resolve, reject) {
|
|
var oReq = new XMLHttpRequest();
|
|
oReq.open("GET", url, true);
|
|
oReq.responseType = "arraybuffer";
|
|
oReq.onload = function (oEvent) {
|
|
var arrayBuffer = oReq.response;
|
|
var byteArray = new Uint8Array(arrayBuffer);
|
|
resolve(byteArray);
|
|
};
|
|
oReq.onerror = function(){
|
|
reject({
|
|
status: oReq.status,
|
|
statusText: oReq.statusText
|
|
});
|
|
}
|
|
oReq.send();
|
|
});
|
|
}
|
|
|
|
async initAudio() {
|
|
|
|
if (!this.audioInited)
|
|
{
|
|
this.audioInited = true;
|
|
this.audioContext = new AudioContext({
|
|
latencyHint: 'interactive',
|
|
sampleRate: 44100, //this number has to match what's in gui.cpp
|
|
});
|
|
this.gainNode = this.audioContext.createGain();
|
|
this.gainNode.gain.value = 0.5;
|
|
this.gainNode.connect(this.audioContext.destination);
|
|
|
|
//point at where the emulator is storing the audio buffer
|
|
this.audioBufferResampled = new Int16Array(Module.HEAP16.buffer,Module._neilGetSoundBufferResampledAddress(),64000);
|
|
|
|
this.audioWritePosition = 0;
|
|
this.audioReadPosition = 0;
|
|
this.audioBackOffCounter = 0;
|
|
this.audioThreadLock = false;
|
|
|
|
|
|
//emulator is synced to the OnAudioProcess event because it's way
|
|
//more accurate than emscripten_set_main_loop or RAF
|
|
//and the old method was having constant emulator slowdown swings
|
|
//so the audio suffered as a result
|
|
this.pcmPlayer = this.audioContext.createScriptProcessor(AUDIOBUFFSIZE, 2, 2);
|
|
this.pcmPlayer.onaudioprocess = this.AudioProcessRecurring.bind(this);
|
|
this.pcmPlayer.connect(this.gainNode);
|
|
}
|
|
|
|
}
|
|
|
|
hasEnoughSamples(){
|
|
|
|
let readPositionTemp = this.audioReadPosition;
|
|
let enoughSamples = true;
|
|
for (let sample = 0; sample < AUDIOBUFFSIZE; sample++)
|
|
{
|
|
if (this.audioWritePosition != readPositionTemp) {
|
|
readPositionTemp += 2;
|
|
|
|
//wrap back around within the ring buffer
|
|
if (readPositionTemp == 64000) {
|
|
readPositionTemp = 0;
|
|
}
|
|
}
|
|
else {
|
|
enoughSamples = false;
|
|
}
|
|
}
|
|
|
|
return enoughSamples;
|
|
}
|
|
|
|
//this method keeps getting called when it needs more audio
|
|
//data to play so we just keep streaming it from the emulator
|
|
AudioProcessRecurring(audioProcessingEvent){
|
|
|
|
//I think this method is thread safe but just in case
|
|
if (this.audioThreadLock || this.rivetsData.beforeEmulatorStarted)
|
|
{
|
|
// console.log('audio thread dupe');
|
|
return;
|
|
}
|
|
|
|
this.audioThreadLock = true;
|
|
|
|
|
|
|
|
var sampleRate = audioProcessingEvent.outputBuffer.sampleRate;
|
|
let outputBuffer = audioProcessingEvent.outputBuffer;
|
|
let outputData1 = outputBuffer.getChannelData(0);
|
|
let outputData2 = outputBuffer.getChannelData(1);
|
|
|
|
if (this.rivetsData.disableAudioSync)
|
|
{
|
|
this.audioWritePosition = Module._neilGetAudioWritePosition();
|
|
}
|
|
else
|
|
{
|
|
Module._runMainLoop();
|
|
|
|
this.audioWritePosition = Module._neilGetAudioWritePosition();
|
|
|
|
|
|
if (!this.hasEnoughSamples())
|
|
{
|
|
Module._runMainLoop();
|
|
}
|
|
|
|
this.audioWritePosition = Module._neilGetAudioWritePosition();
|
|
}
|
|
|
|
|
|
|
|
// if (!this.hasEnoughSamples())
|
|
// console.log('not enough samples');
|
|
|
|
// console.log('Write: ' + this.audioWritePosition + ' Read: ' + this.audioReadPosition);
|
|
|
|
let hadSkip = false;
|
|
|
|
|
|
//the bytes are arranged L,R,L,R,etc.... for each speaker
|
|
for (let sample = 0; sample < AUDIOBUFFSIZE; sample++) {
|
|
|
|
if (this.audioWritePosition != this.audioReadPosition) {
|
|
outputData1[sample] = (this.audioBufferResampled[this.audioReadPosition] / 32768);
|
|
outputData2[sample] = (this.audioBufferResampled[this.audioReadPosition + 1] / 32768);
|
|
|
|
this.audioReadPosition += 2;
|
|
|
|
//wrap back around within the ring buffer
|
|
if (this.audioReadPosition == 64000) {
|
|
this.audioReadPosition = 0;
|
|
}
|
|
}
|
|
else {
|
|
//if there's nothing to play then just play silence
|
|
outputData1[sample] = 0;
|
|
outputData2[sample] = 0;
|
|
|
|
//if we caught up on samples then back off
|
|
//for 2 frames to buffer some audio
|
|
// if (this.audioBackOffCounter == 0) {
|
|
// this.audioBackOffCounter = 2;
|
|
// }
|
|
|
|
hadSkip = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (hadSkip)
|
|
this.rivetsData.audioSkipCount++;
|
|
|
|
//calculate remaining audio in buffer
|
|
let audioBufferRemaining = 0;
|
|
let readPositionTemp = this.audioReadPosition;
|
|
let writePositionTemp = this.audioWritePosition;
|
|
for(let i = 0; i < 64000; i++)
|
|
{
|
|
if (readPositionTemp != writePositionTemp)
|
|
{
|
|
readPositionTemp += 2;
|
|
audioBufferRemaining += 2;
|
|
|
|
if (readPositionTemp == 64000) {
|
|
readPositionTemp = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.setRemainingAudio(audioBufferRemaining);
|
|
//myClass.showToast("Buffer: " + audioBufferRemaining);
|
|
|
|
this.audioThreadLock = false;
|
|
|
|
}
|
|
|
|
beforeRun(){
|
|
//add any overriding logic here before the emulator starts
|
|
}
|
|
|
|
afterRun(){
|
|
//add any overriding logic here after the emulator starts
|
|
}
|
|
|
|
WriteConfigFile()
|
|
{
|
|
let configString = "";
|
|
|
|
//gamepad
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Up + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Down + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Left + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Right + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Action_A + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Action_B + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Action_Start + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Action_Z + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Action_L + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Action_R + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Menu + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Action_CLEFT + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Action_CRIGHT + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Action_CUP + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Joy_Mapping_Action_CDOWN + "\r\n";
|
|
|
|
//keyboard
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Left + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Right + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Up + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Down + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_Start + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_CUP + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_CDOWN + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_CLEFT + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_CRIGHT + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_Z + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_L + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_R + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_B + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_A + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Menu + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_Analog_Up + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_Analog_Down + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_Analog_Left + "\r\n";
|
|
configString += this.rivetsData.inputController.KeyMappings.Mapping_Action_Analog_Right + "\r\n";
|
|
|
|
//load save files
|
|
if (this.eepData == null) configString += "0" + "\r\n"; else configString += "1" + "\r\n";
|
|
if (this.sraData == null) configString += "0" + "\r\n"; else configString += "1" + "\r\n";
|
|
if (this.flaData == null) configString += "0" + "\r\n"; else configString += "1" + "\r\n";
|
|
|
|
//show FPS
|
|
if (this.rivetsData.showFPS) configString += "1" + "\r\n"; else configString += "0" + "\r\n";
|
|
|
|
//swap sticks
|
|
if (this.rivetsData.swapSticks) configString += "1" + "\r\n"; else configString += "0" + "\r\n";
|
|
|
|
//disable audio sync
|
|
if (this.rivetsData.disableAudioSync) configString += "1" + "\r\n"; else configString += "0" + "\r\n";
|
|
|
|
//invert player Y axis
|
|
if (this.rivetsData.invert2P) configString += "1" + "\r\n"; else configString += "0" + "\r\n";
|
|
if (this.rivetsData.invert3P) configString += "1" + "\r\n"; else configString += "0" + "\r\n";
|
|
if (this.rivetsData.invert4P) configString += "1" + "\r\n"; else configString += "0" + "\r\n";
|
|
|
|
//mobile mode
|
|
if (this.rivetsData.settingMobile == 'ForceMobile') this.mobileMode = true;
|
|
if (this.rivetsData.settingMobile == 'ForceDesktop') this.mobileMode = false;
|
|
if (this.mobileMode) configString += "1" + "\r\n"; else configString += "0" + "\r\n";
|
|
|
|
//angrylion software renderer
|
|
if (this.rivetsData.forceAngry) configString += "1" + "\r\n"; else configString += "0" + "\r\n";
|
|
|
|
//mouse mode
|
|
if (this.rivetsData.mouseMode) configString += "1" + "\r\n"; else configString += "0" + "\r\n";
|
|
|
|
//use vbo
|
|
if (this.iosMode) configString += "1" + "\r\n"; else configString += "0" + "\r\n";
|
|
|
|
FS.writeFile('config.txt',configString);
|
|
|
|
//write cheats
|
|
let cheatString = '';
|
|
this.rivetsData.cheats.forEach(cheat => {
|
|
if (cheat.active)
|
|
{
|
|
cheatString += cheat.address + "\r\n" + cheat.value + "\r\n";
|
|
}
|
|
});
|
|
|
|
FS.writeFile('cheat.txt',cheatString);
|
|
|
|
//we don't allow double speed when doing audio sync
|
|
if (!this.rivetsData.disableAudioSync)
|
|
{
|
|
this.rivetsData.showDoubleSpeed = false;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
uploadBrowse() {
|
|
this.initAudio();
|
|
document.getElementById('file-upload').click();
|
|
}
|
|
|
|
uploadEepBrowse() {
|
|
document.getElementById('file-upload-eep').click();
|
|
}
|
|
uploadSraBrowse() {
|
|
document.getElementById('file-upload-sra').click();
|
|
}
|
|
uploadFlaBrowse() {
|
|
document.getElementById('file-upload-fla').click();
|
|
}
|
|
|
|
uploadEep(event) {
|
|
var file = event.currentTarget.files[0];
|
|
console.log(file);
|
|
myClass.rivetsData.eepName = 'File Ready';
|
|
var reader = new FileReader();
|
|
reader.onprogress = function (e) {
|
|
console.log('loaded: ' + e.loaded);
|
|
};
|
|
reader.onload = function (e) {
|
|
console.log('finished loading');
|
|
var byteArray = new Uint8Array(this.result);
|
|
myClass.eepData = byteArray;
|
|
|
|
FS.writeFile(
|
|
"game.eep", // file name
|
|
byteArray
|
|
);
|
|
}
|
|
reader.readAsArrayBuffer(file);
|
|
}
|
|
uploadSra(event) {
|
|
var file = event.currentTarget.files[0];
|
|
console.log(file);
|
|
myClass.rivetsData.sraName = 'File Ready';
|
|
var reader = new FileReader();
|
|
reader.onprogress = function (e) {
|
|
console.log('loaded: ' + e.loaded);
|
|
};
|
|
reader.onload = function (e) {
|
|
console.log('finished loading');
|
|
var byteArray = new Uint8Array(this.result);
|
|
myClass.sraData = byteArray;
|
|
|
|
FS.writeFile(
|
|
"game.sra", // file name
|
|
byteArray
|
|
);
|
|
}
|
|
reader.readAsArrayBuffer(file);
|
|
}
|
|
uploadFla(event) {
|
|
var file = event.currentTarget.files[0];
|
|
console.log(file);
|
|
myClass.rivetsData.flaName = 'File Ready';
|
|
var reader = new FileReader();
|
|
reader.onprogress = function (e) {
|
|
console.log('loaded: ' + e.loaded);
|
|
};
|
|
reader.onload = function (e) {
|
|
console.log('finished loading');
|
|
var byteArray = new Uint8Array(this.result);
|
|
myClass.flaData = byteArray;
|
|
|
|
FS.writeFile(
|
|
"game.fla", // file name
|
|
byteArray
|
|
);
|
|
}
|
|
reader.readAsArrayBuffer(file);
|
|
}
|
|
|
|
uploadRom(event) {
|
|
var file = event.currentTarget.files[0];
|
|
myClass.rom_name = file.name;
|
|
console.log(file);
|
|
var reader = new FileReader();
|
|
reader.onprogress = function (e) {
|
|
console.log('loaded: ' + e.loaded);
|
|
};
|
|
reader.onload = function (e) {
|
|
console.log('finished loading');
|
|
var byteArray = new Uint8Array(this.result);
|
|
myClass.LoadEmulator(byteArray);
|
|
}
|
|
reader.readAsArrayBuffer(file);
|
|
}
|
|
|
|
resizeCanvas() {
|
|
$('#canvas').width(this.canvasSize);
|
|
}
|
|
|
|
zoomOut() {
|
|
|
|
this.canvasSize -= 50;
|
|
localStorage.setItem('n64wasm-size', this.canvasSize.toString());
|
|
this.resizeCanvas();
|
|
}
|
|
|
|
zoomIn() {
|
|
this.canvasSize += 50;
|
|
localStorage.setItem('n64wasm-size', this.canvasSize.toString());
|
|
this.resizeCanvas();
|
|
}
|
|
|
|
|
|
async initModule(){
|
|
console.log('module initialized');
|
|
myClass.rivetsData.moduleInitializing = false;
|
|
//myClass.loadFiles();
|
|
}
|
|
|
|
//not being used currently
|
|
async loadFiles(){
|
|
let files = ["shader_vert.hlsl", "shader_frag.hlsl"];
|
|
|
|
for (let i = 0; i < files.length; i++) {
|
|
let file = files[i];
|
|
let responseText = await $.ajax({
|
|
url: file,
|
|
beforeSend: function (xhr) {
|
|
xhr.overrideMimeType("text/plain; charset=x-user-defined");
|
|
}
|
|
});
|
|
console.log(file,responseText.length);
|
|
FS.writeFile(
|
|
file, // file name
|
|
responseText
|
|
);
|
|
}
|
|
}
|
|
|
|
//DRAG AND DROP ROM
|
|
setupDragDropRom(){
|
|
let dropArea = document.getElementById('dropArea');
|
|
|
|
dropArea.addEventListener('dragenter', this.preventDefaults, false);
|
|
dropArea.addEventListener('dragover', this.preventDefaults, false);
|
|
dropArea.addEventListener('dragleave', this.preventDefaults, false);
|
|
dropArea.addEventListener('drop', this.preventDefaults, false);
|
|
|
|
dropArea.addEventListener('dragenter', this.dragDropHighlight, false);
|
|
dropArea.addEventListener('dragover', this.dragDropHighlight, false);
|
|
dropArea.addEventListener('dragleave', this.dragDropUnHighlight, false);
|
|
dropArea.addEventListener('drop', this.dragDropUnHighlight, false);
|
|
|
|
dropArea.addEventListener('drop', this.handleDrop, false);
|
|
|
|
}
|
|
|
|
preventDefaults(e){
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
|
|
dragDropHighlight(e){
|
|
$('#dropArea').css({"background-color": "lightblue"});
|
|
}
|
|
|
|
dragDropUnHighlight(e){
|
|
$('#dropArea').css({"background-color": "inherit"});
|
|
}
|
|
|
|
handleDrop(e){
|
|
let dt = e.dataTransfer;
|
|
let files = dt.files;
|
|
|
|
var file = files[0];
|
|
myClass.rom_name = file.name;
|
|
console.log(file);
|
|
var reader = new FileReader();
|
|
reader.onprogress = function (e) {
|
|
console.log('loaded: ' + e.loaded);
|
|
};
|
|
reader.onload = function (e) {
|
|
console.log('finished loading');
|
|
var byteArray = new Uint8Array(this.result);
|
|
myClass.LoadEmulator(byteArray);
|
|
}
|
|
reader.readAsArrayBuffer(file);
|
|
|
|
}
|
|
|
|
extractRomName(name){
|
|
if (name.includes('/'))
|
|
{
|
|
name = name.substr(name.lastIndexOf('/')+1);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
loadRomAndSavestate(){
|
|
let selector = document.getElementById('romselect');
|
|
let saveToLoad = document.getElementById('savestateSelect')["value"];
|
|
|
|
for (let i=0;i<selector.options.length;i++)
|
|
{
|
|
let romurl = selector.options[i].value;
|
|
let romname = this.extractRomName(romurl);
|
|
|
|
if (saveToLoad==romname + '.n64wasm')
|
|
{
|
|
selector.selectedIndex = i;
|
|
this.loadSavestateAfterBoot = true;
|
|
}
|
|
}
|
|
|
|
this.loadRom();
|
|
}
|
|
|
|
async loadRom() {
|
|
//get rom url
|
|
let romurl = document.getElementById('romselect')["value"];
|
|
console.log(romurl);
|
|
this.rom_name = this.extractRomName(romurl);
|
|
|
|
this.load_url(romurl);
|
|
}
|
|
|
|
load_url(path) {
|
|
console.log('loading ' + path);
|
|
|
|
var req = new XMLHttpRequest();
|
|
req.open("GET", path);
|
|
req.overrideMimeType("text/plain; charset=x-user-defined");
|
|
req.onerror = () => console.log(`Error loading ${path}: ${req.statusText}`);
|
|
req.responseType = "arraybuffer";
|
|
|
|
req.onload = function () {
|
|
var arrayBuffer = req.response; // Note: not oReq.responseText
|
|
try{
|
|
if (arrayBuffer) {
|
|
var byteArray = new Uint8Array(arrayBuffer);
|
|
myClass.LoadEmulator(byteArray);
|
|
}
|
|
else{
|
|
//toastr.error('Error Loading Cloud Save');
|
|
}
|
|
}
|
|
catch(error){
|
|
console.log(error);
|
|
toastr.error('Error Loading Save');
|
|
}
|
|
};
|
|
|
|
req.send();
|
|
}
|
|
|
|
|
|
|
|
saveStateLocal(){
|
|
console.log('saveStateLocal');
|
|
this.rivetsData.noLocalSave = false;
|
|
Module._neil_serialize();
|
|
}
|
|
|
|
loadStateLocal(){
|
|
console.log('loadStateLocal');
|
|
myClass.loadFromDatabase();
|
|
}
|
|
|
|
createDB() {
|
|
|
|
if (window["indexedDB"]==undefined){
|
|
console.log('indexedDB not available');
|
|
return;
|
|
}
|
|
|
|
var request = indexedDB.open('N64WASMDB');
|
|
request.onupgradeneeded = function (ev) {
|
|
console.log('upgrade needed');
|
|
let db = ev.target.result;
|
|
let objectStore = db.createObjectStore('N64WASMSTATES', { autoIncrement: true });
|
|
objectStore.transaction.oncomplete = function (event) {
|
|
console.log('db created');
|
|
};
|
|
}
|
|
|
|
request.onsuccess = function (ev) {
|
|
var db = ev.target.result;
|
|
var romStore = db.transaction("N64WASMSTATES", "readwrite").objectStore("N64WASMSTATES");
|
|
try {
|
|
//rewrote using cursor instead of getAllKeys
|
|
//for compatibility with MS EDGE
|
|
romStore.openCursor().onsuccess = function (ev) {
|
|
var cursor = ev.target.result;
|
|
if (cursor) {
|
|
let rom = cursor.key.toString();
|
|
myClass.dblist.push(rom);
|
|
cursor.continue();
|
|
}
|
|
else {
|
|
if (myClass.dblist.length > 0) {
|
|
//TODO show savestates grid
|
|
}
|
|
}
|
|
}
|
|
|
|
} catch (error) {
|
|
console.log('error reading keys');
|
|
console.log(error);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
findInDatabase() {
|
|
|
|
if (!window["indexedDB"]==undefined){
|
|
console.log('indexedDB not available');
|
|
return;
|
|
}
|
|
|
|
var request = indexedDB.open('N64WASMDB');
|
|
request.onsuccess = function (ev) {
|
|
var db = ev.target.result;
|
|
var romStore = db.transaction("N64WASMSTATES", "readwrite").objectStore("N64WASMSTATES");
|
|
try {
|
|
romStore.openCursor().onsuccess = function (ev) {
|
|
var cursor = ev.target.result;
|
|
if (cursor) {
|
|
let rom = cursor.key.toString();
|
|
if (myClass.rom_name == rom)
|
|
{
|
|
myClass.rivetsData.noLocalSave = false;
|
|
}
|
|
cursor.continue();
|
|
}
|
|
}
|
|
|
|
} catch (error) {
|
|
console.log('error reading keys');
|
|
console.log(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
async LoadSram() {
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
var request = indexedDB.open('N64WASMDB');
|
|
try
|
|
{
|
|
request.onsuccess = function (ev) {
|
|
var db = ev.target.result;
|
|
var romStore = db.transaction("N64WASMSTATES", "readwrite").objectStore("N64WASMSTATES");
|
|
var rom = romStore.get(myClass.rom_name + '.sram');
|
|
rom.onsuccess = function (event) {
|
|
if (rom.result)
|
|
{
|
|
let byteArray = rom.result; //Uint8Array
|
|
FS.writeFile('/game.savememory', byteArray);
|
|
}
|
|
resolve();
|
|
};
|
|
rom.onerror = function (event) {
|
|
reject();
|
|
}
|
|
}
|
|
request.onerror = function (ev) {
|
|
reject();
|
|
}
|
|
}catch(error){
|
|
reject();
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
SaveSram() {
|
|
|
|
let data = FS.readFile('/game.savememory'); //this is a Uint8Array
|
|
|
|
var request = indexedDB.open('N64WASMDB');
|
|
request.onsuccess = function (ev) {
|
|
var db = ev.target.result;
|
|
var romStore = db.transaction("N64WASMSTATES", "readwrite").objectStore("N64WASMSTATES");
|
|
var addRequest = romStore.put(data, myClass.rom_name + '.sram');
|
|
addRequest.onsuccess = function (event) {
|
|
console.log('sram added');
|
|
};
|
|
addRequest.onerror = function (event) {
|
|
console.log('error adding sram');
|
|
console.log(event);
|
|
};
|
|
}
|
|
|
|
}
|
|
|
|
saveToDatabase(data) {
|
|
|
|
if (!window["indexedDB"]==undefined){
|
|
console.log('indexedDB not available');
|
|
return;
|
|
}
|
|
|
|
console.log('save to database called: ', data.length);
|
|
|
|
var request = indexedDB.open('N64WASMDB');
|
|
request.onsuccess = function (ev) {
|
|
var db = ev.target.result;
|
|
var romStore = db.transaction("N64WASMSTATES", "readwrite").objectStore("N64WASMSTATES");
|
|
var addRequest = romStore.put(data, myClass.rom_name);
|
|
addRequest.onsuccess = function (event) {
|
|
console.log('data added');
|
|
toastr.info('State Saved');
|
|
window["myApp"].showToast("State Saved");
|
|
};
|
|
addRequest.onerror = function (event) {
|
|
console.log('error adding data');
|
|
console.log(event);
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
loadFromDatabase() {
|
|
|
|
var request = indexedDB.open('N64WASMDB');
|
|
request.onsuccess = function (ev) {
|
|
var db = ev.target.result;
|
|
var romStore = db.transaction("N64WASMSTATES", "readwrite").objectStore("N64WASMSTATES");
|
|
var rom = romStore.get(myClass.rom_name);
|
|
rom.onsuccess = function (event) {
|
|
let byteArray = rom.result; //Uint8Array
|
|
FS.writeFile('/savestate.gz',byteArray);
|
|
Module._neil_unserialize();
|
|
|
|
};
|
|
rom.onerror = function (event) {
|
|
toastr.error('error getting rom from store');
|
|
}
|
|
}
|
|
request.onerror = function (ev) {
|
|
toastr.error('error loading from db')
|
|
}
|
|
|
|
}
|
|
|
|
|
|
clearDatabase() {
|
|
|
|
var request = indexedDB.deleteDatabase('N64WASMDB');
|
|
request.onerror = function (event) {
|
|
console.log("Error deleting database.");
|
|
toastr.error("Error deleting database");
|
|
};
|
|
|
|
request.onsuccess = function (event) {
|
|
console.log("Database deleted successfully");
|
|
toastr.error("Database deleted successfully");
|
|
};
|
|
|
|
}
|
|
|
|
|
|
exportEep(){
|
|
Module._neil_export_eep();
|
|
}
|
|
ExportEepEvent()
|
|
{
|
|
console.log('js eep event');
|
|
|
|
let filearray = FS.readFile("/game.eep");
|
|
var file = new File([filearray], "game.eep", {type: "text/plain; charset=x-user-defined"});
|
|
saveAs(file);
|
|
}
|
|
exportSra(){
|
|
Module._neil_export_sra();
|
|
}
|
|
ExportSraEvent()
|
|
{
|
|
console.log('js sra event');
|
|
|
|
let filearray = FS.readFile("/game.sra");
|
|
var file = new File([filearray], "game.sra", {type: "text/plain; charset=x-user-defined"});
|
|
saveAs(file);
|
|
}
|
|
exportFla(){
|
|
Module._neil_export_fla();
|
|
}
|
|
ExportFlaEvent()
|
|
{
|
|
console.log('js fla event');
|
|
|
|
let filearray = FS.readFile("/game.fla");
|
|
var file = new File([filearray], "game.fla", {type: "text/plain; charset=x-user-defined"});
|
|
saveAs(file);
|
|
}
|
|
|
|
//when it returns from emscripten
|
|
SaveStateEvent()
|
|
{
|
|
myClass.hideMobileMenu();
|
|
|
|
console.log('js savestate event');
|
|
let compressed = FS.readFile('/savestate.gz'); //this is a Uint8Array
|
|
|
|
//use local db
|
|
if (!myClass.rivetsData.loggedIn)
|
|
{
|
|
myClass.saveToDatabase(compressed);
|
|
return;
|
|
}
|
|
|
|
|
|
var xhr = new XMLHttpRequest;
|
|
xhr.open("POST", this.rivetsData.settings.CLOUDSAVEURL + "/SendStaveState?name=" + this.rom_name + '.n64wasm' +
|
|
"&password=" + this.rivetsData.password + "&emulator=n64", true);
|
|
xhr.send(compressed);
|
|
|
|
xhr.onreadystatechange = function() {
|
|
try{
|
|
if (xhr.readyState === 4) {
|
|
let result = xhr.response;
|
|
if (result=="\"Success\""){
|
|
myClass.rivetsData.noCloudSave = false;
|
|
toastr.info("Cloud State Saved");
|
|
myClass.showToast("Cloud State Saved");
|
|
}else{
|
|
toastr.error('Error Saving Cloud Save');
|
|
}
|
|
}
|
|
}
|
|
catch(error){
|
|
console.log(error);
|
|
toastr.error('Error Loading Cloud Save');
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
saveCloud(){
|
|
Module._neil_serialize();
|
|
}
|
|
|
|
loadCloud(){
|
|
|
|
myClass.hideMobileMenu();
|
|
|
|
//use local db
|
|
if (!myClass.rivetsData.loggedIn)
|
|
{
|
|
myClass.loadFromDatabase();
|
|
return;
|
|
}
|
|
|
|
var oReq = new XMLHttpRequest();
|
|
oReq.open("GET", this.rivetsData.settings.CLOUDSAVEURL + "/LoadStaveState?name=" + this.rom_name + '.n64wasm' +
|
|
"&password=" + this.rivetsData.password, true);
|
|
oReq.responseType = "arraybuffer";
|
|
|
|
oReq.onload = function (oEvent) {
|
|
var arrayBuffer = oReq.response; // Note: not oReq.responseText
|
|
try{
|
|
if (arrayBuffer) {
|
|
var byteArray = new Uint8Array(arrayBuffer);
|
|
FS.writeFile('/savestate.gz',byteArray);
|
|
Module._neil_unserialize();
|
|
}
|
|
else{
|
|
toastr.error('Error Loading Cloud Save');
|
|
}
|
|
}
|
|
catch(error){
|
|
console.log(error);
|
|
toastr.error('Error Loading Cloud Save');
|
|
}
|
|
|
|
};
|
|
|
|
oReq.send(null);
|
|
}
|
|
|
|
fullscreen() {
|
|
try {
|
|
let el = document.getElementById('canvas');
|
|
|
|
window["myApp"].rivetsData.hadFullscreen = true;
|
|
|
|
if (el.webkitRequestFullScreen) {
|
|
el.webkitRequestFullScreen();
|
|
}
|
|
else {
|
|
el.mozRequestFullScreen();
|
|
}
|
|
} catch (error)
|
|
{
|
|
console.log('full screen failed');
|
|
}
|
|
}
|
|
|
|
newRom(){
|
|
location.reload();
|
|
}
|
|
|
|
configureEmulator(){
|
|
let size = localStorage.getItem('n64wasm-size');
|
|
if (size) {
|
|
console.log('size found');
|
|
let sizeNum = parseInt(size);
|
|
this.canvasSize = sizeNum;
|
|
}
|
|
|
|
if (this.mobileMode)
|
|
{
|
|
this.setupMobileMode();
|
|
}
|
|
|
|
this.resizeCanvas();
|
|
|
|
if (this.rivetsData.password)
|
|
this.loginSilent();
|
|
|
|
if (this.rivetsData.mouseMode)
|
|
{
|
|
document.getElementById('canvasDiv').addEventListener("click", this.canvasClick.bind(this));
|
|
}
|
|
}
|
|
|
|
canvasClick(){
|
|
let isPointerCurrentlyLocked = document.pointerLockElement;
|
|
if (!isPointerCurrentlyLocked)
|
|
this.captureMouse();
|
|
}
|
|
|
|
captureMouse(){
|
|
let canvas = document.getElementById('canvas');
|
|
|
|
//mouse capture
|
|
canvas.requestPointerLock = canvas.requestPointerLock ||
|
|
canvas.mozRequestPointerLock;
|
|
|
|
canvas.requestPointerLock()
|
|
}
|
|
|
|
setupMobileMode() {
|
|
|
|
this.canvasSize = window.innerWidth;
|
|
console.log('canvas size', this.canvasSize);
|
|
|
|
$("#btnHideMenu").show();
|
|
let halfWidth = (window.innerWidth / 2) - 35;
|
|
document.getElementById("menuDiv").style.left = halfWidth + "px";
|
|
|
|
this.rivetsData.inputController.setupMobileControls('divTouchSurface');
|
|
|
|
// document.getElementById('body').style['background-color'] = 'white';
|
|
|
|
$("#mobileDiv").show();
|
|
$("#maindiv").hide();
|
|
$("#middleDiv").hide();
|
|
$('#canvas').appendTo("#mobileCanvas");
|
|
|
|
document.getElementById('maindiv').classList.remove('container');
|
|
|
|
//fixes the small gap between canvas and mobile buttons
|
|
document.getElementById('canvas').style.display = 'block';
|
|
|
|
//scroll back to top
|
|
try {
|
|
document.body.scrollTop = 0; // For Safari
|
|
document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
|
|
} catch (error) { }
|
|
}
|
|
|
|
hideMobileMenu() {
|
|
if (this.mobileMode)
|
|
{
|
|
$("#mobileButtons").hide();
|
|
$('#menuDiv').show();
|
|
}
|
|
}
|
|
|
|
setFromLocalStorage(localStorageName, rivetsName){
|
|
if (localStorage.getItem(localStorageName))
|
|
{
|
|
if (localStorage.getItem(localStorageName)=="true")
|
|
this.rivetsData[rivetsName] = true;
|
|
else if (localStorage.getItem(localStorageName)=="false")
|
|
this.rivetsData[rivetsName] = false;
|
|
else
|
|
this.rivetsData[rivetsName] = localStorage.getItem(localStorageName);
|
|
}
|
|
}
|
|
|
|
setToLocalStorage(localStorageName, rivetsName){
|
|
|
|
if (typeof(myApp.rivetsData[rivetsName]) == 'boolean')
|
|
{
|
|
if (this.rivetsData[rivetsName])
|
|
localStorage.setItem(localStorageName, 'true');
|
|
else
|
|
localStorage.setItem(localStorageName, 'false');
|
|
}
|
|
else
|
|
{
|
|
localStorage.setItem(localStorageName, this.rivetsData[rivetsName]);
|
|
}
|
|
|
|
}
|
|
|
|
retrieveSettings(){
|
|
this.loadCheats();
|
|
this.setFromLocalStorage('n64wasm-showfps','showFPS');
|
|
this.setFromLocalStorage('n64wasm-disableaudiosyncnew','disableAudioSync');
|
|
this.setFromLocalStorage('n64wasm-swapSticks','swapSticks');
|
|
this.setFromLocalStorage('n64wasm-invert2P','invert2P');
|
|
this.setFromLocalStorage('n64wasm-invert3P','invert3P');
|
|
this.setFromLocalStorage('n64wasm-invert4P','invert4P');
|
|
this.setFromLocalStorage('n64wasm-settingMobile','settingMobile');
|
|
this.setFromLocalStorage('n64wasm-mouseMode','mouseMode');
|
|
this.setFromLocalStorage('n64wasm-forceAngry','forceAngry');
|
|
|
|
}
|
|
|
|
saveOptions(){
|
|
|
|
this.rivetsData.showFPS = this.rivetsData.showFPSTemp;
|
|
this.rivetsData.swapSticks = this.rivetsData.swapSticksTemp;
|
|
this.rivetsData.mouseMode = this.rivetsData.mouseModeTemp;
|
|
this.rivetsData.invert2P = this.rivetsData.invert2PTemp;
|
|
this.rivetsData.invert3P = this.rivetsData.invert3PTemp;
|
|
this.rivetsData.invert4P = this.rivetsData.invert4PTemp;
|
|
this.rivetsData.disableAudioSync = this.rivetsData.disableAudioSyncTemp;
|
|
this.rivetsData.settingMobile = this.rivetsData.settingMobileTemp;
|
|
this.rivetsData.forceAngry = this.rivetsData.forceAngryTemp;
|
|
|
|
this.setToLocalStorage('n64wasm-showfps','showFPS');
|
|
this.setToLocalStorage('n64wasm-disableaudiosyncnew','disableAudioSync');
|
|
this.setToLocalStorage('n64wasm-swapSticks','swapSticks');
|
|
this.setToLocalStorage('n64wasm-mouseMode','mouseMode');
|
|
this.setToLocalStorage('n64wasm-invert2P','invert2P');
|
|
this.setToLocalStorage('n64wasm-invert3P','invert3P');
|
|
this.setToLocalStorage('n64wasm-invert4P','invert4P');
|
|
this.setToLocalStorage('n64wasm-settingMobile','settingMobile');
|
|
this.setToLocalStorage('n64wasm-forceAngry','forceAngry');
|
|
|
|
}
|
|
|
|
|
|
showRemapModal() {
|
|
|
|
this.rivetsData.remapPlayer1 = true;
|
|
this.rivetsData.remapOptions = false;
|
|
this.rivetsData.remapGameshark = false;
|
|
|
|
this.rivetsData.showFPSTemp = this.rivetsData.showFPS;
|
|
this.rivetsData.swapSticksTemp = this.rivetsData.swapSticks;
|
|
this.rivetsData.mouseModeTemp = this.rivetsData.mouseMode;
|
|
this.rivetsData.invert2PTemp = this.rivetsData.invert2P;
|
|
this.rivetsData.invert3PTemp = this.rivetsData.invert3P;
|
|
this.rivetsData.invert4PTemp = this.rivetsData.invert4P;
|
|
this.rivetsData.disableAudioSyncTemp = this.rivetsData.disableAudioSync;
|
|
this.rivetsData.settingMobileTemp = this.rivetsData.settingMobile;
|
|
this.rivetsData.forceAngryTemp = this.rivetsData.forceAngry;
|
|
|
|
//start input loop
|
|
if (!this.rivetsData.inputLoopStarted)
|
|
{
|
|
this.rivetsData.inputLoopStarted = true;
|
|
this.rivetsData.inputController.setupGamePad();
|
|
setTimeout(() => {
|
|
myClass.inputLoop();
|
|
}, 100);
|
|
}
|
|
|
|
if (this.rivetsData.inputController.Gamepad_Process_Axis)
|
|
this.rivetsData.chkUseJoypad = true;
|
|
this.rivetsData.remappings = JSON.parse(JSON.stringify(this.rivetsData.inputController.KeyMappings));
|
|
this.rivetsData.remapWait = false;
|
|
$("#buttonsModal").modal();
|
|
}
|
|
|
|
addCheat(){
|
|
let cheat = {
|
|
name: this.rivetsData.cheatName.trim(),
|
|
address: this.rivetsData.cheatAddress.trim(),
|
|
value: this.rivetsData.cheatValue.trim(),
|
|
active: true
|
|
}
|
|
|
|
this.rivetsData.cheats.push(cheat);
|
|
this.rivetsData.cheatName = '';
|
|
this.rivetsData.cheatAddress = '';
|
|
this.rivetsData.cheatValue = '';
|
|
|
|
this.saveCheats();
|
|
}
|
|
|
|
loadCheats(){
|
|
try
|
|
{
|
|
let cheats = JSON.parse(localStorage.getItem('n64wasm-cheats'));
|
|
for(let i = 0; i < cheats.length; i++)
|
|
{
|
|
let cheat = cheats[i];
|
|
if (cheat.name && cheat.address && cheat.value)
|
|
{
|
|
this.rivetsData.cheats.push(cheat);
|
|
}
|
|
}
|
|
}catch(err){}
|
|
}
|
|
|
|
saveCheats(){
|
|
localStorage.setItem('n64wasm-cheats',JSON.stringify(this.rivetsData.cheats));
|
|
}
|
|
|
|
deleteCheat(cheat){
|
|
this.rivetsData.cheats = this.rivetsData.cheats.filter((a)=>{ return a.name != cheat.name; });
|
|
this.saveCheats();
|
|
}
|
|
|
|
swapRemap(id){
|
|
if (id=='options')
|
|
{
|
|
this.rivetsData.remapPlayer1 = false;
|
|
this.rivetsData.remapOptions = true;
|
|
this.rivetsData.remapGameshark = false;
|
|
}
|
|
if (id=='player1')
|
|
{
|
|
this.rivetsData.remapPlayer1 = true;
|
|
this.rivetsData.remapOptions = false;
|
|
this.rivetsData.remapGameshark = false;
|
|
}
|
|
if (id=='gameshark')
|
|
{
|
|
this.rivetsData.remapPlayer1 = false;
|
|
this.rivetsData.remapOptions = false;
|
|
this.rivetsData.remapGameshark = true;
|
|
}
|
|
}
|
|
|
|
|
|
saveRemap() {
|
|
if (this.rivetsData.chkUseJoypad)
|
|
this.rivetsData.inputController.Gamepad_Process_Axis = true;
|
|
else
|
|
this.rivetsData.inputController.Gamepad_Process_Axis = false;
|
|
|
|
this.rivetsData.inputController.KeyMappings = JSON.parse(JSON.stringify(this.rivetsData.remappings));
|
|
this.rivetsData.inputController.setGamePadButtons();
|
|
this.saveOptions();
|
|
localStorage.setItem('n64wasm_mappings_v3', JSON.stringify(this.rivetsData.remappings));
|
|
$("#buttonsModal").modal('hide');
|
|
}
|
|
|
|
btnRemapKey(keynum) {
|
|
console.log(this);
|
|
this.rivetsData.currKey = keynum;
|
|
this.rivetsData.remapMode = 'Key';
|
|
this.readyRemap();
|
|
}
|
|
|
|
btnRemapJoy(joynum) {
|
|
|
|
this.rivetsData.currJoy = joynum;
|
|
this.rivetsData.remapMode = 'Button';
|
|
this.readyRemap();
|
|
}
|
|
|
|
readyRemap() {
|
|
this.rivetsData.remapWait = true;
|
|
this.rivetsData.inputController.Key_Last = '';
|
|
this.rivetsData.inputController.Joy_Last = null;
|
|
this.rivetsData.inputController.Remap_Check = true;
|
|
}
|
|
|
|
restoreDefaultKeymappings(){
|
|
this.rivetsData.remappings = this.rivetsData.inputController.defaultKeymappings();
|
|
this.rivetsData.showFPSTemp = true;
|
|
this.rivetsData.swapSticksTemp = false;
|
|
this.rivetsData.mouseModeTemp = false;
|
|
this.rivetsData.invert2PTemp = false;
|
|
this.rivetsData.invert3PTemp = false;
|
|
this.rivetsData.invert4PTemp = false;
|
|
this.rivetsData.disableAudioSyncTemp = true;
|
|
this.rivetsData.settingMobileTemp = 'Auto';
|
|
this.rivetsData.forceAngryTemp = false;
|
|
}
|
|
|
|
remapPressed() {
|
|
if (this.rivetsData.remapMode == 'Key') {
|
|
var keyLast = this.rivetsData.inputController.Key_Last;
|
|
|
|
//player 1
|
|
if (this.rivetsData.currKey == 1) this.rivetsData.remappings.Mapping_Up = keyLast;
|
|
if (this.rivetsData.currKey == 2) this.rivetsData.remappings.Mapping_Down = keyLast;
|
|
if (this.rivetsData.currKey == 3) this.rivetsData.remappings.Mapping_Left = keyLast;
|
|
if (this.rivetsData.currKey == 4) this.rivetsData.remappings.Mapping_Right = keyLast;
|
|
if (this.rivetsData.currKey == 5) this.rivetsData.remappings.Mapping_Action_A = keyLast;
|
|
if (this.rivetsData.currKey == 6) this.rivetsData.remappings.Mapping_Action_B = keyLast;
|
|
if (this.rivetsData.currKey == 8) this.rivetsData.remappings.Mapping_Action_Start = keyLast;
|
|
if (this.rivetsData.currKey == 9) this.rivetsData.remappings.Mapping_Menu = keyLast;
|
|
if (this.rivetsData.currKey == 10) this.rivetsData.remappings.Mapping_Action_Z = keyLast;
|
|
if (this.rivetsData.currKey == 11) this.rivetsData.remappings.Mapping_Action_L = keyLast;
|
|
if (this.rivetsData.currKey == 12) this.rivetsData.remappings.Mapping_Action_R = keyLast;
|
|
if (this.rivetsData.currKey == 13) this.rivetsData.remappings.Mapping_Action_CUP = keyLast;
|
|
if (this.rivetsData.currKey == 14) this.rivetsData.remappings.Mapping_Action_CDOWN = keyLast;
|
|
if (this.rivetsData.currKey == 15) this.rivetsData.remappings.Mapping_Action_CLEFT = keyLast;
|
|
if (this.rivetsData.currKey == 16) this.rivetsData.remappings.Mapping_Action_CRIGHT = keyLast;
|
|
if (this.rivetsData.currKey == 17) this.rivetsData.remappings.Mapping_Action_Analog_Up = keyLast;
|
|
if (this.rivetsData.currKey == 18) this.rivetsData.remappings.Mapping_Action_Analog_Down = keyLast;
|
|
if (this.rivetsData.currKey == 19) this.rivetsData.remappings.Mapping_Action_Analog_Left = keyLast;
|
|
if (this.rivetsData.currKey == 20) this.rivetsData.remappings.Mapping_Action_Analog_Right = keyLast;
|
|
|
|
}
|
|
if (this.rivetsData.remapMode == 'Button') {
|
|
var joyLast = this.rivetsData.inputController.Joy_Last;
|
|
if (this.rivetsData.currJoy == 1) this.rivetsData.remappings.Joy_Mapping_Up = joyLast;
|
|
if (this.rivetsData.currJoy == 2) this.rivetsData.remappings.Joy_Mapping_Down = joyLast;
|
|
if (this.rivetsData.currJoy == 3) this.rivetsData.remappings.Joy_Mapping_Left = joyLast;
|
|
if (this.rivetsData.currJoy == 4) this.rivetsData.remappings.Joy_Mapping_Right = joyLast;
|
|
if (this.rivetsData.currJoy == 5) this.rivetsData.remappings.Joy_Mapping_Action_A = joyLast;
|
|
if (this.rivetsData.currJoy == 6) this.rivetsData.remappings.Joy_Mapping_Action_B = joyLast;
|
|
if (this.rivetsData.currJoy == 8) this.rivetsData.remappings.Joy_Mapping_Action_Start = joyLast;
|
|
if (this.rivetsData.currJoy == 9) this.rivetsData.remappings.Joy_Mapping_Menu = joyLast;
|
|
if (this.rivetsData.currJoy == 10) this.rivetsData.remappings.Joy_Mapping_Action_Z = joyLast;
|
|
if (this.rivetsData.currJoy == 11) this.rivetsData.remappings.Joy_Mapping_Action_L = joyLast;
|
|
if (this.rivetsData.currJoy == 12) this.rivetsData.remappings.Joy_Mapping_Action_R = joyLast;
|
|
}
|
|
this.rivetsData.remapWait = false;
|
|
}
|
|
|
|
async setupLogin() {
|
|
//prevent submit on enter
|
|
$('#txtPassword').bind("keypress", function (e) {
|
|
if (e.keyCode == 13) {
|
|
e.preventDefault();
|
|
myClass.loginSubmit();
|
|
return false;
|
|
}
|
|
});
|
|
|
|
let pw = localStorage.getItem('n64wasm-password');
|
|
if (pw==null)
|
|
this.rivetsData.password = '';
|
|
else
|
|
this.rivetsData.password = pw;
|
|
|
|
if (this.rivetsData.password){
|
|
await this.loginSilent();
|
|
}
|
|
|
|
}
|
|
|
|
loginModal(){
|
|
$("#loginModal").modal();
|
|
this.loginModalOpened = true;
|
|
setTimeout(() => {
|
|
//focus on textbox
|
|
$("#txtPassword").focus();
|
|
}, 500);
|
|
}
|
|
|
|
logout(){
|
|
this.rivetsData.loggedIn = false;
|
|
this.rivetsData.password = '';
|
|
localStorage.setItem('n64wasm-password', this.rivetsData.password);
|
|
}
|
|
|
|
async loginSubmit(){
|
|
$('#loginModal').modal('hide');
|
|
this.loginModalOpened = false;
|
|
let result = await this.loginToServer();
|
|
if (result=='Success'){
|
|
toastr.success('Logged In');
|
|
localStorage.setItem('n64wasm-password', this.rivetsData.password);
|
|
await this.getSaveStates();
|
|
this.postLoginProcess();
|
|
}
|
|
else{
|
|
toastr.error('Login Failed');
|
|
this.rivetsData.password = '';
|
|
localStorage.setItem('n64wasm-password', '');
|
|
}
|
|
}
|
|
|
|
async loginSilent(){
|
|
if (!this.rivetsData.showLogin)
|
|
return;
|
|
|
|
let result = await this.loginToServer();
|
|
if (result=='Success'){
|
|
await this.getSaveStates();
|
|
this.postLoginProcess();
|
|
}
|
|
}
|
|
|
|
postLoginProcess(){
|
|
//filter by .n64wasm extension and sort by date
|
|
this.rivetsData.n64SaveStates = this.allSaveStates.filter((state)=>{
|
|
return state.Name.endsWith('.n64wasm')
|
|
});
|
|
this.rivetsData.n64SaveStates.forEach(state => {
|
|
state.Date = this.convertCSharpDateTime(state.Date);
|
|
});
|
|
this.rivetsData.n64SaveStates.sort((a,b)=>{ return b.Date.getTime() - a.Date.getTime() });
|
|
this.rivetsData.loggedIn = true;
|
|
}
|
|
|
|
convertCSharpDateTime(initialDate) {
|
|
let dateString = initialDate;
|
|
dateString = dateString.substring(0, dateString.indexOf('T'));
|
|
let timeString = initialDate.substr(initialDate.indexOf("T") + 1);
|
|
let dateComponents = dateString.split('-');
|
|
let timeComponents = timeString.split(':');
|
|
let myDate = null;
|
|
|
|
myDate = new Date(parseInt(dateComponents[0]), parseInt(dateComponents[1]) - 1, parseInt(dateComponents[2]),
|
|
parseInt(timeComponents[0]), parseInt(timeComponents[1]), parseInt(timeComponents[2]));
|
|
return myDate;
|
|
}
|
|
|
|
async loginToServer(){
|
|
let result = await $.get(this.rivetsData.settings.CLOUDSAVEURL + '/Login?password=' + this.rivetsData.password);
|
|
console.log('login result: ' + result);
|
|
return result;
|
|
}
|
|
|
|
async getSaveStates(){
|
|
let result = await $.get(this.rivetsData.settings.CLOUDSAVEURL + '/GetSaveStates?password=' + this.rivetsData.password);
|
|
console.log('getSaveStates result: ', result);
|
|
this.allSaveStates = result;
|
|
result.forEach(element => {
|
|
if (element.Name==this.rom_name + ".n64wasm")
|
|
this.rivetsData.noCloudSave = false;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
reset(){
|
|
Module._neil_reset();
|
|
}
|
|
|
|
localCallback(){
|
|
}
|
|
|
|
|
|
}
|
|
let myClass = new MyClass();
|
|
window["myApp"] = myClass; //so that I can reference from EM_ASM
|
|
|
|
//add any post loading logic to the window object
|
|
if (window.postLoad)
|
|
{
|
|
window.postLoad();
|
|
}
|
|
|
|
window["Module"] = {
|
|
onRuntimeInitialized: myClass.initModule,
|
|
canvas: document.getElementById('canvas'),
|
|
print: (text) => myClass.processPrintStatement(text),
|
|
// printErr: (text) => myClass.print(text)
|
|
}
|
|
|
|
var rando2 = Math.floor(Math.random() * 100000);
|
|
var script2 = document.createElement('script');
|
|
script2.src = 'input_controller.js?v=' + rando2;
|
|
document.getElementsByTagName('head')[0].appendChild(script2);
|
|
|