mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 00:49:02 -06:00 
			
		
		
		
	Merge pull request #2431 from DarkLordZach/game-list-cache
yuzu: Implement a caching mechanism for the game list
This commit is contained in:
		@@ -645,6 +645,8 @@ void Config::ReadUIGamelistValues() {
 | 
			
		||||
    UISettings::values.icon_size = ReadSetting(QStringLiteral("icon_size"), 64).toUInt();
 | 
			
		||||
    UISettings::values.row_1_text_id = ReadSetting(QStringLiteral("row_1_text_id"), 3).toUInt();
 | 
			
		||||
    UISettings::values.row_2_text_id = ReadSetting(QStringLiteral("row_2_text_id"), 2).toUInt();
 | 
			
		||||
    UISettings::values.cache_game_list =
 | 
			
		||||
        ReadSetting(QStringLiteral("cache_game_list"), true).toBool();
 | 
			
		||||
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
}
 | 
			
		||||
@@ -1009,6 +1011,7 @@ void Config::SaveUIGamelistValues() {
 | 
			
		||||
    WriteSetting(QStringLiteral("icon_size"), UISettings::values.icon_size, 64);
 | 
			
		||||
    WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3);
 | 
			
		||||
    WriteSetting(QStringLiteral("row_2_text_id"), UISettings::values.row_2_text_id, 2);
 | 
			
		||||
    WriteSetting(QStringLiteral("cache_game_list"), UISettings::values.cache_game_list, true);
 | 
			
		||||
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,8 @@
 | 
			
		||||
#include <QTimer>
 | 
			
		||||
#include <QTreeView>
 | 
			
		||||
 | 
			
		||||
#include "common/common_paths.h"
 | 
			
		||||
#include "common/file_util.h"
 | 
			
		||||
#include "core/file_sys/control_metadata.h"
 | 
			
		||||
#include "core/file_sys/patch_manager.h"
 | 
			
		||||
#include "core/file_sys/xts_archive.h"
 | 
			
		||||
