mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
DolphinQt2
This commit is contained in:
202
Source/Core/DolphinQt2/GameList/GameFile.cpp
Normal file
202
Source/Core/DolphinQt2/GameList/GameFile.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
// Copyright 2015 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QDataStream>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QImage>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "DiscIO/VolumeCreator.h"
|
||||
#include "DolphinQt2/Resources.h"
|
||||
#include "DolphinQt2/GameList/GameFile.h"
|
||||
|
||||
static const int CACHE_VERSION = 13; // Last changed in PR #3261
|
||||
static const int DATASTREAM_VERSION = QDataStream::Qt_5_5;
|
||||
|
||||
static QMap<DiscIO::IVolume::ELanguage, QString> ConvertLanguageMap(
|
||||
const std::map<DiscIO::IVolume::ELanguage, std::string>& map)
|
||||
{
|
||||
QMap<DiscIO::IVolume::ELanguage, QString> result;
|
||||
for (auto entry : map)
|
||||
result.insert(entry.first, QString::fromStdString(entry.second).trimmed());
|
||||
return result;
|
||||
}
|
||||
|
||||
GameFile::GameFile(QString path) : m_path(path)
|
||||
{
|
||||
m_valid = false;
|
||||
|
||||
if (!LoadFileInfo(path))
|
||||
return;
|
||||
|
||||
if (!TryLoadCache())
|
||||
{
|
||||
if (TryLoadVolume())
|
||||
{
|
||||
LoadState();
|
||||
}
|
||||
else if (!TryLoadElfDol())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_valid = true;
|
||||
}
|
||||
|
||||
DiscIO::IVolume::ELanguage GameFile::GetDefaultLanguage() const
|
||||
{
|
||||
bool wii = m_platform != DiscIO::IVolume::GAMECUBE_DISC;
|
||||
return SConfig::GetInstance().GetCurrentLanguage(wii);
|
||||
}
|
||||
|
||||
QString GameFile::GetCacheFileName() const
|
||||
{
|
||||
QString folder = QString::fromStdString(File::GetUserPath(D_CACHE_IDX));
|
||||
// Append a hash of the full path to prevent name clashes between
|
||||
// files with the same names in different folders.
|
||||
QString hash = QString::fromUtf8(
|
||||
QCryptographicHash::hash(m_path.toUtf8(),
|
||||
QCryptographicHash::Md5).toHex());
|
||||
return folder + m_file_name + hash;
|
||||
}
|
||||
|
||||
void GameFile::ReadBanner(const DiscIO::IVolume& volume)
|
||||
{
|
||||
int width, height;
|
||||
std::vector<u32> buffer = volume.GetBanner(&width, &height);
|
||||
QImage banner(width, height, QImage::Format_RGB888);
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
int x = i % width, y = i / width;
|
||||
banner.setPixel(x, y, qRgb((buffer[i] & 0xFF0000) >> 16,
|
||||
(buffer[i] & 0x00FF00) >> 8,
|
||||
(buffer[i] & 0x0000FF) >> 0));
|
||||
}
|
||||
|
||||
if (!banner.isNull())
|
||||
m_banner = QPixmap::fromImage(banner);
|
||||
else
|
||||
m_banner = Resources::GetMisc(Resources::BANNER_MISSING);
|
||||
}
|
||||
|
||||
bool GameFile::LoadFileInfo(QString path)
|
||||
{
|
||||
QFileInfo info(path);
|
||||
if (!info.exists() || !info.isReadable())
|
||||
return false;
|
||||
|
||||
m_file_name = info.fileName();
|
||||
m_extension = info.suffix();
|
||||
m_folder = info.dir().dirName();
|
||||
m_last_modified = info.lastModified();
|
||||
m_size = info.size();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameFile::LoadState()
|
||||
{
|
||||
IniFile ini = SConfig::LoadGameIni(m_unique_id.toStdString(), m_revision);
|
||||
std::string issues_temp;
|
||||
ini.GetIfExists("EmuState", "EmulationStateId", &m_rating);
|
||||
ini.GetIfExists("EmuState", "EmulationIssues", &issues_temp);
|
||||
m_issues = QString::fromStdString(issues_temp);
|
||||
}
|
||||
|
||||
bool GameFile::IsElfOrDol()
|
||||
{
|
||||
return m_extension == QStringLiteral("elf") ||
|
||||
m_extension == QStringLiteral("dol");
|
||||
}
|
||||
|
||||
bool GameFile::TryLoadCache()
|
||||
{
|
||||
QFile cache(GetCacheFileName());
|
||||
if (!cache.exists())
|
||||
return false;
|
||||
if (!cache.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
if (QFileInfo(cache).lastModified() < m_last_modified)
|
||||
return false;
|
||||
|
||||
QDataStream in(&cache);
|
||||
in.setVersion(DATASTREAM_VERSION);
|
||||
|
||||
int cache_version;
|
||||
in >> cache_version;
|
||||
if (cache_version != CACHE_VERSION)
|
||||
return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameFile::TryLoadVolume()
|
||||
{
|
||||
QSharedPointer<DiscIO::IVolume> volume(DiscIO::CreateVolumeFromFilename(m_path.toStdString()));
|
||||
if (volume == nullptr)
|
||||
return false;
|
||||
|
||||
m_unique_id = QString::fromStdString(volume->GetUniqueID());
|
||||
m_maker_id = QString::fromStdString(volume->GetMakerID());
|
||||
m_revision = volume->GetRevision();
|
||||
m_internal_name = QString::fromStdString(volume->GetInternalName());
|
||||
m_short_names = ConvertLanguageMap(volume->GetNames(false));
|
||||
m_long_names = ConvertLanguageMap(volume->GetNames(true));
|
||||
m_descriptions = ConvertLanguageMap(volume->GetDescriptions());
|
||||
m_company = QString::fromStdString(volume->GetCompany());
|
||||
m_disc_number = volume->GetDiscNumber();
|
||||
m_platform = volume->GetVolumeType();
|
||||
m_country = volume->GetCountry();
|
||||
m_blob_type = volume->GetBlobType();
|
||||
m_raw_size = volume->GetRawSize();
|
||||
|
||||
if (m_company.isEmpty() && m_unique_id.size() >= 6)
|
||||
m_company = QString::fromStdString(
|
||||
DiscIO::GetCompanyFromID(m_unique_id.mid(4, 2).toStdString()));
|
||||
|
||||
ReadBanner(*volume);
|
||||
|
||||
SaveCache();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameFile::TryLoadElfDol()
|
||||
{
|
||||
if (!IsElfOrDol())
|
||||
return false;
|
||||
|
||||
m_revision = 0;
|
||||
m_long_names[DiscIO::IVolume::LANGUAGE_ENGLISH] = m_file_name;
|
||||
m_platform = DiscIO::IVolume::ELF_DOL;
|
||||
m_country = DiscIO::IVolume::COUNTRY_UNKNOWN;
|
||||
m_blob_type = DiscIO::BlobType::DIRECTORY;
|
||||
m_raw_size = m_size;
|
||||
m_banner = Resources::GetMisc(Resources::BANNER_MISSING);
|
||||
m_rating = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
void GameFile::SaveCache()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
QString GameFile::GetLanguageString(QMap<DiscIO::IVolume::ELanguage, QString> m) const
|
||||
{
|
||||
// Try the settings language, then English, then just pick one.
|
||||
if (m.isEmpty())
|
||||
return QString();
|
||||
DiscIO::IVolume::ELanguage current_lang = GetDefaultLanguage();
|
||||
if (m.contains(current_lang))
|
||||
return m[current_lang];
|
||||
if (m.contains(DiscIO::IVolume::LANGUAGE_ENGLISH))
|
||||
return m[DiscIO::IVolume::LANGUAGE_ENGLISH];
|
||||
return m.first();
|
||||
}
|
101
Source/Core/DolphinQt2/GameList/GameFile.h
Normal file
101
Source/Core/DolphinQt2/GameList/GameFile.h
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2015 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QMap>
|
||||
#include <QPixmap>
|
||||
#include <QString>
|
||||
|
||||
#include "DiscIO/Volume.h"
|
||||
|
||||
// TODO cache
|
||||
class GameFile final
|
||||
{
|
||||
public:
|
||||
explicit GameFile(QString path);
|
||||
|
||||
bool IsValid() const { return m_valid; }
|
||||
|
||||
// These will be properly initialized before we try to load the file.
|
||||
QString GetPath() const { return m_path; }
|
||||
QString GetFileName() const { return m_file_name; }
|
||||
QString GetExtension() const { return m_extension; }
|
||||
QString GetFolder() const { return m_folder; }
|
||||
qint64 GetFileSize() const { return m_size; }
|
||||
|
||||
// The rest will not.
|
||||
QString GetUniqueID() const { return m_unique_id; }
|
||||
QString GetMakerID() const { return m_maker_id; }
|
||||
u16 GetRevision() const { return m_revision; }
|
||||
QString GetInternalName() const { return m_internal_name; }
|
||||
QString GetCompany() const { return m_company; }
|
||||
u8 GetDiscNumber() const { return m_disc_number; }
|
||||
u64 GetRawSize() const { return m_raw_size; }
|
||||
QPixmap GetBanner() const { return m_banner; }
|
||||
QString GetIssues() const { return m_issues; }
|
||||
int GetRating() const { return m_rating; }
|
||||
|
||||
DiscIO::IVolume::EPlatform GetPlatform() const { return m_platform; }
|
||||
DiscIO::IVolume::ECountry GetCountry() const { return m_country; }
|
||||
DiscIO::BlobType GetBlobType() const { return m_blob_type; }
|
||||
|
||||
QString GetShortName() const { return GetLanguageString(m_short_names); }
|
||||
QString GetShortName(DiscIO::IVolume::ELanguage lang) const
|
||||
{
|
||||
return m_short_names[lang];
|
||||
}
|
||||
|
||||
QString GetLongName() const { return GetLanguageString(m_long_names); }
|
||||
QString GetLongName(DiscIO::IVolume::ELanguage lang) const
|
||||
{
|
||||
return m_long_names[lang];
|
||||
}
|
||||
|
||||
QString GetDescription() const { return GetLanguageString(m_descriptions); }
|
||||
QString GetDescription(DiscIO::IVolume::ELanguage lang) const
|
||||
{
|
||||
return m_descriptions[lang];
|
||||
}
|
||||
|
||||
private:
|
||||
DiscIO::IVolume::ELanguage GetDefaultLanguage() const;
|
||||
QString GetLanguageString(QMap<DiscIO::IVolume::ELanguage, QString> m) const;
|
||||
|
||||
QString GetCacheFileName() const;
|
||||
void ReadBanner(const DiscIO::IVolume& volume);
|
||||
bool LoadFileInfo(QString path);
|
||||
void LoadState();
|
||||
bool IsElfOrDol();
|
||||
bool TryLoadElfDol();
|
||||
bool TryLoadCache();
|
||||
bool TryLoadVolume();
|
||||
void SaveCache();
|
||||
|
||||
bool m_valid;
|
||||
QString m_path;
|
||||
QString m_file_name;
|
||||
QString m_extension;
|
||||
QString m_folder;
|
||||
QDateTime m_last_modified;
|
||||
qint64 m_size;
|
||||
|
||||
QString m_unique_id;
|
||||
QString m_maker_id;
|
||||
u16 m_revision;
|
||||
QString m_internal_name;
|
||||
QMap<DiscIO::IVolume::ELanguage, QString> m_short_names;
|
||||
QMap<DiscIO::IVolume::ELanguage, QString> m_long_names;
|
||||
QMap<DiscIO::IVolume::ELanguage, QString> m_descriptions;
|
||||
QString m_company;
|
||||
u8 m_disc_number;
|
||||
DiscIO::IVolume::EPlatform m_platform;
|
||||
DiscIO::IVolume::ECountry m_country;
|
||||
DiscIO::BlobType m_blob_type;
|
||||
u64 m_raw_size;
|
||||
QPixmap m_banner;
|
||||
QString m_issues;
|
||||
int m_rating;
|
||||
};
|
88
Source/Core/DolphinQt2/GameList/GameList.cpp
Normal file
88
Source/Core/DolphinQt2/GameList/GameList.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
// Copyright 2015 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QHeaderView>
|
||||
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "DolphinQt2/GameList/GameList.h"
|
||||
|
||||
GameList::GameList(QWidget* parent): QStackedWidget(parent)
|
||||
{
|
||||
m_model = new GameListModel(this);
|
||||
m_proxy = new QSortFilterProxyModel(this);
|
||||
m_proxy->setSourceModel(m_model);
|
||||
|
||||
MakeTableView();
|
||||
MakeListView();
|
||||
|
||||
connect(m_table, &QTableView::doubleClicked, this, &GameList::GameSelected);
|
||||
connect(m_list, &QListView::doubleClicked, this, &GameList::GameSelected);
|
||||
connect(this, &GameList::DirectoryAdded, m_model, &GameListModel::DirectoryAdded);
|
||||
|
||||
addWidget(m_table);
|
||||
addWidget(m_list);
|
||||
setCurrentWidget(m_table);
|
||||
}
|
||||
|
||||
void GameList::MakeTableView()
|
||||
{
|
||||
m_table = new QTableView(this);
|
||||
m_table->setModel(m_proxy);
|
||||
m_table->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
m_table->setAlternatingRowColors(true);
|
||||
m_table->setShowGrid(false);
|
||||
m_table->setSortingEnabled(true);
|
||||
m_table->setCurrentIndex(QModelIndex());
|
||||
|
||||
// These fixed column widths make it so that the DisplayRole is cut
|
||||
// off, which lets us see the icon but sort by the actual value.
|
||||
// It's a bit of a hack. To do it right we need to subclass
|
||||
// QSortFilterProxyModel and not show those items.
|
||||
m_table->setColumnWidth(GameListModel::COL_PLATFORM, 52);
|
||||
m_table->setColumnWidth(GameListModel::COL_COUNTRY, 38);
|
||||
m_table->setColumnWidth(GameListModel::COL_RATING, 52);
|
||||
m_table->setColumnHidden(GameListModel::COL_LARGE_ICON, true);
|
||||
|
||||
m_table->horizontalHeader()->setSectionResizeMode(
|
||||
GameListModel::COL_PLATFORM, QHeaderView::Fixed);
|
||||
m_table->horizontalHeader()->setSectionResizeMode(
|
||||
GameListModel::COL_COUNTRY, QHeaderView::Fixed);
|
||||
m_table->horizontalHeader()->setSectionResizeMode(
|
||||
GameListModel::COL_ID, QHeaderView::ResizeToContents);
|
||||
m_table->horizontalHeader()->setSectionResizeMode(
|
||||
GameListModel::COL_TITLE, QHeaderView::Stretch);
|
||||
m_table->horizontalHeader()->setSectionResizeMode(
|
||||
GameListModel::COL_MAKER, QHeaderView::ResizeToContents);
|
||||
m_table->horizontalHeader()->setSectionResizeMode(
|
||||
GameListModel::COL_SIZE, QHeaderView::ResizeToContents);
|
||||
m_table->horizontalHeader()->setSectionResizeMode(
|
||||
GameListModel::COL_DESCRIPTION, QHeaderView::Stretch);
|
||||
m_table->horizontalHeader()->setSectionResizeMode(
|
||||
GameListModel::COL_RATING, QHeaderView::Fixed);
|
||||
}
|
||||
|
||||
void GameList::MakeListView()
|
||||
{
|
||||
m_list = new QListView(this);
|
||||
m_list->setModel(m_proxy);
|
||||
m_list->setViewMode(QListView::IconMode);
|
||||
m_list->setModelColumn(GameListModel::COL_LARGE_ICON);
|
||||
m_list->setResizeMode(QListView::Adjust);
|
||||
m_list->setUniformItemSizes(true);
|
||||
}
|
||||
|
||||
QString GameList::GetSelectedGame() const
|
||||
{
|
||||
QItemSelectionModel* sel_model;
|
||||
if (currentWidget() == m_table)
|
||||
sel_model = m_table->selectionModel();
|
||||
else
|
||||
sel_model = m_list->selectionModel();
|
||||
|
||||
if (sel_model->hasSelection())
|
||||
return m_model->GetPath(m_proxy->mapToSource(sel_model->selectedIndexes()[0]).row());
|
||||
else
|
||||
return QString();
|
||||
}
|
41
Source/Core/DolphinQt2/GameList/GameList.h
Normal file
41
Source/Core/DolphinQt2/GameList/GameList.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2015 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QListView>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStackedWidget>
|
||||
#include <QTableView>
|
||||
|
||||
#include "DolphinQt2/GameList/GameFile.h"
|
||||
#include "DolphinQt2/GameList/GameListModel.h"
|
||||
|
||||
class GameList final : public QStackedWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GameList(QWidget* parent = nullptr);
|
||||
|
||||
QString GetSelectedGame() const;
|
||||
|
||||
public slots:
|
||||
void SetTableView() { setCurrentWidget(m_table); }
|
||||
void SetListView() { setCurrentWidget(m_list); }
|
||||
void SetViewColumn(int col, bool view) { m_table->setColumnHidden(col, !view); }
|
||||
|
||||
signals:
|
||||
void GameSelected();
|
||||
void DirectoryAdded(QString dir);
|
||||
|
||||
private:
|
||||
void MakeTableView();
|
||||
void MakeListView();
|
||||
|
||||
GameListModel* m_model;
|
||||
QSortFilterProxyModel* m_proxy;
|
||||
QListView* m_list;
|
||||
QTableView* m_table;
|
||||
};
|
132
Source/Core/DolphinQt2/GameList/GameListModel.cpp
Normal file
132
Source/Core/DolphinQt2/GameList/GameListModel.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
// Copyright 2015 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "DolphinQt2/Resources.h"
|
||||
#include "DolphinQt2/GameList/GameListModel.h"
|
||||
|
||||
static QString FormatSize(qint64 size)
|
||||
{
|
||||
QStringList units{
|
||||
QStringLiteral("KB"),
|
||||
QStringLiteral("MB"),
|
||||
QStringLiteral("GB"),
|
||||
QStringLiteral("TB")
|
||||
};
|
||||
QStringListIterator i(units);
|
||||
QString unit = QStringLiteral("B");
|
||||
double num = (double) size;
|
||||
while (num > 1024.0 && i.hasNext())
|
||||
{
|
||||
unit = i.next();
|
||||
num /= 1024.0;
|
||||
}
|
||||
return QStringLiteral("%1 %2").arg(QString::number(num, 'f', 1)).arg(unit);
|
||||
}
|
||||
|
||||
GameListModel::GameListModel(QObject* parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{
|
||||
connect(&m_tracker, &GameTracker::GameLoaded, this, &GameListModel::UpdateGame);
|
||||
connect(&m_tracker, &GameTracker::GameRemoved, this, &GameListModel::RemoveGame);
|
||||
connect(this, &GameListModel::DirectoryAdded, &m_tracker, &GameTracker::AddDirectory);
|
||||
}
|
||||
|
||||
QVariant GameListModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
QSharedPointer<GameFile> game = m_games[index.row()];
|
||||
if (index.column() == COL_PLATFORM && role == Qt::DecorationRole)
|
||||
return QVariant(Resources::GetPlatform(game->GetPlatform()));
|
||||
else if (index.column() == COL_PLATFORM && role == Qt::DisplayRole)
|
||||
return QVariant(game->GetPlatform());
|
||||
|
||||
else if (index.column() == COL_TITLE && role == Qt::DecorationRole)
|
||||
return QVariant(game->GetBanner());
|
||||
else if (index.column() == COL_TITLE && role == Qt::DisplayRole)
|
||||
return QVariant(game->GetLongName());
|
||||
|
||||
else if (index.column() == COL_ID && role == Qt::DisplayRole)
|
||||
return QVariant(game->GetUniqueID());
|
||||
|
||||
else if (index.column() == COL_DESCRIPTION && role == Qt::DisplayRole)
|
||||
return QVariant(game->GetDescription());
|
||||
|
||||
else if (index.column() == COL_MAKER && role == Qt::DisplayRole)
|
||||
return QVariant(game->GetCompany());
|
||||
|
||||
// FIXME this sorts lexicographically, not by size.
|
||||
else if (index.column() == COL_SIZE && role == Qt::DisplayRole)
|
||||
return QVariant(FormatSize(game->GetFileSize()));
|
||||
|
||||
else if (index.column() == COL_COUNTRY && role == Qt::DecorationRole)
|
||||
return QVariant(Resources::GetCountry(game->GetCountry()));
|
||||
else if (index.column() == COL_COUNTRY && role == Qt::DisplayRole)
|
||||
return QVariant(game->GetCountry());
|
||||
|
||||
else if (index.column() == COL_RATING && role == Qt::DecorationRole)
|
||||
return QVariant(Resources::GetRating(game->GetRating()));
|
||||
else if (index.column() == COL_RATING && role == Qt::DisplayRole)
|
||||
return QVariant(game->GetRating());
|
||||
|
||||
else if (index.column() == COL_LARGE_ICON && role == Qt::DecorationRole)
|
||||
return QVariant(game->GetBanner().scaled(144, 48));
|
||||
else if (index.column() == COL_LARGE_ICON && role == Qt::DisplayRole)
|
||||
return QVariant(game->GetLongName());
|
||||
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant GameListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Vertical || role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case COL_TITLE: return QVariant(tr("Title"));
|
||||
case COL_ID: return QVariant(tr("ID"));
|
||||
case COL_DESCRIPTION: return QVariant(tr("Description"));
|
||||
case COL_MAKER: return QVariant(tr("Maker"));
|
||||
case COL_SIZE: return QVariant(tr("Size"));
|
||||
case COL_RATING: return QVariant(tr("Quality"));
|
||||
default: return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
int GameListModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
return m_games.size();
|
||||
}
|
||||
|
||||
int GameListModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
return NUM_COLS;
|
||||
}
|
||||
|
||||
|
||||
void GameListModel::UpdateGame(QSharedPointer<GameFile> game)
|
||||
{
|
||||
QString path = game->GetPath();
|
||||
if (m_entries.contains(path))
|
||||
RemoveGame(path);
|
||||
|
||||
beginInsertRows(QModelIndex(), m_games.size(), m_games.size());
|
||||
m_entries[path] = m_games.size();
|
||||
m_games.append(game);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void GameListModel::RemoveGame(QString path)
|
||||
{
|
||||
int entry = m_entries[path];
|
||||
beginRemoveRows(QModelIndex(), entry, entry);
|
||||
m_entries.remove(path);
|
||||
m_games.removeAt(entry);
|
||||
endRemoveRows();
|
||||
}
|
55
Source/Core/DolphinQt2/GameList/GameListModel.h
Normal file
55
Source/Core/DolphinQt2/GameList/GameListModel.h
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2015 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QString>
|
||||
|
||||
#include "DolphinQt2/GameList/GameFile.h"
|
||||
#include "DolphinQt2/GameList/GameTracker.h"
|
||||
|
||||
class GameListModel final : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GameListModel(QObject* parent = nullptr);
|
||||
|
||||
// Qt's Model/View stuff uses these overrides.
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
int rowCount(const QModelIndex& parent) const;
|
||||
int columnCount(const QModelIndex& parent) const;
|
||||
|
||||
// Path of the Game at the specified index.
|
||||
QString GetPath(int index) const { return m_games[index]->GetPath(); }
|
||||
|
||||
enum
|
||||
{
|
||||
COL_PLATFORM = 0,
|
||||
COL_ID,
|
||||
COL_TITLE,
|
||||
COL_DESCRIPTION,
|
||||
COL_MAKER,
|
||||
COL_SIZE,
|
||||
COL_COUNTRY,
|
||||
COL_RATING,
|
||||
COL_LARGE_ICON,
|
||||
NUM_COLS
|
||||
};
|
||||
|
||||
public slots:
|
||||
void UpdateGame(QSharedPointer<GameFile> game);
|
||||
void RemoveGame(QString path);
|
||||
|
||||
signals:
|
||||
void DirectoryAdded(QString dir);
|
||||
|
||||
private:
|
||||
GameTracker m_tracker;
|
||||
QList<QSharedPointer<GameFile>> m_games;
|
||||
// Path -> index in m_games
|
||||
QMap<QString, int> m_entries;
|
||||
};
|
84
Source/Core/DolphinQt2/GameList/GameTracker.cpp
Normal file
84
Source/Core/DolphinQt2/GameList/GameTracker.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2015 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFile>
|
||||
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "DolphinQt2/GameList/GameTracker.h"
|
||||
|
||||
GameTracker::GameTracker(QObject* parent)
|
||||
: QFileSystemWatcher(parent)
|
||||
{
|
||||
m_loader = new GameLoader;
|
||||
m_loader->moveToThread(&m_loader_thread);
|
||||
|
||||
qRegisterMetaType<QSharedPointer<GameFile>>();
|
||||
connect(&m_loader_thread, &QThread::finished, m_loader, &QObject::deleteLater);
|
||||
connect(this, &QFileSystemWatcher::directoryChanged, this, &GameTracker::UpdateDirectory);
|
||||
connect(this, &QFileSystemWatcher::fileChanged, this, &GameTracker::UpdateFile);
|
||||
connect(this, &GameTracker::PathChanged, m_loader, &GameLoader::LoadGame);
|
||||
connect(m_loader, &GameLoader::GameLoaded, this, &GameTracker::GameLoaded);
|
||||
|
||||
GenerateFilters();
|
||||
|
||||
m_loader_thread.start();
|
||||
|
||||
for (const std::string& dir : SConfig::GetInstance().m_ISOFolder)
|
||||
AddDirectory(QString::fromStdString(dir));
|
||||
}
|
||||
|
||||
GameTracker::~GameTracker()
|
||||
{
|
||||
m_loader_thread.quit();
|
||||
m_loader_thread.wait();
|
||||
}
|
||||
|
||||
void GameTracker::AddDirectory(QString dir)
|
||||
{
|
||||
addPath(dir);
|
||||
UpdateDirectory(dir);
|
||||
}
|
||||
|
||||
void GameTracker::UpdateDirectory(QString dir)
|
||||
{
|
||||
QDirIterator it(dir, m_filters);
|
||||
while (it.hasNext())
|
||||
{
|
||||
QString path = QFileInfo(it.next()).canonicalFilePath();
|
||||
if (!m_tracked_files.contains(path))
|
||||
{
|
||||
addPath(path);
|
||||
m_tracked_files.insert(path);
|
||||
emit PathChanged(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameTracker::UpdateFile(QString file)
|
||||
{
|
||||
if (QFileInfo(file).exists())
|
||||
{
|
||||
emit PathChanged(file);
|
||||
}
|
||||
else if (removePath(file))
|
||||
{
|
||||
m_tracked_files.remove(file);
|
||||
emit GameRemoved(file);
|
||||
}
|
||||
}
|
||||
|
||||
void GameTracker::GenerateFilters()
|
||||
{
|
||||
m_filters.clear();
|
||||
if (SConfig::GetInstance().m_ListGC)
|
||||
m_filters << tr("*.gcm");
|
||||
if (SConfig::GetInstance().m_ListWii || SConfig::GetInstance().m_ListGC)
|
||||
m_filters << tr("*.iso") << tr("*.ciso") << tr("*.gcz") << tr("*.wbfs");
|
||||
if (SConfig::GetInstance().m_ListWad)
|
||||
m_filters << tr("*.wad");
|
||||
if (SConfig::GetInstance().m_ListElfDol)
|
||||
m_filters << tr("*.elf") << tr("*.dol");
|
||||
}
|
66
Source/Core/DolphinQt2/GameList/GameTracker.h
Normal file
66
Source/Core/DolphinQt2/GameList/GameTracker.h
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2015 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QSet>
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QThread>
|
||||
|
||||
#include "DolphinQt2/GameList/GameFile.h"
|
||||
#include "DolphinQt2/GameList/GameTracker.h"
|
||||
|
||||
class GameLoader;
|
||||
|
||||
// Watches directories and loads GameFiles in a separate thread.
|
||||
// To use this, just add directories using AddDirectory, and listen for the
|
||||
// GameLoaded and GameRemoved signals.
|
||||
class GameTracker final : public QFileSystemWatcher
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GameTracker(QObject* parent = nullptr);
|
||||
~GameTracker();
|
||||
|
||||
public slots:
|
||||
void AddDirectory(QString dir);
|
||||
|
||||
signals:
|
||||
void GameLoaded(QSharedPointer<GameFile> game);
|
||||
void GameRemoved(QString path);
|
||||
|
||||
void PathChanged(QString path);
|
||||
|
||||
private:
|
||||
void UpdateDirectory(QString dir);
|
||||
void UpdateFile(QString path);
|
||||
void GenerateFilters();
|
||||
|
||||
QSet<QString> m_tracked_files;
|
||||
QStringList m_filters;
|
||||
QThread m_loader_thread;
|
||||
GameLoader* m_loader;
|
||||
};
|
||||
|
||||
class GameLoader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
void LoadGame(QString path)
|
||||
{
|
||||
GameFile* game = new GameFile(path);
|
||||
if (game->IsValid())
|
||||
emit GameLoaded(QSharedPointer<GameFile>(game));
|
||||
}
|
||||
|
||||
signals:
|
||||
void GameLoaded(QSharedPointer<GameFile> game);
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QSharedPointer<GameFile>)
|
Reference in New Issue
Block a user