From 6d7625ea6b1a1d283755da8f2ecde8a7bcd4f6f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 17 Mar 2025 14:41:52 +0100 Subject: [PATCH] Implement save state support, with backwards compatibility. --- Core/HLE/AtracCtx.h | 4 +++- Core/HLE/AtracCtx2.cpp | 36 ++++++++++++++++++++++++------- Core/HLE/AtracCtx2.h | 5 +++-- Core/HLE/sceAtrac.cpp | 42 +++++++++++++++++++++++++++--------- UI/ImDebugger/ImDebugger.cpp | 4 ++-- 5 files changed, 68 insertions(+), 23 deletions(-) diff --git a/Core/HLE/AtracCtx.h b/Core/HLE/AtracCtx.h index ef946f454d..0e5f2189b9 100644 --- a/Core/HLE/AtracCtx.h +++ b/Core/HLE/AtracCtx.h @@ -229,7 +229,7 @@ public: virtual int GetSoundSample(int *endSample, int *loopStartSample, int *loopEndSample) const = 0; - virtual bool IsNewAtracImpl() const { return false; } + virtual int GetContextVersion() const = 0; protected: u16 outputChannels_ = 2; @@ -325,6 +325,8 @@ public: void UpdateContextFromPSPMem() override; void WriteContextToPSPMem(); + int GetContextVersion() const override { return 1; } + private: void UpdateBufferState(); void ResetData(); diff --git a/Core/HLE/AtracCtx2.cpp b/Core/HLE/AtracCtx2.cpp index 2d3e88e990..308edb0bc5 100644 --- a/Core/HLE/AtracCtx2.cpp +++ b/Core/HLE/AtracCtx2.cpp @@ -1,4 +1,7 @@ #include + +#include "Common/Serialize/Serializer.h" +#include "Common/Serialize/SerializeFuncs.h" #include "Common/Log.h" #include "Core/MemMapHelpers.h" #include "Core/HLE/HLE.h" @@ -162,15 +165,32 @@ int Atrac2::RemainingFrames() const { } Atrac2::Atrac2(u32 contextAddr, int codecType) { - context_ = PSPPointer::Create(contextAddr); - SceAtracIdInfo &info = context_->info; - info.codec = codecType; - info.state = ATRAC_STATUS_NO_DATA; - info.curBuffer = 0; + if (contextAddr) { + context_ = PSPPointer::Create(contextAddr); + // First-time initialization. + SceAtracIdInfo &info = context_->info; + info.codec = codecType; + info.state = ATRAC_STATUS_NO_DATA; + info.curBuffer = 0; + } else { + // We're loading state, we'll restore the context in DoState. + } } void Atrac2::DoState(PointerWrap &p) { - _assert_msg_(false, "Savestates not yet support with new Atrac implementation.\n\nTurn it off in Developer settings.\n\n"); + auto s = p.Section("Atrac2", 1, 1); + if (!s) + return; + + Do(p, outputChannels_); + // The only thing we need to save now is the outputChannels_ and the context pointer. And technically, not even that since + // it can be computed. Still, for future proofing, let's save it. + Do(p, context_); + + const SceAtracIdInfo &info = context_->info; + if (p.mode == p.MODE_READ && info.state != ATRAC_STATUS_NO_DATA) { + CreateDecoder(info.codec, info.sampleSize, info.numChan); + } } bool Atrac2::HasSecondBuffer() const { @@ -299,7 +319,7 @@ int Atrac2::GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int seekPos, bo seekPos += info.firstValidSample; - if ((u32)seekPos > info.endSample) { + if ((u32)seekPos > (u32)info.endSample) { return SCE_ERROR_ATRAC_BAD_SAMPLE; } @@ -935,7 +955,7 @@ int Atrac2::SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) { u32 loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd); if ((info.sampleSize * 3 <= (int)secondBufferSize || (info.fileDataEnd - loopEndFileOffset) <= (int)secondBufferSize)) { - if (info.state == 6) { + if (info.state == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) { info.secondBuffer = secondBuffer; info.secondBufferByte = secondBufferSize; info.secondStreamOff = 0; diff --git a/Core/HLE/AtracCtx2.h b/Core/HLE/AtracCtx2.h index d6e36a2567..e1a57cf9ce 100644 --- a/Core/HLE/AtracCtx2.h +++ b/Core/HLE/AtracCtx2.h @@ -6,7 +6,8 @@ class Atrac2 : public AtracBase { public: - Atrac2(u32 contextAddr, int codecType); + // The default values are only used during save state load, in which case they get restored by DoState. + Atrac2(u32 contextAddr = 0, int codecType = 0); ~Atrac2() { delete[] decodeTemp_; } @@ -51,7 +52,7 @@ public: void UpdateContextFromPSPMem() override {} void NotifyGetContextAddress() override {} - bool IsNewAtracImpl() const override { return true; } + int GetContextVersion() const override { return 2; } u32 GetInternalCodecError() const override; diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 3718303b30..4ad23fa1e4 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -152,30 +152,54 @@ static u32 GetAtracContextAddress(int atracID) { } void __AtracDoState(PointerWrap &p) { - auto s = p.Section("sceAtrac", 1, 2); + auto s = p.Section("sceAtrac", 1, 4); if (!s) return; Do(p, atracInited); + if (s >= 4) { + Do(p, g_atracBSS); + Do(p, g_atracMaxContexts); + if (g_atracMaxContexts > PSP_MAX_ATRAC_IDS) // paranoia + g_atracMaxContexts = PSP_MAX_ATRAC_IDS; + } else { + g_atracBSS = 0; + g_atracMaxContexts = 6; + } for (int i = 0; i < PSP_MAX_ATRAC_IDS; ++i) { bool valid = atracContexts[i] != nullptr; Do(p, valid); if (valid) { - DoSubClass(p, atracContexts[i], i); + int version = atracContexts[i] ? atracContexts[i]->GetContextVersion() : 0; + if (s >= 4) { + Do(p, version); + _dbg_assert_(version != 0); + } else { + // Old versions only support old contexts. + version = 1; + } + switch (version) { + case 1: + DoSubClass(p, atracContexts[i], i); + break; + case 2: + DoSubClass(p, atracContexts[i]); + break; + } } else { delete atracContexts[i]; atracContexts[i] = nullptr; } } DoArray(p, atracContextTypes, PSP_MAX_ATRAC_IDS); - if (s < 2) { - atracLibVersion = 0; - atracLibCrc = 0; - } - else { + if (s >= 2) { Do(p, atracLibVersion); Do(p, atracLibCrc); } + else { + atracLibVersion = 0; + atracLibCrc = 0; + } } static AtracBase *getAtrac(int atracID) { @@ -192,9 +216,7 @@ static AtracBase *getAtrac(int atracID) { static int AllocAndRegisterAtrac(int codecType) { for (int i = 0; i < g_atracMaxContexts; ++i) { if (atracContextTypes[i] == codecType && atracContexts[i] == 0) { - if (g_Config.bUseExperimentalAtrac) { - // Note: This assert isn't really valid until we savestate the new contexts. - _dbg_assert_(g_atracBSS != 0); + if (g_Config.bUseExperimentalAtrac && g_atracBSS != 0) { atracContexts[i] = new Atrac2(GetAtracContextAddress(i), codecType); } else { atracContexts[i] = new Atrac(i, codecType); diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 07e0ca5309..18e9169c78 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -1006,7 +1006,7 @@ void DrawAudioDecodersView(ImConfig &cfg, ImControl &control) { ImGui::TableNextColumn(); ImGui::Text("%d", ctx->RemainingFrames()); ImGui::TableNextColumn(); - ImGui::TextUnformatted(ctx->IsNewAtracImpl() ? "NewImpl" : "Legacy"); + ImGui::TextUnformatted(ctx->GetContextVersion() >= 2 ? "NewImpl" : "Legacy"); } ImGui::EndTable(); @@ -1031,7 +1031,7 @@ void DrawAudioDecodersView(ImConfig &cfg, ImControl &control) { ImGui::SameLine(); ImClickableValue("ctx", ctx->context_.ptr, control, ImCmd::SHOW_IN_MEMORY_VIEWER); } - if (ctx->context_.IsValid() && ctx->IsNewAtracImpl()) { + if (ctx->context_.IsValid() && ctx->GetContextVersion() >= 2) { const auto &info = ctx->context_->info; ImGui::Text("Buffer: (size: %d / %08x) Frame: %d", info.bufferByte, info.bufferByte, info.sampleSize); ImGui::SameLine();