import * as React from 'react'; import CenterElement from '/components/center-element'; import FormSelectFiles from '/components/form-select-files'; import CanvasGBAEmulator from '/components/canvas-gba-emulator'; import OverlayControls from '/components/overlay-controls'; import OverlayMenu from '/components/overlay-menu'; import OverlaySelectFiles from '/components/overlay-select-files'; import CloseButton from '/components/close-button'; import {sendHello, handleSendFrame} from '/packet'; import Endian from '/endian'; import {MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_SEND_FRAME} from '/constants'; export interface handleClickStartEmulationButtonObjectArgs { e: React.MouseEvent; inputRom: HTMLInputElement | null; inputSaveState: HTMLInputElement | null; canvas: HTMLCanvasElement | null; printingFrame: boolean; setPrintingFrame: (c: boolean) => void; setEmulationStarted: (c: boolean) => void, setHiddenMenu: (c: boolean) => void; setHiddenFormSelectFiles: (c: boolean) => void; setWebSocket: (c: WebSocket) => void; }; function handleClickStartEmulationButton({e, inputRom, inputSaveState, canvas, printingFrame, setPrintingFrame, setEmulationStarted, setHiddenMenu, setHiddenFormSelectFiles, setWebSocket}: handleClickStartEmulationButtonObjectArgs) { e.preventDefault(); if (canvas == null) { alert('Canvas does not exists?'); return; } const ctx = canvas.getContext('2d') if (ctx == null) { alert('Unable to create canvas context, doing nothing'); return; } if (inputRom == null || inputSaveState == null || inputRom.files == null || inputSaveState.files == null) { alert('Unable to read the files '); return; } 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://${window.location.host}/ws`); setWebSocket(webSocket); webSocket.binaryType = 'arraybuffer'; webSocket.onclose = (message) => { setEmulationStarted(false); console.log('Closing websocket.'); } webSocket.onopen = () => { console.log('Opened websocket.'); setEmulationStarted(true); sendHello(webSocket, rom_array, savestate_array); setHiddenMenu(true); setHiddenFormSelectFiles(true); }; setPrintingFrame(false); webSocket.addEventListener('message', (event) => { onWebSocketPacket(event, canvas, ctx, printingFrame, setPrintingFrame) }); }); }); } function onWebSocketPacket(event: MessageEvent, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean, setPrintingFrame: (c: boolean) => void) { const buffer = event.data; let packet_u8: Uint8Array | null = new Uint8Array(buffer); const id = Endian.byteArrayToU64BigEndian(packet_u8.slice(0, 8)); packet_u8 = packet_u8.slice(8, packet_u8.length); const size = Endian.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, printingFrame, setPrintingFrame); break; default: console.log(`Received unknown packet ${id}`); } } export default function Page() { const screenDimensions = useScreenDimensions(); const emulatorDimensions = calculateSizeEmulator(screenDimensions); const canvasRef = React.useRef(null); function resizeCanvas() { const canvas = canvasRef.current; if (canvas == null) { return; } if (emulatorDimensions.width === undefined || emulatorDimensions.height === undefined) { return; } canvas.width = emulatorDimensions.width; canvas.height = emulatorDimensions.height; const ctx = canvas.getContext('2d') if (ctx == null) { return; } fillBlack(canvas, ctx); }; const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = React.useState(true); const [printingFrame, setPrintingFrame] = React.useState(false); React.useEffect(resizeCanvas, [emulatorDimensions]); const refInputRom = React.useRef(null); const refInputSaveState = React.useRef(null); const [emulationStarted, setEmulationStarted] = React.useState(false); const [hiddenMenu, setHiddenMenu] = React.useState(true); const [webSocket, setWebSocket] = React.useState(null); const onStartEmulation = (e: React.MouseEvent) => { handleClickStartEmulationButton({ e: e, setEmulationStarted: setEmulationStarted, inputRom: refInputRom.current, inputSaveState: refInputSaveState.current, canvas: canvasRef.current, setPrintingFrame: setPrintingFrame, printingFrame: printingFrame, setHiddenMenu: setHiddenMenu, setHiddenFormSelectFiles: setHiddenFormSelectFiles, setWebSocket: setWebSocket, }); }; const firstMenuElement = React.useRef(null); const screenRef = React.useRef(null); const [isFullscreen, setIsFullscreen] = React.useState(false); return (
); } 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: HTMLCanvasElement, ctx: CanvasRenderingContext2D) { ctx.beginPath(); ctx.rect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#0E0E10'; ctx.fill(); } export interface EmulatorDimensions { width?: number; height?: number; }; function calculateSizeEmulator(screenDimensions: EmulatorDimensions): EmulatorDimensions { if (screenDimensions.width === undefined || screenDimensions.height === undefined) { console.error(screenDimensions, 'screenDimensions has undefined fields'); return {}; } const width = screenDimensions.width; const height = screenDimensions.height; const emulatorDimensions: EmulatorDimensions = {}; if (width < MIN_WIDTH || height < MIN_HEIGHT) { return { width: MIN_WIDTH, height: MIN_HEIGHT, }; } const ratioWidth = width / MIN_WIDTH; const ratioHeight = 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; }