build
[mupen64plus-pandora.git] / source / mupen64plus-core / src / osd / screenshot.cpp
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
28 //#include <SDL_opengles2.h>
29 #include <GLES2/gl2.h>
30 #include <GLES2/gl2ext.h>
31
32 #else
33 #include <SDL_opengl.h>
34 #endif
35 #include <SDL.h>
36 #include <png.h>
37
38 #include "osd.h"
39
40 extern "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
58 static void mupen_png_error(png_structp png_write, const char *message)
59 {
60     DebugMessage(M64MSG_ERROR, "PNG Error: %s", message);
61 }
62
63 static void mupen_png_warn(png_structp png_write, const char *message)
64 {
65     DebugMessage(M64MSG_WARNING, "PNG Warning: %s", message);
66 }
67
68 static 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
75 static 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
85 static 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
142 static int CurrentShotIndex;
143
144 static 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
200 extern "C" void ScreenshotRomOpen(void)
201 {
202     CurrentShotIndex = 0;
203 }
204
205 extern "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