diff --git a/src/core/game_list.cpp b/src/core/game_list.cpp index 32fff7e04..690763055 100644 --- a/src/core/game_list.cpp +++ b/src/core/game_list.cpp @@ -40,7 +40,7 @@ namespace { enum : u32 { - GAME_LIST_CACHE_SIGNATURE = 0x45434C47, + GAME_LIST_CACHE_SIGNATURE = 0x45434C48, GAME_LIST_CACHE_VERSION = 34, PLAYED_TIME_SERIAL_LENGTH = 32, @@ -232,6 +232,19 @@ bool GameList::GetDiscListEntry(const std::string& path, Entry* entry) entry->max_blocks = dentry->max_blocks; entry->supported_controllers = dentry->supported_controllers; entry->compatibility = dentry->compatibility; + + if (!cdi->HasSubImages()) + { + for (size_t i = 0; i < dentry->disc_set_serials.size(); i++) + { + if (dentry->disc_set_serials[i] == entry->serial) + { + entry->disc_set_name = dentry->disc_set_name; + entry->disc_set_index = static_cast(i); + break; + } + } + } } else { @@ -318,12 +331,13 @@ bool GameList::LoadEntriesFromCache(ByteStream* stream) if (!stream->ReadU8(&type) || !stream->ReadU8(®ion) || !stream->ReadSizePrefixedString(&path) || !stream->ReadSizePrefixedString(&ge.serial) || !stream->ReadSizePrefixedString(&ge.title) || - !stream->ReadSizePrefixedString(&ge.genre) || !stream->ReadSizePrefixedString(&ge.publisher) || - !stream->ReadSizePrefixedString(&ge.developer) || !stream->ReadU64(&ge.hash) || - !stream->ReadU64(&ge.total_size) || !stream->ReadU64(reinterpret_cast(&ge.last_modified_time)) || - !stream->ReadU64(&ge.release_date) || !stream->ReadU16(&ge.supported_controllers) || - !stream->ReadU8(&ge.min_players) || !stream->ReadU8(&ge.max_players) || !stream->ReadU8(&ge.min_blocks) || - !stream->ReadU8(&ge.max_blocks) || !stream->ReadU8(&compatibility_rating) || + !stream->ReadSizePrefixedString(&ge.disc_set_name) || !stream->ReadSizePrefixedString(&ge.genre) || + !stream->ReadSizePrefixedString(&ge.publisher) || !stream->ReadSizePrefixedString(&ge.developer) || + !stream->ReadU64(&ge.hash) || !stream->ReadU64(&ge.total_size) || + !stream->ReadU64(reinterpret_cast(&ge.last_modified_time)) || !stream->ReadU64(&ge.release_date) || + !stream->ReadU16(&ge.supported_controllers) || !stream->ReadU8(&ge.min_players) || + !stream->ReadU8(&ge.max_players) || !stream->ReadU8(&ge.min_blocks) || !stream->ReadU8(&ge.max_blocks) || + !stream->ReadS8(&ge.disc_set_index) || !stream->ReadU8(&compatibility_rating) || region >= static_cast(DiscRegion::Count) || type >= static_cast(EntryType::Count) || compatibility_rating >= static_cast(GameDatabase::CompatibilityRating::Count)) { @@ -354,6 +368,7 @@ bool GameList::WriteEntryToCache(const Entry* entry) result &= s_cache_write_stream->WriteSizePrefixedString(entry->path); result &= s_cache_write_stream->WriteSizePrefixedString(entry->serial); result &= s_cache_write_stream->WriteSizePrefixedString(entry->title); + result &= s_cache_write_stream->WriteSizePrefixedString(entry->disc_set_name); result &= s_cache_write_stream->WriteSizePrefixedString(entry->genre); result &= s_cache_write_stream->WriteSizePrefixedString(entry->publisher); result &= s_cache_write_stream->WriteSizePrefixedString(entry->developer); @@ -366,6 +381,7 @@ bool GameList::WriteEntryToCache(const Entry* entry) result &= s_cache_write_stream->WriteU8(entry->max_players); result &= s_cache_write_stream->WriteU8(entry->min_blocks); result &= s_cache_write_stream->WriteU8(entry->max_blocks); + result &= s_cache_write_stream->WriteS8(entry->disc_set_index); result &= s_cache_write_stream->WriteU8(static_cast(entry->compatibility)); return result; } @@ -581,7 +597,7 @@ const GameList::Entry* GameList::GetEntryForPath(const char* path) return nullptr; } -const GameList::Entry* GameList::GetEntryBySerial(const std::string_view& serial) +const GameList::Entry* GameList::GetEntryBySerial(std::string_view serial) { for (const Entry& entry : s_entries) { @@ -595,7 +611,7 @@ const GameList::Entry* GameList::GetEntryBySerial(const std::string_view& serial return nullptr; } -const GameList::Entry* GameList::GetEntryBySerialAndHash(const std::string_view& serial, u64 hash) +const GameList::Entry* GameList::GetEntryBySerialAndHash(std::string_view serial, u64 hash) { for (const Entry& entry : s_entries) { @@ -606,6 +622,19 @@ const GameList::Entry* GameList::GetEntryBySerialAndHash(const std::string_view& return nullptr; } +std::vector GameList::GetDiscSetMembers(std::string_view disc_set_name) +{ + std::vector ret; + for (const Entry& entry : s_entries) + { + if (/*!entry.disc_set_member || */ disc_set_name != entry.disc_set_name) + continue; + + ret.push_back(&entry); + } + return ret; +} + u32 GameList::GetEntryCount() { return static_cast(s_entries.size()); diff --git a/src/core/game_list.h b/src/core/game_list.h index 93fdcdbd3..e5e56c9c0 100644 --- a/src/core/game_list.h +++ b/src/core/game_list.h @@ -39,6 +39,7 @@ struct Entry std::string path; std::string serial; std::string title; + std::string disc_set_name; std::string genre; std::string publisher; std::string developer; @@ -54,6 +55,7 @@ struct Entry u8 max_players = 1; u8 min_blocks = 0; u8 max_blocks = 0; + s8 disc_set_index = -1; GameDatabase::CompatibilityRating compatibility = GameDatabase::CompatibilityRating::Unknown; @@ -75,8 +77,9 @@ bool PopulateEntryFromPath(const std::string& path, Entry* entry); std::unique_lock GetLock(); const Entry* GetEntryByIndex(u32 index); const Entry* GetEntryForPath(const char* path); -const Entry* GetEntryBySerial(const std::string_view& serial); -const Entry* GetEntryBySerialAndHash(const std::string_view& serial, u64 hash); +const Entry* GetEntryBySerial(std::string_view serial); +const Entry* GetEntryBySerialAndHash(std::string_view serial, u64 hash); +std::vector GetDiscSetMembers(std::string_view disc_set_name); u32 GetEntryCount(); bool IsGameListLoaded(); diff --git a/src/core/memory_card_image.cpp b/src/core/memory_card_image.cpp index 777826b29..0b970f71d 100644 --- a/src/core/memory_card_image.cpp +++ b/src/core/memory_card_image.cpp @@ -229,6 +229,9 @@ u32 MemoryCardImage::GetFreeBlockCount(const DataArray& data) std::vector MemoryCardImage::EnumerateFiles(const DataArray& data, bool include_deleted) { + // For getting the icon, we only consider binary transparency. Some games set the alpha to 0. + static constexpr auto icon_to_rgba8 = [](u16 col) { return (col == 0) ? 0u : VRAMRGBA5551ToRGBA8888(col | 0x8000); }; + std::vector files; for (u32 dir_frame = 1; dir_frame < FRAMES_PER_BLOCK; dir_frame++) @@ -295,8 +298,8 @@ std::vector MemoryCardImage::EnumerateFiles(const Dat u32* pixels_ptr = fi.icon_frames[icon_frame].pixels; for (u32 i = 0; i < ICON_WIDTH * ICON_HEIGHT; i += 2) { - *(pixels_ptr++) = VRAMRGBA5551ToRGBA8888(tf->icon_palette[*indices_ptr & 0xF]); - *(pixels_ptr++) = VRAMRGBA5551ToRGBA8888(tf->icon_palette[*indices_ptr >> 4]); + *(pixels_ptr++) = icon_to_rgba8(tf->icon_palette[*indices_ptr & 0xF]); + *(pixels_ptr++) = icon_to_rgba8(tf->icon_palette[*indices_ptr >> 4]); indices_ptr++; } } diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 14c5af45f..54840fce5 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -822,17 +822,25 @@ void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidg } break; case MemoryCardType::PerGame: - paths[i] = QString::fromStdString(g_settings.GetGameMemoryCardPath(entry->serial.c_str(), i)); + paths[i] = QString::fromStdString(g_settings.GetGameMemoryCardPath(entry->serial, i)); break; case MemoryCardType::PerGameTitle: + { paths[i] = QString::fromStdString( - g_settings.GetGameMemoryCardPath(MemoryCard::SanitizeGameTitleForFileName(entry->title).c_str(), i)); - break; + g_settings.GetGameMemoryCardPath(MemoryCard::SanitizeGameTitleForFileName(entry->title), i)); + if (!entry->disc_set_name.empty() && g_settings.memory_card_use_playlist_title && !QFile::exists(paths[i])) + { + paths[i] = QString::fromStdString( + g_settings.GetGameMemoryCardPath(MemoryCard::SanitizeGameTitleForFileName(entry->disc_set_name), i)); + } + } + break; + case MemoryCardType::PerGameFileTitle: { const std::string display_name(FileSystem::GetDisplayNameFromPath(entry->path)); paths[i] = QString::fromStdString(g_settings.GetGameMemoryCardPath( - MemoryCard::SanitizeGameTitleForFileName(Path::GetFileTitle(display_name)).c_str(), i)); + MemoryCard::SanitizeGameTitleForFileName(Path::GetFileTitle(display_name)), i)); } break; default: