changeset 2429:da3dc881d3f0

Initial implementation of storbook artwork display
author Michael Pavone <pavone@retrodev.com>
date Sun, 04 Feb 2024 20:11:39 -0800
parents 65c2e4d990cc
children fb8d6ebf9d5f
files genesis.c genesis.h render.h render_sdl.c
diffstat 4 files changed, 186 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/genesis.c	Sat Feb 03 18:32:41 2024 -0800
+++ b/genesis.c	Sun Feb 04 20:11:39 2024 -0800
@@ -1393,10 +1393,10 @@
 	case 6:
 		return gen->pico_page;
 	case 8:
-		printf("uPD7759 data read @ %u\n", m68k->current_cycle);
+		//printf("uPD7759 data read @ %u\n", m68k->current_cycle);
 		return 0xFF;
 	case 9:
-		printf("uPD7759 contro/status read @ %u\n", m68k->current_cycle);
+		//printf("uPD7759 contro/status read @ %u\n", m68k->current_cycle);
 		return 0;
 	default:
 		printf("Unknown Pico IO read %X @ %u\n", location, m68k->current_cycle);
@@ -1993,6 +1993,41 @@
 	}
 }
 
+static void pico_update_page(genesis_context *gen)
+{
+#ifndef IS_LIB
+	uint8_t page_num = 0;
+	uint8_t page = gen->pico_page;
+	while (page)
+	{
+		page_num++;
+		page >>= 1;
+	}
+	render_clear_window(gen->pico_story_window, 236, 205, 27);
+	int x, width;
+	if (page_num) {
+		x = 0;
+		width = 640;
+	} else {
+		render_fill_rect(gen->pico_story_window, 50, 29, 96, 0, 0, 320, 320);
+		x = 320;
+		width = 320;
+	}
+	if (gen->pico_story_pages[page_num] == 0xFF && page_num == 6 && gen->pico_story_pages[page_num - 1]) {
+		//repeat last page if storybook doesn't have a full 6 pages
+		//hack to deal with storybooks that skip page 5
+		page_num--;
+	}
+	if (gen->pico_story_pages[page_num] != 0xFF) {
+		render_draw_image(gen->pico_story_window, gen->pico_story_pages[page_num], x, 0, width, 320);
+	} else {
+		uint8_t grey = page * (255 / 5);
+		render_fill_rect(gen->pico_story_window, grey, grey, grey, x, 0, width, 320);
+	}
+	render_window_refresh(gen->pico_story_window);
+#endif
+}
+
 static void gamepad_down_pico(system_header *system, uint8_t gamepad_num, uint8_t button)
 {
 	genesis_context *gen = (genesis_context *)system;
@@ -2004,8 +2039,10 @@
 		gen->pico_page <<= 1;
 		gen->pico_page |= 1;
 		gen->pico_page &= 0x3F;
+		pico_update_page(gen);
 	} else if (button == BUTTON_Z) {
 		gen->pico_page >>= 1;
+		pico_update_page(gen);
 	} else if (button < BUTTON_B) {
 		gen->pico_button_state &= ~(1 << (button - 1));
 	}
@@ -2066,10 +2103,17 @@
 static void mouse_motion_absolute_pico(system_header *system, uint8_t mouse_num, uint16_t x, uint16_t y)
 {
 	genesis_context *gen = (genesis_context *)system;
-	//TODO: scale properly
+	//FIXME: coordinate translation feels a little off for storybook area
 	//TODO: limit to mouse motion on emulated storyware/drawing area
-	gen->pico_pen_x = (x >> 1) + 0x3C;
-	gen->pico_pen_y = y + 0x1FC;
+	if (y < 24) {
+		y = 24;
+	}
+	gen->pico_pen_x = x * (0x17D - 0x3C) / 640 + 0x3C;
+	if (y < 320) {
+		gen->pico_pen_y = (y-24) * (0x3F4-0x2F8) / (320-24) + 0x2F8;
+	} else {
+		gen->pico_pen_y = (y - 320) * (0x2f8-0x1FC) / 320 + 0x1FC;
+	}
 }
 
 static void mouse_motion_relative_pico(system_header *system, uint8_t mouse_num, int32_t x, int32_t y)
@@ -2959,5 +3003,48 @@
 
 	set_audio_config(gen);
 	bindings_set_mouse_mode(MOUSE_ABSOLUTE);
+	memset(gen->pico_story_pages, 0xFF, sizeof(gen->pico_story_pages));
+#ifndef IS_LIB
+	gen->pico_story_window = render_create_window("Pico Storybook & Pad", 640, 640, NULL);
+	const char *parts[] = {current_media()->dir, PATH_SEP, current_media()->name, ".manifest"};
+	char *manifest_path = alloc_concat_m(sizeof(parts)/sizeof(*parts), parts);
+	tern_node *manifest = parse_config_file(manifest_path);
+	if (!manifest) {
+		printf("Manifest not found at %s\n", manifest_path);
+	}
+	if (manifest) {
+		tern_node *pages = tern_find_node(manifest, "pages");
+		if (pages) {
+			char numkey[13];
+			for (int i = 0; i < 7; i++) {
+				sprintf(numkey, "%d", i);
+				char *page_path = tern_find_ptr(pages, numkey);
+				if (page_path) {
+					printf("page %d: %s\n", i, page_path);
+				} else {
+					continue;
+				}
+				char *img_path;
+				if (is_absolute_path(current_media()->dir)) {
+					const char *img_parts[] = {current_media()->dir, PATH_SEP, page_path};
+					img_path = alloc_concat_m(sizeof(img_parts)/sizeof(*img_parts), img_parts);
+				} else {
+					const char *img_parts[] = {path_current_dir(), PATH_SEP, current_media()->dir, PATH_SEP, page_path};
+					img_path = alloc_concat_m(sizeof(img_parts)/sizeof(*img_parts), img_parts);
+				}
+				gen->pico_story_pages[i] = render_static_image(gen->pico_story_window, img_path);
+				if (gen->pico_story_pages[i] == 0xFF) {
+					printf("Failed to load page %d from %s\n", i, img_path);
+				}
+				free(img_path);
+			}
+		} else {
+			printf("No pages key in %s\n", manifest_path);
+		}
+		tern_free(manifest);
+	}
+	free(manifest_path);
+	pico_update_page(gen);
+#endif
 	return gen;
 }
