File size: 6,705 Bytes
89610f7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>GBA.js Mobile - Fixed Loader</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style>
body { margin:0; background:#000; font-family:Arial; color:#fff; overflow:hidden; }
#screen { display:block; width:100%; height:auto; image-rendering:pixelated; }
#info { position:absolute; top:10px; left:10px; background:rgba(0,0,0,0.6); padding:10px; border-radius:8px; z-index:30; pointer-events:auto; }
#touchControls { position:absolute; inset:0; pointer-events:none; z-index:20; padding:10px; display:none; box-sizing:border-box; }
.control-row { display:flex; justify-content:space-between; align-items:flex-end; height:100%; pointer-events:none; }
button { background:rgba(80,80,80,0.7); color:white; font-weight:bold; border:none; border-radius:10px; box-shadow:0 3px 8px #000; touch-action:manipulation; -webkit-tap-highlight-color:transparent; }
#dpad { display:grid; grid-template-columns:repeat(3,1fr); grid-template-rows:repeat(3,1fr); gap:6px; width:35%; max-width:160px; aspect-ratio:1/1; pointer-events:auto; }
#dpad button { font-size:clamp(1.6rem,8vw,2.4rem); }
.face-btn { width:clamp(60px,17vw,85px); height:clamp(60px,17vw,85px); border-radius:50%; font-size:clamp(1.8rem,7vw,2.4rem); pointer-events:auto; }
#a { background:rgba(220,40,40,0.8); }
#b { background:rgba(40,100,220,0.8); }
.shoulder { width:clamp(50px,14vw,70px); height:clamp(32px,8vw,40px); font-size:clamp(1rem,4vw,1.3rem); background:rgba(100,100,100,0.8); }
.action { width:clamp(65px,17vw,85px); height:clamp(36px,9vw,44px); font-size:clamp(0.95rem,3.8vw,1.2rem); background:rgba(40,140,40,0.8); }
@media (max-width:360px) { #dpad {max-width:130px;} .face-btn {width:55px;height:55px;} }
</style>
</head>
<body>
<canvas id="screen" width="240" height="160"></canvas>
<div id="info">
<button onclick="document.getElementById('romfile').click()">Load .gba ROM</button>
<input id="romfile" type="file" accept=".gba" style="display:none;">
<p id="status">Waiting for ROM...</p>
</div>
<div id="touchControls">
<div class="control-row">
<!-- D-Pad left -->
<div id="dpad">
<div></div><button id="up">โ</button><div></div>
<button id="left">โ</button><div></div><button id="right">โ</button>
<div></div><button id="down">โ</button><div></div>
</div>
<!-- Right side controls -->
<div style="display:flex; flex-direction:column; align-items:flex-end; gap:8px; width:60%; pointer-events:auto;">
<div style="display:flex; gap:10px;">
<button id="l" class="shoulder">L</button>
<button id="r" class="shoulder">R</button>
</div>
<div style="display:flex; gap:10px;">
<button id="select" class="action">Select</button>
<button id="start" class="action">Start</button>
</div>
<div style="display:flex; gap:18px;">
<button id="b" class="face-btn">B</button>
<button id="a" class="face-btn">A</button>
</div>
</div>
</div>
</div>
<!-- Load GBA.js from reliable CDN mirror -->
<script src="https://cdn.jsdelivr.net/gh/gbajs/gbajs@master/js/util.js"></script>
<script src="https://cdn.jsdelivr.net/gh/gbajs/gbajs@master/js/core.js"></script>
<script src="https://cdn.jsdelivr.net/gh/gbajs/gbajs@master/js/arm.js"></script>
<script src="https://cdn.jsdelivr.net/gh/gbajs/gbajs@master/js/thumb.js"></script>
<script src="https://cdn.jsdelivr.net/gh/gbajs/gbajs@master/js/mmu.js"></script>
<script src="https://cdn.jsdelivr.net/gh/gbajs/gbajs@master/js/io.js"></script>
<script src="https://cdn.jsdelivr.net/gh/gbajs/gbajs@master/js/audio.js"></script>
<script src="https://cdn.jsdelivr.net/gh/gbajs/gbajs@master/js/video.js"></script>
<script src="https://cdn.jsdelivr.net/gh/gbajs/gbajs@master/js/video/software.js"></script>
<script src="https://cdn.jsdelivr.net/gh/gbajs/gbajs@master/js/irq.js"></script>
<script src="https://cdn.jsdelivr.net/gh/gbajs/gbajs@master/js/keypad.js"></script>
<script src="https://cdn.jsdelivr.net/gh/gbajs/gbajs@master/js/gba.js"></script>
<script>
let gba;
const canvas = document.getElementById('screen');
const status = document.getElementById('status');
try {
gba = new GameBoyAdvance();
gba.keypad.eatInput = true;
gba.setCanvas(canvas);
gba.logLevel = 0; // quiet unless error
console.log("GBA core initialized");
} catch(e) {
status.textContent = "Emulator init failed: " + e.message;
alert("Cannot start emulator: " + e);
}
if ('ontouchstart' in window || navigator.maxTouchPoints > 0) {
document.getElementById('touchControls').style.display = 'block';
canvas.style.width = '100%';
canvas.style.height = 'auto';
}
// Key simulation
function press(code) { window.dispatchEvent(new KeyboardEvent('keydown', {keyCode:code, bubbles:true})); }
function release(code) { window.dispatchEvent(new KeyboardEvent('keyup', {keyCode:code, bubbles:true})); }
const keys = {
up:38, down:40, left:37, right:39,
a:88, b:90, l:65, r:83,
start:13, select:16
};
Object.entries(keys).forEach(([id, code]) => {
const btn = document.getElementById(id);
if (btn) {
btn.addEventListener('touchstart', e => { e.preventDefault(); press(code); });
btn.addEventListener('touchend', e => { e.preventDefault(); release(code); });
btn.addEventListener('touchcancel', e => { e.preventDefault(); release(code); });
}
});
// Load ROM
document.getElementById('romfile').addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
status.textContent = "Reading file... (" + (file.size / 1024 / 1024).toFixed(1) + " MB)";
const reader = new FileReader();
reader.onload = function() {
try {
const buffer = reader.result;
if (buffer.byteLength < 0x4000) {
throw new Error("File too small โ probably not a GBA ROM");
}
gba.loadRom(buffer, (success) => {
if (success) {
status.textContent = "ROM loaded! Starting...";
gba.runStable();
setTimeout(() => { document.getElementById('info').style.display = 'none'; }, 1500);
} else {
throw new Error("loadRom returned false");
}
});
} catch (err) {
status.textContent = "Load failed: " + err.message;
alert("Error loading ROM:\n" + err.message + "\n\nTry a different .gba file (clean dump, not zipped).");
console.error(err);
}
};
reader.onerror = () => {
status.textContent = "File read error";
alert("Cannot read the file โ maybe corrupted or browser blocked it.");
};
reader.readAsArrayBuffer(file);
});
</script>
</body>
</html> |