GameSettings: Add a bunch more user settings

This commit is contained in:
Connor McLaughlin 2020-09-02 00:00:48 +10:00
parent bf85fbe331
commit c9cefe4020
10 changed files with 531 additions and 83 deletions

View file

@ -13,6 +13,7 @@ A "BIOS" ROM image is required to to start the emulator and to play games. You c
## Latest News
- 2020/09/01: Many additional user settings available, including memory cards and enhancements. Now you can set these per-game.
- 2020/08/25: Automated builds for macOS now available.
- 2020/08/22: XInput controller backend added.
- 2020/08/20: Per-game setting overrides added. Mostly for compatibility, but some options are customizable.

View file

@ -9,9 +9,13 @@
#include "scmversion/scmversion.h"
#include <QtGui/QClipboard>
#include <QtGui/QGuiApplication>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
static constexpr char MEMORY_CARD_IMAGE_FILTER[] =
QT_TRANSLATE_NOOP("MemoryCardSettingsWidget", "All Memory Card Types (*.mcd *.mcr *.mc)");
GamePropertiesDialog::GamePropertiesDialog(QtHostInterface* host_interface, QWidget* parent /* = nullptr */)
: QDialog(parent), m_host_interface(host_interface)
{
@ -127,13 +131,15 @@ void GamePropertiesDialog::setupAdditionalUi()
qApp->translate("DisplayCropMode", Settings::GetDisplayCropModeDisplayName(static_cast<DisplayCropMode>(i))));
}
m_ui.userResolutionScale->addItem(tr("(unchanged)"));
QtUtils::FillComboBoxWithResolutionScales(m_ui.userResolutionScale);
m_ui.userControllerType1->addItem(tr("(unchanged)"));
for (u32 i = 0; i < static_cast<u32>(ControllerType::Count); i++)
{
m_ui.userControllerType1->addItem(
qApp->translate("ControllerType", Settings::GetControllerTypeDisplayName(static_cast<ControllerType>(i))));
}
m_ui.userControllerType2->addItem(tr("(unchanged)"));
for (u32 i = 0; i < static_cast<u32>(ControllerType::Count); i++)
{
@ -141,6 +147,19 @@ void GamePropertiesDialog::setupAdditionalUi()
qApp->translate("ControllerType", Settings::GetControllerTypeDisplayName(static_cast<ControllerType>(i))));
}
m_ui.userMemoryCard1Type->addItem(tr("(unchanged)"));
for (u32 i = 0; i < static_cast<u32>(MemoryCardType::Count); i++)
{
m_ui.userMemoryCard1Type->addItem(
qApp->translate("MemoryCardType", Settings::GetMemoryCardTypeDisplayName(static_cast<MemoryCardType>(i))));
}
m_ui.userMemoryCard2Type->addItem(tr("(unchanged)"));
for (u32 i = 0; i < static_cast<u32>(MemoryCardType::Count); i++)
{
m_ui.userMemoryCard2Type->addItem(
qApp->translate("MemoryCardType", Settings::GetMemoryCardTypeDisplayName(static_cast<MemoryCardType>(i))));
}
QGridLayout* traits_layout = new QGridLayout(m_ui.compatibilityTraits);
for (u32 i = 0; i < static_cast<u32>(GameSettings::Trait::Count); i++)
{
@ -198,6 +217,26 @@ void GamePropertiesDialog::populateTracksInfo(const std::string& image_path)
}
}
void GamePropertiesDialog::populateBooleanUserSetting(QCheckBox* cb, const std::optional<bool>& value)
{
QSignalBlocker sb(cb);
if (value.has_value())
cb->setCheckState(value.value() ? Qt::Checked : Qt::Unchecked);
else
cb->setCheckState(Qt::PartiallyChecked);
}
void GamePropertiesDialog::connectBooleanUserSetting(QCheckBox* cb, std::optional<bool>* value)
{
connect(cb, &QCheckBox::stateChanged, [this, value](int state) {
if (state == Qt::PartiallyChecked)
value->reset();
else
*value = (state == Qt::Checked);
saveGameSettings();
});
}
void GamePropertiesDialog::populateGameSettings()
{
const GameSettings::Entry& gs = m_game_settings;
@ -230,6 +269,27 @@ void GamePropertiesDialog::populateGameSettings()
m_ui.userAspectRatio->setCurrentIndex(static_cast<int>(gs.display_aspect_ratio.value()) + 1);
}
populateBooleanUserSetting(m_ui.userLinearUpscaling, gs.display_linear_upscaling);
populateBooleanUserSetting(m_ui.userIntegerUpscaling, gs.display_integer_upscaling);
if (gs.gpu_resolution_scale.has_value())
{
QSignalBlocker sb(m_ui.userResolutionScale);
m_ui.userResolutionScale->setCurrentIndex(static_cast<int>(gs.gpu_resolution_scale.value()) + 1);
}
else
{
QSignalBlocker sb(m_ui.userResolutionScale);
m_ui.userResolutionScale->setCurrentIndex(0);
}
populateBooleanUserSetting(m_ui.userTrueColor, gs.gpu_true_color);
populateBooleanUserSetting(m_ui.userScaledDithering, gs.gpu_scaled_dithering);
populateBooleanUserSetting(m_ui.userBilinearTextureFiltering, gs.gpu_bilinear_texture_filtering);
populateBooleanUserSetting(m_ui.userForceNTSCTimings, gs.gpu_force_ntsc_timings);
populateBooleanUserSetting(m_ui.userWidescreenHack, gs.gpu_widescreen_hack);
populateBooleanUserSetting(m_ui.userPGXP, gs.gpu_pgxp);
if (gs.controller_1_type.has_value())
{
QSignalBlocker sb(m_ui.userControllerType1);
@ -240,15 +300,26 @@ void GamePropertiesDialog::populateGameSettings()
QSignalBlocker sb(m_ui.userControllerType2);
m_ui.userControllerType2->setCurrentIndex(static_cast<int>(gs.controller_2_type.value()) + 1);
}
if (gs.gpu_widescreen_hack.has_value())
if (gs.memory_card_1_type.has_value())
{
QSignalBlocker sb(m_ui.userWidescreenHack);
m_ui.userWidescreenHack->setCheckState(gs.gpu_widescreen_hack.value() ? Qt::Checked : Qt::Unchecked);
QSignalBlocker sb(m_ui.userMemoryCard1Type);
m_ui.userMemoryCard1Type->setCurrentIndex(static_cast<int>(gs.memory_card_1_type.value()) + 1);
}
else
if (gs.memory_card_2_type.has_value())
{
QSignalBlocker sb(m_ui.userWidescreenHack);
m_ui.userWidescreenHack->setCheckState(Qt::PartiallyChecked);
QSignalBlocker sb(m_ui.userMemoryCard2Type);
m_ui.userMemoryCard2Type->setCurrentIndex(static_cast<int>(gs.memory_card_2_type.value()) + 1);
}
if (!gs.memory_card_1_shared_path.empty())
{
QSignalBlocker sb(m_ui.userMemoryCard1SharedPath);
m_ui.userMemoryCard1SharedPath->setText(QString::fromStdString(gs.memory_card_1_shared_path));
}
if (!gs.memory_card_2_shared_path.empty())
{
QSignalBlocker sb(m_ui.userMemoryCard2SharedPath);
m_ui.userMemoryCard2SharedPath->setText(QString::fromStdString(gs.memory_card_2_shared_path));
}
}
@ -306,6 +377,24 @@ void GamePropertiesDialog::connectUi()
saveGameSettings();
});
connectBooleanUserSetting(m_ui.userLinearUpscaling, &m_game_settings.display_linear_upscaling);
connectBooleanUserSetting(m_ui.userIntegerUpscaling, &m_game_settings.display_integer_upscaling);
connect(m_ui.userResolutionScale, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
if (index <= 0)
m_game_settings.gpu_resolution_scale.reset();
else
m_game_settings.gpu_resolution_scale = static_cast<u32>(index - 1);
saveGameSettings();
});
connectBooleanUserSetting(m_ui.userTrueColor, &m_game_settings.gpu_true_color);
connectBooleanUserSetting(m_ui.userScaledDithering, &m_game_settings.gpu_scaled_dithering);
connectBooleanUserSetting(m_ui.userForceNTSCTimings, &m_game_settings.gpu_force_ntsc_timings);
connectBooleanUserSetting(m_ui.userBilinearTextureFiltering, &m_game_settings.gpu_bilinear_texture_filtering);
connectBooleanUserSetting(m_ui.userWidescreenHack, &m_game_settings.gpu_widescreen_hack);
connectBooleanUserSetting(m_ui.userPGXP, &m_game_settings.gpu_pgxp);
connect(m_ui.userControllerType1, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
if (index <= 0)
m_game_settings.controller_1_type.reset();
@ -322,13 +411,50 @@ void GamePropertiesDialog::connectUi()
saveGameSettings();
});
connect(m_ui.userWidescreenHack, &QCheckBox::stateChanged, [this](int state) {
if (state == Qt::PartiallyChecked)
m_game_settings.gpu_widescreen_hack.reset();
connect(m_ui.userMemoryCard1Type, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
if (index <= 0)
m_game_settings.memory_card_1_type.reset();
else
m_game_settings.gpu_widescreen_hack = (state == Qt::Checked);
m_game_settings.memory_card_1_type = static_cast<MemoryCardType>(index - 1);
saveGameSettings();
});
connect(m_ui.userMemoryCard1SharedPath, &QLineEdit::textChanged, [this](const QString& text) {
if (text.isEmpty())
std::string().swap(m_game_settings.memory_card_1_shared_path);
else
m_game_settings.memory_card_1_shared_path = text.toStdString();
saveGameSettings();
});
connect(m_ui.userMemoryCard1SharedPathBrowse, &QPushButton::clicked, [this]() {
QString path = QFileDialog::getOpenFileName(this, tr("Select path to memory card image"), QString(),
qApp->translate("MemoryCardSettingsWidget", MEMORY_CARD_IMAGE_FILTER));
if (path.isEmpty())
return;
m_ui.userMemoryCard1SharedPath->setText(path);
});
connect(m_ui.userMemoryCard2Type, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
if (index <= 0)
m_game_settings.memory_card_2_type.reset();
else
m_game_settings.memory_card_2_type = static_cast<MemoryCardType>(index - 1);
saveGameSettings();
});
connect(m_ui.userMemoryCard2SharedPath, &QLineEdit::textChanged, [this](const QString& text) {
if (text.isEmpty())
std::string().swap(m_game_settings.memory_card_2_shared_path);
else
m_game_settings.memory_card_2_shared_path = text.toStdString();
saveGameSettings();
});
connect(m_ui.userMemoryCard2SharedPathBrowse, &QPushButton::clicked, [this]() {
QString path = QFileDialog::getOpenFileName(this, tr("Select path to memory card image"), QString(),
qApp->translate("MemoryCardSettingsWidget", MEMORY_CARD_IMAGE_FILTER));
if (path.isEmpty())
return;
m_ui.userMemoryCard2SharedPath->setText(path);
});
for (u32 i = 0; i < static_cast<u32>(GameSettings::Trait::Count); i++)
{

View file

@ -43,6 +43,8 @@ private:
void populateCompatibilityInfo(const std::string& game_code);
void populateTracksInfo(const std::string& image_path);
void populateGameSettings();
void populateBooleanUserSetting(QCheckBox* cb, const std::optional<bool>& value);
void connectBooleanUserSetting(QCheckBox* cb, std::optional<bool>* value);
void saveGameSettings();
void fillEntryFromUi(GameListCompatibilityEntry* entry);
void computeTrackHashes();

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>793</width>
<height>647</height>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
@ -21,7 +21,7 @@
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
@ -190,39 +190,132 @@
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>GPU Settings</string>
<string>GPU Screen Display</string>
</property>
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Crop Mode:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="userCropMode"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Aspect Ratio:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="0" column="1">
<widget class="QComboBox" name="userAspectRatio"/>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="userWidescreenHack">
<item row="1" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Widescreen Hack</string>
<string>Crop Mode:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="userCropMode"/>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="userLinearUpscaling">
<property name="text">
<string>Linear Upscaling</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="userIntegerUpscaling">
<property name="text">
<string>Integer Upscaling</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>GPU Enhancements</string>
</property>
<layout class="QFormLayout" name="formLayout_6">
<item row="0" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Resolution Scale:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="userResolutionScale"/>
</item>
<item row="1" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="userTrueColor">
<property name="text">
<string>True Color Rendering (24-bit, disables dithering)</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="userScaledDithering">
<property name="text">
<string>Scaled Dithering (scale dither pattern to resolution)</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="userWidescreenHack">
<property name="text">
<string>Widescreen Hack</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="userForceNTSCTimings">
<property name="text">
<string>Force NTSC Timings (60hz-on-PAL)</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="userBilinearTextureFiltering">
<property name="text">
<string>Bilinear Texture Filtering</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="userPGXP">
<property name="text">
<string>PGXP Geometry Correction</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
@ -256,7 +349,78 @@
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Memory Card Settings</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>Memory Card 1 Type:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="userMemoryCard1Type"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Memory Card 1 Shared Path:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLineEdit" name="userMemoryCard1SharedPath"/>
</item>
<item>
<widget class="QPushButton" name="userMemoryCard1SharedPathBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Memory Card 2 Type:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="userMemoryCard2Type"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Memory Card 2 Shared Path:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLineEdit" name="userMemoryCard2SharedPath"/>
</item>
<item>
<widget class="QPushButton" name="userMemoryCard2SharedPathBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>

View file

@ -1,6 +1,7 @@
#include "gpusettingswidget.h"
#include "core/gpu.h"
#include "core/settings.h"
#include "qtutils.h"
#include "settingsdialog.h"
#include "settingwidgetbinder.h"
@ -62,14 +63,15 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
dialog->registerWidgetHelp(
m_ui.renderer, tr("Renderer"), Settings::GetRendererDisplayName(Settings::DEFAULT_GPU_RENDERER),
tr(
"Chooses the backend to use for rendering the console/game visuals. <br>Depending on your system and hardware, "
"Direct3D 11 and OpenGL hardware backends may be available. <br>The software renderer offers the best compatibility, "
"but is the slowest and does not offer any enhancements."));
tr("Chooses the backend to use for rendering the console/game visuals. <br>Depending on your system and hardware, "
"Direct3D 11 and OpenGL hardware backends may be available. <br>The software renderer offers the best "
"compatibility, "
"but is the slowest and does not offer any enhancements."));
dialog->registerWidgetHelp(
m_ui.adapter, tr("Adapter"), tr("(Default)"),
tr("If your system contains multiple GPUs or adapters, you can select which GPU you wish to use for the hardware "
"renderers. <br>This option is only supported in Direct3D and Vulkan. OpenGL will always use the default device."));
"renderers. <br>This option is only supported in Direct3D and Vulkan. OpenGL will always use the default "
"device."));
dialog->registerWidgetHelp(
m_ui.displayAspectRatio, tr("Aspect Ratio"), QStringLiteral("4:3"),
tr("Changes the aspect ratio used to display the console's output to the screen. The default "
@ -82,14 +84,16 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
"compromise between stability and hiding black borders."));
dialog->registerWidgetHelp(
m_ui.disableInterlacing, tr("Disable Interlacing (force progressive render/scan)"), tr("Unchecked"),
tr("Forces the rendering and display of frames to progressive mode. <br>This removes the \"combing\" effect seen in "
"480i games by rendering them in 480p. Usually safe to enable.<br> "
"<b><u>May not be compatible with all games.</u></b>"));
dialog->registerWidgetHelp(
m_ui.displayLinearFiltering, tr("Linear Upscaling"), tr("Checked"),
tr("Uses bilinear texture filtering when displaying the console's framebuffer to the screen. <br>Disabling filtering "
"will producer a sharper, blockier/pixelated image. Enabling will smooth out the image. <br>The option will be less "
"noticable the higher the resolution scale."));
tr(
"Forces the rendering and display of frames to progressive mode. <br>This removes the \"combing\" effect seen in "
"480i games by rendering them in 480p. Usually safe to enable.<br> "
"<b><u>May not be compatible with all games.</u></b>"));
dialog->registerWidgetHelp(m_ui.displayLinearFiltering, tr("Linear Upscaling"), tr("Checked"),
tr("Uses bilinear texture filtering when displaying the console's framebuffer to the "
"screen. <br>Disabling filtering "
"will producer a sharper, blockier/pixelated image. Enabling will smooth out the "
"image. <br>The option will be less "
"noticable the higher the resolution scale."));
dialog->registerWidgetHelp(
m_ui.displayIntegerScaling, tr("Integer Upscaling"), tr("Unchecked"),
tr("Adds padding to the display area to ensure that the ratio between pixels on the host to "
@ -114,12 +118,12 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
m_ui.scaledDithering, tr("Scaled Dithering (scale dither pattern to resolution)"), tr("Checked"),
tr("Scales the dither pattern to the resolution scale of the emulated GPU. This makes the dither pattern much less "
"obvious at higher resolutions. <br>Usually safe to enable, and only supported by the hardware renderers."));
dialog->registerWidgetHelp(
m_ui.forceNTSCTimings, tr("Force NTSC Timings (60hz-on-PAL)"), tr("Unchecked"),
tr(
"Uses NTSC frame timings when the console is in PAL mode, forcing PAL games to run at 60hz. <br>For most games which "
"have a speed tied to the framerate, this will result in the game running approximately 17% faster. <br>For variable "
"frame rate games, it may not affect the speed."));
dialog->registerWidgetHelp(m_ui.forceNTSCTimings, tr("Force NTSC Timings (60hz-on-PAL)"), tr("Unchecked"),
tr("Uses NTSC frame timings when the console is in PAL mode, forcing PAL games to run at "
"60hz. <br>For most games which "
"have a speed tied to the framerate, this will result in the game running "
"approximately 17% faster. <br>For variable "
"frame rate games, it may not affect the speed."));
dialog->registerWidgetHelp(
m_ui.linearTextureFiltering, tr("Bilinear Texture Filtering"), tr("Unchecked"),
tr("Smooths out the blockyness of magnified textures on 3D object by using bilinear filtering. <br>Will have a "
@ -128,7 +132,8 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
m_ui.widescreenHack, tr("Widescreen Hack"), tr("Unchecked"),
tr("Scales vertex positions in screen-space to a widescreen aspect ratio, essentially "
"increasing the field of view from 4:3 to 16:9 in 3D games. <br>For 2D games, or games which "
"use pre-rendered backgrounds, this enhancement will not work as expected. <br><b><u>May not be compatible with all games.</u></b>"));
"use pre-rendered backgrounds, this enhancement will not work as expected. <br><b><u>May not be compatible with "
"all games.</u></b>"));
dialog->registerWidgetHelp(
m_ui.pgxpEnable, tr("Geometry Correction"), tr("Unchecked"),
tr("Reduces \"wobbly\" polygons and \"warping\" textures that are common in PS1 games. <br>Only "
@ -178,29 +183,7 @@ void GPUSettingsWidget::setupAdditionalUi()
qApp->translate("DisplayCropMode", Settings::GetDisplayCropModeDisplayName(static_cast<DisplayCropMode>(i))));
}
std::array<QString, GPU::MAX_RESOLUTION_SCALE + 1> resolution_suffixes = {{
QString(), // auto
QString(), // 1x
QString(), // 2x
tr(" (for 720p)"), // 3x
QString(), // 4x
tr(" (for 1080p)"), // 5x
tr(" (for 1440p)"), // 6x
QString(), // 7x
QString(), // 8x
tr(" (for 4K)"), // 9x
QString(), // 10x
QString(), // 11x
QString(), // 12x
QString(), // 13x
QString(), // 14x
QString(), // 15x
QString() // 16x
}};
m_ui.resolutionScale->addItem(tr("Automatic based on window size"));
for (u32 i = 1; i <= GPU::MAX_RESOLUTION_SCALE; i++)
m_ui.resolutionScale->addItem(tr("%1x%2").arg(i).arg(resolution_suffixes[i]));
QtUtils::FillComboBoxWithResolutionScales(m_ui.resolutionScale);
}
void GPUSettingsWidget::populateGPUAdapters()