--- a/genesis.h	Sat Feb 03 18:32:41 2024 -0800
+++ b/genesis.h	Sun Feb 04 20:11:39 2024 -0800
@@ -81,6 +81,8 @@
 	uint8_t         tmss;
 	uint8_t         vdp_unlocked;
 	uint8_t         enter_z80_debugger;
+	uint8_t         pico_story_window;
+	uint8_t         pico_story_pages[7];
 	eeprom_state    eeprom;
 	nor_state       nor;
 };
--- a/render.h	Sat Feb 03 18:32:41 2024 -0800
+++ b/render.h	Sun Feb 04 20:11:39 2024 -0800
@@ -151,6 +151,11 @@
 int render_ui_to_pixels_y(int ui);
 #ifndef IS_LIB
 uint8_t render_create_thread(render_thread *thread, const char *name, render_thread_fun fun, void *data);
+uint8_t render_static_image(uint8_t window, char *path);
+void render_draw_image(uint8_t window, uint8_t image, int x, int y, int width, int height);
+void render_clear_window(uint8_t window, uint8_t r, uint8_t g, uint8_t b);
+void render_fill_rect(uint8_t window, uint8_t r, uint8_t g, uint8_t b, int x, int y, int width, int height);
+void render_window_refresh(uint8_t window);
 #endif
 
 #endif //RENDER_H_
--- a/render_sdl.c	Sat Feb 03 18:32:41 2024 -0800
+++ b/render_sdl.c	Sun Feb 04 20:11:39 2024 -0800
@@ -1528,6 +1528,93 @@
 	extra_windows[win_idx] = NULL;
 }
 
+SDL_Texture **static_images;
+uint8_t num_static;
+uint8_t render_static_image(uint8_t window, char *path)
+{
+	uint32_t fsize;
+	uint8_t *buffer;
+	if (is_absolute_path(path)) {
+		FILE *f = fopen(path, "rb");
+		if (!f) {
+			return 0xFF;
+		}
+		fsize = file_size(f);
+		buffer = calloc(1, fsize);
+		if (fread(buffer, 1, fsize, f) != fsize) {
+			free(buffer);
+			fclose(f);
+			return 0xFF;
+		}
+		fclose(f);
+	} else {
+		buffer = (uint8_t *)read_bundled_file(path, &fsize);
+	}
+	if (!buffer) {
+		return 0xFF;
+	}
+	
+	uint32_t width, height;
+	uint32_t *pixels = load_png(buffer, fsize, &width, &height);
+	free(buffer);
+	if (!pixels) {
+		return 0xFF;
+	}
+	uint8_t img_index = 0;
+	if (!num_static) {
+		num_static = 8;
+		static_images = calloc(num_static, sizeof(SDL_Texture*));
+	}
+	for (; img_index < num_static; img_index++)
+	{
+		if (!static_images[img_index]) {
+			break;
+		}
+	}
+	if (img_index == num_static) {
+		num_static *= 2;
+		static_images = realloc(static_images, num_static * sizeof(SDL_Texture*));
+	}
+	static_images[img_index] = SDL_CreateTexture(extra_renderers[window - FRAMEBUFFER_USER_START], SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, width, height);
+	SDL_UpdateTexture(static_images[img_index], NULL, pixels, sizeof(uint32_t) * width);
+	free(pixels);
+	return img_index;
+}
+
+void render_draw_image(uint8_t window, uint8_t image, int x, int y, int width, int height)
+{
+	SDL_Rect dst = {
+		.x = x,
+		.y = y,
+		.w = width,
+		.h = height
+	};
+	SDL_RenderCopy(extra_renderers[window - FRAMEBUFFER_USER_START], static_images[image], NULL, &dst);
+}
+
+void render_clear_window(uint8_t window, uint8_t r, uint8_t g, uint8_t b)
+{
+	SDL_SetRenderDrawColor(extra_renderers[window - FRAMEBUFFER_USER_START], r, g, b, 255);
+	SDL_RenderClear(extra_renderers[window - FRAMEBUFFER_USER_START]);
+}
+
+void render_fill_rect(uint8_t window, uint8_t r, uint8_t g, uint8_t b, int x, int y, int width, int height)
+{
+	SDL_SetRenderDrawColor(extra_renderers[window - FRAMEBUFFER_USER_START], r, g, b, 255);
+	SDL_Rect dst = {
+		.x = x,
+		.y = y,
+		.w = width,
+		.h = height
+	};
+	SDL_RenderFillRect(extra_renderers[window - FRAMEBUFFER_USER_START], &dst);
+}
+
+void render_window_refresh(uint8_t window)
+{
+	SDL_RenderPresent(extra_renderers[window - FRAMEBUFFER_USER_START]);
+}
+
 uint32_t *locked_pixels;
 uint32_t locked_pitch;
 uint32_t *render_get_framebuffer(uint8_t which, int *pitch)