// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include #include #include "common/path_util.h" #include "compatibility_info.h" struct GameInfo { std::filesystem::path path; // root path of game directory // (normally directory that contains eboot.bin) std::filesystem::path icon_path; // path of icon0.png std::filesystem::path pic_path; // path of pic1.png std::filesystem::path snd0_path; // path of snd0.at9 QImage icon; std::string size; // variables extracted from param.sfo std::string name = "Unknown"; std::string serial = "Unknown"; std::string version = "Unknown"; std::string region = "Unknown"; std::string fw = "Unknown"; std::string play_time = "Unknown"; CompatibilityEntry compatibility = CompatibilityEntry{CompatibilityStatus::Unknown}; }; class GameListUtils : public QObject { Q_OBJECT public: static QString FormatSize(qint64 size) { static const QStringList suffixes = {tr("B"), tr("KB"), tr("MB"), tr("GB"), tr("TB")}; int suffixIndex = 0; double gameSize = static_cast(size); while (gameSize >= 1024 && suffixIndex < suffixes.size() - 1) { gameSize /= 1024; ++suffixIndex; } // Format the size with a specified precision QString sizeString; if (gameSize < 10.0) { sizeString = QString::number(gameSize, 'f', 2); } else if (gameSize < 100.0) { sizeString = QString::number(gameSize, 'f', 1); } else { sizeString = QString::number(gameSize, 'f', 0); } return sizeString + " " + suffixes[suffixIndex]; } static void GetFolderSize(GameInfo& game) { QString dirPath; Common::FS::PathToQString(dirPath, game.path); QDir dir(dirPath); QDirIterator it(dir.absolutePath(), QDirIterator::Subdirectories); qint64 total = 0; if (!Config::GetLoadGameSizeEnabled()) { game.size = FormatSize(0).toStdString(); return; } // Cache path QFile size_cache_file(Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game.serial / "size_cache.txt"); QFileInfo cacheInfo(size_cache_file); QFileInfo dirInfo(dirPath); // Check if cache file exists and is valid if (size_cache_file.exists() && cacheInfo.lastModified() >= dirInfo.lastModified()) { if (size_cache_file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in(&size_cache_file); QString cachedSize = in.readLine(); size_cache_file.close(); if (!cachedSize.isEmpty()) { game.size = cachedSize.toStdString(); return; } } } // Cache is invalid or does not exist; calculate size while (it.hasNext()) { it.next(); total += it.fileInfo().size(); } game.size = FormatSize(total).toStdString(); // Save new cache if (size_cache_file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&size_cache_file); out << QString::fromStdString(game.size) << "\n"; size_cache_file.close(); } } static QString GetRegion(char region) { switch (region) { case 'U': return "USA"; case 'E': return "Europe"; case 'J': return "Japan"; case 'H': return "Asia"; case 'I': return "World"; default: return "Unknown"; } } static QString GetAppType(int type) { switch (type) { case 0: return "Not Specified"; case 1: return "FULL APP"; case 2: return "UPGRADABLE"; case 3: return "DEMO"; case 4: return "FREEMIUM"; default: return "Unknown"; } } QImage BlurImage(const QImage& image, const QRect& rect, int radius) { int tab[] = {14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2}; int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius - 1]; QImage result = image.convertToFormat(QImage::Format_ARGB32); int r1 = rect.top(); int r2 = rect.bottom(); int c1 = rect.left(); int c2 = rect.right(); int bpl = result.bytesPerLine(); int rgba[4]; unsigned char* p; int i1 = 0; int i2 = 3; for (int col = c1; col <= c2; col++) { p = result.scanLine(r1) + col * 4; for (int i = i1; i <= i2; i++) rgba[i] = p[i] << 4; p += bpl; for (int j = r1; j < r2; j++, p += bpl) for (int i = i1; i <= i2; i++) p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; } for (int row = r1; row <= r2; row++) { p = result.scanLine(row) + c1 * 4; for (int i = i1; i <= i2; i++) rgba[i] = p[i] << 4; p += 4; for (int j = c1; j < c2; j++, p += 4) for (int i = i1; i <= i2; i++) p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; } for (int col = c1; col <= c2; col++) { p = result.scanLine(r2) + col * 4; for (int i = i1; i <= i2; i++) rgba[i] = p[i] << 4; p -= bpl; for (int j = r1; j < r2; j++, p -= bpl) for (int i = i1; i <= i2; i++) p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; } for (int row = r1; row <= r2; row++) { p = result.scanLine(row) + c2 * 4; for (int i = i1; i <= i2; i++) rgba[i] = p[i] << 4; p -= 4; for (int j = c1; j < c2; j++, p -= 4) for (int i = i1; i <= i2; i++) p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; } return result; } };