mirror of
https://github.com/db48x/emularity.git
synced 2026-09-15 02:52:57 +00:00
pulled the current version of the code from loaderlab
This commit is contained in:
+26
-2
@@ -1,7 +1,11 @@
|
||||
var ar = new Array(33,34,35,36,37,38,39,40);
|
||||
|
||||
function getfullscreenenabler() {
|
||||
return canvas.webkitRequestFullScreen || canvas.mozRequestFullScreen || canvas.requestFullScreen;
|
||||
return canvas.webkitRequestFullScreen || canvas.mozRequestFullScreen || canvas.requestFullScreen;
|
||||
}
|
||||
|
||||
function getpointerlockenabler() {
|
||||
return canvas.requestPointerLock || canvas.mozRequestPointerLock || canvas.webkitRequestPointerLock;
|
||||
}
|
||||
|
||||
function isfullscreensupported() {
|
||||
@@ -67,6 +71,12 @@ window.onkeydown = keypress;
|
||||
emulator = new DOSBOX(canvas).setscale(get('scale') ? parseFloat(get('scale')) : 1)
|
||||
.setmodule(module)
|
||||
.setgame(getgameurl(loader_game));
|
||||
disableRightClickContextMenu(canvas);
|
||||
|
||||
// Emscripten doesn't use the proper prefixed functions for fullscreen requests,
|
||||
// so let's map the prefixed versions to the correct function.
|
||||
canvas.requestPointerLock = getpointerlockenabler();
|
||||
|
||||
if (get('autostart')) {
|
||||
emulator.start();
|
||||
}
|
||||
@@ -86,8 +96,22 @@ window.onkeydown = keypress;
|
||||
}
|
||||
|
||||
function getgameurl(game) {
|
||||
// NOTE: deliberately use cors.archive.org since this will 302 rewrite to iaXXXXX.us.archive.org/XX/items/...
|
||||
// and need to keep that "artificial" extra domain-ish name to avoid CORS issues with IE/Safari
|
||||
return (game === 'NONE') ? undefined
|
||||
: ('//archive.org/cors/'+ game);
|
||||
: ('//cors.archive.org/cors/'+ game);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the right click menu for the given element.
|
||||
*/
|
||||
function disableRightClickContextMenu(element) {
|
||||
element.addEventListener('contextmenu', function(e) {
|
||||
if (e.button == 2) {
|
||||
// Block right-click menu thru preventing default action.
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function switchgame(e) {
|
||||
|
||||
+231
-169
@@ -1,6 +1,7 @@
|
||||
var Module = null;
|
||||
|
||||
function DOSBOX(canvas, module, game, precallback, callback, scale) {
|
||||
var js_url;
|
||||
var moduledata;
|
||||
var requests = [];
|
||||
var drawloadingtimer;
|
||||
@@ -8,7 +9,9 @@ function DOSBOX(canvas, module, game, precallback, callback, scale) {
|
||||
var spinnerrot = 0;
|
||||
var splashimg = new Image();
|
||||
var spinnerimg = new Image();
|
||||
// TODO: Have an enum value that communicates the current state of DOSBOX, e.g. 'initializing', 'loading', 'running'.
|
||||
var has_started = false;
|
||||
var loading = false;
|
||||
var LOADING_TEXT;
|
||||
|
||||
var SAMPLE_RATE = (function () {
|
||||
@@ -20,6 +23,10 @@ function DOSBOX(canvas, module, game, precallback, callback, scale) {
|
||||
return sample.sampleRate.toString();
|
||||
}());
|
||||
|
||||
var can_start = function () {
|
||||
return !!canvas && !!module && !!game && !!scale && !has_started;
|
||||
};
|
||||
|
||||
this.setscale = function(_scale) {
|
||||
scale = _scale;
|
||||
try_start();
|
||||
@@ -48,128 +55,10 @@ function DOSBOX(canvas, module, game, precallback, callback, scale) {
|
||||
return this;
|
||||
};
|
||||
|
||||
try_start();
|
||||
|
||||
function try_start () {
|
||||
if (!can_start()) {
|
||||
return;
|
||||
}
|
||||
has_started = true;
|
||||
var config = fetch_file('ModuleInfo',
|
||||
'//archive.org/cors/jsmess_engine_v2/' + module + '.json',
|
||||
'text', true, true);
|
||||
config.then(function (data) {
|
||||
moduledata = data;
|
||||
window.addEventListener('keypress', keyevent);
|
||||
canvas.addEventListener('click', start);
|
||||
drawsplash();
|
||||
});
|
||||
}
|
||||
|
||||
function can_start() {
|
||||
return !!canvas && !!module && !!game && !!scale && !has_started;
|
||||
};
|
||||
|
||||
var keyevent = function(e) {
|
||||
if (typeof(loader_game)=='object') return; // game will start with click-to-play instead of [SPACE] char
|
||||
if (e.which == 32) {
|
||||
e.preventDefault();
|
||||
start();
|
||||
}
|
||||
};
|
||||
|
||||
var start = function() {
|
||||
window.removeEventListener('keypress', keyevent);
|
||||
canvas.removeEventListener('click', start);
|
||||
drawloadingtimer = window.setInterval(draw_loading_status, 1000/60);
|
||||
if (precallback) {
|
||||
window.setTimeout(precallback, 0);
|
||||
}
|
||||
|
||||
LOADING_TEXT = 'Parsing config';
|
||||
var modulecfg = JSON.parse(moduledata);
|
||||
|
||||
var nr = modulecfg['native_resolution'];
|
||||
DOSBOX.width = nr[0] * scale;
|
||||
DOSBOX.height = nr[1] * scale;
|
||||
|
||||
file_countdown = 2;
|
||||
|
||||
LOADING_TEXT = 'Downloading game data...';
|
||||
Promise.all([fetch_file('Metadata',
|
||||
get_meta_url(game),
|
||||
'document', true),
|
||||
fetch_file('Game',
|
||||
game)])
|
||||
.then(function(game_data) {
|
||||
LOADING_TEXT = 'Loading game file into file system';
|
||||
DOSBOX.BFSMountZip(new BrowserFS.BFSRequire('buffer').Buffer(game_file));
|
||||
Module = init_module(modulecfg, game_data[0], game_data[1]);
|
||||
if (modulecfg['js_filename']) {
|
||||
LOADING_TEXT = 'Launching DosBox';
|
||||
attach_script(modulecfg['js_filename']);
|
||||
} else {
|
||||
LOADING_TEXT = 'Invalid System Disk';
|
||||
}
|
||||
handle_mute();
|
||||
});
|
||||
return this;
|
||||
};
|
||||
this.start = start;
|
||||
//global hook to method (so can be invoked with a "click to play" image being clicked)
|
||||
window.DOSBOXstart = start;
|
||||
|
||||
function init_module(modulecfg, meta_file, game_file) {
|
||||
return { arguments: build_dosbox_arguments(modulecfg,
|
||||
meta_file.getElementsByTagName("emulator_start")
|
||||
.item(0)
|
||||
.textContent),
|
||||
screenIsReadOnly: true,
|
||||
print: function (text) { console.log(text); },
|
||||
canvas: canvas,
|
||||
noInitialRun: false,
|
||||
preInit: function () {
|
||||
window.clearInterval(drawloadingtimer);
|
||||
if (callback) {
|
||||
modulecfg.canvas = canvas;
|
||||
window.setTimeout(function() {
|
||||
callback(modulecfg);
|
||||
},
|
||||
0);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var drawsplash = function() {
|
||||
var context = canvas.getContext('2d');
|
||||
splashimg.onload = function() {
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
context.save();
|
||||
context.drawImage(splashimg,
|
||||
canvas.width / 2 - (splashimg.width / 2),
|
||||
canvas.height / 3 - (splashimg.height / 2));
|
||||
context.font = '18px sans-serif';
|
||||
context.fillStyle = 'Black';
|
||||
context.textAlign = 'center';
|
||||
context.fillText('Press the SPACEBAR to start.',
|
||||
canvas.width / 2,
|
||||
(canvas.height / 2) + (splashimg.height / 2));
|
||||
context.textAlign = 'start';
|
||||
context.restore();
|
||||
};
|
||||
spinnerimg.onload = function() {
|
||||
splashimg.src = '/images/dosbox.png';;
|
||||
};
|
||||
spinnerimg.src = '/images/spinner.png';
|
||||
};
|
||||
|
||||
var draw_loading_status = function() {
|
||||
var context = canvas.getContext('2d');
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
context.drawImage(splashimg,
|
||||
canvas.width / 2 - (splashimg.width / 2),
|
||||
canvas.height / 3 - (splashimg.height / 2));
|
||||
context.drawImage(splashimg, canvas.width / 2 - (splashimg.width / 2), canvas.height / 3 - (splashimg.height / 2));
|
||||
var spinnerpos = (canvas.height / 2 + splashimg.height / 2) + 16;
|
||||
context.save();
|
||||
context.translate((canvas.width / 2), spinnerpos);
|
||||
@@ -180,9 +69,7 @@ function DOSBOX(canvas, module, game, precallback, callback, scale) {
|
||||
context.font = '18px sans-serif';
|
||||
context.fillStyle = 'Black';
|
||||
context.textAlign = 'center';
|
||||
context.fillText(LOADING_TEXT,
|
||||
canvas.width / 2,
|
||||
(canvas.height / 2) + (splashimg.height / 4));
|
||||
context.fillText(LOADING_TEXT, canvas.width / 2, (canvas.height / 2) + (splashimg.height / 4));
|
||||
context.restore();
|
||||
spinnerrot += .25;
|
||||
};
|
||||
@@ -196,36 +83,60 @@ function DOSBOX(canvas, module, game, precallback, callback, scale) {
|
||||
}
|
||||
};
|
||||
|
||||
var fetch_file = function(title, url, rt, raw, unmanaged) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = rt ? rt : 'arraybuffer';
|
||||
xhr.onload = function(e) {
|
||||
if (xhr.status != 200) {
|
||||
return;
|
||||
}
|
||||
if (!unmanaged) {
|
||||
xhr.progress = 1.0;
|
||||
}
|
||||
resolve(raw ? xhr.response
|
||||
: new Int8Array(xhr.response));
|
||||
};
|
||||
xhr.onerror = reject;
|
||||
if (!unmanaged) {
|
||||
xhr.onprogress = progress_fetch_file;
|
||||
xhr.title = title;
|
||||
xhr.progress = 0;
|
||||
xhr.total = 0;
|
||||
xhr.loaded = 0;
|
||||
xhr.lengthComputable = false;
|
||||
requests.push(xhr);
|
||||
}
|
||||
xhr.send();
|
||||
});
|
||||
var fetch_file = function(title, url, cb, rt, raw, unmanaged) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = rt ? rt : 'arraybuffer';
|
||||
xhr.onload = function(e) {
|
||||
if (xhr.status != 200) {
|
||||
return;
|
||||
}
|
||||
if (!unmanaged) {
|
||||
xhr.progress = 1.0;
|
||||
}
|
||||
var ints = raw ? xhr.response : new Int8Array(xhr.response);
|
||||
cb(ints);
|
||||
};
|
||||
if (!unmanaged) {
|
||||
xhr.onprogress = progress_fetch_file;
|
||||
xhr.title = title;
|
||||
xhr.progress = 0;
|
||||
xhr.total = 0;
|
||||
xhr.loaded = 0;
|
||||
xhr.lengthComputable = false;
|
||||
requests.push(xhr);
|
||||
}
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
var update_countdown = function() {
|
||||
file_countdown -= 1;
|
||||
if (file_countdown <= 0) {
|
||||
loading = false;
|
||||
|
||||
if (js_url) {
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
var newScript = document.createElement('script');
|
||||
newScript.type = 'text/javascript';
|
||||
newScript.src = get_js_url(js_url);
|
||||
head.appendChild(newScript);
|
||||
}
|
||||
|
||||
// see archive.js for the mute/unmute button/JS
|
||||
if (!($.cookie && $.cookie('unmute'))){
|
||||
setTimeout(function(){
|
||||
// someone moved it from 1st to 2nd!
|
||||
if (DOSBOX && typeof(DOSBOX.sdl_pauseaudio)!='undefined')
|
||||
DOSBOX.sdl_pauseaudio(1);
|
||||
else if (typeof _SDL_PauseAudio !== "undefined")
|
||||
_SDL_PauseAudio(1);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var build_dosbox_arguments = function (config, emulator_start) {
|
||||
LOADING_TEXT = 'Building arguments';
|
||||
return ['/dosprogram/'+ emulator_start];
|
||||
};
|
||||
|
||||
@@ -235,33 +146,151 @@ function DOSBOX(canvas, module, game, precallback, callback, scale) {
|
||||
|
||||
var get_meta_url = function (game_path) {
|
||||
var path = game_path.split('/');
|
||||
return "//archive.org/cors/"+ path[4] +"/"+ path[4] +"_meta.xml";
|
||||
// NOTE: deliberately use cors.archive.org since this will 302 rewrite to iaXXXXX.us.archive.org/XX/items/...
|
||||
// and need to keep that "artificial" extra domain-ish name to avoid CORS issues with IE/Safari
|
||||
return "//cors.archive.org/cors/"+ path[4] +"/"+ path[4] +"_meta.xml";
|
||||
};
|
||||
|
||||
var get_js_url = function (js_filename) {
|
||||
return "//archive.org/cors/jsmess_engine_v2/"+ js_filename;
|
||||
// NOTE: deliberately use cors.archive.org since this will 302 rewrite to iaXXXXX.us.archive.org/XX/items/...
|
||||
// and need to keep that "artificial" extra domain-ish name to avoid CORS issues with IE/Safari
|
||||
return "//cors.archive.org/cors/jsmess_engine_v2/"+ js_filename;
|
||||
};
|
||||
|
||||
function attach_script(url) {
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
var newScript = document.createElement('script');
|
||||
newScript.type = 'text/javascript';
|
||||
newScript.src = get_js_url(url);
|
||||
head.appendChild(newScript);
|
||||
var init_module = function() {
|
||||
LOADING_TEXT = 'Loading Program';
|
||||
var modulecfg = JSON.parse(moduledata);
|
||||
js_url = modulecfg['js_filename'];
|
||||
|
||||
var game_file = null,
|
||||
meta_file = null;
|
||||
|
||||
var nr = modulecfg['native_resolution'];
|
||||
DOSBOX.width = nr[0] * scale;
|
||||
DOSBOX.height = nr[1] * scale;
|
||||
|
||||
// Makes the keyboard 'focusable' to let the canvas accept keyboard input.
|
||||
// http://gamedev.stackexchange.com/questions/50223/receiving-keyboard-events-on-a-canvas-in-javascript
|
||||
canvas.setAttribute('tabindex', '0');
|
||||
// Emscripten blocks the 'default action' of all mouse events, which
|
||||
// prevents users from selecting the canvas for keyboard input!
|
||||
// Prevent Emscripten from blocking users from selecting the canvas by
|
||||
// manually 'focusing' the canvas when it is clicked.
|
||||
canvas.addEventListener('mousedown', function() {
|
||||
canvas.focus();
|
||||
});
|
||||
// Start the canvas focused.
|
||||
canvas.focus();
|
||||
|
||||
Module = {
|
||||
arguments: undefined,
|
||||
screenIsReadOnly: true,
|
||||
print: (function() {
|
||||
return function(text) {
|
||||
console.log(text);
|
||||
};
|
||||
})(),
|
||||
canvas: canvas,
|
||||
// Prevent Emscripten from listening / blocking key events to the rest of the page by
|
||||
// isolating keyboard input to the canvas.
|
||||
keyboardListeningElement: canvas,
|
||||
noInitialRun: false,
|
||||
preInit: function() {
|
||||
Module.arguments = build_dosbox_arguments(modulecfg,
|
||||
meta_file.getElementsByTagName("emulator_start")
|
||||
.item(0)
|
||||
.textContent);;
|
||||
LOADING_TEXT = 'Loading game file into file system';
|
||||
DOSBOX.BFSMountZip(game_file);
|
||||
DOSBOX.moveConfigToRoot();
|
||||
window.clearInterval(drawloadingtimer);
|
||||
if (callback) {
|
||||
modulecfg.canvas = canvas;
|
||||
window.setTimeout(function() { callback(modulecfg); }, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
file_countdown = 2;
|
||||
|
||||
fetch_file('Metadata',
|
||||
get_meta_url(game),
|
||||
function(data) {
|
||||
meta_file = data;
|
||||
update_countdown();
|
||||
},
|
||||
'document', true);
|
||||
fetch_file('Game',
|
||||
game,
|
||||
function(data) {
|
||||
game_file = new BrowserFS.BFSRequire('buffer').Buffer(data);
|
||||
update_countdown();
|
||||
});
|
||||
};
|
||||
|
||||
var keyevent = function(e) {
|
||||
if (typeof(loader_game)=='object') return; // game will start with click-to-play instead of [SPACE] char
|
||||
if (e.which == 32) {
|
||||
e.preventDefault();
|
||||
start();
|
||||
}
|
||||
};
|
||||
|
||||
var start = function() {
|
||||
// Prevent loading the game multiple times.
|
||||
if (loading) {
|
||||
return false;
|
||||
}
|
||||
window.removeEventListener('keypress', keyevent);
|
||||
canvas.removeEventListener('click', start);
|
||||
loading = true;
|
||||
drawloadingtimer = window.setInterval(draw_loading_status, 1000/60);
|
||||
if (precallback) {
|
||||
window.setTimeout(precallback, 0);
|
||||
}
|
||||
init_module();
|
||||
return this;
|
||||
};
|
||||
this.start = start;
|
||||
window.DOSBOXstart = start;//global hook to method (so can be invoked with a "click to play" image being clicked)
|
||||
|
||||
var drawsplash = function() {
|
||||
var context = canvas.getContext('2d');
|
||||
splashimg.onload = function(){
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
context.save();
|
||||
context.drawImage(splashimg, canvas.width / 2 - (splashimg.width / 2), canvas.height / 3 - (splashimg.height / 2));
|
||||
context.font = '18px sans-serif';
|
||||
context.fillStyle = 'Black';
|
||||
context.textAlign = 'center';
|
||||
context.fillText('Click here to start', canvas.width / 2, (canvas.height / 2) + (splashimg.height / 2));
|
||||
context.textAlign = 'start';
|
||||
context.restore();
|
||||
};
|
||||
spinnerimg.onload = function() {
|
||||
splashimg.src = '/images/dosbox.png';;
|
||||
};
|
||||
spinnerimg.src = '/images/spinner.png';
|
||||
};
|
||||
|
||||
var configLoaded = function (data) {
|
||||
moduledata = data;
|
||||
window.addEventListener('keypress', keyevent);
|
||||
canvas.addEventListener('click', start);
|
||||
drawsplash();
|
||||
};
|
||||
|
||||
function try_start () {
|
||||
if (!can_start()) {
|
||||
return;
|
||||
}
|
||||
has_started = true;
|
||||
// NOTE: deliberately use cors.archive.org since this will 302 rewrite to iaXXXXX.us.archive.org/XX/items/jsmess_engine_v2/...json
|
||||
// and need to keep that "artificial" extra domain-ish name to avoid CORS issues with IE/Safari
|
||||
fetch_file('ModuleInfo', '//cors.archive.org/cors/jsmess_engine_v2/' + module + '.json', configLoaded, 'text', true, true);
|
||||
}
|
||||
|
||||
function handle_mute() {
|
||||
// see archive.js for the mute/unmute button/JS
|
||||
if (!($.cookie && $.cookie('unmute'))){
|
||||
setTimeout(function(){
|
||||
// someone moved it from 1st to 2nd!
|
||||
if (DOSBOX && typeof(DOSBOX.sdl_pauseaudio)!='undefined')
|
||||
DOSBOX.sdl_pauseaudio(1);
|
||||
else if (typeof _SDL_PauseAudio !== "undefined")
|
||||
_SDL_PauseAudio(1);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
try_start();
|
||||
}
|
||||
|
||||
DOSBOX._readySet = false;
|
||||
@@ -347,3 +376,36 @@ DOSBOX.recursiveCopy = function recursiveCopy(oldDir, newDir) {
|
||||
fs.writeFileSync(newFile, fs.readFileSync(oldFile));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Searches for dosbox.conf, and moves it to '/dosbox.conf' so dosbox uses it.
|
||||
*/
|
||||
DOSBOX.moveConfigToRoot = function moveConfigToRoot() {
|
||||
if (typeof FS !== 'undefined') {
|
||||
var dosboxConfPath = null;
|
||||
// Recursively search for dosbox.conf.
|
||||
function searchDirectory(dirPath) {
|
||||
FS.readdir(dirPath).forEach(function(item) {
|
||||
// Avoid infinite recursion by ignoring these entries, which exist at
|
||||
// the root.
|
||||
if (item === '.' || item === '..') {
|
||||
return;
|
||||
}
|
||||
// Append '/' between dirPath and the item's name... unless dirPath
|
||||
// already ends in it (which always occurs if dirPath is the root, '/').
|
||||
var itemPath = dirPath + (dirPath[dirPath.length - 1] !== '/' ? "/" : "") + item,
|
||||
itemStat = FS.stat(itemPath);
|
||||
if (FS.isDir(itemStat.mode)) {
|
||||
searchDirectory(itemPath);
|
||||
} else if (item === 'dosbox.conf') {
|
||||
dosboxConfPath = itemPath;
|
||||
}
|
||||
});
|
||||
}
|
||||
searchDirectory('/');
|
||||
|
||||
if (dosboxConfPath !== null) {
|
||||
FS.writeFile('/dosbox.conf', FS.readFile(dosboxConfPath), { encoding: 'binary' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
var ar = new Array(33,34,35,36,37,38,39,40);
|
||||
|
||||
function getfullscreenenabler() {
|
||||
return canvas.webkitRequestFullScreen || canvas.mozRequestFullScreen || canvas.requestFullScreen;
|
||||
}
|
||||
|
||||
function isfullscreensupported() {
|
||||
return !!(getfullscreenenabler());
|
||||
}
|
||||
|
||||
function gofullscreen() {
|
||||
Module.requestFullScreen(1,0);
|
||||
}
|
||||
|
||||
function keypress(e) {
|
||||
if (typeof(loader_game)=='object' && !loader_game.started)
|
||||
return true; // Don't ignore certain keys yet (until game started by "click to play")
|
||||
|
||||
var key = e.which;
|
||||
if($.inArray(key,ar) > -1) {
|
||||
e.preventDefault(); //Don't let arrow, pg up/down, home, end affect page position
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
window.onkeydown = keypress;
|
||||
|
||||
(function() {
|
||||
function get(name) {
|
||||
if(typeof(loader_game)=='object')
|
||||
return loader_game[name]; //alternate case where dont have CGI args to parse...
|
||||
if(name=(new RegExp('[?&]'+encodeURIComponent(name)+'=([^&]*)')).exec(location.search)) {
|
||||
return decodeURIComponent(name[1]);
|
||||
}
|
||||
}
|
||||
|
||||
var games;
|
||||
var mess;
|
||||
var module;
|
||||
|
||||
function getmodule() {
|
||||
module = get('module');
|
||||
module = module ? module : 'test';
|
||||
}
|
||||
|
||||
function init() {
|
||||
getmodule();
|
||||
ready();
|
||||
}
|
||||
|
||||
function ready() {
|
||||
var fullscreenbutton = document.getElementById('gofullscreen')
|
||||
if (isfullscreensupported()) {
|
||||
fullscreenbutton.addEventListener('click', gofullscreen);
|
||||
if ('onfullscreenchange' in document) {
|
||||
document.addEventListener('fullscreenchange', JSMESS.fullScreenChangeHandler);
|
||||
} else if ('onmozfullscreenchange' in document) {
|
||||
document.addEventListener('mozfullscreenchange', JSMESS.fullScreenChangeHandler);
|
||||
} else if ('onwebkitfullscreenchange' in document) {
|
||||
document.addEventListener('webkitfullscreenchange', JSMESS.fullScreenChangeHandler);
|
||||
}
|
||||
} else {
|
||||
fullscreenbutton.disabled = true;
|
||||
}
|
||||
var canvas = document.getElementById('canvas');
|
||||
mess = new JSMESS(canvas)
|
||||
.setscale(get('scale') ? parseFloat(get('scale')) : 1)
|
||||
.setmodule(module)
|
||||
setgame(loader_game);
|
||||
if (get('autostart')) {
|
||||
mess.start();
|
||||
}
|
||||
// Gamepad text
|
||||
if (detectgamepadsupport()) {
|
||||
var gamepadDiv = document.getElementById('gamepadtext');
|
||||
gamepadDiv.innerHTML = "No gamepads detected. Press a button on a gamepad to use it.";
|
||||
listenforgamepads(function(gamepads, newgamepad) {
|
||||
var s = (gamepads.length === 1 ? '' : 's');
|
||||
gamepadDiv.innerHTML = gamepads.length + ' gamepad'+s+' detected. If the game does not ' +
|
||||
'respond to your gamepad'+s+', refresh the browser and try again.';
|
||||
if (mess.hasStarted) {
|
||||
gamepadDiv.innerHTML += "<br />Restart MESS to use new gamepads.";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setgame(game) {
|
||||
game = (game == 'NONE') ? undefined : game;
|
||||
// NOTE: deliberately use cors.archive.org since this will 302 rewrite to iaXXXXX.us.archive.org/XX/items/...
|
||||
// and need to keep that "artificial" extra domain-ish name to avoid CORS issues with IE/Safari
|
||||
mess.setgame(game ? '//cors.archive.org/cors/'+ game : undefined);
|
||||
}
|
||||
|
||||
function switchgame(e) {
|
||||
setgame(e.target.value);
|
||||
}
|
||||
|
||||
// Firefox will not give us Joystick data unless we register this NOP
|
||||
// callback.
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=936104
|
||||
addEventListener("gamepadconnected", function() {});
|
||||
var getgamepads = navigator.getGamepads || navigator.webkitGamepads ||
|
||||
navigator.mozGamepads || navigator.gamepads || navigator.webkitGetGamepads;
|
||||
/**
|
||||
* Does the current browser support the Gamepad API?
|
||||
* Returns a boolean.
|
||||
*/
|
||||
function detectgamepadsupport() {
|
||||
return typeof getgamepads === 'function';
|
||||
}
|
||||
// The timer that listens for gamepads, in case we ever want to stop it.
|
||||
var gamepadlistener;
|
||||
/**
|
||||
* Listens for new gamepads, and triggers the callback when it detects a
|
||||
* change.
|
||||
* The callback is passed an array of active gamepads.
|
||||
*/
|
||||
function listenforgamepads(cb, freq) {
|
||||
// NOP if the browser doesn't support gamepads.
|
||||
if (!detectgamepadsupport()) return;
|
||||
// Map from gamepad id to gamepad information.
|
||||
var prevgamepads = {};
|
||||
// DEFAULT: Check gamepads every second.
|
||||
if (typeof freq === 'undefined') freq = 1000;
|
||||
gamepadlistener = setInterval(function() {
|
||||
// Browsers get cranky when you don't apply this on the navigator object.
|
||||
var gamepads = getgamepads.apply(navigator);
|
||||
var currentgamepads = {};
|
||||
var i;
|
||||
var hasChanged = false;
|
||||
for (i = 0; i < gamepads.length; i++) {
|
||||
var gamepad = gamepads[i];
|
||||
if (gamepad != null) {
|
||||
currentgamepads[gamepad.id] = gamepad;
|
||||
if (!prevgamepads.hasOwnProperty(gamepad.id)) {
|
||||
// Gamepad has been added.
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Has a gamepad been removed?
|
||||
if (!hasChanged) {
|
||||
for (var gamepadid in prevgamepads) {
|
||||
if (!currentgamepads.hasOwnProperty(gamepadid)) {
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prevgamepads = currentgamepads;
|
||||
|
||||
if (hasChanged) {
|
||||
// Actual gamepads, filtered from gamepads. Chrome puts empty items into
|
||||
// its gamepadlist.
|
||||
var actualgamepads = [];
|
||||
for (i = 0; i < gamepads.length; i++) {
|
||||
if (gamepads[i] != null) actualgamepads.push(gamepads[i]);
|
||||
}
|
||||
cb(actualgamepads);
|
||||
}
|
||||
}, freq);
|
||||
}
|
||||
|
||||
window.addEventListener('load', init);
|
||||
})();
|
||||
@@ -0,0 +1,394 @@
|
||||
var Module = null;
|
||||
|
||||
function JSMESS(canvas, module, game, precallback, callback, scale) {
|
||||
var js_data;
|
||||
var moduledata;
|
||||
var requests = [];
|
||||
var drawloadingtimer;
|
||||
var file_countdown;
|
||||
var spinnerrot = 0;
|
||||
var splashimg = new Image();
|
||||
var spinnerimg = new Image();
|
||||
var has_started = false;
|
||||
var loading = false;
|
||||
var LOADING_TEXT;
|
||||
|
||||
var SAMPLE_RATE = (function () {
|
||||
var audio_ctx = window.AudioContext || window.webkitAudioContext || false;
|
||||
if (!audio_ctx) {
|
||||
return false;
|
||||
}
|
||||
var sample = new audio_ctx;
|
||||
return sample.sampleRate.toString();
|
||||
}());
|
||||
|
||||
var can_start = function () {
|
||||
return !!canvas && !!module && !!game && !!scale && !has_started
|
||||
};
|
||||
|
||||
this.setscale = function(_scale) {
|
||||
scale = _scale;
|
||||
try_start();
|
||||
return this;
|
||||
}
|
||||
|
||||
this.setprecallback = function(_precallback) {
|
||||
precallback = _precallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
this.setcallback = function(_callback) {
|
||||
callback = _callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
this.setmodule = function(_module) {
|
||||
module = _module;
|
||||
try_start();
|
||||
return this;
|
||||
}
|
||||
|
||||
this.setgame = function(_game) {
|
||||
game = _game;
|
||||
try_start();
|
||||
return this;
|
||||
}
|
||||
|
||||
var draw_loading_status = function() {
|
||||
var context = canvas.getContext('2d');
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
context.drawImage(splashimg, canvas.width / 2 - (splashimg.width / 2), canvas.height / 3 - (splashimg.height / 2));
|
||||
var spinnerpos = (canvas.height / 2 + splashimg.height / 2) + 16;
|
||||
context.save();
|
||||
context.translate((canvas.width / 2), spinnerpos);
|
||||
context.rotate(spinnerrot);
|
||||
context.drawImage(spinnerimg, -(64/2), -(64/2), 64, 64);
|
||||
context.restore();
|
||||
context.save();
|
||||
context.font = '18px sans-serif';
|
||||
context.fillStyle = 'Black';
|
||||
context.textAlign = 'center';
|
||||
context.fillText(LOADING_TEXT, canvas.width / 2, (canvas.height / 2) + (splashimg.height / 4));
|
||||
context.restore();
|
||||
spinnerrot += .25;
|
||||
};
|
||||
|
||||
var progress_fetch_file = function(e) {
|
||||
if (e.lengthComputable) {
|
||||
e.target.progress = e.loaded / e.total;
|
||||
e.target.loaded = e.loaded;
|
||||
e.target.total = e.total;
|
||||
e.target.lengthComputable = e.lengthComputable;
|
||||
}
|
||||
};
|
||||
|
||||
var fetch_file = function(title, url, cb, rt, raw, unmanaged) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = rt ? rt : 'arraybuffer';
|
||||
xhr.onload = function(e) {
|
||||
if (xhr.status != 200) {
|
||||
return;
|
||||
}
|
||||
if (!unmanaged) {
|
||||
xhr.progress = 1.0;
|
||||
}
|
||||
var ints = raw ? xhr.response : new Int8Array(xhr.response);
|
||||
cb(ints);
|
||||
};
|
||||
if (!unmanaged) {
|
||||
xhr.onprogress = progress_fetch_file;
|
||||
xhr.title = title;
|
||||
xhr.progress = 0;
|
||||
xhr.total = 0;
|
||||
xhr.loaded = 0;
|
||||
xhr.lengthComputable = false;
|
||||
requests.push(xhr);
|
||||
}
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
var update_countdown = function() {
|
||||
file_countdown -= 1
|
||||
if (file_countdown <= 0) {
|
||||
loading = false;
|
||||
var headID = document.getElementsByTagName('head')[0];
|
||||
var newScript = document.createElement('script');
|
||||
newScript.type = 'text/javascript';
|
||||
newScript.text = js_data;
|
||||
headID.appendChild(newScript);
|
||||
|
||||
// see archive.js for the mute/unmute button/JS
|
||||
if (!($.cookie && $.cookie('unmute'))){
|
||||
setTimeout(function(){
|
||||
// someone moved it from 1st to 2nd!
|
||||
if (JSMESS && typeof(JSMESS.sdl_pauseaudio)!='undefined')
|
||||
JSMESS.sdl_pauseaudio(1);
|
||||
else if (_SDL_PauseAudio)
|
||||
_SDL_PauseAudio(1);
|
||||
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var build_mess_arguments = function (config) {
|
||||
LOADING_TEXT = 'Building arguments';
|
||||
var nr = config['native_resolution'];
|
||||
// see archive.js for the mute/unmute button/JS
|
||||
var muted = (!(typeof($.cookie)!='undefined' && $.cookie('unmute')));
|
||||
|
||||
var args = [
|
||||
config['driver'],
|
||||
'-verbose',
|
||||
'-rompath','.',
|
||||
'-window',
|
||||
'-resolution', nr[0]+'x' + nr[1],
|
||||
'-nokeepaspect'
|
||||
];
|
||||
|
||||
if (config.autoboot) {
|
||||
args.push('-autoboot_command');
|
||||
}
|
||||
|
||||
if (muted){
|
||||
args.push('-sound', 'none');
|
||||
} else if (SAMPLE_RATE) {
|
||||
args.push('-samplerate', SAMPLE_RATE);
|
||||
}
|
||||
|
||||
if (game) {
|
||||
args.push('-' + config['peripherals'][0], game.replace(/\//g,'_'))
|
||||
}
|
||||
|
||||
if (config['extra_args']) {
|
||||
args = args.concat(config['extra_args'])
|
||||
}
|
||||
|
||||
return args
|
||||
};
|
||||
|
||||
var build_mame_arguments = function (config) {
|
||||
LOADING_TEXT = 'Building arguments';
|
||||
var nr = config['native_resolution'];
|
||||
// see archive.js for the mute/unmute button/JS
|
||||
var muted = (!(typeof($.cookie)!='undefined' && $.cookie('unmute')));
|
||||
|
||||
var args = [
|
||||
config['driver'],
|
||||
'-verbose',
|
||||
'-rompath','.',
|
||||
'-window',
|
||||
'-resolution', nr[0]+'x' + nr[1],
|
||||
'-nokeepaspect'
|
||||
];
|
||||
|
||||
if (muted){
|
||||
args.push('-sound', 'none');
|
||||
} else if (SAMPLE_RATE) {
|
||||
args.push('-samplerate', SAMPLE_RATE);
|
||||
}
|
||||
|
||||
if (config['extra_args']) {
|
||||
args = args.concat(config['extra_args'])
|
||||
}
|
||||
|
||||
return args
|
||||
};
|
||||
|
||||
get_game_name = function (game_path) {
|
||||
return game_path.split('/').pop();
|
||||
};
|
||||
|
||||
var init_module = function() {
|
||||
LOADING_TEXT = 'Parsing config';
|
||||
var modulecfg = JSON.parse(moduledata);
|
||||
|
||||
var game_file = null;
|
||||
var keymap = null;
|
||||
var bios_filenames = modulecfg['bios_filenames'];
|
||||
var bios_files = {};
|
||||
|
||||
var nr = modulecfg['native_resolution'];
|
||||
|
||||
JSMESS.width = nr[0] * scale;
|
||||
JSMESS.height = nr[1] * scale;
|
||||
|
||||
var use_mame = parseInt(modulecfg['arcade'], 10);
|
||||
var arguments;
|
||||
|
||||
if (use_mame) {
|
||||
arguments = build_mame_arguments(modulecfg);
|
||||
} else {
|
||||
arguments = build_mess_arguments(modulecfg);
|
||||
}
|
||||
|
||||
Module = {
|
||||
arguments: arguments,
|
||||
screenIsReadOnly: true,
|
||||
print: (function() {
|
||||
return function(text) {
|
||||
console.log(text);
|
||||
};
|
||||
})(),
|
||||
canvas: canvas,
|
||||
noInitialRun: false,
|
||||
preInit: function() {
|
||||
LOADING_TEXT = 'Loading binary files into file system';
|
||||
// Load the downloaded binary files into the filesystem.
|
||||
for (var bios_fname in bios_files) {
|
||||
if (bios_files.hasOwnProperty(bios_fname)) {
|
||||
Module['FS_createDataFile']('/', bios_fname, bios_files[bios_fname], true, true);
|
||||
}
|
||||
}
|
||||
if (game && !use_mame) {
|
||||
LOADING_TEXT = 'Loading game file into file system';
|
||||
Module['FS_createDataFile']('/', game.replace(/\//g,'_'), game_file, true, true);
|
||||
}
|
||||
Module['FS_createFolder']('/', 'cfg', true, true);
|
||||
Module['FS_createDataFile']('/cfg', modulecfg['driver'] + '.cfg', keymap, true, true);
|
||||
window.clearInterval(drawloadingtimer);
|
||||
if (callback) {
|
||||
modulecfg.canvas = canvas;
|
||||
window.setTimeout(function() {callback(modulecfg)}, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bios_filenames = bios_filenames.filter(String);
|
||||
file_countdown = bios_filenames.length + (game ? 1 : 0) + 2
|
||||
|
||||
// Fetch the BIOS and the game we want to run.
|
||||
LOADING_TEXT = 'Fetching BIOS and Game';
|
||||
if (!use_mame) {
|
||||
for (var i=0; i < bios_filenames.length; i++) {
|
||||
var fname = bios_filenames[i];
|
||||
// NOTE: deliberately use cors.archive.org since this will 302 rewrite to iaXXXXX.us.archive.org/XX/items/...
|
||||
// and need to keep that "artificial" extra domain-ish name to avoid CORS issues with IE/Safari
|
||||
fetch_file('Bios', '//cors.archive.org/cors/jsmess_bios_v2/' + fname, function(data) { bios_files[fname] = data; update_countdown(); });
|
||||
}
|
||||
} else {
|
||||
fetch_file('Bios', game, function (data) { bios_files[get_game_name(game)] = data; update_countdown(); });
|
||||
}
|
||||
|
||||
if (game && !use_mame) {
|
||||
fetch_file('Game', game, function(data) { game_file = data; update_countdown(); });
|
||||
}
|
||||
|
||||
// NOTE: deliberately use cors.archive.org since this will 302 rewrite to iaXXXXX.us.archive.org/XX/items/...
|
||||
// and need to keep that "artificial" extra domain-ish name to avoid CORS issues with IE/Safari
|
||||
fetch_file('Keymap', '//cors.archive.org/cors/jsmess_config_v2/' + modulecfg['driver'] + '.cfg', function(data) { keymap = data; update_countdown(); }, 'text', true, true);
|
||||
fetch_file('Javascript', '//cors.archive.org/cors/jsmess_engine_v2/' + modulecfg['js_filename'], function(data) { js_data = data; update_countdown(); }, 'text', true);
|
||||
|
||||
};
|
||||
|
||||
var keyevent = function(e) {
|
||||
if (typeof(loader_game)=='object') return; // game will start with click-to-play instead of [SPACE] char
|
||||
if (e.which == 32) {
|
||||
e.preventDefault();
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
var start = function() {
|
||||
window.removeEventListener('keypress', keyevent);
|
||||
canvas.removeEventListener('click', start);
|
||||
loading = true;
|
||||
drawloadingtimer = window.setInterval(draw_loading_status, 1000/60);
|
||||
if (precallback) {
|
||||
window.setTimeout(function() {precallback()}, 0);
|
||||
}
|
||||
init_module();
|
||||
return this;
|
||||
}
|
||||
this.start = start;
|
||||
window.JSMESSstart = start;//global hook to method (so can be invoked with a "click to play" image being clicked)
|
||||
|
||||
var drawsplash = function() {
|
||||
var context = canvas.getContext('2d');
|
||||
splashimg.onload = function(){
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
context.save();
|
||||
context.drawImage(splashimg, canvas.width / 2 - (splashimg.width / 2), canvas.height / 3 - (splashimg.height / 2));
|
||||
context.font = '18px sans-serif';
|
||||
context.fillStyle = 'Black';
|
||||
context.textAlign = 'center';
|
||||
context.fillText('Click here to start', canvas.width / 2, (canvas.height / 2) + (splashimg.height / 2));
|
||||
context.textAlign = 'start';
|
||||
context.restore();
|
||||
};
|
||||
spinnerimg.onload = function() {
|
||||
var use_mame = parseInt(JSON.parse(moduledata).arcade, 10);
|
||||
var src;
|
||||
if (use_mame) {
|
||||
src = '/images/mame.png';
|
||||
} else {
|
||||
src = '/images/mess.png';
|
||||
}
|
||||
splashimg.src = src;
|
||||
}
|
||||
spinnerimg.src = '/images/spinner.png';
|
||||
}
|
||||
|
||||
var configLoaded = function (data) {
|
||||
moduledata = data;
|
||||
window.addEventListener('keypress', keyevent);
|
||||
canvas.addEventListener('click', start);
|
||||
drawsplash();
|
||||
};
|
||||
|
||||
function try_start () {
|
||||
if (!can_start()) {
|
||||
return;
|
||||
}
|
||||
has_started = true;
|
||||
// NOTE: deliberately use cors.archive.org since this will 302 rewrite to iaXXXXX.us.archive.org/XX/items/jsmess_engine_v2/...json
|
||||
// and need to keep that "artificial" extra domain-ish name to avoid CORS issues with IE/Safari
|
||||
fetch_file('ModuleInfo', '//cors.archive.org/cors/jsmess_engine_v2/' + module + '.json', configLoaded, 'text', true, true);
|
||||
}
|
||||
|
||||
try_start();
|
||||
}
|
||||
|
||||
JSMESS._readySet = false;
|
||||
|
||||
JSMESS._readyList = [];
|
||||
|
||||
JSMESS._runReadies = function() {
|
||||
if (JSMESS._readyList) {
|
||||
for (var r=0; r < JSMESS._readyList.length; r++) {
|
||||
JSMESS._readyList[r].call(window, []);
|
||||
};
|
||||
JSMESS._readyList = [];
|
||||
};
|
||||
};
|
||||
|
||||
JSMESS._readyCheck = function() {
|
||||
if (JSMESS.running) {
|
||||
JSMESS._runReadies();
|
||||
} else {
|
||||
JSMESS._readySet = setTimeout(JSMESS._readyCheck, 10);
|
||||
};
|
||||
};
|
||||
|
||||
JSMESS.ready = function(r) {
|
||||
if (JSMESS.running) {
|
||||
r.call(window, []);
|
||||
} else {
|
||||
JSMESS._readyList.push(function() { canvas.style.width = JSMESS.width + 'px'; canvas.style.height = JSMESS.height + 'px'; } );
|
||||
if (!(JSMESS._readySet)) {
|
||||
JSMESS._readyCheck();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
JSMESS.setScale = function() {
|
||||
Module.canvas.style.width = JSMESS.width + 'px';
|
||||
Module.canvas.style.height = JSMESS.height + 'px';
|
||||
};
|
||||
|
||||
JSMESS.fullScreenChangeHandler = function() {
|
||||
if (!(document.mozFullScreenElement || document.fullScreenElement)) {
|
||||
setTimeout(JSMESS.setScale, 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user