# HG changeset patch # User Michael Pavone # Date 1488657014 28800 # Node ID 5f65a16c23ffc68a5d4a060c9bf035db8c620baf # Parent 462d9770d467466b8cdda104aab29d62079164fe Implement raw screenshot functionality requested in ticket:10 diff -r 462d9770d467 -r 5f65a16c23ff Makefile --- 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 diff -r 462d9770d467 -r 5f65a16c23ff default.cfg --- 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 { diff -r 462d9770d467 -r 5f65a16c23ff io.c --- 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 { diff -r 462d9770d467 -r 5f65a16c23ff ppm.c --- /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 +#include + +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 -r 462d9770d467 -r 5f65a16c23ff ppm.h --- /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_ diff -r 462d9770d467 -r 5f65a16c23ff render.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); diff -r 462d9770d467 -r 5f65a16c23ff render_sdl.c --- 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 @@ -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;