CDVD: Read NVRAM on startup, cache, and save on shutdown

This commit is contained in:
Stenzek 2024-06-09 17:19:40 +10:00 committed by Connor McLaughlin
parent ac38a350a0
commit 919da4d97a
5 changed files with 102 additions and 86 deletions

View File

@ -87,14 +87,14 @@ namespace Common
} // namespace Common } // namespace Common
template <typename T> template <typename T>
[[maybe_unused]] __fi static T GetBufferT(u8* buffer, u32 offset) [[maybe_unused]] __fi static T GetBufferT(const u8* buffer, u32 offset)
{ {
T value; T value;
std::memcpy(&value, buffer + offset, sizeof(value)); std::memcpy(&value, buffer + offset, sizeof(value));
return value; return value;
} }
[[maybe_unused]] __fi static u8 GetBufferU8(u8* buffer, u32 offset) { return GetBufferT<u8>(buffer, offset); } [[maybe_unused]] __fi static u8 GetBufferU8(const u8* buffer, u32 offset) { return GetBufferT<u8>(buffer, offset); }
[[maybe_unused]] __fi static u16 GetBufferU16(u8* buffer, u32 offset) { return GetBufferT<u16>(buffer, offset); } [[maybe_unused]] __fi static u16 GetBufferU16(const u8* buffer, u32 offset) { return GetBufferT<u16>(buffer, offset); }
[[maybe_unused]] __fi static u32 GetBufferU32(u8* buffer, u32 offset) { return GetBufferT<u32>(buffer, offset); } [[maybe_unused]] __fi static u32 GetBufferU32(const u8* buffer, u32 offset) { return GetBufferT<u32>(buffer, offset); }
[[maybe_unused]] __fi static u64 GetBufferU64(u8* buffer, u32 offset) { return GetBufferT<u64>(buffer, offset); } [[maybe_unused]] __fi static u64 GetBufferU64(const u8* buffer, u32 offset) { return GetBufferT<u64>(buffer, offset); }

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+ // SPDX-License-Identifier: LGPL-3.0+
#include "CDVD/CDVD.h" #include "CDVD/CDVD.h"
@ -34,9 +34,12 @@ cdvdStruct cdvd;
s64 PSXCLK = 36864000; s64 PSXCLK = 36864000;
u8 monthmap[13] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static constexpr u8 monthmap[13] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
u8 cdvdParamLength[16] = { 0, 0, 0, 0, 0, 4, 11, 11, 11, 1, 255, 255, 7, 2, 11, 1 }; static constexpr u8 cdvdParamLength[16] = { 0, 0, 0, 0, 0, 4, 11, 11, 11, 1, 255, 255, 7, 2, 11, 1 };
static constexpr size_t NVRAM_SIZE = 1024;
static u8 s_nvram[NVRAM_SIZE];
static __fi void SetSCMDResultSize(u8 size) noexcept static __fi void SetSCMDResultSize(u8 size) noexcept
{ {
@ -137,134 +140,140 @@ static void cdvdGetMechaVer(u8* ver)
Console.Error("Failed to read from %s. Did only %zu/4 bytes", mecfile.c_str(), ret); Console.Error("Failed to read from %s. Did only %zu/4 bytes", mecfile.c_str(), ret);
} }
NVMLayout* getNvmLayout() noexcept const NVMLayout* getNvmLayout() noexcept
{ {
NVMLayout* nvmLayout = NULL; return (nvmlayouts[1].biosVer <= BiosVersion) ? &nvmlayouts[1] : &nvmlayouts[0];
if (nvmlayouts[1].biosVer <= BiosVersion)
nvmLayout = &nvmlayouts[1];
else
nvmLayout = &nvmlayouts[0];
return nvmLayout;
} }
static void cdvdCreateNewNVM(std::FILE* fp) static void cdvdCreateNewNVM()
{ {
u8 zero[1024] = {}; std::memset(s_nvram, 0, sizeof(s_nvram));
std::fwrite(&zero[0], sizeof(zero), 1, fp);
// Write NVM ILink area with dummy data (Age of Empires 2) // Write NVM ILink area with dummy data (Age of Empires 2)
// Also write language data defaulting to English (Guitar Hero 2) // Also write language data defaulting to English (Guitar Hero 2)
// Also write PStwo region defaults // Also write PStwo region defaults
const NVMLayout* nvmLayout = getNvmLayout();
NVMLayout* nvmLayout = getNvmLayout();
if (((BiosVersion >> 8) == 2) && ((BiosVersion & 0xff) != 10)) // bios >= 200, except of 0x210 for PSX2 DESR if (((BiosVersion >> 8) == 2) && ((BiosVersion & 0xff) != 10)) // bios >= 200, except of 0x210 for PSX2 DESR
{ std::memcpy(&s_nvram[nvmLayout->regparams], PStwoRegionDefaults[BiosRegion], 12);
u8 RegParams[12] = { 0 };
memcpy(&RegParams[0], &PStwoRegionDefaults[BiosRegion][0], 12);
std::fseek(fp, nvmLayout->regparams, SEEK_SET);
std::fwrite(&RegParams[0], sizeof(RegParams), 1, fp);
}
constexpr u8 ILinkID_Data[8] = {0x00, 0xAC, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0x86}; static constexpr u8 ILinkID_Data[8] = {0x00, 0xAC, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0x86};
std::fseek(fp, nvmLayout->ilinkId, SEEK_SET); std::memcpy(&s_nvram[nvmLayout->ilinkId], ILinkID_Data, sizeof(ILinkID_Data));
std::fwrite(&ILinkID_Data[0], sizeof(ILinkID_Data), 1, fp);
if (nvmlayouts[1].biosVer <= BiosVersion) if (nvmlayouts[1].biosVer <= BiosVersion)
{ {
constexpr u8 ILinkID_checksum[2] = {0x00, 0x18}; static constexpr u8 ILinkID_checksum[2] = {0x00, 0x18};
std::fseek(fp, nvmLayout->ilinkId + 0x08, SEEK_SET); std::memcpy(&s_nvram[nvmLayout->ilinkId + 0x08], ILinkID_checksum, sizeof(ILinkID_checksum));
std::fwrite(&ILinkID_checksum[0], sizeof(ILinkID_checksum), 1, fp);
} }
u8 biosLanguage[16] = { 0 };
memcpy(&biosLanguage[0], &biosLangDefaults[BiosRegion][0], 16);
// Config sections first 16 bytes are generally blank expect the last byte which is PS1 mode stuff // Config sections first 16 bytes are generally blank expect the last byte which is PS1 mode stuff
// So let's ignore that and just write the PS2 mode stuff // So let's ignore that and just write the PS2 mode stuff
std::fseek(fp, nvmLayout->config1 + 0x10, SEEK_SET); std::memcpy(&s_nvram[nvmLayout->config1 + 0x10], biosLangDefaults[BiosRegion], 16);
std::fwrite(&biosLanguage[0], sizeof(biosLanguage), 1, fp);
} }
static void cdvdNVM(u8* buffer, int offset, size_t bytes, bool read) static std::string cdvdGetNVRAMPath()
{ {
std::string nvmfile(Path::ReplaceExtension(BiosPath, "nvm")); return Path::ReplaceExtension(BiosPath, "nvm");
auto fp = FileSystem::OpenManagedCFile(nvmfile.c_str(), "r+b"); }
if (!fp || FileSystem::FSize64(fp.get()) < 1024)
void cdvdLoadNVRAM()
{ {
fp.reset(); Error error;
fp = FileSystem::OpenManagedCFile(nvmfile.c_str(), "w+b"); const std::string nvmfile = cdvdGetNVRAMPath();
auto fp = FileSystem::OpenManagedCFile(nvmfile.c_str(), "rb", &error);
if (!fp || std::fread(s_nvram, sizeof(s_nvram), 1, fp.get()) != 1)
{
ERROR_LOG("Failed to open or read NVRAM at {}: {}", Path::GetFileName(nvmfile), error.GetDescription());
cdvdCreateNewNVM();
}
else
{
// Verify NVRAM is sane.
const NVMLayout* nvmLayout = getNvmLayout();
constexpr u8 zero[16] = {0};
if (std::memcmp(&s_nvram[nvmLayout->config1 + 0x10], zero, 16) == 0 ||
(((BiosVersion >> 8) == 2) && ((BiosVersion & 0xff) != 10) &&
(std::memcmp(&s_nvram[nvmLayout->regparams], zero, 12) == 0)))
{
ERROR_LOG("Language or Region Parameters missing, filling in defaults");
cdvdCreateNewNVM();
}
}
}
void cdvdSaveNVRAM()
{
Error error;
const std::string nvmfile = cdvdGetNVRAMPath();
auto fp = FileSystem::OpenManagedCFile(nvmfile.c_str(), "r+b", &error);
if (!fp) if (!fp)
{ {
Console.Error("Failed to open NVM file '%s' for writing", nvmfile.c_str()); ERROR_LOG("Failed to open NVRAM at {} for updating: {}", Path::GetFileName(nvmfile), error.GetDescription());
if (read)
std::memset(buffer, 0, bytes);
return; return;
} }
cdvdCreateNewNVM(fp.get()); u8 existing_nvram[NVRAM_SIZE];
if (std::fread(existing_nvram, sizeof(existing_nvram), 1, fp.get()) == 1 &&
std::memcmp(existing_nvram, s_nvram, NVRAM_SIZE) == 0)
{
DEV_LOG("NVRAM has not changed, not writing to disk.");
return;
}
if (FileSystem::FSeek64(fp.get(), 0, SEEK_SET) == 0 &&
std::fwrite(s_nvram, NVRAM_SIZE, 1, fp.get()) == 1)
{
INFO_LOG("NVRAM saved to {}.", Path::GetFileName(nvmfile));
} }
else else
{ {
constexpr u8 zero[16] = { 0 }; ERROR_LOG("Failed to save NVRAM to {}: {}", Path::GetFileName(nvmfile), Error::CreateErrno(errno).GetDescription());
u8 LanguageParams[16] = { 0 };
u8 RegParams[12] = { 0 };
NVMLayout* nvmLayout = getNvmLayout();
if (std::fseek(fp.get(), nvmLayout->config1 + 0x10, SEEK_SET) != 0 ||
std::fread(&LanguageParams[0], 16, 1, fp.get()) != 1 ||
std::memcmp(&LanguageParams[0], zero, sizeof(LanguageParams)) == 0 ||
(((BiosVersion >> 8) == 2) && ((BiosVersion & 0xff) != 10) &&
(std::fseek(fp.get(), nvmLayout->regparams, SEEK_SET) != 0 ||
std::fread(&RegParams[0], 12, 1, fp.get()) != 1 ||
std::memcmp(&RegParams[0], zero, sizeof(RegParams)) == 0)))
{
Console.Warning("Language or Region Parameters missing, filling in defaults");
FileSystem::FSeek64(fp.get(), 0, SEEK_SET);
cdvdCreateNewNVM(fp.get());
} }
} }
std::fseek(fp.get(), offset, SEEK_SET);
size_t ret;
if (read)
ret = std::fread(buffer, 1, bytes, fp.get());
else
ret = std::fwrite(buffer, 1, bytes, fp.get());
if (ret != bytes)
Console.Error("Failed to %s %s. Did only %zu/%zu bytes",
read ? "read from" : "write to", nvmfile.c_str(), ret, bytes);
}
static void cdvdReadNVM(u8* dst, int offset, int bytes) static void cdvdReadNVM(u8* dst, int offset, int bytes)
{ {
cdvdNVM(dst, offset, bytes, true); int to_read = bytes;
if ((offset + bytes) > sizeof(s_nvram)) [[unlikely]]
{
WARNING_LOG("CDVD: Out of bounds NVRAM read: offset={}, bytes={}", offset, bytes);
to_read = std::max(static_cast<int>(sizeof(s_nvram)) - offset, 0);
pxAssert((bytes - to_read) > 0);
std::memset(dst + to_read, 0, bytes - to_read);
}
if (to_read > 0) [[likely]]
std::memcpy(dst, &s_nvram[offset], to_read);
} }
static void cdvdWriteNVM(const u8* src, int offset, int bytes) static void cdvdWriteNVM(const u8* src, int offset, int bytes)
{ {
cdvdNVM(const_cast<u8*>(src), offset, bytes, false); int to_write = bytes;
if ((offset + bytes) > sizeof(s_nvram)) [[unlikely]]
{
WARNING_LOG("CDVD: Out of bounds NVRAM write: offset={}, bytes={}", offset, bytes);
to_write = std::max(static_cast<int>(sizeof(s_nvram)) - offset, 0);
}
if (to_write > 0) [[likely]]
std::memcpy(&s_nvram[offset], src, to_write);
} }
void getNvmData(u8* buffer, s32 offset, s32 size, s32 fmtOffset) void getNvmData(u8* buffer, s32 offset, s32 size, s32 fmtOffset)
{ {
// find the correct bios version // find the correct bios version
NVMLayout* nvmLayout = getNvmLayout(); const NVMLayout* nvmLayout = getNvmLayout();
// get data from eeprom // get data from eeprom
cdvdReadNVM(buffer, *reinterpret_cast<s32*>((reinterpret_cast<u8*>(nvmLayout)) + fmtOffset) + offset, size); cdvdReadNVM(buffer, *reinterpret_cast<const s32*>((reinterpret_cast<const u8*>(nvmLayout)) + fmtOffset) + offset, size);
} }
void setNvmData(const u8* buffer, s32 offset, s32 size, s32 fmtOffset) void setNvmData(const u8* buffer, s32 offset, s32 size, s32 fmtOffset)
{ {
// find the correct bios version // find the correct bios version
NVMLayout* nvmLayout = getNvmLayout(); const NVMLayout* nvmLayout = getNvmLayout();
// set data in eeprom // set data in eeprom
cdvdWriteNVM(buffer, GetBufferU32(&reinterpret_cast<u8*>(nvmLayout)[0], fmtOffset) + offset, size); cdvdWriteNVM(buffer, GetBufferU32(&reinterpret_cast<const u8*>(nvmLayout)[0], fmtOffset) + offset, size);
} }
static void cdvdReadConsoleID(u8* id) static void cdvdReadConsoleID(u8* id)