View file

@ -1,8 +1,10 @@
#include "qtutils.h"
#include "common/byte_stream.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QMetaObject>
#include <QtGui/QDesktopServices>
#include <QtGui/QKeyEvent>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDialog>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QMainWindow>
@ -646,4 +648,25 @@ void OpenURL(QWidget* parent, const char* url)
return OpenURL(parent, QUrl::fromEncoded(QByteArray(url, static_cast<int>(std::strlen(url)))));
}
void FillComboBoxWithResolutionScales(QComboBox* cb)
{
cb->addItem(qApp->translate("GPUSettingsWidget", "Automatic based on window size"));
cb->addItem(qApp->translate("GPUSettingsWidget", "1x"));
cb->addItem(qApp->translate("GPUSettingsWidget", "2x"));
cb->addItem(qApp->translate("GPUSettingsWidget", "3x (for 720p)"));
cb->addItem(qApp->translate("GPUSettingsWidget", "4x"));
cb->addItem(qApp->translate("GPUSettingsWidget", "5x (for 1080p)"));
cb->addItem(qApp->translate("GPUSettingsWidget", "6x (for 1440p)"));
cb->addItem(qApp->translate("GPUSettingsWidget", "7x"));
cb->addItem(qApp->translate("GPUSettingsWidget", "8x"));
cb->addItem(qApp->translate("GPUSettingsWidget", "9x"));
cb->addItem(qApp->translate("GPUSettingsWidget", "10x"));
cb->addItem(qApp->translate("GPUSettingsWidget", "11x"));
cb->addItem(qApp->translate("GPUSettingsWidget", "12x"));
cb->addItem(qApp->translate("GPUSettingsWidget", "13x"));
cb->addItem(qApp->translate("GPUSettingsWidget", "14x"));
cb->addItem(qApp->translate("GPUSettingsWidget", "15x"));
cb->addItem(qApp->translate("GPUSettingsWidget", "16x"));
}
} // namespace QtUtils

