msgba-web/js-src/packet.ts

113 lines
4.3 KiB
TypeScript

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<bigint, (saveFile: Uint8Array) => 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)
}