Mirai's Miscellaneous Misadventures

M42 / engines / wasm.c

// license: AGPLv3 or later
// copyright 2023 zamfofex

#include <stdlib.h>

#include <mimimi/assets.h>
#include <mimimi/engines.h>
#include <mimimi/behaviors.h>
#include <mimimi/geometry.h>
#include <mimimi/chapters.h>
#include <mimimi/malloc.h>
#include <mimimi/coroutine-providers.h>

// textures

static void *mimimi_wasm_texture(void *data, struct mimimi_image *image)
{
	(void) data;
	return image;
}

static void mimimi_wasm_invalidate(void *data, void *texture)
{
	(void) data;
	(void) texture;
}

static void mimimi_wasm_stamp(void *data, int x1, int y1, void *texture)
{
	static unsigned char rch[] = {0x11, 0x44, 0x77, 0x99, 0xCC, 0xFF};
	static unsigned char gch[] = {0x11, 0x33, 0x55, 0x77, 0x99, 0xBB, 0xDD, 0xFF};
	static unsigned char bch[] = {0x22, 0x55, 0x88, 0xBB, 0xEE};
	
	struct mimimi_image *image = texture;
	
	unsigned char (*buffer)[4] = data;
	
	int x0 = 0;
	int y0 = 0;
	
	if (x1 < 0) x0 = -x1;
	if (y1 < 0) y0 = -y1;
	
	int width = image->width;
	int height = image->height;
	
	if (width + x1 > 512) width = 512 - x1;
	if (height + y1 > 256) height = 256 - y1;
	
	for (int y = y0 ; y < height ; y++)
	for (int x = x0 ; x < width ; x++)
	{
		unsigned char color = image->colors[x + y * image->width];
		if (color == 0) continue;
		
		int x2 = x + x1;
		int y2 = y + y1;
		
		int offset = x2 + y2 * 512;
		
		if (color < 0x10)
		{
			unsigned char ch = color * 0x11;
			buffer[offset][0] = ch;
			buffer[offset][1] = ch;
			buffer[offset][2] = ch;
			buffer[offset][3] = 0xFF;
		}
		else
		{
			color -= 0x10;
			unsigned char r, g, b;
			r = rch[(color / 40) % 6];
			g = gch[(color / 5) % 8];
			b = bch[(color / 1) % 5];
			
			buffer[offset][0] = r;
			buffer[offset][1] = g;
			buffer[offset][2] = b;
			buffer[offset][3] = 0xFF;
		}
	}
}

// coroutines

struct mimimi_wasm_asyncify_buffer
{
	void *begin;
	void *end;
};

struct mimimi_wasm_coroutine_data
{
	unsigned int state;
	unsigned int size;
	void *stack;
	struct mimimi_wasm_asyncify_buffer buffer;
	void (*start)(struct mimimi_behavior *behavior, void *data);
	void (*finish)(void *data);
	void *data;
	struct mimimi_behavior *behavior;
	struct mimimi_behavior *coroutine;
};

void mimimi_wasm_yield(void *data);

void mimimi_wasm_continue(void *data);

void mimimi_wasm_start_coroutine(void *data)
{
	struct mimimi_wasm_coroutine_data *coroutine_data = data;
	(*coroutine_data->start)(coroutine_data->behavior, coroutine_data->data);
}

void mimimi_wasm_finish_coroutine(void *data)
{
	struct mimimi_wasm_coroutine_data *coroutine_data = data;
	
	coroutine_data->state = 2;
	
	(*coroutine_data->finish)(coroutine_data->data);
	free(coroutine_data->buffer.begin);
	free(coroutine_data->stack);
	free(coroutine_data->behavior);
}

static void mimimi_wasm_finish_coroutine2(void *data)
{
	struct mimimi_wasm_coroutine_data *coroutine_data = data;
	
	if (coroutine_data->state != 2) mimimi_wasm_finish_coroutine(coroutine_data);
	
	free(coroutine_data->coroutine);
	free(coroutine_data);
}

static struct mimimi_behavior *mimimi_wasm_create_coroutine(void (*start)(struct mimimi_behavior *behavior, void *data), void (*finish)(void *data), void *data, void *self)
{
	(void) self;
	
	unsigned char *begin = mimimi_allocate(mimimi_malloc, 8192);
	
	struct mimimi_wasm_coroutine_data *coroutine_data = mimimi_allocate(mimimi_malloc, sizeof *coroutine_data);
	coroutine_data->start = start;
	coroutine_data->finish = finish;
	coroutine_data->data = data;
	coroutine_data->stack = mimimi_allocate(mimimi_malloc, 8192);
	coroutine_data->buffer.begin = begin;
	coroutine_data->buffer.end = begin + 8192;
	coroutine_data->state = 0;
	
	struct mimimi_behavior *behavior = mimimi_allocate(mimimi_malloc, sizeof *behavior);
	behavior->behave = &mimimi_wasm_yield;
	behavior->finish = &mimimi_wasm_finish_coroutine;
	behavior->data = coroutine_data;
	coroutine_data->behavior = behavior;
	
	struct mimimi_behavior *coroutine = mimimi_allocate(mimimi_malloc, sizeof *coroutine);
	coroutine->behave = &mimimi_wasm_continue;
	coroutine->finish = &mimimi_wasm_finish_coroutine2;
	coroutine->data = coroutine_data;
	coroutine_data->coroutine = coroutine;
	
	return coroutine;
}

static struct mimimi_coroutine_provider mimimi_wasm_coroutines = {&mimimi_wasm_create_coroutine, 0};

// engine

static struct mimimi_chapter *mimimi_wasm_chapter;
static unsigned char (*mimimi_wasm_buffer)[4];

void *mimimi_wasm_behave(unsigned char left, unsigned char right)
{
	mimimi_wasm_chapter->left = left;
	mimimi_wasm_chapter->right = right;
	struct mimimi_behavior *behavior = mimimi_wasm_chapter->behavior;
	(*behavior->behave)(behavior->data);
	return mimimi_wasm_buffer;
}

void mimimi_wasm(struct mimimi_chapter *(*start)(struct mimimi_engine *engine))
{
	static struct mimimi_size size = {512, 256};
	mimimi_wasm_buffer = mimimi_allocate(mimimi_malloc, size.width * size.height * 4);
	
	static struct mimimi_engine engine;
	engine.data = mimimi_wasm_buffer;
	engine.texture = &mimimi_wasm_texture;
	engine.invalidate = &mimimi_wasm_invalidate;
	engine.stamp = &mimimi_wasm_stamp;
	engine.size = &size;
	engine.allocator = mimimi_malloc;
	engine.coroutines = &mimimi_wasm_coroutines;
	mimimi_wasm_chapter = (*start)(&engine);
}