2015-09-01 06:35:33 +02:00
|
|
|
// Copyright 2015 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <atomic>
|
2016-04-13 23:04:05 +02:00
|
|
|
#include <QImage>
|
2015-09-01 06:35:33 +02:00
|
|
|
#include <QRunnable>
|
|
|
|
#include <QStandardItem>
|
|
|
|
#include <QString>
|
|
|
|
#include "citra_qt/util/util.h"
|
2016-09-18 02:38:01 +02:00
|
|
|
#include "common/string_util.h"
|
2016-05-18 01:42:45 +02:00
|
|
|
#include "core/loader/smdh.h"
|
2016-04-13 23:04:05 +02:00
|
|
|
|
|
|
|
/**
|
2017-02-27 02:58:51 +01:00
|
|
|
* Gets the game icon from SMDH data.
|
|
|
|
* @param smdh SMDH data
|
2016-04-13 23:04:05 +02:00
|
|
|
* @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
|
|
|
|
* @return QPixmap game icon
|
|
|
|
*/
|
2016-05-18 01:42:45 +02:00
|
|
|
static QPixmap GetQPixmapFromSMDH(const Loader::SMDH& smdh, bool large) {
|
|
|
|
std::vector<u16> icon_data = smdh.GetIcon(large);
|
|
|
|
const uchar* data = reinterpret_cast<const uchar*>(icon_data.data());
|
|
|
|
int size = large ? 48 : 24;
|
|
|
|
QImage icon(data, size, size, QImage::Format::Format_RGB16);
|
2016-04-13 23:04:05 +02:00
|
|
|
return QPixmap::fromImage(icon);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the default icon (for games without valid SMDH)
|
|
|
|
* @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
|
|
|
|
* @return QPixmap default icon
|
|
|
|
*/
|
|
|
|
static QPixmap GetDefaultIcon(bool large) {
|
|
|
|
int size = large ? 48 : 24;
|
|
|
|
QPixmap icon(size, size);
|
|
|
|
icon.fill(Qt::transparent);
|
|
|
|
return icon;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-02-27 02:58:51 +01:00
|
|
|
* Gets the short game title from SMDH data.
|
|
|
|
* @param smdh SMDH data
|
2016-04-13 23:04:05 +02:00
|
|
|
* @param language title language
|
|
|
|
* @return QString short title
|
|
|
|
*/
|
2016-09-18 02:38:01 +02:00
|
|
|
static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh,
|
|
|
|
Loader::SMDH::TitleLanguage language) {
|
2016-05-18 01:42:45 +02:00
|
|
|
return QString::fromUtf16(smdh.GetShortTitle(language).data());
|
2016-04-13 23:04:05 +02:00
|
|
|
}
|
2015-09-01 06:35:33 +02:00
|
|
|
|
|
|
|
class GameListItem : public QStandardItem {
|
|
|
|
|
|
|
|
public:
|
2016-09-19 03:01:46 +02:00
|
|
|
GameListItem() : QStandardItem() {}
|
|
|
|
GameListItem(const QString& string) : QStandardItem(string) {}
|
|
|
|
virtual ~GameListItem() override {}
|
2015-09-01 06:35:33 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A specialization of GameListItem for path values.
|
|
|
|
* This class ensures that for every full path value it holds, a correct string representation
|
|
|
|
* of just the filename (with no extension) will be displayed to the user.
|
2016-10-20 16:26:59 +02:00
|
|
|
* If this class receives valid SMDH data, it will also display game icons and titles.
|
2015-09-01 06:35:33 +02:00
|
|
|
*/
|
|
|
|
class GameListItemPath : public GameListItem {
|
|
|
|
|
|
|
|
public:
|
|
|
|
static const int FullPathRole = Qt::UserRole + 1;
|
2016-04-13 23:04:05 +02:00
|
|
|
static const int TitleRole = Qt::UserRole + 2;
|
2016-12-15 10:55:03 +01:00
|
|
|
static const int ProgramIdRole = Qt::UserRole + 3;
|
2015-09-01 06:35:33 +02:00
|
|
|
|
2016-09-19 03:01:46 +02:00
|
|
|
GameListItemPath() : GameListItem() {}
|
2018-01-24 04:32:27 +01:00
|
|
|
GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id)
|
2016-12-15 10:55:03 +01:00
|
|
|
: GameListItem() {
|
2015-09-01 06:35:33 +02:00
|
|
|
setData(game_path, FullPathRole);
|
2016-12-15 10:55:03 +01:00
|
|
|
setData(qulonglong(program_id), ProgramIdRole);
|
2016-04-13 23:04:05 +02:00
|
|
|
|
2018-01-24 04:32:27 +01:00
|
|
|
if (!Loader::IsValidSMDH(smdh_data)) {
|
2016-04-13 23:04:05 +02:00
|
|
|
// SMDH is not valid, set a default icon
|
|
|
|
setData(GetDefaultIcon(true), Qt::DecorationRole);
|
|
|
|
return;
|
|
|
|
}
|
2018-01-24 17:16:40 +01:00
|
|
|
|
|
|
|
Loader::SMDH smdh;
|
2018-01-24 04:32:27 +01:00
|
|
|
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
|
|
|
|
2016-04-13 23:04:05 +02:00
|
|
|
// Get icon from SMDH
|
2016-05-18 01:42:45 +02:00
|
|
|
setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole);
|
2016-04-13 23:04:05 +02:00
|
|
|
|
2018-01-20 18:33:14 +01:00
|
|
|
// Get title from SMDH
|
2016-09-18 02:38:01 +02:00
|
|
|
setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English),
|
|
|
|
TitleRole);
|
2015-09-01 06:35:33 +02:00
|
|
|
}
|
|
|
|
|
2016-04-13 23:04:05 +02:00
|
|
|
QVariant data(int role) const override {
|
|
|
|
if (role == Qt::DisplayRole) {
|
2015-09-01 06:35:33 +02:00
|
|
|
std::string filename;
|
2016-09-18 02:38:01 +02:00
|
|
|
Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename,
|
|
|
|
nullptr);
|
2016-04-13 23:04:05 +02:00
|
|
|
QString title = data(TitleRole).toString();
|
|
|
|
return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n " + title);
|
2015-09-01 06:35:33 +02:00
|
|
|
} else {
|
2016-04-13 23:04:05 +02:00
|
|
|
return GameListItem::data(role);
|
2015-09-01 06:35:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A specialization of GameListItem for size values.
|
|
|
|
* This class ensures that for every numerical size value it holds (in bytes), a correct
|
|
|
|
* human-readable string representation will be displayed to the user.
|
|
|
|
*/
|
|
|
|
class GameListItemSize : public GameListItem {
|
|
|
|
|
|
|
|
public:
|
|
|
|
static const int SizeRole = Qt::UserRole + 1;
|
|
|
|
|
2016-09-19 03:01:46 +02:00
|
|
|
GameListItemSize() : GameListItem() {}
|
2016-09-18 02:38:01 +02:00
|
|
|
GameListItemSize(const qulonglong size_bytes) : GameListItem() {
|
2015-09-01 06:35:33 +02:00
|
|
|
setData(size_bytes, SizeRole);
|
|
|
|
}
|
|
|
|
|
2016-09-18 02:38:01 +02:00
|
|
|
void setData(const QVariant& value, int role) override {
|
2015-09-01 06:35:33 +02:00
|
|
|
// By specializing setData for SizeRole, we can ensure that the numerical and string
|
|
|
|
// representations of the data are always accurate and in the correct format.
|
|
|
|
if (role == SizeRole) {
|
|
|
|
qulonglong size_bytes = value.toULongLong();
|
|
|
|
GameListItem::setData(ReadableByteSize(size_bytes), Qt::DisplayRole);
|
|
|
|
GameListItem::setData(value, SizeRole);
|
|
|
|
} else {
|
|
|
|
GameListItem::setData(value, role);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This operator is, in practice, only used by the TreeView sorting systems.
|
2016-09-18 02:38:01 +02:00
|
|
|
* Override it so that it will correctly sort by numerical value instead of by string
|
|
|
|
* representation.
|
2015-09-01 06:35:33 +02:00
|
|
|
*/
|
2016-09-18 02:38:01 +02:00
|
|
|
bool operator<(const QStandardItem& other) const override {
|
2015-09-01 06:35:33 +02:00
|
|
|
return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Asynchronous worker object for populating the game list.
|
|
|
|
* Communicates with other threads through Qt's signal/slot system.
|
|
|
|
*/
|
|
|
|
class GameListWorker : public QObject, public QRunnable {
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
2016-09-18 02:38:01 +02:00
|
|
|
GameListWorker(QString dir_path, bool deep_scan)
|
2016-09-19 03:01:46 +02:00
|
|
|
: QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {}
|
2015-09-01 06:35:33 +02:00
|
|
|
|
|
|
|
public slots:
|
|
|
|
/// Starts the processing of directory tree information.
|
|
|
|
void run() override;
|
|
|
|
/// Tells the worker that it should no longer continue processing. Thread-safe.
|
|
|
|
void Cancel();
|
|
|
|
|
|
|
|
signals:
|
|
|
|
/**
|
|
|
|
* The `EntryReady` signal is emitted once an entry has been prepared and is ready
|
|
|
|
* to be added to the game list.
|
|
|
|
* @param entry_items a list with `QStandardItem`s that make up the columns of the new entry.
|
|
|
|
*/
|
|
|
|
void EntryReady(QList<QStandardItem*> entry_items);
|
2017-04-18 04:53:40 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* After the worker has traversed the game directory looking for entries, this signal is emmited
|
|
|
|
* with a list of folders that should be watched for changes as well.
|
|
|
|
*/
|
|
|
|
void Finished(QStringList watch_list);
|
2015-09-01 06:35:33 +02:00
|
|
|
|
|
|
|
private:
|
2017-04-18 04:53:40 +02:00
|
|
|
QStringList watch_list;
|
2015-09-01 06:35:33 +02:00
|
|
|
QString dir_path;
|
|
|
|
bool deep_scan;
|
|
|
|
std::atomic_bool stop_processing;
|
|
|
|
|
2015-09-06 08:59:04 +02:00
|
|
|
void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
|
2015-09-01 06:35:33 +02:00
|
|
|
};
|