diff --git a/js-src/components/overlay-controls.tsx b/js-src/components/overlay-controls.tsx index a32890e..a5e2f6c 100644 --- a/js-src/components/overlay-controls.tsx +++ b/js-src/components/overlay-controls.tsx @@ -1,12 +1,14 @@ import * as React from 'react'; import {HOME_BUTTON_IMAGE} from '/constants'; +import {sendKeyDown} from '/packet'; export interface OverlayControlsProps { firstMenuElement: React.RefObject, setHiddenMenu: (c: boolean) => void, + webSocket: WebSocket | null; }; -export default function OverlayControls({firstMenuElement, setHiddenMenu}: OverlayControlsProps) { +export default function OverlayControls({firstMenuElement, setHiddenMenu, webSocket}: OverlayControlsProps) { function showOverlayMenu() { setHiddenMenu(false); setTimeout(() => { @@ -16,20 +18,103 @@ export default function OverlayControls({firstMenuElement, setHiddenMenu}: Overl firstMenuElement.current.focus(); }, 100); } + const [onGoingTouches, setOnGoingTouches] = React.useState<{[id: string]: number}>({}); + function mouseDown(e: React.MouseEvent | React.TouchEvent, key: number) { + e.preventDefault(); + if (webSocket == null) { + console.log('There is not websocket'); + return; + } + sendKeyDown(webSocket, true, key); + } + function touchStart(e: React.TouchEvent, key: number) { + e.preventDefault(); + if (webSocket == null) { + console.log('There is not websocket'); + return; + } + for (let i = 0; i < e.changedTouches.length; i++) { + const touch = e.changedTouches[i]; + const idx = touch.identifier; + onGoingTouches[idx] = key; + sendKeyDown(webSocket, true, key); + } + } + function touchEnd(e: React.TouchEvent) { + e.preventDefault(); + if (webSocket == null) { + console.log('There is not websocket'); + return; + } + for (let i = 0; i < e.changedTouches.length; i++) { + const touch = e.changedTouches[i]; + const idx = touch.identifier; + const key = onGoingTouches[idx]; + delete onGoingTouches[idx]; + sendKeyDown(webSocket, false, key); + } + } + + function mouseUp(e: React.MouseEvent | React.TouchEvent, key: number) { + e.preventDefault(); + if (webSocket == null) { + console.log('There is not websocket'); + return; + } + sendKeyDown(webSocket, false, key); + } + + document.onselectstart = () => false; return ( -
+
- + Go to menu. (House icon) - A - B - ^ - v - < - > + ) => mouseDown(e, 0)} + onMouseUp={(e: React.MouseEvent) => mouseUp(e, 0)} + onTouchStart={(e: React.TouchEvent) => touchStart(e, 0)} + onTouchEnd={(e: React.TouchEvent) => touchEnd(e)} + onClick={(e: React.MouseEvent) => e.preventDefault()} + className="control-a control control-button">A + ) => mouseDown(e, 1)} + onMouseUp={(e: React.MouseEvent) => mouseUp(e, 1)} + onTouchStart={(e: React.TouchEvent) => touchStart(e, 1)} + onTouchEnd={(e: React.TouchEvent) => touchEnd(e)} + onClick={(e: React.MouseEvent) => e.preventDefault()} + className="control-b control control-button">B + ) => mouseDown(e, 6)} + onMouseUp={(e: React.MouseEvent) => mouseUp(e, 6)} + onTouchStart={(e: React.TouchEvent) => touchStart(e, 6)} + onTouchEnd={(e: React.TouchEvent) => touchEnd(e)} + onClick={(e: React.MouseEvent) => e.preventDefault()} + className="control-up control control-pad-button">^ + ) => mouseDown(e, 7)} + onMouseUp={(e: React.MouseEvent) => mouseUp(e, 7)} + onTouchStart={(e: React.TouchEvent) => touchStart(e, 7)} + onTouchEnd={(e: React.TouchEvent) => touchEnd(e)} + onClick={(e: React.MouseEvent) => e.preventDefault()} + className="control-down control control-pad-button">v + ) => mouseDown(e, 8)} + onMouseUp={(e: React.MouseEvent) => mouseUp(e, 8)} + onTouchStart={(e: React.TouchEvent) => touchStart(e, 8)} + onTouchEnd={(e: React.TouchEvent) => touchEnd(e)} + onClick={(e: React.MouseEvent) => e.preventDefault()} + className="control-left control control-pad-button">< + ) => mouseDown(e, 9)} + onMouseUp={(e: React.MouseEvent) => mouseUp(e, 9)} + onTouchStart={(e: React.TouchEvent) => touchStart(e, 9)} + onTouchEnd={(e: React.TouchEvent) => touchEnd(e)} + onClick={(e: React.MouseEvent) => e.preventDefault()} + className="control-right control control-pad-button">>
); diff --git a/js-src/components/page.tsx b/js-src/components/page.tsx index f1b59f8..250e9a7 100644 --- a/js-src/components/page.tsx +++ b/js-src/components/page.tsx @@ -7,28 +7,28 @@ 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'; -type setBooleanCallback = (c: boolean) => boolean; - export interface handleClickStartEmulationButtonObjectArgs { e: React.MouseEvent; inputRom: HTMLInputElement | null; inputSaveState: HTMLInputElement | null; canvas: HTMLCanvasElement | null; printingFrame: boolean; - setPrintingFrame: (c: setBooleanCallback) => void; + 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}: handleClickStartEmulationButtonObjectArgs) { + setHiddenFormSelectFiles, setWebSocket}: handleClickStartEmulationButtonObjectArgs) { e.preventDefault(); if (canvas == null) { alert('Canvas does not exists?'); @@ -57,60 +57,29 @@ function handleClickStartEmulationButton({e, inputRom, inputSaveState, canvas, p 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`); - websocket.binaryType = 'arraybuffer'; - websocket.onclose = (message) => { + 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 = () => { + webSocket.onopen = () => { console.log('Opened websocket.'); setEmulationStarted(true); - sendHello(websocket, rom_array, savestate_array); + sendHello(webSocket, rom_array, savestate_array); setHiddenMenu(true); setHiddenFormSelectFiles(true); }; - setPrintingFrame(c => false); - websocket.addEventListener('message', (event) => { + setPrintingFrame(false); + webSocket.addEventListener('message', (event) => { onWebSocketPacket(event, canvas, ctx, printingFrame, setPrintingFrame) }); }); }); } -function concatU8Array(array1: Uint8Array, array2: Uint8Array) { - const final_array = new Uint8Array(array1.length + array2.length); - final_array.set(array1); - final_array.set(array2, array1.length); - return final_array; -} - -function sendPacket(websocket: WebSocket, id: bigint, raw_data: Uint8Array) { - const packet_u8 = concatU8Array( - concatU8Array(Endian.u64ToByteArrayBigEndian(id), Endian.u64ToByteArrayBigEndian(BigInt(raw_data.length))), - raw_data - ); - const packet_buffer = packet_u8.buffer; - console.log('Sending packet'); - websocket.send(packet_buffer); -} - - function sendHello(websocket: WebSocket, rom_array: Uint8Array, savestate_array: Uint8Array) { - console.log('Sending hello.'); - const length_rom = BigInt(rom_array.length); - const length_savestate = BigInt(savestate_array.length); - const raw_data = - concatU8Array( - concatU8Array( - concatU8Array(Endian.u64ToByteArrayBigEndian(length_rom), rom_array), - Endian.u64ToByteArrayBigEndian(length_savestate) - ), - savestate_array - ); - sendPacket(websocket, PACKET_ID_HELLO, raw_data); -} - -function onWebSocketPacket(event: MessageEvent, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean, setPrintingFrame: (c: setBooleanCallback) => void) { +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)); @@ -127,34 +96,6 @@ function onWebSocketPacket(event: MessageEvent, canvas: HTMLCanvasElement, ctx: } } -function handleSendFrame(raw_data: Uint8Array, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean, setPrintingFrame: (c: setBooleanCallback) => void) { - if (printingFrame) { - return; - } - setPrintingFrame(c => true); - let data: Uint8Array | null = raw_data; - const stride = Endian.byteArrayToU32BigEndian(data.slice(0, 4)); - data = data.slice(4, data.length); - const output_buffer_size = Endian.byteArrayToU64BigEndian(data.slice(0, 8)); - data = data.slice(8, data.length); - 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, printingFrame)); -} - -function drawBitmap(bitmap: ImageBitmap, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean) { - ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height); - printingFrame = false; -} - export default function Page() { const screenDimensions = useScreenDimensions(); const emulatorDimensions = calculateSizeEmulator(screenDimensions); @@ -182,6 +123,7 @@ export default function Page() { 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, @@ -193,6 +135,8 @@ export default function Page() { printingFrame: printingFrame, setHiddenMenu: setHiddenMenu, setHiddenFormSelectFiles: setHiddenFormSelectFiles, + setWebSocket: setWebSocket, + }); }; const firstMenuElement = React.useRef(null); @@ -201,7 +145,7 @@ export default function Page() { return (
+ setHiddenMenu={setHiddenMenu} webSocket={webSocket}/> void) { + if (printingFrame) { + return; + } + setPrintingFrame(true); + let data: Uint8Array | null = raw_data; + const stride = Endian.byteArrayToU32BigEndian(data.slice(0, 4)); + data = data.slice(4, data.length); + const output_buffer_size = Endian.byteArrayToU64BigEndian(data.slice(0, 8)); + data = data.slice(8, data.length); + 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, printingFrame)); +} + +function drawBitmap(bitmap: ImageBitmap, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean) { + ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height); + printingFrame = false; +} diff --git a/public/css/styles.css b/public/css/styles.css index efece3c..8fa806f 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -1,3 +1,7 @@ +*:focus { + outline: none; +} + body { background: #0E0E10; color: #F5F5F5; diff --git a/public/js/bundle.js b/public/js/bundle.js index dba3faa..be1e7ca 100644 --- a/public/js/bundle.js +++ b/public/js/bundle.js @@ -126,7 +126,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 _constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../constants */ \"./js-src/constants.ts\");\n\n\n;\nfunction OverlayControls({ firstMenuElement, setHiddenMenu }) {\n function showOverlayMenu() {\n setHiddenMenu(false);\n setTimeout(() => {\n if (firstMenuElement.current == null) {\n return;\n }\n firstMenuElement.current.focus();\n }, 100);\n }\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"overlay\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"vertical-padding\" }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"controls\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { href: \"#\", className: \"gear control\", onClick: showOverlayMenu },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"img\", { src: _constants__WEBPACK_IMPORTED_MODULE_1__.HOME_BUTTON_IMAGE, alt: \"Go to menu. (House icon)\" })),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { href: \"#\", className: \"control-a control control-button\" }, \"A\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { href: \"#\", className: \"control-b control control-button\" }, \"B\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { href: \"#\", className: \"control-up control control-pad-button\" }, \"^\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { href: \"#\", className: \"control-down control control-pad-button\" }, \"v\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { href: \"#\", className: \"control-left control control-pad-button\" }, \"<\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { href: \"#\", className: \"control-right control control-pad-button\" }, \">\"))));\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 _constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../constants */ \"./js-src/constants.ts\");\n/* harmony import */ var _packet__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../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, setOnGoingTouches] = react__WEBPACK_IMPORTED_MODULE_0__.useState({});\n function mouseDown(e, key) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n (0,_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n function touchStart(e, key) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const idx = touch.identifier;\n onGoingTouches[idx] = key;\n (0,_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n }\n function touchEnd(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const idx = touch.identifier;\n const key = onGoingTouches[idx];\n delete onGoingTouches[idx];\n (0,_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, key);\n }\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,_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, key);\n }\n document.onselectstart = () => false;\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"overlay\", onTouchEnd: touchEnd },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"vertical-padding\" }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"controls\" },\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: _constants__WEBPACK_IMPORTED_MODULE_1__.HOME_BUTTON_IMAGE, alt: \"Go to menu. (House icon)\" })),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, onMouseDown: (e) => mouseDown(e, 0), onMouseUp: (e) => mouseUp(e, 0), onTouchStart: (e) => touchStart(e, 0), onTouchEnd: (e) => touchEnd(e), onClick: (e) => e.preventDefault(), className: \"control-a control control-button\" }, \"A\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, onMouseDown: (e) => mouseDown(e, 1), onMouseUp: (e) => mouseUp(e, 1), onTouchStart: (e) => touchStart(e, 1), onTouchEnd: (e) => touchEnd(e), onClick: (e) => e.preventDefault(), className: \"control-b control control-button\" }, \"B\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, onMouseDown: (e) => mouseDown(e, 6), onMouseUp: (e) => mouseUp(e, 6), onTouchStart: (e) => touchStart(e, 6), onTouchEnd: (e) => touchEnd(e), onClick: (e) => e.preventDefault(), className: \"control-up control control-pad-button\" }, \"^\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, onMouseDown: (e) => mouseDown(e, 7), onMouseUp: (e) => mouseUp(e, 7), onTouchStart: (e) => touchStart(e, 7), onTouchEnd: (e) => touchEnd(e), onClick: (e) => e.preventDefault(), className: \"control-down control control-pad-button\" }, \"v\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, onMouseDown: (e) => mouseDown(e, 8), onMouseUp: (e) => mouseUp(e, 8), onTouchStart: (e) => touchStart(e, 8), onTouchEnd: (e) => touchEnd(e), onClick: (e) => e.preventDefault(), className: \"control-left control control-pad-button\" }, \"<\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, onMouseDown: (e) => mouseDown(e, 9), onMouseUp: (e) => mouseUp(e, 9), onTouchStart: (e) => touchStart(e, 9), onTouchEnd: (e) => touchEnd(e), onClick: (e) => e.preventDefault(), className: \"control-right control control-pad-button\" }, \">\"))));\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 _components_center_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../components/center-element */ \"./js-src/components/center-element.tsx\");\n/* harmony import */ var _components_canvas_gba_emulator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../components/canvas-gba-emulator */ \"./js-src/components/canvas-gba-emulator.tsx\");\n/* harmony import */ var _components_overlay_controls__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../components/overlay-controls */ \"./js-src/components/overlay-controls.tsx\");\n/* harmony import */ var _components_overlay_menu__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../components/overlay-menu */ \"./js-src/components/overlay-menu.tsx\");\n/* harmony import */ var _components_overlay_select_files__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../components/overlay-select-files */ \"./js-src/components/overlay-select-files.tsx\");\n/* harmony import */ var _endian__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../../endian */ \"./js-src/endian.ts\");\n/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../../constants */ \"./js-src/constants.ts\");\n\n\n\n\n\n\n\n\n;\nfunction handleClickStartEmulationButton({ e, inputRom, inputSaveState, canvas, printingFrame, setPrintingFrame, setEmulationStarted, setHiddenMenu, setHiddenFormSelectFiles }) {\n e.preventDefault();\n if (canvas == null) {\n alert('Canvas does not exists?');\n return;\n }\n const ctx = canvas.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 rom_file = inputRom.files[0];\n const savestate_file = inputSaveState.files[0];\n rom_file.arrayBuffer().then((rom_buffer) => {\n savestate_file.arrayBuffer().then((savestate_buffer) => {\n const rom_array = new Uint8Array(rom_buffer);\n const savestate_array = new Uint8Array(savestate_buffer);\n const websocket = new WebSocket(`ws://${window.location.host}/ws`);\n websocket.binaryType = 'arraybuffer';\n websocket.onclose = (message) => {\n setEmulationStarted(false);\n console.log('Closing websocket.');\n };\n websocket.onopen = () => {\n console.log('Opened websocket.');\n setEmulationStarted(true);\n sendHello(websocket, rom_array, savestate_array);\n setHiddenMenu(true);\n setHiddenFormSelectFiles(true);\n };\n setPrintingFrame(c => false);\n websocket.addEventListener('message', (event) => {\n onWebSocketPacket(event, canvas, ctx, printingFrame, setPrintingFrame);\n });\n });\n });\n}\nfunction concatU8Array(array1, array2) {\n const final_array = new Uint8Array(array1.length + array2.length);\n final_array.set(array1);\n final_array.set(array2, array1.length);\n return final_array;\n}\nfunction sendPacket(websocket, id, raw_data) {\n const packet_u8 = concatU8Array(concatU8Array(_endian__WEBPACK_IMPORTED_MODULE_6__[\"default\"].u64ToByteArrayBigEndian(id), _endian__WEBPACK_IMPORTED_MODULE_6__[\"default\"].u64ToByteArrayBigEndian(BigInt(raw_data.length))), raw_data);\n const packet_buffer = packet_u8.buffer;\n console.log('Sending packet');\n websocket.send(packet_buffer);\n}\nfunction sendHello(websocket, rom_array, savestate_array) {\n console.log('Sending hello.');\n const length_rom = BigInt(rom_array.length);\n const length_savestate = BigInt(savestate_array.length);\n const raw_data = concatU8Array(concatU8Array(concatU8Array(_endian__WEBPACK_IMPORTED_MODULE_6__[\"default\"].u64ToByteArrayBigEndian(length_rom), rom_array), _endian__WEBPACK_IMPORTED_MODULE_6__[\"default\"].u64ToByteArrayBigEndian(length_savestate)), savestate_array);\n sendPacket(websocket, _constants__WEBPACK_IMPORTED_MODULE_7__.PACKET_ID_HELLO, raw_data);\n}\nfunction onWebSocketPacket(event, canvas, ctx, printingFrame, setPrintingFrame) {\n const buffer = event.data;\n let packet_u8 = new Uint8Array(buffer);\n const id = _endian__WEBPACK_IMPORTED_MODULE_6__[\"default\"].byteArrayToU64BigEndian(packet_u8.slice(0, 8));\n packet_u8 = packet_u8.slice(8, packet_u8.length);\n const size = _endian__WEBPACK_IMPORTED_MODULE_6__[\"default\"].byteArrayToU64BigEndian(packet_u8.slice(0, 8));\n const raw_data = packet_u8.slice(8, packet_u8.length);\n packet_u8 = null;\n switch (id) {\n case _constants__WEBPACK_IMPORTED_MODULE_7__.PACKET_ID_SEND_FRAME:\n handleSendFrame(raw_data, canvas, ctx, printingFrame, setPrintingFrame);\n break;\n default:\n console.log(`Received unknown packet ${id}`);\n }\n}\nfunction handleSendFrame(raw_data, canvas, ctx, printingFrame, setPrintingFrame) {\n if (printingFrame) {\n return;\n }\n setPrintingFrame(c => true);\n let data = raw_data;\n const stride = _endian__WEBPACK_IMPORTED_MODULE_6__[\"default\"].byteArrayToU32BigEndian(data.slice(0, 4));\n data = data.slice(4, data.length);\n const output_buffer_size = _endian__WEBPACK_IMPORTED_MODULE_6__[\"default\"].byteArrayToU64BigEndian(data.slice(0, 8));\n data = data.slice(8, data.length);\n const img_data = ctx.createImageData(_constants__WEBPACK_IMPORTED_MODULE_7__.MIN_WIDTH, _constants__WEBPACK_IMPORTED_MODULE_7__.MIN_HEIGHT);\n const img_data_u8 = new Uint8Array(img_data.data.buffer);\n for (let i = 0; i < data.length; i++) {\n if (i % 4 == 3) {\n img_data_u8[i] = 255;\n continue;\n }\n img_data_u8[i] = data[i];\n }\n data = null;\n createImageBitmap(img_data).then((bitmap) => drawBitmap(bitmap, canvas, ctx, printingFrame));\n}\nfunction drawBitmap(bitmap, canvas, ctx, printingFrame) {\n ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);\n printingFrame = false;\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 ;\n const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = react__WEBPACK_IMPORTED_MODULE_0__.useState(true);\n const [printingFrame, setPrintingFrame] = react__WEBPACK_IMPORTED_MODULE_0__.useState(false);\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 onStartEmulation = (e) => {\n handleClickStartEmulationButton({\n e: e,\n setEmulationStarted: setEmulationStarted,\n inputRom: refInputRom.current,\n inputSaveState: refInputSaveState.current,\n canvas: canvasRef.current,\n setPrintingFrame: setPrintingFrame,\n printingFrame: printingFrame,\n setHiddenMenu: setHiddenMenu,\n setHiddenFormSelectFiles: setHiddenFormSelectFiles,\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(_components_overlay_controls__WEBPACK_IMPORTED_MODULE_3__[\"default\"], { firstMenuElement: firstMenuElement, setHiddenMenu: setHiddenMenu }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_components_overlay_menu__WEBPACK_IMPORTED_MODULE_4__[\"default\"], { hiddenMenu: hiddenMenu, setHiddenMenu: setHiddenMenu, emulationStarted: emulationStarted, setHiddenFormSelectFiles: setHiddenFormSelectFiles, screenRef: screenRef, isFullscreen: isFullscreen, setIsFullscreen: setIsFullscreen, firstMenuElement: firstMenuElement }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_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(_components_center_element__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { full: true },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_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 < _constants__WEBPACK_IMPORTED_MODULE_7__.MIN_WIDTH || height < _constants__WEBPACK_IMPORTED_MODULE_7__.MIN_HEIGHT) {\n return {\n width: _constants__WEBPACK_IMPORTED_MODULE_7__.MIN_WIDTH,\n height: _constants__WEBPACK_IMPORTED_MODULE_7__.MIN_HEIGHT,\n };\n }\n const ratioWidth = width / _constants__WEBPACK_IMPORTED_MODULE_7__.MIN_WIDTH;\n const ratioHeight = height / _constants__WEBPACK_IMPORTED_MODULE_7__.MIN_HEIGHT;\n if (ratioWidth < ratioHeight) {\n emulatorDimensions.width = _constants__WEBPACK_IMPORTED_MODULE_7__.MIN_WIDTH * ratioWidth;\n emulatorDimensions.height = _constants__WEBPACK_IMPORTED_MODULE_7__.MIN_HEIGHT * ratioWidth;\n }\n else {\n emulatorDimensions.height = _constants__WEBPACK_IMPORTED_MODULE_7__.MIN_HEIGHT * ratioHeight;\n emulatorDimensions.width = _constants__WEBPACK_IMPORTED_MODULE_7__.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 _components_center_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../components/center-element */ \"./js-src/components/center-element.tsx\");\n/* harmony import */ var _components_canvas_gba_emulator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../components/canvas-gba-emulator */ \"./js-src/components/canvas-gba-emulator.tsx\");\n/* harmony import */ var _components_overlay_controls__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../components/overlay-controls */ \"./js-src/components/overlay-controls.tsx\");\n/* harmony import */ var _components_overlay_menu__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../components/overlay-menu */ \"./js-src/components/overlay-menu.tsx\");\n/* harmony import */ var _components_overlay_select_files__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../components/overlay-select-files */ \"./js-src/components/overlay-select-files.tsx\");\n/* harmony import */ var _packet__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../../packet */ \"./js-src/packet.ts\");\n/* harmony import */ var _endian__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../../endian */ \"./js-src/endian.ts\");\n/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../../constants */ \"./js-src/constants.ts\");\n\n\n\n\n\n\n\n\n\n;\nfunction handleClickStartEmulationButton({ e, inputRom, inputSaveState, canvas, printingFrame, setPrintingFrame, setEmulationStarted, setHiddenMenu, setHiddenFormSelectFiles, setWebSocket }) {\n e.preventDefault();\n if (canvas == null) {\n alert('Canvas does not exists?');\n return;\n }\n const ctx = canvas.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 rom_file = inputRom.files[0];\n const savestate_file = inputSaveState.files[0];\n rom_file.arrayBuffer().then((rom_buffer) => {\n savestate_file.arrayBuffer().then((savestate_buffer) => {\n const rom_array = new Uint8Array(rom_buffer);\n const savestate_array = new Uint8Array(savestate_buffer);\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 };\n webSocket.onopen = () => {\n console.log('Opened websocket.');\n setEmulationStarted(true);\n (0,_packet__WEBPACK_IMPORTED_MODULE_6__.sendHello)(webSocket, rom_array, savestate_array);\n setHiddenMenu(true);\n setHiddenFormSelectFiles(true);\n };\n setPrintingFrame(false);\n webSocket.addEventListener('message', (event) => {\n onWebSocketPacket(event, canvas, ctx, printingFrame, setPrintingFrame);\n });\n });\n });\n}\nfunction onWebSocketPacket(event, canvas, ctx, printingFrame, setPrintingFrame) {\n const buffer = event.data;\n let packet_u8 = new Uint8Array(buffer);\n const id = _endian__WEBPACK_IMPORTED_MODULE_7__[\"default\"].byteArrayToU64BigEndian(packet_u8.slice(0, 8));\n packet_u8 = packet_u8.slice(8, packet_u8.length);\n const size = _endian__WEBPACK_IMPORTED_MODULE_7__[\"default\"].byteArrayToU64BigEndian(packet_u8.slice(0, 8));\n const raw_data = packet_u8.slice(8, packet_u8.length);\n packet_u8 = null;\n switch (id) {\n case _constants__WEBPACK_IMPORTED_MODULE_8__.PACKET_ID_SEND_FRAME:\n (0,_packet__WEBPACK_IMPORTED_MODULE_6__.handleSendFrame)(raw_data, canvas, ctx, printingFrame, setPrintingFrame);\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 ;\n const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = react__WEBPACK_IMPORTED_MODULE_0__.useState(true);\n const [printingFrame, setPrintingFrame] = react__WEBPACK_IMPORTED_MODULE_0__.useState(false);\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: e,\n setEmulationStarted: setEmulationStarted,\n inputRom: refInputRom.current,\n inputSaveState: refInputSaveState.current,\n canvas: canvasRef.current,\n setPrintingFrame: setPrintingFrame,\n printingFrame: printingFrame,\n setHiddenMenu: setHiddenMenu,\n setHiddenFormSelectFiles: setHiddenFormSelectFiles,\n setWebSocket: 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(_components_overlay_controls__WEBPACK_IMPORTED_MODULE_3__[\"default\"], { firstMenuElement: firstMenuElement, setHiddenMenu: setHiddenMenu, webSocket: webSocket }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_components_overlay_menu__WEBPACK_IMPORTED_MODULE_4__[\"default\"], { hiddenMenu: hiddenMenu, setHiddenMenu: setHiddenMenu, emulationStarted: emulationStarted, setHiddenFormSelectFiles: setHiddenFormSelectFiles, screenRef: screenRef, isFullscreen: isFullscreen, setIsFullscreen: setIsFullscreen, firstMenuElement: firstMenuElement }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_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(_components_center_element__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { full: true },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_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 < _constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH || height < _constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT) {\n return {\n width: _constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH,\n height: _constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT,\n };\n }\n const ratioWidth = width / _constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH;\n const ratioHeight = height / _constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT;\n if (ratioWidth < ratioHeight) {\n emulatorDimensions.width = _constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH * ratioWidth;\n emulatorDimensions.height = _constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT * ratioWidth;\n }\n else {\n emulatorDimensions.height = _constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT * ratioHeight;\n emulatorDimensions.width = _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?"); /***/ }), @@ -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_SEND_FRAME\": () => (/* binding */ PACKET_ID_SEND_FRAME),\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nconst MIN_WIDTH = 240;\nconst MIN_HEIGHT = 160;\nconst PACKET_ID_HELLO = 0n;\nconst PACKET_ID_SEND_FRAME = 1n;\nconst CLOSE_BUTTON_IMAGE = \"/img/close.png\";\nconst HOME_BUTTON_IMAGE = \"/img/home.png\";\nclass Constants {\n}\nConstants.MIN_WIDTH = MIN_WIDTH;\nConstants.MIN_HEIGHT = MIN_HEIGHT;\nConstants.PACKET_ID_HELLO = PACKET_ID_HELLO;\nConstants.PACKET_ID_SEND_FRAME = PACKET_ID_SEND_FRAME;\nConstants.CLOSE_BUTTON_IMAGE = CLOSE_BUTTON_IMAGE;\nConstants.HOME_BUTTON_IMAGE = HOME_BUTTON_IMAGE;\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Constants);\n;\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\": () => (/* 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_SEND_FRAME\": () => (/* binding */ PACKET_ID_SEND_FRAME),\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\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 CLOSE_BUTTON_IMAGE = \"/img/close.png\";\nconst HOME_BUTTON_IMAGE = \"/img/home.png\";\nclass Constants {\n}\nConstants.MIN_WIDTH = MIN_WIDTH;\nConstants.MIN_HEIGHT = MIN_HEIGHT;\nConstants.PACKET_ID_HELLO = PACKET_ID_HELLO;\nConstants.PACKET_ID_SEND_FRAME = PACKET_ID_SEND_FRAME;\nConstants.CLOSE_BUTTON_IMAGE = CLOSE_BUTTON_IMAGE;\nConstants.HOME_BUTTON_IMAGE = HOME_BUTTON_IMAGE;\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Constants);\n;\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/constants.ts?"); /***/ }), @@ -176,7 +176,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 */ Endian)\n/* harmony export */ });\n\nlet littleEndian = true;\n(() => {\n let buf = new ArrayBuffer(4);\n let buf8 = new Uint8ClampedArray(buf);\n let data = new Uint32Array(buf);\n data[0] = 0xdeadbeef;\n if (buf8[0] === 0xde) {\n littleEndian = false;\n }\n})();\nclass Endian {\n static isLittleEndian() {\n return littleEndian;\n }\n static byteArrayToU32BigEndian(inputArray) {\n if (Endian.isLittleEndian()) {\n inputArray = inputArray.reverse();\n }\n const buffer = inputArray.buffer;\n const outputU32Array = new Uint32Array(buffer);\n return outputU32Array[0];\n }\n static byteArrayToU64BigEndian(inputArray) {\n if (Endian.isLittleEndian()) {\n inputArray = inputArray.reverse();\n }\n const buffer = inputArray.buffer;\n const outputU64Array = new BigUint64Array(buffer);\n return outputU64Array[0];\n }\n static u64ToByteArrayBigEndian(inputNumber) {\n const buffer = new ArrayBuffer(8);\n const buffer8 = new Uint8Array(buffer);\n const buffer64 = new BigUint64Array(buffer);\n buffer64[0] = inputNumber;\n if (Endian.isLittleEndian()) {\n buffer8.reverse();\n }\n return buffer8;\n }\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/endian.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Endian)\n/* harmony export */ });\n\nlet littleEndian = true;\n(() => {\n let buf = new ArrayBuffer(4);\n let buf8 = new Uint8ClampedArray(buf);\n let data = new Uint32Array(buf);\n data[0] = 0xdeadbeef;\n if (buf8[0] === 0xde) {\n littleEndian = false;\n }\n})();\nclass Endian {\n static isLittleEndian() {\n return littleEndian;\n }\n static byteArrayToU32BigEndian(inputArray) {\n if (Endian.isLittleEndian()) {\n inputArray = inputArray.reverse();\n }\n const buffer = inputArray.buffer;\n const outputU32Array = new Uint32Array(buffer);\n return outputU32Array[0];\n }\n static byteArrayToU64BigEndian(inputArray) {\n if (Endian.isLittleEndian()) {\n inputArray = inputArray.reverse();\n }\n const buffer = inputArray.buffer;\n const outputU64Array = new BigUint64Array(buffer);\n return outputU64Array[0];\n }\n static u64ToByteArrayBigEndian(inputNumber) {\n const buffer = new ArrayBuffer(8);\n const buffer8 = new Uint8Array(buffer);\n const buffer64 = new BigUint64Array(buffer);\n buffer64[0] = inputNumber;\n if (Endian.isLittleEndian()) {\n buffer8.reverse();\n }\n return buffer8;\n }\n static u32ToByteArrayBigEndian(inputNumber) {\n const buffer = new ArrayBuffer(4);\n const buffer8 = new Uint8Array(buffer);\n const buffer32 = new Uint32Array(buffer);\n buffer32[0] = inputNumber;\n if (Endian.isLittleEndian()) {\n buffer8.reverse();\n }\n return buffer8;\n }\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/endian.ts?"); /***/ }), @@ -188,6 +188,16 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac eval("__webpack_require__.r(__webpack_exports__);\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 react_dom_client__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-dom/client */ \"./node_modules/react-dom/client.js\");\n/* harmony import */ var _components_page__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../components/page */ \"./js-src/components/page.tsx\");\n\n\n\n\nconst body = document.querySelector('body');\nif (body != null) {\n fillBody(body);\n}\nfunction fillBody(body) {\n const app = document.createElement('div');\n body.appendChild(app);\n const root = react_dom_client__WEBPACK_IMPORTED_MODULE_1__.createRoot(app);\n root.render(react__WEBPACK_IMPORTED_MODULE_0__.createElement(_components_page__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null));\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/index.tsx?"); +/***/ }), + +/***/ "./js-src/packet.ts": +/*!**************************!*\ + !*** ./js-src/packet.ts ***! + \**************************/ +/***/ ((__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 */ });\n/* harmony import */ var _endian__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../endian */ \"./js-src/endian.ts\");\n/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../constants */ \"./js-src/constants.ts\");\n\n\nfunction concatU8Array(array1, array2) {\n const final_array = new Uint8Array(array1.length + array2.length);\n final_array.set(array1);\n final_array.set(array2, array1.length);\n return final_array;\n}\nfunction sendHello(websocket, rom_array, savestate_array) {\n console.log('Sending hello.');\n const length_rom = BigInt(rom_array.length);\n const length_savestate = BigInt(savestate_array.length);\n const raw_data = concatU8Array(concatU8Array(concatU8Array(_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(length_rom), rom_array), _endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(length_savestate)), savestate_array);\n sendPacket(websocket, _constants__WEBPACK_IMPORTED_MODULE_1__.PACKET_ID_HELLO, raw_data);\n}\nfunction sendKeyDown(websocket, isDown, key) {\n console.log('Sending keyDown.');\n console.log(_constants__WEBPACK_IMPORTED_MODULE_1__.PACKET_ID_KEY_DOWN);\n const isDownArray = new Uint8Array(1);\n isDownArray[0] = isDown ? 1 : 0;\n const rawData = concatU8Array(isDownArray, _endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u32ToByteArrayBigEndian(key));\n console.log(rawData.length);\n sendPacket(websocket, _constants__WEBPACK_IMPORTED_MODULE_1__.PACKET_ID_KEY_DOWN, rawData);\n}\nfunction sendPacket(websocket, id, raw_data) {\n const packet_u8 = concatU8Array(concatU8Array(_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(id), _endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(BigInt(raw_data.length))), raw_data);\n const packet_buffer = packet_u8.buffer;\n console.log('Sending packet');\n websocket.send(packet_buffer);\n}\nfunction handleSendFrame(raw_data, canvas, ctx, printingFrame, setPrintingFrame) {\n if (printingFrame) {\n return;\n }\n setPrintingFrame(true);\n let data = raw_data;\n const stride = _endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].byteArrayToU32BigEndian(data.slice(0, 4));\n data = data.slice(4, data.length);\n const output_buffer_size = _endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].byteArrayToU64BigEndian(data.slice(0, 8));\n data = data.slice(8, data.length);\n const img_data = ctx.createImageData(_constants__WEBPACK_IMPORTED_MODULE_1__.MIN_WIDTH, _constants__WEBPACK_IMPORTED_MODULE_1__.MIN_HEIGHT);\n const img_data_u8 = new Uint8Array(img_data.data.buffer);\n for (let i = 0; i < data.length; i++) {\n if (i % 4 == 3) {\n img_data_u8[i] = 255;\n continue;\n }\n img_data_u8[i] = data[i];\n }\n data = null;\n createImageBitmap(img_data).then((bitmap) => drawBitmap(bitmap, canvas, ctx, printingFrame));\n}\nfunction drawBitmap(bitmap, canvas, ctx, printingFrame) {\n ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);\n printingFrame = false;\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/packet.ts?"); + /***/ }) /******/ });