citra-emu
/
citra
Archived
1
0
Fork 0

citra_qt: Add Game List configuration

This adds a Game List configuration group box which is similar to yuzu's, with features including icon size setting, row 1/2 text, and ability to hide invalid titles (those without a valid SMDH). I also added a UI tab and moved the language and theme settings there.
This commit is contained in:
zhupengfei 2018-09-23 11:22:44 +08:00 committed by zhupengfei
parent f405134913
commit 90f9d32f13
No known key found for this signature in database
GPG Key ID: DD129E108BD09378
15 changed files with 405 additions and 120 deletions

View File

@ -43,6 +43,8 @@ add_executable(citra-qt
configuration/configure_motion_touch.h
configuration/configure_system.cpp
configuration/configure_system.h
configuration/configure_ui.cpp
configuration/configure_ui.h
configuration/configure_web.cpp
configuration/configure_web.h
debugger/console.h
@ -119,6 +121,7 @@ set(UIS
configuration/configure_input.ui
configuration/configure_motion_touch.ui
configuration/configure_system.ui
configuration/configure_ui.ui
configuration/configure_web.ui
debugger/registers.ui
multiplayer/direct_connect.ui

View File

@ -219,6 +219,31 @@ void Config::ReadValues() {
ReadSetting("microProfileDialogVisible", false).toBool();
qt_config->endGroup();
qt_config->beginGroup("GameList");
UISettings::values.game_list_icon_size = ReadSetting("iconSize", 2).toInt();
if (UISettings::values.game_list_icon_size < 0 || UISettings::values.game_list_icon_size > 2) {
LOG_ERROR(Config, "Invalid value for game_list_icon_size: {}",
UISettings::values.game_list_icon_size);
UISettings::values.game_list_icon_size = 2;
}
UISettings::values.game_list_row_1 = ReadSetting("row1", 2).toInt();
if (UISettings::values.game_list_row_1 < 0 || UISettings::values.game_list_row_1 > 3) {
LOG_ERROR(Config, "Invalid value for game_list_row_1: {}",
UISettings::values.game_list_row_1);
UISettings::values.game_list_row_1 = 2;
}
UISettings::values.game_list_row_2 = ReadSetting("row2", 0).toInt();
if (UISettings::values.game_list_row_2 < -1 || UISettings::values.game_list_row_2 > 3) {
LOG_ERROR(Config, "Invalid value for game_list_row_2: {}",
UISettings::values.game_list_row_2);
UISettings::values.game_list_row_2 = 0;
}
UISettings::values.game_list_hide_no_icon = ReadSetting("hideNoIcon", false).toBool();
qt_config->endGroup();
qt_config->beginGroup("Paths");
UISettings::values.roms_path = ReadSetting("romsPath").toString();
UISettings::values.symbols_path = ReadSetting("symbolsPath").toString();
@ -448,6 +473,13 @@ void Config::SaveValues() {
WriteSetting("microProfileDialogVisible", UISettings::values.microprofile_visible, false);
qt_config->endGroup();
qt_config->beginGroup("GameList");
WriteSetting("iconSize", UISettings::values.game_list_icon_size, 2);
WriteSetting("row1", UISettings::values.game_list_row_1, 2);
WriteSetting("row2", UISettings::values.game_list_row_2, 0);
WriteSetting("hideNoIcon", UISettings::values.game_list_hide_no_icon, false);
qt_config->endGroup();
qt_config->beginGroup("Paths");
WriteSetting("romsPath", UISettings::values.roms_path);
WriteSetting("symbolsPath", UISettings::values.symbols_path);

View File

@ -63,6 +63,11 @@
<string>Web</string>
</attribute>
</widget>
<widget class="ConfigureUi" name="uiTab">
<attribute name="title">
<string>UI</string>
</attribute>
</widget>
</widget>
</item>
</layout>
@ -125,6 +130,12 @@
<header>configuration/configure_web.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureUi</class>
<extends>QWidget</extends>
<header>configuration/configure_ui.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>

View File

@ -16,8 +16,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry
ui->generalTab->PopulateHotkeyList(registry);
this->setConfiguration();
this->PopulateSelectionList();
connect(ui->generalTab, &ConfigureGeneral::languageChanged, this,
&ConfigureDialog::onLanguageChanged);
connect(ui->uiTab, &ConfigureUi::languageChanged, this, &ConfigureDialog::onLanguageChanged);
connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
&ConfigureDialog::UpdateVisibleTabs);
@ -39,6 +38,7 @@ void ConfigureDialog::applyConfiguration() {
ui->cameraTab->applyConfiguration();
ui->debugTab->applyConfiguration();
ui->webTab->applyConfiguration();
ui->uiTab->applyConfiguration();
Settings::Apply();
Settings::LogSettings();
}
@ -46,7 +46,7 @@ void ConfigureDialog::applyConfiguration() {
void ConfigureDialog::PopulateSelectionList() {
const std::array<std::pair<QString, QStringList>, 4> items{
{{tr("General"), {tr("General"), tr("Web"), tr("Debug")}},
{{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("UI")}},
{tr("System"), {tr("System"), tr("Audio"), tr("Camera")}},
{tr("Graphics"), {tr("Graphics")}},
{tr("Controls"), {tr("Input")}}}};
@ -70,6 +70,7 @@ void ConfigureDialog::onLanguageChanged(const QString& locale) {
ui->cameraTab->retranslateUi();
ui->debugTab->retranslateUi();
ui->webTab->retranslateUi();
ui->uiTab->retranslateUi();
}
void ConfigureDialog::UpdateVisibleTabs() {
@ -77,11 +78,15 @@ void ConfigureDialog::UpdateVisibleTabs() {
if (items.isEmpty())
return;
const QHash<QString, QWidget*> widgets = {
{tr("General"), ui->generalTab}, {tr("System"), ui->systemTab},
{tr("Input"), ui->inputTab}, {tr("Graphics"), ui->graphicsTab},
{tr("Audio"), ui->audioTab}, {tr("Camera"), ui->cameraTab},
{tr("Debug"), ui->debugTab}, {tr("Web"), ui->webTab}};
const QHash<QString, QWidget*> widgets = {{tr("General"), ui->generalTab},
{tr("System"), ui->systemTab},
{tr("Input"), ui->inputTab},
{tr("Graphics"), ui->graphicsTab},
{tr("Audio"), ui->audioTab},
{tr("Camera"), ui->cameraTab},
{tr("Debug"), ui->debugTab},
{tr("Web"), ui->webTab},
{tr("UI"), ui->uiTab}};
ui->tabWidget->clear();

View File

@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QDirIterator>
#include "citra_qt/configuration/configure_general.h"
#include "citra_qt/ui_settings.h"
#include "core/core.h"
@ -13,28 +12,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGeneral) {
ui->setupUi(this);
ui->language_combobox->addItem(tr("<System>"), QString(""));
ui->language_combobox->addItem(tr("English"), QString("en"));
QDirIterator it(":/languages", QDirIterator::NoIteratorFlags);
while (it.hasNext()) {
QString locale = it.next();
locale.truncate(locale.lastIndexOf('.'));
locale.remove(0, locale.lastIndexOf('/') + 1);
QString lang = QLocale::languageToString(QLocale(locale).language());
ui->language_combobox->addItem(lang, locale);
}
// Unlike other configuration changes, interface language changes need to be reflected on the
// interface immediately. This is done by passing a signal to the main window, and then
// retranslating when passing back.
connect(ui->language_combobox,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&ConfigureGeneral::onLanguageChanged);
for (const auto& theme : UISettings::themes) {
ui->theme_combobox->addItem(theme.first, theme.second);
}
this->setConfiguration();
ui->updateBox->setVisible(UISettings::values.updater_found);
@ -50,10 +27,6 @@ void ConfigureGeneral::setConfiguration() {
// The first item is "auto-select" with actual value -1, so plus one here will do the trick
ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1);
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
ui->language_combobox->setCurrentIndex(
ui->language_combobox->findData(UISettings::values.language));
}
void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
@ -62,8 +35,6 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
void ConfigureGeneral::applyConfiguration() {
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
UISettings::values.theme =
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked();
UISettings::values.update_on_close = ui->toggle_auto_update->isChecked();
@ -71,13 +42,6 @@ void ConfigureGeneral::applyConfiguration() {
Settings::values.region_value = ui->region_combobox->currentIndex() - 1;
}
void ConfigureGeneral::onLanguageChanged(int index) {
if (index == -1)
return;
emit languageChanged(ui->language_combobox->itemData(index).toString());
}
void ConfigureGeneral::retranslateUi() {
ui->retranslateUi(this);
ui->hotkeysDialog->retranslateUi();

View File

@ -24,12 +24,6 @@ public:
void applyConfiguration();
void retranslateUi();
private slots:
void onLanguageChanged(int index);
signals:
void languageChanged(const QString& locale);
private:
void setConfiguration();

View File

@ -31,20 +31,6 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="language_label">
<property name="text">
<string>Interface language</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="language_combobox"/>
</item>
</layout>
</item>
</layout>
</item>
</layout>
@ -145,33 +131,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="theme_group_box">
<property name="title">
<string>Theme</string>
</property>
<layout class="QHBoxLayout" name="theme_qhbox_layout">
<item>
<layout class="QVBoxLayout" name="theme_qvbox_layout">
<item>
<layout class="QHBoxLayout" name="theme_qhbox_layout_2">
<item>
<widget class="QLabel" name="theme_label">
<property name="text">
<string>Theme:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="theme_combobox"/>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">

View File

@ -0,0 +1,67 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QDirIterator>
#include "citra_qt/configuration/configure_ui.h"
#include "citra_qt/ui_settings.h"
#include "ui_configure_ui.h"
ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureUi) {
ui->setupUi(this);
ui->language_combobox->addItem(tr("<System>"), QString(""));
ui->language_combobox->addItem(tr("English"), QString("en"));
QDirIterator it(":/languages", QDirIterator::NoIteratorFlags);
while (it.hasNext()) {
QString locale = it.next();
locale.truncate(locale.lastIndexOf('.'));
locale.remove(0, locale.lastIndexOf('/') + 1);
QString lang = QLocale::languageToString(QLocale(locale).language());
ui->language_combobox->addItem(lang, locale);
}
// Unlike other configuration changes, interface language changes need to be reflected on the
// interface immediately. This is done by passing a signal to the main window, and then
// retranslating when passing back.
connect(ui->language_combobox,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&ConfigureUi::onLanguageChanged);
for (const auto& theme : UISettings::themes) {
ui->theme_combobox->addItem(theme.first, theme.second);
}
this->setConfiguration();
}
ConfigureUi::~ConfigureUi() = default;
void ConfigureUi::setConfiguration() {
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
ui->language_combobox->setCurrentIndex(
ui->language_combobox->findData(UISettings::values.language));
ui->icon_size_combobox->setCurrentIndex(UISettings::values.game_list_icon_size);
ui->row_1_text_combobox->setCurrentIndex(UISettings::values.game_list_row_1);
ui->row_2_text_combobox->setCurrentIndex(UISettings::values.game_list_row_2 + 1);
ui->toggle_hide_no_icon->setChecked(UISettings::values.game_list_hide_no_icon);
}
void ConfigureUi::applyConfiguration() {
UISettings::values.theme =
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
UISettings::values.game_list_icon_size = ui->icon_size_combobox->currentIndex();
UISettings::values.game_list_row_1 = ui->row_1_text_combobox->currentIndex();
UISettings::values.game_list_row_2 = ui->row_2_text_combobox->currentIndex() - 1;
UISettings::values.game_list_hide_no_icon = ui->toggle_hide_no_icon->isChecked();
}
void ConfigureUi::onLanguageChanged(int index) {
if (index == -1)
return;
emit languageChanged(ui->language_combobox->itemData(index).toString());
}
void ConfigureUi::retranslateUi() {
ui->retranslateUi(this);
}

View File

@ -0,0 +1,34 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <QWidget>
namespace Ui {
class ConfigureUi;
}
class ConfigureUi : public QWidget {
Q_OBJECT
public:
explicit ConfigureUi(QWidget* parent = nullptr);
~ConfigureUi();
void applyConfiguration();
void retranslateUi();
private slots:
void onLanguageChanged(int index);
signals:
void languageChanged(const QString& locale);
private:
void setConfiguration();
std::unique_ptr<Ui::ConfigureUi> ui;
};

View File

@ -0,0 +1,194 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureUi</class>
<widget class="QWidget" name="ConfigureUi">
<property name="windowTitle">
<string>Form</string>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>290</width>
<height>280</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="general_groupBox">
<property name="title">
<string>General</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="language_label">
<property name="text">
<string>Interface language:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="language_combobox"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="theme_label">
<property name="text">
<string>Theme:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="theme_combobox"/>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="game_list_groupBox">
<property name="title">
<string>Game List</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="icon_size_label">
<property name="text">
<string>Icon Size:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="icon_size_combobox">
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Small (24x24)</string>
</property>
</item>
<item>
<property name="text">
<string>Large (48x48)</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="row_1_text_label">
<property name="text">
<string>Row 1 Text:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="row_1_text_combobox">
<item>
<property name="text">
<string>File Name</string>
</property>
</item>
<item>
<property name="text">
<string>Full Path</string>
</property>
</item>
<item>
<property name="text">
<string>Title Name</string>
</property>
</item>
<item>
<property name="text">
<string>Title ID</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="row_2_text_label">
<property name="text">
<string>Row 2 Text:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="row_2_text_combobox">
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>File Name</string>
</property>
</item>
<item>
<property name="text">
<string>Full Path</string>
</property>
</item>
<item>
<property name="text">
<string>Title Name</string>
</property>
</item>
<item>
<property name="text">
<string>Title ID</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="toggle_hide_no_icon">
<property name="text">
<string>Hide Titles without Icon</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -723,6 +723,11 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
return update_smdh;
}();
if (!Loader::IsValidSMDH(smdh) && UISettings::values.game_list_hide_no_icon) {
// Skip this invalid entry
return true;
}
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
// The game list uses this as compatibility number for untested games

View File

@ -62,6 +62,8 @@ public:
QString FindGameByProgramID(u64 program_id);
void RefreshGameDirectory();
static const QStringList supported_file_extensions;
signals:
@ -87,8 +89,6 @@ private:
void ValidateEntry(const QModelIndex& item);
void DonePopulating(QStringList watch_list);
void RefreshGameDirectory();
void PopupContextMenu(const QPoint& menu_location);
void AddGamePopup(QMenu& context_menu, const QString& path, u64 program_id, u64 extdata_id);
void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected);

View File

@ -145,9 +145,17 @@ public:
setData(qulonglong(program_id), ProgramIdRole);
setData(qulonglong(extdata_id), ExtdataIdRole);
if (!UISettings::values.game_list_icon_size) {
// Do not display icons
setData(QPixmap(), Qt::DecorationRole);
}
bool large = UISettings::values.game_list_icon_size == 2;
if (!Loader::IsValidSMDH(smdh_data)) {
// SMDH is not valid, set a default icon
setData(GetDefaultIcon(true), Qt::DecorationRole);
if (UISettings::values.game_list_icon_size)
setData(GetDefaultIcon(large), Qt::DecorationRole);
return;
}
@ -155,7 +163,8 @@ public:
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
// Get icon from SMDH
setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole);
if (UISettings::values.game_list_icon_size)
setData(GetQPixmapFromSMDH(smdh, large), Qt::DecorationRole);
// Get title from SMDH
setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English),
@ -171,29 +180,23 @@ public:
std::string path, filename, extension;
Common::SplitPath(data(FullPathRole).toString().toStdString(), &path, &filename,
&extension);
QString title = data(TitleRole).toString();
QString second_name = QString::fromStdString(filename + extension);
static QRegExp installed_pattern(
const std::array<QString, 4> display_texts{{
QString::fromStdString(filename + extension), // file name
data(FullPathRole).toString(), // full path
data(TitleRole).toString(), // title name
QString::fromStdString(
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) +
"Nintendo "
"3DS/00000000000000000000000000000000/00000000000000000000000000000000/"
"title/0004000(0|e)/[0-9a-f]{8}/content/")
.replace("\\", "\\\\"));
static QRegExp system_pattern(
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"00000000000000000000000000000000/"
"title/00040010/[0-9a-f]{8}/content/")
.replace("\\", "\\\\"));
if (installed_pattern.exactMatch(QString::fromStdString(path)) ||
system_pattern.exactMatch(QString::fromStdString(path))) {
// Use a different mechanism for system / installed titles showing program ID
second_name = QString("%1-%2")
.arg(data(ProgramIdRole).toULongLong(), 16, 16, QChar('0'))
.toUpper()
.arg(QString::fromStdString(filename));
fmt::format("{:016X}", data(ProgramIdRole).toULongLong())), // title id
}};
const QString& row1 = display_texts.at(UISettings::values.game_list_row_1);
QString row2;
int row_2_id = UISettings::values.game_list_row_2;
if (row_2_id != -1) {
row2 = (row1.isEmpty() ? "" : "\n ") + display_texts.at(row_2_id);
}
return title + (title.isEmpty() ? "" : "\n ") + second_name;
return row1 + row2;
} else {
return GameListItem::data(role);
}
@ -320,18 +323,22 @@ public:
UISettings::GameDir* game_dir = &directory;
setData(QVariant::fromValue(game_dir), GameDirRole);
constexpr std::array<int, 3> icon_sizes{{0, 24, 48}};
int icon_size = icon_sizes[UISettings::values.game_list_icon_size];
switch (dir_type) {
case GameListItemType::InstalledDir:
setData(QIcon::fromTheme("sd_card").pixmap(48), Qt::DecorationRole);
setData(QIcon::fromTheme("sd_card").pixmap(icon_size), Qt::DecorationRole);
setData("Installed Titles", Qt::DisplayRole);
break;
case GameListItemType::SystemDir:
setData(QIcon::fromTheme("chip").pixmap(48), Qt::DecorationRole);
setData(QIcon::fromTheme("chip").pixmap(icon_size), Qt::DecorationRole);
setData("System Titles", Qt::DisplayRole);
break;
case GameListItemType::CustomDir:
QString icon_name = QFileInfo::exists(game_dir->path) ? "folder" : "bad_folder";
setData(QIcon::fromTheme(icon_name).pixmap(48), Qt::DecorationRole);
setData(QIcon::fromTheme(icon_name).pixmap(icon_size), Qt::DecorationRole);
setData(game_dir->path, Qt::DisplayRole);
break;
};
@ -349,7 +356,10 @@ class GameListAddDir : public GameListItem {
public:
explicit GameListAddDir() {
setData(type(), TypeRole);
setData(QIcon::fromTheme("plus").pixmap(48), Qt::DecorationRole);
constexpr std::array<int, 3> icon_sizes{{0, 24, 48}};
int icon_size = icon_sizes[UISettings::values.game_list_icon_size];
setData(QIcon::fromTheme("plus").pixmap(icon_size), Qt::DecorationRole);
setData("Add New Game Directory", Qt::DisplayRole);
}

View File

@ -1257,6 +1257,7 @@ void GMainWindow::OnConfigure() {
SetDiscordEnabled(UISettings::values.enable_discord_presence);
emit UpdateThemedIcons();
SyncMenuUISettings();
game_list->RefreshGameDirectory();
config->Save();
}
}

View File

@ -58,6 +58,12 @@ struct Values {
// Discord RPC
bool enable_discord_presence;
// Game List
int game_list_icon_size;
int game_list_row_1;
int game_list_row_2;
bool game_list_hide_no_icon;
QString roms_path;
QString symbols_path;
QString movie_record_path;