build
[mupen64plus-pandora.git] / source / mupen64plus-core / src / osd / screenshot.cpp
CommitLineData
451ab91e 1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus - screenshot.c *
3 * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
4 * Copyright (C) 2008 Richard42 *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
20 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21
22#include <stdlib.h>
23#include <string.h>
24#include <stdio.h>
25#include <ctype.h>
26
27#ifdef PANDORA
70282791 28//#include <SDL_opengles2.h>
29#include <GLES2/gl2.h>
30#include <GLES2/gl2ext.h>
31
451ab91e 32#else
33#include <SDL_opengl.h>
34#endif
35#include <SDL.h>
36#include <png.h>
37
38#include "osd.h"
39
40extern "C" {
41#define M64P_CORE_PROTOTYPES 1
42#include "api/m64p_types.h"
43#include "api/callbacks.h"
44#include "api/m64p_config.h"
45#include "api/config.h"
46#include "main/main.h"
47#include "main/util.h"
48#include "main/rom.h"
49#include "osal/files.h"
50#include "osal/preproc.h"
51#include "plugin/plugin.h"
52}
53
54/*********************************************************************************************************
55* PNG support functions for writing screenshot files
56*/
57
58static void mupen_png_error(png_structp png_write, const char *message)
59{
60 DebugMessage(M64MSG_ERROR, "PNG Error: %s", message);
61}
62
63static void mupen_png_warn(png_structp png_write, const char *message)
64{
65 DebugMessage(M64MSG_WARNING, "PNG Warning: %s", message);
66}
67
68static void user_write_data(png_structp png_write, png_bytep data, png_size_t length)
69{
70 FILE *fPtr = (FILE *) png_get_io_ptr(png_write);
71 if (fwrite(data, 1, length, fPtr) != length)
72 DebugMessage(M64MSG_ERROR, "Failed to write %zi bytes to screenshot file.", length);
73}
74
75static void user_flush_data(png_structp png_write)
76{
77 FILE *fPtr = (FILE *) png_get_io_ptr(png_write);
78 fflush(fPtr);
79}
80
81/*********************************************************************************************************
82* Other Local (static) functions
83*/
84
85static int SaveRGBBufferToFile(const char *filename, const unsigned char *buf, int width, int height, int pitch)
86{
87 int i;
88
89 // allocate PNG structures
90 png_structp png_write = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, mupen_png_error, mupen_png_warn);
91 if (!png_write)
92 {
93 DebugMessage(M64MSG_ERROR, "Error creating PNG write struct.");
94 return 1;
95 }
96 png_infop png_info = png_create_info_struct(png_write);
97 if (!png_info)
98 {
99 png_destroy_write_struct(&png_write, (png_infopp)NULL);
100 DebugMessage(M64MSG_ERROR, "Error creating PNG info struct.");
101 return 2;
102 }
103 // Set the jumpback
104 if (setjmp(png_jmpbuf(png_write)))
105 {
106 png_destroy_write_struct(&png_write, &png_info);
107 DebugMessage(M64MSG_ERROR, "Error calling setjmp()");
108 return 3;
109 }
110 // open the file to write
111 FILE *savefile = fopen(filename, "wb");
112 if (savefile == NULL)
113 {
114 DebugMessage(M64MSG_ERROR, "Error opening '%s' to save screenshot.", filename);
115 return 4;
116 }
117 // set function pointers in the PNG library, for write callbacks
118 png_set_write_fn(png_write, (png_voidp) savefile, user_write_data, user_flush_data);
119 // set the info
120 png_set_IHDR(png_write, png_info, width, height, 8, PNG_COLOR_TYPE_RGB,
121 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
122 // allocate row pointers and scale each row to 24-bit color
123 png_byte **row_pointers;
124 row_pointers = (png_byte **) malloc(height * sizeof(png_bytep));
125 for (i = 0; i < height; i++)
126 {
127 row_pointers[i] = (png_byte *) (buf + (height - 1 - i) * pitch);
128 }
129 // set the row pointers
130 png_set_rows(png_write, png_info, row_pointers);
131 // write the picture to disk
132 png_write_png(png_write, png_info, 0, NULL);
133 // free memory
134 free(row_pointers);
135 png_destroy_write_struct(&png_write, &png_info);
136 // close file
137 fclose(savefile);
138 // all done
139 return 0;
140}
141
142static int CurrentShotIndex;
143
144static char *GetNextScreenshotPath(void)
145{
146 char *ScreenshotPath;
147 char ScreenshotFileName[20 + 8 + 1];
148
149 // generate the base name of the screenshot
150 // add the ROM name, convert to lowercase, convert spaces to underscores
151 strcpy(ScreenshotFileName, ROM_PARAMS.headername);
152 for (char *pch = ScreenshotFileName; *pch != '\0'; pch++)
153 *pch = (*pch == ' ') ? '_' : tolower(*pch);
154 strcat(ScreenshotFileName, "-###.png");
155
156 // add the base path to the screenshot file name
157 const char *SshotDir = ConfigGetParamString(g_CoreConfig, "ScreenshotPath");
158 if (SshotDir == NULL || *SshotDir == '\0')
159 {
160 // note the trick to avoid an allocation. we add a NUL character
161 // instead of the separator, call mkdir, then add the separator
162 ScreenshotPath = formatstr("%sscreenshot%c%s", ConfigGetUserDataPath(), '\0', ScreenshotFileName);
163 if (ScreenshotPath == NULL)
164 return NULL;
165 osal_mkdirp(ScreenshotPath, 0700);
166 ScreenshotPath[strlen(ScreenshotPath)] = OSAL_DIR_SEPARATORS[0];
167 }
168 else
169 {
170 ScreenshotPath = combinepath(SshotDir, ScreenshotFileName);
171 if (ScreenshotPath == NULL)
172 return NULL;
173 }
174
175 // patch the number part of the name (the '###' part) until we find a free spot
176 char *NumberPtr = ScreenshotPath + strlen(ScreenshotPath) - 7;
177 for (; CurrentShotIndex < 1000; CurrentShotIndex++)
178 {
179 sprintf(NumberPtr, "%03i.png", CurrentShotIndex);
180 FILE *pFile = fopen(ScreenshotPath, "r");
181 if (pFile == NULL)
182 break;
183 fclose(pFile);
184 }
185
186 if (CurrentShotIndex >= 1000)
187 {
188 DebugMessage(M64MSG_ERROR, "Can't save screenshot; folder already contains 1000 screenshots for this ROM");
189 return NULL;
190 }
191 CurrentShotIndex++;
192
193 return ScreenshotPath;
194}
195
196/*********************************************************************************************************
197* Global screenshot functions
198*/
199
200extern "C" void ScreenshotRomOpen(void)
201{
202 CurrentShotIndex = 0;
203}
204
205extern "C" void TakeScreenshot(int iFrameNumber)
206{
207 char *filename;
208
209 // look for an unused screenshot filename
210 filename = GetNextScreenshotPath();
211 if (filename == NULL)
212 return;
213
214 // get the width and height
215 int width = 640;
216 int height = 480;
217 gfx.readScreen(NULL, &width, &height, 0);
218
219 // allocate memory for the image
220 unsigned char *pucFrame = (unsigned char *) malloc(width * height * 3);
221 if (pucFrame == NULL)
222 {
223 free(filename);
224 return;
225 }
226
227 // grab the back image from OpenGL by calling the video plugin
228 gfx.readScreen(pucFrame, &width, &height, 0);
229
230 // write the image to a PNG
231 SaveRGBBufferToFile(filename, pucFrame, width, height, width * 3);
232 // free the memory
233 free(pucFrame);
234 free(filename);
235 // print message -- this allows developers to capture frames and use them in the regression test
236 main_message(M64MSG_INFO, OSD_BOTTOM_LEFT, "Captured screenshot for frame %i.", iFrameNumber);
237}
238