342 lines
9.8 KiB
HTML
342 lines
9.8 KiB
HTML
<html>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
<title>GBA.js</title>
|
||
<link rel="stylesheet" href="resources/main.css">
|
||
<script src="js/util.js"></script>
|
||
<script src="js/core.js"></script>
|
||
<script src="js/arm.js"></script>
|
||
<script src="js/thumb.js"></script>
|
||
<script src="js/mmu.js"></script>
|
||
<script src="js/io.js"></script>
|
||
<script src="js/audio.js"></script>
|
||
<script src="js/video.js"></script>
|
||
<script src="js/video/proxy.js"></script>
|
||
<script src="js/video/software.js"></script>
|
||
<script src="js/irq.js"></script>
|
||
<script src="js/keypad.js"></script>
|
||
<script src="js/sio.js"></script>
|
||
<script src="js/savedata.js"></script>
|
||
<script src="js/gpio.js"></script>
|
||
<script src="js/gba.js"></script>
|
||
<script src="resources/xhr.js"></script>
|
||
|
||
<script>
|
||
var gba;
|
||
var runCommands = [];
|
||
var debug = null;
|
||
|
||
try {
|
||
gba = new GameBoyAdvance();
|
||
gba.keypad.eatInput = true;
|
||
gba.setLogger(function(level, error) {
|
||
console.log(error);
|
||
gba.pause();
|
||
var screen = document.getElementById('screen');
|
||
if (screen.getAttribute('class') == 'dead') {
|
||
console.log('We appear to have crashed multiple times without reseting.');
|
||
return;
|
||
}
|
||
var crash = document.createElement('img');
|
||
crash.setAttribute('id', 'crash');
|
||
crash.setAttribute('src', 'resources/crash.png');
|
||
screen.parentElement.insertBefore(crash, screen);
|
||
screen.setAttribute('class', 'dead');
|
||
});
|
||
} catch (exception) {
|
||
gba = null;
|
||
}
|
||
|
||
window.onload = function() {
|
||
if (gba && FileReader) {
|
||
var canvas = document.getElementById('screen');
|
||
gba.setCanvas(canvas);
|
||
|
||
gba.logLevel = gba.LOG_ERROR;
|
||
|
||
loadRom('resources/bios.bin', function(bios) {
|
||
gba.setBios(bios);
|
||
});
|
||
|
||
if (!gba.audio.context) {
|
||
// Remove the sound box if sound isn't available
|
||
var soundbox = document.getElementById('sound');
|
||
soundbox.parentElement.removeChild(soundbox);
|
||
}
|
||
|
||
if (window.navigator.appName == 'Microsoft Internet Explorer') {
|
||
// Remove the pixelated option if it doesn't work
|
||
var pixelatedBox = document.getElementById('pixelated');
|
||
pixelatedBox.parentElement.removeChild(pixelatedBox);
|
||
}
|
||
} else {
|
||
var dead = document.getElementById('controls');
|
||
dead.parentElement.removeChild(dead);
|
||
}
|
||
}
|
||
|
||
function fadeOut(id, nextId, kill) {
|
||
var e = document.getElementById(id);
|
||
var e2 = document.getElementById(nextId);
|
||
if (!e) {
|
||
return;
|
||
}
|
||
var removeSelf = function() {
|
||
if (kill) {
|
||
e.parentElement.removeChild(e);
|
||
} else {
|
||
e.setAttribute('class', 'dead');
|
||
e.removeEventListener('webkitTransitionEnd', removeSelf);
|
||
e.removeEventListener('oTransitionEnd', removeSelf);
|
||
e.removeEventListener('transitionend', removeSelf);
|
||
}
|
||
if (e2) {
|
||
e2.setAttribute('class', 'hidden');
|
||
setTimeout(function() {
|
||
e2.removeAttribute('class');
|
||
}, 0);
|
||
}
|
||
}
|
||
|
||
e.addEventListener('webkitTransitionEnd', removeSelf, false);
|
||
e.addEventListener('oTransitionEnd', removeSelf, false);
|
||
e.addEventListener('transitionend', removeSelf, false);
|
||
e.setAttribute('class', 'hidden');
|
||
}
|
||
|
||
function run(file) {
|
||
var dead = document.getElementById('loader');
|
||
dead.value = '';
|
||
var load = document.getElementById('select');
|
||
load.textContent = 'Loading...';
|
||
load.removeAttribute('onclick');
|
||
var pause = document.getElementById('pause');
|
||
pause.textContent = "PAUSE";
|
||
gba.loadRomFromFile(file, function(result) {
|
||
if (result) {
|
||
for (var i = 0; i < runCommands.length; ++i) {
|
||
runCommands[i]();
|
||
}
|
||
runCommands = [];
|
||
fadeOut('preload', 'ingame');
|
||
fadeOut('instructions', null, true);
|
||
gba.runStable();
|
||
} else {
|
||
load.textContent = 'FAILED';
|
||
setTimeout(function() {
|
||
load.textContent = 'SELECT';
|
||
load.onclick = function() {
|
||
document.getElementById('loader').click();
|
||
}
|
||
}, 3000);
|
||
}
|
||
});
|
||
}
|
||
var gameState = null;
|
||
function savestate() {
|
||
var state = gba.freeze();
|
||
gameState = Serializer.serialize(state);
|
||
// localStorage.setItem('gbadata', gameState);
|
||
// Serializer.serializePNG(Serializer.serialize(state), document.getElementById('screen'), function(url) {
|
||
// var img = document.getElementById('saveState');
|
||
// img.setAttribute('src', url);
|
||
// });
|
||
}
|
||
function loadSavestate() {
|
||
try {
|
||
Serializer.deserialize(gameState, function (state) {
|
||
console.log(state);
|
||
gba.defrost(state);
|
||
});
|
||
|
||
} catch (exception) {
|
||
gba.ERROR('Failed to load savestate', exception);
|
||
}
|
||
}
|
||
function reset() {
|
||
gba.pause();
|
||
gba.reset();
|
||
var load = document.getElementById('select');
|
||
load.textContent = 'SELECT';
|
||
var crash = document.getElementById('crash');
|
||
if (crash) {
|
||
var context = gba.targetCanvas.getContext('2d');
|
||
context.clearRect(0, 0, 480, 320);
|
||
gba.video.drawCallback();
|
||
crash.parentElement.removeChild(crash);
|
||
var canvas = document.getElementById('screen');
|
||
canvas.removeAttribute('class');
|
||
} else {
|
||
lcdFade(gba.context, gba.targetCanvas.getContext('2d'), gba.video.drawCallback);
|
||
}
|
||
load.onclick = function() {
|
||
document.getElementById('loader').click();
|
||
}
|
||
fadeOut('ingame', 'preload');
|
||
}
|
||
|
||
function uploadSavedataPending(file) {
|
||
runCommands.push(function() { gba.loadSavedataFromFile(file) });
|
||
}
|
||
|
||
function togglePause() {
|
||
var e = document.getElementById('pause');
|
||
if (gba.paused) {
|
||
if (debug && debug.gbaCon) {
|
||
debug.gbaCon.run();
|
||
} else {
|
||
gba.runStable();
|
||
}
|
||
e.textContent = "PAUSE";
|
||
} else {
|
||
if (debug && debug.gbaCon) {
|
||
debug.gbaCon.pause();
|
||
} else {
|
||
gba.pause();
|
||
}
|
||
e.textContent = "UNPAUSE";
|
||
}
|
||
}
|
||
|
||
function screenshot() {
|
||
var canvas = gba.indirectCanvas;
|
||
window.open(canvas.toDataURL('image/png'), 'screenshot');
|
||
}
|
||
|
||
function lcdFade(context, target, callback) {
|
||
var i = 0;
|
||
var drawInterval = setInterval(function() {
|
||
i++;
|
||
var pixelData = context.getImageData(0, 0, 240, 160);
|
||
for (var y = 0; y < 160; ++y) {
|
||
for (var x = 0; x < 240; ++x) {
|
||
var xDiff = Math.abs(x - 120);
|
||
var yDiff = Math.abs(y - 80) * 0.8;
|
||
var xFactor = (120 - i - xDiff) / 120;
|
||
var yFactor = (80 - i - ((y & 1) * 10) - yDiff + Math.pow(xDiff, 1 / 2)) / 80;
|
||
pixelData.data[(x + y * 240) * 4 + 3] *= Math.pow(xFactor, 1 / 3) * Math.pow(yFactor, 1 / 2);
|
||
}
|
||
}
|
||
context.putImageData(pixelData, 0, 0);
|
||
target.clearRect(0, 0, 480, 320);
|
||
if (i > 40) {
|
||
clearInterval(drawInterval);
|
||
} else {
|
||
callback();
|
||
}
|
||
}, 50);
|
||
}
|
||
|
||
function setVolume(value) {
|
||
gba.audio.masterVolume = Math.pow(2, value) - 1;
|
||
}
|
||
|
||
function setPixelated(pixelated) {
|
||
var screen = document.getElementById('screen');
|
||
var context = screen.getContext('2d');
|
||
if (context.webkitImageSmoothingEnabled) {
|
||
context.webkitImageSmoothingEnabled = !pixelated;
|
||
} else if (context.mozImageSmoothingEnabled) {
|
||
context.mozImageSmoothingEnabled = !pixelated;
|
||
} else if (window.navigator.appName != 'Microsoft Internet Explorer') {
|
||
if (pixelated) {
|
||
screen.setAttribute('width', '240');
|
||
screen.setAttribute('height', '160');
|
||
} else {
|
||
screen.setAttribute('width', '480');
|
||
screen.setAttribute('height', '320');
|
||
}
|
||
if (window.navigator.appName == 'Opera') {
|
||
// Ugly hack! Ew!
|
||
if (pixelated) {
|
||
screen.style.marginTop = '0';
|
||
screen.style.marginBottom = '-325px';
|
||
} else {
|
||
delete screen.style;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function enableDebug() {
|
||
window.onmessage = function(message) {
|
||
if (message.origin != document.domain && (message.origin != 'file://' || document.domain)) {
|
||
console.log('Failed XSS');
|
||
return;
|
||
}
|
||
switch (message.data) {
|
||
case 'connect':
|
||
if (message.source == debug) {
|
||
debug.postMessage('connect', document.domain || '*');
|
||
}
|
||
break;
|
||
case 'connected':
|
||
break;
|
||
case 'disconnect':
|
||
if (message.source == debug) {
|
||
debug = null;
|
||
}
|
||
}
|
||
}
|
||
window.onunload = function() {
|
||
if (debug && debug.postMessage) {
|
||
debug.postMessage('disconnect', document.domain || '*');
|
||
}
|
||
}
|
||
if (!debug || !debug.postMessage) {
|
||
debug = window.open('debugger.html', 'debug');
|
||
} else {
|
||
debug.postMessage('connect', document.domain || '*');
|
||
}
|
||
}
|
||
|
||
document.addEventListener('webkitfullscreenchange', function() {
|
||
var canvas = document.getElementById('screen');
|
||
if (document.webkitIsFullScreen) {
|
||
canvas.setAttribute('height', document.body.offsetHeight);
|
||
canvas.setAttribute('width', document.body.offsetHeight / 2 * 3);
|
||
canvas.setAttribute('style', 'margin: 0');
|
||
} else {
|
||
canvas.setAttribute('height', 320);
|
||
canvas.setAttribute('width', 480);
|
||
canvas.removeAttribute('style');
|
||
}
|
||
}, false);
|
||
</script>
|
||
</head>
|
||
<body>
|
||
<canvas id="screen" width="480" height="320"></canvas>
|
||
<section id="controls">
|
||
<div id="preload">
|
||
<button class="bigbutton" id="select" onclick="document.getElementById('loader').click()">SELECT</button>
|
||
<input id="loader" type="file" accept=".gba" onchange="run(this.files[0]);">
|
||
<button onclick="document.getElementById('saveloader').click()">Upload Savegame</button>
|
||
<input id="saveloader" type="file" onchange="uploadSavedataPending(this.files[0]);">
|
||
</div>
|
||
<div id="ingame" class="hidden">
|
||
<button id="pause" class="bigbutton" onclick="togglePause()">PAUSE</button>
|
||
<button class="bigbutton" onclick="reset()">RESET</button>
|
||
<button class="bigbutton" onclick="savestate()">SAVE</button>
|
||
<button class="bigbutton" onclick="loadSavestate()">LOAD</button>
|
||
<button onclick="gba.downloadSavedata()">Download Savegame</button>
|
||
<button onclick="screenshot()">Screenshot</button>
|
||
<label id="pixelated">
|
||
<input type="checkbox" onchange="setPixelated(this.checked)">
|
||
<p>Pixelated</p>
|
||
</label>
|
||
<div id="sound">
|
||
<input type="checkbox" checked onchange="gba.audio.masterEnable = this.checked">
|
||
<p>Sound</p>
|
||
<input type="range" min="0" max="1" value="1" step="any" onchange="setVolume(this.value)" oninput="setVolume(this.value)">
|
||
</div>
|
||
<p id="openDebug" onclick="enableDebug()">Debugger</p>
|
||
</div>
|
||
</section>
|
||
<ul id="links">
|
||
<li><a href="http://github.com/endrift/gbajs/">Fork me on GitHub!</a></li>
|
||
<li><a href="https://github.com/endrift/gbajs/wiki/Compatibility-List">Compatibility list</a></li>
|
||
</ul>
|
||
<footer>© 2012 – 2013 <a href="http://endrift.com/">Jeffrey Pfau</a></footer>
|
||
</body>
|
||
</html>
|