changeset 1263:5f65a16c23ff

Implement raw screenshot functionality requested in ticket:10
author Michael Pavone <pavone@retrodev.com>
date Sat, 04 Mar 2017 11:50:14 -0800
parents 462d9770d467
children 555f2ac537e7
files Makefile default.cfg io.c ppm.c ppm.h render.h render_sdl.c
diffstat 7 files changed, 99 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Fri Mar 03 23:51:29 2017 -0800
+++ b/Makefile	Sat Mar 04 11:50:14 2017 -0800
@@ -128,7 +128,7 @@
 AUDIOOBJS=ym2612.o psg.o wave.o
 CONFIGOBJS=config.o tern.o util.o
 
-MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o render_sdl.o io.o romdb.o menu.o xband.o realtec.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
+MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o render_sdl.o ppm.o io.o romdb.o menu.o xband.o realtec.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
 
 ifeq ($(CPU),x86_64)
 CFLAGS+=-DX86_64 -m64
--- a/default.cfg	Fri Mar 03 23:51:29 2017 -0800
+++ b/default.cfg	Sat Mar 04 11:50:14 2017 -0800
@@ -18,6 +18,7 @@
 		[ ui.vdp_debug_mode
 		] ui.vdp_debug_pal
 		u ui.enter_debugger
