pcsx2/pcsx2/SPU2/spu2freeze.cpp

189 lines
5.4 KiB
C++

/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "SPU2/Global.h"
#include "SPU2/spu2.h" // hopefully temporary, until I resolve lClocks depdendency
#include "IopMem.h"
namespace SPU2Savestate
{
// Arbitrary ID to identify SPU2 saves.
static const u32 SAVE_ID = 0x1227521;
// versioning for saves.
// Increment this when changes to the savestate system are made.
static const u32 SAVE_VERSION = 0x000e;
static void wipe_the_cache()
{
memset(pcm_cache_data, 0, pcm_BlockCount * sizeof(PcmCacheEntry));
}
} // namespace SPU2Savestate
struct SPU2Savestate::DataBlock
{
u32 spu2id; // SPU2 state identifier lets ZeroGS/PeopsSPU2 know this isn't their state)
u8 unkregs[0x10000]; // SPU2 raw register memory
u8 mem[0x200000]; // SPU2 raw sample memory
u32 version; // SPU2 version identifier
V_Core Cores[2];
V_SPDIF Spdif;
u16 OutPos;
u16 InputPos;
u32 Cycles;
u32 lClocks;
int PlayMode;
};
s32 SPU2Savestate::FreezeIt(DataBlock& spud)
{
spud.spu2id = SAVE_ID;
spud.version = SAVE_VERSION;
pxAssertMsg(spu2regs && _spu2mem, "Looks like PCSX2 is trying to savestate while components are shut down. That's a no-no! It shouldn't crash, but the savestate will probably be corrupted.");
if (spu2regs != nullptr)
memcpy(spud.unkregs, spu2regs, sizeof(spud.unkregs));
if (_spu2mem != nullptr)
memcpy(spud.mem, _spu2mem, sizeof(spud.mem));
memcpy(spud.Cores, Cores, sizeof(Cores));
memcpy(&spud.Spdif, &Spdif, sizeof(Spdif));
// Convert pointers to offsets so we can safely restore them when loading.
// We use -1 for null, and anything else as an offset from iop memory.
#define FIX_POINTER(x) \
if (!(x)) \
{ \
x = reinterpret_cast<decltype(x)>(-1); \
} \
else \
{ \
pxAssert(reinterpret_cast<const u8*>((x)) >= iopPhysMem(0) && reinterpret_cast<const u8*>((x)) < iopPhysMem(0x1fffff)); \
x = reinterpret_cast<decltype(x)>(reinterpret_cast<const u8*>((x)) - iopPhysMem(0)); \
}
for (u32 i = 0; i < 2; i++)
{
V_Core& core = spud.Cores[i];
FIX_POINTER(core.DMAPtr);
FIX_POINTER(core.DMARPtr);
}
#undef FIX_POINTER
spud.OutPos = OutPos;
spud.InputPos = InputPos;
spud.Cycles = Cycles;
spud.lClocks = lClocks;
spud.PlayMode = PlayMode;
// note: Don't save the cache. PCSX2 doesn't offer a safe method of predicting
// the required size of the savestate prior to saving, plus this is just too
// "implementation specific" for the intended spec of a savestate. Let's just
// force the user to rebuild their cache instead.
return 0;
}
s32 SPU2Savestate::ThawIt(DataBlock& spud)
{
if (spud.spu2id != SAVE_ID || spud.version < SAVE_VERSION)
{
fprintf(stderr, "\n*** SPU2 Warning:\n");
if (spud.spu2id == SAVE_ID)
fprintf(stderr, "\tSavestate version is from an older version of PCSX2.\n");
else
fprintf(stderr, "\tThe savestate you are trying to load is incorrect or corrupted.\n");
fprintf(stderr,
"\tAudio may not recover correctly. Save your game to memorycard, reset,\n\n"
"\tand then continue from there.\n\n");
// Do *not* reset the cores.
// We'll need some "hints" as to how the cores should be initialized, and the
// only way to get that is to use the game's existing core settings and hope
// they kinda match the settings for the savestate (IRQ enables and such).
// adpcm cache : Clear all the cache flags and buffers.
wipe_the_cache();
}
else
{
SndBuffer::ClearContents();
pxAssertMsg(spu2regs && _spu2mem, "Looks like PCSX2 is trying to loadstate while components are shut down. That's a no-no! It shouldn't crash, but the savestate will probably be corrupted.");
// base stuff
if (spu2regs)
memcpy(spu2regs, spud.unkregs, sizeof(spud.unkregs));
if (_spu2mem)
memcpy(_spu2mem, spud.mem, sizeof(spud.mem));
memcpy(Cores, spud.Cores, sizeof(Cores));
memcpy(&Spdif, &spud.Spdif, sizeof(Spdif));
// Reverse the pointer offset from above.
#define FIX_POINTER(x) \
if ((x) == reinterpret_cast<decltype(x)>(-1)) \
{ \
x = nullptr; \
} \
else \
{ \
pxAssert(reinterpret_cast<size_t>((x)) <= 0x1fffff); \
x = reinterpret_cast<decltype(x)>(iopPhysMem(0) + reinterpret_cast<size_t>((x))); \
}
for (u32 i = 0; i < 2; i++)
{
V_Core& core = spud.Cores[i];
FIX_POINTER(core.DMAPtr);
FIX_POINTER(core.DMARPtr);
}
#undef FIX_POINTER
OutPos = spud.OutPos;
InputPos = spud.InputPos;
Cycles = spud.Cycles;
lClocks = spud.lClocks;
PlayMode = spud.PlayMode;
wipe_the_cache();
// Go through the V_Voice structs and recalculate SBuffer pointer from
// the NextA setting.
for (int c = 0; c < 2; c++)
{
for (int v = 0; v < 24; v++)
{
const int cacheIdx = Cores[c].Voices[v].NextA / pcm_WordsPerBlock;
Cores[c].Voices[v].SBuffer = pcm_cache_data[cacheIdx].Sampledata;
}
}
}
return 0;
}
s32 SPU2Savestate::SizeIt()
{
return sizeof(DataBlock);
}