mirror of https://github.com/PCSX2/pcsx2.git
CDVD: Read NVRAM on startup, cache, and save on shutdown
This commit is contained in:
parent
ac38a350a0
commit
919da4d97a
|
@ -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); }
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue