| <html> |
| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
| <title>GBA.js - Mobile Fit</title> |
| <link rel="stylesheet" href="resources/main.css"> |
| <style> |
| |
| body, html { |
| margin: 0; |
| padding: 0; |
| width: 100%; |
| height: 100%; |
| background-color: #5b4482; |
| overflow: hidden; |
| display: flex; |
| flex-direction: column; |
| } |
| |
| |
| #screen-container { |
| width: 100%; |
| padding-top: 20px; |
| display: flex; |
| justify-content: center; |
| align-items: flex-start; |
| } |
| |
| #screen { |
| width: 90vw; |
| height: auto; |
| aspect-ratio: 3 / 2; |
| background: #000; |
| border: 8px solid #222; |
| border-radius: 10px; |
| image-rendering: pixelated; |
| } |
| |
| |
| #ingame { |
| text-align: center; |
| padding: 10px; |
| } |
| |
| #ingame button { |
| background: rgba(255,255,255,0.2); |
| border: none; |
| color: white; |
| padding: 5px 15px; |
| margin: 0 5px; |
| border-radius: 5px; |
| font-size: 12px; |
| } |
| |
| |
| #touchControls { |
| margin-top: auto; |
| width: 100%; |
| height: 40%; |
| display: none; |
| flex-direction: row; |
| justify-content: space-between; |
| align-items: flex-end; |
| padding: 30px 20px; |
| box-sizing: border-box; |
| pointer-events: none; |
| } |
| |
| #touchControls button { |
| pointer-events: auto; |
| background: rgba(255, 255, 255, 0.15); |
| color: rgba(255, 255, 255, 0.8); |
| border: none; |
| font-weight: bold; |
| } |
| |
| |
| .dpad-grid { |
| display: grid; |
| grid-template-columns: repeat(3, 50px); |
| grid-template-rows: repeat(3, 50px); |
| gap: 5px; |
| } |
| .dpad-btn { width: 50px; height: 500px; border-radius: 8px; } |
| |
| |
| .action-group { |
| display: flex; |
| flex-direction: column; |
| align-items: flex-end; |
| gap: 15px; |
| } |
| .ab-btns { display: flex; gap: 15px; } |
| .round-btn { width: 70px; height: 70px; border-radius: 50%; font-size: 20px; } |
| .shoulder-btns { display: flex; gap: 10px; } |
| .rect-btn { width: 50px; height: 30px; border-radius: 5px; font-size: 12px; } |
| .meta-btns { display: flex; gap: 10px; } |
| .pill-btn { width: 60px; height: 20px; border-radius: 10px; font-size: 10px; } |
| </style> |
|
|
| <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 = []; |
| |
| try { |
| gba = new GameBoyAdvance(); |
| gba.keypad.eatInput = true; |
| } catch (exception) { gba = null; } |
| |
| window.onload = function() { |
| if (gba && FileReader) { |
| var canvas = document.getElementById('screen'); |
| gba.setCanvas(canvas); |
| loadRom('resources/bios.bin', function(bios) { gba.setBios(bios); }); |
| |
| if ('ontouchstart' in window || navigator.maxTouchPoints > 0) { |
| document.getElementById('touchControls').style.display = 'flex'; |
| } |
| setupTouchControls(); |
| } |
| } |
| |
| function run(file) { |
| gba.loadRomFromFile(file, function(result) { |
| if (result) { |
| document.getElementById('preload').style.display = 'none'; |
| document.getElementById('ingame').classList.remove('hidden'); |
| gba.runStable(); |
| } |
| }); |
| } |
| |
| function setupTouchControls() { |
| function press(c) { window.dispatchEvent(new KeyboardEvent('keydown', {keyCode: c})); } |
| function release(c) { window.dispatchEvent(new KeyboardEvent('keyup', {keyCode: c})); } |
| |
| const map = { |
| 'up': 38, 'down': 40, 'left': 37, 'right': 39, |
| 'a': 88, 'b': 90, 'l': 65, 'r': 83, |
| 'start': 13, 'select': 16 |
| }; |
| |
| Object.keys(map).forEach(id => { |
| const el = document.getElementById(id); |
| if(el) { |
| el.addEventListener('touchstart', (e) => { e.preventDefault(); press(map[id]); }); |
| el.addEventListener('touchend', (e) => { e.preventDefault(); release(map[id]); }); |
| } |
| }); |
| } |
| </script> |
| </head> |
| <body> |
|
|
| <div id="screen-container"> |
| <canvas id="screen" width="480" height="320"></canvas> |
| </div> |
|
|
| <section id="controls"> |
| <div id="preload"> |
| <button class="bigbutton" onclick="document.getElementById('loader').click()">SELECT ROM</button> |
| <input id="loader" type="file" accept=".gba" onchange="run(this.files[0]);" style="display:none"> |
| </div> |
| <div id="ingame" class="hidden"> |
| <button onclick="gba.pause()">PAUSE</button> |
| <button onclick="gba.reset()">RESET</button> |
| </div> |
| </section> |
|
|
| <div id="touchControls"> |
| <div class="dpad-grid"> |
| <button id="up" class="dpad-btn" style="grid-column: 2; grid-row: 1;">↑</button> |
| <button id="left" class="dpad-btn" style="grid-column: 1; grid-row: 2;">←</button> |
| <button id="right" class="dpad-btn" style="grid-column: 3; grid-row: 2;">→</button> |
| <button id="down" class="dpad-btn" style="grid-column: 2; grid-row: 3;">↓</button> |
| </div> |
|
|
| <div class="action-group"> |
| <div class="shoulder-btns"> |
| <button id="l" class="rect-btn">L</button> |
| <button id="r" class="rect-btn">R</button> |
| </div> |
| <div class="ab-btns"> |
| <button id="b" class="round-btn">B</button> |
| <button id="a" class="round-btn">A</button> |
| </div> |
| <div class="meta-btns"> |
| <button id="select" class="pill-btn">SELECT</button> |
| <button id="start" class="pill-btn">START</button> |
| </div> |
| </div> |
| </div> |
|
|
| </body> |
| </html> |
|
|