Mirai's Miscellaneous Misadventures

M49 / engines / minifb.c

/* license: AGPLv3 or later */
/* copyright 2024 zamfofex */

#include <stdlib.h>
#include <MiniFB.h>
#include <mimimi.h>

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

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

static void mimimi_minifb_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;
	uint32_t *buffer;
	int x, y;
	int x0, y0;
	int x2, y2;
	int width, height;
	unsigned char color;
	int offset;
	unsigned char r, g, b;
	
	image = texture;
	
	buffer = data;
	
	x0 = 0;
	y0 = 0;
	
	if (x1 < 0) x0 = -x1;
	if (y1 < 0) y0 = -y1;
	
	width = image->width;
	height = image->height;
	
	if (width + x1 > 512) width = 512 - x1;
	if (height + y1 > 256) height = 256 - y1;
	
	for (y = y0 ; y < height ; y++)
	for (x = x0 ; x < width ; x++)
	{
		color = image->colors[x + y * image->width];
		if (color == 0) continue;
		
		x2 = x + x1;
		y2 = y + y1;
		
		offset = x2 + y2 * 512;
		
		if (color < 0x10)
		{
			r = color * 0x11;
			buffer[offset] = MFB_RGB(r, r, r);
		}
		else
		{
			color -= 0x10;
			r = rch[(color / 40) % 6];
			g = gch[(color / 5) % 8];
			b = bch[(color / 1) % 5];
			buffer[offset] = MFB_RGB(r, g, b);
		}
	}
}

int mimimi_minifb(void (*start)(void *chapter, struct mimimi_engine *engine), void (*tick)(void *chapter, unsigned char left, unsigned char right), unsigned long int chapter_size)
{
	struct mfb_window *window;
	uint32_t *buffer;
	struct mimimi_engine engine;
	unsigned char *keys;
	void *chapter;
	mfb_update_state state;
	
	buffer = malloc(512 * 256 * 4);
	if (buffer == NULL) return 1;
	
	chapter = malloc(chapter_size);
	if (chapter == NULL) return 1;
	
	window = mfb_open_ex("mimimi game", 512, 256, WF_RESIZABLE);
	if (window == NULL) return 1;
	
	engine.data = buffer;
	engine.texture = &mimimi_minifb_texture;
	engine.invalidate = &mimimi_minifb_invalidate;
	engine.stamp = &mimimi_minifb_stamp;
	engine.size.width = 512;
	engine.size.height = 256;
	
	(*start)(chapter, &engine);
	
	for (;;)
	{
		keys = (void *) mfb_get_key_buffer(window);
		if (keys[KB_KEY_Q] != 0) break;
		
		(*tick)(chapter, keys[KB_KEY_LEFT], keys[KB_KEY_RIGHT]);
		
		state = mfb_update_ex(window, buffer, 512, 256);
		if (state == -1) break;
		if (state < 0) return 1;
		if (mfb_wait_sync(window) == 0) return 1;
	}
	
	mfb_close(window);
	free(chapter);
	free(buffer);
	
	return 0;
}