mirror of https://github.com/PCSX2/pcsx2.git
827 lines
17 KiB
C++
827 lines
17 KiB
C++
/* ZZ Open GL graphics plugin
|
|
* Copyright (c)2009-2010 zeydlitz@gmail.com, arcum42@gmail.com
|
|
* Based on Zerofrog's ZeroGS KOSMOS (c)2005-2008
|
|
*
|
|
* 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 "Util.h"
|
|
#include "GS.h"
|
|
#include "Profile.h"
|
|
#include "GLWin.h"
|
|
#include "ZZoglFlushHack.h"
|
|
#include "ZZoglShaders.h"
|
|
|
|
|
|
using namespace std;
|
|
|
|
extern void SaveSnapshot(const char* filename);
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable:4244)
|
|
#endif
|
|
|
|
GLWindow GLWin;
|
|
GSinternal gs;
|
|
GSconf conf;
|
|
GSDump g_dump;
|
|
|
|
int ppf, g_GSMultiThreaded, CurrentSavestate = 0;
|
|
int g_LastCRC = 0, g_TransferredToGPU = 0, s_frameskipping = 0;
|
|
int g_SkipFlushFrame = 0;
|
|
GetSkipCount GetSkipCount_Handler = 0;
|
|
|
|
int UPDATE_FRAMES = 16, g_nFrame = 0, g_nRealFrame = 0;
|
|
float fFPS = 0;
|
|
|
|
void (*GSirq)();
|
|
u8* g_pBasePS2Mem = NULL;
|
|
wxString s_strIniPath(L"inis"); // Air's new ini path (r2361)
|
|
|
|
bool SaveStateExists = true; // We could not know save slot status before first change occured
|
|
const char* SaveStateFile = NULL; // Name of SaveFile for access check.
|
|
|
|
extern const char* s_aa[5];
|
|
extern const char* pbilinear[];
|
|
// statistics
|
|
u32 g_nGenVars = 0, g_nTexVars = 0, g_nAlphaVars = 0, g_nResolve = 0;
|
|
|
|
#define VER 4
|
|
const unsigned char zgsversion = PS2E_GS_VERSION;
|
|
unsigned char zgsrevision = 0; // revision and build gives plugin version
|
|
unsigned char zgsbuild = VER;
|
|
unsigned char zgsminor = 0;
|
|
|
|
#ifdef _DEBUG
|
|
char *libraryName = "ZZ Ogl PG (Debug) ";
|
|
#elif defined(ZEROGS_DEVBUILD)
|
|
char *libraryName = "ZZ Ogl PG (Dev)";
|
|
#else
|
|
char *libraryName = "ZZ Ogl PG ";
|
|
#endif
|
|
|
|
extern int g_nPixelShaderVer, g_nFrameRender, g_nFramesSkipped;
|
|
|
|
extern void WriteAA();
|
|
extern void WriteBilinear();
|
|
extern void ZZDestroy();
|
|
extern bool ZZCreate(int width, int height);
|
|
extern void ZZGSStateReset();
|
|
extern int ZZSave(s8* pbydata);
|
|
extern bool ZZLoad(s8* pbydata);
|
|
|
|
// switches the render target to the real target, flushes the current render targets and renders the real image
|
|
extern void RenderCRTC();
|
|
|
|
#if defined(_WIN32) && defined(_DEBUG)
|
|
HANDLE g_hCurrentThread = NULL;
|
|
#endif
|
|
|
|
extern int VALIDATE_THRESH;
|
|
extern u32 TEXDESTROY_THRESH;
|
|
|
|
EXPORT_C_(u32) PS2EgetLibType()
|
|
{
|
|
return PS2E_LT_GS;
|
|
}
|
|
|
|
EXPORT_C_(char*) PS2EgetLibName()
|
|
{
|
|
return libraryName;
|
|
}
|
|
|
|
EXPORT_C_(u32) PS2EgetLibVersion2(u32 type)
|
|
{
|
|
return (zgsversion << 16) | (zgsrevision << 8) | zgsbuild | (zgsminor << 24);
|
|
}
|
|
|
|
EXPORT_C_(void) GSsetBaseMem(void* pmem)
|
|
{
|
|
g_pBasePS2Mem = (u8*)pmem;
|
|
}
|
|
|
|
EXPORT_C_(void) GSsetSettingsDir(const char* dir)
|
|
{
|
|
s_strIniPath = (dir == NULL) ? wxString(L"inis") : wxString(dir, wxConvFile);
|
|
}
|
|
|
|
EXPORT_C_(void) GSsetLogDir(const char* dir)
|
|
{
|
|
ZZLog::SetDir(dir);
|
|
}
|
|
|
|
EXPORT_C_(void) GSsetGameCRC(int crc, int options)
|
|
{
|
|
// build a list of function pointer for GetSkipCount (SkipDraw)
|
|
static GetSkipCount GSC_list[NUMBER_OF_TITLES];
|
|
static bool inited = false;
|
|
|
|
if (!inited)
|
|
{
|
|
inited = true;
|
|
|
|
memset(GSC_list, 0, sizeof(GSC_list));
|
|
|
|
GSC_list[Okami] = GSC_Okami;
|
|
GSC_list[MetalGearSolid3] = GSC_MetalGearSolid3;
|
|
GSC_list[DBZBT2] = GSC_DBZBT2;
|
|
GSC_list[DBZBT3] = GSC_DBZBT3;
|
|
GSC_list[SFEX3] = GSC_SFEX3;
|
|
GSC_list[Bully] = GSC_Bully;
|
|
GSC_list[BullyCC] = GSC_BullyCC;
|
|
GSC_list[SoTC] = GSC_SoTC;
|
|
GSC_list[OnePieceGrandAdventure] = GSC_OnePieceGrandAdventure;
|
|
GSC_list[OnePieceGrandBattle] = GSC_OnePieceGrandBattle;
|
|
GSC_list[ICO] = GSC_ICO;
|
|
GSC_list[GT4] = GSC_GT4;
|
|
GSC_list[WildArms4] = GSC_WildArms4;
|
|
GSC_list[WildArms5] = GSC_WildArms5;
|
|
GSC_list[Manhunt2] = GSC_Manhunt2;
|
|
GSC_list[CrashBandicootWoC] = GSC_CrashBandicootWoC;
|
|
GSC_list[ResidentEvil4] = GSC_ResidentEvil4;
|
|
GSC_list[Spartan] = GSC_Spartan;
|
|
GSC_list[AceCombat4] = GSC_AceCombat4;
|
|
GSC_list[Drakengard2] = GSC_Drakengard2;
|
|
GSC_list[Tekken5] = GSC_Tekken5;
|
|
GSC_list[IkkiTousen] = GSC_IkkiTousen;
|
|
GSC_list[GodOfWar] = GSC_GodOfWar;
|
|
GSC_list[GodOfWar2] = GSC_GodOfWar2;
|
|
GSC_list[GiTS] = GSC_GiTS;
|
|
GSC_list[Onimusha3] = GSC_Onimusha3;
|
|
GSC_list[TalesOfAbyss] = GSC_TalesOfAbyss;
|
|
GSC_list[SonicUnleashed] = GSC_SonicUnleashed;
|
|
GSC_list[Genji] = GSC_Genji;
|
|
GSC_list[StarOcean3] = GSC_StarOcean3;
|
|
GSC_list[ValkyrieProfile2] = GSC_ValkyrieProfile2;
|
|
GSC_list[RadiataStories] = GSC_RadiataStories;
|
|
}
|
|
|
|
// TEXDESTROY_THRESH starts out at 16.
|
|
VALIDATE_THRESH = 8;
|
|
conf.mrtdepth = (conf.settings().disable_mrt_depth != 0);
|
|
|
|
if (!conf.mrtdepth)
|
|
ZZLog::WriteLn("Disabling MRT depth writing.");
|
|
else
|
|
ZZLog::WriteLn("Enabling MRT depth writing.");
|
|
|
|
bool CRCValueChanged = (g_LastCRC != crc);
|
|
|
|
g_LastCRC = crc;
|
|
|
|
if (crc != 0) ZZLog::WriteLn("Current game CRC is %x.", crc);
|
|
|
|
if (CRCValueChanged && (crc != 0))
|
|
{
|
|
for (int i = 0; i < GAME_INFO_INDEX; i++)
|
|
{
|
|
if (crc_game_list[i].crc == crc)
|
|
{
|
|
ZZLog::WriteLn("Found CRC[%x] in crc game list.", crc);
|
|
|
|
if (crc_game_list[i].v_thresh > 0)
|
|
{
|
|
VALIDATE_THRESH = crc_game_list[i].v_thresh;
|
|
ZZLog::WriteLn("Setting VALIDATE_THRESH to %d", VALIDATE_THRESH);
|
|
}
|
|
|
|
if (crc_game_list[i].t_thresh > 0)
|
|
{
|
|
TEXDESTROY_THRESH = crc_game_list[i].t_thresh;
|
|
ZZLog::WriteLn("Setting TEXDESTROY_THRESH to %d", TEXDESTROY_THRESH);
|
|
}
|
|
|
|
// FIXME need to check SkipDraw is positive (enabled by users)
|
|
GetSkipCount_Handler = GSC_list[crc_game_list[i].title];
|
|
|
|
if (!conf.disableHacks)
|
|
{
|
|
conf.def_hacks._u32 |= crc_game_list[i].flags;
|
|
ListHacks();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
ListHacks();
|
|
}
|
|
|
|
EXPORT_C_(void) GSsetFrameSkip(int frameskip)
|
|
{
|
|
FUNCLOG
|
|
s_frameskipping |= frameskip;
|
|
|
|
if (frameskip && g_nFrameRender > 1)
|
|
{
|
|
SetFrameSkip(true);
|
|
}
|
|
else if (!frameskip && g_nFrameRender <= 0)
|
|
{
|
|
SetFrameSkip(false);
|
|
}
|
|
}
|
|
|
|
EXPORT_C_(void) GSreset()
|
|
{
|
|
FUNCLOG
|
|
|
|
memset(&gs, 0, sizeof(gs));
|
|
|
|
ZZGSStateReset();
|
|
|
|
gs.prac = 1;
|
|
prim = &gs._prim[0];
|
|
gs.transferring = false;
|
|
gs.q = 1;
|
|
}
|
|
|
|
EXPORT_C_(void) GSgifSoftReset(u32 mask)
|
|
{
|
|
FUNCLOG
|
|
|
|
if (mask & 1) memset(&gs.path[0], 0, sizeof(gs.path[0]));
|
|
if (mask & 2) memset(&gs.path[1], 0, sizeof(gs.path[1]));
|
|
if (mask & 4) memset(&gs.path[2], 0, sizeof(gs.path[2]));
|
|
|
|
gs.transferring = false;
|
|
gs.q = 1;
|
|
}
|
|
|
|
EXPORT_C_(s32) GSinit()
|
|
{
|
|
FUNCLOG
|
|
|
|
ZZLog::Open();
|
|
ZZLog::WriteLn("Calling GSinit.");
|
|
|
|
WriteTempRegs();
|
|
GSreset();
|
|
|
|
ZZLog::WriteLn("GSinit finished.");
|
|
return 0;
|
|
}
|
|
|
|
__forceinline void InitMisc()
|
|
{
|
|
WriteBilinear();
|
|
WriteAA();
|
|
InitProfile();
|
|
InitPath();
|
|
ResetRegs();
|
|
}
|
|
|
|
EXPORT_C_(s32) GSopen(void *pDsp, char *Title, int multithread)
|
|
{
|
|
FUNCLOG
|
|
|
|
g_GSMultiThreaded = multithread;
|
|
|
|
ZZLog::WriteLn("Calling GSopen.");
|
|
|
|
#if defined(_WIN32) && defined(_DEBUG)
|
|
g_hCurrentThread = GetCurrentThread();
|
|
#endif
|
|
|
|
LoadConfig();
|
|
strcpy(GLWin.title, Title);
|
|
|
|
ZZLog::GS_Log("Using %s:%d.%d.%d.", libraryName, zgsrevision, zgsbuild, zgsminor);
|
|
|
|
ZZLog::WriteLn("Creating ZZOgl window.");
|
|
if ((!GLWin.CreateWindow(pDsp)) || (!ZZCreate(conf.width, conf.height))) return -1;
|
|
|
|
ZZLog::WriteLn("Initialization successful.");
|
|
|
|
InitMisc();
|
|
ZZLog::GS_Log("GSopen finished.");
|
|
return 0;
|
|
}
|
|
|
|
#ifdef USE_GSOPEN2
|
|
EXPORT_C_(s32) GSopen2( void* pDsp, u32 flags )
|
|
{
|
|
FUNCLOG
|
|
|
|
g_GSMultiThreaded = true;
|
|
|
|
ZZLog::WriteLn("Calling GSopen2.");
|
|
|
|
#if defined(_WIN32) && defined(_DEBUG)
|
|
g_hCurrentThread = GetCurrentThread();
|
|
#endif
|
|
|
|
LoadConfig();
|
|
|
|
ZZLog::GS_Log("Using %s:%d.%d.%d.", libraryName, zgsrevision, zgsbuild, zgsminor);
|
|
|
|
ZZLog::WriteLn("Capturing ZZOgl window.");
|
|
if ((!GLWin.CreateWindow(pDsp)) || (!ZZCreate(conf.width, conf.height))) return -1;
|
|
|
|
ZZLog::WriteLn("Initialization successful.");
|
|
|
|
InitMisc();
|
|
ZZLog::GS_Log("GSopen2 finished.");
|
|
ZZLog::Check_GL_Error();
|
|
return 0;
|
|
|
|
}
|
|
#endif
|
|
|
|
EXPORT_C_(void) GSshutdown()
|
|
{
|
|
FUNCLOG
|
|
|
|
ZZLog::Close();
|
|
}
|
|
EXPORT_C_(void) GSclose()
|
|
{
|
|
FUNCLOG
|
|
|
|
// Clean shader. Must be done before the context is delete
|
|
ZZshExitCleaning();
|
|
ZZDestroy();
|
|
|
|
GLWin.CloseWindow();
|
|
|
|
// Free alocated memory. We could close plugin without closing pcsx2, so we SHOULD free all allocated resources
|
|
SaveStateFile = NULL;
|
|
SaveStateExists = true; // default value
|
|
g_LastCRC = 0;
|
|
}
|
|
|
|
EXPORT_C_(void) GSirqCallback(void (*callback)())
|
|
{
|
|
FUNCLOG
|
|
|
|
GSirq = callback;
|
|
}
|
|
|
|
EXPORT_C_(void) GSwriteCSR(u32 write)
|
|
{
|
|
FUNCLOG
|
|
|
|
gs.CSRw = write;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
#define access _access
|
|
#endif
|
|
|
|
EXPORT_C_(void) GSchangeSaveState(int newstate, const char* filename)
|
|
{
|
|
FUNCLOG
|
|
|
|
char str[255];
|
|
sprintf(str, "save state %d", newstate);
|
|
ZZAddMessage(str);
|
|
CurrentSavestate = newstate;
|
|
|
|
SaveStateFile = filename;
|
|
SaveStateExists = (access(SaveStateFile, 0) == 0);
|
|
}
|
|
|
|
static bool get_snapshot_filename(char *filename, char* path, const char* extension)
|
|
{
|
|
FUNCLOG
|
|
|
|
FILE *bmpfile;
|
|
u32 snapshotnr = 0;
|
|
|
|
// increment snapshot value & try to get filename
|
|
|
|
for (;;)
|
|
{
|
|
snapshotnr++;
|
|
|
|
sprintf(filename, "%s/snap%03d.%s", path, snapshotnr, extension);
|
|
|
|
bmpfile = fopen(filename, "rb");
|
|
|
|
if (bmpfile == NULL) break;
|
|
|
|
fclose(bmpfile);
|
|
}
|
|
|
|
// try opening new snapshot file
|
|
if ((bmpfile = fopen(filename, "wb")) == NULL)
|
|
{
|
|
char strdir[255];
|
|
sprintf(strdir, "%s", path);
|
|
|
|
#ifdef _WIN32
|
|
CreateDirectory(wxString::FromUTF8(strdir), NULL);
|
|
#else
|
|
mkdir(path, 0777);
|
|
#endif
|
|
|
|
if ((bmpfile = fopen(filename, "wb")) == NULL) return false;
|
|
}
|
|
|
|
fclose(bmpfile);
|
|
|
|
return true;
|
|
}
|
|
|
|
EXPORT_C_(void) GSmakeSnapshot(char *path)
|
|
{
|
|
FUNCLOG
|
|
|
|
char filename[256];
|
|
if (get_snapshot_filename(filename, path, (conf.zz_options.tga_snap) ? "bmp" : "jpg"))
|
|
SaveSnapshot(filename);
|
|
}
|
|
|
|
// I'll probably move this somewhere else later, but it's got a ton of dependencies.
|
|
static __forceinline void SetGSTitle()
|
|
{
|
|
char strtitle[256];
|
|
|
|
#if !defined(ZEROGS_DEVBUILD)
|
|
const char* g_pShaders[4] = { "full", "reduced", "accurate", "accurate-reduced" };
|
|
const char* g_pInterlace[3] = { "interlace 0 |", "interlace 1 |", "" };
|
|
const char* g_pBilinear[3] = { "", "bilinear |", "forced bilinear |" };
|
|
|
|
if (SaveStateFile != NULL && !SaveStateExists)
|
|
SaveStateExists = (access(SaveStateFile, 0) == 0);
|
|
else
|
|
SaveStateExists = true;
|
|
|
|
sprintf(strtitle, "ZZ Open GL 0.%d.%d | %.1f fps | %s%s%s savestate %d%s | shaders %s | (%.1f)", zgsbuild, zgsminor, fFPS,
|
|
g_pInterlace[conf.interlace], g_pBilinear[conf.bilinear], (conf.aa ? s_aa[conf.aa] : ""),
|
|
CurrentSavestate, (SaveStateExists ? "" : "*"),
|
|
g_pShaders[g_nPixelShaderVer], (ppf&0xfffff) / (float)UPDATE_FRAMES);
|
|
|
|
#else
|
|
sprintf(strtitle, "%d | %.1f fps (sk:%d%%) | g: %.1f, t: %.1f, a: %.1f, r: %.1f | p: %.1f | tex: %d %d (%d kbpf)", g_nFrame, fFPS,
|
|
100*g_nFramesSkipped / g_nFrame,
|
|
g_nGenVars / (float)UPDATE_FRAMES, g_nTexVars / (float)UPDATE_FRAMES, g_nAlphaVars / (float)UPDATE_FRAMES,
|
|
g_nResolve / (float)UPDATE_FRAMES, (ppf&0xfffff) / (float)UPDATE_FRAMES,
|
|
g_MemTargs.listTargets.size(), g_MemTargs.listClearedTargets.size(), g_TransferredToGPU >> 10);
|
|
|
|
//_snprintf(strtitle, 512, "%x %x", *(int*)(g_pbyGSMemory + 256 * 0x3e0c + 4), *(int*)(g_pbyGSMemory + 256 * 0x3e04 + 4));
|
|
#endif
|
|
|
|
// if( g_nFrame > 100 && fFPS > 60.0f ) {
|
|
// ZZLog::Debug_Log("Set profile.");
|
|
// g_bWriteProfile = 1;
|
|
// }
|
|
GLWin.SetTitle(strtitle);
|
|
}
|
|
|
|
// This isn't implemented for some reason? Adding a field for it for the moment, till I get a chance to look closer.
|
|
EXPORT_C_(void) GSsetVsync(int enabled)
|
|
{
|
|
FUNCLOG
|
|
|
|
ZZLog::Debug_Log("Setting VSync to 0x%x.", enabled);
|
|
gs.vsync = enabled;
|
|
}
|
|
|
|
EXPORT_C_(void) GSvsync(int current_interlace)
|
|
{
|
|
FUNCLOG
|
|
|
|
//ZZLog::GS_Log("Calling GSvsync.");
|
|
|
|
static u32 dwTime = timeGetTime();
|
|
static int nToNextUpdate = 1;
|
|
#ifdef _DEBUG
|
|
if (conf.dump & 0x1) {
|
|
freezeData fd;
|
|
fd.size = ZZSave(NULL);
|
|
s8* payload = (s8*)malloc(fd.size);
|
|
fd.data = payload;
|
|
|
|
ZZSave(fd.data);
|
|
|
|
char filename[256];
|
|
// FIXME, there is probably a better solution than /tmp ...
|
|
// A possibility will be to save the path from GSmakeSnapshot but you still need to call
|
|
// GSmakeSnapshot first.
|
|
if (get_snapshot_filename(filename, "/tmp", "gs"))
|
|
g_dump.Open(filename, g_LastCRC, fd, g_pBasePS2Mem);
|
|
conf.dump--;
|
|
|
|
free(payload);
|
|
}
|
|
g_dump.VSync(current_interlace, (conf.dump == 0), g_pBasePS2Mem);
|
|
#endif
|
|
|
|
GL_REPORT_ERRORD();
|
|
|
|
g_nRealFrame++;
|
|
|
|
// The value passed seems to either be 0 or 0x2000, and we want 0 or 1. Perhaps !! would be better...
|
|
gs.interlace = !current_interlace;
|
|
RenderCRTC();
|
|
|
|
GLWin.ProcessEvents();
|
|
|
|
if (--nToNextUpdate <= 0)
|
|
{
|
|
u32 d = timeGetTime();
|
|
fFPS = UPDATE_FRAMES * 1000.0f / (float)max(d - dwTime, (u32)1);
|
|
dwTime = d;
|
|
g_nFrame += UPDATE_FRAMES;
|
|
#ifndef USE_GSOPEN2
|
|
// let PCSX2 manage the title
|
|
SetGSTitle();
|
|
#endif
|
|
|
|
// if( g_nFrame > 100 && fFPS > 60.0f ) {
|
|
// ZZLog::Debug_Log("Set profile.");
|
|
// g_bWriteProfile = 1;
|
|
// }
|
|
|
|
if (fFPS < 16)
|
|
UPDATE_FRAMES = 4;
|
|
else if (fFPS < 32)
|
|
UPDATE_FRAMES = 8;
|
|
else
|
|
UPDATE_FRAMES = 16;
|
|
|
|
nToNextUpdate = UPDATE_FRAMES;
|
|
|
|
ppf = 0;
|
|
g_TransferredToGPU = 0;
|
|
g_nGenVars = 0;
|
|
g_nTexVars = 0;
|
|
g_nAlphaVars = 0;
|
|
g_nResolve = 0;
|
|
g_nFramesSkipped = 0;
|
|
g_SkipFlushFrame = 0;
|
|
}
|
|
|
|
#if defined(ZEROGS_DEVBUILD)
|
|
if (g_bWriteProfile)
|
|
{
|
|
//g_bWriteProfile = 0;
|
|
DVProfWrite("prof.txt", UPDATE_FRAMES);
|
|
DVProfClear();
|
|
}
|
|
|
|
#endif
|
|
GL_REPORT_ERRORD();
|
|
|
|
}
|
|
|
|
EXPORT_C_(void) GSreadFIFO(u64 *pMem)
|
|
{
|
|
FUNCLOG
|
|
|
|
//ZZLog::GS_Log("Calling GSreadFIFO.");
|
|
#ifdef _DEBUG
|
|
g_dump.ReadFIFO(1);
|
|
#endif
|
|
|
|
TransferLocalHost((u32*)pMem, 1);
|
|
}
|
|
|
|
EXPORT_C_(void) GSreadFIFO2(u64 *pMem, int qwc)
|
|
{
|
|
FUNCLOG
|
|
|
|
//ZZLog::GS_Log("Calling GSreadFIFO2.");
|
|
#ifdef _DEBUG
|
|
g_dump.ReadFIFO(qwc);
|
|
#endif
|
|
|
|
TransferLocalHost((u32*)pMem, qwc);
|
|
}
|
|
|
|
EXPORT_C_(int) GSsetupRecording(int start, void* pData)
|
|
{
|
|
FUNCLOG
|
|
|
|
if (start)
|
|
StartCapture();
|
|
else
|
|
StopCapture();
|
|
|
|
return 1;
|
|
}
|
|
|
|
EXPORT_C_(s32) GSfreeze(int mode, freezeData *data)
|
|
{
|
|
FUNCLOG
|
|
|
|
switch (mode)
|
|
{
|
|
case FREEZE_LOAD:
|
|
if (!ZZLoad(data->data)) ZZLog::Error_Log("GS: Bad load format!");
|
|
g_nRealFrame += 100;
|
|
break;
|
|
|
|
case FREEZE_SAVE:
|
|
ZZSave(data->data);
|
|
break;
|
|
|
|
case FREEZE_SIZE:
|
|
data->size = ZZSave(NULL);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef __LINUX__
|
|
|
|
struct Packet
|
|
{
|
|
u8 type, param;
|
|
u32 size, addr;
|
|
u32 real_size;
|
|
vector<u32> buff;
|
|
};
|
|
|
|
EXPORT_C_(void) GSReplay(char* lpszCmdLine)
|
|
{
|
|
if(FILE* fp = fopen(lpszCmdLine, "rb"))
|
|
{
|
|
GSinit();
|
|
|
|
u8 regs[0x2000];
|
|
GSsetBaseMem(regs);
|
|
|
|
//s_vsync = !!theApp.GetConfig("vsync", 0);
|
|
|
|
void* hWnd = NULL;
|
|
|
|
//_GSopen((void**)&hWnd, "", renderer);
|
|
GSopen((void**)&hWnd, "", 0);
|
|
|
|
u32 crc;
|
|
fread(&crc, 4, 1, fp);
|
|
GSsetGameCRC(crc, 0);
|
|
|
|
freezeData fd;
|
|
fread(&fd.size, 4, 1, fp);
|
|
fd.data = new s8[fd.size];
|
|
fread(fd.data, fd.size, 1, fp);
|
|
GSfreeze(FREEZE_LOAD, &fd);
|
|
delete [] fd.data;
|
|
|
|
fread(regs, 0x2000, 1, fp);
|
|
|
|
long start = ftell(fp);
|
|
|
|
GSvsync(1);
|
|
|
|
list<Packet*> packets;
|
|
vector<u8> buff;
|
|
int type;
|
|
|
|
while((type = fgetc(fp)) != EOF)
|
|
{
|
|
Packet* p = new Packet();
|
|
|
|
p->type = (u8)type;
|
|
|
|
switch(type)
|
|
{
|
|
case 0:
|
|
|
|
p->param = (u8)fgetc(fp);
|
|
|
|
fread(&p->size, 4, 1, fp);
|
|
fread(&p->real_size, 4, 1, fp);
|
|
|
|
switch(p->param)
|
|
{
|
|
case 0:
|
|
p->buff.resize(0x4000);
|
|
//p->addr = 0x4000 - p->size;
|
|
//fread(&p->buff[p->addr], p->size, 1, fp);
|
|
fread(&p->buff[0], p->size, 1, fp);
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
p->buff.resize(p->size);
|
|
fread(&p->buff[0], p->size, 1, fp);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
fread(&p->param, 4, 1, fp);
|
|
//p->param = (u8)fgetc(fp);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
fread(&p->size, 4, 1, fp);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
p->buff.resize(0x2000);
|
|
|
|
fread(&p->buff[0], 0x2000, 1, fp);
|
|
|
|
break;
|
|
|
|
default: assert(0);
|
|
}
|
|
|
|
packets.push_back(p);
|
|
}
|
|
|
|
sleep(1);
|
|
|
|
//while(IsWindowVisible(hWnd))
|
|
//FIXME map?
|
|
int finished = 2;
|
|
while(finished > 0)
|
|
{
|
|
unsigned long start = timeGetTime();
|
|
unsigned long frame_number = 0;
|
|
for(list<Packet*>::iterator i = packets.begin(); i != packets.end(); i++)
|
|
{
|
|
Packet* p = *i;
|
|
|
|
switch(p->type)
|
|
{
|
|
case 0:
|
|
|
|
//fprintf(stderr, "TRANSFER %d size %d\n", p->param, p->real_size);
|
|
switch(p->param)
|
|
{
|
|
//case 0: GSgifTransfer1((u32*)&p->buff[0], p->addr); break;
|
|
case 0: _GSgifTransfer<0>(&p->buff[0], p->real_size); break;
|
|
case 1: GSgifTransfer2((u32*)&p->buff[0], p->real_size); break;
|
|
case 2: GSgifTransfer3((u32*)&p->buff[0], p->real_size); break;
|
|
case 3: GSgifTransfer((u32*)&p->buff[0], p->real_size); break;
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
GSvsync(p->param);
|
|
frame_number++;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if(buff.size() < p->size) buff.resize(p->size);
|
|
|
|
// FIXME
|
|
// GSreadFIFO2(&buff[0], p->size / 16);
|
|
fprintf(stderr, "GSreadFIFO2 not yet implemented");
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
memcpy(regs, &p->buff[0], 0x2000);
|
|
|
|
break;
|
|
}
|
|
}
|
|
unsigned long end = timeGetTime();
|
|
fprintf(stderr, "The %ld frames of the scene was render on %ldms\n", frame_number, end - start);
|
|
fprintf(stderr, "A means of %fms by frame (limit 16ms/f)\n", (float)(end - start)/(float)frame_number);
|
|
|
|
sleep(1);
|
|
finished--;
|
|
}
|
|
|
|
|
|
for(list<Packet*>::iterator i = packets.begin(); i != packets.end(); i++)
|
|
{
|
|
delete *i;
|
|
}
|
|
|
|
packets.clear();
|
|
|
|
sleep(1);
|
|
|
|
GSclose();
|
|
GSshutdown();
|
|
|
|
fclose(fp);
|
|
}
|
|
}
|
|
#endif
|