// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2024 A-La-Karte Contributors #include "game.h" #include #include #include #include #include Game::Game(QObject *parent) : QObject(parent) , m_id(QUuid::createUuid().toString(QUuid::WithoutBraces)) , m_dateAdded(QDateTime::currentDateTime()) { } Game::Game(const QString &id, const QString &name, QObject *parent) : QObject(parent) , m_id(id.isEmpty() ? QUuid::createUuid().toString(QUuid::WithoutBraces) : id) , m_name(name) , m_dateAdded(QDateTime::currentDateTime()) { } QString Game::id() const { return m_id; } QString Game::name() const { return m_name; } void Game::setName(const QString &name) { if (m_name != name) { m_name = name; Q_EMIT nameChanged(); } } QString Game::description() const { return m_description; } void Game::setDescription(const QString &description) { if (m_description != description) { m_description = description; Q_EMIT descriptionChanged(); } } QString Game::developer() const { return m_developer; } void Game::setDeveloper(const QString &developer) { if (m_developer != developer) { m_developer = developer; Q_EMIT developerChanged(); } } QString Game::publisher() const { return m_publisher; } void Game::setPublisher(const QString &publisher) { if (m_publisher != publisher) { m_publisher = publisher; Q_EMIT publisherChanged(); } } QUrl Game::coverUrl() const { return m_coverUrl; } void Game::setCoverUrl(const QUrl &url) { if (m_coverUrl != url) { m_coverUrl = url; Q_EMIT coverUrlChanged(); } } QUrl Game::iconUrl() const { return m_iconUrl; } void Game::setIconUrl(const QUrl &url) { if (m_iconUrl != url) { m_iconUrl = url; Q_EMIT iconUrlChanged(); } } QString Game::launchCommand() const { return m_launchCommand; } void Game::setLaunchCommand(const QString &command) { if (m_launchCommand != command) { m_launchCommand = command; Q_EMIT launchCommandChanged(); } } QString Game::workingDirectory() const { return m_workingDirectory; } void Game::setWorkingDirectory(const QString &dir) { if (m_workingDirectory != dir) { m_workingDirectory = dir; Q_EMIT workingDirectoryChanged(); } } QVariantMap Game::launchEnv() const { return m_launchEnv; } void Game::setLaunchEnv(const QVariantMap &env) { if (m_launchEnv != env) { m_launchEnv = env; Q_EMIT launchEnvChanged(); } } QString Game::launchRunner() const { return m_launchRunner; } void Game::setLaunchRunner(const QString &runner) { if (m_launchRunner != runner) { m_launchRunner = runner; Q_EMIT launchRunnerChanged(); } } QString Game::launchRunnerPath() const { return m_launchRunnerPath; } void Game::setLaunchRunnerPath(const QString &path) { if (m_launchRunnerPath != path) { m_launchRunnerPath = path; Q_EMIT launchRunnerPathChanged(); } } QString Game::launchPrefixPath() const { return m_launchPrefixPath; } void Game::setLaunchPrefixPath(const QString &path) { if (m_launchPrefixPath != path) { m_launchPrefixPath = path; Q_EMIT launchPrefixPathChanged(); } } QString Game::platform() const { return m_platform; } void Game::setPlatform(const QString &platform) { if (m_platform != platform) { m_platform = platform; Q_EMIT platformChanged(); } } QString Game::platformId() const { return m_platformId; } void Game::setPlatformId(const QString &id) { if (m_platformId != id) { m_platformId = id; Q_EMIT platformIdChanged(); } } QDateTime Game::dateAdded() const { return m_dateAdded; } void Game::setDateAdded(const QDateTime &dateTime) { if (m_dateAdded != dateTime) { m_dateAdded = dateTime; Q_EMIT dateAddedChanged(); } } QDateTime Game::lastPlayed() const { return m_lastPlayed; } void Game::setLastPlayed(const QDateTime &dateTime) { if (m_lastPlayed != dateTime) { m_lastPlayed = dateTime; Q_EMIT lastPlayedChanged(); } } qint64 Game::playTime() const { return m_playTime; } void Game::setPlayTime(qint64 seconds) { if (m_playTime != seconds) { m_playTime = seconds; Q_EMIT playTimeChanged(); } } bool Game::favorite() const { return m_favorite; } void Game::setFavorite(bool favorite) { if (m_favorite != favorite) { m_favorite = favorite; Q_EMIT favoriteChanged(); } } bool Game::hidden() const { return m_hidden; } void Game::setHidden(bool hidden) { if (m_hidden != hidden) { m_hidden = hidden; Q_EMIT hiddenChanged(); } } bool Game::installed() const { return m_installed; } void Game::setInstalled(bool installed) { if (m_installed != installed) { m_installed = installed; Q_EMIT installedChanged(); } } bool Game::running() const { return m_running; } void Game::setRunning(bool running) { if (m_running != running) { m_running = running; Q_EMIT runningChanged(); } } QJsonObject Game::toJson() const { QJsonObject obj; obj[QStringLiteral("id")] = m_id; obj[QStringLiteral("name")] = m_name; obj[QStringLiteral("description")] = m_description; obj[QStringLiteral("developer")] = m_developer; obj[QStringLiteral("publisher")] = m_publisher; obj[QStringLiteral("coverUrl")] = m_coverUrl.toString(); obj[QStringLiteral("iconUrl")] = m_iconUrl.toString(); obj[QStringLiteral("launchCommand")] = m_launchCommand; obj[QStringLiteral("workingDirectory")] = m_workingDirectory; obj[QStringLiteral("platform")] = m_platform; obj[QStringLiteral("platformId")] = m_platformId; obj[QStringLiteral("dateAdded")] = m_dateAdded.toString(Qt::ISODate); obj[QStringLiteral("lastPlayed")] = m_lastPlayed.toString(Qt::ISODate); obj[QStringLiteral("playTime")] = m_playTime; obj[QStringLiteral("favorite")] = m_favorite; obj[QStringLiteral("hidden")] = m_hidden; obj[QStringLiteral("installed")] = m_installed; const bool hasLaunchConfig = !m_launchEnv.isEmpty() || !m_launchRunner.isEmpty() || !m_launchRunnerPath.isEmpty() || !m_launchPrefixPath.isEmpty(); if (hasLaunchConfig) { QJsonObject launchObj; if (!m_launchEnv.isEmpty()) { QJsonObject envObj; for (auto it = m_launchEnv.constBegin(); it != m_launchEnv.constEnd(); ++it) { envObj.insert(it.key(), QJsonValue::fromVariant(it.value())); } launchObj.insert(QStringLiteral("env"), envObj); } if (!m_launchRunner.isEmpty()) { launchObj.insert(QStringLiteral("runner"), m_launchRunner); } if (!m_launchRunnerPath.isEmpty()) { launchObj.insert(QStringLiteral("runnerPath"), m_launchRunnerPath); } if (!m_launchPrefixPath.isEmpty()) { launchObj.insert(QStringLiteral("prefixPath"), m_launchPrefixPath); } obj.insert(QStringLiteral("launch"), launchObj); } return obj; } Game *Game::fromJson(const QJsonObject &json, QObject *parent) { QString id = json[QStringLiteral("id")].toString(); QString name = json[QStringLiteral("name")].toString(); if (name.isEmpty()) { return nullptr; } const QString platformId = json[QStringLiteral("platformId")].toString(); const QString platform = json[QStringLiteral("platform")].toString(); const QString launchCommand = json[QStringLiteral("launchCommand")].toString(); const QString workingDirectory = json[QStringLiteral("workingDirectory")].toString(); if (id.isEmpty()) { const QByteArray fingerprint = (platformId + QLatin1Char('|') + platform + QLatin1Char('|') + name + QLatin1Char('|') + launchCommand + QLatin1Char('|') + workingDirectory) .toUtf8(); const QByteArray hash = QCryptographicHash::hash(fingerprint, QCryptographicHash::Sha1).toHex(); id = QStringLiteral("legacy-") + QString::fromLatin1(hash); } Game *game = new Game(id, name, parent); game->setDescription(json[QStringLiteral("description")].toString()); game->setDeveloper(json[QStringLiteral("developer")].toString()); game->setPublisher(json[QStringLiteral("publisher")].toString()); game->setCoverUrl(QUrl(json[QStringLiteral("coverUrl")].toString())); game->setIconUrl(QUrl(json[QStringLiteral("iconUrl")].toString())); game->setLaunchCommand(launchCommand); game->setWorkingDirectory(workingDirectory); game->setPlatform(platform); game->setPlatformId(platformId); game->setDateAdded(QDateTime::fromString(json[QStringLiteral("dateAdded")].toString(), Qt::ISODate)); game->setLastPlayed(QDateTime::fromString(json[QStringLiteral("lastPlayed")].toString(), Qt::ISODate)); game->setPlayTime(json[QStringLiteral("playTime")].toInteger()); game->setFavorite(json[QStringLiteral("favorite")].toBool()); game->setHidden(json[QStringLiteral("hidden")].toBool()); game->setInstalled(json[QStringLiteral("installed")].toBool(true)); QVariantMap env; QString runner; QString runnerPath; QString prefixPath; const QJsonValue launchValue = json.value(QStringLiteral("launch")); if (launchValue.isObject()) { const QJsonObject launchObj = launchValue.toObject(); const QJsonValue envValue = launchObj.value(QStringLiteral("env")); if (envValue.isObject()) { env = envValue.toObject().toVariantMap(); } runner = launchObj.value(QStringLiteral("runner")).toString(); runnerPath = launchObj.value(QStringLiteral("runnerPath")).toString(); prefixPath = launchObj.value(QStringLiteral("prefixPath")).toString(); } const QJsonValue legacyEnvValue = json.value(QStringLiteral("launchEnv")); if (env.isEmpty() && legacyEnvValue.isObject()) { env = legacyEnvValue.toObject().toVariantMap(); } if (runner.isEmpty()) { runner = json.value(QStringLiteral("launchRunner")).toString(); } if (runnerPath.isEmpty()) { runnerPath = json.value(QStringLiteral("launchRunnerPath")).toString(); } if (prefixPath.isEmpty()) { prefixPath = json.value(QStringLiteral("launchPrefixPath")).toString(); } if (!env.isEmpty()) { game->setLaunchEnv(env); } if (!runner.isEmpty()) { game->setLaunchRunner(runner); } if (!runnerPath.isEmpty()) { game->setLaunchRunnerPath(runnerPath); } if (!prefixPath.isEmpty()) { game->setLaunchPrefixPath(prefixPath); } return game; } QString Game::playTimeFormatted() const { if (m_playTime == 0) { return i18n("Never played"); } qint64 hours = m_playTime / 3600; qint64 minutes = (m_playTime % 3600) / 60; if (hours > 0) { return i18np("%1 hour", "%1 hours", hours) + QStringLiteral(" ") + i18np("%1 minute", "%1 minutes", minutes); } return i18np("%1 minute", "%1 minutes", minutes); } QString Game::lastPlayedFormatted() const { if (!m_lastPlayed.isValid()) { return i18n("Never"); } QDateTime now = QDateTime::currentDateTime(); qint64 days = m_lastPlayed.daysTo(now); if (days == 0) { return i18n("Today"); } else if (days == 1) { return i18n("Yesterday"); } else if (days < 7) { return i18np("%1 day ago", "%1 days ago", days); } else if (days < 30) { return i18np("%1 week ago", "%1 weeks ago", days / 7); } else if (days < 365) { return i18np("%1 month ago", "%1 months ago", days / 30); } return QLocale().toString(m_lastPlayed, QLocale::ShortFormat); } QString Game::dateAddedFormatted() const { if (!m_dateAdded.isValid()) { return i18n("Unknown"); } QDateTime now = QDateTime::currentDateTime(); qint64 days = m_dateAdded.daysTo(now); if (days == 0) { return i18n("Today"); } else if (days == 1) { return i18n("Yesterday"); } else if (days < 7) { return i18np("%1 day ago", "%1 days ago", days); } else if (days < 30) { return i18np("%1 week ago", "%1 weeks ago", days / 7); } else if (days < 365) { return i18np("%1 month ago", "%1 months ago", days / 30); } return QLocale().toString(m_dateAdded, QLocale::ShortFormat); }