summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSND\ckain_cp <SND\ckain_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97>2014-01-24 13:59:46 +0000
committerSND\ckain_cp <SND\ckain_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97>2014-01-24 13:59:46 +0000
commite1108dba69ad14fed7bc64f996ea09342c900a81 (patch)
treea3798c4031cce49eeb5964ec9ae49a9627bc5a91
parent47b0777b946e413500bc0420d91298aa4e8f8301 (diff)
downloadpcsxr-e1108dba69ad14fed7bc64f996ea09342c900a81.tar.gz
Added rewind feature. Currently only supported via GTK and needs SHM. Rewind save state depth is configured via RewindCount config param. Recommended value is 200 or so -> uses less than 1G of memory
git-svn-id: https://pcsxr.svn.codeplex.com/svn/pcsxr@88433 e17a0e51-4ae3-4d35-97c3-1a29b211df97
-rw-r--r--configure.ac1
-rwxr-xr-xgui/Config.c2
-rwxr-xr-xgui/Linux.h2
-rwxr-xr-xgui/LnxMain.c14
-rwxr-xr-xgui/Plugin.c30
-rwxr-xr-xlibpcsxcore/misc.c177
-rwxr-xr-xlibpcsxcore/misc.h8
-rwxr-xr-xlibpcsxcore/psxcommon.h1
-rwxr-xr-xplugins/dfinput/cfg-gtk.c5
-rwxr-xr-xplugins/dfinput/cfg.c9
-rwxr-xr-xplugins/dfinput/pad.h1
11 files changed, 205 insertions, 45 deletions
diff --git a/configure.ac b/configure.ac
index b82248a4..0f6d6508 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,6 +57,7 @@ AC_CHECK_LIB(dl, dlsym, [LIBS="$LIBS -ldl"], [])
AC_CHECK_LIB(socket, socket, [LIBS="$LIBS -lsocket"], [])
AC_CHECK_LIB(nsl, gethostbyname, [LIBS="$LIBS -lnsl"], [])
AC_CHECK_LIB(umem, umem_alloc, [LIBS="$LIBS -lumem"], [])
+AC_CHECK_LIB(shm, shm_open, [LIBS="$LIBS -lrt"], [AC_DEFINE([NO_RT_SHM], [1], []])
AM_CONDITIONAL(SOUND_OSS, false)
AM_CONDITIONAL(SOUND_SDL, false)
diff --git a/gui/Config.c b/gui/Config.c
index 1f370a82..60c2b4cc 100755
--- a/gui/Config.c
+++ b/gui/Config.c
@@ -142,6 +142,7 @@ int LoadConfig(PcsxConfig *Conf) {
Config.Cpu = GetValuel(data, "Cpu");
Config.PsxType = GetValuel(data, "PsxType");
+ Config.RewindCount = GetValuel(data, "RewindCount");
free(data);
@@ -189,6 +190,7 @@ void SaveConfig() {
SetValuel("Cpu", Config.Cpu);
SetValuel("PsxType", Config.PsxType);
+ SetValuel("RewindCount", Config.RewindCount);
fclose(f);
}
diff --git a/gui/Linux.h b/gui/Linux.h
index 791f103b..0d2d1493 100755
--- a/gui/Linux.h
+++ b/gui/Linux.h
@@ -68,4 +68,6 @@ void UpdatePluginsBIOS();
void SysErrorMessage(gchar *primary, gchar *secondary);
void SysInfoMessage(gchar *primary, gchar *secondary);
+extern u8 rew_timer;
+
#endif /* __LINUX_H__ */
diff --git a/gui/LnxMain.c b/gui/LnxMain.c
index 14c295cd..67cb75ac 100755
--- a/gui/LnxMain.c
+++ b/gui/LnxMain.c
@@ -502,6 +502,8 @@ void SysClose() {
EmuShutdown();
ReleasePlugins();
+ CleanupMemSaveStates();
+
StopDebugger();
if (emuLog != NULL) fclose(emuLog);
@@ -573,11 +575,17 @@ static void SysDisableScreenSaver() {
}
}
+u8 rew_timer = 0u; // TODO: change to scaled ms based or psxcycle based
void SysUpdate() {
- PADhandleKey(PAD1_keypressed());
- PADhandleKey(PAD2_keypressed());
+ PADhandleKey(PAD1_keypressed() );
+ PADhandleKey(PAD2_keypressed() );
+
+ if (Config.RewindCount > 0 && rew_timer++ > 35) {
+ CreateRewindState();
+ rew_timer = 0;
+ }
- SysDisableScreenSaver();
+ //SysDisableScreenSaver();
}
/* ADB TODO Replace RunGui() with StartGui ()*/
diff --git a/gui/Plugin.c b/gui/Plugin.c
index 0c7901f7..05b10c58 100755
--- a/gui/Plugin.c
+++ b/gui/Plugin.c
@@ -60,8 +60,8 @@ void gpuShowPic() {
f = gzopen(state_filename, "rb");
if (f != NULL) {
gzseek(f, 32, SEEK_SET); // skip header
- gzseek(f, sizeof(u32), SEEK_CUR);
- gzseek(f, sizeof(boolean), SEEK_CUR);
+ gzseek(f, sizeof(u32), SEEK_CUR);
+ gzseek(f, sizeof(boolean), SEEK_CUR);
gzread(f, pMem, 128*96*3);
gzclose(f);
} else {
@@ -103,8 +103,9 @@ void KeyStateLoad(int i) {
}
// todo: make toggle config param
-static short modctrl = 0, modalt = 0, toggle = 0, pressed = 0;
-int lastpressed = 0;
+static s16 modctrl = 0, modalt = 0, toggle = 0, pressed = 0;
+s32 lastpressed = 0;
+time_t tslastpressed = 0;
/* Handle keyboard keystrokes */
void PADhandleKey(int key) {
@@ -113,7 +114,8 @@ void PADhandleKey(int key) {
short rel = 0; //released key flag
- if (key == 0 || key == lastpressed)
+ // Allow rewind key to repeat
+ if (key == 0 || (key == lastpressed && key != XK_BackSpace))
return;
if ((key >> 30) & 1) //specific to dfinput (padJoy)
@@ -284,7 +286,15 @@ void PADhandleKey(int key) {
case XK_F12:
psxReset();
break;
- case XK_Escape:
+ case XK_BackSpace:
+ rew_timer = 0;
+ time_t now = clock();
+ u32 millis = (((now - tslastpressed) * 1000) / CLOCKS_PER_SEC);
+ if (millis <= 130) break;
+ tslastpressed = now;
+ RewindState();
+ break;
+ case XK_Escape:
// TODO
// the architecture is too broken to actually restart the GUI
// because SysUpdate is called from deep within the actual
@@ -345,12 +355,12 @@ int _OpenPlugins() {
if (ret < 0) { SysMessage(_("Error opening GPU plugin!")); return -1; }
ret = PAD1_open(&gpuDisp);
if (ret < 0) { SysMessage(_("Error opening Controller 1 plugin!")); return -1; }
- PAD1_registerVibration(GPU_visualVibration);
- PAD1_registerCursor(GPU_cursor);
+ PAD1_registerVibration(GPU_visualVibration);
+ PAD1_registerCursor(GPU_cursor);
ret = PAD2_open(&gpuDisp);
if (ret < 0) { SysMessage(_("Error opening Controller 2 plugin!")); return -1; }
- PAD2_registerVibration(GPU_visualVibration);
- PAD2_registerCursor(GPU_cursor);
+ PAD2_registerVibration(GPU_visualVibration);
+ PAD2_registerCursor(GPU_cursor);
#ifdef ENABLE_SIO1API
ret = SIO1_open(&gpuDisp);
if (ret < 0) { SysMessage(_("Error opening SIO1 plugin!")); return -1; }
diff --git a/libpcsxcore/misc.c b/libpcsxcore/misc.c
index 7918466a..34da0ed5 100755
--- a/libpcsxcore/misc.c
+++ b/libpcsxcore/misc.c
@@ -499,7 +499,8 @@ int Load(const char *ExePath) {
}
// STATES
-
+static const u8 PCSXR_HEADER_SZ = 10U;
+static const u32 SZ_GPUPIC = 128 * 96 * 3;
static const char PcsxrHeader[32] = "STv4 PCSXR v" PACKAGE_VERSION;
// Savestate Versioning!
@@ -508,23 +509,143 @@ static const u32 SaveVersion = 0x8b410008;
int SaveState(const char *file) {
gzFile f;
- GPUFreeze_t *gpufP;
+ long size;
+
+ f = gzopen(file, "wb9"); // Best ratio but slow
+ if (f == NULL) return -1;
+ return SaveStateGz(f, &size);
+}
+
+int LoadState(const char *file) {
+ gzFile f;
+
+ f = gzopen(file, "rb");
+ if (f == NULL) return -1;
+ return LoadStateGz(f);
+}
+
+u32 mem_cur_save_count=0, mem_last_save;
+boolean mem_wrapped=FALSE; // Whether we went past max count and restarted counting
+
+void CreateRewindState() {
+ SaveStateMem(mem_last_save=mem_cur_save_count++);
+
+ if (mem_cur_save_count > Config.RewindCount) {
+ mem_cur_save_count = 0;
+ mem_wrapped=TRUE;
+ }
+}
+
+void RewindState() {
+ mem_cur_save_count--;
+ if (mem_cur_save_count > Config.RewindCount && mem_wrapped) {
+ mem_cur_save_count = Config.RewindCount;
+ mem_wrapped = FALSE;
+ } else if (mem_cur_save_count > Config.RewindCount && !mem_wrapped) {
+ mem_cur_save_count++;
+ return;
+ } else if (mem_last_save == mem_cur_save_count-1) {
+ mem_cur_save_count = 0;
+ return;
+ }
+ LoadStateMem(mem_cur_save_count);
+}
+
+/*
+
+Pros of using SHM
++ No need to change SaveState interface (gzip OK)
++ Possibiliy to preserve saves after pcsxr crash
+
+Cons of using SHM
+- UNIX only
+- Possibility of leaving left over shm files
+- Possibly not the quickest way to allocate memory
+
+*/
+#ifndef NO_RT_SHM
+#include <sys/mman.h>
+#include <sys/stat.h> /* For mode constants */
+#include <fcntl.h> /* For O_* constants */
+#include <errno.h>
+
+#define SHM_SS_NAME_TEMPLATE "/pcsxrmemsavestate%.4u"
+
+int SaveStateMem(const u32 id) {
+ char name[32];
+ int ret = -1;
+
+ snprintf(name, 32, SHM_SS_NAME_TEMPLATE, id);
+ int fd = shm_open(name, O_CREAT | O_RDWR | O_TRUNC, 0666);
+
+ if (fd >= 0) {
+ gzFile f = gzdopen(fd, "wb0R"); // Fast and no compression
+ if (f != NULL) {
+ long size;
+ ret = SaveStateGz(f, &size);
+ //printf("Saved %s/%i (ID: %i SZ: %lik)\n", name, fd, id, size/1024);
+ } else {
+ SysMessage("GZ OPEN FAIL %i\n", errno );
+ }
+ } else {
+ SysMessage("FD OPEN FAIL %i\n", errno );
+ }
+ return ret;
+}
+
+int LoadStateMem(const u32 id) {
+ char name[32];
+ int ret = -1;
+
+ snprintf(name, 32, SHM_SS_NAME_TEMPLATE, id);
+ int fd = shm_open(name, O_RDONLY, 0444);
+
+ if (fd >= 0) {
+ gzFile f = gzdopen(fd, "rb");
+ if (f != NULL) {
+ ret = LoadStateGz(f);
+ //printf("Loaded %s/%i (ID: %i RET: %i)\n", name, fd, id, ret);
+ shm_unlink(name);
+ } else {
+ SysMessage("GZ OPEN FAIL %i\n", errno);
+ }
+ } else {
+ SysMessage("FD OPEN FAIL %i (%s)\n", errno, name);
+ }
+ return ret;
+}
+
+void CleanupMemSaveStates() {
+ char name[32];
+ u32 i;
+
+ for (i=0; i <= Config.RewindCount; i++) {
+ snprintf(name, sizeof(name), SHM_SS_NAME_TEMPLATE, i);
+ if (shm_unlink(name) != 0) {
+ //break;
+ }
+ }
+}
+#else
+int SaveStateMem(const u32 id) {return 0;}
+int LoadStateMem(const u32 id) {return 0;}
+void CleanupMemSaveStates() {}
+#endif
+
+int SaveStateGz(gzFile f, long* gzsize) {
+ GPUFreeze_t gpufP;
SPUFreeze_t *spufP;
int Size;
- unsigned char *pMem;
+ unsigned char pMemGpuPic[SZ_GPUPIC], pMemSpuT[16];
- f = gzopen(file, "wb");
if (f == NULL) return -1;
- gzwrite(f, (void *)PcsxrHeader, 32);
+ gzwrite(f, (void *)PcsxrHeader, sizeof(PcsxrHeader));
gzwrite(f, (void *)&SaveVersion, sizeof(u32));
gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
- pMem = (unsigned char *)malloc(128 * 96 * 3);
- if (pMem == NULL) return -1;
- GPU_getScreenPic(pMem);
- gzwrite(f, pMem, 128 * 96 * 3);
- free(pMem);
+ GPU_getScreenPic(pMemGpuPic);
+ gzwrite(f, pMemGpuPic, SZ_GPUPIC);
if (Config.HLE)
psxBiosFreeze(1);
@@ -535,19 +656,16 @@ int SaveState(const char *file) {
gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
// gpu
- gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
- gpufP->ulFreezeVersion = 1;
- GPU_freeze(1, gpufP);
- gzwrite(f, gpufP, sizeof(GPUFreeze_t));
- free(gpufP);
+ gpufP.ulFreezeVersion = 1;
+ GPU_freeze(1, &gpufP);
+ gzwrite(f, &gpufP, sizeof(GPUFreeze_t));
// spu
- spufP = (SPUFreeze_t *) malloc(16); // only first 3 elements (up to Size)
+ spufP = (SPUFreeze_t *)pMemSpuT; // only first 3 elements (up to Size)
SPU_freeze(2, spufP);
Size = spufP->Size; gzwrite(f, &Size, 4);
if (Size <= 0)
return 1; // error
- free(spufP);
spufP = (SPUFreeze_t *) malloc(Size);
SPU_freeze(1, spufP);
gzwrite(f, spufP, Size);
@@ -559,34 +677,34 @@ int SaveState(const char *file) {
psxRcntFreeze(f, 1);
mdecFreeze(f, 1);
+ *gzsize = gztell(f);
gzclose(f);
return 0;
}
-int LoadState(const char *file) {
- gzFile f;
- GPUFreeze_t *gpufP;
+int LoadStateGz(gzFile f) {
+ GPUFreeze_t gpufP;
SPUFreeze_t *spufP;
int Size;
- char header[32];
+ char header[sizeof(PcsxrHeader)];
u32 version;
boolean hle;
- f = gzopen(file, "rb");
if (f == NULL) return -1;
gzread(f, header, sizeof(header));
gzread(f, &version, sizeof(u32));
gzread(f, &hle, sizeof(boolean));
- if (strncmp("STv4 PCSXR", header, 10) != 0 || version != SaveVersion || hle != Config.HLE) {
+ // Compare header only "STv4 PCSXR" part no version
+ if (strncmp(PcsxrHeader, header, PCSXR_HEADER_SZ) != 0 || version != SaveVersion || hle != Config.HLE) {
gzclose(f);
return -1;
}
psxCpu->Reset();
- gzseek(f, 128 * 96 * 3, SEEK_CUR);
+ gzseek(f, SZ_GPUPIC, SEEK_CUR);
gzread(f, psxM, 0x00200000);
gzread(f, psxR, 0x00080000);
@@ -597,10 +715,8 @@ int LoadState(const char *file) {
psxBiosFreeze(0);
// gpu
- gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
- gzread(f, gpufP, sizeof(GPUFreeze_t));
- GPU_freeze(0, gpufP);
- free(gpufP);
+ gzread(f, &gpufP, sizeof(GPUFreeze_t));
+ GPU_freeze(0, &gpufP);
// spu
gzread(f, &Size, 4);
@@ -622,7 +738,7 @@ int LoadState(const char *file) {
int CheckState(const char *file) {
gzFile f;
- char header[32];
+ char header[sizeof(PcsxrHeader)];
u32 version;
boolean hle;
@@ -635,7 +751,8 @@ int CheckState(const char *file) {
gzclose(f);
- if (strncmp("STv4 PCSXR", header, 10) != 0 || version != SaveVersion || hle != Config.HLE)
+ // Compare header only "STv4 PCSXR" part no version
+ if (strncmp(PcsxrHeader, header, PCSXR_HEADER_SZ) != 0 || version != SaveVersion || hle != Config.HLE)
return -1;
return 0;
diff --git a/libpcsxcore/misc.h b/libpcsxcore/misc.h
index 626cf5c5..860c1d29 100755
--- a/libpcsxcore/misc.h
+++ b/libpcsxcore/misc.h
@@ -62,12 +62,20 @@ int CheckCdrom();
int Load(const char *ExePath);
int SaveState(const char *file);
+int SaveStateMem(const u32 id);
+int SaveStateGz(gzFile f, long* gzsize);
int LoadState(const char *file);
+int LoadStateMem(const u32 id);
+int LoadStateGz(gzFile f);
int CheckState(const char *file);
int SendPcsxInfo();
int RecvPcsxInfo();
+void CreateRewindState(); // Creates save state and stores it to volatile memory
+void RewindState(); // Restores state previously created with CreateRewindState();
+void CleanupMemSaveStates(); // Removes all save states stored by memory funcs like CreateRewindState()
+
void trim(char *str);
u16 calcCrc(u8 *d, int len);
diff --git a/libpcsxcore/psxcommon.h b/libpcsxcore/psxcommon.h
index bd12ef51..16428115 100755
--- a/libpcsxcore/psxcommon.h
+++ b/libpcsxcore/psxcommon.h
@@ -157,6 +157,7 @@ typedef struct {
s32 WindowPos[2];
u8 Cpu; // CPU_DYNAREC or CPU_INTERPRETER
u8 PsxType; // PSX_TYPE_NTSC or PSX_TYPE_PAL
+ u32 RewindCount;
#ifdef _WIN32
char Lang[256];
#endif
diff --git a/plugins/dfinput/cfg-gtk.c b/plugins/dfinput/cfg-gtk.c
index 7b5df6c2..80443f3a 100755
--- a/plugins/dfinput/cfg-gtk.c
+++ b/plugins/dfinput/cfg-gtk.c
@@ -60,7 +60,8 @@ const char *EmuKeyText[EMU_TOTAL] = {
N_("Load state"),
N_("Save state"),
N_("Screenshot"),
- N_("Escape")
+ N_("Escape"),
+ N_("Rewind")
};
const char *DPadText[DKEY_TOTAL] = {
@@ -403,7 +404,7 @@ static void ReadDKeyEvent(int padnum, int key) {
for (i = 0; i < numAxes; i++) {
axis = SDL_JoystickGetAxis(js, i);
- if (abs(axis) > 16383 && (abs(axis - InitAxisPos[i]) > 4096 || abs(axis - PrevAxisPos[i]) > 4096)) {
+ if (abs(axis) > 16383 && (abs(axis - InitAxisPos[i]) > 4096 || abs(axis - PrevAxisPos[i]) > 4096) && (abs(axis) < 32768)) {
keydef->JoyEvType = AXIS;
keydef->J.Axis = (i + 1) * (axis > 0 ? 1 : -1);
goto end;
diff --git a/plugins/dfinput/cfg.c b/plugins/dfinput/cfg.c
index 6e45a122..2757bd74 100755
--- a/plugins/dfinput/cfg.c
+++ b/plugins/dfinput/cfg.c
@@ -125,6 +125,7 @@ static void SetDefaultConfig() {
g.cfg.E.EmuDef[EMU_INCREMENTSTATE].EmuKeyEvent = XK_F2;
g.cfg.E.EmuDef[EMU_SCREENSHOT].EmuKeyEvent = XK_F8;
g.cfg.E.EmuDef[EMU_ESCAPE].EmuKeyEvent = XK_Escape;
+ g.cfg.E.EmuDef[EMU_REWIND].EmuKeyEvent = XK_BackSpace;
}
void LoadPADConfig() {
@@ -193,6 +194,11 @@ void LoadPADConfig() {
g.cfg.E.EmuDef[EMU_ESCAPE].Mapping.Key = a;
g.cfg.E.EmuDef[EMU_ESCAPE].Mapping.JoyEvType = b;
g.cfg.E.EmuDef[EMU_ESCAPE].Mapping.J.d = c;
+ } else if (strncmp(buf, "EMU_REWIND=", 11) == 0) {
+ sscanf(buf, "EMU_REWIND=%d,%d,%d", &a, &b, &c);
+ g.cfg.E.EmuDef[EMU_REWIND].Mapping.Key = a;
+ g.cfg.E.EmuDef[EMU_REWIND].Mapping.JoyEvType = b;
+ g.cfg.E.EmuDef[EMU_REWIND].Mapping.J.d = c;
} else if (strncmp(buf, "Select=", 7) == 0) {
sscanf(buf, "Select=%d,%d,%d", &a, &b, &c);
g.cfg.PadDef[current].KeyDef[DKEY_SELECT].Key = a;
@@ -428,5 +434,8 @@ void SavePADConfig() {
fprintf(fp, "EMU_ESCAPE=%d,%d,%d\n", g.cfg.E.EmuDef[EMU_ESCAPE].Mapping.Key,
g.cfg.E.EmuDef[EMU_ESCAPE].Mapping.JoyEvType,
g.cfg.E.EmuDef[EMU_ESCAPE].Mapping.J.d);
+ fprintf(fp, "EMU_REWIND=%d,%d,%d\n", g.cfg.E.EmuDef[EMU_REWIND].Mapping.Key,
+ g.cfg.E.EmuDef[EMU_REWIND].Mapping.JoyEvType,
+ g.cfg.E.EmuDef[EMU_REWIND].Mapping.J.d);
fclose(fp);
}
diff --git a/plugins/dfinput/pad.h b/plugins/dfinput/pad.h
index 9e72bf88..205bae41 100755
--- a/plugins/dfinput/pad.h
+++ b/plugins/dfinput/pad.h
@@ -119,6 +119,7 @@ enum {
EMU_SAVESTATE,
EMU_SCREENSHOT,
EMU_ESCAPE,
+ EMU_REWIND,
EMU_TOTAL
};