summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--default.cfg5
-rw-r--r--io.c25
-rw-r--r--ppm.c21
-rw-r--r--ppm.h6
-rw-r--r--render.h1
-rwxr-xr-xrender_sdl.c40
7 files changed, 99 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 82bd4a5..e90416f 100644
--- a/Makefile
+++ b/Makefile
@@ -128,7 +128,7 @@ Z80OBJS=z80inst.o z80_to_x86.o
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
diff --git a/default.cfg b/default.cfg
index f3a4432..7a5edcd 100644
--- a/default.cfg
+++ b/default.cfg
@@ -18,6 +18,7 @@ bindings {
[ 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 @@ clocks {
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 {
diff --git a/io.c b/io.c
index 8999242..2081e2f 100644
--- a/io.c
+++ b/io.c
@@ -74,6 +74,7 @@ typedef enum {
UI_TOGGLE_KEYBOARD_CAPTURE,
UI_TOGGLE_FULLSCREEN,
UI_SOFT_RESET,
+ UI_SCREENSHOT,
UI_EXIT
} ui_action;
@@ -402,6 +403,10 @@ uint8_t keyboard_connected(sega_io *io)
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 @@ void handle_binding_up(keybinding * binding)
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 @@ int parse_binding_target(char * target, tern_node * padbuttons, tern_node *mouse
*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 {
diff --git a/ppm.c b/ppm.c
new file mode 100644
index 0000000..5e46793
--- /dev/null
+++ b/ppm.c
@@ -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);
+ }
+}
diff --git a/ppm.h b/ppm.h
new file mode 100644
index 0000000..27a2984
--- /dev/null
+++ b/ppm.h
@@ -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_
diff --git a/render.h b/render.h
index 130d4f2..9e55022 100644
--- a/render.h
+++ b/render.h
@@ -70,6 +70,7 @@ typedef enum {
#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);
diff --git a/render_sdl.c b/render_sdl.c
index f29c76e..e0fe476 100755
--- a/render_sdl.c
+++ b/render_sdl.c
@@ -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 @@ void render_update_caption(char *title)
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 @@ void render_framebuffer_updated(uint8_t which, int width)
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 @@ void render_framebuffer_updated(uint8_t which, int width)
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 @@ void render_framebuffer_updated(uint8_t which, int width)
}
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 @@ void render_framebuffer_updated(uint8_t which, int width)
#ifndef DISABLE_OPENGL
}
#endif
+ if (screenshot_file) {
+ fclose(screenshot_file);
+ }
if (which <= FRAMEBUFFER_EVEN) {
last = which;
static uint32_t frame_counter, start;