pcsx2/plugins/spu2-x/src/spu2freeze.cpp

150 lines
4.8 KiB
C++

/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X 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.
*
* SPU2-X 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "PS2E-spu2.h" // hopefully temporary, until I resolve lClocks depdendency
namespace Savestate
{
// Arbitrary ID to identify SPU2-X 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 = 0x000d;
static void wipe_the_cache()
{
memset( pcm_cache_data, 0, pcm_BlockCount * sizeof(PcmCacheEntry) );
}
}
struct Savestate::DataBlock
{
u32 spu2id; // SPU2-X 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-X version identifier
V_Core Cores[2];
V_SPDIF Spdif;
s16 OutPos;
s16 InputPos;
u32 Cycles;
u32 lClocks;
int PlayMode;
};
s32 __fastcall Savestate::FreezeIt( DataBlock& spud )
{
spud.spu2id = SAVE_ID;
spud.version = SAVE_VERSION;
pxAssertMsg( spu2regs && _spu2mem, "Looks like PCSX2 is trying to savestate while pluigns are shut down. That's a no-no! It shouldn't crash, but the savestate will probably be corrupted." );
if( spu2regs != NULL ) memcpy(spud.unkregs, spu2regs, sizeof(spud.unkregs));
if( _spu2mem != NULL ) memcpy(spud.mem, _spu2mem, sizeof(spud.mem));
memcpy(spud.Cores, Cores, sizeof(Cores));
memcpy(&spud.Spdif, &Spdif, sizeof(Spdif));
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 __fastcall Savestate::ThawIt( DataBlock& spud )
{
if( spud.spu2id != SAVE_ID || spud.version < SAVE_VERSION )
{
fprintf(stderr, "\n*** SPU2-X Warning:\n");
if( spud.spu2id == SAVE_ID )
fprintf(stderr, "\tSavestate version is from an older version of this plugin.\n");
else
fprintf(stderr, "\tThe savestate you are trying to load was not made with this plugin.\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 pluigns 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));
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;
}
}
// HACKFIX!! DMAPtr can be invalid after a savestate load, so force it to NULL and
// ignore it on any pending ADMA writes. (the DMAPtr concept used to work in old VM
// editions of PCSX2 with fixed addressing, but new PCSX2s have dynamic memory
// addressing).
Cores[0].DMAPtr = Cores[1].DMAPtr = NULL;
}
return 0;
}
s32 __fastcall Savestate::SizeIt()
{
return sizeof(DataBlock);
}