GameDatabase: Store strings as views
Saves duplicating everything in memory, and a ton of heap allocations.
This commit is contained in:
parent
86d66ddf82
commit
d8fef6f22e
|
@ -41,6 +41,20 @@
|
|||
return true;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static bool GetStringFromObject(const ryml::ConstNodeRef& object, std::string_view key,
|
||||
std::string_view* dest)
|
||||
{
|
||||
const ryml::ConstNodeRef member = object.find_child(to_csubstr(key));
|
||||
if (!member.valid())
|
||||
{
|
||||
*dest = std::string_view();
|
||||
return false;
|
||||
}
|
||||
|
||||
*dest = to_stringview(member.val());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[maybe_unused]] static bool GetUIntFromObject(const ryml::ConstNodeRef& object, std::string_view key, T* dest)
|
||||
{
|
||||
|
|
|
@ -6418,12 +6418,13 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
|||
if (selected_entry->dbentry && !selected_entry->dbentry->developer.empty())
|
||||
{
|
||||
text_width =
|
||||
ImGui::CalcTextSize(selected_entry->dbentry->developer.c_str(),
|
||||
selected_entry->dbentry->developer.c_str() + selected_entry->dbentry->developer.length(),
|
||||
ImGui::CalcTextSize(selected_entry->dbentry->developer.data(),
|
||||
selected_entry->dbentry->developer.data() + selected_entry->dbentry->developer.length(),
|
||||
false, work_width)
|
||||
.x;
|
||||
ImGui::SetCursorPosX((work_width - text_width) / 2.0f);
|
||||
ImGui::TextWrapped("%s", selected_entry->dbentry->developer.c_str());
|
||||
ImGui::TextWrapped("%.*s", static_cast<int>(selected_entry->dbentry->developer.size()),
|
||||
selected_entry->dbentry->developer.data());
|
||||
}
|
||||
|
||||
// code
|
||||
|
@ -6452,7 +6453,10 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
|||
|
||||
// genre
|
||||
if (selected_entry->dbentry && !selected_entry->dbentry->genre.empty())
|
||||
ImGui::Text(FSUI_CSTR("Genre: %s"), selected_entry->dbentry->genre.c_str());
|
||||
{
|
||||
ImGui::Text(FSUI_CSTR("Genre: %.*s"), static_cast<int>(selected_entry->dbentry->genre.size()),
|
||||
selected_entry->dbentry->genre.data());
|
||||
}
|
||||
|
||||
// release date
|
||||
ImGui::Text(FSUI_CSTR("Release Date: %s"), selected_entry->GetReleaseDateString().c_str());
|
||||
|
|
|
@ -147,6 +147,7 @@ static constexpr const char* DISCDB_YAML_FILENAME = "discdb.yaml";
|
|||
static bool s_loaded = false;
|
||||
static bool s_track_hashes_loaded = false;
|
||||
|
||||
static DynamicHeapArray<u8> s_db_data; // we take strings from the data, so store a copy
|
||||
static std::vector<GameDatabase::Entry> s_entries;
|
||||
static PreferUnorderedStringMap<u32> s_code_lookup;
|
||||
|
||||
|
@ -167,8 +168,15 @@ void GameDatabase::EnsureLoaded()
|
|||
s_entries = {};
|
||||
s_code_lookup = {};
|
||||
|
||||
LoadGameDBYaml();
|
||||
SaveToCache();
|
||||
if (LoadGameDBYaml())
|
||||
{
|
||||
SaveToCache();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_entries = {};
|
||||
s_code_lookup = {};
|
||||
}
|
||||
}
|
||||
|
||||
INFO_LOG("Database load of {} entries took {:.0f}ms.", s_entries.size(), timer.GetTimeMilliseconds());
|
||||
|
@ -848,14 +856,15 @@ static std::string GetCacheFile()
|
|||
|
||||
bool GameDatabase::LoadFromCache()
|
||||
{
|
||||
auto fp = FileSystem::OpenManagedCFile(GetCacheFile().c_str(), "rb");
|
||||
if (!fp)
|
||||
Error error;
|
||||
std::optional<DynamicHeapArray<u8>> db_data = FileSystem::ReadBinaryFile(GetCacheFile().c_str(), &error);
|
||||
if (!db_data.has_value())
|
||||
{
|
||||
DEV_LOG("Cache does not exist, loading full database.");
|
||||
DEV_LOG("Failed to read cache, loading full database: {}", error.GetDescription());
|
||||
return false;
|
||||
}
|
||||
|
||||
BinaryFileReader reader(fp.get());
|
||||
BinarySpanReader reader(db_data->cspan());
|
||||
const u64 gamedb_ts = Host::GetResourceFileTimestamp("gamedb.yaml", false).value_or(0);
|
||||
|
||||
u32 signature, version, num_entries, num_codes;
|
||||
|
@ -950,6 +959,7 @@ bool GameDatabase::LoadFromCache()
|
|||
s_code_lookup.emplace(std::move(code), index);
|
||||
}
|
||||
|
||||
s_db_data = std::move(db_data.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1051,7 +1061,7 @@ void GameDatabase::SetRymlCallbacks()
|
|||
|
||||
bool GameDatabase::LoadGameDBYaml()
|
||||
{
|
||||
const std::optional<std::string> gamedb_data = Host::ReadResourceFileToString(GAMEDB_YAML_FILENAME, false);
|
||||
std::optional<DynamicHeapArray<u8>> gamedb_data = Host::ReadResourceFile(GAMEDB_YAML_FILENAME, false);
|
||||
if (!gamedb_data.has_value())
|
||||
{
|
||||
ERROR_LOG("Failed to read game database");
|
||||
|
@ -1060,7 +1070,8 @@ bool GameDatabase::LoadGameDBYaml()
|
|||
|
||||
SetRymlCallbacks();
|
||||
|
||||
const ryml::Tree tree = ryml::parse_in_arena(to_csubstr(GAMEDB_YAML_FILENAME), to_csubstr(gamedb_data.value()));
|
||||
const ryml::Tree tree = ryml::parse_in_place(
|
||||
to_csubstr(GAMEDB_YAML_FILENAME), c4::substr(reinterpret_cast<char*>(gamedb_data->data()), gamedb_data->size()));
|
||||
const ryml::ConstNodeRef root = tree.rootref();
|
||||
s_entries.reserve(root.num_children());
|
||||
|
||||
|
@ -1108,7 +1119,14 @@ bool GameDatabase::LoadGameDBYaml()
|
|||
ERROR_LOG("Failed to insert code {}", code);
|
||||
}
|
||||
|
||||
return !s_entries.empty();
|
||||
if (s_entries.empty())
|
||||
{
|
||||
ERROR_LOG("Game database is empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
s_db_data = std::move(gamedb_data.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameDatabase::ParseYamlEntry(Entry* entry, const ryml::ConstNodeRef& value)
|
||||
|
|
|
@ -92,14 +92,13 @@ enum class Language : u8
|
|||
|
||||
struct Entry
|
||||
{
|
||||
// TODO: Make string_view.
|
||||
std::string serial;
|
||||
std::string title;
|
||||
std::string genre;
|
||||
std::string developer;
|
||||
std::string publisher;
|
||||
std::string compatibility_version_tested;
|
||||
std::string compatibility_comments;
|
||||
std::string_view serial;
|
||||
std::string_view title;
|
||||
std::string_view genre;
|
||||
std::string_view developer;
|
||||
std::string_view publisher;
|
||||
std::string_view compatibility_version_tested;
|
||||
std::string_view compatibility_comments;
|
||||
u64 release_date;
|
||||
u8 min_players;
|
||||
u8 max_players;
|
||||
|
|
|
@ -415,24 +415,22 @@ QVariant GameListModel::data(const QModelIndex& index, int role, const GameList:
|
|||
switch (index.column())
|
||||
{
|
||||
case Column_Serial:
|
||||
return QString::fromStdString(ge->serial);
|
||||
return QtUtils::StringViewToQString(ge->serial);
|
||||
|
||||
case Column_Title:
|
||||
return QString::fromStdString(ge->title);
|
||||
return QtUtils::StringViewToQString(ge->title);
|
||||
|
||||
case Column_FileTitle:
|
||||
return QtUtils::StringViewToQString(Path::GetFileTitle(ge->path));
|
||||
|
||||
case Column_Developer:
|
||||
return (ge->dbentry && !ge->dbentry->developer.empty()) ? QString::fromStdString(ge->dbentry->developer) :
|
||||
QString();
|
||||
return ge->dbentry ? QtUtils::StringViewToQString(ge->dbentry->developer) : QString();
|
||||
|
||||
case Column_Publisher:
|
||||
return (ge->dbentry && !ge->dbentry->publisher.empty()) ? QString::fromStdString(ge->dbentry->publisher) :
|
||||
QString();
|
||||
return ge->dbentry ? QtUtils::StringViewToQString(ge->dbentry->publisher) : QString();
|
||||
|
||||
case Column_Genre:
|
||||
return (ge->dbentry && !ge->dbentry->genre.empty()) ? QString::fromStdString(ge->dbentry->genre) : QString();
|
||||
return ge->dbentry ? QtUtils::StringViewToQString(ge->dbentry->genre) : QString();
|
||||
|
||||
case Column_Year:
|
||||
{
|
||||
|
@ -505,15 +503,13 @@ QVariant GameListModel::data(const QModelIndex& index, int role, const GameList:
|
|||
return QtUtils::StringViewToQString(Path::GetFileTitle(ge->path));
|
||||
|
||||
case Column_Developer:
|
||||
return (ge->dbentry && !ge->dbentry->developer.empty()) ? QString::fromStdString(ge->dbentry->developer) :
|
||||
QString();
|
||||
return ge->dbentry ? QtUtils::StringViewToQString(ge->dbentry->developer) : QString();
|
||||
|
||||
case Column_Publisher:
|
||||
return (ge->dbentry && !ge->dbentry->publisher.empty()) ? QString::fromStdString(ge->dbentry->publisher) :
|
||||
QString();
|
||||
return ge->dbentry ? QtUtils::StringViewToQString(ge->dbentry->publisher) : QString();
|
||||
|
||||
case Column_Genre:
|
||||
return (ge->dbentry && !ge->dbentry->genre.empty()) ? QString::fromStdString(ge->dbentry->genre) : QString();
|
||||
return ge->dbentry ? QtUtils::StringViewToQString(ge->dbentry->genre) : QString();
|
||||
|
||||
case Column_Year:
|
||||
return ge->dbentry ? QDateTime::fromSecsSinceEpoch(static_cast<qint64>(ge->dbentry->release_date), Qt::UTC)
|
||||
|
|
|
@ -83,17 +83,17 @@ void GameSummaryWidget::populateUi(const std::string& path, const std::string& s
|
|||
|
||||
if (entry)
|
||||
{
|
||||
m_ui.title->setText(QString::fromStdString(entry->title));
|
||||
m_ui.title->setText(QtUtils::StringViewToQString(entry->title));
|
||||
m_ui.compatibility->setCurrentIndex(static_cast<int>(entry->compatibility));
|
||||
m_ui.genre->setText(entry->genre.empty() ? tr("Unknown") : QString::fromStdString(entry->genre));
|
||||
m_ui.genre->setText(entry->genre.empty() ? tr("Unknown") : QtUtils::StringViewToQString(entry->genre));
|
||||
if (!entry->developer.empty() && !entry->publisher.empty() && entry->developer != entry->publisher)
|
||||
m_ui.developer->setText(tr("%1 (Published by %2)")
|
||||
.arg(QString::fromStdString(entry->developer))
|
||||
.arg(QString::fromStdString(entry->publisher)));
|
||||
.arg(QtUtils::StringViewToQString(entry->developer))
|
||||
.arg(QtUtils::StringViewToQString(entry->publisher)));
|
||||
else if (!entry->developer.empty())
|
||||
m_ui.developer->setText(QString::fromStdString(entry->developer));
|
||||
m_ui.developer->setText(QtUtils::StringViewToQString(entry->developer));
|
||||
else if (!entry->publisher.empty())
|
||||
m_ui.developer->setText(tr("Published by %1").arg(QString::fromStdString(entry->publisher)));
|
||||
m_ui.developer->setText(tr("Published by %1").arg(QtUtils::StringViewToQString(entry->publisher)));
|
||||
else
|
||||
m_ui.developer->setText(tr("Unknown"));
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ SettingsWindow::SettingsWindow() : QWidget()
|
|||
connectUi();
|
||||
}
|
||||
|
||||
SettingsWindow::SettingsWindow(const std::string& path, const std::string& serial, DiscRegion region,
|
||||
SettingsWindow::SettingsWindow(const std::string& path, std::string serial, DiscRegion region,
|
||||
const GameDatabase::Entry* entry, std::unique_ptr<INISettingsInterface> sif)
|
||||
: QWidget(), m_sif(std::move(sif)), m_database_entry(entry)
|
||||
{
|
||||
|
@ -656,7 +656,7 @@ void SettingsWindow::openGamePropertiesDialog(const std::string& path, const std
|
|||
}
|
||||
}
|
||||
|
||||
const std::string& real_serial = dentry ? dentry->serial : serial;
|
||||
std::string real_serial = dentry ? std::string(dentry->serial) : std::move(serial);
|
||||
std::string ini_filename = System::GetGameSettingsPath(real_serial);
|
||||
|
||||
// check for an existing dialog with this crc
|
||||
|
@ -677,7 +677,7 @@ void SettingsWindow::openGamePropertiesDialog(const std::string& path, const std
|
|||
if (FileSystem::FileExists(sif->GetFileName().c_str()))
|
||||
sif->Load();
|
||||
|
||||
SettingsWindow* dialog = new SettingsWindow(path, real_serial, region, dentry, std::move(sif));
|
||||
SettingsWindow* dialog = new SettingsWindow(path, std::string(real_serial), region, dentry, std::move(sif));
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
|
|
|
@ -41,8 +41,8 @@ class SettingsWindow final : public QWidget
|
|||
|
||||
public:
|
||||
SettingsWindow();
|
||||
SettingsWindow(const std::string& path, const std::string& serial, DiscRegion region,
|
||||
const GameDatabase::Entry* entry, std::unique_ptr<INISettingsInterface> sif);
|
||||
SettingsWindow(const std::string& path, std::string serial, DiscRegion region, const GameDatabase::Entry* entry,
|
||||
std::unique_ptr<INISettingsInterface> sif);
|
||||
~SettingsWindow();
|
||||
|
||||
static void openGamePropertiesDialog(const std::string& path, const std::string& title, const std::string& serial,
|
||||
|
|
Loading…
Reference in New Issue