diff --git a/js-src/components/overlay-menu.tsx b/js-src/components/overlay-menu.tsx index 0ee73f8..dc07f2c 100644 --- a/js-src/components/overlay-menu.tsx +++ b/js-src/components/overlay-menu.tsx @@ -12,6 +12,7 @@ export interface OverlayMenuProps { setIsFullscreen: (c: boolean) => void firstMenuElement: React.RefObject websocket: WebSocket | null + onSaveResponseLambdas: Map void> }; type Style = Record @@ -19,7 +20,7 @@ type Style = Record export default function OverlayMenu ({ hiddenMenu, setHiddenMenu, emulationStarted, setHiddenFormSelectFiles, screenRef, isFullscreen, setIsFullscreen, - firstMenuElement, websocket + firstMenuElement, websocket, onSaveResponseLambdas }: OverlayMenuProps): JSX.Element { function exitMenu (): void { setHiddenMenu(true) @@ -68,6 +69,25 @@ export default function OverlayMenu ({ } const currentSave = saveIdentifier setSaveidentifier((c: bigint) => c + 1n) + console.log('Reachs here') + onSaveResponseLambdas.set(currentSave, (saveFile: Uint8Array) => { + const anchor = document.createElement('a') + const blobSaveResponseFile = new Blob([saveFile.buffer]) + const fileReader = new FileReader() + fileReader.onload = () => { + if (typeof fileReader.result === 'string') { + anchor.style.display = 'none' + document.body.appendChild(anchor) + anchor.href = fileReader.result + anchor.download = 'ruby.ss1' + anchor.click() + return true + } + return false + } + fileReader.readAsDataURL(blobSaveResponseFile) + }) + console.log(onSaveResponseLambdas) sendSaveRequest(websocket, currentSave) } diff --git a/js-src/components/page.tsx b/js-src/components/page.tsx index 8aab085..9d399c0 100644 --- a/js-src/components/page.tsx +++ b/js-src/components/page.tsx @@ -5,11 +5,11 @@ import CanvasGBAEmulator from '@msgba/components/canvas-gba-emulator' import OverlayControls from '@msgba/components/overlay-controls' import OverlayMenu from '@msgba/components/overlay-menu' import OverlaySelectFiles from '@msgba/components/overlay-select-files' -import { sendHello, handleSendFrame } from '@msgba/packet' +import { sendHello, handleSendFrame, handleSaveResponse } from '@msgba/packet' import Endian from '@msgba/endian' -import { MIN_WIDTH_GBA, MIN_HEIGHT_GBA, MIN_WIDTH_GBC, MIN_HEIGHT_GBC, PACKET_ID_SEND_FRAME } from '@msgba/constants' +import { MIN_WIDTH_GBA, MIN_HEIGHT_GBA, MIN_WIDTH_GBC, MIN_HEIGHT_GBC, PACKET_ID_SEND_FRAME, PACKET_ID_SAVE_RESPONSE } from '@msgba/constants' export interface handleClickStartEmulationButtonObjectArgs { e: React.MouseEvent @@ -23,85 +23,41 @@ export interface handleClickStartEmulationButtonObjectArgs { isGBC: boolean setIsGBC: (c: boolean) => void controlsRef: React.RefObject -} - -function handleClickStartEmulationButton ({ - e, inputRom, inputSaveState, canvas, - setEmulationStarted, setHiddenMenu, - setHiddenFormSelectFiles, setWebSocket, - isGBC, setIsGBC, controlsRef -}: handleClickStartEmulationButtonObjectArgs): void { - e.preventDefault() - if (canvas.current == null) { - alert('Canvas does not exists?') - return - } - const ctx = canvas.current.getContext('2d') - if (ctx == null) { - alert('Unable to create canvas context, doing nothing') - return - } - if (inputRom == null || inputSaveState == null || inputRom.files == null || inputSaveState.files == null) { - alert('Unable to read the files ') - return - } - if (inputRom.files.length === 0) { - alert('There is no rom still') - return - } - if (inputSaveState.files.length === 0) { - alert('There is no savestate still') - return - } - const romFile = inputRom.files[0] - const savestateFile = inputSaveState.files[0] - romFile.arrayBuffer().then((romBuffer) => { - savestateFile.arrayBuffer().then((savestateBuffer) => { - const romArray = new Uint8Array(romBuffer) - const savestateArray = new Uint8Array(savestateBuffer) - const webSocket = new WebSocket(`ws://${window.location.host}/ws`) - setWebSocket(webSocket) - webSocket.binaryType = 'arraybuffer' - webSocket.onclose = (message) => { - setEmulationStarted(false) - console.log('Closing websocket.') - setWebSocket(null) - } - webSocket.onopen = () => { - console.log('Opened websocket.') - setEmulationStarted(true) - sendHello(webSocket, romArray, savestateArray) - setHiddenMenu(true) - setHiddenFormSelectFiles(true) - } - webSocket.addEventListener('message', (event) => { - onWebSocketPacket(event, canvas.current, ctx, isGBC, setIsGBC) - }) - }).catch((c: string) => { - console.log('Unable to convert file to array_buffer') - }) - }).catch((c: string) => { - console.log('Unable to convert file to array_buffer') - }) + onSaveResponseLambdas: Map void> } function onWebSocketPacket (event: MessageEvent, canvas: HTMLCanvasElement | null, ctx: CanvasRenderingContext2D, - isGBC: boolean, setIsGBC: (c: boolean) => void): void { + isGBC: boolean, setIsGBC: (c: boolean) => void, + onSaveResponseLambdas: Map void>): void { const buffer = event.data let packetU8: Uint8Array | null = new Uint8Array(buffer) const id = Endian.byteArrayToU64BigEndian(packetU8.slice(0, 8)) packetU8 = packetU8.slice(8, packetU8.length) const size = Endian.byteArrayToU64BigEndian(packetU8.slice(0, 8)) - const rawData = packetU8.slice(8, Number(size)) + console.log(size) + const rawData = packetU8.slice(8, Number(size) + 8) + console.log(rawData.length) packetU8 = null + handlePacket(id, rawData, canvas, setIsGBC, onSaveResponseLambdas).catch((error: string) => { + console.log('Error handling packet', error) + }) +} + +async function handlePacket (id: bigint, rawData: Uint8Array, + canvas: HTMLCanvasElement | null, + setIsGBC: (c: boolean) => void, + onSaveResponseLambdas: Map void>): Promise { switch (id) { case PACKET_ID_SEND_FRAME: handleSendFrame(rawData, canvas, setIsGBC) break + case PACKET_ID_SAVE_RESPONSE: + handleSaveResponse(rawData, canvas, onSaveResponseLambdas) + break default: - console.log(`Received unknown packet ${id}`) + throw new Error(`Received unknown packet ${id}`) } } @@ -133,6 +89,7 @@ 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 [onSaveResponseLambdas] = React.useState(new Map void>()) const controlsRef = React.useRef(null) React.useEffect(() => { @@ -156,21 +113,65 @@ export default function Page (): JSX.Element { } }, 100) }, [hiddenMenu, hiddenFormSelectFiles]) - const onStartEmulation = (e: React.MouseEvent): void => { - handleClickStartEmulationButton({ - e, - setEmulationStarted, - inputRom: refInputRom.current, - inputSaveState: refInputSaveState.current, - canvas: canvasRef, - setHiddenMenu, - setHiddenFormSelectFiles, - setWebSocket, - isGBC, - setIsGBC, - controlsRef + function handleClickStartEmulationButton (e: React.MouseEvent): void { + e.preventDefault() + if (canvasRef.current == null) { + alert('Canvas does not exists?') + return + } + const ctx = canvasRef.current.getContext('2d') + if (ctx == null) { + alert('Unable to create canvas context, doing nothing') + return + } + const inputRom = refInputRom.current + const inputSaveState = refInputSaveState.current + if (inputRom == null || inputSaveState == null || inputRom.files == null || inputSaveState.files == null) { + alert('Unable to read the files ') + return + } + if (inputRom.files.length === 0) { + alert('There is no rom still') + return + } + if (inputSaveState.files.length === 0) { + alert('There is no savestate still') + return + } + const romFile = inputRom.files[0] + const savestateFile = inputSaveState.files[0] + romFile.arrayBuffer().then((romBuffer) => { + savestateFile.arrayBuffer().then((savestateBuffer) => { + const romArray = new Uint8Array(romBuffer) + const savestateArray = new Uint8Array(savestateBuffer) + const webSocket = new WebSocket(`ws://${window.location.host}/ws`) + setWebSocket(webSocket) + webSocket.binaryType = 'arraybuffer' + webSocket.onclose = (message) => { + setEmulationStarted(false) + console.log('Closing websocket.') + setWebSocket(null) + } + webSocket.onopen = () => { + console.log('Opened websocket.') + setEmulationStarted(true) + sendHello(webSocket, romArray, savestateArray) + setHiddenMenu(true) + setHiddenFormSelectFiles(true) + } + webSocket.addEventListener('message', (event) => { + onWebSocketPacket(event, canvasRef.current, ctx, isGBC, setIsGBC, onSaveResponseLambdas) + }) + }).catch((c: string) => { + console.log('Unable to convert file to array_buffer') + }) + }).catch((c: string) => { + console.log('Unable to convert file to array_buffer') }) } + const onStartEmulation = (e: React.MouseEvent): void => { + handleClickStartEmulationButton(e) + } const firstMenuElement = React.useRef(null) const screenRef = React.useRef(null) const [isFullscreen, setIsFullscreen] = React.useState(false) @@ -182,7 +183,8 @@ export default function Page (): JSX.Element { setHiddenMenu={setHiddenMenu} emulationStarted={emulationStarted} setHiddenFormSelectFiles={setHiddenFormSelectFiles} screenRef={screenRef} isFullscreen={isFullscreen} setIsFullscreen={setIsFullscreen} - firstMenuElement={firstMenuElement} websocket={webSocket}/> + firstMenuElement={firstMenuElement} websocket={webSocket} + onSaveResponseLambdas={onSaveResponseLambdas}/> void>): void { + let data = rawData + console.log(data.length) + const identifier = Endian.byteArrayToU64BigEndian(data.slice(0, 8)) + console.log(data.length) + data = data.slice(8, data.length) + console.log(data.length) + const saveFileSize = Endian.byteArrayToU64BigEndian(data.slice(0, 8)) + console.log(data.length) + const saveFile = data.slice(8, Number(saveFileSize) + 8) + console.log(data.length) + const currentLambda = onSaveResponseLambdas.get(identifier) + console.log(onSaveResponseLambdas) + if (currentLambda == null) { + throw new Error(`We received a save request for an unknown identifier ${identifier}`) + } + currentLambda(saveFile) +} + export function handleSendFrame (rawData: Uint8Array, canvas: HTMLCanvasElement | null, setIsGBC: (c: boolean) => void): void { if (canvas == null) { console.log('No canvas') diff --git a/public/js/bundle.js b/public/js/bundle.js index b1c4722..bd0423e 100644 --- a/public/js/bundle.js +++ b/public/js/bundle.js @@ -10,16 +10,6 @@ /******/ "use strict"; /******/ var __webpack_modules__ = ({ -/***/ "./js-src/components/overlay-menu.js": -/*!*******************************************!*\ - !*** ./js-src/components/overlay-menu.js ***! - \*******************************************/ -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -eval("\n\nObject.defineProperty(exports, \"__esModule\", ({\n value: true\n}));\nvar React = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\nvar close_button_1 = __webpack_require__(/*! ../../../components/close-button */ \"./js-src/components/close-button.tsx\");\n;\n;\nfunction OverlayMenu(_a) {\n var hiddenMenu = _a.hiddenMenu,\n setHiddenMenu = _a.setHiddenMenu,\n emulationStarted = _a.emulationStarted,\n setHiddenFormSelectFiles = _a.setHiddenFormSelectFiles,\n screenRef = _a.screenRef,\n isFullscreen = _a.isFullscreen,\n setIsFullscreen = _a.setIsFullscreen,\n firstMenuElement = _a.firstMenuElement;\n function exitMenu() {\n setHiddenMenu(true);\n }\n function openSelectFilesMenu() {\n setHiddenFormSelectFiles(false);\n }\n function toggleFullscreen() {\n if (isFullscreen) {\n document.exitFullscreen();\n setIsFullscreen(false);\n return;\n }\n if (screenRef.current != null) {\n screenRef.current.requestFullscreen().then(function () {\n setIsFullscreen(true);\n });\n }\n }\n var styleSelectRom = {};\n if (emulationStarted) {\n styleSelectRom.display = 'none';\n }\n var styleMenu = {};\n if (hiddenMenu) {\n styleMenu.display = 'none';\n }\n var toggleFullscreenText = 'Set fullscreen';\n if (isFullscreen) {\n toggleFullscreenText = 'End fullscreen';\n }\n var saveButton = React.useRef(null);\n var _b = React.useState(0n),\n saveIdentifier = _b[0],\n setSaveidentifier = _b[1];\n function onSave() {\n setSaveidentifier(function (c) {\n return c + 1n;\n });\n }\n return React.createElement(\"div\", {\n style: styleMenu,\n className: \"overlay-menu-div\"\n }, React.createElement(\"div\", {\n className: \"overlay-menu-div-header\"\n }, React.createElement(close_button_1.default, {\n onClick: exitMenu\n })), React.createElement(\"div\", {\n className: \"overlay-menu\"\n }, React.createElement(\"ul\", null, React.createElement(\"li\", null, React.createElement(\"a\", {\n ref: firstMenuElement,\n style: styleSelectRom,\n onClick: openSelectFilesMenu,\n href: \"#\"\n }, \"Select rom\")), React.createElement(\"li\", null, React.createElement(\"a\", {\n ref: saveButton,\n onClick: onSave,\n href: \"#\"\n }, \"Save\")), React.createElement(\"li\", null, React.createElement(\"a\", {\n onClick: toggleFullscreen,\n href: \"#\"\n }, toggleFullscreenText)), React.createElement(\"li\", null, React.createElement(\"a\", {\n href: \"#\",\n onClick: exitMenu\n }, \"Exit\")))));\n}\nexports[\"default\"] = OverlayMenu;\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/components/overlay-menu.js?"); - -/***/ }), - /***/ "./node_modules/react-dom/cjs/react-dom.development.js": /*!*************************************************************!*\ !*** ./node_modules/react-dom/cjs/react-dom.development.js ***! @@ -140,6 +130,16 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ }), +/***/ "./js-src/components/overlay-menu.tsx": +/*!********************************************!*\ + !*** ./js-src/components/overlay-menu.tsx ***! + \********************************************/ +/***/ ((__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 */ OverlayMenu)\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_close_button__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @msgba/components/close-button */ \"./js-src/components/close-button.tsx\");\n/* harmony import */ var _msgba_packet__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @msgba/packet */ \"./js-src/packet.ts\");\n\n\n\n;\nfunction OverlayMenu({ hiddenMenu, setHiddenMenu, emulationStarted, setHiddenFormSelectFiles, screenRef, isFullscreen, setIsFullscreen, firstMenuElement, websocket, onSaveResponseLambdas }) {\n function exitMenu() {\n setHiddenMenu(true);\n }\n function openSelectFilesMenu() {\n setHiddenFormSelectFiles(false);\n }\n function toggleFullscreen() {\n if (isFullscreen) {\n document.exitFullscreen().catch((c) => { console.log(c); });\n setIsFullscreen(false);\n }\n if (screenRef.current != null) {\n screenRef.current.requestFullscreen().then(() => {\n setIsFullscreen(true);\n }).catch((error) => {\n console.log(error);\n });\n }\n }\n const styleSelectRom = {};\n if (emulationStarted) {\n styleSelectRom.display = 'none';\n }\n const styleMenu = {};\n if (hiddenMenu) {\n styleMenu.display = 'none';\n }\n let toggleFullscreenText = 'Set fullscreen';\n if (isFullscreen) {\n toggleFullscreenText = 'End fullscreen';\n }\n const saveButton = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const [saveIdentifier, setSaveidentifier] = react__WEBPACK_IMPORTED_MODULE_0__.useState(0n);\n function onSave() {\n if (websocket == null) {\n console.log('No websocket still');\n return;\n }\n const currentSave = saveIdentifier;\n setSaveidentifier((c) => c + 1n);\n console.log('Reachs here');\n onSaveResponseLambdas.set(currentSave, (saveFile) => {\n const anchor = document.createElement('a');\n const blobSaveResponseFile = new Blob([saveFile.buffer]);\n const fileReader = new FileReader();\n fileReader.onload = () => {\n if (typeof fileReader.result === 'string') {\n anchor.style.display = 'none';\n document.body.appendChild(anchor);\n anchor.href = fileReader.result;\n anchor.download = 'ruby.ss1';\n anchor.click();\n return true;\n }\n return false;\n };\n fileReader.readAsDataURL(blobSaveResponseFile);\n });\n console.log(onSaveResponseLambdas);\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendSaveRequest)(websocket, currentSave);\n }\n const styleSave = {};\n if (!emulationStarted) {\n styleSave.display = 'none';\n }\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { style: styleMenu, className: \"overlay-menu-div\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"overlay-menu-div-header\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_close_button__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { onClick: exitMenu })),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"overlay-menu\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"ul\", null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"li\", null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { ref: firstMenuElement, style: styleSelectRom, onClick: openSelectFilesMenu, href: \"#\" }, \"Select rom\")),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"li\", null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { ref: saveButton, style: styleSave, onClick: onSave, href: \"#\" }, \"Save\")),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"li\", null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { onClick: toggleFullscreen, href: \"#\" }, toggleFullscreenText)),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"li\", null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { href: \"#\", onClick: exitMenu }, \"Exit\"))))));\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/components/overlay-menu.tsx?"); + +/***/ }), + /***/ "./js-src/components/overlay-select-files.tsx": /*!****************************************************!*\ !*** ./js-src/components/overlay-select-files.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, 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?"); +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.tsx\");\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 onWebSocketPacket(event, canvas, ctx, isGBC, setIsGBC, onSaveResponseLambdas) {\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 console.log(size);\n const rawData = packetU8.slice(8, Number(size) + 8);\n console.log(rawData.length);\n packetU8 = null;\n handlePacket(id, rawData, canvas, setIsGBC, onSaveResponseLambdas).catch((error) => {\n console.log('Error handling packet', error);\n });\n}\nasync function handlePacket(id, rawData, canvas, setIsGBC, onSaveResponseLambdas) {\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 case _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.PACKET_ID_SAVE_RESPONSE:\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_6__.handleSaveResponse)(rawData, canvas, onSaveResponseLambdas);\n break;\n default:\n throw new Error(`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 [onSaveResponseLambdas] = react__WEBPACK_IMPORTED_MODULE_0__.useState(new Map());\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 function handleClickStartEmulationButton(e) {\n e.preventDefault();\n if (canvasRef.current == null) {\n alert('Canvas does not exists?');\n return;\n }\n const ctx = canvasRef.current.getContext('2d');\n if (ctx == null) {\n alert('Unable to create canvas context, doing nothing');\n return;\n }\n const inputRom = refInputRom.current;\n const inputSaveState = refInputSaveState.current;\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, canvasRef.current, ctx, isGBC, setIsGBC, onSaveResponseLambdas);\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 }\n const onStartEmulation = (e) => {\n handleClickStartEmulationButton(e);\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, onSaveResponseLambdas: onSaveResponseLambdas }),\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?"); /***/ }), @@ -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, 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?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"handleSaveResponse\": () => (/* binding */ handleSaveResponse),\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 handleSaveResponse(rawData, canvas, onSaveResponseLambdas) {\n let data = rawData;\n console.log(data.length);\n const identifier = _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].byteArrayToU64BigEndian(data.slice(0, 8));\n console.log(data.length);\n data = data.slice(8, data.length);\n console.log(data.length);\n const saveFileSize = _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].byteArrayToU64BigEndian(data.slice(0, 8));\n console.log(data.length);\n const saveFile = data.slice(8, Number(saveFileSize) + 8);\n console.log(data.length);\n const currentLambda = onSaveResponseLambdas.get(identifier);\n console.log(onSaveResponseLambdas);\n if (currentLambda == null) {\n throw new Error(`We received a save request for an unknown identifier ${identifier}`);\n }\n currentLambda(saveFile);\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?"); /***/ })