Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libmupen64plus/mupen64plus-core/src/osd/screenshot.cpp
2 views
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
#include <SDL_opengl.h>
28
#include <SDL.h>
29
#include <png.h>
30
31
#include "osd.h"
32
33
extern "C" {
34
#define M64P_CORE_PROTOTYPES 1
35
#include "api/m64p_types.h"
36
#include "api/callbacks.h"
37
#include "api/m64p_config.h"
38
#include "api/config.h"
39
#include "main/main.h"
40
#include "main/util.h"
41
#include "main/rom.h"
42
#include "osal/files.h"
43
#include "osal/preproc.h"
44
#include "plugin/plugin.h"
45
}
46
47
/*********************************************************************************************************
48
* PNG support functions for writing screenshot files
49
*/
50
51
static void mupen_png_error(png_structp png_write, const char *message)
52
{
53
DebugMessage(M64MSG_ERROR, "PNG Error: %s", message);
54
}
55
56
static void mupen_png_warn(png_structp png_write, const char *message)
57
{
58
DebugMessage(M64MSG_WARNING, "PNG Warning: %s", message);
59
}
60
61
static void user_write_data(png_structp png_write, png_bytep data, png_size_t length)
62
{
63
FILE *fPtr = (FILE *) png_get_io_ptr(png_write);
64
if (fwrite(data, 1, length, fPtr) != length)
65
DebugMessage(M64MSG_ERROR, "Failed to write %zi bytes to screenshot file.", length);
66
}
67
68
static void user_flush_data(png_structp png_write)
69
{
70
FILE *fPtr = (FILE *) png_get_io_ptr(png_write);
71
fflush(fPtr);
72
}
73
74
/*********************************************************************************************************
75
* Other Local (static) functions
76
*/
77
78
static int SaveRGBBufferToFile(const char *filename, const unsigned char *buf, int width, int height, int pitch)
79
{
80
int i;
81
82
// allocate PNG structures
83
png_structp png_write = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, mupen_png_error, mupen_png_warn);
84
if (!png_write)
85
{
86
DebugMessage(M64MSG_ERROR, "Error creating PNG write struct.");
87
return 1;
88
}
89
png_infop png_info = png_create_info_struct(png_write);
90
if (!png_info)
91
{
92
png_destroy_write_struct(&png_write, (png_infopp)NULL);
93
DebugMessage(M64MSG_ERROR, "Error creating PNG info struct.");
94
return 2;
95
}
96
// Set the jumpback
97
if (setjmp(png_jmpbuf(png_write)))
98
{
99
png_destroy_write_struct(&png_write, &png_info);
100
DebugMessage(M64MSG_ERROR, "Error calling setjmp()");
101
return 3;
102
}
103
// open the file to write
104
FILE *savefile = fopen(filename, "wb");
105
if (savefile == NULL)
106
{
107
DebugMessage(M64MSG_ERROR, "Error opening '%s' to save screenshot.", filename);
108
return 4;
109
}
110
// set function pointers in the PNG library, for write callbacks
111
png_set_write_fn(png_write, (png_voidp) savefile, user_write_data, user_flush_data);
112
// set the info
113
png_set_IHDR(png_write, png_info, width, height, 8, PNG_COLOR_TYPE_RGB,
114
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
115
// allocate row pointers and scale each row to 24-bit color
116
png_byte **row_pointers;
117
row_pointers = (png_byte **) malloc(height * sizeof(png_bytep));
118
for (i = 0; i < height; i++)
119
{
120
row_pointers[i] = (png_byte *) (buf + (height - 1 - i) * pitch);
121
}
122
// set the row pointers
123
png_set_rows(png_write, png_info, row_pointers);
124
// write the picture to disk
125
png_write_png(png_write, png_info, 0, NULL);
126
// free memory
127
free(row_pointers);
128
png_destroy_write_struct(&png_write, &png_info);
129
// close file
130
fclose(savefile);
131
// all done
132
return 0;
133
}
134
135
static int CurrentShotIndex;
136
137
static char *GetNextScreenshotPath(void)
138
{
139
char *ScreenshotPath;
140
char ScreenshotFileName[20 + 8 + 1];
141
142
// generate the base name of the screenshot
143
// add the ROM name, convert to lowercase, convert spaces to underscores
144
strcpy(ScreenshotFileName, ROM_PARAMS.headername);
145
for (char *pch = ScreenshotFileName; *pch != '\0'; pch++)
146
*pch = (*pch == ' ') ? '_' : tolower(*pch);
147
strcat(ScreenshotFileName, "-###.png");
148
149
// add the base path to the screenshot file name
150
const char *SshotDir = ConfigGetParamString(g_CoreConfig, "ScreenshotPath");
151
if (SshotDir == NULL || *SshotDir == '\0')
152
{
153
// note the trick to avoid an allocation. we add a NUL character
154
// instead of the separator, call mkdir, then add the separator
155
ScreenshotPath = formatstr("%sscreenshot%c%s", ConfigGetUserDataPath(), '\0', ScreenshotFileName);
156
if (ScreenshotPath == NULL)
157
return NULL;
158
osal_mkdirp(ScreenshotPath, 0700);
159
ScreenshotPath[strlen(ScreenshotPath)] = OSAL_DIR_SEPARATORS[0];
160
}
161
else
162
{
163
ScreenshotPath = combinepath(SshotDir, ScreenshotFileName);
164
if (ScreenshotPath == NULL)
165
return NULL;
166
}
167
168
// patch the number part of the name (the '###' part) until we find a free spot
169
char *NumberPtr = ScreenshotPath + strlen(ScreenshotPath) - 7;
170
for (; CurrentShotIndex < 1000; CurrentShotIndex++)
171
{
172
sprintf(NumberPtr, "%03i.png", CurrentShotIndex);
173
FILE *pFile = fopen(ScreenshotPath, "r");
174
if (pFile == NULL)
175
break;
176
fclose(pFile);
177
}
178
179
if (CurrentShotIndex >= 1000)
180
{
181
DebugMessage(M64MSG_ERROR, "Can't save screenshot; folder already contains 1000 screenshots for this ROM");
182
return NULL;
183
}
184
CurrentShotIndex++;
185
186
return ScreenshotPath;
187
}
188
189
/*********************************************************************************************************
190
* Global screenshot functions
191
*/
192
193
extern "C" void ScreenshotRomOpen(void)
194
{
195
CurrentShotIndex = 0;
196
}
197
198
extern "C" void TakeScreenshot(int iFrameNumber)
199
{
200
char *filename;
201
202
// look for an unused screenshot filename
203
filename = GetNextScreenshotPath();
204
if (filename == NULL)
205
return;
206
207
// get the width and height
208
int width = 640;
209
int height = 480;
210
gfx.readScreen(NULL, &width, &height, 0);
211
212
// allocate memory for the image
213
unsigned char *pucFrame = (unsigned char *) malloc(width * height * 3);
214
if (pucFrame == NULL)
215
{
216
free(filename);
217
return;
218
}
219
220
// grab the back image from OpenGL by calling the video plugin
221
gfx.readScreen(pucFrame, &width, &height, 0);
222
223
// write the image to a PNG
224
SaveRGBBufferToFile(filename, pucFrame, width, height, width * 3);
225
// free the memory
226
free(pucFrame);
227
free(filename);
228
// print message -- this allows developers to capture frames and use them in the regression test
229
main_message(M64MSG_INFO, OSD_BOTTOM_LEFT, "Captured screenshot for frame %i.", iFrameNumber);
230
}
231
232
233