import React from 'react'; import CenterElement from '/components/center-element'; import FormSelectFiles from '/components/form-select-files'; import CanvasGBAEmulator from '/components/canvas-gba-emulator'; import Endian from '/endian'; import {MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_SEND_FRAME} from '/constants'; function handleClickStartEmulationButton({e, inputRom, inputSaveState, setHiddenFormSelectFiles, canvas}) { const ctx = canvas.getContext('2d') e.preventDefault(); if (inputRom.files.length == 0) { alert('There is no rom still'); return; } if (inputSaveState.files.length == 0) { alert('There is no savestate still'); return; } const rom_file = inputRom.files[0]; const savestate_file = inputSaveState.files[0]; rom_file.arrayBuffer().then((rom_buffer) => { savestate_file.arrayBuffer().then((savestate_buffer) => { const rom_array = new Uint8Array(rom_buffer); const savestate_array = new Uint8Array(savestate_buffer); const websocket = new WebSocket(`ws://localhost:3000/ws`); websocket.binaryType = 'arraybuffer'; websocket.onclose = (message) => console.log('CLOSE', message); websocket.onopen = () => { setHiddenFormSelectFiles(c => false); console.log('Opened websocket.'); sendHello(websocket, rom_array, savestate_array); }; websocket.addEventListener('message', (event) => onWebSocketPacket(event, canvas, ctx)); }); }); } function concatU8Array(array1, array2) { const final_array = new Uint8Array(array1.length + array2.length); final_array.set(array1); final_array.set(array2, array1.length); return final_array; } function u64ToByteArrayBigEndian(input_number) { const buffer = new ArrayBuffer(8); const buffer8 = new Uint8Array(buffer); const buffer64 = new BigUint64Array(buffer); buffer64[0] = input_number; if (Endian.isLittleEndian()) { buffer8.reverse(); } return buffer8; } function sendPacket(websocket, id, raw_data) { const packet_u8 = concatU8Array( concatU8Array(u64ToByteArrayBigEndian(id), u64ToByteArrayBigEndian(BigInt(raw_data.length))), raw_data ); const packet_buffer = packet_u8.buffer; console.log('Sending packet'); websocket.send(packet_buffer); } function sendHello(websocket, rom_array, savestate_array) { console.log('Sending hello.'); const length_rom = BigInt(rom_array.length); const length_savestate = BigInt(savestate_array.length); const raw_data = concatU8Array( concatU8Array( concatU8Array(u64ToByteArrayBigEndian(length_rom), rom_array), u64ToByteArrayBigEndian(length_savestate) ), savestate_array ); sendPacket(websocket, PACKET_ID_HELLO, raw_data); } function onWebSocketPacket(event, canvas, ctx) { const buffer = event.data; let packet_u8 = new Uint8Array(buffer); const id = byteArrayToU64BigEndian(packet_u8.slice(0, 8)); packet_u8 = packet_u8.slice(8, packet_u8.length); const size = byteArrayToU64BigEndian(packet_u8.slice(0, 8)); const raw_data = packet_u8.slice(8, packet_u8.length); packet_u8 = null; switch (id) { case PACKET_ID_SEND_FRAME: handleSendFrame(raw_data, canvas, ctx); break; default: console.log(`Received unknown packet ${id}`); } } let printing_frame = false; function handleSendFrame(raw_data, canvas, ctx) { if (printing_frame) { return; } printing_frame = true; let data = raw_data; const stride = byteArrayToU32BigEndian(data.slice(0, 4)); data = data.slice(4, data.length); const output_buffer_size = byteArrayToU32BigEndian(data.slice(0, 8)); data = data.slice(8, data.length); console.log(data.length / 4 / MIN_WIDTH); const img_data = ctx.createImageData(MIN_WIDTH, MIN_HEIGHT); const img_data_u8 = new Uint8Array(img_data.data.buffer); for (let i = 0; i drawBitmap(bitmap, canvas, ctx)); } function byteArrayToU32BigEndian(input_array) { if (Endian.isLittleEndian()) { input_array = input_array.reverse(); } const buffer = input_array.buffer; const output_u32_array = new Uint32Array(buffer); return output_u32_array[0]; } function byteArrayToU64BigEndian(input_array) { if (Endian.isLittleEndian()) { input_array = input_array.reverse(); } const buffer = input_array.buffer; const output_u64_array = new BigUint64Array(buffer); return output_u64_array[0]; } function drawBitmap(bitmap, canvas, ctx) { ctx.drawImage(bitmap, 0, 0, canvas1.width, canvas1.height); printing_frame = false; } export default function Page() { const screenDimensions = useScreenDimensions(); const emulatorDimensions = calculateSizeEmulator(screenDimensions); const canvasRef = React.useRef(null); function resizeCanvas(node) { const canvas = canvasRef.current; if (canvas) { canvas.width = emulatorDimensions.width; canvas.height = emulatorDimensions.height; const ctx = canvas.getContext('2d') fillBlack(canvas, ctx); } }; const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = React.useState(false); React.useEffect(resizeCanvas, [emulatorDimensions]); const refInputRom = React.useRef(null); const refInputSaveState = React.useRef(null); const onStartEmulation = (e) => { handleClickStartEmulationButton({ e: e, setHiddenFormSelectFiles: setHiddenFormSelectFiles, inputRom: refInputRom.current, inputSaveState: refInputSaveState.current, canvas: canvasRef.current, }); }; return (

msGBA Emulator Online for GBA.

); } function getScreenDimensions() { return { width: document.body.clientWidth, height: document.body.clientHeight }; } function useScreenDimensions() { const [screenDimensions, setScreenDimensions] = React.useState(getScreenDimensions()); React.useEffect(() => { function onResize() { setScreenDimensions(getScreenDimensions()); } window.addEventListener("resize", onResize); return () => { window.removeEventListener("resize", onResize); } }, []); return screenDimensions; } function fillBlack(canvas, ctx) { ctx.beginPath(); ctx.rect(0, 0, canvas.width, canvas.height); ctx.fillStyle = 'black'; ctx.fill(); } function calculateSizeEmulator(screenDimensions) { const width = screenDimensions.width; const height = screenDimensions.height * 0.75; const emulatorDimensions = {}; if (width < MIN_WIDTH || height < MIN_HEIGHT) { return { width: MIN_WIDTH, height: MIN_HEIGHT, }; } const ratioWidth = Math.floor(width / MIN_WIDTH); const ratioHeight = Math.floor(height / MIN_HEIGHT); if (ratioWidth < ratioHeight) { emulatorDimensions.width = MIN_WIDTH * ratioWidth; emulatorDimensions.height = MIN_HEIGHT * ratioWidth; } else { emulatorDimensions.height = MIN_HEIGHT * ratioHeight; emulatorDimensions.width = MIN_WIDTH * ratioHeight; } return emulatorDimensions; }