+		p ui.screenshot
 		esc ui.exit
 		` ui.save_state
 		0 ui.set_speed.0
@@ -187,6 +188,10 @@
 
 ui {
 	rom menu.bin
+	#initial_path defaults to your home/user profile directory if not specified
+	#screenshot_path behaves the same way
+	#see strftime for the format specifiers valid in screenshot_template
+	screenshot_template blastem_%c.ppm
 }
 
 system {
--- a/io.c	Fri Mar 03 23:51:29 2017 -0800
+++ b/io.c	Sat Mar 04 11:50:14 2017 -0800
@@ -74,6 +74,7 @@
 	UI_TOGGLE_KEYBOARD_CAPTURE,
 	UI_TOGGLE_FULLSCREEN,
 	UI_SOFT_RESET,
+	UI_SCREENSHOT,
 	UI_EXIT
 } ui_action;
 
@@ -402,6 +403,10 @@
 	return is_keyboard(io->ports) || is_keyboard(io->ports+1) || is_keyboard(io->ports+2);
 }
 
+#ifdef _WIN32
+#define localtime_r(a,b) localtime(a)
+#endif
+
 void handle_binding_up(keybinding * binding)
 {
 	switch(binding->bind_type)
@@ -491,6 +496,24 @@
 		case UI_SOFT_RESET:
 			current_system->soft_reset(current_system);
 			break;
+		case UI_SCREENSHOT: {
+			char *screenshot_base = tern_find_path(config, "ui\0screenshot_path\0").ptrval;
+			if (!screenshot_base) {
+				screenshot_base = get_home_dir();
+			}
+			time_t now = time(NULL);
+			struct tm local_store;
+			char fname_part[256];
+			char *template = tern_find_path(config, "ui\0screenshot_template\0").ptrval;
+			if (!template) {
+				template = "blastem_%c.ppm";
+			}
+			strftime(fname_part, sizeof(fname_part), template, localtime_r(&now, &local_store));
+			char const *parts[] = {screenshot_base, PATH_SEP, fname_part};
+			char *path = alloc_concat_m(3, parts);
+			render_save_screenshot(path);
+			break;
+		}
 		case UI_EXIT:
 			current_system->request_exit(current_system);
 			break;
@@ -665,6 +688,8 @@
 			*ui_out = UI_TOGGLE_FULLSCREEN;
 		} else if (!strcmp(target + 3, "soft_reset")) {
 			*ui_out = UI_SOFT_RESET;
+		} else if (!strcmp(target + 3, "screenshot")) {
+			*ui_out = UI_SCREENSHOT;
 		} else if(!strcmp(target + 3, "exit")) {
 			*ui_out = UI_EXIT;
 		} else {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ppm.c	Sat Mar 04 11:50:14 2017 -0800
@@ -0,0 +1,21 @@
+#include <stdint.h>
+#include <stdio.h>
+
+void save_ppm(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
+{
+	fprintf(f, "P6\n%d %d\n255\n", width, height);
+	for(uint32_t y = 0; y < height; y++)
+	{
+		uint32_t *line = buffer;
+		for (uint32_t x = 0; x < width; x++, line++)
+		{
+			uint8_t buf[3] = {
+				*line >> 16, //red
+				*line >> 8,  //green
+				*line        //blue
+			};
+			fwrite(buf, 1, sizeof(buf), f);
+		}
+		buffer = buffer + pitch / sizeof(uint32_t);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ppm.h	Sat Mar 04 11:50:14 2017 -0800
@@ -0,0 +1,6 @@
+#ifndef PPM_H_
+#define PPM_H_
+
+void save_ppm(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch);
+
+#endif //PPM_H_
--- a/render.h	Fri Mar 03 23:51:29 2017 -0800
+++ b/render.h	Sat Mar 04 11:50:14 2017 -0800
@@ -70,6 +70,7 @@
 #define RENDER_NOT_PLUGGED_IN -3
 
 uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b);
+void render_save_screenshot(char *path);
 uint32_t *render_get_framebuffer(uint8_t which, int *pitch);
 void render_framebuffer_updated(uint8_t which, int width);
 void render_init(int width, int height, char * title, uint8_t fullscreen);
--- a/render_sdl.c	Fri Mar 03 23:51:29 2017 -0800
+++ b/render_sdl.c	Sat Mar 04 11:50:14 2017 -0800
@@ -12,6 +12,7 @@
 #include "genesis.h"
 #include "io.h"
 #include "util.h"
+#include "ppm.h"
 
 #ifndef DISABLE_OPENGL
 #include <GL/glew.h>
@@ -488,6 +489,15 @@
 	fps_caption = NULL;
 }
 
+static char *screenshot_path;
+void render_save_screenshot(char *path)
+{
+	if (screenshot_path) {
+		free(screenshot_path);
+	}
+	screenshot_path = path;
+}
+
 uint32_t *locked_pixels;
 uint32_t locked_pitch;
 uint32_t *render_get_framebuffer(uint8_t which, int *pitch)
@@ -538,6 +548,19 @@
 	uint32_t height = which <= FRAMEBUFFER_EVEN 
 		? (video_standard == VID_NTSC ? 243 : 294) - (overscan_top[video_standard] + overscan_bot[video_standard])
 		: 240;
+	FILE *screenshot_file = NULL;
+	uint32_t shot_height;
+	if (screenshot_path && which == FRAMEBUFFER_ODD) {
+		screenshot_file = fopen(screenshot_path, "wb");
+		if (screenshot_file) {
+			info_message("Saving screenshot to %s\n", screenshot_path);
+		} else {
+			warning("Failed to open screenshot file %s for writing\n", screenshot_path);
+		}
+		free(screenshot_path);
+		screenshot_path = NULL;
+		shot_height = video_standard == VID_NTSC ? 243 : 294;
+	}
 #ifndef DISABLE_OPENGL
 	if (render_gl && which <= FRAMEBUFFER_EVEN) {
 		glBindTexture(GL_TEXTURE_2D, textures[which]);
@@ -568,6 +591,11 @@
 		glDisableVertexAttribArray(at_pos);
 
 		SDL_GL_SwapWindow(main_window);
+		
+		if (screenshot_file) {
+			//properly supporting interlaced modes here is non-trivial, so only save the odd field for now
+			save_ppm(screenshot_file, texture_buf, width, shot_height, 320*sizeof(uint32_t));
+		}
 	} else {
 #endif
 		if (which <= FRAMEBUFFER_EVEN && last != which) {
@@ -586,6 +614,15 @@
 			}
 			height = 480;
 		}
+		if (screenshot_file) {
+			uint32_t shot_pitch = locked_pitch;
+			if (which == FRAMEBUFFER_EVEN) {
+				shot_height *= 2;
+			} else {
+				shot_pitch *= 2;
+			}
+			save_ppm(screenshot_file, locked_pixels, width, shot_height, shot_pitch);
+		}
 		SDL_UnlockTexture(sdl_textures[which]);
 		SDL_Rect src_clip = {
 			.x = 0,
@@ -598,6 +635,9 @@
 #ifndef DISABLE_OPENGL
 	}
 #endif
+	if (screenshot_file) {
+		fclose(screenshot_file);
+	}
 	if (which <= FRAMEBUFFER_EVEN) {
 		last = which;
 		static uint32_t frame_counter, start;