/* 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 . */ #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 = 0x0006; 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; memcpy(spud.unkregs, spu2regs, sizeof(spud.unkregs)); 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(); // base stuff memcpy(spu2regs, spud.unkregs, sizeof(spud.unkregs)); 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); }