<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); } }); } 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 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>