From 864e7f0c9179ba6e257c7c38022e09708acdfb71 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Fri, 30 Apr 2021 02:48:48 +1000 Subject: [PATCH] GameSettings: Add custom aspect ratios --- NEWS.md | 1 + README.md | 1 + src/duckstation-qt/gamepropertiesdialog.cpp | 39 ++++++++++++++ src/duckstation-qt/gamepropertiesdialog.h | 1 + src/duckstation-qt/gamepropertiesdialog.ui | 35 ++++++++++++- src/frontend-common/game_list.h | 2 +- src/frontend-common/game_settings.cpp | 58 +++++++++++++++++++++ src/frontend-common/game_settings.h | 2 + 8 files changed, 136 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 9be0be5fd..69aeb5423 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,4 @@ +- 2021/04/29: Custom aspect ratio support added. - 2021/03/20: Memory card editor added to Android app. - 2021/03/17: Add support for loading **homebrew** PBP images. PSN images are not loadable due to potential legal issues surrounding the encryption. - 2021/03/14: Multiple controllers, multitap, and external controller vibration added to Android app. You will need to rebind your controllers. diff --git a/README.md b/README.md index 200cfad98..74924dd41 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ A "BIOS" ROM image is required to to start the emulator and to play games. You c ## Latest News Older entries are available at https://github.com/stenzek/duckstation/blob/master/NEWS.md +- 2021/04/29: Custom aspect ratio support added. - 2021/03/20: Memory card editor added to Android app. - 2021/03/17: Add support for loading **homebrew** PBP images. PSN images are not loadable due to potential legal issues surrounding the encryption. - 2021/03/14: Multiple controllers, multitap, and external controller vibration added to Android app. You will need to rebind your controllers. diff --git a/src/duckstation-qt/gamepropertiesdialog.cpp b/src/duckstation-qt/gamepropertiesdialog.cpp index 8eb56bec0..14d1bbbe9 100644 --- a/src/duckstation-qt/gamepropertiesdialog.cpp +++ b/src/duckstation-qt/gamepropertiesdialog.cpp @@ -368,11 +368,25 @@ void GamePropertiesDialog::populateGameSettings() QSignalBlocker sb(m_ui.userCropMode); m_ui.userCropMode->setCurrentIndex(static_cast(gs.display_crop_mode.value()) + 1); } + if (gs.display_aspect_ratio.has_value()) { QSignalBlocker sb(m_ui.userAspectRatio); m_ui.userAspectRatio->setCurrentIndex(static_cast(gs.display_aspect_ratio.value()) + 1); } + if (gs.display_aspect_ratio_custom_numerator.has_value()) + { + QSignalBlocker sb(m_ui.userCustomAspectRatioNumerator); + m_ui.userCustomAspectRatioNumerator->setValue(static_cast(gs.display_aspect_ratio_custom_numerator.value())); + } + if (gs.display_aspect_ratio_custom_denominator.has_value()) + { + QSignalBlocker sb(m_ui.userCustomAspectRatioDenominator); + m_ui.userCustomAspectRatioDenominator->setValue( + static_cast(gs.display_aspect_ratio_custom_denominator.value())); + } + onUserAspectRatioChanged(); + if (gs.gpu_downsample_mode.has_value()) { QSignalBlocker sb(m_ui.userDownsampleMode); @@ -566,6 +580,21 @@ void GamePropertiesDialog::connectUi() else m_game_settings.display_aspect_ratio = static_cast(index - 1); saveGameSettings(); + onUserAspectRatioChanged(); + }); + connect(m_ui.userCustomAspectRatioNumerator, QOverload::of(&QSpinBox::valueChanged), [this](int value) { + if (value <= 0) + m_game_settings.display_aspect_ratio_custom_numerator.reset(); + else + m_game_settings.display_aspect_ratio_custom_numerator = static_cast(value); + saveGameSettings(); + }); + connect(m_ui.userCustomAspectRatioDenominator, QOverload::of(&QSpinBox::valueChanged), [this](int value) { + if (value <= 0) + m_game_settings.display_aspect_ratio_custom_denominator.reset(); + else + m_game_settings.display_aspect_ratio_custom_denominator = static_cast(value); + saveGameSettings(); }); connect(m_ui.userCropMode, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { @@ -795,6 +824,16 @@ void GamePropertiesDialog::updateCPUClockSpeedLabel() m_ui.userCPUClockSpeedLabel->setText(tr("%1% (%2MHz)").arg(percent).arg(frequency / 1000000.0, 0, 'f', 2)); } +void GamePropertiesDialog::onUserAspectRatioChanged() +{ + const int index = m_ui.userAspectRatio->currentIndex(); + const bool is_custom = (index > 0 && static_cast(index - 1) == DisplayAspectRatio::Custom); + + m_ui.userCustomAspectRatioNumerator->setVisible(is_custom); + m_ui.userCustomAspectRatioDenominator->setVisible(is_custom); + m_ui.userCustomAspectRatioSeparator->setVisible(is_custom); +} + void GamePropertiesDialog::fillEntryFromUi(GameListCompatibilityEntry* entry) { entry->code = m_game_code; diff --git a/src/duckstation-qt/gamepropertiesdialog.h b/src/duckstation-qt/gamepropertiesdialog.h index d3fde36ac..d42ebfd85 100644 --- a/src/duckstation-qt/gamepropertiesdialog.h +++ b/src/duckstation-qt/gamepropertiesdialog.h @@ -50,6 +50,7 @@ private: void fillEntryFromUi(GameListCompatibilityEntry* entry); void computeTrackHashes(); void onResize(); + void onUserAspectRatioChanged(); Ui::GamePropertiesDialog m_ui; std::array(GameSettings::Trait::Count)> m_trait_checkboxes{}; diff --git a/src/duckstation-qt/gamepropertiesdialog.ui b/src/duckstation-qt/gamepropertiesdialog.ui index c37a4d4a6..ee8f3db04 100644 --- a/src/duckstation-qt/gamepropertiesdialog.ui +++ b/src/duckstation-qt/gamepropertiesdialog.ui @@ -7,7 +7,7 @@ 0 0 793 - 619 + 651 @@ -269,7 +269,38 @@ - + + + + + + + + 0 + + + 9999 + + + + + + + : + + + + + + + 0 + + + 9999 + + + + diff --git a/src/frontend-common/game_list.h b/src/frontend-common/game_list.h index 8d9e43e1d..caab3c9de 100644 --- a/src/frontend-common/game_list.h +++ b/src/frontend-common/game_list.h @@ -129,7 +129,7 @@ private: enum : u32 { GAME_LIST_CACHE_SIGNATURE = 0x45434C47, - GAME_LIST_CACHE_VERSION = 27 + GAME_LIST_CACHE_VERSION = 28 }; using CacheMap = std::unordered_map; diff --git a/src/frontend-common/game_settings.cpp b/src/frontend-common/game_settings.cpp index 2d2e35b49..91d3b1fe8 100644 --- a/src/frontend-common/game_settings.cpp +++ b/src/frontend-common/game_settings.cpp @@ -122,6 +122,8 @@ bool Entry::LoadFromStream(ByteStream* stream) !ReadOptionalFromStream(stream, &display_linear_upscaling) || !ReadOptionalFromStream(stream, &display_integer_upscaling) || !ReadOptionalFromStream(stream, &display_force_4_3_for_24bit) || + !ReadOptionalFromStream(stream, &display_aspect_ratio_custom_numerator) || + !ReadOptionalFromStream(stream, &display_aspect_ratio_custom_denominator) || !ReadOptionalFromStream(stream, &gpu_resolution_scale) || !ReadOptionalFromStream(stream, &gpu_multisamples) || !ReadOptionalFromStream(stream, &gpu_per_sample_shading) || !ReadOptionalFromStream(stream, &gpu_true_color) || !ReadOptionalFromStream(stream, &gpu_scaled_dithering) || @@ -173,6 +175,8 @@ bool Entry::SaveToStream(ByteStream* stream) const WriteOptionalToStream(stream, display_linear_upscaling) && WriteOptionalToStream(stream, display_integer_upscaling) && WriteOptionalToStream(stream, display_force_4_3_for_24bit) && + WriteOptionalToStream(stream, display_aspect_ratio_custom_numerator) && + WriteOptionalToStream(stream, display_aspect_ratio_custom_denominator) && WriteOptionalToStream(stream, gpu_resolution_scale) && WriteOptionalToStream(stream, gpu_multisamples) && WriteOptionalToStream(stream, gpu_per_sample_shading) && WriteOptionalToStream(stream, gpu_true_color) && WriteOptionalToStream(stream, gpu_scaled_dithering) && WriteOptionalToStream(stream, gpu_force_ntsc_timings) && @@ -248,6 +252,18 @@ 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); + lvalue = ini.GetLongValue(section, "CustomAspectRatioNumerator", 0); + if (lvalue != 0) + { + entry->display_aspect_ratio_custom_numerator = + static_cast(std::clamp(lvalue, 1, std::numeric_limits::max())); + } + lvalue = ini.GetLongValue(section, "CustomAspectRatioDenominator", 0); + if (lvalue != 0) + { + entry->display_aspect_ratio_custom_denominator = + static_cast(std::clamp(lvalue, 1, std::numeric_limits::max())); + } cvalue = ini.GetValue(section, "GPUDownsampleMode", nullptr); if (cvalue) entry->gpu_downsample_mode = Settings::ParseDownsampleModeName(cvalue); @@ -371,6 +387,16 @@ static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA ini.SetValue(section, "DisplayAspectRatio", Settings::GetDisplayAspectRatioName(entry.display_aspect_ratio.value())); } + if (entry.display_aspect_ratio_custom_numerator.has_value()) + { + ini.SetLongValue(section, "CustomAspectRatioNumerator", + static_cast(entry.display_aspect_ratio_custom_numerator.value())); + } + if (entry.display_aspect_ratio_custom_denominator.has_value()) + { + ini.SetLongValue(section, "CustomAspectRatioDenominator", + static_cast(entry.display_aspect_ratio_custom_denominator.value())); + } if (entry.gpu_downsample_mode.has_value()) { ini.SetValue(section, "GPUDownsampleMode", Settings::GetDownsampleModeName(entry.gpu_downsample_mode.value())); @@ -499,6 +525,20 @@ static std::optional GetEntryValueForKey(const Entry& entry, const else return Settings::GetDisplayAspectRatioName(entry.display_aspect_ratio.value()); } + else if (key == "CustomAspectRatioNumerator") + { + if (!entry.display_aspect_ratio_custom_numerator.has_value()) + return std::nullopt; + else + return std::to_string(entry.display_aspect_ratio_custom_numerator.value()); + } + else if (key == "CustomAspectRatioDenominator") + { + if (!entry.display_aspect_ratio_custom_denominator.has_value()) + return std::nullopt; + else + return std::to_string(entry.display_aspect_ratio_custom_denominator.value()); + } else if (key == "GPUDownsampleMode") { if (!entry.gpu_downsample_mode.has_value()) @@ -713,6 +753,20 @@ static void SetEntryValueForKey(Entry& entry, const std::string_view& key, const else entry.display_aspect_ratio = Settings::ParseDisplayAspectRatio(value->c_str()); } + else if (key == "CustomAspectRatioNumerator") + { + if (!value.has_value()) + entry.display_aspect_ratio_custom_numerator.reset(); + else + entry.display_aspect_ratio_custom_numerator = StringUtil::FromChars(value.value()); + } + else if (key == "CustomAspectRatioDenominator") + { + if (!value.has_value()) + entry.display_aspect_ratio_custom_denominator.reset(); + else + entry.display_aspect_ratio_custom_denominator = StringUtil::FromChars(value.value()); + } else if (key == "GPUDownsampleMode") { if (!value.has_value()) @@ -1031,6 +1085,10 @@ 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_aspect_ratio_custom_numerator.has_value()) + g_settings.display_aspect_ratio_custom_numerator = display_aspect_ratio_custom_numerator.value(); + if (display_aspect_ratio_custom_denominator.has_value()) + g_settings.display_aspect_ratio_custom_denominator = display_aspect_ratio_custom_denominator.value(); if (gpu_downsample_mode.has_value()) g_settings.gpu_downsample_mode = gpu_downsample_mode.value(); if (display_linear_upscaling.has_value()) diff --git a/src/frontend-common/game_settings.h b/src/frontend-common/game_settings.h index bf7e946dc..3995a072a 100644 --- a/src/frontend-common/game_settings.h +++ b/src/frontend-common/game_settings.h @@ -61,6 +61,8 @@ struct Entry std::optional display_linear_upscaling; std::optional display_integer_upscaling; std::optional display_force_4_3_for_24bit; + std::optional display_aspect_ratio_custom_numerator; + std::optional display_aspect_ratio_custom_denominator; std::optional gpu_resolution_scale; std::optional gpu_multisamples; std::optional gpu_per_sample_shading;