import Endian from '@msgba/endian' import { MIN_WIDTH_GBC, MIN_WIDTH_GBA, MIN_HEIGHT_GBA, MIN_HEIGHT_GBC, PACKET_ID_HELLO, PACKET_ID_KEY_DOWN, PACKET_ID_SAVE_REQUEST } from '@msgba/constants' function concatU8Array (array1: Uint8Array, array2: Uint8Array): Uint8Array { const finalArray = new Uint8Array(array1.length + array2.length) finalArray.set(array1) finalArray.set(array2, array1.length) return finalArray } 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(lengthRom), romArray), Endian.u64ToByteArrayBigEndian(lengthSavestate) ), savestateArray ) sendPacket(websocket, PACKET_ID_HELLO, 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 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 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 handleSaveResponse (rawData: Uint8Array, canvas: HTMLCanvasElement | null, onSaveResponseLambdas: Map 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') return } const ctx = canvas.getContext('2d') if (ctx == null) { console.log('No context') return } let data: Uint8Array | null = rawData const stride = Endian.byteArrayToU32BigEndian(data.slice(0, 4)) let isGBC = false if (stride === 160) { isGBC = true setIsGBC(true) } else { setIsGBC(false) } data = data.slice(4, data.length) const outputBufferSize = Endian.byteArrayToU64BigEndian(data.slice(0, 8)) // TODO: This number conversion is not great. Is there other option? data = data.slice(8, Number(outputBufferSize)) let imgData = ctx.createImageData(MIN_WIDTH_GBA, MIN_HEIGHT_GBA) if (isGBC) { imgData = ctx.createImageData(MIN_WIDTH_GBC, MIN_HEIGHT_GBC) } const imgDataU8 = new Uint8Array(imgData.data.buffer) for (let i = 0; i < data.length; i++) { if (i % 4 === 3) { imgDataU8[i] = 255 continue } imgDataU8[i] = data[i] } 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): void { ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height) }