/* 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 #include #include #include #include #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