113 lines
4.3 KiB
TypeScript
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)
|
|
}
|