diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..4265542 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,37 @@ +module.exports = { + env: { + browser: true, + es2021: true + }, + extends: [ + 'plugin:react/recommended', + 'standard-with-typescript' + ], + overrides: [ + ], + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: 'tsconfig.json' + }, + plugins: [ + 'react', + 'no-relative-import-paths' + ], + rules: { + indent: ['error', 4, { SwitchCase: 1 }], + 'no-relative-import-paths/no-relative-import-paths': ['warn', { allowSameFolder: true }], + '@typescript-eslint/indent': ['error', 4], + 'react/jsx-indent': ['error', 4], + 'react/jsx-indent-props': ['error', 4] + }, + settings: { + 'import/resolver': { + typescript: { + project: [ + 'tsconfig.json' + ] + } + } + } +} diff --git a/js-src/components/overlay-controls.tsx b/js-src/components/overlay-controls.tsx index 49b317b..46417d0 100644 --- a/js-src/components/overlay-controls.tsx +++ b/js-src/components/overlay-controls.tsx @@ -1,233 +1,241 @@ -import * as React from 'react'; -import {HOME_BUTTON_IMAGE} from '/constants'; -import {sendKeyDown} from '/packet'; +import * as React from 'react' +import { HOME_BUTTON_IMAGE } from '@msgba/constants' +import { sendKeyDown } from '@msgba/packet' export interface OverlayControlsProps { - firstMenuElement: React.RefObject, - setHiddenMenu: (c: boolean) => void, - webSocket: WebSocket | null; + firstMenuElement: React.RefObject + setHiddenMenu: (c: boolean) => void + webSocket: WebSocket | null }; -interface ControlMap { - [id: string]: {key: number, ref: React.RefObject, sym: string, classes: string, transformX?: number, transformY?: number} -}; +interface ControlValue { + key: number + ref: React.RefObject + sym: string + classes: string + transformX?: number + transformY?: number +} -export default function OverlayControls({firstMenuElement, setHiddenMenu, webSocket}: OverlayControlsProps) { - function showOverlayMenu() { - setHiddenMenu(false); +type ControlMap = Record + +export default function OverlayControls ({ firstMenuElement, setHiddenMenu, webSocket }: OverlayControlsProps): JSX.Element { + function showOverlayMenu (): void { + setHiddenMenu(false) setTimeout(() => { if (firstMenuElement.current == null) { - return; + return } - firstMenuElement.current.focus(); - }, 100); + firstMenuElement.current.focus() + }, 100) } - const [onGoingTouches, setOnGoingTouches] = React.useState<{[id: string]: number}>({}); - function mouseDown(e: React.MouseEvent | React.TouchEvent, key: number) { - e.preventDefault(); + const onGoingTouches = new Map() + + function mouseDown (e: React.MouseEvent, key: number): void { + e.preventDefault() if (webSocket == null) { - console.log('There is not websocket'); - return; + console.log('There is not websocket') + return } - sendKeyDown(webSocket, true, key); + sendKeyDown(webSocket, true, key) } - function mouseUp(e: React.MouseEvent | React.TouchEvent, key: number) { - e.preventDefault(); + function mouseUp (e: React.MouseEvent, key: number): void { + e.preventDefault() if (webSocket == null) { - console.log('There is not websocket'); - return; + console.log('There is not websocket') + return } - sendKeyDown(webSocket, false, key); + sendKeyDown(webSocket, false, key) } - const controls: ControlMap = {}; + const controls: ControlMap = {} controls.a = { - ref: React.useRef(null), + ref: React.useRef(null), key: 0, sym: 'A', - classes: 'control-a control-button-a-b control control-button', - }; + classes: 'control-a control-button-a-b control control-button' + } controls.b = { - ref: React.useRef(null), + ref: React.useRef(null), key: 1, sym: 'B', transformX: 50, - classes: 'control-b control-button-a-b control control-button', - }; + classes: 'control-b control-button-a-b control control-button' + } controls.l = { key: 2, sym: 'L', classes: 'control-l control-button-l-r control', - ref: React.useRef(null), - }; + ref: React.useRef(null) + } controls.r = { key: 3, sym: 'R', classes: 'control-r control-button-l-r control', - ref: React.useRef(null), - }; + ref: React.useRef(null) + } controls.start = { - ref: React.useRef(null), key: 4, sym: 'START', classes: 'control-start control-button-start-select control', transformX: 25, - }; + ref: React.useRef(null) + } controls.select = { - ref: React.useRef(null), key: 5, sym: 'SEL', classes: 'control-select control-button-start-select control', transformX: -25, - }; + ref: React.useRef(null) + } controls.up = { - ref: React.useRef(null), key: 6, sym: '^', transformX: 100, classes: 'control-up control control-pad-button', + ref: React.useRef(null) } controls.down = { - ref: React.useRef(null), key: 7, sym: 'v', transformX: 100, classes: 'control-down control control-pad-button', + ref: React.useRef(null) } controls.left = { - ref: React.useRef(null), key: 8, sym: '<', classes: 'control-left control control-pad-button', + ref: React.useRef(null) } controls.right = { - ref: React.useRef(null), key: 9, sym: '>', transformX: 200, classes: 'control-right control control-pad-button', + ref: React.useRef(null) } - function determineKey(e: React.TouchEvent, touch: React.Touch): number | null { - const x = touch.pageX; - const y = touch.pageY; + function determineKey (e: React.TouchEvent, touch: React.Touch): number | null { + const x = touch.pageX + const y = touch.pageY for (const control of Object.keys(controls)) { - const ref = controls[control].ref.current; + const ref = controls[control].ref.current if (ref == null) { - console.log('No ref found'); - continue; + console.log('No ref found') + continue } - let top = ref.getBoundingClientRect().top + document.documentElement.scrollTop; - const currentControl = controls[control]; - const transformX = currentControl.transformX; - const transformY = currentControl.transformY; - let offsetLeft = ref.offsetLeft; - const offsetWidth = ref.offsetWidth; - let offsetTop = top; - const offsetHeight = ref.offsetHeight; + const top = ref.getBoundingClientRect().top + document.documentElement.scrollTop + const currentControl = controls[control] + const transformX = currentControl.transformX + const transformY = currentControl.transformY + let offsetLeft = ref.offsetLeft + const offsetWidth = ref.offsetWidth + let offsetTop = top + const offsetHeight = ref.offsetHeight if (transformX != null) { - offsetLeft += offsetWidth * (transformX / 100); + offsetLeft += offsetWidth * (transformX / 100) } if (transformY != null) { - offsetTop += offsetHeight * (transformY / 100); + offsetTop += offsetHeight * (transformY / 100) } - console.log(x, y, offsetLeft, offsetTop, offsetWidth, offsetHeight); if (x >= offsetLeft && x <= offsetLeft + offsetWidth && y >= offsetTop && y <= offsetTop + offsetHeight) { - return controls[control].key; + return controls[control].key } } - return null; + return null } - function touchStartControls(e: React.TouchEvent) { - e.preventDefault(); + function touchStartControls (e: React.TouchEvent): boolean { + e.preventDefault() if (webSocket == null) { - console.log('There is not websocket'); - return; + console.log('There is not websocket') + return false } for (let i = 0; i < e.changedTouches.length; i++) { - const touch = e.changedTouches[i]; - let key: number|null = determineKey(e, touch); + const touch = e.changedTouches[i] + const key: number | null = determineKey(e, touch) if (key == null) { - continue; + continue } - const idx = touch.identifier; - onGoingTouches[idx] = key; - sendKeyDown(webSocket, true, key); + const idx = touch.identifier + onGoingTouches.set(idx, key) + sendKeyDown(webSocket, true, key) } - return false; + return false } - function touchMoveControls(e: React.TouchEvent) { - e.preventDefault(); + function touchMoveControls (e: React.TouchEvent): boolean { + e.preventDefault() if (webSocket == null) { - console.log('There is not websocket'); - return; + console.log('There is not websocket') + return false } for (let i = 0; i < e.changedTouches.length; i++) { - const touch = e.changedTouches[i]; - let key: number|null = determineKey(e, touch); - const idx = touch.identifier; + const touch = e.changedTouches[i] + const key: number | null = determineKey(e, touch) + const idx = touch.identifier if (key == null) { - continue; + continue } - if (onGoingTouches[idx] != null) { - sendKeyDown(webSocket, false, onGoingTouches[idx]); - delete onGoingTouches[idx]; + const oldKey = onGoingTouches.get(idx) + if (oldKey != null) { + sendKeyDown(webSocket, false, oldKey) + onGoingTouches.delete(idx) } - onGoingTouches[idx] = key; - sendKeyDown(webSocket, true, key); + onGoingTouches.set(idx, key) + sendKeyDown(webSocket, true, key) } - return false; + return false } - - function touchEndControls(e: React.TouchEvent) { - e.preventDefault(); + function touchEndControls (e: React.TouchEvent): boolean { + e.preventDefault() if (webSocket == null) { - console.log('There is not websocket'); - return; + console.log('There is not websocket') + return false } for (let i = 0; i < e.changedTouches.length; i++) { - const touch = e.changedTouches[i]; - const idx = touch.identifier; - if (onGoingTouches[idx] == null) { - return; + const touch = e.changedTouches[i] + const idx = touch.identifier + const oldKey = onGoingTouches.get(idx) + if (oldKey == null) { + return false } - sendKeyDown(webSocket, false, onGoingTouches[idx]); - delete onGoingTouches[idx]; + sendKeyDown(webSocket, false, oldKey) + onGoingTouches.delete(idx) } - return false; + return false } - const keyMap: string[] = ["KeyZ", "KeyX", "KeyA", "KeyS", - "Enter", "Space", "ArrowUp", "ArrowDown", - "ArrowLeft", "ArrowRight"]; - function onPressControl(e: React.KeyboardEvent) { + const keyMap: string[] = ['KeyZ', 'KeyX', 'KeyA', 'KeyS', + 'Enter', 'Space', 'ArrowUp', 'ArrowDown', + 'ArrowLeft', 'ArrowRight'] + function onPressControl (e: React.KeyboardEvent): void { if (webSocket == null) { - console.log('There is not websocket'); - return; + console.log('There is not websocket') + return } - let key = keyMap.findIndex((c: string) => c == e.code); - if (key != -1) { - e.preventDefault(); - sendKeyDown(webSocket, true, key); + const key = keyMap.findIndex((c: string) => c === e.code) + if (key !== -1) { + e.preventDefault() + sendKeyDown(webSocket, true, key) } } - function onUnpressControl(e: React.KeyboardEvent) { + function onUnpressControl (e: React.KeyboardEvent): void { if (webSocket == null) { - console.log('There is not websocket'); - return; + console.log('There is not websocket') + return } - let key = keyMap.findIndex((c: string) => c == e.code); - if (key != -1) { - e.preventDefault(); - sendKeyDown(webSocket, false, key); + const key = keyMap.findIndex((c: string) => c === e.code) + if (key !== -1) { + e.preventDefault() + sendKeyDown(webSocket, false, key) } } - document.onselectstart = () => false; + document.onselectstart = () => false return ( -
+ - ); + ) } diff --git a/js-src/components/overlay-menu.tsx b/js-src/components/overlay-menu.tsx index 10d4f62..0ee73f8 100644 --- a/js-src/components/overlay-menu.tsx +++ b/js-src/components/overlay-menu.tsx @@ -1,59 +1,79 @@ -import * as React from 'react'; -import CloseButton from '/components/close-button'; -import {CLOSE_BUTTON_IMAGE} from '/constants'; +import * as React from 'react' +import CloseButton from '@msgba/components/close-button' +import { sendSaveRequest } from '@msgba/packet' export interface OverlayMenuProps { - hiddenMenu: boolean, - setHiddenMenu: (c: boolean) => void, - emulationStarted: boolean, - setHiddenFormSelectFiles: (c: boolean) => void, - screenRef: React.RefObject; - isFullscreen: boolean; - setIsFullscreen: (c:boolean) => void; - firstMenuElement: React.RefObject, + hiddenMenu: boolean + setHiddenMenu: (c: boolean) => void + emulationStarted: boolean + setHiddenFormSelectFiles: (c: boolean) => void + screenRef: React.RefObject + isFullscreen: boolean + setIsFullscreen: (c: boolean) => void + firstMenuElement: React.RefObject + websocket: WebSocket | null }; -interface Style { - [id: string]: string; -}; +type Style = Record -export default function OverlayMenu({hiddenMenu, setHiddenMenu, emulationStarted, - setHiddenFormSelectFiles, screenRef, isFullscreen, setIsFullscreen, - firstMenuElement}: OverlayMenuProps) { - function exitMenu() { - setHiddenMenu(true); +export default function OverlayMenu ({ + hiddenMenu, setHiddenMenu, emulationStarted, + setHiddenFormSelectFiles, screenRef, isFullscreen, setIsFullscreen, + firstMenuElement, websocket +}: OverlayMenuProps): JSX.Element { + function exitMenu (): void { + setHiddenMenu(true) } - function openSelectFilesMenu() { - setHiddenFormSelectFiles(false); + function openSelectFilesMenu (): void { + setHiddenFormSelectFiles(false) } - function toggleFullscreen() { + function toggleFullscreen (): void { if (isFullscreen) { - document.exitFullscreen(); - setIsFullscreen(false); - return; + document.exitFullscreen().catch((c: string) => { console.log(c) }) + setIsFullscreen(false) } if (screenRef.current != null) { screenRef.current.requestFullscreen().then(() => { - setIsFullscreen(true); - }); + setIsFullscreen(true) + }).catch((error: string) => { + console.log(error) + }) } } - const styleSelectRom: Style = {}; + const styleSelectRom: Style = {} if (emulationStarted) { - styleSelectRom.display = 'none'; + styleSelectRom.display = 'none' } - const styleMenu: Style = {}; + const styleMenu: Style = {} if (hiddenMenu) { - styleMenu.display = 'none'; + styleMenu.display = 'none' } - let toggleFullscreenText: string = 'Set fullscreen'; + let toggleFullscreenText: string = 'Set fullscreen' if (isFullscreen) { - toggleFullscreenText = 'End fullscreen'; + toggleFullscreenText = 'End fullscreen' + } + + const saveButton = React.useRef(null) + const [saveIdentifier, setSaveidentifier] = React.useState(0n) + + function onSave (): void { + if (websocket == null) { + console.log('No websocket still') + return + } + const currentSave = saveIdentifier + setSaveidentifier((c: bigint) => c + 1n) + sendSaveRequest(websocket, currentSave) + } + + const styleSave: Style = {} + if (!emulationStarted) { + styleSave.display = 'none' } return ( @@ -64,10 +84,11 @@ export default function OverlayMenu({hiddenMenu, setHiddenMenu, emulationStarted
- ); + ) } diff --git a/js-src/components/page.tsx b/js-src/components/page.tsx index 662a8ed..fa9ad06 100644 --- a/js-src/components/page.tsx +++ b/js-src/components/page.tsx @@ -1,141 +1,147 @@ import * as React from 'react'; -import CenterElement from '/components/center-element'; -import FormSelectFiles from '/components/form-select-files'; -import CanvasGBAEmulator from '/components/canvas-gba-emulator'; -import OverlayControls from '/components/overlay-controls'; -import OverlayMenu from '/components/overlay-menu'; -import OverlaySelectFiles from '/components/overlay-select-files'; -import CloseButton from '/components/close-button'; -import {sendHello, handleSendFrame} from '/packet'; +import CenterElement from '@msgba/components/center-element' +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 Endian from '/endian'; +import Endian from '@msgba/endian' -import {MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_SEND_FRAME} from '/constants'; +import { MIN_WIDTH, MIN_HEIGHT, PACKET_ID_SEND_FRAME } from '@msgba/constants' export interface handleClickStartEmulationButtonObjectArgs { - e: React.MouseEvent; - inputRom: HTMLInputElement | null; - inputSaveState: HTMLInputElement | null; - canvas: React.RefObject; - setEmulationStarted: (c: boolean) => void, - setHiddenMenu: (c: boolean) => void; - setHiddenFormSelectFiles: (c: boolean) => void; - setWebSocket: (c: WebSocket | null) => void; -}; + e: React.MouseEvent + inputRom: HTMLInputElement | null + inputSaveState: HTMLInputElement | null + canvas: React.RefObject + setEmulationStarted: (c: boolean) => void + setHiddenMenu: (c: boolean) => void + setHiddenFormSelectFiles: (c: boolean) => void + setWebSocket: (c: WebSocket | null) => void +} -function handleClickStartEmulationButton({e, inputRom, inputSaveState, canvas, setEmulationStarted, setHiddenMenu, - setHiddenFormSelectFiles, setWebSocket}: handleClickStartEmulationButtonObjectArgs) { - e.preventDefault(); +function handleClickStartEmulationButton ({ + e, inputRom, inputSaveState, canvas, + setEmulationStarted, setHiddenMenu, + setHiddenFormSelectFiles, setWebSocket +}: handleClickStartEmulationButtonObjectArgs): void { + e.preventDefault() if (canvas.current == null) { - alert('Canvas does not exists?'); - return; + alert('Canvas does not exists?') + return } const ctx = canvas.current.getContext('2d') if (ctx == null) { - alert('Unable to create canvas context, doing nothing'); - return; + 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; + alert('Unable to read the files ') + return } - if (inputRom.files.length == 0) { - alert('There is no rom still'); - 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; + if (inputSaveState.files.length === 0) { + alert('There is no savestate still') + return } - const rom_file = inputRom.files[0]; - const savestate_file = inputSaveState.files[0]; - rom_file.arrayBuffer().then((rom_buffer) => { - savestate_file.arrayBuffer().then((savestate_buffer) => { - const rom_array = new Uint8Array(rom_buffer); - const savestate_array = new Uint8Array(savestate_buffer); - const webSocket = new WebSocket(`ws://${window.location.host}/ws`); - setWebSocket(webSocket); - webSocket.binaryType = 'arraybuffer'; + 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); + setEmulationStarted(false) + console.log('Closing websocket.') + setWebSocket(null) } webSocket.onopen = () => { - console.log('Opened websocket.'); - setEmulationStarted(true); - sendHello(webSocket, rom_array, savestate_array); - setHiddenMenu(true); - setHiddenFormSelectFiles(true); - }; + console.log('Opened websocket.') + setEmulationStarted(true) + sendHello(webSocket, romArray, savestateArray) + setHiddenMenu(true) + setHiddenFormSelectFiles(true) + } webSocket.addEventListener('message', (event) => { onWebSocketPacket(event, canvas.current, ctx) - }); - }); - }); + }) + }).catch((c: string) => { + console.log('Unable to convert file to array_buffer') + }) + }).catch((c: string) => { + console.log('Unable to convert file to array_buffer') + }) } -function onWebSocketPacket(event: MessageEvent, canvas: HTMLCanvasElement | null, ctx: CanvasRenderingContext2D) { - const buffer = event.data; - let packet_u8: Uint8Array | null = new Uint8Array(buffer); - const id = Endian.byteArrayToU64BigEndian(packet_u8.slice(0, 8)); - packet_u8 = packet_u8.slice(8, packet_u8.length); - const size = Endian.byteArrayToU64BigEndian(packet_u8.slice(0, 8)); - const raw_data = packet_u8.slice(8, packet_u8.length); - packet_u8 = null; +function onWebSocketPacket (event: MessageEvent, + canvas: HTMLCanvasElement | null, + ctx: CanvasRenderingContext2D): 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)) + packetU8 = null switch (id) { case PACKET_ID_SEND_FRAME: - handleSendFrame(raw_data, canvas, ctx); - break; + handleSendFrame(rawData, canvas, ctx) + break default: - console.log(`Received unknown packet ${id}`); + console.log(`Received unknown packet ${id}`) } } -export default function Page() { - const screenDimensions = useScreenDimensions(); - const emulatorDimensions = calculateSizeEmulator(screenDimensions); - const canvasRef = React.useRef(null); - function resizeCanvas() { - const canvas = canvasRef.current; +export default function Page (): JSX.Element { + const screenDimensions = useScreenDimensions() + const emulatorDimensions = calculateSizeEmulator(screenDimensions) + const canvasRef = React.useRef(null) + function resizeCanvas (): void { + const canvas = canvasRef.current if (canvas == null) { - return; + return } if (emulatorDimensions.width === undefined || emulatorDimensions.height === undefined) { - return; + return } - canvas.width = emulatorDimensions.width; - canvas.height = emulatorDimensions.height; + canvas.width = emulatorDimensions.width + canvas.height = emulatorDimensions.height const ctx = canvas.getContext('2d') if (ctx == null) { - return; + return } - fillBlack(canvas, ctx); - }; - const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = React.useState(true); - React.useEffect(resizeCanvas, [emulatorDimensions]); - const refInputRom = React.useRef(null); - const refInputSaveState = React.useRef(null); - const [emulationStarted, setEmulationStarted] = React.useState(false); - const [hiddenMenu, setHiddenMenu] = React.useState(true); - const [webSocket, setWebSocket] = React.useState(null); - const onStartEmulation = (e: React.MouseEvent) => { + fillBlack(canvas, ctx) + } + const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = React.useState(true) + React.useEffect(resizeCanvas, [emulatorDimensions]) + const refInputRom = React.useRef(null) + const refInputSaveState = React.useRef(null) + const [emulationStarted, setEmulationStarted] = React.useState(false) + const [hiddenMenu, setHiddenMenu] = React.useState(true) + const [webSocket, setWebSocket] = React.useState(null) + const onStartEmulation = (e: React.MouseEvent): void => { handleClickStartEmulationButton({ - e: e, - setEmulationStarted: setEmulationStarted, + e, + setEmulationStarted, inputRom: refInputRom.current, inputSaveState: refInputSaveState.current, canvas: canvasRef, - setHiddenMenu: setHiddenMenu, - setHiddenFormSelectFiles: setHiddenFormSelectFiles, - setWebSocket: setWebSocket, - - }); - }; - const firstMenuElement = React.useRef(null); - const screenRef = React.useRef(null); - const [isFullscreen, setIsFullscreen] = React.useState(false); + setHiddenMenu, + setHiddenFormSelectFiles, + setWebSocket + }) + } + const firstMenuElement = React.useRef(null) + const screenRef = React.useRef(null) + const [isFullscreen, setIsFullscreen] = React.useState(false) return (
+ firstMenuElement={firstMenuElement} websocket={webSocket}/>
- +
- ); + ) } -function getScreenDimensions() { +function getScreenDimensions (): EmulatorDimensions { return { width: document.body.clientWidth, height: document.body.clientHeight - }; + } } -function useScreenDimensions() { - const [screenDimensions, setScreenDimensions] = React.useState(getScreenDimensions()); +function useScreenDimensions (): EmulatorDimensions { + const [screenDimensions, setScreenDimensions] = React.useState(getScreenDimensions()) React.useEffect(() => { - function onResize() { - setScreenDimensions(getScreenDimensions()); + function onResize (): void { + setScreenDimensions(getScreenDimensions()) } - window.addEventListener("resize", onResize); - + window.addEventListener('resize', onResize) + return () => { - window.removeEventListener("resize", onResize); + window.removeEventListener('resize', onResize) } - }, []); - - return screenDimensions; + }, []) + return screenDimensions } -function fillBlack(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) { - ctx.beginPath(); - ctx.rect(0, 0, canvas.width, canvas.height); - ctx.fillStyle = '#0E0E10'; - ctx.fill(); +function fillBlack (canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D): void { + ctx.beginPath() + ctx.rect(0, 0, canvas.width, canvas.height) + ctx.fillStyle = '#0E0E10' + ctx.fill() } export interface EmulatorDimensions { - width?: number; - height?: number; + width?: number + height?: number }; -function calculateSizeEmulator(screenDimensions: EmulatorDimensions): EmulatorDimensions { + +function calculateSizeEmulator (screenDimensions: EmulatorDimensions): EmulatorDimensions { if (screenDimensions.width === undefined || screenDimensions.height === undefined) { - console.error(screenDimensions, 'screenDimensions has undefined fields'); - return {}; + console.error(screenDimensions, 'screenDimensions has undefined fields') + return {} } - const width = screenDimensions.width; - const height = screenDimensions.height; - const emulatorDimensions: EmulatorDimensions = {}; + const width = screenDimensions.width + const height = screenDimensions.height + const emulatorDimensions: EmulatorDimensions = {} if (width < MIN_WIDTH || height < MIN_HEIGHT) { return { width: MIN_WIDTH, - height: MIN_HEIGHT, - }; + height: MIN_HEIGHT + } } - const ratioWidth = width / MIN_WIDTH; - const ratioHeight = height / MIN_HEIGHT; + const ratioWidth = width / MIN_WIDTH + const ratioHeight = height / MIN_HEIGHT if (ratioWidth < ratioHeight) { - emulatorDimensions.width = MIN_WIDTH * ratioWidth; - emulatorDimensions.height = MIN_HEIGHT * ratioWidth; + emulatorDimensions.width = MIN_WIDTH * ratioWidth + emulatorDimensions.height = MIN_HEIGHT * ratioWidth } else { - emulatorDimensions.height = MIN_HEIGHT * ratioHeight; - emulatorDimensions.width = MIN_WIDTH * ratioHeight; + emulatorDimensions.height = MIN_HEIGHT * ratioHeight + emulatorDimensions.width = MIN_WIDTH * ratioHeight } - return emulatorDimensions; + return emulatorDimensions } diff --git a/js-src/constants.ts b/js-src/constants.ts index 573f6c2..13e1945 100644 --- a/js-src/constants.ts +++ b/js-src/constants.ts @@ -1,16 +1,9 @@ -export const MIN_WIDTH = 240; -export const MIN_HEIGHT = 160; -export const PACKET_ID_HELLO = 0n; -export const PACKET_ID_SEND_FRAME = 1n; -export const PACKET_ID_KEY_DOWN = 2n; -export const CLOSE_BUTTON_IMAGE: string = "/img/close.png"; -export const HOME_BUTTON_IMAGE: string = "/img/home.png"; - -export default class Constants { - public static MIN_WIDTH: number = MIN_WIDTH; - public static MIN_HEIGHT: number = MIN_HEIGHT; - public static PACKET_ID_HELLO: bigint = PACKET_ID_HELLO; - public static PACKET_ID_SEND_FRAME: bigint = PACKET_ID_SEND_FRAME; - public static CLOSE_BUTTON_IMAGE: string = CLOSE_BUTTON_IMAGE; - public static HOME_BUTTON_IMAGE: string = HOME_BUTTON_IMAGE; -}; +export const MIN_WIDTH = 240 +export const MIN_HEIGHT = 160 +export const PACKET_ID_HELLO = 0n +export const PACKET_ID_SEND_FRAME = 1n +export const PACKET_ID_KEY_DOWN = 2n +export const PACKET_ID_SAVE_REQUEST = 3n +export const PACKET_ID_SAVE_RESPONSE = 4n +export const CLOSE_BUTTON_IMAGE: string = '/img/close.png' +export const HOME_BUTTON_IMAGE: string = '/img/home.png' diff --git a/js-src/index.tsx b/js-src/index.tsx index cb4d83b..e59f194 100644 --- a/js-src/index.tsx +++ b/js-src/index.tsx @@ -2,8 +2,8 @@ import * as React from 'react'; import * as ReactDOMClient from 'react-dom/client'; -import Endian from '/endian'; -import Page from '/components/page'; +import Endian from '@msgba/endian'; +import Page from '@msgba/components/page'; const body = document.querySelector('body'); if (body != null) { diff --git a/js-src/packet.ts b/js-src/packet.ts index 7a124c7..4907423 100644 --- a/js-src/packet.ts +++ b/js-src/packet.ts @@ -1,70 +1,78 @@ -import Endian from '/endian'; -import {MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_SEND_FRAME, PACKET_ID_KEY_DOWN} from '/constants'; +import Endian from '@msgba/endian' +import { MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_KEY_DOWN, PACKET_ID_SAVE_REQUEST } from '@msgba/constants' -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 concatU8Array (array1: Uint8Array, array2: Uint8Array): Uint8Array { + const finalArray = new Uint8Array(array1.length + array2.length) + finalArray.set(array1) + finalArray.set(array2, array1.length) + return finalArray } -export 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 = +export function sendHello (websocket: WebSocket, romArray: Uint8Array, savestateArray: Uint8Array): void { + console.log('Sending hello.') + const lengthRom = BigInt(romArray.length) + const lengthSavestate = BigInt(savestateArray.length) + const rawData = concatU8Array( concatU8Array( - concatU8Array(Endian.u64ToByteArrayBigEndian(length_rom), rom_array), - Endian.u64ToByteArrayBigEndian(length_savestate) + concatU8Array(Endian.u64ToByteArrayBigEndian(lengthRom), romArray), + Endian.u64ToByteArrayBigEndian(lengthSavestate) ), - savestate_array - ); - sendPacket(websocket, PACKET_ID_HELLO, raw_data); + savestateArray + ) + sendPacket(websocket, PACKET_ID_HELLO, rawData) } -export function sendKeyDown(websocket: WebSocket, isDown: boolean, key: number) { - console.log('Sending keyDown.', isDown); - const isDownArray = new Uint8Array(1); - isDownArray[0] = isDown ? 1: 0; - const rawData = concatU8Array(isDownArray, Endian.u32ToByteArrayBigEndian(key)); - sendPacket(websocket, PACKET_ID_KEY_DOWN, rawData); +export function sendKeyDown (websocket: WebSocket, isDown: boolean, key: number): void { + console.log('Sending keyDown.', isDown) + const isDownArray = new Uint8Array(1) + isDownArray[0] = isDown ? 1 : 0 + const rawData = concatU8Array(isDownArray, Endian.u32ToByteArrayBigEndian(key)) + sendPacket(websocket, PACKET_ID_KEY_DOWN, rawData) } -export 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); +export function sendSaveRequest (websocket: WebSocket, identifier: bigint): void { + console.log('Sendidng save request', identifier) + const rawData = Endian.u64ToByteArrayBigEndian(identifier) + sendPacket(websocket, PACKET_ID_SAVE_REQUEST, rawData) } -export function handleSendFrame(raw_data: Uint8Array, canvas: HTMLCanvasElement | null, ctx: CanvasRenderingContext2D) { - console.log('Reachs here'); +export function sendPacket (websocket: WebSocket, id: bigint, rawData: Uint8Array): void { + const packetU8 = concatU8Array( + concatU8Array(Endian.u64ToByteArrayBigEndian(id), Endian.u64ToByteArrayBigEndian(BigInt(rawData.length))), + rawData + ) + const packetBuffer = packetU8.buffer + console.log('Sending packet') + websocket.send(packetBuffer) +} + +export function handleSendFrame (rawData: Uint8Array, canvas: HTMLCanvasElement | null, ctx: CanvasRenderingContext2D): void { + console.log('Reachs here') if (canvas == null) { - console.log('No canvas'); - return; + console.log('No canvas') + return } - 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)); + data = null + createImageBitmap(imgData).then((bitmap) => { drawBitmap(bitmap, canvas, ctx) }).catch((c: string) => { + console.log(`Unable to print to the canvas the frame because: ${c}`) + }) } -function drawBitmap(bitmap: ImageBitmap, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) { - ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height); +function drawBitmap (bitmap: ImageBitmap, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D): void { + ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height) } diff --git a/package.json b/package.json index 2c215ad..d8d1de6 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,20 @@ "@babel/preset-react": "^7.18.6", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", + "@typescript-eslint/eslint-plugin": "^5.57.0", "babel-loader": "^9.1.2", + "eslint": "^8.36.0", + "eslint-config-standard-with-typescript": "^34.0.1", + "eslint-import-resolver-typescript": "^3.5.3", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^15.6.1", + "eslint-plugin-no-relative-import-paths": "^1.5.2", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.32.2", "file-loader": "^6.2.0", "ts-loader": "^9.4.2", "typescript": "^5.0.2", + "typescript-transform-paths": "^3.4.6", "url-loader": "^4.1.1", "webpack": "^5.38.1", "webpack-cli": "^4.7.2" diff --git a/public/js/bundle.js b/public/js/bundle.js index e8b92e8..c61ce14 100644 --- a/public/js/bundle.js +++ b/public/js/bundle.js @@ -10,6 +10,16 @@ /******/ "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 ***! @@ -126,17 +136,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ OverlayControls)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _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;\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 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 const controls = {};\n controls.a = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 0,\n sym: 'A',\n classes: 'control-a control-button-a-b control control-button',\n };\n controls.b = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 1,\n sym: 'B',\n transformX: 50,\n classes: 'control-b control-button-a-b control control-button',\n };\n controls.l = {\n key: 2,\n sym: 'L',\n classes: 'control-l control-button-l-r control',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n };\n controls.r = {\n key: 3,\n sym: 'R',\n classes: 'control-r control-button-l-r control',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n };\n controls.start = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 4,\n sym: 'START',\n classes: 'control-start control-button-start-select control',\n transformX: 25,\n };\n controls.select = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 5,\n sym: 'SEL',\n classes: 'control-select control-button-start-select control',\n transformX: -25,\n };\n controls.up = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 6,\n sym: '^',\n transformX: 100,\n classes: 'control-up control control-pad-button',\n };\n controls.down = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 7,\n sym: 'v',\n transformX: 100,\n classes: 'control-down control control-pad-button',\n };\n controls.left = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 8,\n sym: '<',\n classes: 'control-left control control-pad-button',\n };\n controls.right = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 9,\n sym: '>',\n transformX: 200,\n classes: 'control-right control control-pad-button',\n };\n function determineKey(e, touch) {\n const x = touch.pageX;\n const y = touch.pageY;\n for (const control of Object.keys(controls)) {\n const ref = controls[control].ref.current;\n if (ref == null) {\n console.log('No ref found');\n continue;\n }\n let top = ref.getBoundingClientRect().top + document.documentElement.scrollTop;\n const currentControl = controls[control];\n const transformX = currentControl.transformX;\n const transformY = currentControl.transformY;\n let offsetLeft = ref.offsetLeft;\n const offsetWidth = ref.offsetWidth;\n let offsetTop = top;\n const offsetHeight = ref.offsetHeight;\n if (transformX != null) {\n offsetLeft += offsetWidth * (transformX / 100);\n }\n if (transformY != null) {\n offsetTop += offsetHeight * (transformY / 100);\n }\n console.log(x, y, offsetLeft, offsetTop, offsetWidth, offsetHeight);\n if (x >= offsetLeft && x <= offsetLeft + offsetWidth && y >= offsetTop && y <= offsetTop + offsetHeight) {\n return controls[control].key;\n }\n }\n return null;\n }\n function touchStartControls(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n let key = determineKey(e, touch);\n if (key == null) {\n continue;\n }\n const idx = touch.identifier;\n onGoingTouches[idx] = key;\n (0,_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n return false;\n }\n function touchMoveControls(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n let key = determineKey(e, touch);\n const idx = touch.identifier;\n if (key == null) {\n continue;\n }\n if (onGoingTouches[idx] != null) {\n (0,_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, onGoingTouches[idx]);\n delete onGoingTouches[idx];\n }\n onGoingTouches[idx] = key;\n (0,_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n return false;\n }\n function touchEndControls(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const idx = touch.identifier;\n if (onGoingTouches[idx] == null) {\n return;\n }\n (0,_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, onGoingTouches[idx]);\n delete onGoingTouches[idx];\n }\n return false;\n }\n const keyMap = [\"KeyZ\", \"KeyX\", \"KeyA\", \"KeyS\",\n \"Enter\", \"Space\", \"ArrowUp\", \"ArrowDown\",\n \"ArrowLeft\", \"ArrowRight\"];\n function onPressControl(e) {\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n let key = keyMap.findIndex((c) => c == e.code);\n if (key != -1) {\n e.preventDefault();\n (0,_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n }\n function onUnpressControl(e) {\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n let key = keyMap.findIndex((c) => c == e.code);\n if (key != -1) {\n e.preventDefault();\n (0,_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, key);\n }\n }\n document.onselectstart = () => false;\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { tabIndex: -1, className: \"overlay\", onKeyDown: onPressControl, onKeyUp: onUnpressControl },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"vertical-padding\" }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"controls\", onTouchStart: touchStartControls, onTouchMove: touchMoveControls, onTouchEnd: touchEndControls },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, className: \"gear control\", onClick: showOverlayMenu, onTouchStart: showOverlayMenu },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"img\", { src: _constants__WEBPACK_IMPORTED_MODULE_1__.HOME_BUTTON_IMAGE, alt: \"Go to menu. (House icon)\" })),\n Object.keys(controls).map((key) => react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, className: controls[key].classes, ref: controls[key].ref, key: key, onMouseDown: (e) => mouseDown(e, controls[key].key), onMouseUp: (e) => mouseUp(e, controls[key].key) }, controls[key].sym)))));\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/components/overlay-controls.tsx?"); - -/***/ }), - -/***/ "./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 _components_close_button__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../components/close-button */ \"./js-src/components/close-button.tsx\");\n\n\n;\n;\nfunction OverlayMenu({ hiddenMenu, setHiddenMenu, emulationStarted, setHiddenFormSelectFiles, screenRef, isFullscreen, setIsFullscreen, 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(() => {\n setIsFullscreen(true);\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 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(_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\", { 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?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ OverlayControls)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _msgba_constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @msgba/constants */ \"./js-src/constants.ts\");\n/* harmony import */ var _msgba_packet__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @msgba/packet */ \"./js-src/packet.ts\");\n\n\n\n;\nfunction OverlayControls({ firstMenuElement, setHiddenMenu, webSocket }) {\n function showOverlayMenu() {\n setHiddenMenu(false);\n setTimeout(() => {\n if (firstMenuElement.current == null) {\n return;\n }\n firstMenuElement.current.focus();\n }, 100);\n }\n const onGoingTouches = new Map();\n function mouseDown(e, key) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n function mouseUp(e, key) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, key);\n }\n const controls = {};\n controls.a = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 0,\n sym: 'A',\n classes: 'control-a control-button-a-b control control-button'\n };\n controls.b = {\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null),\n key: 1,\n sym: 'B',\n transformX: 50,\n classes: 'control-b control-button-a-b control control-button'\n };\n controls.l = {\n key: 2,\n sym: 'L',\n classes: 'control-l control-button-l-r control',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.r = {\n key: 3,\n sym: 'R',\n classes: 'control-r control-button-l-r control',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.start = {\n key: 4,\n sym: 'START',\n classes: 'control-start control-button-start-select control',\n transformX: 25,\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.select = {\n key: 5,\n sym: 'SEL',\n classes: 'control-select control-button-start-select control',\n transformX: -25,\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.up = {\n key: 6,\n sym: '^',\n transformX: 100,\n classes: 'control-up control control-pad-button',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.down = {\n key: 7,\n sym: 'v',\n transformX: 100,\n classes: 'control-down control control-pad-button',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.left = {\n key: 8,\n sym: '<',\n classes: 'control-left control control-pad-button',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n controls.right = {\n key: 9,\n sym: '>',\n transformX: 200,\n classes: 'control-right control control-pad-button',\n ref: react__WEBPACK_IMPORTED_MODULE_0__.useRef(null)\n };\n function determineKey(e, touch) {\n const x = touch.pageX;\n const y = touch.pageY;\n for (const control of Object.keys(controls)) {\n const ref = controls[control].ref.current;\n if (ref == null) {\n console.log('No ref found');\n continue;\n }\n const top = ref.getBoundingClientRect().top + document.documentElement.scrollTop;\n const currentControl = controls[control];\n const transformX = currentControl.transformX;\n const transformY = currentControl.transformY;\n let offsetLeft = ref.offsetLeft;\n const offsetWidth = ref.offsetWidth;\n let offsetTop = top;\n const offsetHeight = ref.offsetHeight;\n if (transformX != null) {\n offsetLeft += offsetWidth * (transformX / 100);\n }\n if (transformY != null) {\n offsetTop += offsetHeight * (transformY / 100);\n }\n if (x >= offsetLeft && x <= offsetLeft + offsetWidth && y >= offsetTop && y <= offsetTop + offsetHeight) {\n return controls[control].key;\n }\n }\n return null;\n }\n function touchStartControls(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return false;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const key = determineKey(e, touch);\n if (key == null) {\n continue;\n }\n const idx = touch.identifier;\n onGoingTouches.set(idx, key);\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n return false;\n }\n function touchMoveControls(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return false;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const key = determineKey(e, touch);\n const idx = touch.identifier;\n if (key == null) {\n continue;\n }\n const oldKey = onGoingTouches.get(idx);\n if (oldKey != null) {\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, oldKey);\n onGoingTouches.delete(idx);\n }\n onGoingTouches.set(idx, key);\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n return false;\n }\n function touchEndControls(e) {\n e.preventDefault();\n if (webSocket == null) {\n console.log('There is not websocket');\n return false;\n }\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const idx = touch.identifier;\n const oldKey = onGoingTouches.get(idx);\n if (oldKey == null) {\n return false;\n }\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, oldKey);\n onGoingTouches.delete(idx);\n }\n return false;\n }\n const keyMap = ['KeyZ', 'KeyX', 'KeyA', 'KeyS',\n 'Enter', 'Space', 'ArrowUp', 'ArrowDown',\n 'ArrowLeft', 'ArrowRight'];\n function onPressControl(e) {\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n const key = keyMap.findIndex((c) => c === e.code);\n if (key !== -1) {\n e.preventDefault();\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, true, key);\n }\n }\n function onUnpressControl(e) {\n if (webSocket == null) {\n console.log('There is not websocket');\n return;\n }\n const key = keyMap.findIndex((c) => c === e.code);\n if (key !== -1) {\n e.preventDefault();\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_2__.sendKeyDown)(webSocket, false, key);\n }\n }\n document.onselectstart = () => false;\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { tabIndex: -1, className: \"overlay\", onKeyDown: onPressControl, onKeyUp: onUnpressControl },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"vertical-padding\" }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"controls\", onTouchStart: touchStartControls, onTouchMove: touchMoveControls, onTouchEnd: touchEndControls },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, className: \"gear control\", onClick: showOverlayMenu, onTouchStart: showOverlayMenu },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"img\", { src: _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.HOME_BUTTON_IMAGE, alt: \"Go to menu. (House icon)\" })),\n Object.keys(controls).map((key) => react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { tabIndex: -1, className: controls[key].classes, ref: controls[key].ref, key: key, onMouseDown: (e) => { mouseDown(e, controls[key].key); }, onMouseUp: (e) => { mouseUp(e, controls[key].key); } }, controls[key].sym)))));\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/components/overlay-controls.tsx?"); /***/ }), @@ -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 _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, setEmulationStarted, setHiddenMenu, setHiddenFormSelectFiles, setWebSocket }) {\n e.preventDefault();\n if (canvas.current == null) {\n alert('Canvas does not exists?');\n return;\n }\n const ctx = canvas.current.getContext('2d');\n if (ctx == null) {\n alert('Unable to create canvas context, doing nothing');\n return;\n }\n if (inputRom == null || inputSaveState == null || inputRom.files == null || inputSaveState.files == null) {\n alert('Unable to read the files ');\n return;\n }\n if (inputRom.files.length == 0) {\n alert('There is no rom still');\n return;\n }\n if (inputSaveState.files.length == 0) {\n alert('There is no savestate still');\n return;\n }\n const 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 setWebSocket(null);\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 webSocket.addEventListener('message', (event) => {\n onWebSocketPacket(event, canvas.current, ctx);\n });\n });\n });\n}\nfunction onWebSocketPacket(event, canvas, ctx) {\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);\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 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,\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?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Page)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _msgba_components_center_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @msgba/components/center-element */ \"./js-src/components/center-element.tsx\");\n/* harmony import */ var _msgba_components_canvas_gba_emulator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @msgba/components/canvas-gba-emulator */ \"./js-src/components/canvas-gba-emulator.tsx\");\n/* harmony import */ var _msgba_components_overlay_controls__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @msgba/components/overlay-controls */ \"./js-src/components/overlay-controls.tsx\");\n/* harmony import */ var _msgba_components_overlay_menu__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @msgba/components/overlay-menu */ \"./js-src/components/overlay-menu.js\");\n/* harmony import */ var _msgba_components_overlay_select_files__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @msgba/components/overlay-select-files */ \"./js-src/components/overlay-select-files.tsx\");\n/* harmony import */ var _msgba_packet__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @msgba/packet */ \"./js-src/packet.ts\");\n/* harmony import */ var _msgba_endian__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @msgba/endian */ \"./js-src/endian.ts\");\n/* harmony import */ var _msgba_constants__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @msgba/constants */ \"./js-src/constants.ts\");\n\n\n\n\n\n\n\n\n\nfunction handleClickStartEmulationButton({ e, inputRom, inputSaveState, canvas, setEmulationStarted, setHiddenMenu, setHiddenFormSelectFiles, setWebSocket }) {\n e.preventDefault();\n if (canvas.current == null) {\n alert('Canvas does not exists?');\n return;\n }\n const ctx = canvas.current.getContext('2d');\n if (ctx == null) {\n alert('Unable to create canvas context, doing nothing');\n return;\n }\n if (inputRom == null || inputSaveState == null || inputRom.files == null || inputSaveState.files == null) {\n alert('Unable to read the files ');\n return;\n }\n if (inputRom.files.length === 0) {\n alert('There is no rom still');\n return;\n }\n if (inputSaveState.files.length === 0) {\n alert('There is no savestate still');\n return;\n }\n const romFile = inputRom.files[0];\n const savestateFile = inputSaveState.files[0];\n romFile.arrayBuffer().then((romBuffer) => {\n savestateFile.arrayBuffer().then((savestateBuffer) => {\n const romArray = new Uint8Array(romBuffer);\n const savestateArray = new Uint8Array(savestateBuffer);\n const webSocket = new WebSocket(`ws://${window.location.host}/ws`);\n setWebSocket(webSocket);\n webSocket.binaryType = 'arraybuffer';\n webSocket.onclose = (message) => {\n setEmulationStarted(false);\n console.log('Closing websocket.');\n setWebSocket(null);\n };\n webSocket.onopen = () => {\n console.log('Opened websocket.');\n setEmulationStarted(true);\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_6__.sendHello)(webSocket, romArray, savestateArray);\n setHiddenMenu(true);\n setHiddenFormSelectFiles(true);\n };\n webSocket.addEventListener('message', (event) => {\n onWebSocketPacket(event, canvas.current, ctx);\n });\n }).catch((c) => {\n console.log('Unable to convert file to array_buffer');\n });\n }).catch((c) => {\n console.log('Unable to convert file to array_buffer');\n });\n}\nfunction onWebSocketPacket(event, canvas, ctx) {\n const buffer = event.data;\n let packetU8 = new Uint8Array(buffer);\n const id = _msgba_endian__WEBPACK_IMPORTED_MODULE_7__[\"default\"].byteArrayToU64BigEndian(packetU8.slice(0, 8));\n packetU8 = packetU8.slice(8, packetU8.length);\n const size = _msgba_endian__WEBPACK_IMPORTED_MODULE_7__[\"default\"].byteArrayToU64BigEndian(packetU8.slice(0, 8));\n const rawData = packetU8.slice(8, Number(size));\n packetU8 = null;\n switch (id) {\n case _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.PACKET_ID_SEND_FRAME:\n (0,_msgba_packet__WEBPACK_IMPORTED_MODULE_6__.handleSendFrame)(rawData, canvas, ctx);\n break;\n default:\n console.log(`Received unknown packet ${id}`);\n }\n}\nfunction Page() {\n const screenDimensions = useScreenDimensions();\n const emulatorDimensions = calculateSizeEmulator(screenDimensions);\n const canvasRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n function resizeCanvas() {\n const canvas = canvasRef.current;\n if (canvas == null) {\n return;\n }\n if (emulatorDimensions.width === undefined || emulatorDimensions.height === undefined) {\n return;\n }\n canvas.width = emulatorDimensions.width;\n canvas.height = emulatorDimensions.height;\n const ctx = canvas.getContext('2d');\n if (ctx == null) {\n return;\n }\n fillBlack(canvas, ctx);\n }\n const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = react__WEBPACK_IMPORTED_MODULE_0__.useState(true);\n react__WEBPACK_IMPORTED_MODULE_0__.useEffect(resizeCanvas, [emulatorDimensions]);\n const refInputRom = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const refInputSaveState = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const [emulationStarted, setEmulationStarted] = react__WEBPACK_IMPORTED_MODULE_0__.useState(false);\n const [hiddenMenu, setHiddenMenu] = react__WEBPACK_IMPORTED_MODULE_0__.useState(true);\n const [webSocket, setWebSocket] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const onStartEmulation = (e) => {\n handleClickStartEmulationButton({\n e,\n setEmulationStarted,\n inputRom: refInputRom.current,\n inputSaveState: refInputSaveState.current,\n canvas: canvasRef,\n setHiddenMenu,\n setHiddenFormSelectFiles,\n setWebSocket\n });\n };\n const firstMenuElement = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const screenRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);\n const [isFullscreen, setIsFullscreen] = react__WEBPACK_IMPORTED_MODULE_0__.useState(false);\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { ref: screenRef },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_overlay_controls__WEBPACK_IMPORTED_MODULE_3__[\"default\"], { firstMenuElement: firstMenuElement, setHiddenMenu: setHiddenMenu, webSocket: webSocket }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_overlay_menu__WEBPACK_IMPORTED_MODULE_4__[\"default\"], { hiddenMenu: hiddenMenu, setHiddenMenu: setHiddenMenu, emulationStarted: emulationStarted, setHiddenFormSelectFiles: setHiddenFormSelectFiles, screenRef: screenRef, isFullscreen: isFullscreen, setIsFullscreen: setIsFullscreen, firstMenuElement: firstMenuElement, websocket: webSocket }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_overlay_select_files__WEBPACK_IMPORTED_MODULE_5__[\"default\"], { hiddenFormSelectFiles: hiddenFormSelectFiles, setHiddenFormSelectFiles: setHiddenFormSelectFiles, refInputRom: refInputRom, refInputSaveState: refInputSaveState, onStartEmulation: onStartEmulation }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_center_element__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { full: true },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_msgba_components_canvas_gba_emulator__WEBPACK_IMPORTED_MODULE_2__[\"default\"], { canvasRef: canvasRef })))));\n}\nfunction getScreenDimensions() {\n return {\n width: document.body.clientWidth,\n height: document.body.clientHeight\n };\n}\nfunction useScreenDimensions() {\n const [screenDimensions, setScreenDimensions] = react__WEBPACK_IMPORTED_MODULE_0__.useState(getScreenDimensions());\n react__WEBPACK_IMPORTED_MODULE_0__.useEffect(() => {\n function onResize() {\n setScreenDimensions(getScreenDimensions());\n }\n window.addEventListener('resize', onResize);\n return () => {\n window.removeEventListener('resize', onResize);\n };\n }, []);\n return screenDimensions;\n}\nfunction fillBlack(canvas, ctx) {\n ctx.beginPath();\n ctx.rect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = '#0E0E10';\n ctx.fill();\n}\n;\nfunction calculateSizeEmulator(screenDimensions) {\n if (screenDimensions.width === undefined || screenDimensions.height === undefined) {\n console.error(screenDimensions, 'screenDimensions has undefined fields');\n return {};\n }\n const width = screenDimensions.width;\n const height = screenDimensions.height;\n const emulatorDimensions = {};\n if (width < _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH || height < _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT) {\n return {\n width: _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH,\n height: _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT\n };\n }\n const ratioWidth = width / _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH;\n const ratioHeight = height / _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT;\n if (ratioWidth < ratioHeight) {\n emulatorDimensions.width = _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH * ratioWidth;\n emulatorDimensions.height = _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT * ratioWidth;\n }\n else {\n emulatorDimensions.height = _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_HEIGHT * ratioHeight;\n emulatorDimensions.width = _msgba_constants__WEBPACK_IMPORTED_MODULE_8__.MIN_WIDTH * ratioHeight;\n }\n return emulatorDimensions;\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/components/page.tsx?"); /***/ }), @@ -166,7 +166,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"CLOSE_BUTTON_IMAGE\": () => (/* binding */ CLOSE_BUTTON_IMAGE),\n/* harmony export */ \"HOME_BUTTON_IMAGE\": () => (/* binding */ HOME_BUTTON_IMAGE),\n/* harmony export */ \"MIN_HEIGHT\": () => (/* binding */ MIN_HEIGHT),\n/* harmony export */ \"MIN_WIDTH\": () => (/* binding */ MIN_WIDTH),\n/* harmony export */ \"PACKET_ID_HELLO\": () => (/* binding */ PACKET_ID_HELLO),\n/* harmony export */ \"PACKET_ID_KEY_DOWN\": () => (/* binding */ PACKET_ID_KEY_DOWN),\n/* harmony export */ \"PACKET_ID_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?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"CLOSE_BUTTON_IMAGE\": () => (/* binding */ CLOSE_BUTTON_IMAGE),\n/* harmony export */ \"HOME_BUTTON_IMAGE\": () => (/* binding */ HOME_BUTTON_IMAGE),\n/* harmony export */ \"MIN_HEIGHT\": () => (/* binding */ MIN_HEIGHT),\n/* harmony export */ \"MIN_WIDTH\": () => (/* binding */ MIN_WIDTH),\n/* harmony export */ \"PACKET_ID_HELLO\": () => (/* binding */ PACKET_ID_HELLO),\n/* harmony export */ \"PACKET_ID_KEY_DOWN\": () => (/* binding */ PACKET_ID_KEY_DOWN),\n/* harmony export */ \"PACKET_ID_SAVE_REQUEST\": () => (/* binding */ PACKET_ID_SAVE_REQUEST),\n/* harmony export */ \"PACKET_ID_SAVE_RESPONSE\": () => (/* binding */ PACKET_ID_SAVE_RESPONSE),\n/* harmony export */ \"PACKET_ID_SEND_FRAME\": () => (/* binding */ PACKET_ID_SEND_FRAME)\n/* harmony export */ });\nconst MIN_WIDTH = 240;\nconst MIN_HEIGHT = 160;\nconst PACKET_ID_HELLO = 0n;\nconst PACKET_ID_SEND_FRAME = 1n;\nconst PACKET_ID_KEY_DOWN = 2n;\nconst PACKET_ID_SAVE_REQUEST = 3n;\nconst PACKET_ID_SAVE_RESPONSE = 4n;\nconst CLOSE_BUTTON_IMAGE = '/img/close.png';\nconst HOME_BUTTON_IMAGE = '/img/home.png';\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/constants.ts?"); /***/ }), @@ -186,7 +186,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 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?"); +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 _msgba_components_page__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @msgba/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(_msgba_components_page__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null));\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/index.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 */ });\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.', isDown);\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 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) {\n console.log('Reachs here');\n if (canvas == null) {\n console.log('No canvas');\n return;\n }\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));\n}\nfunction drawBitmap(bitmap, canvas, ctx) {\n ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/packet.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"handleSendFrame\": () => (/* binding */ handleSendFrame),\n/* harmony export */ \"sendHello\": () => (/* binding */ sendHello),\n/* harmony export */ \"sendKeyDown\": () => (/* binding */ sendKeyDown),\n/* harmony export */ \"sendPacket\": () => (/* binding */ sendPacket),\n/* harmony export */ \"sendSaveRequest\": () => (/* binding */ sendSaveRequest)\n/* harmony export */ });\n/* harmony import */ var _msgba_endian__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @msgba/endian */ \"./js-src/endian.ts\");\n/* harmony import */ var _msgba_constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @msgba/constants */ \"./js-src/constants.ts\");\n\n\nfunction concatU8Array(array1, array2) {\n const finalArray = new Uint8Array(array1.length + array2.length);\n finalArray.set(array1);\n finalArray.set(array2, array1.length);\n return finalArray;\n}\nfunction sendHello(websocket, romArray, savestateArray) {\n console.log('Sending hello.');\n const lengthRom = BigInt(romArray.length);\n const lengthSavestate = BigInt(savestateArray.length);\n const rawData = concatU8Array(concatU8Array(concatU8Array(_msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(lengthRom), romArray), _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(lengthSavestate)), savestateArray);\n sendPacket(websocket, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.PACKET_ID_HELLO, rawData);\n}\nfunction sendKeyDown(websocket, isDown, key) {\n console.log('Sending keyDown.', isDown);\n const isDownArray = new Uint8Array(1);\n isDownArray[0] = isDown ? 1 : 0;\n const rawData = concatU8Array(isDownArray, _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u32ToByteArrayBigEndian(key));\n sendPacket(websocket, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.PACKET_ID_KEY_DOWN, rawData);\n}\nfunction sendSaveRequest(websocket, identifier) {\n console.log('Sendidng save request', identifier);\n const rawData = _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(identifier);\n sendPacket(websocket, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.PACKET_ID_SAVE_REQUEST, rawData);\n}\nfunction sendPacket(websocket, id, rawData) {\n const packetU8 = concatU8Array(concatU8Array(_msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(id), _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].u64ToByteArrayBigEndian(BigInt(rawData.length))), rawData);\n const packetBuffer = packetU8.buffer;\n console.log('Sending packet');\n websocket.send(packetBuffer);\n}\nfunction handleSendFrame(rawData, canvas, ctx) {\n console.log('Reachs here');\n if (canvas == null) {\n console.log('No canvas');\n return;\n }\n let data = rawData;\n data = data.slice(4, data.length);\n const outputBufferSize = _msgba_endian__WEBPACK_IMPORTED_MODULE_0__[\"default\"].byteArrayToU64BigEndian(data.slice(0, 8));\n // TODO: This number conversion is not great. Is there other option?\n data = data.slice(8, Number(outputBufferSize));\n const imgData = ctx.createImageData(_msgba_constants__WEBPACK_IMPORTED_MODULE_1__.MIN_WIDTH, _msgba_constants__WEBPACK_IMPORTED_MODULE_1__.MIN_HEIGHT);\n const imgDataU8 = new Uint8Array(imgData.data.buffer);\n for (let i = 0; i < data.length; i++) {\n if (i % 4 === 3) {\n imgDataU8[i] = 255;\n continue;\n }\n imgDataU8[i] = data[i];\n }\n data = null;\n createImageBitmap(imgData).then((bitmap) => { drawBitmap(bitmap, canvas, ctx); }).catch((c) => {\n console.log(`Unable to print to the canvas the frame because: ${c}`);\n });\n}\nfunction drawBitmap(bitmap, canvas, ctx) {\n ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);\n}\n\n\n//# sourceURL=webpack://MSGBA-Web/./js-src/packet.ts?"); /***/ }) diff --git a/tsconfig.json b/tsconfig.json index 1cb9716..d178d08 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,8 +10,21 @@ "strictNullChecks": true, "baseUrl": ".", "paths": { - "*": ["js-src/*"] - } + "@msgba/*": ["js-src/*"], + "/*": ["js-src/*"] + }, + "plugins": [ + { + "transform": "typescript-transform-paths" + }, + { + "transform": "typescript-transform-paths", + "afterDeclarations": true + } + ] }, - "include": ["js-src/*.ts", "js-src/*/*.ts" ] + "include": [ + "js-src/*.ts", "js-src/*/*.ts", + "js-src/*.tsx", "js-src/*/*.tsx" + ] } diff --git a/webpack.config.js b/webpack.config.js index 94a54bb..f5c2b69 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,35 +1,38 @@ -const path = require('path'); +const path = require('path') module.exports = { entry: './js-src/index.tsx', mode: 'development', output: { filename: 'bundle.js', - path: path.resolve(__dirname, 'public/js/'), + path: path.resolve(__dirname, 'public/js/') }, resolve: { - extensions: [ '.js', '.jsx','.ts', '.tsx' ], + extensions: ['.js', '.jsx', '.ts', '.tsx'], roots: [ path.resolve(__dirname, 'js-src/') - ] + ], + alias: { + '@msgba': path.resolve(__dirname, 'js-src') + } }, module: { rules: [ { test: /\.tsx?$/, use: 'ts-loader', - exclude: /node_modules/, + exclude: /node_modules/ }, { test: /\.jpe?g|png$/, exclude: /node_modules/, - use: ["url-loader", "file-loader"] + use: ['url-loader', 'file-loader'] }, { test: /\.(js|jsx)$/, exclude: /node_modules/, - loader: "babel-loader" + loader: 'babel-loader' } ] } -}; +}