Mirai's Miscellaneous Misadventures

M29 / 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 Test</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 memcpy = (a, b, n) =>
51	{
52		new Uint8Array(memory.buffer, a, n).set(new Uint8Array(memory.buffer, b, n))
53		return a
54	}
55	
56	let memmove = (a, b, n) =>
57	{
58		new Uint8Array(memory.buffer, a, n).set(new Uint8Array(memory.buffer, b, n).slice())
59		return a
60	}
61	
62	let memset = (a, b, n) =>
63	{
64		new Uint8Array(memory.buffer, a, n).set(Array(n).fill(b))
65		return a
66	}
67	
68	let memcmp = (a, b, n) =>
69	{
70		a = new Uint8Array(memory.buffer, a, n)
71		b = new Uint8Array(memory.buffer, b, n)
72		for (let i = 0 ; i < n ; i++)
73			if (a[i] !== b[i]) return a[i] - b[i]
74		return 0
75	}
76	
77	let cache = new Map()
78	
79	let rch = [0x11, 0x44, 0x77, 0x99, 0xCC, 0xFF]
80	let gch = [0x11, 0x33, 0x55, 0x77, 0x99, 0xBB, 0xDD, 0xFF]
81	let bch = [0x22, 0x55, 0x88, 0xBB, 0xEE]
82	let texture = (data0, asset) =>
83	{
84		let view = getView()
85		
86		let width = view.getUint32(asset, true)
87		let height = view.getUint32(asset + 4, true)
88		let colors = view.getUint32(asset + 8, true)
89		
90		if (width === 0 || height === 0)
91		{
92			cache.set(asset, null)
93			return null
94		}
95		
96		let image = new ImageData(width, height)
97		let {data} = image
98		
99		for (let x = 0 ; x < width ; x++)
100		for (let y = 0 ; y < height ; y++)
101		{
102			let o = x + y * width
103			let color = view.getUint8(colors + o)
104			
105			if (color === 0) continue
106			
107			if (color < 0x10)
108			{
109				let ch = color * 0x11
110				data[o * 4 + 0] = ch
111				data[o * 4 + 1] = ch
112				data[o * 4 + 2] = ch
113				data[o * 4 + 3] = 0xFF
114			}
115			else
116			{
117				color -= 0x10
118				let r = rch[Math.floor(color / 40) % 6]
119				let g = gch[Math.floor(color / 5) % 8]
120				let b = bch[Math.floor(color / 1) % 5]
121				
122				data[o * 4 + 0] = r
123				data[o * 4 + 1] = g
124				data[o * 4 + 2] = b
125				data[o * 4 + 3] = 0xFF
126			}
127		}
128		
129		let canvas = document.createElement("canvas")
130		let ctx = canvas.getContext("2d")
131		canvas.width = image.width
132		canvas.height = image.height
133		ctx.putImageData(image, 0, 0)
134		
135		cache.set(asset, canvas)
136		return asset
137	}
138	
139	let stamp = (data, x, y, asset) => context.drawImage(cache.get(asset), x, y)
140	
141	let invalidate = (data, asset) => cache.delete(asset)
142	
143	let imports =
144	{
145		env:
146		{
147		 	memcpy, memmove, memset, memcmp,
148			mimimi_wasm_javascript_texture: texture,
149			mimimi_wasm_javascript_invalidate: invalidate,
150			mimimi_wasm_javascript_stamp: stamp,
151		}
152	}
153	let module
154	try { module = await WebAssembly.instantiateStreaming(fetch("mimimi.wasm"), imports) }
155	catch (e) { module = await WebAssembly.instantiate(await (await fetch("mimimi.wasm")).arrayBuffer(), imports) }
156	
157	let {instance: {exports}} = module
158	
159	let {memory, __indirect_function_table: table} = exports
160	let {mimimi_wasm_allocator} = exports
161	let {mimimi_wasm_allocate} = exports
162	let {mimimi_wasm_texture, mimimi_wasm_invalidate, mimimi_wasm_stamp} = exports
163	let {mimimi_test} = exports
164	
165	let getView = () => new DataView(memory.buffer)
166	
167	let f = table.grow(3)
168	table.set(f + 0, mimimi_wasm_texture)
169	table.set(f + 1, mimimi_wasm_invalidate)
170	table.set(f + 2, mimimi_wasm_stamp)
171	
172	let size = mimimi_wasm_allocate(0, 8)
173	getView().setUint32(size + 0, canvas.width, true)
174	getView().setUint32(size + 4, canvas.height, true)
175	
176	let allocator = getView().getUint32(global(mimimi_wasm_allocator), true)
177	
178	let engine = mimimi_wasm_allocate(0, 16)
179	getView().setUint32(engine + 4, f + 0, true)
180	getView().setUint32(engine + 8, f + 1, true)
181	getView().setUint32(engine + 12, f + 2, true)
182	getView().setUint32(engine + 16, size, true)
183	getView().setUint32(engine + 20, allocator, true)
184	
185	let chapter = mimimi_test(engine)
186	let behavior = getView().getUint32(chapter, true)
187	let keys = chapter + 4
188	
189	let inputs = {ArrowLeft: 1, ArrowRight: 2, KeyA: 1, KeyD: 2}
190	let down = 0
191	
192	addEventListener("keydown", ({code}) =>
193	{
194		let v = inputs[code]
195		if (v) down = down | v
196	})
197	
198	addEventListener("keyup", ({code}) =>
199	{
200		let v = inputs[code]
201		if (v) down = down & ~v
202		down %= 0x100
203	})
204	
205	let tap = 0
206	addEventListener("pointerdown", ({x, y, button}) =>
207	{
208		if (button !== 0) return
209		
210		if (x < innerWidth / 2)
211			tap = tap | 1
212		else
213			tap = tap | 2
214	})
215	addEventListener("pointerup", () => tap = 0)
216	
217	let past = performance.now()
218	let step = () =>
219	{
220		let view = getView()
221		
222		view.setUint8(keys, down|tap)
223		table.get(view.getUint32(behavior, true))(view.getUint32(behavior + 8, true))
224		
225		let now = performance.now()
226		setTimeout(step, (100/3) + past - now)
227		past = now
228	}
229	step()
230}
231
232main()
233
234</script>
235
236<canvas width="512" height="256"></canvas>