Mirai's Miscellaneous Misadventures

M21 / index.html

<!-- copyright 2022 zamfofex -->
<!-- license: AGPLv3 or later -->

<!doctype html>
<html lang="en">
<meta charset="utf-8">

<title>Mirai’s Miscellaneous Misadventures</title>

<style>

html, body
{
	width: 100%;
	height: 100%;
	display: flex;
}

body
{
	margin: 0;
	background: #000;
	touch-action: manipulation;
}

canvas
{
	width: 100%;
	height: 100%;
	image-rendering: crisp-edges;
	image-rendering: pixelated;
	object-fit: contain;
}

</style>

<script type="module">

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, asset) =>
	{
		let image = toImage(asset)
		if (!image) return
		context.drawImage(image, x, y)
	}
	
	let memcpy = (a, b, n) =>
	{
		new Uint8Array(memory.buffer, a, n).set(new Uint8Array(memory.buffer, b, n))
		return a
	}
	
	let memmove = (a, b, n) =>
	{
		new Uint8Array(memory.buffer, a, n).set(new Uint8Array(memory.buffer, b, n).slice())
		return a
	}
	
	let memset = (a, b, n) =>
	{
		new Uint8Array(memory.buffer, a, n).set(Array(n).fill(b))
		return a
	}
	
	let memcmp = (a, b, n) =>
	{
		a = new Uint8Array(memory.buffer, a, n)
		b = new Uint8Array(memory.buffer, b, n)
		for (let i = 0 ; i < n ; i++)
			if (a[i] !== b[i]) return a[i] - b[i]
		return 0
	}
	
	let imports = {env: {mimimi_wasm_javascript_stamp: stamp, memcpy, memmove, memset, memcmp}}
	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, __indirect_function_table: table} = exports
	let {mimimi_wasm_allocator} = exports
	let {mimimi_wasm_allocate} = exports
	let {mimimi_wasm_stamp} = exports
	let {mimimi_test} = exports
	
	let getView = () => new DataView(memory.buffer)
	let allocator = getView().getUint32(global(mimimi_wasm_allocator), true)
	
	let chapter = mimimi_test(allocator)
	let behavior = getView().getUint32(chapter, true)
	let engine = getView().getUint32(chapter + 4, true)
	let keys = chapter + 8
	
	let size = mimimi_wasm_allocate(8)
	getView().setUint32(size + 0, canvas.width, true)
	getView().setUint32(size + 4, canvas.height, true)
	
	let f = table.grow(1)
	table.set(f, mimimi_wasm_stamp)
	
	getView().setUint32(engine + 4, f, true)
	getView().setUint32(engine + 8, size, true)
	
	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, button}) =>
	{
		if (button !== 0) return
		
		if (x < innerWidth / 2)
			tap = tap | 1
		else
			tap = tap | 2
	})
	addEventListener("pointerup", () => tap = 0)
	
	let cache = new Map()
	
	let rch = [0x11, 0x44, 0x77, 0x99, 0xCC, 0xFF]
	let gch = [0x11, 0x33, 0x55, 0x77, 0x99, 0xBB, 0xDD, 0xFF]
	let bch = [0x22, 0x55, 0x88, 0xBB, 0xEE]
	let toImage = asset =>
	{
		let view = getView()
		
		let cached = cache.get(asset)
		if (cached !== undefined) return cached
		
		let width = view.getUint32(asset, true)
		let height = view.getUint32(asset + 4, true)
		let colors = view.getUint32(asset + 8, true)
		
		if (width === 0 || height === 0)
		{
			cache.set(asset, null)
			return null
		}
		
		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) continue
			
			if (color < 0x10)
			{
				let ch = color * 0x11
				data[o * 4 + 0] = ch
				data[o * 4 + 1] = ch
				data[o * 4 + 2] = ch
				data[o * 4 + 3] = 0xFF
			}
			else
			{
				color -= 0x10
				let r = rch[Math.floor(color / 40) % 6]
				let g = gch[Math.floor(color / 5) % 8]
				let b = bch[Math.floor(color / 1) % 5]
				
				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)
		
		cache.set(asset, canvas)
		return canvas
	}
	
	let past = performance.now()
	let step = () =>
	{
		let view = getView()
		
		view.setUint32(keys, down|tap, true)
		table.get(view.getUint32(behavior, true))(view.getUint32(behavior + 8, true))
		
		let now = performance.now()
		setTimeout(step, (100/3) + past - now)
		past = now
	}
	step()
}

main()

</script>

<canvas width="512" height="256"></canvas>