@@ -79,6 +81,14 @@ void ConfigurePerGameGeneral::applyConfiguration() {
 | 
			
		||||
            disabled_addons.push_back(item.front()->text().toStdString());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto current = Settings::values.disabled_addons[title_id];
 | 
			
		||||
    std::sort(disabled_addons.begin(), disabled_addons.end());
 | 
			
		||||
    std::sort(current.begin(), current.end());
 | 
			
		||||
    if (disabled_addons != current) {
 | 
			
		||||
        FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
 | 
			
		||||
                         "game_list" + DIR_SEP + fmt::format("{:016X}.pv.txt", title_id));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Settings::values.disabled_addons[title_id] = disabled_addons;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@
 | 
			
		||||
 | 
			
		||||
#include <QDir>
 | 
			
		||||
#include <QFileInfo>
 | 
			
		||||
#include <QSettings>
 | 
			
		||||
 | 
			
		||||
#include "common/common_paths.h"
 | 
			
		||||
#include "common/file_util.h"
 | 
			
		||||
@@ -30,13 +31,119 @@
 | 
			
		||||
#include "yuzu/ui_settings.h"
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
T GetGameListCachedObject(const std::string& filename, const std::string& ext,
 | 
			
		||||
                          const std::function<T()>& generator);
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
QString GetGameListCachedObject(const std::string& filename, const std::string& ext,
 | 
			
		||||
                                const std::function<QString()>& generator) {
 | 
			
		||||
    if (!UISettings::values.cache_game_list || filename == "0000000000000000") {
 | 
			
		||||
        return generator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto path = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" +
 | 
			
		||||
                      DIR_SEP + filename + '.' + ext;
 | 
			
		||||
 | 
			
		||||
    FileUtil::CreateFullPath(path);
 | 
			
		||||
 | 
			
		||||
    if (!FileUtil::Exists(path)) {
 | 
			
		||||
        const auto str = generator();
 | 
			
		||||
 | 
			
		||||
        std::ofstream stream(path);
 | 
			
		||||
        if (stream) {
 | 
			
		||||
            stream << str.toStdString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return str;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ifstream stream(path);
 | 
			
		||||
 | 
			
		||||
    if (stream) {
 | 
			
		||||
        const std::string out(std::istreambuf_iterator<char>{stream},
 | 
			
		||||
                              std::istreambuf_iterator<char>{});
 | 
			
		||||
        return QString::fromStdString(out);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return generator();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
std::pair<std::vector<u8>, std::string> GetGameListCachedObject(
 | 
			
		||||
    const std::string& filename, const std::string& ext,
 | 
			
		||||
    const std::function<std::pair<std::vector<u8>, std::string>()>& generator) {
 | 
			
		||||
    if (!UISettings::values.cache_game_list || filename == "0000000000000000") {
 | 
			
		||||
        return generator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto path1 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" +
 | 
			
		||||
                       DIR_SEP + filename + ".jpeg";
 | 
			
		||||
    const auto path2 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" +
 | 
			
		||||
                       DIR_SEP + filename + ".appname.txt";
 | 
			
		||||
 | 
			
		||||
    FileUtil::CreateFullPath(path1);
 | 
			
		||||
 | 
			
		||||
    if (!FileUtil::Exists(path1) || !FileUtil::Exists(path2)) {
 | 
			
		||||
        const auto [icon, nacp] = generator();
 | 
			
		||||
 | 
			
		||||
        FileUtil::IOFile file1(path1, "wb");
 | 
			
		||||
        if (!file1.IsOpen()) {
 | 
			
		||||
            LOG_ERROR(Frontend, "Failed to open cache file.");
 | 
			
		||||
            return generator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!file1.Resize(icon.size())) {
 | 
			
		||||
            LOG_ERROR(Frontend, "Failed to resize cache file to necessary size.");
 | 
			
		||||
            return generator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (file1.WriteBytes(icon.data(), icon.size()) != icon.size()) {
 | 
			
		||||
            LOG_ERROR(Frontend, "Failed to write data to cache file.");
 | 
			
		||||
            return generator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::ofstream stream2(path2, std::ios::out);
 | 
			
		||||
        if (stream2) {
 | 
			
		||||
            stream2 << nacp;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return std::make_pair(icon, nacp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FileUtil::IOFile file1(path1, "rb");
 | 
			
		||||
    std::ifstream stream2(path2);
 | 
			
		||||
 | 
			
		||||
    if (!file1.IsOpen()) {
 | 
			
		||||
        LOG_ERROR(Frontend, "Failed to open cache file for reading.");
 | 
			
		||||
        return generator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!stream2) {
 | 
			
		||||
        LOG_ERROR(Frontend, "Failed to open cache file for reading.");
 | 
			
		||||
        return generator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<u8> vec(file1.GetSize());
 | 
			
		||||
    file1.ReadBytes(vec.data(), vec.size());
 | 
			
		||||
 | 
			
		||||
    if (stream2 && !vec.empty()) {
 | 
			
		||||
        const std::string out(std::istreambuf_iterator<char>{stream2},
 | 
			
		||||
                              std::istreambuf_iterator<char>{});
 | 
			
		||||
        return std::make_pair(vec, out);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return generator();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const FileSys::NCA& nca,
 | 
			
		||||
                               std::vector<u8>& icon, std::string& name) {
 | 
			
		||||
    auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca);
 | 
			
		||||
    if (icon_file != nullptr)
 | 
			
		||||
        icon = icon_file->ReadAllBytes();
 | 
			
		||||
    if (nacp != nullptr)
 | 
			
		||||
        name = nacp->GetApplicationName();
 | 
			
		||||
    std::tie(icon, name) = GetGameListCachedObject<std::pair<std::vector<u8>, std::string>>(
 | 
			
		||||
        fmt::format("{:016X}", patch_manager.GetTitleID()), {}, [&patch_manager, &nca] {
 | 
			
		||||
            const auto [nacp, icon_f] = patch_manager.ParseControlNCA(nca);
 | 
			
		||||
            return std::make_pair(icon_f->ReadAllBytes(), nacp->GetApplicationName());
 | 
			
		||||
        });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool HasSupportedFileExtension(const std::string& file_name) {
 | 
			
		||||
@@ -114,8 +221,11 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (UISettings::values.show_add_ons) {
 | 
			
		||||
        list.insert(
 | 
			
		||||
            2, new GameListItem(FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable())));
 | 
			
		||||
        const auto patch_versions = GetGameListCachedObject<QString>(
 | 
			
		||||
            fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] {
 | 
			
		||||
                return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable());
 | 
			
		||||
            });
 | 
			
		||||
        list.insert(2, new GameListItem(patch_versions));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return list;
 | 
			
		||||
 
 | 
			
		||||
@@ -1396,6 +1396,8 @@ void GMainWindow::OnMenuInstallToNAND() {
 | 
			
		||||
                                 tr("The file was successfully installed."));
 | 
			
		||||
        game_list->PopulateAsync(UISettings::values.game_directory_path,
 | 
			
		||||
                                 UISettings::values.game_directory_deepscan);
 | 
			
		||||
        FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
 | 
			
		||||
                                       DIR_SEP + "game_list");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const auto failed = [this]() {
 | 
			
		||||
 
 | 
			
		||||
@@ -79,6 +79,7 @@ struct Values {
 | 
			
		||||
    uint8_t row_1_text_id;
 | 
			
		||||
    uint8_t row_2_text_id;
 | 
			
		||||
    std::atomic_bool is_game_list_reload_pending{false};
 | 
			
		||||
    bool cache_game_list;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern Values values;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user