| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> |
| <title>GBA Remote - Auto-Load</title> |
| <style> |
| |
| body, html { |
| margin: 0; padding: 0; width: 100%; height: 100%; |
| background-color: #000; overflow: hidden; |
| display: flex; flex-direction: column; |
| font-family: sans-serif; -webkit-tap-highlight-color: transparent; |
| } |
| |
| |
| #screen-container { |
| width: 100%; flex: 0 0 auto; |
| display: flex; flex-direction: column; align-items: center; |
| padding-top: 10px; |
| } |
| |
| #screen { |
| width: 100vw; height: auto; aspect-ratio: 3 / 2; |
| image-rendering: pixelated; background: #000; |
| } |
| |
| |
| .status-text { |
| color: #0f0; font-family: monospace; |
| margin-bottom: 5px; font-size: 14px; |
| } |
| |
| |
| #touchControls { |
| flex: 1; position: relative; width: 100%; |
| user-select: none; -webkit-user-select: none; |
| } |
| |
| |
| .ctrl-btn { |
| position: absolute; background: #A6A6A6; |
| border: none; color: #fff; |
| display: flex; align-items: center; justify-content: center; |
| pointer-events: auto; padding: 0; |
| } |
| |
| |
| #dpad { |
| position: absolute; bottom: 110px; left: 20px; |
| width: 150px; height: 150px; |
| } |
| #up { top: 0; left: 50px; width: 50px; height: 55px; border-radius: 4px 4px 0 0; } |
| #down { bottom: 0; left: 50px; width: 50px; height: 55px; border-radius: 0 0 4px 4px; } |
| #left { top: 50px; left: 0; width: 55px; height: 50px; border-radius: 4px 0 0 4px; } |
| #right { top: 50px; right: 0; width: 55px; height: 50px; border-radius: 0 4px 4px 0; } |
| #center-block { position: absolute; top: 50px; left: 50px; width: 50px; height: 50px; background: #A6A6A6; } |
| |
| |
| #action-cluster { |
| position: absolute; bottom: 110px; right: 20px; |
| width: 150px; height: 150px; |
| display: grid; grid-template-columns: 1fr 1fr; grid-gap: 10px; |
| } |
| .round-btn { position: relative; width: 70px; height: 70px; border-radius: 50%; } |
| |
| |
| .pill-btn { |
| bottom: 40px; width: 90px; height: 35px; border-radius: 20px; |
| font-size: 12px; background: #A6A6A6; color: #fff; |
| } |
| #select { left: 60px; } |
| #start { right: 60px; } |
| |
| |
| .hidden { display: none !important; } |
| </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; |
| try { |
| gba = new GameBoyAdvance(); |
| gba.keypad.eatInput = true; |
| } catch (e) { gba = null; } |
| |
| window.onload = function() { |
| if (gba) { |
| var canvas = document.getElementById('screen'); |
| gba.setCanvas(canvas); |
| |
| |
| loadRom('resources/bios.bin', function(bios) { |
| gba.setBios(bios); |
| |
| autoLoadGame('1.gba'); |
| }); |
| |
| setupTouchControls(); |
| } |
| } |
| |
| |
| function autoLoadGame(url) { |
| var xhr = new XMLHttpRequest(); |
| xhr.open('GET', url, true); |
| xhr.responseType = 'blob'; |
| xhr.onload = function() { |
| if (xhr.status === 200) { |
| gba.loadRomFromFile(xhr.response, function(result) { |
| if (result) gba.runStable(); |
| }); |
| } else { |
| console.error("1.gba not found. Use the manual selector."); |
| document.getElementById('manual-loader').classList.remove('hidden'); |
| } |
| }; |
| xhr.send(); |
| } |
| |
| function setupTouchControls() { |
| const map = { |
| 'up': 38, 'down': 40, 'left': 37, 'right': 39, |
| 'a': 88, 'b': 90, 'x': 83, 'y': 65, |
| 'start': 13, 'select': 16 |
| }; |
| |
| const press = (c) => window.dispatchEvent(new KeyboardEvent('keydown', {keyCode: c})); |
| const release = (c) => window.dispatchEvent(new KeyboardEvent('keyup', {keyCode: c})); |
| |
| Object.keys(map).forEach(id => { |
| const el = document.getElementById(id); |
| if(el) { |
| el.addEventListener('touchstart', (e) => { e.preventDefault(); press(map[id]); el.style.opacity = "0.7"; }); |
| el.addEventListener('touchend', (e) => { e.preventDefault(); release(map[id]); el.style.opacity = "1"; }); |
| } |
| }); |
| } |
| </script> |
| </head> |
| <body> |
|
|
| <div id="screen-container"> |
| <div class="status-text">Connecting to Screen...</div> |
| <canvas id="screen" width="480" height="320"></canvas> |
| </div> |
|
|
| <div id="manual-loader" class="hidden" style="text-align:center; padding: 20px;"> |
| <button onclick="document.getElementById('file-in').click()" style="background:#333; color:#0f0; border:1px solid #444; padding:10px;">Load Different ROM</button> |
| <input id="file-in" type="file" accept=".gba" style="display:none" onchange="gba.loadRomFromFile(this.files[0], (r) => r && gba.runStable())"> |
| </div> |
|
|
| <div id="touchControls"> |
| <div id="dpad"> |
| <button id="up" class="ctrl-btn"></button> |
| <button id="left" class="ctrl-btn"></button> |
| <div id="center-block"></div> |
| <button id="right" class="ctrl-btn"></button> |
| <button id="down" class="ctrl-btn"></button> |
| </div> |
|
|
| <div id="action-cluster"> |
| <button id="x" class="ctrl-btn round-btn"></button> |
| <button id="y" class="ctrl-btn round-btn"></button> |
| <button id="b" class="ctrl-btn round-btn"></button> |
| <button id="a" class="ctrl-btn round-btn"></button> |
| </div> |
|
|
| <button id="select" class="ctrl-btn pill-btn">SELECT</button> |
| <button id="start" class="ctrl-btn pill-btn">START</button> |
| </div> |
|
|
| </body> |
| </html> |
|
|