From fbdb62c71f35d679d33d0420eeab5c4a9df8ede6 Mon Sep 17 00:00:00 2001 From: Francisco Javier Trujillo Mata Date: Mon, 12 May 2025 22:26:28 +0200 Subject: [PATCH 1/3] Create function header --- src/debug/Makefile.am | 1 + src/debug/pspdebug.h | 10 ++++++++++ src/debug/screenshot.c | 17 +++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 src/debug/screenshot.c diff --git a/src/debug/Makefile.am b/src/debug/Makefile.am index fb48c579..4ea86c5d 100644 --- a/src/debug/Makefile.am +++ b/src/debug/Makefile.am @@ -33,6 +33,7 @@ libpspdebug_a_SOURCES = \ kprintf.c \ stacktrace.c \ profiler.c \ + screenshot.c \ stdio.c \ sio.c diff --git a/src/debug/pspdebug.h b/src/debug/pspdebug.h index b9ff1046..91dee9ea 100644 --- a/src/debug/pspdebug.h +++ b/src/debug/pspdebug.h @@ -433,6 +433,16 @@ void pspDebugSioEnableKprintf(void); */ void pspDebugSioDisableKprintf(void); +/** + * Save a screenshot to a file + * + * @param filename - The filename to save the screenshot for the current frame buffer displayed on the screen. + * The filename will be saved with a BMP extension. + * + * @return 0 on success, -1 on error + */ +int pspScreenshotSave(const char *filename); + /**@}*/ #ifdef __cplusplus diff --git a/src/debug/screenshot.c b/src/debug/screenshot.c new file mode 100644 index 00000000..0d175e6b --- /dev/null +++ b/src/debug/screenshot.c @@ -0,0 +1,17 @@ +/* + * PSP Software Development Kit - https://github.com/pspdev + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * screenshot.c - Simple screen debug keyboard + * + * Copyright (c) 2025, Francisco Javier Trujillo Mata + * + */ + +#include "pspdebug.h" + +int pspScreenshotSave(const char *filename) +{ + return 0; +} \ No newline at end of file From 38b8f5f2cc7d317d6b5b44eb032b88df717fb878 Mon Sep 17 00:00:00 2001 From: Francisco Javier Trujillo Mata Date: Mon, 12 May 2025 23:01:59 +0200 Subject: [PATCH 2/3] Add implementation for bitmapwrite --- src/debug/Makefile.am | 1 + src/debug/bitmap.c | 201 +++++++++++++++++++++++++++++++++++++++++ src/debug/screenshot.c | 17 +++- 3 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 src/debug/bitmap.c diff --git a/src/debug/Makefile.am b/src/debug/Makefile.am index 4ea86c5d..b7677921 100644 --- a/src/debug/Makefile.am +++ b/src/debug/Makefile.am @@ -24,6 +24,7 @@ libpspdebuginclude_HEADERS = \ lib_LIBRARIES = libpspdebug.a libpspgdb.a libpspgdb_user.a libpspgdb_kernel.a libpspdebugkb.a libpspdebug_a_SOURCES = \ + bitmap.c \ callstack.c \ callstackget.S \ font.c \ diff --git a/src/debug/bitmap.c b/src/debug/bitmap.c new file mode 100644 index 00000000..4804fcd3 --- /dev/null +++ b/src/debug/bitmap.c @@ -0,0 +1,201 @@ +/* + * PSP Software Development Kit - https://github.com/pspdev + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * bitmap.c - Take a screenshot and save it as a bitmap file + * + * Copyright (c) 2025, Francisco Javier Trujillo Mata + * + */ + +#include +#include +#include +#include +#include + +#define BMP_ID "BM" +#define PSP_SCREEN_WIDTH 480 +#define PSP_SCREEN_HEIGHT 272 +#define PSP_LINE_SIZE 512 +#define BMP_RGB_BYTES_PER_PIXEL 3 // BMP format uses 24-bit RGB (3 bytes per pixel) + +// Helper function to get pixel depth in bytes for a given format +static int get_pixel_depth(int format) +{ + switch (format) + { + case PSP_DISPLAY_PIXEL_FORMAT_565: + case PSP_DISPLAY_PIXEL_FORMAT_5551: + case PSP_DISPLAY_PIXEL_FORMAT_4444: + return 2; // 16-bit formats + case PSP_DISPLAY_PIXEL_FORMAT_8888: + return 4; // 32-bit format + default: + return 0; // Invalid format + } +} + +struct BitmapHeader +{ + char id[2]; + uint32_t filesize; + uint32_t reserved; + uint32_t offset; + uint32_t headsize; + uint32_t width; + uint32_t height; + uint16_t planes; + uint16_t bpp; + uint32_t comp; + uint32_t bitmapsize; + uint32_t hres; + uint32_t vres; + uint32_t colors; + uint32_t impcolors; +} __attribute__((packed)); + +static int fixed_write(int fd, void *data, int len) +{ + int writelen = 0; + + while (writelen < len) + { + int ret; + + ret = sceIoWrite(fd, data + writelen, len - writelen); + if (ret <= 0) + { + writelen = -1; + break; + } + writelen += ret; + } + + return writelen; +} + +void write_8888_line(void *frame, void *line_buf, int line) +{ + uint8_t *line_data = line_buf; + const int pixel_depth = get_pixel_depth(PSP_DISPLAY_PIXEL_FORMAT_8888); + uint8_t *p = frame + (line * PSP_LINE_SIZE * pixel_depth); + int i; + + for (i = 0; i < PSP_SCREEN_WIDTH; i++) + { + line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 2] = p[i * pixel_depth]; + line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 1] = p[(i * pixel_depth) + 1]; + line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 0] = p[(i * pixel_depth) + 2]; + } +} + +void write_5551_line(void *frame, void *line_buf, int line) +{ + uint8_t *line_data = line_buf; + const int pixel_depth = get_pixel_depth(PSP_DISPLAY_PIXEL_FORMAT_5551); + uint16_t *p = frame + (line * PSP_LINE_SIZE * pixel_depth); + int i; + + for (i = 0; i < PSP_SCREEN_WIDTH; i++) + { + line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 2] = (p[i] & 0x1F) << 3; + line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 1] = ((p[i] >> 5) & 0x1F) << 3; + line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 0] = ((p[i] >> 10) & 0x1F) << 3; + } +} + +void write_565_line(void *frame, void *line_buf, int line) +{ + uint8_t *line_data = line_buf; + const int pixel_depth = get_pixel_depth(PSP_DISPLAY_PIXEL_FORMAT_565); + uint16_t *p = frame + (line * PSP_LINE_SIZE * pixel_depth); + int i; + + for (i = 0; i < PSP_SCREEN_WIDTH; i++) + { + line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 2] = (p[i] & 0x1F) << 3; + line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 1] = ((p[i] >> 5) & 0x3F) << 2; + line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 0] = ((p[i] >> 11) & 0x1F) << 3; + } +} + +void write_4444_line(void *frame, void *line_buf, int line) +{ + uint8_t *line_data = line_buf; + const int pixel_depth = get_pixel_depth(PSP_DISPLAY_PIXEL_FORMAT_4444); + uint16_t *p = frame + (line * PSP_LINE_SIZE * pixel_depth); + int i; + + for (i = 0; i < PSP_SCREEN_WIDTH; i++) + { + line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 2] = (p[i] & 0xF) << 4; + line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 1] = ((p[i] >> 4) & 0xF) << 4; + line_data[(i * BMP_RGB_BYTES_PER_PIXEL) + 0] = ((p[i] >> 8) & 0xF) << 4; + } +} + +int bitmapWrite(void *frame_addr, int format, const char *file) +{ + struct BitmapHeader bmp; + uint8_t line_buf[PSP_SCREEN_WIDTH * BMP_RGB_BYTES_PER_PIXEL]; // Stack buffer for one line of RGB data + int fd; + int line; + + // Initialize header + memset(&bmp, 0, sizeof(struct BitmapHeader)); + memcpy(bmp.id, BMP_ID, sizeof(bmp.id)); + bmp.filesize = PSP_SCREEN_WIDTH * PSP_SCREEN_HEIGHT * BMP_RGB_BYTES_PER_PIXEL + sizeof(struct BitmapHeader); + bmp.offset = sizeof(struct BitmapHeader); + bmp.headsize = 0x28; + bmp.width = PSP_SCREEN_WIDTH; + bmp.height = PSP_SCREEN_HEIGHT; + bmp.planes = 1; + bmp.bpp = BMP_RGB_BYTES_PER_PIXEL * 8; // Convert bytes to bits + bmp.bitmapsize = PSP_SCREEN_WIDTH * PSP_SCREEN_HEIGHT * BMP_RGB_BYTES_PER_PIXEL; + bmp.hres = 2834; + bmp.vres = 2834; + + fd = sceIoOpen(file, PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC, 0777); + if (fd < 0) + { + return -1; + } + + // Write header first + if (fixed_write(fd, &bmp, sizeof(struct BitmapHeader)) != sizeof(struct BitmapHeader)) + { + sceIoClose(fd); + return -1; + } + + // Process and write each line (from bottom to top as required by BMP format) + for (line = PSP_SCREEN_HEIGHT - 1; line >= 0; line--) + { + switch (format) + { + case PSP_DISPLAY_PIXEL_FORMAT_565: + write_565_line(frame_addr, line_buf, line); + break; + case PSP_DISPLAY_PIXEL_FORMAT_5551: + write_5551_line(frame_addr, line_buf, line); + break; + case PSP_DISPLAY_PIXEL_FORMAT_4444: + write_4444_line(frame_addr, line_buf, line); + break; + case PSP_DISPLAY_PIXEL_FORMAT_8888: + write_8888_line(frame_addr, line_buf, line); + break; + } + + if (fixed_write(fd, line_buf, sizeof(line_buf)) != sizeof(line_buf)) + { + sceIoClose(fd); + return -1; + } + } + + sceIoClose(fd); + return 0; +} diff --git a/src/debug/screenshot.c b/src/debug/screenshot.c index 0d175e6b..2857345c 100644 --- a/src/debug/screenshot.c +++ b/src/debug/screenshot.c @@ -10,8 +10,23 @@ */ #include "pspdebug.h" +#include "pspdisplay.h" +#include "pspuser.h" + +#define PSP_SCREEN_HEIGHT 272 + +int bitmapWrite(void *frame_addr, int format, const char *file); int pspScreenshotSave(const char *filename) { - return 0; + void* buff; + int bufferwidth, pixelformat; + + int ret = sceDisplayGetFrameBuf(&buff, &bufferwidth, &pixelformat, PSP_DISPLAY_SETBUF_NEXTHSYNC); + if (ret != 0 || buff == NULL || bufferwidth == 0) { + return -1; + } + + // write the screenshot to the file + return bitmapWrite(buff, pixelformat, filename); } \ No newline at end of file From ee30cb26683b3e500357c8ba39931ab5031bcabe Mon Sep 17 00:00:00 2001 From: Francisco Javier Trujillo Mata Date: Tue, 13 May 2025 21:00:45 +0200 Subject: [PATCH 3/3] screenshot example --- src/samples/debug/screenshot/Makefile.sample | 17 +++++ src/samples/debug/screenshot/main.c | 69 ++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/samples/debug/screenshot/Makefile.sample create mode 100644 src/samples/debug/screenshot/main.c diff --git a/src/samples/debug/screenshot/Makefile.sample b/src/samples/debug/screenshot/Makefile.sample new file mode 100644 index 00000000..1421d31a --- /dev/null +++ b/src/samples/debug/screenshot/Makefile.sample @@ -0,0 +1,17 @@ +TARGET = screenshot +OBJS = main.o + +INCDIR = +CFLAGS = -O2 -Wall +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +LIBDIR = +LDFLAGS = +LIBS= + +EXTRA_TARGETS = EBOOT.PBP +PSP_EBOOT_TITLE = Screenshot Test + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build.mak diff --git a/src/samples/debug/screenshot/main.c b/src/samples/debug/screenshot/main.c new file mode 100644 index 00000000..3d9e0b6f --- /dev/null +++ b/src/samples/debug/screenshot/main.c @@ -0,0 +1,69 @@ +/* + * PSP Software Development Kit - https://github.com/pspdev + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * main.c - Basic sample to demonstrate the kprintf handler. + * + * Copyright (c) 2005 Marcus R. Brown + * Copyright (c) 2005 James Forshaw + * Copyright (c) 2005 John Kelley + * + */ + +#include +#include +#include +#include +#include + +PSP_MODULE_INFO("Screenshot Sample", 0, 1, 1); +PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER); + +/* Exit callback */ +int exit_callback(int arg1, int arg2, void *common) +{ + exit(0); + return 0; +} + +/* Callback thread */ +int CallbackThread(SceSize args, void *argp) +{ + int cbid; + + cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL); + sceKernelRegisterExitCallback(cbid); + + sceKernelSleepThreadCB(); + + return 0; +} + +/* Sets up the callback thread and returns its thread id */ +int SetupCallbacks(void) +{ + int thid = 0; + + thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, THREAD_ATTR_USER, 0); + if(thid >= 0) + { + sceKernelStartThread(thid, 0, 0); + } + + return thid; +} + +int main(void) +{ + pspDebugScreenInit(); + SetupCallbacks(); + + pspDebugScreenSetTextColor(0xFF); + pspDebugScreenPrintf("\n\n\n\n\n******************** Screenshot Sample *********************\n\n\n\n"); + + sceDisplayWaitVblankStart(); + pspScreenshotSave("screenshot.bmp"); + + while(1); +} \ No newline at end of file