300 lines
6.3 KiB
C
300 lines
6.3 KiB
C
|
|
/* FCE Ultra - NES/Famicom Emulator
|
||
|
|
*
|
||
|
|
* Copyright notice for this file:
|
||
|
|
* Copyright (C) 2002 Xodnizel
|
||
|
|
*
|
||
|
|
* This program is free software; you can redistribute it and/or modify
|
||
|
|
* it under the terms of the GNU General Public License as published by
|
||
|
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
|
* (at your option) any later version.
|
||
|
|
*
|
||
|
|
* This program is distributed in the hope that it will be useful,
|
||
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
|
* GNU General Public License for more details.
|
||
|
|
*
|
||
|
|
* You should have received a copy of the GNU General Public License
|
||
|
|
* along with this program; if not, write to the Free Software
|
||
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <string.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <stdarg.h>
|
||
|
|
#include <zlib.h>
|
||
|
|
|
||
|
|
#include "fceu-types.h"
|
||
|
|
#include "video.h"
|
||
|
|
#include "fceu.h"
|
||
|
|
#include "general.h"
|
||
|
|
#include "memory.h"
|
||
|
|
#include "crc32.h"
|
||
|
|
#include "state.h"
|
||
|
|
#include "movie.h"
|
||
|
|
#include "palette.h"
|
||
|
|
#include "nsf.h"
|
||
|
|
#include "input.h"
|
||
|
|
#include "vsuni.h"
|
||
|
|
|
||
|
|
uint8 *XBuf = NULL;
|
||
|
|
static uint8 *xbsave = NULL;
|
||
|
|
|
||
|
|
#ifndef __LIBRETRO__
|
||
|
|
#define SNAPSHOTS_ENABLED
|
||
|
|
#endif
|
||
|
|
|
||
|
|
void FCEU_KillVirtualVideo(void) {
|
||
|
|
if (xbsave) {
|
||
|
|
free(xbsave);
|
||
|
|
xbsave = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int FCEU_InitVirtualVideo(void) {
|
||
|
|
if (!XBuf) // Some driver code may allocate XBuf externally.
|
||
|
|
// 256 bytes per scanline, * 240 scanline maximum, +8 for alignment,
|
||
|
|
if (!(XBuf = (uint8*)(FCEU_malloc(256 * 256 + 8))))
|
||
|
|
return 0;
|
||
|
|
xbsave = XBuf;
|
||
|
|
|
||
|
|
if (sizeof(uint8*) == 4) {
|
||
|
|
uint32 m;
|
||
|
|
m = (uint32)XBuf;
|
||
|
|
m = (4 - m) & 3;
|
||
|
|
XBuf += m;
|
||
|
|
}
|
||
|
|
memset(XBuf, 128, 256 * 256);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int howlong;
|
||
|
|
static char errmsg[65];
|
||
|
|
|
||
|
|
#include "drawing.h"
|
||
|
|
#ifdef FRAMESKIP
|
||
|
|
|
||
|
|
//#define SHOWFPS
|
||
|
|
void ShowFPS(void);
|
||
|
|
void FCEU_PutImageDummy(void) {
|
||
|
|
#ifdef SHOWFPS
|
||
|
|
ShowFPS();
|
||
|
|
#endif
|
||
|
|
if (GameInfo->type != GIT_NSF) {
|
||
|
|
FCEU_DrawNTSCControlBars(XBuf);
|
||
|
|
FCEU_DrawSaveStates(XBuf);
|
||
|
|
FCEU_DrawMovies(XBuf);
|
||
|
|
}
|
||
|
|
if (howlong) howlong--; /* DrawMessage() */
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
static int dosnapsave = 0;
|
||
|
|
void FCEUI_SaveSnapshot(void) {
|
||
|
|
dosnapsave = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void ReallySnap(void) {
|
||
|
|
int x = SaveSnapshot();
|
||
|
|
if (!x)
|
||
|
|
FCEU_DispMessage("Error saving screen snapshot.");
|
||
|
|
else
|
||
|
|
FCEU_DispMessage("Screen snapshot %d saved.", x - 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
void FCEU_PutImage(void) {
|
||
|
|
#ifdef SHOWFPS
|
||
|
|
ShowFPS();
|
||
|
|
#endif
|
||
|
|
if (GameInfo->type == GIT_NSF) {
|
||
|
|
DrawNSF(XBuf);
|
||
|
|
// Save snapshot after NSF screen is drawn. Why would we want to
|
||
|
|
// do it before?
|
||
|
|
if (dosnapsave) {
|
||
|
|
ReallySnap();
|
||
|
|
dosnapsave = 0;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// Save snapshot before overlay stuff is written.
|
||
|
|
if (dosnapsave) {
|
||
|
|
ReallySnap();
|
||
|
|
dosnapsave = 0;
|
||
|
|
}
|
||
|
|
if (GameInfo->type == GIT_VSUNI)
|
||
|
|
FCEU_VSUniDraw(XBuf);
|
||
|
|
FCEU_DrawSaveStates(XBuf);
|
||
|
|
FCEU_DrawMovies(XBuf);
|
||
|
|
FCEU_DrawNTSCControlBars(XBuf);
|
||
|
|
}
|
||
|
|
DrawMessage();
|
||
|
|
FCEU_DrawInput(XBuf);
|
||
|
|
}
|
||
|
|
|
||
|
|
void FCEU_DispMessage(char *format, ...) {
|
||
|
|
va_list ap;
|
||
|
|
|
||
|
|
va_start(ap, format);
|
||
|
|
vsprintf(errmsg, format, ap);
|
||
|
|
va_end(ap);
|
||
|
|
|
||
|
|
howlong = 180;
|
||
|
|
}
|
||
|
|
|
||
|
|
void FCEU_ResetMessages(void) {
|
||
|
|
howlong = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static int WritePNGChunk(FILE *fp, uint32 size, char *type, uint8 *data) {
|
||
|
|
uint32 crc;
|
||
|
|
|
||
|
|
uint8 tempo[4];
|
||
|
|
|
||
|
|
tempo[0] = size >> 24;
|
||
|
|
tempo[1] = size >> 16;
|
||
|
|
tempo[2] = size >> 8;
|
||
|
|
tempo[3] = size;
|
||
|
|
|
||
|
|
if (fwrite(tempo, 4, 1, fp) != 1)
|
||
|
|
return 0;
|
||
|
|
if (fwrite(type, 4, 1, fp) != 1)
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
if (size)
|
||
|
|
if (fwrite(data, 1, size, fp) != size)
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
crc = CalcCRC32(0, (uint8*)type, 4);
|
||
|
|
if (size)
|
||
|
|
crc = CalcCRC32(crc, data, size);
|
||
|
|
|
||
|
|
tempo[0] = crc >> 24;
|
||
|
|
tempo[1] = crc >> 16;
|
||
|
|
tempo[2] = crc >> 8;
|
||
|
|
tempo[3] = crc;
|
||
|
|
|
||
|
|
if (fwrite(tempo, 4, 1, fp) != 1)
|
||
|
|
return 0;
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
int SaveSnapshot(void) {
|
||
|
|
#ifdef SNAPSHOTS_ENABLED
|
||
|
|
static uint32 lastu = 0;
|
||
|
|
|
||
|
|
char *fn = 0;
|
||
|
|
int totallines = FSettings.LastSLine - FSettings.FirstSLine + 1;
|
||
|
|
int x, u, y;
|
||
|
|
FILE *pp = NULL;
|
||
|
|
uint8 *compmem = NULL;
|
||
|
|
uLongf compmemsize = totallines * 263 + 12;
|
||
|
|
|
||
|
|
if (!(compmem = (uint8*)FCEU_malloc(compmemsize)))
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
for (u = lastu; u < 99999; u++) {
|
||
|
|
pp = FCEUD_UTF8fopen((fn = FCEU_MakeFName(FCEUMKF_SNAP, u, "png")), "rb");
|
||
|
|
if (pp == NULL) break;
|
||
|
|
fclose(pp);
|
||
|
|
}
|
||
|
|
|
||
|
|
lastu = u;
|
||
|
|
|
||
|
|
if (!(pp = FCEUD_UTF8fopen(fn, "wb"))) {
|
||
|
|
free(fn);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
free(fn);
|
||
|
|
{
|
||
|
|
static uint8 header[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
|
||
|
|
if (fwrite(header, 8, 1, pp) != 1)
|
||
|
|
goto PNGerr;
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
uint8 chunko[13];
|
||
|
|
|
||
|
|
chunko[0] = chunko[1] = chunko[3] = 0;
|
||
|
|
chunko[2] = 0x1; // Width of 256
|
||
|
|
|
||
|
|
chunko[4] = chunko[5] = chunko[6] = 0;
|
||
|
|
chunko[7] = totallines; // Height
|
||
|
|
|
||
|
|
chunko[8] = 8; // bit depth
|
||
|
|
chunko[9] = 3; // Color type; indexed 8-bit
|
||
|
|
chunko[10] = 0; // compression: deflate
|
||
|
|
chunko[11] = 0; // Basic adapative filter set(though none are used).
|
||
|
|
chunko[12] = 0; // No interlace.
|
||
|
|
|
||
|
|
if (!WritePNGChunk(pp, 13, "IHDR", chunko))
|
||
|
|
goto PNGerr;
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
uint8 pdata[256 * 3];
|
||
|
|
for (x = 0; x < 256; x++)
|
||
|
|
FCEUD_GetPalette(x, pdata + x * 3, pdata + x * 3 + 1, pdata + x * 3 + 2);
|
||
|
|
if (!WritePNGChunk(pp, 256 * 3, "PLTE", pdata))
|
||
|
|
goto PNGerr;
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
uint8 *tmp = XBuf + FSettings.FirstSLine * 256;
|
||
|
|
uint8 *dest, *mal, *mork;
|
||
|
|
|
||
|
|
if (!(mal = mork = dest = (uint8*)malloc((totallines << 8) + totallines)))
|
||
|
|
goto PNGerr;
|
||
|
|
// mork=dest=XBuf;
|
||
|
|
|
||
|
|
for (y = 0; y < totallines; y++) {
|
||
|
|
*dest = 0; // No filter.
|
||
|
|
dest++;
|
||
|
|
for (x = 256; x; x--, tmp++, dest++)
|
||
|
|
*dest = *tmp;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (compress(compmem, &compmemsize, mork, (totallines << 8) + totallines) != Z_OK) {
|
||
|
|
if (mal) free(mal);
|
||
|
|
goto PNGerr;
|
||
|
|
}
|
||
|
|
if (mal) free(mal);
|
||
|
|
if (!WritePNGChunk(pp, compmemsize, "IDAT", compmem))
|
||
|
|
goto PNGerr;
|
||
|
|
}
|
||
|
|
if (!WritePNGChunk(pp, 0, "IEND", 0))
|
||
|
|
goto PNGerr;
|
||
|
|
|
||
|
|
free(compmem);
|
||
|
|
fclose(pp);
|
||
|
|
|
||
|
|
return u + 1;
|
||
|
|
|
||
|
|
PNGerr:
|
||
|
|
if (compmem)
|
||
|
|
free(compmem);
|
||
|
|
if (pp)
|
||
|
|
fclose(pp);
|
||
|
|
#endif
|
||
|
|
return(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef SHOWFPS
|
||
|
|
uint64 FCEUD_GetTime(void);
|
||
|
|
uint64 FCEUD_GetTimeFreq(void);
|
||
|
|
|
||
|
|
static uint64 boop[60];
|
||
|
|
static int boopcount = 0;
|
||
|
|
|
||
|
|
void ShowFPS(void) {
|
||
|
|
uint64 da = FCEUD_GetTime() - boop[boopcount];
|
||
|
|
char fpsmsg[16];
|
||
|
|
int booplimit = PAL ? 50 : 60;
|
||
|
|
boop[boopcount] = FCEUD_GetTime();
|
||
|
|
|
||
|
|
sprintf(fpsmsg, "%8.1f", (double)booplimit / ((double)da / FCEUD_GetTimeFreq()));
|
||
|
|
DrawTextTrans(XBuf + (256 - 8 - 8 * 8) + (FSettings.FirstSLine + 4) * 256, 256, fpsmsg, 4);
|
||
|
|
// It's not averaging FPS over exactly 1 second, but it's close enough.
|
||
|
|
boopcount = (boopcount + 1) % booplimit;
|
||
|
|
}
|
||
|
|
#endif
|