mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-03-27 01:03:09 +00:00
320 lines
9.9 KiB
C++
320 lines
9.9 KiB
C++
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||
|
|
// SPDX-FileCopyrightText: 2024 A-La-Karte Contributors
|
||
|
|
|
||
|
|
#include "heroicimporter.h"
|
||
|
|
|
||
|
|
#include <QDir>
|
||
|
|
#include <QFile>
|
||
|
|
#include <QJsonArray>
|
||
|
|
#include <QJsonDocument>
|
||
|
|
#include <QJsonObject>
|
||
|
|
#include <QStandardPaths>
|
||
|
|
|
||
|
|
HeroicImporter::HeroicImporter(QObject *parent)
|
||
|
|
: PlatformImporter(parent)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
QString HeroicImporter::platformName() const
|
||
|
|
{
|
||
|
|
return QStringLiteral("Heroic");
|
||
|
|
}
|
||
|
|
|
||
|
|
QString HeroicImporter::platformId() const
|
||
|
|
{
|
||
|
|
return QStringLiteral("heroic");
|
||
|
|
}
|
||
|
|
|
||
|
|
bool HeroicImporter::isAvailable() const
|
||
|
|
{
|
||
|
|
return directoryExists(findHeroicConfigPath());
|
||
|
|
}
|
||
|
|
|
||
|
|
QString HeroicImporter::findHeroicConfigPath() const
|
||
|
|
{
|
||
|
|
QStringList possiblePaths = {expandPath(QStringLiteral("~/.config/heroic")),
|
||
|
|
expandPath(QStringLiteral("~/.var/app/com.heroicgameslauncher.hgl/config/heroic"))};
|
||
|
|
|
||
|
|
for (const QString &path : possiblePaths) {
|
||
|
|
if (directoryExists(path)) {
|
||
|
|
return path;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
QUrl HeroicImporter::findCoverImage(const QString &appName, const QString &store) const
|
||
|
|
{
|
||
|
|
QString configPath = findHeroicConfigPath();
|
||
|
|
if (configPath.isEmpty()) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check images cache
|
||
|
|
QStringList imageDirs = {configPath + QStringLiteral("/images-cache"), configPath + QStringLiteral("/images")};
|
||
|
|
|
||
|
|
QStringList extensions = {QStringLiteral(".jpg"), QStringLiteral(".png"), QStringLiteral(".webp")};
|
||
|
|
QStringList prefixes = {appName, appName + QStringLiteral("_cover"), appName + QStringLiteral("_library")};
|
||
|
|
|
||
|
|
for (const QString &imageDir : imageDirs) {
|
||
|
|
QDir dir(imageDir);
|
||
|
|
if (!dir.exists())
|
||
|
|
continue;
|
||
|
|
|
||
|
|
for (const QString &prefix : prefixes) {
|
||
|
|
for (const QString &ext : extensions) {
|
||
|
|
QString imagePath = imageDir + QStringLiteral("/") + prefix + ext;
|
||
|
|
if (QFile::exists(imagePath)) {
|
||
|
|
return QUrl::fromLocalFile(imagePath);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Also try looking in subdirectories
|
||
|
|
QStringList subDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||
|
|
for (const QString &subDir : subDirs) {
|
||
|
|
for (const QString &prefix : prefixes) {
|
||
|
|
for (const QString &ext : extensions) {
|
||
|
|
QString imagePath = imageDir + QStringLiteral("/") + subDir + QStringLiteral("/") + prefix + ext;
|
||
|
|
if (QFile::exists(imagePath)) {
|
||
|
|
return QUrl::fromLocalFile(imagePath);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
QList<Game *> HeroicImporter::importGames()
|
||
|
|
{
|
||
|
|
QList<Game *> games;
|
||
|
|
|
||
|
|
games.append(importEpicGames());
|
||
|
|
games.append(importGOGGames());
|
||
|
|
games.append(importAmazonGames());
|
||
|
|
|
||
|
|
return games;
|
||
|
|
}
|
||
|
|
|
||
|
|
QList<Game *> HeroicImporter::importEpicGames()
|
||
|
|
{
|
||
|
|
QList<Game *> games;
|
||
|
|
QString configPath = findHeroicConfigPath();
|
||
|
|
|
||
|
|
if (configPath.isEmpty()) {
|
||
|
|
return games;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check for installed.json
|
||
|
|
QString installedPath = configPath + QStringLiteral("/legendaryConfig/legendary/installed.json");
|
||
|
|
QFile installedFile(installedPath);
|
||
|
|
|
||
|
|
if (!installedFile.open(QIODevice::ReadOnly)) {
|
||
|
|
// Try alternative path
|
||
|
|
installedPath = configPath + QStringLiteral("/store_cache/legendary_library.json");
|
||
|
|
installedFile.setFileName(installedPath);
|
||
|
|
if (!installedFile.open(QIODevice::ReadOnly)) {
|
||
|
|
return games;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
QJsonDocument doc = QJsonDocument::fromJson(installedFile.readAll());
|
||
|
|
|
||
|
|
if (doc.isObject()) {
|
||
|
|
QJsonObject obj = doc.object();
|
||
|
|
int current = 0;
|
||
|
|
int total = obj.keys().count();
|
||
|
|
|
||
|
|
for (const QString &appName : obj.keys()) {
|
||
|
|
QJsonObject gameObj = obj[appName].toObject();
|
||
|
|
|
||
|
|
QString title = gameObj[QStringLiteral("title")].toString();
|
||
|
|
if (title.isEmpty()) {
|
||
|
|
title = appName;
|
||
|
|
}
|
||
|
|
|
||
|
|
QString installPath = gameObj[QStringLiteral("install_path")].toString();
|
||
|
|
bool isInstalled = !installPath.isEmpty();
|
||
|
|
|
||
|
|
Game *game = new Game(QStringLiteral("heroic-epic-%1").arg(appName), title);
|
||
|
|
game->setPlatform(QStringLiteral("Heroic (Epic)"));
|
||
|
|
game->setPlatformId(appName);
|
||
|
|
game->setLaunchCommand(QStringLiteral("heroic --launch %1").arg(appName));
|
||
|
|
game->setWorkingDirectory(installPath);
|
||
|
|
game->setInstalled(isInstalled);
|
||
|
|
|
||
|
|
// Developer/Publisher info
|
||
|
|
QString developer = gameObj[QStringLiteral("developer")].toString();
|
||
|
|
QString publisher = gameObj[QStringLiteral("publisher")].toString();
|
||
|
|
game->setDeveloper(developer);
|
||
|
|
game->setPublisher(publisher);
|
||
|
|
|
||
|
|
QUrl coverUrl = findCoverImage(appName, QStringLiteral("epic"));
|
||
|
|
if (coverUrl.isValid()) {
|
||
|
|
game->setCoverUrl(coverUrl);
|
||
|
|
}
|
||
|
|
|
||
|
|
games.append(game);
|
||
|
|
current++;
|
||
|
|
Q_EMIT importProgress(current, total);
|
||
|
|
}
|
||
|
|
} else if (doc.isArray()) {
|
||
|
|
QJsonArray arr = doc.array();
|
||
|
|
int current = 0;
|
||
|
|
int total = arr.count();
|
||
|
|
|
||
|
|
for (const QJsonValue &value : arr) {
|
||
|
|
QJsonObject gameObj = value.toObject();
|
||
|
|
|
||
|
|
QString appName = gameObj[QStringLiteral("app_name")].toString();
|
||
|
|
QString title = gameObj[QStringLiteral("title")].toString();
|
||
|
|
|
||
|
|
if (appName.isEmpty() || title.isEmpty())
|
||
|
|
continue;
|
||
|
|
|
||
|
|
bool isInstalled = gameObj[QStringLiteral("is_installed")].toBool();
|
||
|
|
|
||
|
|
Game *game = new Game(QStringLiteral("heroic-epic-%1").arg(appName), title);
|
||
|
|
game->setPlatform(QStringLiteral("Heroic (Epic)"));
|
||
|
|
game->setPlatformId(appName);
|
||
|
|
game->setLaunchCommand(QStringLiteral("heroic --launch %1").arg(appName));
|
||
|
|
game->setInstalled(isInstalled);
|
||
|
|
|
||
|
|
QUrl coverUrl = findCoverImage(appName, QStringLiteral("epic"));
|
||
|
|
if (coverUrl.isValid()) {
|
||
|
|
game->setCoverUrl(coverUrl);
|
||
|
|
}
|
||
|
|
|
||
|
|
games.append(game);
|
||
|
|
current++;
|
||
|
|
Q_EMIT importProgress(current, total);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return games;
|
||
|
|
}
|
||
|
|
|
||
|
|
QList<Game *> HeroicImporter::importGOGGames()
|
||
|
|
{
|
||
|
|
QList<Game *> games;
|
||
|
|
QString configPath = findHeroicConfigPath();
|
||
|
|
|
||
|
|
if (configPath.isEmpty()) {
|
||
|
|
return games;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check for GOG installed games
|
||
|
|
QString installedPath = configPath + QStringLiteral("/gog_store/installed.json");
|
||
|
|
QFile installedFile(installedPath);
|
||
|
|
|
||
|
|
if (!installedFile.open(QIODevice::ReadOnly)) {
|
||
|
|
// Try library file
|
||
|
|
installedPath = configPath + QStringLiteral("/store_cache/gog_library.json");
|
||
|
|
installedFile.setFileName(installedPath);
|
||
|
|
if (!installedFile.open(QIODevice::ReadOnly)) {
|
||
|
|
return games;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
QJsonDocument doc = QJsonDocument::fromJson(installedFile.readAll());
|
||
|
|
|
||
|
|
auto parseGames = [this, &games](const QJsonArray &arr) {
|
||
|
|
for (const QJsonValue &value : arr) {
|
||
|
|
QJsonObject gameObj = value.toObject();
|
||
|
|
|
||
|
|
QString appName = gameObj[QStringLiteral("app_name")].toString();
|
||
|
|
if (appName.isEmpty()) {
|
||
|
|
appName = gameObj[QStringLiteral("appName")].toString();
|
||
|
|
}
|
||
|
|
|
||
|
|
QString title = gameObj[QStringLiteral("title")].toString();
|
||
|
|
if (title.isEmpty()) {
|
||
|
|
title = gameObj[QStringLiteral("name")].toString();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (appName.isEmpty() || title.isEmpty())
|
||
|
|
continue;
|
||
|
|
|
||
|
|
bool isInstalled = gameObj[QStringLiteral("is_installed")].toBool() || gameObj[QStringLiteral("isInstalled")].toBool();
|
||
|
|
|
||
|
|
Game *game = new Game(QStringLiteral("heroic-gog-%1").arg(appName), title);
|
||
|
|
game->setPlatform(QStringLiteral("Heroic (GOG)"));
|
||
|
|
game->setPlatformId(appName);
|
||
|
|
game->setLaunchCommand(QStringLiteral("heroic --launch %1").arg(appName));
|
||
|
|
game->setInstalled(isInstalled);
|
||
|
|
|
||
|
|
QUrl coverUrl = findCoverImage(appName, QStringLiteral("gog"));
|
||
|
|
if (coverUrl.isValid()) {
|
||
|
|
game->setCoverUrl(coverUrl);
|
||
|
|
}
|
||
|
|
|
||
|
|
games.append(game);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
if (doc.isArray()) {
|
||
|
|
parseGames(doc.array());
|
||
|
|
} else if (doc.isObject()) {
|
||
|
|
QJsonObject obj = doc.object();
|
||
|
|
if (obj.contains(QStringLiteral("games"))) {
|
||
|
|
parseGames(obj[QStringLiteral("games")].toArray());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return games;
|
||
|
|
}
|
||
|
|
|
||
|
|
QList<Game *> HeroicImporter::importAmazonGames()
|
||
|
|
{
|
||
|
|
QList<Game *> games;
|
||
|
|
QString configPath = findHeroicConfigPath();
|
||
|
|
|
||
|
|
if (configPath.isEmpty()) {
|
||
|
|
return games;
|
||
|
|
}
|
||
|
|
|
||
|
|
QString libraryPath = configPath + QStringLiteral("/store_cache/nile_library.json");
|
||
|
|
QFile libraryFile(libraryPath);
|
||
|
|
|
||
|
|
if (!libraryFile.open(QIODevice::ReadOnly)) {
|
||
|
|
return games;
|
||
|
|
}
|
||
|
|
|
||
|
|
QJsonDocument doc = QJsonDocument::fromJson(libraryFile.readAll());
|
||
|
|
|
||
|
|
if (!doc.isArray()) {
|
||
|
|
return games;
|
||
|
|
}
|
||
|
|
|
||
|
|
QJsonArray arr = doc.array();
|
||
|
|
for (const QJsonValue &value : arr) {
|
||
|
|
QJsonObject gameObj = value.toObject();
|
||
|
|
|
||
|
|
QString appName = gameObj[QStringLiteral("app_name")].toString();
|
||
|
|
QString title = gameObj[QStringLiteral("title")].toString();
|
||
|
|
|
||
|
|
if (appName.isEmpty() || title.isEmpty())
|
||
|
|
continue;
|
||
|
|
|
||
|
|
bool isInstalled = gameObj[QStringLiteral("is_installed")].toBool();
|
||
|
|
|
||
|
|
Game *game = new Game(QStringLiteral("heroic-amazon-%1").arg(appName), title);
|
||
|
|
game->setPlatform(QStringLiteral("Heroic (Amazon)"));
|
||
|
|
game->setPlatformId(appName);
|
||
|
|
game->setLaunchCommand(QStringLiteral("heroic --launch %1").arg(appName));
|
||
|
|
game->setInstalled(isInstalled);
|
||
|
|
|
||
|
|
QUrl coverUrl = findCoverImage(appName, QStringLiteral("amazon"));
|
||
|
|
if (coverUrl.isValid()) {
|
||
|
|
game->setCoverUrl(coverUrl);
|
||
|
|
}
|
||
|
|
|
||
|
|
games.append(game);
|
||
|
|
}
|
||
|
|
|
||
|
|
return games;
|
||
|
|
}
|