From 7ffeb360e7c8587846d546f8698ea08fccdb3887 Mon Sep 17 00:00:00 2001 From: Sergiotarxz Date: Wed, 29 Mar 2023 16:40:50 +0200 Subject: [PATCH] Adding GBC support and keyboard navigation. --- js-src/components/canvas-gba-emulator.tsx | 8 +-- js-src/components/overlay-controls.tsx | 16 ++--- js-src/components/page.tsx | 76 ++++++++++++++++------- js-src/constants.ts | 6 +- js-src/packet.ts | 23 +++++-- public/js/bundle.js | 10 +-- 6 files changed, 95 insertions(+), 44 deletions(-) diff --git a/js-src/components/canvas-gba-emulator.tsx b/js-src/components/canvas-gba-emulator.tsx index a65ab63..f9f0ffb 100644 --- a/js-src/components/canvas-gba-emulator.tsx +++ b/js-src/components/canvas-gba-emulator.tsx @@ -1,11 +1,11 @@ -import * as React from 'react'; +import * as React from 'react' -import {MIN_WIDTH, MIN_HEIGHT} from '/constants'; +import { MIN_WIDTH_GBA, MIN_HEIGHT_GBA } from '@msgba/constants' export interface CanvasGBAEmulatorProps { canvasRef: React.RefObject } -export default function CanvasGBAEmulator(props: CanvasGBAEmulatorProps) { - return (); +export default function CanvasGBAEmulator (props: CanvasGBAEmulatorProps): JSX.Element { + return () } diff --git a/js-src/components/overlay-controls.tsx b/js-src/components/overlay-controls.tsx index 46417d0..ce1f9c8 100644 --- a/js-src/components/overlay-controls.tsx +++ b/js-src/components/overlay-controls.tsx @@ -4,6 +4,7 @@ import { sendKeyDown } from '@msgba/packet' export interface OverlayControlsProps { firstMenuElement: React.RefObject + controlsRef: React.RefObject setHiddenMenu: (c: boolean) => void webSocket: WebSocket | null }; @@ -19,15 +20,9 @@ interface ControlValue { type ControlMap = Record -export default function OverlayControls ({ firstMenuElement, setHiddenMenu, webSocket }: OverlayControlsProps): JSX.Element { +export default function OverlayControls ({ firstMenuElement, setHiddenMenu, webSocket, controlsRef }: OverlayControlsProps): JSX.Element { function showOverlayMenu (): void { setHiddenMenu(false) - setTimeout(() => { - if (firstMenuElement.current == null) { - return - } - firstMenuElement.current.focus() - }, 100) } const onGoingTouches = new Map() @@ -212,6 +207,11 @@ export default function OverlayControls ({ firstMenuElement, setHiddenMenu, webS 'Enter', 'Space', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'] function onPressControl (e: React.KeyboardEvent): void { + if (e.code === 'Escape') { + e.preventDefault() + showOverlayMenu() + return + } if (webSocket == null) { console.log('There is not websocket') return @@ -235,7 +235,7 @@ export default function OverlayControls ({ firstMenuElement, setHiddenMenu, webS } document.onselectstart = () => false return ( -
+
diff --git a/js-src/components/page.tsx b/js-src/components/page.tsx index fa9ad06..8aab085 100644 --- a/js-src/components/page.tsx +++ b/js-src/components/page.tsx @@ -9,7 +9,7 @@ import { sendHello, handleSendFrame } from '@msgba/packet' import Endian from '@msgba/endian' -import { MIN_WIDTH, MIN_HEIGHT, PACKET_ID_SEND_FRAME } from '@msgba/constants' +import { MIN_WIDTH_GBA, MIN_HEIGHT_GBA, MIN_WIDTH_GBC, MIN_HEIGHT_GBC, PACKET_ID_SEND_FRAME } from '@msgba/constants' export interface handleClickStartEmulationButtonObjectArgs { e: React.MouseEvent @@ -20,12 +20,16 @@ export interface handleClickStartEmulationButtonObjectArgs { setHiddenMenu: (c: boolean) => void setHiddenFormSelectFiles: (c: boolean) => void setWebSocket: (c: WebSocket | null) => void + isGBC: boolean + setIsGBC: (c: boolean) => void + controlsRef: React.RefObject } function handleClickStartEmulationButton ({ e, inputRom, inputSaveState, canvas, setEmulationStarted, setHiddenMenu, - setHiddenFormSelectFiles, setWebSocket + setHiddenFormSelectFiles, setWebSocket, + isGBC, setIsGBC, controlsRef }: handleClickStartEmulationButtonObjectArgs): void { e.preventDefault() if (canvas.current == null) { @@ -71,7 +75,7 @@ function handleClickStartEmulationButton ({ setHiddenFormSelectFiles(true) } webSocket.addEventListener('message', (event) => { - onWebSocketPacket(event, canvas.current, ctx) + onWebSocketPacket(event, canvas.current, ctx, isGBC, setIsGBC) }) }).catch((c: string) => { console.log('Unable to convert file to array_buffer') @@ -83,7 +87,8 @@ function handleClickStartEmulationButton ({ function onWebSocketPacket (event: MessageEvent, canvas: HTMLCanvasElement | null, - ctx: CanvasRenderingContext2D): void { + ctx: CanvasRenderingContext2D, + isGBC: boolean, setIsGBC: (c: boolean) => void): void { const buffer = event.data let packetU8: Uint8Array | null = new Uint8Array(buffer) const id = Endian.byteArrayToU64BigEndian(packetU8.slice(0, 8)) @@ -93,7 +98,7 @@ function onWebSocketPacket (event: MessageEvent, packetU8 = null switch (id) { case PACKET_ID_SEND_FRAME: - handleSendFrame(rawData, canvas, ctx) + handleSendFrame(rawData, canvas, setIsGBC) break default: console.log(`Received unknown packet ${id}`) @@ -101,8 +106,9 @@ function onWebSocketPacket (event: MessageEvent, } export default function Page (): JSX.Element { - const screenDimensions = useScreenDimensions() - const emulatorDimensions = calculateSizeEmulator(screenDimensions) + const [isGBC, setIsGBC] = React.useState(false) + const screenDimensions = useScreenDimensions(isGBC) + const emulatorDimensions = calculateSizeEmulator(screenDimensions, isGBC) const canvasRef = React.useRef(null) function resizeCanvas (): void { const canvas = canvasRef.current @@ -127,6 +133,29 @@ export default function Page (): JSX.Element { const [emulationStarted, setEmulationStarted] = React.useState(false) const [hiddenMenu, setHiddenMenu] = React.useState(true) const [webSocket, setWebSocket] = React.useState(null) + + const controlsRef = React.useRef(null) + React.useEffect(() => { + console.log('Focusing the main screen') + setTimeout(() => { + if (!hiddenFormSelectFiles) { + if (refInputRom.current != null) { + refInputRom.current.focus() + } + return + } + if (!hiddenMenu) { + if (firstMenuElement.current != null) { + firstMenuElement.current.focus() + } + return + } + + if (controlsRef.current != null && hiddenMenu) { + controlsRef.current.focus() + } + }, 100) + }, [hiddenMenu, hiddenFormSelectFiles]) const onStartEmulation = (e: React.MouseEvent): void => { handleClickStartEmulationButton({ e, @@ -136,7 +165,10 @@ export default function Page (): JSX.Element { canvas: canvasRef, setHiddenMenu, setHiddenFormSelectFiles, - setWebSocket + setWebSocket, + isGBC, + setIsGBC, + controlsRef }) } const firstMenuElement = React.useRef(null) @@ -144,7 +176,7 @@ export default function Page (): JSX.Element { const [isFullscreen, setIsFullscreen] = React.useState(false) return (
- (getScreenDimensions()) React.useEffect(() => { @@ -184,7 +216,7 @@ function useScreenDimensions (): EmulatorDimensions { return () => { window.removeEventListener('resize', onResize) } - }, []) + }, [isGBC]) return screenDimensions } @@ -200,7 +232,7 @@ export interface EmulatorDimensions { height?: number }; -function calculateSizeEmulator (screenDimensions: EmulatorDimensions): EmulatorDimensions { +function calculateSizeEmulator (screenDimensions: EmulatorDimensions, isGBC: boolean): EmulatorDimensions { if (screenDimensions.width === undefined || screenDimensions.height === undefined) { console.error(screenDimensions, 'screenDimensions has undefined fields') return {} @@ -208,20 +240,22 @@ function calculateSizeEmulator (screenDimensions: EmulatorDimensions): EmulatorD const width = screenDimensions.width const height = screenDimensions.height const emulatorDimensions: EmulatorDimensions = {} - if (width < MIN_WIDTH || height < MIN_HEIGHT) { + const minWidth = !isGBC ? MIN_WIDTH_GBA : MIN_WIDTH_GBC + const minHeight = !isGBC ? MIN_HEIGHT_GBA : MIN_HEIGHT_GBC + if (width < minWidth || height < minHeight) { return { - width: MIN_WIDTH, - height: MIN_HEIGHT + width: minWidth, + height: minHeight } } - const ratioWidth = width / MIN_WIDTH - const ratioHeight = height / MIN_HEIGHT + const ratioWidth = width / minWidth + const ratioHeight = height / minHeight if (ratioWidth < ratioHeight) { - emulatorDimensions.width = MIN_WIDTH * ratioWidth - emulatorDimensions.height = MIN_HEIGHT * ratioWidth + emulatorDimensions.width = minWidth * ratioWidth + emulatorDimensions.height = minHeight * ratioWidth } else { - emulatorDimensions.height = MIN_HEIGHT * ratioHeight - emulatorDimensions.width = MIN_WIDTH * ratioHeight + emulatorDimensions.height = minHeight * ratioHeight + emulatorDimensions.width = minWidth * ratioHeight } return emulatorDimensions } diff --git a/js-src/constants.ts b/js-src/constants.ts index 13e1945..4325459 100644 --- a/js-src/constants.ts +++ b/js-src/constants.ts @@ -1,5 +1,7 @@ -export const MIN_WIDTH = 240 -export const MIN_HEIGHT = 160 +export const MIN_WIDTH_GBA = 240 +export const MIN_HEIGHT_GBA = 160 +export const MIN_WIDTH_GBC = 160 +export const MIN_HEIGHT_GBC = 144 export const PACKET_ID_HELLO = 0n export const PACKET_ID_SEND_FRAME = 1n export const PACKET_ID_KEY_DOWN = 2n diff --git a/js-src/packet.ts b/js-src/packet.ts index 4907423..5beff7b 100644 --- a/js-src/packet.ts +++ b/js-src/packet.ts @@ -1,5 +1,5 @@ import Endian from '@msgba/endian' -import { MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_KEY_DOWN, PACKET_ID_SAVE_REQUEST } from '@msgba/constants' +import { MIN_WIDTH_GBC, MIN_WIDTH_GBA, MIN_HEIGHT_GBA, MIN_HEIGHT_GBC, PACKET_ID_HELLO, PACKET_ID_KEY_DOWN, PACKET_ID_SAVE_REQUEST } from '@msgba/constants' function concatU8Array (array1: Uint8Array, array2: Uint8Array): Uint8Array { const finalArray = new Uint8Array(array1.length + array2.length) @@ -47,18 +47,33 @@ export function sendPacket (websocket: WebSocket, id: bigint, rawData: Uint8Arra websocket.send(packetBuffer) } -export function handleSendFrame (rawData: Uint8Array, canvas: HTMLCanvasElement | null, ctx: CanvasRenderingContext2D): void { - console.log('Reachs here') +export function handleSendFrame (rawData: Uint8Array, canvas: HTMLCanvasElement | null, setIsGBC: (c: boolean) => void): void { if (canvas == null) { console.log('No canvas') return } + const ctx = canvas.getContext('2d') + if (ctx == null) { + console.log('No context') + return + } let data: Uint8Array | null = rawData + const stride = Endian.byteArrayToU32BigEndian(data.slice(0, 4)) + let isGBC = false + if (stride === 160) { + isGBC = true + setIsGBC(true) + } else { + setIsGBC(false) + } data = data.slice(4, data.length) const outputBufferSize = Endian.byteArrayToU64BigEndian(data.slice(0, 8)) // TODO: This number conversion is not great. Is there other option? data = data.slice(8, Number(outputBufferSize)) - const imgData = ctx.createImageData(MIN_WIDTH, MIN_HEIGHT) + let imgData = ctx.createImageData(MIN_WIDTH_GBA, MIN_HEIGHT_GBA) + if (isGBC) { + imgData = ctx.createImageData(MIN_WIDTH_GBC, MIN_HEIGHT_GBC) + } const imgDataU8 = new Uint8Array(imgData.data.buffer) for (let i = 0; i < data.length; i++) { if (i % 4 === 3) { diff --git a/public/js/bundle.js b/public/js/bundle.js index c61ce14..b1c4722 100644 --- a/public/js/bundle.js +++ b/public/js/bundle.js @@ -96,7 +96,7 @@ eval("\n\nif (false) {} else {\n module.exports = __webpack_require__(/*! ./cjs \***************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ CanvasGBAEmulator)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../constants */ \"./js-src/constants.ts\");\n\n\nfunction CanvasGBAEmulator(props) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"canvas\", { ref: props.canvasRef, width: _constants__WEBPACK_IMPORTED_MODULE_1__.MIN_WIDTH, height: _constants__WEBPACK_IMPORTED_MODULE_1__.MIN_HEIGHT }));\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/components/canvas-gba-emulator.tsx?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ CanvasGBAEmulator)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _msgba_constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @msgba/constants */ \"./js-src/constants.ts\");\n\n\nfunction CanvasGBAEmulator(props) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"canvas\", { ref: props.canvasRef, width: _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.MIN_WIDTH_GBA, height: _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.MIN_HEIGHT_GBA }));\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/components/canvas-gba-emulator.tsx?"); /***/ }), @@ -136,7 +136,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ OverlayControls)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _msgba_constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @msgba/constants */ \"./js-src/constants.ts\");\n/* harmony import */ var _msgba_packet__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @msgba/packet */ \"./js-src/packet.ts\");\n\n\n\n;\nfunction OverlayControls({ firstMenuElement, setHiddenMenu, webSocket }) {\n function showOverlayMenu() {\n setHiddenMenu(false);\n setTimeout(() => {\n if (firstMenuElement.current == null) {\n return;\n }\n firstMenuElement.current.focus();\n }, 100);\n }\n const onGoingTouches = new Map();\n function mouseDown(e, key) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n function mouseUp(e, key) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, key);\n }\n const controls = {};\n controls.a = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 0,\n sym: 'A',\n classes: 'control-a control-button-a-b control control-button'\n };\n controls.b = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 1,\n sym: 'B',\n transformX: 50,\n classes: 'control-b control-button-a-b control control-button'\n };\n controls.l = {\n key: 2,\n sym: 'L',\n classes: 'control-l control-button-l-r control',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.r = {\n key: 3,\n sym: 'R',\n classes: 'control-r control-button-l-r control',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.start = {\n key: 4,\n sym: 'START',\n classes: 'control-start control-button-start-select control',\n transformX: 25,\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.select = {\n key: 5,\n sym: 'SEL',\n classes: 'control-select control-button-start-select control',\n transformX: -25,\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.up = {\n key: 6,\n sym: '^',\n transformX: 100,\n classes: 'control-up control control-pad-button',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.down = {\n key: 7,\n sym: 'v',\n transformX: 100,\n classes: 'control-down control control-pad-button',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.left = {\n key: 8,\n sym: '<',\n classes: 'control-left control control-pad-button',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.right = {\n key: 9,\n sym: '>',\n transformX: 200,\n classes: 'control-right control control-pad-button',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n function determineKey(e, touch) {\n const x = touch.pageX;\n const y = touch.pageY;\n for (const control of Object.keys(controls)) {\n const ref = controls[control].ref.current;\n if (ref == null) {\n console.log('No ref found');\n continue;\n }\n const top = ref.getBoundingClientRect().top + document.documentElement.scrollTop;\n const currentControl = controls[control];\n const transformX = currentControl.transformX;\n const transformY = currentControl.transformY;\n let offsetLeft = ref.offsetLeft;\n const offsetWidth = ref.offsetWidth;\n let offsetTop = top;\n const offsetHeight = ref.offsetHeight;\n if (transformX != null) {\n offsetLeft += offsetWidth * (transformX / 100);\n }\n if (transformY != null) {\n offsetTop += offsetHeight * (transformY / 100);\n }\n if (x >= offsetLeft && x <= offsetLeft + offsetWidth && y >= offsetTop && y <= offsetTop + offsetHeight) {\n return controls[control].key;\n }\n }\n return null;\n }\n function touchStartControls(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return false;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const key = determineKey(e, touch);\n if (key == null) {\n continue;\n }\n const idx = touch.identifier;\n onGoingTouches.set(idx, key);\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n return false;\n }\n function touchMoveControls(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return false;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const key = determineKey(e, touch);\n const idx = touch.identifier;\n if (key == null) {\n continue;\n }\n const oldKey = onGoingTouches.get(idx);\n if (oldKey != null) {\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, oldKey);\n onGoingTouches.delete(idx);\n }\n onGoingTouches.set(idx, key);\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n return false;\n }\n function touchEndControls(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return false;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const idx = touch.identifier;\n const oldKey = onGoingTouches.get(idx);\n if (oldKey == null) {\n return false;\n }\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, oldKey);\n onGoingTouches.delete(idx);\n }\n return false;\n }\n const keyMap = ['KeyZ', 'KeyX', 'KeyA', 'KeyS',\n 'Enter', 'Space', 'ArrowUp', 'ArrowDown',\n 'ArrowLeft', 'ArrowRight'];\n function onPressControl(e) {\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n const key = keyMap.findIndex((c) => c === e.code);\n if (key !== -1) {\n e.preventDefault();\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n }\n function onUnpressControl(e) {\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n const key = keyMap.findIndex((c) => c === e.code);\n if (key !== -1) {\n e.preventDefault();\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, key);\n }\n }\n document.onselectstart = () => false;\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { tabIndex: -1, className: \"overlay\", onKeyDown: onPressControl, onKeyUp: onUnpressControl },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"vertical-padding\" }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"controls\", onTouchStart: touchStartControls, onTouchMove: touchMoveControls, onTouchEnd: touchEndControls },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, className: \"gear control\", onClick: showOverlayMenu, onTouchStart: showOverlayMenu },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"img\", { src: _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.HOME_BUTTON_IMAGE, alt: \"Go to menu. (House icon)\" })),\n Object.keys(controls).map((key) => react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, className: controls[key].classes, ref: controls[key].ref, key: key, onMouseDown: (e) => { mouseDown(e, controls[key].key); }, onMouseUp: (e) => { mouseUp(e, controls[key].key); } }, controls[key].sym)))));\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/components/overlay-controls.tsx?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ OverlayControls)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _msgba_constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @msgba/constants */ \"./js-src/constants.ts\");\n/* harmony import */ var _msgba_packet__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @msgba/packet */ \"./js-src/packet.ts\");\n\n\n\n;\nfunction OverlayControls({ firstMenuElement, setHiddenMenu, webSocket, controlsRef }) {\n function showOverlayMenu() {\n setHiddenMenu(false);\n }\n const onGoingTouches = new Map();\n function mouseDown(e, key) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n function mouseUp(e, key) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, key);\n }\n const controls = {};\n controls.a = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 0,\n sym: 'A',\n classes: 'control-a control-button-a-b control control-button'\n };\n controls.b = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 1,\n sym: 'B',\n transformX: 50,\n classes: 'control-b control-button-a-b control control-button'\n };\n controls.l = {\n key: 2,\n sym: 'L',\n classes: 'control-l control-button-l-r control',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.r = {\n key: 3,\n sym: 'R',\n classes: 'control-r control-button-l-r control',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.start = {\n key: 4,\n sym: 'START',\n classes: 'control-start control-button-start-select control',\n transformX: 25,\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.select = {\n key: 5,\n sym: 'SEL',\n classes: 'control-select control-button-start-select control',\n transformX: -25,\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.up = {\n key: 6,\n sym: '^',\n transformX: 100,\n classes: 'control-up control control-pad-button',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.down = {\n key: 7,\n sym: 'v',\n transformX: 100,\n classes: 'control-down control control-pad-button',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.left = {\n key: 8,\n sym: '<',\n classes: 'control-left control control-pad-button',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.right = {\n key: 9,\n sym: '>',\n transformX: 200,\n classes: 'control-right control control-pad-button',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n function determineKey(e, touch) {\n const x = touch.pageX;\n const y = touch.pageY;\n for (const control of Object.keys(controls)) {\n const ref = controls[control].ref.current;\n if (ref == null) {\n console.log('No ref found');\n continue;\n }\n const top = ref.getBoundingClientRect().top + document.documentElement.scrollTop;\n const currentControl = controls[control];\n const transformX = currentControl.transformX;\n const transformY = currentControl.transformY;\n let offsetLeft = ref.offsetLeft;\n const offsetWidth = ref.offsetWidth;\n let offsetTop = top;\n const offsetHeight = ref.offsetHeight;\n if (transformX != null) {\n offsetLeft += offsetWidth * (transformX / 100);\n }\n if (transformY != null) {\n offsetTop += offsetHeight * (transformY / 100);\n }\n if (x >= offsetLeft && x <= offsetLeft + offsetWidth && y >= offsetTop && y <= offsetTop + offsetHeight) {\n return controls[control].key;\n }\n }\n return null;\n }\n function touchStartControls(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return false;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const key = determineKey(e, touch);\n if (key == null) {\n continue;\n }\n const idx = touch.identifier;\n onGoingTouches.set(idx, key);\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n return false;\n }\n function touchMoveControls(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return false;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const key = determineKey(e, touch);\n const idx = touch.identifier;\n if (key == null) {\n continue;\n }\n const oldKey = onGoingTouches.get(idx);\n if (oldKey != null) {\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, oldKey);\n onGoingTouches.delete(idx);\n }\n onGoingTouches.set(idx, key);\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n return false;\n }\n function touchEndControls(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return false;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const idx = touch.identifier;\n const oldKey = onGoingTouches.get(idx);\n if (oldKey == null) {\n return false;\n }\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, oldKey);\n onGoingTouches.delete(idx);\n }\n return false;\n }\n const keyMap = ['KeyZ', 'KeyX', 'KeyA', 'KeyS',\n 'Enter', 'Space', 'ArrowUp', 'ArrowDown',\n 'ArrowLeft', 'ArrowRight'];\n function onPressControl(e) {\n if (e.code === 'Escape') {\n e.preventDefault();\n showOverlayMenu();\n return;\n }\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n const key = keyMap.findIndex((c) => c === e.code);\n if (key !== -1) {\n e.preventDefault();\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n }\n function onUnpressControl(e) {\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n const key = keyMap.findIndex((c) => c === e.code);\n if (key !== -1) {\n e.preventDefault();\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, key);\n }\n }\n document.onselectstart = () => false;\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { ref: controlsRef, tabIndex: -1, className: \"overlay\", onKeyDown: onPressControl, onKeyUp: onUnpressControl },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"vertical-padding\" }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"controls\", onTouchStart: touchStartControls, onTouchMove: touchMoveControls, onTouchEnd: touchEndControls },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, className: \"gear control\", onClick: showOverlayMenu, onTouchStart: showOverlayMenu },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"img\", { src: _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.HOME_BUTTON_IMAGE, alt: \"Go to menu. (House icon)\" })),\n Object.keys(controls).map((key) => react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, className: controls[key].classes, ref: controls[key].ref, key: key, onMouseDown: (e) => { mouseDown(e, controls[key].key); }, onMouseUp: (e) => { mouseUp(e, controls[key].key); } }, controls[key].sym)))));\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/components/overlay-controls.tsx?"); /***/ }), @@ -156,7 +156,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Page)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _msgba_components_center_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @msgba/components/center-element */ \"./js-src/components/center-element.tsx\");\n/* harmony import */ var _msgba_components_canvas_gba_emulator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @msgba/components/canvas-gba-emulator */ \"./js-src/components/canvas-gba-emulator.tsx\");\n/* harmony import */ var _msgba_components_overlay_controls__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @msgba/components/overlay-controls */ \"./js-src/components/overlay-controls.tsx\");\n/* harmony import */ var _msgba_components_overlay_menu__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @msgba/components/overlay-menu */ \"./js-src/components/overlay-menu.js\");\n/* harmony import */ var _msgba_components_overlay_select_files__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @msgba/components/overlay-select-files */ \"./js-src/components/overlay-select-files.tsx\");\n/* harmony import */ var _msgba_packet__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @msgba/packet */ \"./js-src/packet.ts\");\n/* harmony import */ var _msgba_endian__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @msgba/endian */ \"./js-src/endian.ts\");\n/* harmony import */ var _msgba_constants__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @msgba/constants */ \"./js-src/constants.ts\");\n\n\n\n\n\n\n\n\n\nfunction handleClickStartEmulationButton({ e, inputRom, inputSaveState, canvas, setEmulationStarted, setHiddenMenu, setHiddenFormSelectFiles, setWebSocket }) {\n e.preventDefault();\n if (canvas.current == null) {\n alert('Canvas does not exists?');\n return;\n }\n const ctx = canvas.current.getContext('2d');\n if (ctx == null) {\n alert('Unable to create canvas context, doing nothing');\n return;\n }\n if (inputRom == null || inputSaveState == null || inputRom.files == null || inputSaveState.files == null) {\n alert('Unable to read the files ');\n return;\n }\n if (inputRom.files.length === 0) {\n alert('There is no rom still');\n return;\n }\n if (inputSaveState.files.length === 0) {\n alert('There is no savestate still');\n return;\n }\n const romFile = inputRom.files[0];\n const savestateFile = inputSaveState.files[0];\n romFile.arrayBuffer().then((romBuffer) => {\n savestateFile.arrayBuffer().then((savestateBuffer) => {\n const romArray = new Uint8Array(romBuffer);\n const savestateArray = new Uint8Array(savestateBuffer);\n const webSocket = new WebSocket(`ws://${window.location.host}/ws`);\n setWebSocket(webSocket);\n webSocket.binaryType = 'arraybuffer';\n webSocket.onclose = (message) => {\n setEmulationStarted(false);\n console.log('Closing websocket.');\n setWebSocket(null);\n };\n webSocket.onopen = () => {\n console.log('Opened websocket.');\n setEmulationStarted(true);\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_6__.sendHello)(webSocket, romArray, savestateArray);\n setHiddenMenu(true);\n setHiddenFormSelectFiles(true);\n };\n webSocket.addEventListener('message', (event) => {\n onWebSocketPacket(event, canvas.current, ctx);\n });\n }).catch((c) => {\n console.log('Unable to convert file to array_buffer');\n });\n }).catch((c) => {\n console.log('Unable to convert file to array_buffer');\n });\n}\nfunction onWebSocketPacket(event, canvas, ctx) {\n const buffer = event.data;\n let packetU8 = new Uint8Array(buffer);\n const id = _msgba_endian__WEBPACK_IMPORTED_MODULE_7__[\"default\"].byteArrayToU64BigEndian(packetU8.slice(0, 8));\n packetU8 = packetU8.slice(8, packetU8.length);\n const size = _msgba_endian__WEBPACK_IMPORTED_MODULE_7__[\"default\"].byteArrayToU64BigEndian(packetU8.slice(0, 8));\n const rawData = packetU8.slice(8, Number(size));\n packetU8 = null;\n switch (id) {\n case _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.PACKET_ID_SEND_FRAME:\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_6__.handleSendFrame)(rawData, canvas, ctx);\n break;\n default:\n console.log(`Received unknown packet ${id}`);\n }\n}\nfunction Page() {\n const screenDimensions = useScreenDimensions();\n const emulatorDimensions = calculateSizeEmulator(screenDimensions);\n const canvasRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n function resizeCanvas() {\n const canvas = canvasRef.current;\n if (canvas == null) {\n return;\n }\n if (emulatorDimensions.width === undefined || emulatorDimensions.height === undefined) {\n return;\n }\n canvas.width = emulatorDimensions.width;\n canvas.height = emulatorDimensions.height;\n const ctx = canvas.getContext('2d');\n if (ctx == null) {\n return;\n }\n fillBlack(canvas, ctx);\n }\n const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = react__WEBPACK_IMPORTED_MODULE_0__.useState(true);\n react__WEBPACK_IMPORTED_MODULE_0__.useEffect(resizeCanvas, [emulatorDimensions]);\n const refInputRom = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const refInputSaveState = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const [emulationStarted, setEmulationStarted] = react__WEBPACK_IMPORTED_MODULE_0__.useState(false);\n const [hiddenMenu, setHiddenMenu] = react__WEBPACK_IMPORTED_MODULE_0__.useState(true);\n const [webSocket, setWebSocket] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const onStartEmulation = (e) => {\n handleClickStartEmulationButton({\n e,\n setEmulationStarted,\n inputRom: refInputRom.current,\n inputSaveState: refInputSaveState.current,\n canvas: canvasRef,\n setHiddenMenu,\n setHiddenFormSelectFiles,\n setWebSocket\n });\n };\n const firstMenuElement = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const screenRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const [isFullscreen, setIsFullscreen] = react__WEBPACK_IMPORTED_MODULE_0__.useState(false);\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { ref: screenRef },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_overlay_controls__WEBPACK_IMPORTED_MODULE_3__[\"default\"], { firstMenuElement: firstMenuElement, setHiddenMenu: setHiddenMenu, webSocket: webSocket }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_overlay_menu__WEBPACK_IMPORTED_MODULE_4__[\"default\"], { hiddenMenu: hiddenMenu, setHiddenMenu: setHiddenMenu, emulationStarted: emulationStarted, setHiddenFormSelectFiles: setHiddenFormSelectFiles, screenRef: screenRef, isFullscreen: isFullscreen, setIsFullscreen: setIsFullscreen, firstMenuElement: firstMenuElement, websocket: webSocket }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_overlay_select_files__WEBPACK_IMPORTED_MODULE_5__[\"default\"], { hiddenFormSelectFiles: hiddenFormSelectFiles, setHiddenFormSelectFiles: setHiddenFormSelectFiles, refInputRom: refInputRom, refInputSaveState: refInputSaveState, onStartEmulation: onStartEmulation }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_center_element__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { full: true },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_canvas_gba_emulator__WEBPACK_IMPORTED_MODULE_2__[\"default\"], { canvasRef: canvasRef })))));\n}\nfunction getScreenDimensions() {\n return {\n width: document.body.clientWidth,\n height: document.body.clientHeight\n };\n}\nfunction useScreenDimensions() {\n const [screenDimensions, setScreenDimensions] = react__WEBPACK_IMPORTED_MODULE_0__.useState(getScreenDimensions());\n react__WEBPACK_IMPORTED_MODULE_0__.useEffect(() => {\n function onResize() {\n setScreenDimensions(getScreenDimensions());\n }\n window.addEventListener('resize', onResize);\n return () => {\n window.removeEventListener('resize', onResize);\n };\n }, []);\n return screenDimensions;\n}\nfunction fillBlack(canvas, ctx) {\n ctx.beginPath();\n ctx.rect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = '#0E0E10';\n ctx.fill();\n}\n;\nfunction calculateSizeEmulator(screenDimensions) {\n if (screenDimensions.width === undefined || screenDimensions.height === undefined) {\n console.error(screenDimensions, 'screenDimensions has undefined fields');\n return {};\n }\n const width = screenDimensions.width;\n const height = screenDimensions.height;\n const emulatorDimensions = {};\n if (width < _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH || height < _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT) {\n return {\n width: _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH,\n height: _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT\n };\n }\n const ratioWidth = width / _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH;\n const ratioHeight = height / _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT;\n if (ratioWidth < ratioHeight) {\n emulatorDimensions.width = _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH * ratioWidth;\n emulatorDimensions.height = _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT * ratioWidth;\n }\n else {\n emulatorDimensions.height = _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT * ratioHeight;\n emulatorDimensions.width = _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH * ratioHeight;\n }\n return emulatorDimensions;\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/components/page.tsx?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Page)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _msgba_components_center_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @msgba/components/center-element */ \"./js-src/components/center-element.tsx\");\n/* harmony import */ var _msgba_components_canvas_gba_emulator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @msgba/components/canvas-gba-emulator */ \"./js-src/components/canvas-gba-emulator.tsx\");\n/* harmony import */ var _msgba_components_overlay_controls__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @msgba/components/overlay-controls */ \"./js-src/components/overlay-controls.tsx\");\n/* harmony import */ var _msgba_components_overlay_menu__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @msgba/components/overlay-menu */ \"./js-src/components/overlay-menu.js\");\n/* harmony import */ var _msgba_components_overlay_select_files__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @msgba/components/overlay-select-files */ \"./js-src/components/overlay-select-files.tsx\");\n/* harmony import */ var _msgba_packet__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @msgba/packet */ \"./js-src/packet.ts\");\n/* harmony import */ var _msgba_endian__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @msgba/endian */ \"./js-src/endian.ts\");\n/* harmony import */ var _msgba_constants__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @msgba/constants */ \"./js-src/constants.ts\");\n\n\n\n\n\n\n\n\n\nfunction handleClickStartEmulationButton({ e, inputRom, inputSaveState, canvas, setEmulationStarted, setHiddenMenu, setHiddenFormSelectFiles, setWebSocket, isGBC, setIsGBC, controlsRef }) {\n e.preventDefault();\n if (canvas.current == null) {\n alert('Canvas does not exists?');\n return;\n }\n const ctx = canvas.current.getContext('2d');\n if (ctx == null) {\n alert('Unable to create canvas context, doing nothing');\n return;\n }\n if (inputRom == null || inputSaveState == null || inputRom.files == null || inputSaveState.files == null) {\n alert('Unable to read the files ');\n return;\n }\n if (inputRom.files.length === 0) {\n alert('There is no rom still');\n return;\n }\n if (inputSaveState.files.length === 0) {\n alert('There is no savestate still');\n return;\n }\n const romFile = inputRom.files[0];\n const savestateFile = inputSaveState.files[0];\n romFile.arrayBuffer().then((romBuffer) => {\n savestateFile.arrayBuffer().then((savestateBuffer) => {\n const romArray = new Uint8Array(romBuffer);\n const savestateArray = new Uint8Array(savestateBuffer);\n const webSocket = new WebSocket(`ws://${window.location.host}/ws`);\n setWebSocket(webSocket);\n webSocket.binaryType = 'arraybuffer';\n webSocket.onclose = (message) => {\n setEmulationStarted(false);\n console.log('Closing websocket.');\n setWebSocket(null);\n };\n webSocket.onopen = () => {\n console.log('Opened websocket.');\n setEmulationStarted(true);\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_6__.sendHello)(webSocket, romArray, savestateArray);\n setHiddenMenu(true);\n setHiddenFormSelectFiles(true);\n };\n webSocket.addEventListener('message', (event) => {\n onWebSocketPacket(event, canvas.current, ctx, isGBC, setIsGBC);\n });\n }).catch((c) => {\n console.log('Unable to convert file to array_buffer');\n });\n }).catch((c) => {\n console.log('Unable to convert file to array_buffer');\n });\n}\nfunction onWebSocketPacket(event, canvas, ctx, isGBC, setIsGBC) {\n const buffer = event.data;\n let packetU8 = new Uint8Array(buffer);\n const id = _msgba_endian__WEBPACK_IMPORTED_MODULE_7__[\"default\"].byteArrayToU64BigEndian(packetU8.slice(0, 8));\n packetU8 = packetU8.slice(8, packetU8.length);\n const size = _msgba_endian__WEBPACK_IMPORTED_MODULE_7__[\"default\"].byteArrayToU64BigEndian(packetU8.slice(0, 8));\n const rawData = packetU8.slice(8, Number(size));\n packetU8 = null;\n switch (id) {\n case _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.PACKET_ID_SEND_FRAME:\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_6__.handleSendFrame)(rawData, canvas, setIsGBC);\n break;\n default:\n console.log(`Received unknown packet ${id}`);\n }\n}\nfunction Page() {\n const [isGBC, setIsGBC] = react__WEBPACK_IMPORTED_MODULE_0__.useState(false);\n const screenDimensions = useScreenDimensions(isGBC);\n const emulatorDimensions = calculateSizeEmulator(screenDimensions, isGBC);\n const canvasRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n function resizeCanvas() {\n const canvas = canvasRef.current;\n if (canvas == null) {\n return;\n }\n if (emulatorDimensions.width === undefined || emulatorDimensions.height === undefined) {\n return;\n }\n canvas.width = emulatorDimensions.width;\n canvas.height = emulatorDimensions.height;\n const ctx = canvas.getContext('2d');\n if (ctx == null) {\n return;\n }\n fillBlack(canvas, ctx);\n }\n const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = react__WEBPACK_IMPORTED_MODULE_0__.useState(true);\n react__WEBPACK_IMPORTED_MODULE_0__.useEffect(resizeCanvas, [emulatorDimensions]);\n const refInputRom = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const refInputSaveState = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const [emulationStarted, setEmulationStarted] = react__WEBPACK_IMPORTED_MODULE_0__.useState(false);\n const [hiddenMenu, setHiddenMenu] = react__WEBPACK_IMPORTED_MODULE_0__.useState(true);\n const [webSocket, setWebSocket] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const controlsRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n react__WEBPACK_IMPORTED_MODULE_0__.useEffect(() => {\n console.log('Focusing the main screen');\n setTimeout(() => {\n if (!hiddenFormSelectFiles) {\n if (refInputRom.current != null) {\n refInputRom.current.focus();\n }\n return;\n }\n if (!hiddenMenu) {\n if (firstMenuElement.current != null) {\n firstMenuElement.current.focus();\n }\n return;\n }\n if (controlsRef.current != null && hiddenMenu) {\n controlsRef.current.focus();\n }\n }, 100);\n }, [hiddenMenu, hiddenFormSelectFiles]);\n const onStartEmulation = (e) => {\n handleClickStartEmulationButton({\n e,\n setEmulationStarted,\n inputRom: refInputRom.current,\n inputSaveState: refInputSaveState.current,\n canvas: canvasRef,\n setHiddenMenu,\n setHiddenFormSelectFiles,\n setWebSocket,\n isGBC,\n setIsGBC,\n controlsRef\n });\n };\n const firstMenuElement = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const screenRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const [isFullscreen, setIsFullscreen] = react__WEBPACK_IMPORTED_MODULE_0__.useState(false);\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { ref: screenRef },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_overlay_controls__WEBPACK_IMPORTED_MODULE_3__[\"default\"], { controlsRef: controlsRef, firstMenuElement: firstMenuElement, setHiddenMenu: setHiddenMenu, webSocket: webSocket }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_overlay_menu__WEBPACK_IMPORTED_MODULE_4__[\"default\"], { hiddenMenu: hiddenMenu, setHiddenMenu: setHiddenMenu, emulationStarted: emulationStarted, setHiddenFormSelectFiles: setHiddenFormSelectFiles, screenRef: screenRef, isFullscreen: isFullscreen, setIsFullscreen: setIsFullscreen, firstMenuElement: firstMenuElement, websocket: webSocket }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_overlay_select_files__WEBPACK_IMPORTED_MODULE_5__[\"default\"], { hiddenFormSelectFiles: hiddenFormSelectFiles, setHiddenFormSelectFiles: setHiddenFormSelectFiles, refInputRom: refInputRom, refInputSaveState: refInputSaveState, onStartEmulation: onStartEmulation }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_center_element__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { full: true },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_canvas_gba_emulator__WEBPACK_IMPORTED_MODULE_2__[\"default\"], { canvasRef: canvasRef })))));\n}\nfunction getScreenDimensions() {\n return {\n width: document.body.clientWidth,\n height: document.body.clientHeight\n };\n}\nfunction useScreenDimensions(isGBC) {\n const [screenDimensions, setScreenDimensions] = react__WEBPACK_IMPORTED_MODULE_0__.useState(getScreenDimensions());\n react__WEBPACK_IMPORTED_MODULE_0__.useEffect(() => {\n function onResize() {\n setScreenDimensions(getScreenDimensions());\n }\n window.addEventListener('resize', onResize);\n return () => {\n window.removeEventListener('resize', onResize);\n };\n }, [isGBC]);\n return screenDimensions;\n}\nfunction fillBlack(canvas, ctx) {\n ctx.beginPath();\n ctx.rect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = '#0E0E10';\n ctx.fill();\n}\n;\nfunction calculateSizeEmulator(screenDimensions, isGBC) {\n if (screenDimensions.width === undefined || screenDimensions.height === undefined) {\n console.error(screenDimensions, 'screenDimensions has undefined fields');\n return {};\n }\n const width = screenDimensions.width;\n const height = screenDimensions.height;\n const emulatorDimensions = {};\n const minWidth = !isGBC ? _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH_GBA : _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH_GBC;\n const minHeight = !isGBC ? _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT_GBA : _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT_GBC;\n if (width < minWidth || height < minHeight) {\n return {\n width: minWidth,\n height: minHeight\n };\n }\n const ratioWidth = width / minWidth;\n const ratioHeight = height / minHeight;\n if (ratioWidth < ratioHeight) {\n emulatorDimensions.width = minWidth * ratioWidth;\n emulatorDimensions.height = minHeight * ratioWidth;\n }\n else {\n emulatorDimensions.height = minHeight * ratioHeight;\n emulatorDimensions.width = minWidth * ratioHeight;\n }\n return emulatorDimensions;\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/components/page.tsx?"); /***/ }), @@ -166,7 +166,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"CLOSE_BUTTON_IMAGE\": () => (/* binding */ CLOSE_BUTTON_IMAGE),\n/* harmony export */ \"HOME_BUTTON_IMAGE\": () => (/* binding */ HOME_BUTTON_IMAGE),\n/* harmony export */ \"MIN_HEIGHT\": () => (/* binding */ MIN_HEIGHT),\n/* harmony export */ \"MIN_WIDTH\": () => (/* binding */ MIN_WIDTH),\n/* harmony export */ \"PACKET_ID_HELLO\": () => (/* binding */ PACKET_ID_HELLO),\n/* harmony export */ \"PACKET_ID_KEY_DOWN\": () => (/* binding */ PACKET_ID_KEY_DOWN),\n/* harmony export */ \"PACKET_ID_SAVE_REQUEST\": () => (/* binding */ PACKET_ID_SAVE_REQUEST),\n/* harmony export */ \"PACKET_ID_SAVE_RESPONSE\": () => (/* binding */ PACKET_ID_SAVE_RESPONSE),\n/* harmony export */ \"PACKET_ID_SEND_FRAME\": () => (/* binding */ PACKET_ID_SEND_FRAME)\n/* harmony export */ });\nconst MIN_WIDTH = 240;\nconst MIN_HEIGHT = 160;\nconst PACKET_ID_HELLO = 0n;\nconst PACKET_ID_SEND_FRAME = 1n;\nconst PACKET_ID_KEY_DOWN = 2n;\nconst PACKET_ID_SAVE_REQUEST = 3n;\nconst PACKET_ID_SAVE_RESPONSE = 4n;\nconst CLOSE_BUTTON_IMAGE = '/img/close.png';\nconst HOME_BUTTON_IMAGE = '/img/home.png';\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/constants.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"CLOSE_BUTTON_IMAGE\": () => (/* binding */ CLOSE_BUTTON_IMAGE),\n/* harmony export */ \"HOME_BUTTON_IMAGE\": () => (/* binding */ HOME_BUTTON_IMAGE),\n/* harmony export */ \"MIN_HEIGHT_GBA\": () => (/* binding */ MIN_HEIGHT_GBA),\n/* harmony export */ \"MIN_HEIGHT_GBC\": () => (/* binding */ MIN_HEIGHT_GBC),\n/* harmony export */ \"MIN_WIDTH_GBA\": () => (/* binding */ MIN_WIDTH_GBA),\n/* harmony export */ \"MIN_WIDTH_GBC\": () => (/* binding */ MIN_WIDTH_GBC),\n/* harmony export */ \"PACKET_ID_HELLO\": () => (/* binding */ PACKET_ID_HELLO),\n/* harmony export */ \"PACKET_ID_KEY_DOWN\": () => (/* binding */ PACKET_ID_KEY_DOWN),\n/* harmony export */ \"PACKET_ID_SAVE_REQUEST\": () => (/* binding */ PACKET_ID_SAVE_REQUEST),\n/* harmony export */ \"PACKET_ID_SAVE_RESPONSE\": () => (/* binding */ PACKET_ID_SAVE_RESPONSE),\n/* harmony export */ \"PACKET_ID_SEND_FRAME\": () => (/* binding */ PACKET_ID_SEND_FRAME)\n/* harmony export */ });\nconst MIN_WIDTH_GBA = 240;\nconst MIN_HEIGHT_GBA = 160;\nconst MIN_WIDTH_GBC = 160;\nconst MIN_HEIGHT_GBC = 144;\nconst PACKET_ID_HELLO = 0n;\nconst PACKET_ID_SEND_FRAME = 1n;\nconst PACKET_ID_KEY_DOWN = 2n;\nconst PACKET_ID_SAVE_REQUEST = 3n;\nconst PACKET_ID_SAVE_RESPONSE = 4n;\nconst CLOSE_BUTTON_IMAGE = '/img/close.png';\nconst HOME_BUTTON_IMAGE = '/img/home.png';\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/constants.ts?"); /***/ }), @@ -196,7 +196,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var reac \**************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"handleSendFrame\": () => (/* binding */ handleSendFrame),\n/* harmony export */ \"sendHello\": () => (/* binding */ sendHello),\n/* harmony export */ \"sendKeyDown\": () => (/* binding */ sendKeyDown),\n/* harmony export */ \"sendPacket\": () => (/* binding */ sendPacket),\n/* harmony export */ \"sendSaveRequest\": () => (/* binding */ sendSaveRequest)\n/* harmony export */ });\n/* harmony import */ var _msgba_endian__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @msgba/endian */ \"./js-src/endian.ts\");\n/* harmony import */ var _msgba_constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @msgba/constants */ \"./js-src/constants.ts\");\n\n\nfunction concatU8Array(array1, array2) {\n const finalArray = new Uint8Array(array1.length + array2.length);\n finalArray.set(array1);\n finalArray.set(array2, array1.length);\n return finalArray;\n}\nfunction sendHello(websocket, romArray, savestateArray) {\n console.log('Sending hello.');\n const lengthRom = BigInt(romArray.length);\n const lengthSavestate = BigInt(savestateArray.length);\n const rawData = concatU8Array(concatU8Array(concatU8Array(_msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(lengthRom), romArray), _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(lengthSavestate)), savestateArray);\n sendPacket(websocket, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.PACKET_ID_HELLO, rawData);\n}\nfunction sendKeyDown(websocket, isDown, key) {\n console.log('Sending keyDown.', isDown);\n const isDownArray = new Uint8Array(1);\n isDownArray[0] = isDown ? 1 : 0;\n const rawData = concatU8Array(isDownArray, _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u32ToByteArrayBigEndian(key));\n sendPacket(websocket, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.PACKET_ID_KEY_DOWN, rawData);\n}\nfunction sendSaveRequest(websocket, identifier) {\n console.log('Sendidng save request', identifier);\n const rawData = _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(identifier);\n sendPacket(websocket, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.PACKET_ID_SAVE_REQUEST, rawData);\n}\nfunction sendPacket(websocket, id, rawData) {\n const packetU8 = concatU8Array(concatU8Array(_msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(id), _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(BigInt(rawData.length))), rawData);\n const packetBuffer = packetU8.buffer;\n console.log('Sending packet');\n websocket.send(packetBuffer);\n}\nfunction handleSendFrame(rawData, canvas, ctx) {\n console.log('Reachs here');\n if (canvas == null) {\n console.log('No canvas');\n return;\n }\n let data = rawData;\n data = data.slice(4, data.length);\n const outputBufferSize = _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].byteArrayToU64BigEndian(data.slice(0, 8));\n // TODO: This number conversion is not great. Is there other option?\n data = data.slice(8, Number(outputBufferSize));\n const imgData = ctx.createImageData(_msgba_constants__WEBPACK_IMPORTED_MODULE_1__.MIN_WIDTH, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.MIN_HEIGHT);\n const imgDataU8 = new Uint8Array(imgData.data.buffer);\n for (let i = 0; i < data.length; i++) {\n if (i % 4 === 3) {\n imgDataU8[i] = 255;\n continue;\n }\n imgDataU8[i] = data[i];\n }\n data = null;\n createImageBitmap(imgData).then((bitmap) => { drawBitmap(bitmap, canvas, ctx); }).catch((c) => {\n console.log(`Unable to print to the canvas the frame because: ${c}`);\n });\n}\nfunction drawBitmap(bitmap, canvas, ctx) {\n ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/packet.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"handleSendFrame\": () => (/* binding */ handleSendFrame),\n/* harmony export */ \"sendHello\": () => (/* binding */ sendHello),\n/* harmony export */ \"sendKeyDown\": () => (/* binding */ sendKeyDown),\n/* harmony export */ \"sendPacket\": () => (/* binding */ sendPacket),\n/* harmony export */ \"sendSaveRequest\": () => (/* binding */ sendSaveRequest)\n/* harmony export */ });\n/* harmony import */ var _msgba_endian__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @msgba/endian */ \"./js-src/endian.ts\");\n/* harmony import */ var _msgba_constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @msgba/constants */ \"./js-src/constants.ts\");\n\n\nfunction concatU8Array(array1, array2) {\n const finalArray = new Uint8Array(array1.length + array2.length);\n finalArray.set(array1);\n finalArray.set(array2, array1.length);\n return finalArray;\n}\nfunction sendHello(websocket, romArray, savestateArray) {\n console.log('Sending hello.');\n const lengthRom = BigInt(romArray.length);\n const lengthSavestate = BigInt(savestateArray.length);\n const rawData = concatU8Array(concatU8Array(concatU8Array(_msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(lengthRom), romArray), _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(lengthSavestate)), savestateArray);\n sendPacket(websocket, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.PACKET_ID_HELLO, rawData);\n}\nfunction sendKeyDown(websocket, isDown, key) {\n console.log('Sending keyDown.', isDown);\n const isDownArray = new Uint8Array(1);\n isDownArray[0] = isDown ? 1 : 0;\n const rawData = concatU8Array(isDownArray, _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u32ToByteArrayBigEndian(key));\n sendPacket(websocket, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.PACKET_ID_KEY_DOWN, rawData);\n}\nfunction sendSaveRequest(websocket, identifier) {\n console.log('Sendidng save request', identifier);\n const rawData = _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(identifier);\n sendPacket(websocket, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.PACKET_ID_SAVE_REQUEST, rawData);\n}\nfunction sendPacket(websocket, id, rawData) {\n const packetU8 = concatU8Array(concatU8Array(_msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(id), _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(BigInt(rawData.length))), rawData);\n const packetBuffer = packetU8.buffer;\n console.log('Sending packet');\n websocket.send(packetBuffer);\n}\nfunction handleSendFrame(rawData, canvas, setIsGBC) {\n if (canvas == null) {\n console.log('No canvas');\n return;\n }\n const ctx = canvas.getContext('2d');\n if (ctx == null) {\n console.log('No context');\n return;\n }\n let data = rawData;\n const stride = _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].byteArrayToU32BigEndian(data.slice(0, 4));\n let isGBC = false;\n if (stride === 160) {\n isGBC = true;\n setIsGBC(true);\n }\n else {\n setIsGBC(false);\n }\n data = data.slice(4, data.length);\n const outputBufferSize = _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].byteArrayToU64BigEndian(data.slice(0, 8));\n // TODO: This number conversion is not great. Is there other option?\n data = data.slice(8, Number(outputBufferSize));\n let imgData = ctx.createImageData(_msgba_constants__WEBPACK_IMPORTED_MODULE_1__.MIN_WIDTH_GBA, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.MIN_HEIGHT_GBA);\n if (isGBC) {\n imgData = ctx.createImageData(_msgba_constants__WEBPACK_IMPORTED_MODULE_1__.MIN_WIDTH_GBC, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.MIN_HEIGHT_GBC);\n }\n const imgDataU8 = new Uint8Array(imgData.data.buffer);\n for (let i = 0; i < data.length; i++) {\n if (i % 4 === 3) {\n imgDataU8[i] = 255;\n continue;\n }\n imgDataU8[i] = data[i];\n }\n data = null;\n createImageBitmap(imgData).then((bitmap) => { drawBitmap(bitmap, canvas, ctx); }).catch((c) => {\n console.log(`Unable to print to the canvas the frame because: ${c}`);\n });\n}\nfunction drawBitmap(bitmap, canvas, ctx) {\n ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/packet.ts?"); /***/ })