View File

@ -166,6 +166,8 @@ extern cdvdStruct cdvd;
extern void cdvdReadLanguageParams(u8* config); extern void cdvdReadLanguageParams(u8* config);
extern void cdvdLoadNVRAM();
extern void cdvdSaveNVRAM();
extern void cdvdReset(); extern void cdvdReset();
extern void cdvdVsync(); extern void cdvdVsync();
extern void cdvdActionInterrupt(); extern void cdvdActionInterrupt();

View File

@ -257,13 +257,13 @@ struct NVMLayout
}; };
#define NVM_FORMAT_MAX 2 #define NVM_FORMAT_MAX 2
static NVMLayout nvmlayouts[NVM_FORMAT_MAX] = static constexpr NVMLayout nvmlayouts[NVM_FORMAT_MAX] =
{ {
{0x000, 0x280, 0x300, 0x200, 0x1C8, 0x1C0, 0x1A0, 0x180, 0x198}, // eeproms from bios v0.00 and up {0x000, 0x280, 0x300, 0x200, 0x1C8, 0x1C0, 0x1A0, 0x180, 0x198}, // eeproms from bios v0.00 and up
{0x146, 0x270, 0x2B0, 0x200, 0x1F0, 0x1E0, 0x1B0, 0x180, 0x198}, // eeproms from bios v1.70 and up {0x146, 0x270, 0x2B0, 0x200, 0x1F0, 0x1E0, 0x1B0, 0x180, 0x198}, // eeproms from bios v1.70 and up
}; };
static u8 PStwoRegionDefaults[13][12] = static constexpr u8 PStwoRegionDefaults[13][12] =
{ {
{0x4a, 0x4a, 0x6a, 0x70, 0x6e, 0x4a, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00}, // JJjpnJJ - Japan {0x4a, 0x4a, 0x6a, 0x70, 0x6e, 0x4a, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00}, // JJjpnJJ - Japan
{0x41, 0x41, 0x65, 0x6e, 0x67, 0x41, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00}, // AAengAU - USA {0x41, 0x41, 0x65, 0x6e, 0x67, 0x41, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00}, // AAengAU - USA
@ -280,7 +280,7 @@ static u8 PStwoRegionDefaults[13][12] =
{0x48, 0x48, 0x74, 0x63, 0x68, 0x4a, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00}, // HHtchJA - Taiwan {0x48, 0x48, 0x74, 0x63, 0x68, 0x4a, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00}, // HHtchJA - Taiwan
}; };
static u8 biosLangDefaults[11][16] = static constexpr u8 biosLangDefaults[11][16] =
{ {
{0x20, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30}, // Japan (Japanese) {0x20, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30}, // Japan (Japanese)
{0x30, 0x21, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41}, // USA (English) {0x30, 0x21, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41}, // USA (English)

View File

@ -1320,6 +1320,9 @@ bool VMManager::Initialize(VMBootParameters boot_params)
"Please consult the FAQs and Guides for further instructions.")); "Please consult the FAQs and Guides for further instructions."));
return false; return false;
} }
// Must happen after BIOS load, depends on BIOS version.
cdvdLoadNVRAM();
} }
Error error; Error error;
@ -1619,6 +1622,8 @@ void VMManager::Shutdown(bool save_resume_state)
if (GSDumpReplayer::IsReplayingDump()) if (GSDumpReplayer::IsReplayingDump())
GSDumpReplayer::Shutdown(); GSDumpReplayer::Shutdown();
else
cdvdSaveNVRAM();
s_state.store(VMState::Shutdown, std::memory_order_release); s_state.store(VMState::Shutdown, std::memory_order_release);
FullscreenUI::OnVMDestroyed(); FullscreenUI::OnVMDestroyed();