System: Share memory cards in multi-disc games
Even without playlists.
This commit is contained in:
parent
7d914a9384
commit
c7f987bfb7
|
@ -3583,8 +3583,8 @@ void FullscreenUI::DrawMemoryCardSettingsPage()
|
||||||
SetSettingsChanged(bsi);
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_SEARCH, "Use Single Card For Sub-Images"),
|
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_SEARCH, "Use Single Card For Multi-Disc Games"),
|
||||||
FSUI_CSTR("When using a multi-disc image (m3u/pbp) and per-game (title) memory cards, "
|
FSUI_CSTR("When playing a multi-disc game and using per-game (title) memory cards, "
|
||||||
"use a single memory card for all discs."),
|
"use a single memory card for all discs."),
|
||||||
"MemoryCards", "UsePlaylistTitle", true);
|
"MemoryCards", "UsePlaylistTitle", true);
|
||||||
|
|
||||||
|
|
|
@ -192,17 +192,14 @@ bool Pad::DoStateController(StateWrapper& sw, u32 i)
|
||||||
if (g_settings.load_devices_from_save_states)
|
if (g_settings.load_devices_from_save_states)
|
||||||
{
|
{
|
||||||
Host::AddFormattedOSDMessage(
|
Host::AddFormattedOSDMessage(
|
||||||
10.0f,
|
10.0f, TRANSLATE("OSDMessage", "Save state contains controller type %s in port %u, but %s is used. Switching."),
|
||||||
TRANSLATE("OSDMessage",
|
|
||||||
"Save state contains controller type %s in port %u, but %s is used. Switching."),
|
|
||||||
Settings::GetControllerTypeName(state_controller_type), i + 1u,
|
Settings::GetControllerTypeName(state_controller_type), i + 1u,
|
||||||
Settings::GetControllerTypeName(controller_type));
|
Settings::GetControllerTypeName(controller_type));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Host::AddFormattedOSDMessage(
|
Host::AddFormattedOSDMessage(10.0f, TRANSLATE("OSDMessage", "Ignoring mismatched controller type %s in port %u."),
|
||||||
10.0f, TRANSLATE("OSDMessage", "Ignoring mismatched controller type %s in port %u."),
|
Settings::GetControllerTypeName(state_controller_type), i + 1u);
|
||||||
Settings::GetControllerTypeName(state_controller_type), i + 1u);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dev-friendly untranslated console log.
|
// dev-friendly untranslated console log.
|
||||||
|
@ -256,8 +253,7 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i, bool is_memory_state)
|
||||||
{
|
{
|
||||||
Host::AddFormattedOSDMessage(
|
Host::AddFormattedOSDMessage(
|
||||||
20.0f,
|
20.0f,
|
||||||
TRANSLATE("OSDMessage",
|
TRANSLATE("OSDMessage", "Memory card %u present in save state but not in system. Creating temporary card."),
|
||||||
"Memory card %u present in save state but not in system. Creating temporary card."),
|
|
||||||
i + 1u);
|
i + 1u);
|
||||||
s_memory_cards[i] = MemoryCard::Create();
|
s_memory_cards[i] = MemoryCard::Create();
|
||||||
}
|
}
|
||||||
|
@ -296,7 +292,7 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i, bool is_memory_state)
|
||||||
Host::AddFormattedOSDMessage(
|
Host::AddFormattedOSDMessage(
|
||||||
20.0f,
|
20.0f,
|
||||||
TRANSLATE("OSDMessage",
|
TRANSLATE("OSDMessage",
|
||||||
"Memory card %u from save state does match current card data. Simulating replugging."),
|
"Memory card %u from save state does match current card data. Simulating replugging."),
|
||||||
i + 1u);
|
i + 1u);
|
||||||
|
|
||||||
// this is a potentially serious issue - some games cache info from memcards and jumping around
|
// this is a potentially serious issue - some games cache info from memcards and jumping around
|
||||||
|
@ -312,8 +308,7 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i, bool is_memory_state)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Host::AddFormattedOSDMessage(
|
Host::AddFormattedOSDMessage(
|
||||||
20.0f,
|
20.0f, TRANSLATE("OSDMessage", "Memory card %u present in save state but not in system. Ignoring card."),
|
||||||
TRANSLATE("OSDMessage", "Memory card %u present in save state but not in system. Ignoring card."),
|
|
||||||
i + 1u);
|
i + 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,16 +320,14 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i, bool is_memory_state)
|
||||||
if (g_settings.load_devices_from_save_states)
|
if (g_settings.load_devices_from_save_states)
|
||||||
{
|
{
|
||||||
Host::AddFormattedOSDMessage(
|
Host::AddFormattedOSDMessage(
|
||||||
20.0f,
|
20.0f, TRANSLATE("OSDMessage", "Memory card %u present in system but not in save state. Removing card."),
|
||||||
TRANSLATE("OSDMessage", "Memory card %u present in system but not in save state. Removing card."),
|
|
||||||
i + 1u);
|
i + 1u);
|
||||||
s_memory_cards[i].reset();
|
s_memory_cards[i].reset();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Host::AddFormattedOSDMessage(
|
Host::AddFormattedOSDMessage(
|
||||||
20.0f,
|
20.0f, TRANSLATE("OSDMessage", "Memory card %u present in system but not in save state. Replugging card."),
|
||||||
TRANSLATE("OSDMessage", "Memory card %u present in system but not in save state. Replugging card."),
|
|
||||||
i + 1u);
|
i + 1u);
|
||||||
s_memory_cards[i]->Reset();
|
s_memory_cards[i]->Reset();
|
||||||
}
|
}
|
||||||
|
@ -545,6 +538,10 @@ MemoryCard* Pad::GetMemoryCard(u32 slot)
|
||||||
|
|
||||||
void Pad::SetMemoryCard(u32 slot, std::unique_ptr<MemoryCard> dev)
|
void Pad::SetMemoryCard(u32 slot, std::unique_ptr<MemoryCard> dev)
|
||||||
{
|
{
|
||||||
|
Log_InfoPrintf("Memory card slot %u: %s", slot,
|
||||||
|
dev ? (dev->GetFilename().empty() ? "<no file configured>" : dev->GetFilename().c_str()) :
|
||||||
|
"<unplugged>");
|
||||||
|
|
||||||
s_memory_cards[slot] = std::move(dev);
|
s_memory_cards[slot] = std::move(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -798,7 +795,8 @@ void Pad::DoTransfer(TickCount ticks_late)
|
||||||
const u32 frame_number = System::GetFrameNumber();
|
const u32 frame_number = System::GetFrameNumber();
|
||||||
|
|
||||||
// consider u32 overflow case
|
// consider u32 overflow case
|
||||||
if (ShouldAvoidSavingToState() && (frame_number - s_last_memory_card_transfer_frame) > GetMaximumRollbackFrames())
|
if (ShouldAvoidSavingToState() &&
|
||||||
|
(frame_number - s_last_memory_card_transfer_frame) > GetMaximumRollbackFrames())
|
||||||
BackupMemoryCardState();
|
BackupMemoryCardState();
|
||||||
|
|
||||||
s_last_memory_card_transfer_frame = frame_number;
|
s_last_memory_card_transfer_frame = frame_number;
|
||||||
|
|
|
@ -1271,7 +1271,7 @@ std::string Settings::GetSharedMemoryCardPath(u32 slot) const
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Settings::GetGameMemoryCardPath(const char* serial, u32 slot)
|
std::string Settings::GetGameMemoryCardPath(const std::string_view& serial, u32 slot)
|
||||||
{
|
{
|
||||||
return Path::Combine(EmuFolders::MemoryCards, fmt::format("{}_{}.mcd", serial, slot + 1));
|
return Path::Combine(EmuFolders::MemoryCards, fmt::format("{}_{}.mcd", serial, slot + 1));
|
||||||
}
|
}
|
||||||
|
|
|
@ -312,7 +312,7 @@ struct Settings
|
||||||
std::string GetSharedMemoryCardPath(u32 slot) const;
|
std::string GetSharedMemoryCardPath(u32 slot) const;
|
||||||
|
|
||||||
/// Returns the default path to a memory card for a specific game.
|
/// Returns the default path to a memory card for a specific game.
|
||||||
static std::string GetGameMemoryCardPath(const char* serial, u32 slot);
|
static std::string GetGameMemoryCardPath(const std::string_view& serial, u32 slot);
|
||||||
|
|
||||||
static void CPUOverclockPercentToFraction(u32 percent, u32* numerator, u32* denominator);
|
static void CPUOverclockPercentToFraction(u32 percent, u32* numerator, u32* denominator);
|
||||||
static u32 CPUOverclockFractionToPercent(u32 numerator, u32 denominator);
|
static u32 CPUOverclockFractionToPercent(u32 numerator, u32 denominator);
|
||||||
|
|
|
@ -2304,8 +2304,8 @@ bool System::InternalSaveState(ByteStream* state, u32 screenshot_size /* = 256 *
|
||||||
u32 screenshot_stride;
|
u32 screenshot_stride;
|
||||||
GPUTexture::Format screenshot_format;
|
GPUTexture::Format screenshot_format;
|
||||||
if (g_gpu_device->RenderScreenshot(screenshot_width, screenshot_height,
|
if (g_gpu_device->RenderScreenshot(screenshot_width, screenshot_height,
|
||||||
Common::Rectangle<s32>::FromExtents(0, 0, screenshot_width, screenshot_height),
|
Common::Rectangle<s32>::FromExtents(0, 0, screenshot_width, screenshot_height),
|
||||||
&screenshot_buffer, &screenshot_stride, &screenshot_format) &&
|
&screenshot_buffer, &screenshot_stride, &screenshot_format) &&
|
||||||
GPUTexture::ConvertTextureDataToRGBA8(screenshot_width, screenshot_height, screenshot_buffer, screenshot_stride,
|
GPUTexture::ConvertTextureDataToRGBA8(screenshot_width, screenshot_height, screenshot_buffer, screenshot_stride,
|
||||||
screenshot_format))
|
screenshot_format))
|
||||||
{
|
{
|
||||||
|
@ -2893,8 +2893,43 @@ std::unique_ptr<MemoryCard> System::GetMemoryCardForSlot(u32 slot, MemoryCardTyp
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return MemoryCard::Open(g_settings.GetGameMemoryCardPath(
|
std::string card_path;
|
||||||
MemoryCard::SanitizeGameTitleForFileName(s_running_game_title).c_str(), slot));
|
|
||||||
|
// Playlist - use title if different.
|
||||||
|
if (HasMediaSubImages() && s_running_game_entry && s_running_game_title != s_running_game_entry->title)
|
||||||
|
{
|
||||||
|
card_path = g_settings.GetGameMemoryCardPath(
|
||||||
|
MemoryCard::SanitizeGameTitleForFileName(s_running_game_entry->title), slot);
|
||||||
|
}
|
||||||
|
// Multi-disc game - use disc set name.
|
||||||
|
else if (s_running_game_entry && !s_running_game_entry->disc_set_name.empty())
|
||||||
|
{
|
||||||
|
card_path = g_settings.GetGameMemoryCardPath(
|
||||||
|
MemoryCard::SanitizeGameTitleForFileName(s_running_game_entry->disc_set_name), slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// But prefer a disc-specific card if one already exists.
|
||||||
|
std::string disc_card_path =
|
||||||
|
g_settings.GetGameMemoryCardPath(MemoryCard::SanitizeGameTitleForFileName(s_running_game_entry->title), slot);
|
||||||
|
if (disc_card_path != card_path)
|
||||||
|
{
|
||||||
|
if (card_path.empty() || !g_settings.memory_card_use_playlist_title ||
|
||||||
|
FileSystem::FileExists(disc_card_path.c_str()))
|
||||||
|
{
|
||||||
|
if (g_settings.memory_card_use_playlist_title && !card_path.empty())
|
||||||
|
{
|
||||||
|
Host::AddIconOSDMessage(
|
||||||
|
fmt::format("DiscSpecificMC{}", slot), ICON_FA_SD_CARD,
|
||||||
|
fmt::format(TRANSLATE_FS("System", "Using disc-specific memory card '{}' instead of per-game card."),
|
||||||
|
Path::GetFileName(disc_card_path)),
|
||||||
|
Host::OSD_INFO_DURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
card_path = std::move(disc_card_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MemoryCard::Open(card_path.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3145,9 +3180,9 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting)
|
||||||
s_running_game_title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
|
s_running_game_title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image->HasSubImages() && g_settings.memory_card_use_playlist_title)
|
if (image->HasSubImages())
|
||||||
{
|
{
|
||||||
std::string image_title(image->GetMetadata("title"));
|
std::string image_title = image->GetMetadata("title");
|
||||||
if (!image_title.empty())
|
if (!image_title.empty())
|
||||||
s_running_game_title = std::move(image_title);
|
s_running_game_title = std::move(image_title);
|
||||||
}
|
}
|
||||||
|
@ -3456,8 +3491,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||||
|
|
||||||
if (g_settings.memory_card_types != old_settings.memory_card_types ||
|
if (g_settings.memory_card_types != old_settings.memory_card_types ||
|
||||||
g_settings.memory_card_paths != old_settings.memory_card_paths ||
|
g_settings.memory_card_paths != old_settings.memory_card_paths ||
|
||||||
(g_settings.memory_card_use_playlist_title != old_settings.memory_card_use_playlist_title &&
|
(g_settings.memory_card_use_playlist_title != old_settings.memory_card_use_playlist_title))
|
||||||
HasMediaSubImages()))
|
|
||||||
{
|
{
|
||||||
UpdateMemoryCardTypes();
|
UpdateMemoryCardTypes();
|
||||||
}
|
}
|
||||||
|
@ -4448,7 +4482,7 @@ void System::UpdateSoftwareCursor()
|
||||||
if (image && image->IsValid())
|
if (image && image->IsValid())
|
||||||
{
|
{
|
||||||
g_gpu_device->SetSoftwareCursor(image->GetPixels(), image->GetWidth(), image->GetHeight(), image->GetPitch(),
|
g_gpu_device->SetSoftwareCursor(image->GetPixels(), image->GetWidth(), image->GetHeight(), image->GetPitch(),
|
||||||
image_scale);
|
image_scale);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -58,13 +58,13 @@ void MemoryCardSettingsWidget::createUi(SettingsDialog* dialog)
|
||||||
box_layout->addLayout(hbox);
|
box_layout->addLayout(hbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
QCheckBox* playlist_title_as_game_title = new QCheckBox(tr("Use Single Card For Sub-Images"), box);
|
QCheckBox* playlist_title_as_game_title = new QCheckBox(tr("Use Single Card For Multi-Disc Games"), box);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_dialog->getSettingsInterface(), playlist_title_as_game_title,
|
SettingWidgetBinder::BindWidgetToBoolSetting(m_dialog->getSettingsInterface(), playlist_title_as_game_title,
|
||||||
"MemoryCards", "UsePlaylistTitle", true);
|
"MemoryCards", "UsePlaylistTitle", true);
|
||||||
box_layout->addWidget(playlist_title_as_game_title);
|
box_layout->addWidget(playlist_title_as_game_title);
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
playlist_title_as_game_title, tr("Use Single Card For Sub-Images"), tr("Checked"),
|
playlist_title_as_game_title, tr("Use Single Card For Multi-Disc Games"), tr("Checked"),
|
||||||
tr("When using a multi-disc format (m3u/pbp) and per-game (title) memory cards, a single memory card "
|
tr("When playing a multi-disc game and using per-game (title) memory cards, a single memory card "
|
||||||
"will be used for all discs. If unchecked, a separate card will be used for each disc."));
|
"will be used for all discs. If unchecked, a separate card will be used for each disc."));
|
||||||
|
|
||||||
box_layout->addWidget(QtUtils::CreateHorizontalLine(box));
|
box_layout->addWidget(QtUtils::CreateHorizontalLine(box));
|
||||||
|
|
Loading…
Reference in New Issue