Mirai's Miscellaneous Misadventures

M9 / main.js

let canvas = document.querySelector("canvas")
let context = canvas.getContext("2d")

let global = v =>
{
	if (typeof v === "number") return v
	return v.value
}

let main = async () =>
{
	let stamp = (data, x, y, image) =>
	{
		let view = getView()
		let width = view.getUint32(image, true)
		let height = view.getUint32(image + 4, true)
		
		let i = (image - assets) / 12
		
		context.drawImage(images[i], x - width / 2, y - height)
	}
	
	let imports = {env: {mirai_wasm_javascript_stamp: stamp}}
	let module
	try {module = await WebAssembly.instantiateStreaming(fetch("mimimi.wasm"), imports) }
	catch (e) { module = await WebAssembly.instantiate(await (await fetch("mimimi.wasm")).arrayBuffer(), imports) }
	
	let {instance: {exports}} = module
	
	let {memory, __heap_base: base, __indirect_function_table: table} = exports
	let {mirai_game_size, mirai_width, mirai_height} = exports
	let {mirai_start, mirai_step, mirai_assets} = exports
	let {mirai_wasm_stamp} = exports
	
	let getView = () => new DataView(memory.buffer)
	
	let view = getView()
	
	let offset = global(base)
	let size = view.getUint32(global(mirai_game_size), true)
	let width = view.getUint32(global(mirai_width), true)
	let height = view.getUint32(global(mirai_height), true)
	canvas.width = width
	canvas.height = height
	
	let alloc = size =>
	{
		let view = getView()
		let available = view.byteLength - offset
		
		if (available < size)
			memory.grow(Math.ceil((size - available) / 1024))
		
		let result = offset
		offset += size
		return result
	}
	
	let game = alloc(size)
	let engine = alloc(8)
	let keys = alloc(1)
	
	let f = table.grow(1)
	getView().setUint32(engine + 4, f, true)
	table.set(f, mirai_wasm_stamp)
	
	let inputs = {ArrowLeft: 1, ArrowRight: 2}
	let down = 0
	
	addEventListener("keydown", ({code}) =>
	{
		let v = inputs[code]
		if (v) down = down | v
	})
	
	addEventListener("keyup", ({code}) =>
	{
		let v = inputs[code]
		if (v) down = down & ~v
		down %= 0x100
	})
	
	let tap = 0
	addEventListener("pointerdown", ({x, y}) =>
	{
		if (x < innerWidth / 2)
			tap = tap | 1
		else
			tap = tap | 2
	})
	addEventListener("pointerup", () => tap = 0)
	
	let images = []
	let assets = mirai_assets()
	let asset = assets
	
	view = getView()
	let xterm = [0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF]
	while (true)
	{
		let width = view.getUint32(asset, true)
		let height = view.getUint32(asset + 4, true)
		let colors = view.getUint32(asset + 8, true)
		
		if (width === 0) break
		asset += 12
		
		let image = new ImageData(width, height)
		let {data} = image
		
		for (let x = 0 ; x < width ; x++)
		for (let y = 0 ; y < height ; y++)
		{
			let o = x + y * width
			let color = view.getUint8(colors + o)
			
			if (color !== 0)
			{
				color -= 16
				let r = xterm[Math.floor(color / 36) % 6]
				let g = xterm[Math.floor(color / 6) % 6]
				let b = xterm[color % 6]
				
				data[o * 4 + 0] = r
				data[o * 4 + 1] = g
				data[o * 4 + 2] = b
				data[o * 4 + 3] = 0xFF
			}
		}
		
		let canvas = document.createElement("canvas")
		let ctx = canvas.getContext("2d")
		canvas.width = image.width
		canvas.height = image.height
		ctx.putImageData(image, 0, 0)
		images.push(canvas)
	}
	
	mirai_start(game, engine)
	
	view = getView()
	let past = performance.now()
	context.fillStyle = "#EEE"
	let step = () =>
	{
		context.beginPath()
		context.rect(0, 0, width, height)
		context.fill()
		view.setUint32(keys, down|tap, true)
		mirai_step(game, keys)
		
		let now = performance.now()
		setTimeout(step, (100/3) + past - now)
		past = now
	}
	step()
}

main()