Mirai's Miscellaneous Misadventures

M22 / index.html

1<!-- copyright 2022 zamfofex -->
2<!-- license: AGPLv3 or later -->
3
4<!doctype html>
5<html lang="en">
6<meta charset="utf-8">
7
8<title>Mirai’s Miscellaneous Misadventures</title>
9
10<style>
11
12html, body
13{
14	width: 100%;
15	height: 100%;
16	display: flex;
17}
18
19body
20{
21	margin: 0;
22	background: #000;
23	touch-action: manipulation;
24}
25
26canvas
27{
28	width: 100%;
29	height: 100%;
30	image-rendering: crisp-edges;
31	image-rendering: pixelated;
32	object-fit: contain;
33}
34
35</style>
36
37<script type="module">
38
39let canvas = document.querySelector("canvas")
40let context = canvas.getContext("2d")
41
42let global = v =>
43{
44	if (typeof v === "number") return v
45	return v.value
46}
47
48let main = async () =>
49{
50	let stamp = (data, x, y, asset) =>
51	{
52		let image = toImage(asset)
53		if (!image) return
54		context.drawImage(image, x, y)
55	}
56	
57	let memcpy = (a, b, n) =>
58	{
59		new Uint8Array(memory.buffer, a, n).set(new Uint8Array(memory.buffer, b, n))
60		return a
61	}
62	
63	let memmove = (a, b, n) =>
64	{
65		new Uint8Array(memory.buffer, a, n).set(new Uint8Array(memory.buffer, b, n).slice())
66		return a
67	}
68	
69	let memset = (a, b, n) =>
70	{
71		new Uint8Array(memory.buffer, a, n).set(Array(n).fill(b))
72		return a
73	}
74	
75	let memcmp = (a, b, n) =>
76	{
77		a = new Uint8Array(memory.buffer, a, n)
78		b = new Uint8Array(memory.buffer, b, n)
79		for (let i = 0 ; i < n ; i++)
80			if (a[i] !== b[i]) return a[i] - b[i]
81		return 0
82	}
83	
84	let imports = {env: {mimimi_wasm_javascript_stamp: stamp, memcpy, memmove, memset, memcmp}}
85	let module
86	try { module = await WebAssembly.instantiateStreaming(fetch("mimimi.wasm"), imports) }
87	catch (e) { module = await WebAssembly.instantiate(await (await fetch("mimimi.wasm")).arrayBuffer(), imports) }
88	
89	let {instance: {exports}} = module
90	
91	let {memory, __indirect_function_table: table} = exports
92	let {mimimi_wasm_allocator} = exports
93	let {mimimi_wasm_allocate} = exports
94	let {mimimi_wasm_stamp} = exports
95	let {mimimi_test} = exports
96	
97	let getView = () => new DataView(memory.buffer)
98	let allocator = getView().getUint32(global(mimimi_wasm_allocator), true)
99	
100	let chapter = mimimi_test(allocator)
101	let behavior = getView().getUint32(chapter, true)
102	let engine = getView().getUint32(chapter + 4, true)
103	let keys = chapter + 8
104	
105	let size = mimimi_wasm_allocate(8)
106	getView().setUint32(size + 0, canvas.width, true)
107	getView().setUint32(size + 4, canvas.height, true)
108	
109	let f = table.grow(1)
110	table.set(f, mimimi_wasm_stamp)
111	
112	getView().setUint32(engine + 4, f, true)
113	getView().setUint32(engine + 8, size, true)
114	
115	let inputs = {ArrowLeft: 1, ArrowRight: 2}
116	let down = 0
117	
118	addEventListener("keydown", ({code}) =>
119	{
120		let v = inputs[code]
121		if (v) down = down | v
122	})
123	
124	addEventListener("keyup", ({code}) =>
125	{
126		let v = inputs[code]
127		if (v) down = down & ~v
128		down %= 0x100
129	})
130	
131	let tap = 0
132	addEventListener("pointerdown", ({x, y, button}) =>
133	{
134		if (button !== 0) return
135		
136		if (x < innerWidth / 2)
137			tap = tap | 1
138		else
139			tap = tap | 2
140	})
141	addEventListener("pointerup", () => tap = 0)
142	
143	let cache = new Map()
144	
145	let rch = [0x11, 0x44, 0x77, 0x99, 0xCC, 0xFF]
146	let gch = [0x11, 0x33, 0x55, 0x77, 0x99, 0xBB, 0xDD, 0xFF]
147	let bch = [0x22, 0x55, 0x88, 0xBB, 0xEE]
148	let toImage = asset =>
149	{
150		let view = getView()
151		
152		let cached = cache.get(asset)
153		if (cached !== undefined) return cached
154		
155		let width = view.getUint32(asset, true)
156		let height = view.getUint32(asset + 4, true)
157		let colors = view.getUint32(asset + 8, true)
158		
159		if (width === 0 || height === 0)
160		{
161			cache.set(asset, null)
162			return null
163		}
164		
165		let image = new ImageData(width, height)
166		let {data} = image
167		
168		for (let x = 0 ; x < width ; x++)
169		for (let y = 0 ; y < height ; y++)
170		{
171			let o = x + y * width
172			let color = view.getUint8(colors + o)
173			
174			if (color === 0) continue
175			
176			if (color < 0x10)
177			{
178				let ch = color * 0x11
179				data[o * 4 + 0] = ch
180				data[o * 4 + 1] = ch
181				data[o * 4 + 2] = ch
182				data[o * 4 + 3] = 0xFF
183			}
184			else
185			{
186				color -= 0x10
187				let r = rch[Math.floor(color / 40) % 6]
188				let g = gch[Math.floor(color / 5) % 8]
189				let b = bch[Math.floor(color / 1) % 5]
190				
191				data[o * 4 + 0] = r
192				data[o * 4 + 1] = g
193				data[o * 4 + 2] = b
194				data[o * 4 + 3] = 0xFF
195			}
196		}
197		
198		let canvas = document.createElement("canvas")
199		let ctx = canvas.getContext("2d")
200		canvas.width = image.width
201		canvas.height = image.height
202		ctx.putImageData(image, 0, 0)
203		
204		cache.set(asset, canvas)
205		return canvas
206	}
207	
208	let past = performance.now()
209	let step = () =>
210	{
211		let view = getView()
212		
213		view.setUint8(keys, down|tap)
214		table.get(view.getUint32(behavior, true))(view.getUint32(behavior + 8, true))
215		
216		let now = performance.now()
217		setTimeout(step, (100/3) + past - now)
218		past = now
219	}
220	step()
221}
222
223main()
224
225</script>
226
227<canvas width="512" height="256"></canvas>