Load Save State issue

OK so. After trying to set the memory I ran into several problems. At 1st I thought it was because I tried to change too many things to fast. And then after walking back several changes The reason seemed to be because Save States were not loading at all. The main reason I can think of for this is because the version I ForkedHad an issue with it.

I think there are several ways to do with This. One would be to look for a different version to fork. And another will be the right tools for looking at save states directly. As far as looking at save States directly I mean is you is looking into the Decompression. No in general would make more sense to use a version that actually works. So I think I’m going to look into the apt-get version and find out where that repository is.

https://github.com/pcsxr/PCSX-Reloaded

Edit:

After a few tests I found that trying to move around the order so that the system memory comes at the start of the save state has the side effect that save states don’t seem to be listed when loading. So there could be some check that happens ahead of time to throw these away. Not sure if I want to track down this process or not.

And then I also find that the 10th save state tends to be pretty annoying. The 10th state seems to a backup state that is automatically created when the emulation had stopped to be able to restore the state again. So I might try and check the last three characters of the save state, and if it’s ‘010’, then skip and don’t write a state.

Pcsx-Reloaded Memory Debug Edition

I’ve finally had a tiny bit of time to put into looking into PSX graphics again. And I think it would be a good idea to fork pcsx-r to try and re-write the save state writing and save state loading to make it easier to debug system memory. The cloned version is over at https://gitlab.com/kion-dgl/pcsxr-mem.

The code responsible for reading and writing states is libpcsxcore/misc.c.

int SaveState(const char *file) {
    gzFile f;
    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);
}

We have two functions SaveState and LoadState. And they both use a gxFile object type to create a compressed snapshot of memory to save as a state. We really don’t need this to be compressed. An easy change would probably be to change the compression from a 9 to a 0 for no compression. But I’m also tempted to change the order the file is written to make editing and reading easier.

nt SaveStateGz(gzFile f, long* gzsize) {
	int Size;
	unsigned char pMemGpuPic[SZ_GPUPIC];

	//if (f == NULL) return -1;

	gzwrite(f, (void *)PcsxrHeader, sizeof(PcsxrHeader));
	gzwrite(f, (void *)&SaveVersion, sizeof(u32));
	gzwrite(f, (void *)&Config.HLE, sizeof(boolean));

	if (gzsize)GPU_getScreenPic(pMemGpuPic); // Not necessary with ephemeral saves
	gzwrite(f, pMemGpuPic, SZ_GPUPIC);

	if (Config.HLE)
		psxBiosFreeze(1);

	gzwrite(f, psxM, 0x00200000);
	gzwrite(f, psxR, 0x00080000);
	gzwrite(f, psxH, 0x00010000);
	gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));

	// gpu
	if (!gpufP)gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
	gpufP->ulFreezeVersion = 1;
	GPU_freeze(1, gpufP);
	gzwrite(f, gpufP, sizeof(GPUFreeze_t));

	// SPU Plugin cannot change during run, so we query size info just once per session
	if (!spufP) {
		spufP = (SPUFreeze_t *)malloc(offsetof(SPUFreeze_t, SPUPorts)); // only first 3 elements (up to Size)        
		SPU_freeze(2, spufP);
		Size = spufP->Size;
		SysPrintf("SPUFreezeSize %i/(%i)\n", Size, offsetof(SPUFreeze_t, SPUPorts));
		free(spufP);
		spufP = (SPUFreeze_t *) malloc(Size);
		spufP->Size = Size;

		if (spufP->Size <= 0) {
			gzclose(f);
			free(spufP);
			spufP = NULL;
			return 1; // error
		}
	}
	// spu
	gzwrite(f, &(spufP->Size), 4);
	SPU_freeze(1, spufP);
	gzwrite(f, spufP, spufP->Size);

	sioFreeze(f, 1);
	cdrFreeze(f, 1);
	psxHwFreeze(f, 1);
	psxRcntFreeze(f, 1);
	mdecFreeze(f, 1);

	if(gzsize)*gzsize = gztell(f);
	gzclose(f);

	return 0;
}

Here is the save state. And I think we’re interested in system memory and the framebuffer specifically. So we we’re interested in:

gzwrite(f, psxM, 0x00200000); 
...
// gpu
if (!gpufP)gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
gpufP->ulFreezeVersion = 1;
GPU_freeze(1, gpufP);
gzwrite(f, gpufP, sizeof(GPUFreeze_t));

And I think if we turn off compression and move these to the start of the function, we should get an uncompressed save state with memory for the first 2MB, followed by vram for the next 1MB, and then everything else thrown in after that. But then we need to make sure the Load state function also matches.

int LoadStateGz(gzFile f) {
	SPUFreeze_t *_spufP;
	int Size;
	char header[sizeof(PcsxrHeader)];
	u32 version;
	boolean hle;

	if (f == NULL) return -1;

	gzread(f, header, sizeof(header));
	gzread(f, &version, sizeof(u32));
	gzread(f, &hle, sizeof(boolean));

	// 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, SZ_GPUPIC, SEEK_CUR);

	gzread(f, psxM, 0x00200000);
	gzread(f, psxR, 0x00080000);
	gzread(f, psxH, 0x00010000);
	gzread(f, (void *)&psxRegs, sizeof(psxRegs));

	if (Config.HLE)
		psxBiosFreeze(0);

	// gpu
	if (!gpufP)gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
	gzread(f, gpufP, sizeof(GPUFreeze_t));
	GPU_freeze(0, gpufP);

	// spu
	gzread(f, &Size, 4);
	_spufP = (SPUFreeze_t *)malloc(Size);
	gzread(f, _spufP, Size);
	SPU_freeze(0, _spufP);
	free(_spufP);

	sioFreeze(f, 0);
	cdrFreeze(f, 0);
	psxHwFreeze(f, 0);
	psxRcntFreeze(f, 0);
	mdecFreeze(f, 0);

	gzclose(f);

	return 0;
}

In which case we also need to move the read system ram and vram to the start of the function.

gzread(f, psxM, 0x00200000);
...
// gpu
if (!gpufP)gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
gzread(f, gpufP, sizeof(GPUFreeze_t));
GPU_freeze(0, gpufP);

That means the version checks for the state state version will come after, but I think that should be okay. So we might as well give this a shot and see if it works.