Download Touch.html from Studytime171/lucky: direct link, hf CLI and curl.
- Browser
- Download file 14 kB
-
https://huggingface.co/Studytime171/lucky/resolve/main/Touch.html
- Command line
-
hf download hf://Studytime171/lucky/Touch.html
-
curl -L -o Touch.html https://huggingface.co/Studytime171/lucky/resolve/main/Touch.html
14 kB
| <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); | |
| } | |
| // Detect touch device and show controls | |
| if ('ontouchstart' in window || navigator.maxTouchPoints > 0) { | |
| document.getElementById('touchControls').style.display = 'flex'; | |
| // Make canvas responsive on mobile | |
| canvas.style.width = '100%'; | |
| canvas.style.height = 'auto'; | |
| } | |
| // Wire up touch events to simulate key presses | |
| setupTouchControls(); | |
| } 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); | |
| // New function to setup touch controls | |
| function setupTouchControls() { | |
| // Function to simulate key press/release | |
| function pressKey(code) { | |
| var e = new KeyboardEvent('keydown', {keyCode: code, bubbles: true}); | |
| window.dispatchEvent(e); | |
| } | |
| function releaseKey(code) { | |
| var e = new KeyboardEvent('keyup', {keyCode: code, bubbles: true}); | |
| window.dispatchEvent(e); | |
| } | |
| // Assuming standard mappings: Arrows for D-Pad, Z for A, X for B, Enter for Start, Shift for Select, A for L, S for R | |
| // D-Pad buttons | |
| document.getElementById('up').addEventListener('touchstart', () => pressKey(38)); // Up arrow | |
| document.getElementById('up').addEventListener('touchend', () => releaseKey(38)); | |
| document.getElementById('down').addEventListener('touchstart', () => pressKey(40)); // Down arrow | |
| document.getElementById('down').addEventListener('touchend', () => releaseKey(40)); | |
| document.getElementById('left').addEventListener('touchstart', () => pressKey(37)); // Left arrow | |
| document.getElementById('left').addEventListener('touchend', () => releaseKey(37)); | |
| document.getElementById('right').addEventListener('touchstart', () => pressKey(39)); // Right arrow | |
| document.getElementById('right').addEventListener('touchend', () => releaseKey(39)); | |
| // Face buttons | |
| document.getElementById('a').addEventListener('touchstart', () => pressKey(88)); // X for A | |
| document.getElementById('a').addEventListener('touchend', () => releaseKey(88)); | |
| document.getElementById('b').addEventListener('touchstart', () => pressKey(90)); // Z for B | |
| document.getElementById('b').addEventListener('touchend', () => releaseKey(90)); | |
| // Shoulders | |
| document.getElementById('l').addEventListener('touchstart', () => pressKey(65)); // A for L | |
| document.getElementById('l').addEventListener('touchend', () => releaseKey(65)); | |
| document.getElementById('r').addEventListener('touchstart', () => pressKey(83)); // S for R | |
| document.getElementById('r').addEventListener('touchend', () => releaseKey(83)); | |
| // Start/Select | |
| document.getElementById('start').addEventListener('touchstart', () => pressKey(13)); // Enter for Start | |
| document.getElementById('start').addEventListener('touchend', () => releaseKey(13)); | |
| document.getElementById('select').addEventListener('touchstart', () => pressKey(16)); // Shift for Select | |
| document.getElementById('select').addEventListener('touchend', () => releaseKey(16)); | |
| } | |
| </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> | |
| <!-- Added Touch Controls Overlay --> | |
| <div id="touchControls" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; display: none; z-index: 10; flex-direction: row; justify-content: space-between; align-items: flex-end; padding: 10px; box-sizing: border-box;"> | |
| <!-- D-Pad (left side) --> | |
| <div id="dpad" style="display: grid; grid-template-columns: repeat(3, 50px); grid-template-rows: repeat(3, 50px); gap: 5px; pointer-events: auto;"> | |
| <button id="up" style="grid-column: 2; grid-row: 1; background: rgba(0,0,0,0.5); color: white; border-radius: 10px;">↑</button> | |
| <button id="left" style="grid-column: 1; grid-row: 2; background: rgba(0,0,0,0.5); color: white; border-radius: 10px;">←</button> | |
| <button id="right" style="grid-column: 3; grid-row: 2; background: rgba(0,0,0,0.5); color: white; border-radius: 10px;">→</button> | |
| <button id="down" style="grid-column: 2; grid-row: 3; background: rgba(0,0,0,0.5); color: white; border-radius: 10px;">↓</button> | |
| </div> | |
| <!-- Face Buttons (right side) --> | |
| <div id="faceButtons" style="display: flex; flex-direction: column; align-items: flex-end; pointer-events: auto;"> | |
| <div style="display: flex; gap: 10px; margin-bottom: 10px;"> | |
| <button id="l" style="width:60px; height:30px; background: rgba(128,128,128,0.5); color: white; border-radius: 5px;">L</button> | |
| <button id="r" style="width:60px; height:30px; background: rgba(128,128,128,0.5); color: white; border-radius: 5px;">R</button> | |
| </div> | |
| <div style="display: flex; gap: 10px; margin-bottom: 10px;"> | |
| <button id="select" style="width:60px; height:30px; background: rgba(0,128,0,0.5); color: white; border-radius: 5px;">Select</button> | |
| <button id="start" style="width:60px; height:30px; background: rgba(0,128,0,0.5); color: white; border-radius: 5px;">Start</button> | |
| </div> | |
| <div style="display: flex; gap: 20px;"> | |
| <button id="b" style="width:70px; height:70px; border-radius:50%; background: rgba(0,0,255,0.5); color: white;">B</button> | |
| <button id="a" style="width:70px; height:70px; border-radius:50%; background: rgba(255,0,0,0.5); color: white;">A</button> | |
| </div> | |
| </div> | |
| </div> | |
| </body> | |
| </html> |