View file

@ -9,6 +9,7 @@ Q_DECLARE_METATYPE(std::optional<bool>);
class ByteStream;
class QComboBox;
class QFrame;
class QKeyEvent;
class QTableView;
@ -54,4 +55,7 @@ void OpenURL(QWidget* parent, const QUrl& qurl);
/// Opens a URL string with the default handler.
void OpenURL(QWidget* parent, const char* url);
/// Fills a combo box with resolution scale options.
void FillComboBoxWithResolutionScales(QComboBox* cb);
} // namespace QtUtils

View file

@ -112,7 +112,7 @@ private:
enum : u32
{
GAME_LIST_CACHE_SIGNATURE = 0x45434C47,
GAME_LIST_CACHE_VERSION = 7
GAME_LIST_CACHE_VERSION = 8
};
using DatabaseMap = std::unordered_map<std::string, GameListDatabaseEntry>;

View file

@ -70,6 +70,19 @@ bool ReadOptionalFromStream(ByteStream* stream, std::optional<T>* dest)
return true;
}
static bool ReadStringFromStream(ByteStream* stream, std::string* dest)
{
u32 size;
if (!stream->Read2(&size, sizeof(size)))
return false;
dest->resize(size);
if (!stream->Read2(dest->data(), size))
return false;
return true;
}
template<typename T>
bool WriteOptionalToStream(ByteStream* stream, const std::optional<T>& src)
{
@ -83,6 +96,12 @@ bool WriteOptionalToStream(ByteStream* stream, const std::optional<T>& src)
return stream->Write2(&src.value(), sizeof(T));
}
static bool WriteStringToStream(ByteStream* stream, const std::string& str)
{
const u32 size = static_cast<u32>(str.size());
return (stream->Write2(&size, sizeof(size)) && (size == 0 || stream->Write2(str.data(), size)));
}
bool Entry::LoadFromStream(ByteStream* stream)
{
constexpr u32 num_bytes = (static_cast<u32>(Trait::Count) + 7) / 8;
@ -91,8 +110,17 @@ bool Entry::LoadFromStream(ByteStream* stream)
if (!stream->Read2(bits.data(), num_bytes) || !ReadOptionalFromStream(stream, &display_active_start_offset) ||
!ReadOptionalFromStream(stream, &display_active_end_offset) ||
!ReadOptionalFromStream(stream, &display_crop_mode) || !ReadOptionalFromStream(stream, &display_aspect_ratio) ||
!ReadOptionalFromStream(stream, &display_linear_upscaling) ||
!ReadOptionalFromStream(stream, &display_integer_upscaling) ||
!ReadOptionalFromStream(stream, &gpu_resolution_scale) || !ReadOptionalFromStream(stream, &gpu_true_color) ||
!ReadOptionalFromStream(stream, &gpu_scaled_dithering) ||
!ReadOptionalFromStream(stream, &gpu_force_ntsc_timings) ||
!ReadOptionalFromStream(stream, &gpu_bilinear_texture_filtering) ||
!ReadOptionalFromStream(stream, &gpu_widescreen_hack) || !ReadOptionalFromStream(stream, &gpu_pgxp) ||
!ReadOptionalFromStream(stream, &controller_1_type) || !ReadOptionalFromStream(stream, &controller_2_type) ||
!ReadOptionalFromStream(stream, &gpu_widescreen_hack))
!ReadOptionalFromStream(stream, &memory_card_1_type) || !ReadOptionalFromStream(stream, &memory_card_2_type) ||
!ReadStringFromStream(stream, &memory_card_1_shared_path) ||
!ReadStringFromStream(stream, &memory_card_2_shared_path))
{
return false;
}
@ -120,8 +148,17 @@ bool Entry::SaveToStream(ByteStream* stream) const
return stream->Write2(bits.data(), num_bytes) && WriteOptionalToStream(stream, display_active_start_offset) &&
WriteOptionalToStream(stream, display_active_end_offset) && WriteOptionalToStream(stream, display_crop_mode) &&
WriteOptionalToStream(stream, display_aspect_ratio) && WriteOptionalToStream(stream, controller_1_type) &&
WriteOptionalToStream(stream, controller_2_type) && WriteOptionalToStream(stream, gpu_widescreen_hack);
WriteOptionalToStream(stream, display_linear_upscaling) &&
WriteOptionalToStream(stream, display_integer_upscaling) &&
WriteOptionalToStream(stream, display_aspect_ratio) && WriteOptionalToStream(stream, gpu_resolution_scale) &&
WriteOptionalToStream(stream, gpu_true_color) && WriteOptionalToStream(stream, gpu_scaled_dithering) &&
WriteOptionalToStream(stream, gpu_force_ntsc_timings) &&
WriteOptionalToStream(stream, gpu_bilinear_texture_filtering) &&
WriteOptionalToStream(stream, gpu_widescreen_hack) && WriteOptionalToStream(stream, gpu_pgxp) &&
WriteOptionalToStream(stream, controller_1_type) && WriteOptionalToStream(stream, controller_2_type) &&
WriteOptionalToStream(stream, memory_card_1_type) && WriteOptionalToStream(stream, memory_card_2_type) &&
WriteStringToStream(stream, memory_card_1_shared_path) &&
WriteStringToStream(stream, memory_card_2_shared_path);
}
static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA& ini)
@ -145,6 +182,34 @@ static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA
cvalue = ini.GetValue(section, "DisplayAspectRatio", nullptr);
if (cvalue)
entry->display_aspect_ratio = Settings::ParseDisplayAspectRatio(cvalue);
cvalue = ini.GetValue(section, "DisplayLinearUpscaling", nullptr);
if (cvalue)
entry->display_linear_upscaling = StringUtil::FromChars<bool>(cvalue);
cvalue = ini.GetValue(section, "DisplayIntegerUpscaling", nullptr);
if (cvalue)
entry->display_integer_upscaling = StringUtil::FromChars<bool>(cvalue);
cvalue = ini.GetValue(section, "GPUResolutionScale", nullptr);
if (cvalue)
entry->gpu_resolution_scale = StringUtil::FromChars<u32>(cvalue);
cvalue = ini.GetValue(section, "GPUTrueColor", nullptr);
if (cvalue)
entry->gpu_true_color = StringUtil::FromChars<bool>(cvalue);
cvalue = ini.GetValue(section, "GPUScaledDithering", nullptr);
if (cvalue)
entry->gpu_scaled_dithering = StringUtil::FromChars<bool>(cvalue);
cvalue = ini.GetValue(section, "GPUBilinearTextureFiltering", nullptr);
if (cvalue)
entry->gpu_bilinear_texture_filtering = StringUtil::FromChars<bool>(cvalue);
cvalue = ini.GetValue(section, "GPUForceNTSCTimings", nullptr);
if (cvalue)
entry->gpu_force_ntsc_timings = StringUtil::FromChars<bool>(cvalue);
cvalue = ini.GetValue(section, "GPUWidescreenHack", nullptr);
if (cvalue)
entry->gpu_widescreen_hack = StringUtil::FromChars<bool>(cvalue);
cvalue = ini.GetValue(section, "GPUPGXP", nullptr);
if (cvalue)
entry->gpu_pgxp = StringUtil::FromChars<bool>(cvalue);
cvalue = ini.GetValue(section, "Controller1Type", nullptr);
if (cvalue)
@ -153,9 +218,18 @@ static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA
if (cvalue)
entry->controller_2_type = Settings::ParseControllerTypeName(cvalue);
cvalue = ini.GetValue(section, "GPUWidescreenHack", nullptr);
cvalue = ini.GetValue(section, "MemoryCard1Type", nullptr);
if (cvalue)
entry->gpu_widescreen_hack = StringUtil::FromChars<bool>(cvalue);
entry->memory_card_1_type = Settings::ParseMemoryCardTypeName(cvalue);
cvalue = ini.GetValue(section, "MemoryCard2Type", nullptr);
if (cvalue)
entry->memory_card_2_type = Settings::ParseMemoryCardTypeName(cvalue);
cvalue = ini.GetValue(section, "MemoryCard1SharedPath");
if (cvalue)
entry->memory_card_1_shared_path = cvalue;
cvalue = ini.GetValue(section, "MemoryCard2SharedPath");
if (cvalue)
entry->memory_card_2_shared_path = cvalue;
}
static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA& ini)
@ -179,14 +253,46 @@ static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA
ini.SetValue(section, "DisplayAspectRatio",
Settings::GetDisplayAspectRatioName(entry.display_aspect_ratio.value()));
}
if (entry.display_linear_upscaling.has_value())
ini.SetValue(section, "DisplayLinearUpscaling", entry.display_linear_upscaling.value() ? "true" : "false");
if (entry.display_integer_upscaling.has_value())
ini.SetValue(section, "DisplayIntegerUpscaling", entry.display_integer_upscaling.value() ? "true" : "false");
if (entry.gpu_resolution_scale.has_value())
ini.SetLongValue(section, "GPUResolutionScale", static_cast<s32>(entry.gpu_resolution_scale.value()));
if (entry.gpu_true_color.has_value())
ini.SetValue(section, "GPUTrueColor", entry.gpu_true_color.value() ? "true" : "false");
if (entry.gpu_scaled_dithering.has_value())
ini.SetValue(section, "GPUScaledDithering", entry.gpu_scaled_dithering.value() ? "true" : "false");
if (entry.gpu_bilinear_texture_filtering.has_value())
{
ini.SetValue(section, "GPUBilinearTextureFiltering",
entry.gpu_bilinear_texture_filtering.value() ? "true" : "false");
}
if (entry.gpu_force_ntsc_timings.has_value())
ini.SetValue(section, "GPUForceNTSCTimings", entry.gpu_force_ntsc_timings.value() ? "true" : "false");
if (entry.gpu_widescreen_hack.has_value())
ini.SetValue(section, "GPUWidescreenHack", entry.gpu_widescreen_hack.value() ? "true" : "false");
if (entry.gpu_pgxp.has_value())
ini.SetValue(section, "GPUPGXP", entry.gpu_pgxp.value() ? "true" : "false");
if (entry.controller_1_type.has_value())
ini.SetValue(section, "Controller1Type", Settings::GetControllerTypeName(entry.controller_1_type.value()));
if (entry.controller_2_type.has_value())
ini.SetValue(section, "Controller2Type", Settings::GetControllerTypeName(entry.controller_2_type.value()));
if (entry.gpu_widescreen_hack.has_value())
ini.SetValue(section, "GPUWidescreenHack", entry.gpu_widescreen_hack.value() ? "true" : "false");
if (entry.controller_1_type.has_value())
ini.SetValue(section, "Controller1Type", Settings::GetControllerTypeName(entry.controller_1_type.value()));
if (entry.controller_2_type.has_value())
ini.SetValue(section, "Controller2Type", Settings::GetControllerTypeName(entry.controller_2_type.value()));
if (entry.memory_card_1_type.has_value())
ini.SetValue(section, "MemoryCard1Type", Settings::GetMemoryCardTypeName(entry.memory_card_1_type.value()));
if (entry.memory_card_2_type.has_value())
ini.SetValue(section, "MemoryCard2Type", Settings::GetMemoryCardTypeName(entry.memory_card_2_type.value()));
if (!entry.memory_card_1_shared_path.empty())
ini.SetValue(section, "MemoryCard1SharedPath", entry.memory_card_1_shared_path.c_str());
if (!entry.memory_card_2_shared_path.empty())
ini.SetValue(section, "MemoryCard2SharedPath", entry.memory_card_2_shared_path.c_str());
}
Database::Database() = default;
@ -297,12 +403,39 @@ void Entry::ApplySettings(bool display_osd_messages) const
g_settings.display_crop_mode = display_crop_mode.value();
if (display_aspect_ratio.has_value())
g_settings.display_aspect_ratio = display_aspect_ratio.value();
if (display_linear_upscaling.has_value())
g_settings.display_linear_filtering = display_linear_upscaling.value();
if (display_integer_upscaling.has_value())
g_settings.display_integer_scaling = display_integer_upscaling.value();
if (gpu_resolution_scale.has_value())
g_settings.gpu_resolution_scale = gpu_resolution_scale.value();
if (gpu_true_color.has_value())
g_settings.gpu_true_color = gpu_true_color.value();
if (gpu_scaled_dithering.has_value())
g_settings.gpu_scaled_dithering = gpu_scaled_dithering.value();
if (gpu_force_ntsc_timings.has_value())
g_settings.gpu_force_ntsc_timings = gpu_force_ntsc_timings.value();
if (gpu_bilinear_texture_filtering)
g_settings.gpu_texture_filtering = gpu_bilinear_texture_filtering.value();
if (gpu_widescreen_hack.has_value())
g_settings.gpu_widescreen_hack = gpu_widescreen_hack.value();
if (gpu_pgxp.has_value())
g_settings.gpu_pgxp_enable = gpu_pgxp.value();
if (controller_1_type.has_value())
g_settings.controller_types[0] = controller_1_type.value();
if (controller_2_type.has_value())
g_settings.controller_types[1] = controller_2_type.value();
if (gpu_widescreen_hack.has_value())
g_settings.gpu_widescreen_hack = gpu_widescreen_hack.value();
if (memory_card_1_type.has_value())
g_settings.memory_card_types[0] = memory_card_1_type.value();
if (!memory_card_1_shared_path.empty())
g_settings.memory_card_paths[0] = memory_card_1_shared_path;
if (memory_card_2_type.has_value())
g_settings.memory_card_types[1] = memory_card_2_type.value();
if (!memory_card_1_shared_path.empty())
g_settings.memory_card_paths[1] = memory_card_2_shared_path;
if (HasTrait(Trait::ForceInterpreter))
{

View file

@ -40,9 +40,21 @@ struct Entry
// user settings
std::optional<DisplayCropMode> display_crop_mode;
std::optional<DisplayAspectRatio> display_aspect_ratio;
std::optional<bool> display_linear_upscaling;
std::optional<bool> display_integer_upscaling;
std::optional<u32> gpu_resolution_scale;
std::optional<bool> gpu_true_color;
std::optional<bool> gpu_scaled_dithering;
std::optional<bool> gpu_force_ntsc_timings;
std::optional<bool> gpu_bilinear_texture_filtering;
std::optional<bool> gpu_widescreen_hack;
std::optional<bool> gpu_pgxp;
std::optional<ControllerType> controller_1_type;
std::optional<ControllerType> controller_2_type;
std::optional<bool> gpu_widescreen_hack;
std::optional<MemoryCardType> memory_card_1_type;
std::optional<MemoryCardType> memory_card_2_type;
std::string memory_card_1_shared_path;
std::string memory_card_2_shared_path;
ALWAYS_INLINE bool HasTrait(Trait trait) const { return traits[static_cast<int>(trait)]; }
ALWAYS_INLINE void AddTrait(Trait trait) { traits[static_cast<int>(trait)] = true; }