mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-25 15:15:40 +00:00
System: Support per-game memory cards
This commit is contained in:
parent
39498849bd
commit
74e455a5f7
|
@ -769,7 +769,7 @@ std::string HostInterface::GetSharedMemoryCardPath(u32 slot) const
|
||||||
|
|
||||||
std::string HostInterface::GetGameMemoryCardPath(const char* game_code, u32 slot) const
|
std::string HostInterface::GetGameMemoryCardPath(const char* game_code, u32 slot) const
|
||||||
{
|
{
|
||||||
return GetUserDirectoryRelativePath("memcards/game_card_%s_%d.mcd", game_code, slot + 1);
|
return GetUserDirectoryRelativePath("memcards/%s_%d.mcd", game_code, slot + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HostInterface::SaveStateInfo> HostInterface::GetAvailableSaveStates(const char* game_code) const
|
std::vector<HostInterface::SaveStateInfo> HostInterface::GetAvailableSaveStates(const char* game_code) const
|
||||||
|
@ -959,8 +959,10 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
|
||||||
si.SetStringValue("Controller1", "Type", Settings::GetControllerTypeName(ControllerType::DigitalController));
|
si.SetStringValue("Controller1", "Type", Settings::GetControllerTypeName(ControllerType::DigitalController));
|
||||||
si.SetStringValue("Controller2", "Type", Settings::GetControllerTypeName(ControllerType::None));
|
si.SetStringValue("Controller2", "Type", Settings::GetControllerTypeName(ControllerType::None));
|
||||||
|
|
||||||
|
si.SetStringValue("MemoryCards", "Card1Type", "PerGame");
|
||||||
si.SetStringValue("MemoryCards", "Card1Path", "memcards/shared_card_1.mcd");
|
si.SetStringValue("MemoryCards", "Card1Path", "memcards/shared_card_1.mcd");
|
||||||
si.SetStringValue("MemoryCards", "Card2Path", "");
|
si.SetStringValue("MemoryCards", "Card2Type", "None");
|
||||||
|
si.SetStringValue("MemoryCards", "Card2Path", "memcards/shared_card_2.mcd");
|
||||||
|
|
||||||
si.SetBoolValue("Debug", "ShowVRAM", false);
|
si.SetBoolValue("Debug", "ShowVRAM", false);
|
||||||
si.SetBoolValue("Debug", "DumpCPUToVRAMCopies", false);
|
si.SetBoolValue("Debug", "DumpCPUToVRAMCopies", false);
|
||||||
|
@ -996,6 +998,9 @@ void HostInterface::UpdateSettings(const std::function<void()>& apply_callback)
|
||||||
const bool old_display_linear_filtering = m_settings.display_linear_filtering;
|
const bool old_display_linear_filtering = m_settings.display_linear_filtering;
|
||||||
const bool old_cdrom_read_thread = m_settings.cdrom_read_thread;
|
const bool old_cdrom_read_thread = m_settings.cdrom_read_thread;
|
||||||
std::array<ControllerType, NUM_CONTROLLER_AND_CARD_PORTS> old_controller_types = m_settings.controller_types;
|
std::array<ControllerType, NUM_CONTROLLER_AND_CARD_PORTS> old_controller_types = m_settings.controller_types;
|
||||||
|
std::array<MemoryCardType, NUM_CONTROLLER_AND_CARD_PORTS> old_memory_card_types = m_settings.memory_card_types;
|
||||||
|
std::array<std::string, NUM_CONTROLLER_AND_CARD_PORTS> old_memory_card_paths =
|
||||||
|
std::move(m_settings.memory_card_paths);
|
||||||
|
|
||||||
apply_callback();
|
apply_callback();
|
||||||
|
|
||||||
|
@ -1050,6 +1055,9 @@ void HostInterface::UpdateSettings(const std::function<void()>& apply_callback)
|
||||||
|
|
||||||
if (m_settings.cdrom_read_thread != old_cdrom_read_thread)
|
if (m_settings.cdrom_read_thread != old_cdrom_read_thread)
|
||||||
m_system->GetCDROM()->SetUseReadThread(m_settings.cdrom_read_thread);
|
m_system->GetCDROM()->SetUseReadThread(m_settings.cdrom_read_thread);
|
||||||
|
|
||||||
|
if (m_settings.memory_card_types != old_memory_card_types || m_settings.memory_card_paths != old_memory_card_paths)
|
||||||
|
m_system->UpdateMemoryCards();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool controllers_updated = false;
|
bool controllers_updated = false;
|
||||||
|
|
|
@ -254,6 +254,7 @@ std::unique_ptr<MemoryCard> MemoryCard::Open(System* system, std::string_view fi
|
||||||
message.AppendString(filename.data(), static_cast<u32>(filename.length()));
|
message.AppendString(filename.data(), static_cast<u32>(filename.length()));
|
||||||
message.AppendString("' could not be read, formatting.");
|
message.AppendString("' could not be read, formatting.");
|
||||||
Log_ErrorPrint(message);
|
Log_ErrorPrint(message);
|
||||||
|
system->GetHostInterface()->AddOSDMessage(message, 5.0f);
|
||||||
mc->Format();
|
mc->Format();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,6 +349,7 @@ bool MemoryCard::LoadFromFile()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log_InfoPrintf("Loaded memory card from %s", m_filename.c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,8 +383,10 @@ bool MemoryCard::SaveIfChanged(bool display_osd_message)
|
||||||
|
|
||||||
Log_InfoPrintf("Saved memory card to '%s'", m_filename.c_str());
|
Log_InfoPrintf("Saved memory card to '%s'", m_filename.c_str());
|
||||||
if (display_osd_message)
|
if (display_osd_message)
|
||||||
|
{
|
||||||
m_system->GetHostInterface()->AddOSDMessage(
|
m_system->GetHostInterface()->AddOSDMessage(
|
||||||
SmallString::FromFormat("Saved memory card to '%s'", m_filename.c_str()));
|
SmallString::FromFormat("Saved memory card to '%s'", m_filename.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,8 +63,18 @@ void Settings::Load(SettingsInterface& si)
|
||||||
controller_types[1] =
|
controller_types[1] =
|
||||||
ParseControllerTypeName(si.GetStringValue("Controller2", "Type", "None").c_str()).value_or(ControllerType::None);
|
ParseControllerTypeName(si.GetStringValue("Controller2", "Type", "None").c_str()).value_or(ControllerType::None);
|
||||||
|
|
||||||
|
// NOTE: The default value here if not present in the config is shared, but SetDefaultSettings() makes per-game.
|
||||||
|
// This is so we don't break older builds which had the shared card by default.
|
||||||
|
memory_card_types[0] =
|
||||||
|
ParseMemoryCardTypeName(
|
||||||
|
si.GetStringValue("MemoryCards", "Card1Type", GetMemoryCardTypeName(MemoryCardType::Shared)).c_str())
|
||||||
|
.value_or(MemoryCardType::Shared);
|
||||||
memory_card_paths[0] = si.GetStringValue("MemoryCards", "Card1Path", "memcards/shared_card_1.mcd");
|
memory_card_paths[0] = si.GetStringValue("MemoryCards", "Card1Path", "memcards/shared_card_1.mcd");
|
||||||
memory_card_paths[1] = si.GetStringValue("MemoryCards", "Card2Path", "");
|
memory_card_types[1] =
|
||||||
|
ParseMemoryCardTypeName(
|
||||||
|
si.GetStringValue("MemoryCards", "Card2Type", GetMemoryCardTypeName(MemoryCardType::None)).c_str())
|
||||||
|
.value_or(MemoryCardType::None);
|
||||||
|
memory_card_paths[1] = si.GetStringValue("MemoryCards", "Card2Path", "memcards/shared_card_2.mcd");
|
||||||
|
|
||||||
debugging.show_vram = si.GetBoolValue("Debug", "ShowVRAM");
|
debugging.show_vram = si.GetBoolValue("Debug", "ShowVRAM");
|
||||||
debugging.dump_cpu_to_vram_copies = si.GetBoolValue("Debug", "DumpCPUToVRAMCopies");
|
debugging.dump_cpu_to_vram_copies = si.GetBoolValue("Debug", "DumpCPUToVRAMCopies");
|
||||||
|
@ -129,15 +139,10 @@ void Settings::Save(SettingsInterface& si) const
|
||||||
else
|
else
|
||||||
si.DeleteValue("Controller2", "Type");
|
si.DeleteValue("Controller2", "Type");
|
||||||
|
|
||||||
if (!memory_card_paths[0].empty())
|
si.SetStringValue("MemoryCards", "Card1Type", GetMemoryCardTypeName(memory_card_types[0]));
|
||||||
si.SetStringValue("MemoryCards", "Card1Path", memory_card_paths[0].c_str());
|
si.SetStringValue("MemoryCards", "Card1Path", memory_card_paths[0].c_str());
|
||||||
else
|
si.SetStringValue("MemoryCards", "Card2Type", GetMemoryCardTypeName(memory_card_types[1]));
|
||||||
si.DeleteValue("MemoryCards", "Card1Path");
|
si.SetStringValue("MemoryCards", "Card2Path", memory_card_paths[1].c_str());
|
||||||
|
|
||||||
if (!memory_card_paths[1].empty())
|
|
||||||
si.SetStringValue("MemoryCards", "Card2Path", memory_card_paths[1].c_str());
|
|
||||||
else
|
|
||||||
si.DeleteValue("MemoryCards", "Card2Path");
|
|
||||||
|
|
||||||
si.SetBoolValue("Debug", "ShowVRAM", debugging.show_vram);
|
si.SetBoolValue("Debug", "ShowVRAM", debugging.show_vram);
|
||||||
si.SetBoolValue("Debug", "DumpCPUToVRAMCopies", debugging.dump_cpu_to_vram_copies);
|
si.SetBoolValue("Debug", "DumpCPUToVRAMCopies", debugging.dump_cpu_to_vram_copies);
|
||||||
|
@ -377,3 +382,31 @@ const char* Settings::GetControllerTypeDisplayName(ControllerType type)
|
||||||
{
|
{
|
||||||
return s_controller_display_names[static_cast<int>(type)];
|
return s_controller_display_names[static_cast<int>(type)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::array<const char*, 3> s_memory_card_type_names = {{"None", "Shared", "PerGame"}};
|
||||||
|
static std::array<const char*, 3> s_memory_card_type_display_names = {
|
||||||
|
{"No Memory Card", "Shared Between All Games", "Separate Card Per Game"}};
|
||||||
|
|
||||||
|
std::optional<MemoryCardType> Settings::ParseMemoryCardTypeName(const char* str)
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
for (const char* name : s_memory_card_type_names)
|
||||||
|
{
|
||||||
|
if (StringUtil::Strcasecmp(name, str) == 0)
|
||||||
|
return static_cast<MemoryCardType>(index);
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Settings::GetMemoryCardTypeName(MemoryCardType type)
|
||||||
|
{
|
||||||
|
return s_memory_card_type_names[static_cast<int>(type)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Settings::GetMemoryCardTypeDisplayName(MemoryCardType type)
|
||||||
|
{
|
||||||
|
return s_memory_card_type_display_names[static_cast<int>(type)];
|
||||||
|
}
|
||||||
|
|
|
@ -92,8 +92,14 @@ struct Settings
|
||||||
bool bios_patch_fast_boot = false;
|
bool bios_patch_fast_boot = false;
|
||||||
|
|
||||||
std::array<ControllerType, NUM_CONTROLLER_AND_CARD_PORTS> controller_types{};
|
std::array<ControllerType, NUM_CONTROLLER_AND_CARD_PORTS> controller_types{};
|
||||||
|
std::array<MemoryCardType, NUM_CONTROLLER_AND_CARD_PORTS> memory_card_types{};
|
||||||
std::array<std::string, NUM_CONTROLLER_AND_CARD_PORTS> memory_card_paths{};
|
std::array<std::string, NUM_CONTROLLER_AND_CARD_PORTS> memory_card_paths{};
|
||||||
|
|
||||||
|
ALWAYS_INLINE bool HasAnyPerGameMemoryCards() const
|
||||||
|
{
|
||||||
|
return (memory_card_types[0] == MemoryCardType::PerGame || memory_card_types[1] == MemoryCardType::PerGame);
|
||||||
|
}
|
||||||
|
|
||||||
void Load(SettingsInterface& si);
|
void Load(SettingsInterface& si);
|
||||||
void Save(SettingsInterface& si) const;
|
void Save(SettingsInterface& si) const;
|
||||||
|
|
||||||
|
@ -129,6 +135,10 @@ struct Settings
|
||||||
static const char* GetControllerTypeName(ControllerType type);
|
static const char* GetControllerTypeName(ControllerType type);
|
||||||
static const char* GetControllerTypeDisplayName(ControllerType type);
|
static const char* GetControllerTypeDisplayName(ControllerType type);
|
||||||
|
|
||||||
|
static std::optional<MemoryCardType> ParseMemoryCardTypeName(const char* str);
|
||||||
|
static const char* GetMemoryCardTypeName(MemoryCardType type);
|
||||||
|
static const char* GetMemoryCardTypeDisplayName(MemoryCardType type);
|
||||||
|
|
||||||
// Default to D3D11 on Windows as it's more performant and at this point, less buggy.
|
// Default to D3D11 on Windows as it's more performant and at this point, less buggy.
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
static constexpr GPURenderer DEFAULT_GPU_RENDERER = GPURenderer::HardwareD3D11;
|
static constexpr GPURenderer DEFAULT_GPU_RENDERER = GPURenderer::HardwareD3D11;
|
||||||
|
|
|
@ -191,6 +191,9 @@ bool System::Boot(const SystemBootParameters& params)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify change of disc.
|
||||||
|
UpdateRunningGame(params.filename.c_str(), media.get());
|
||||||
|
|
||||||
// Component setup.
|
// Component setup.
|
||||||
InitializeComponents();
|
InitializeComponents();
|
||||||
UpdateControllers();
|
UpdateControllers();
|
||||||
|
@ -214,9 +217,6 @@ bool System::Boot(const SystemBootParameters& params)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify change of disc.
|
|
||||||
UpdateRunningGame(params.filename.c_str(), media.get());
|
|
||||||
|
|
||||||
// Insert CD, and apply fastboot patch if enabled.
|
// Insert CD, and apply fastboot patch if enabled.
|
||||||
if (media)
|
if (media)
|
||||||
m_cdrom->InsertMedia(std::move(media));
|
m_cdrom->InsertMedia(std::move(media));
|
||||||
|
@ -798,10 +798,47 @@ void System::UpdateMemoryCards()
|
||||||
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
||||||
{
|
{
|
||||||
m_pad->SetMemoryCard(i, nullptr);
|
m_pad->SetMemoryCard(i, nullptr);
|
||||||
if (settings.memory_card_paths[i].empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::unique_ptr<MemoryCard> card = MemoryCard::Open(this, settings.memory_card_paths[i]);
|
const MemoryCardType type = settings.memory_card_types[i];
|
||||||
|
std::unique_ptr<MemoryCard> card;
|
||||||
|
switch (settings.memory_card_types[i])
|
||||||
|
{
|
||||||
|
case MemoryCardType::None:
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case MemoryCardType::PerGame:
|
||||||
|
{
|
||||||
|
if (m_running_game_code.empty())
|
||||||
|
{
|
||||||
|
m_host_interface->AddFormattedOSDMessage(5.0f,
|
||||||
|
"Per-game memory card cannot be used for slot %u as the running "
|
||||||
|
"game has no code. Using shared card instead.",
|
||||||
|
i + 1u);
|
||||||
|
card = MemoryCard::Open(this, m_host_interface->GetSharedMemoryCardPath(i));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
card = MemoryCard::Open(this, m_host_interface->GetGameMemoryCardPath(m_running_game_code.c_str(), i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MemoryCardType::Shared:
|
||||||
|
{
|
||||||
|
if (settings.memory_card_paths[i].empty())
|
||||||
|
{
|
||||||
|
m_host_interface->AddFormattedOSDMessage(2.0f, "Memory card path for slot %u is missing, using default.",
|
||||||
|
i + 1u);
|
||||||
|
card = MemoryCard::Open(this, m_host_interface->GetSharedMemoryCardPath(i));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
card = MemoryCard::Open(this, settings.memory_card_paths[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (card)
|
if (card)
|
||||||
m_pad->SetMemoryCard(i, std::move(card));
|
m_pad->SetMemoryCard(i, std::move(card));
|
||||||
}
|
}
|
||||||
|
@ -820,6 +857,13 @@ bool System::InsertMedia(const char* path)
|
||||||
|
|
||||||
UpdateRunningGame(path, image.get());
|
UpdateRunningGame(path, image.get());
|
||||||
m_cdrom->InsertMedia(std::move(image));
|
m_cdrom->InsertMedia(std::move(image));
|
||||||
|
|
||||||
|
if (GetSettings().HasAnyPerGameMemoryCards())
|
||||||
|
{
|
||||||
|
m_host_interface->AddOSDMessage("Game changed, reloading memory cards.", 2.0f);
|
||||||
|
UpdateMemoryCards();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,14 @@ enum class ControllerType
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class MemoryCardType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Shared,
|
||||||
|
PerGame,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
enum : u32
|
enum : u32
|
||||||
{
|
{
|
||||||
NUM_CONTROLLER_AND_CARD_PORTS = 2
|
NUM_CONTROLLER_AND_CARD_PORTS = 2
|
||||||
|
|
Loading…
Reference in a new issue