Compare commits

..

54 Commits

Author SHA1 Message Date
Jordan Woyak 0938fca6e3 Core/VideoCommon: Fix some weird (!eof) logic. 2024-12-30 20:13:59 -06:00
Tilka 05cad38abc
Merge pull request #13243 from nlebeck/stringutil-tests
Add some unit test coverage of the `SplitPath` function
2024-12-27 18:10:47 +01:00
JMC47 532a8621da
Merge pull request #13116 from mitaclaw/ranges-modernization-8-trivial-of
Ranges Algorithms Modernization - Of
2024-12-26 16:51:53 -05:00
Niel Lebeck bae4616dd1 Add some unit test coverage of the SplitPath function 2024-12-26 11:42:01 -08:00
JMC47 f9ce2b9d76
Merge pull request #13232 from TryTwo/PR_hotfix
Qt crash hotfix
2024-12-23 18:04:51 -05:00
TryTwo 27ac5fa777 Qt crash fix. Don't store Config::Info variable as a reference. 2024-12-23 15:40:07 -07:00
JMC47 c528a70e64
Merge pull request #13211 from Sintendo/blendvpd
Jit_FloatingPoint: fselx - Prefer BLENDVPD over VBLENDVPD
2024-12-22 18:35:11 -05:00
JMC47 a1d6aa7d3e
Merge pull request #13212 from JosJuice/jitarm64-ps-sel-same-reg
JitArm64: Optimize ps_sel with d == b || d == c
2024-12-22 18:34:32 -05:00
JosJuice 5641b83d4e
Merge pull request #13063 from TryTwo/PR_GameSettings
Add ability to edit game-specific GFX settings from game properties tab.
2024-12-22 20:42:28 +01:00
Admiral H. Curtiss bb8c0a795f
Merge pull request #13214 from JosJuice/sethardcoremode-private
AchievementManager: Make SetHardcoreMode private
2024-12-22 20:09:29 +01:00
JMC47 1ba8541da9
Merge pull request #13091 from mitaclaw/ranges-modernization-2-returns
Ranges Algorithms Modernization - Return
2024-12-20 12:50:19 -05:00
JMC47 ac0d6cbaaa
Merge pull request #13224 from Sintendo/jitarm64-subfic2
JitArm64_Integer: Optimize subfic for -1
2024-12-18 12:07:23 -05:00
OatmealDome 01f6810a9d
Merge pull request #13207 from OatmealDome/vulkan-hdr-color-space
VKSwapChain: Always use surface formats with a normal sRGB color space if not RGBA16F
2024-12-16 17:29:19 -05:00
mitaclaw 2b0cd16c8c Modernize `std::none_of` with ranges
In JitRegCache.cpp, the lambda predicate were replaced by a pointer to member function because ranges algorithms are able to invoke those.

In ConvertDialog.cpp, the `std::mem_fn` helper was removed because ranges algorithms are able to handle pointers to member functions as predicates.

In BoundingBox.cpp, the lambda predicate was returning the bool element unchanged, so `std::identity` was a better fit.
2024-12-15 19:54:17 -08:00
mitaclaw 140252ffc0 Modernize `std::any_of` with ranges
In WiimoteReal.cpp, JitRegCache.cpp, lambda predicates were replaced by pointers to member functions because ranges algorithms are able invoke those.

In ConvertDialog.cpp, the `std::mem_fn` helper was removed because ranges algorithms are able to handle pointers to member functions as predicates.
2024-12-15 19:54:16 -08:00
mitaclaw 860e6cf5cb Modernize `std::all_of` with ranges
In DITSpecification.cpp, MaterialAsset.cpp, and ShaderAsset.cpp, lambda predicates were replaced by pointers to member functions because ranges algorithms are able invoke those.

In NetPlayClient.cpp, the non-trivial `NetPlay::Player` elements were being passed by value in `NetPlayClient::DoAllPlayersHaveGame()`. This has been fixed.

In WIABlob.cpp, the second example's predicate was returning the `std::optional` by value instead of implicitly converting it to a bool. This has been fixed.
2024-12-15 19:50:34 -08:00
mitaclaw 826e2bbf98 StringUtil: More Wrappers For <cctype>
`Common::IsLower(char)` was omitted as nothing needed it.
2024-12-15 19:50:34 -08:00
Jordan Woyak b4a1967310
Merge pull request #13226 from JosJuice/achievementmanager-forward-declarations
AchievementManager: Add required forward declarations
2024-12-15 14:02:56 -06:00
JosJuice ad24ddb6bb VerifyTool: Add missing USE_RETRO_ACHIEVEMENTS ifdefs 2024-12-15 18:15:57 +01:00
JosJuice 84ab15e020 AchievementManager: Add required forward declarations
This was causing compilation errors when building without
USE_RETRO_ACHIEVEMENTS.
2024-12-15 18:00:14 +01:00
Sintendo d81213c4a5 JitArm64_Integer: Optimize subfic for -1
Another one backported from x86. Not sure why I didn't do this in #12891
already.

- Without carry
Before:
0x2a3a03fb   mvn    w27, w26
0x6b1a037b   subs   w27, w27, w26

After:
0x1280001b   mov    w27, #-0x1                ; =-1

- With carry
Before:
0x2a3b03f7   mvn    w23, w27
0x6b1b02f7   subs   w23, w23, w27
0x1a9f37f6   cset   w22, hs
0x390bd3b6   strb   w22, [x29, #0x2f4]

After:
0x12800017   mov    w23, #-0x1                ; =-1
2024-12-15 02:24:30 +01:00
JosJuice ada646a795
Merge pull request #12682 from JosJuice/jit-fallback-discard-assert
Jit: Skip discarded registers when flushing for interpreter fallback
2024-12-12 23:48:37 +01:00
TryTwo 9541bb6cf7 Add method to bold slider/spin labels when a user game ini setting is being used 2024-12-10 13:42:30 -07:00
TryTwo ac129d318b EnhancementsWidget:: Move to using ConfigControls and add new control for ComboBoxes that set two settings at once. 2024-12-10 13:42:17 -07:00
TryTwo 84a937ae65 Add GFX property tabs to game properties window, allowing them to be set to the user game ini. Additionally, refactor ConfigWidgets to reduce duplication. Refactor GameConfigWidget to use config system.
Creates a layer outside the game config layer system and passes it to the created gfx widows, so as to not interfere with the global config system.

Supports multiple game properties being open at once.
Supports editing while a game is playing, but the options only save and update the active game when the window is closed.
Right-clicking will remove a property from the game ini.
2024-12-10 13:40:30 -07:00
JMC47 6ea8edd531
Merge pull request #13209 from jordan-woyak/sdl-touchpad
InputCommon/SDL: Add touchpad inputs.
2024-12-10 12:52:43 -05:00
JMC47 394db8b798
Merge pull request #13215 from Tilka/efb_access
GameSettings: set EFBAccessEnable=True for Neighbours from Hell
2024-12-09 14:23:45 -05:00
Tillmann Karras 445fe2248c GameSettings: set EFBAccessEnable=True for Neighbours from Hell
This fixes the loading screens that show a walking animation.
2024-12-08 11:29:15 +00:00
JosJuice 687fe65709
Merge pull request #13166 from Sintendo/stX-opt
JitArm64_LoadStore: Optimize zero stores in stX
2024-12-08 11:06:33 +01:00
OatmealDome 57b1234feb
Merge pull request #13113 from CelestialAmber/mwld-map
Core: Store object name separately for symbols
2024-12-07 17:13:13 -05:00
JosJuice 7a31c8f10b AchievementManager: Make SetHardcoreMode private
This is an implementation detail that doesn't need to be exposed.
2024-12-07 19:29:53 +01:00
TryTwo 08df9a66e0 DolphinQt: Refactor, add ConfigControl class
This reduces code duplication in the different ConfigControls. This is
helpful for the next commit, which will modify the now deduplicated
code.
2024-12-07 16:31:34 +01:00
JosJuice e3bfff5cb6 JitArm64: Optimize ps_sel with d == b || d == c 2024-12-07 12:20:24 +01:00
Sintendo 065165f749 Jit_FloatingPoint: Prefer BLENDVPD over VBLENDVPD
Prefer BLENDVPD over VBLENDVPD if the latter doesn't save any
instructions.

VBLENDVPD allows separate source and destination registers, which can
eliminate a MOVAPD/MOVSD. However, on Intel since Skylake, VBLENDVPD
takes additional uops to execute compared to BLENDVPD (according to
https://uops.info). On AMD and older Intel microarchitectures there is no
difference.
2024-12-07 11:22:28 +01:00
JosJuice 0a84d93a8e
Merge pull request #13196 from jordan-woyak/expanding-numeric-settings
DolphinQt: Make mapping window spinboxes horizontally expanding.
2024-12-07 11:14:55 +01:00
OatmealDome a5d1172d97
Merge pull request #13186 from OatmealDome/flatpak-gitignore
gitignore: Ignore flatpak-builder's cache directory
2024-12-05 15:10:18 -05:00
JosJuice c6b0629275
Merge pull request #13200 from Dentomologist/irwidget_move_header_constants_into_class
IRWidget: Move header constants into class
2024-12-05 18:53:17 +01:00
Jordan Woyak ad1511982a InputCommon/SDL: Add touchpad inputs. 2024-12-04 21:08:18 -06:00
JMC47 8c3b9c9cf6
Merge pull request #13174 from jordan-woyak/sdl-battery
ControllerInterface/SDL: Add Battery Input.
2024-12-04 21:46:07 -05:00
JMC47 38675eab9b
Merge pull request #13206 from mbc07/gameini/eternal-darkness-dualcore
GameINI: drop Single Core override from Eternal Darkness
2024-12-04 21:45:41 -05:00
OatmealDome e6f335bfcf VKSwapChain: Always use surface formats with a normal sRGB color space if not RGBA16F
Co-authored-by: TellowKrinkle <tellowkrinkle@gmail.com>
2024-12-03 20:49:50 -05:00
Mateus B. Cassiano 7e4b1780e1
GameINI: drop Single Core override from Eternal Darkness 2024-12-03 16:48:54 -04:00
Dentomologist a9b1c1f5f8 IRWidget: Move header constants into class
This apparently didn't compile on macOS six years ago before c++20, but
it should be fine by now.

While I'm at it, make the constants upper case per convention.
2024-11-26 13:50:13 -08:00
Jordan Woyak ea93b65d21 DolphinQt: Make mapping window spinboxes horizontally expanding. 2024-11-24 18:59:18 -06:00
Amber Brault 1c4bfc35d9 Core: Store object name separately for symbols 2024-11-11 12:36:53 -05:00
OatmealDome 704c75a2f5 gitignore: Ignore flatpak-builder's cache directory 2024-11-11 12:17:53 -05:00
Jordan Woyak 83ed817ad2 ControllerInterface/SDL: Add Battery Input. 2024-11-04 22:24:42 -06:00
Sintendo a307d9d9b8 JitArm64_LoadStore: Optimize zero stores in stX
The value being stored must be loaded into a register. In the case of an
immediate value, this means it must be materialized. The value is
eventually byteswapped before performing the store.

This can be simplified for the value 0 for two reasons:
- ARM64 has a dedicated zero register, so does not need to be
  materialized.
- Byteswapping zero is still zero, so we can skip this step.

We could skip byteswapping for other values by immediately materializing
the byteswapped value in a register, but the benefits are not so clear
there (if the value needs to be materialized anyway, it is better to do
it up front).

Before:
0x5280001b   mov    w27, #0x0                 ; =0
0xb9404fba   ldr    w26, [x29, #0x4c]
0x12881862   mov    w2, #-0x40c4              ; =-16580
0x0b020342   add    w2, w26, w2
0x5ac00b61   rev    w1, w27
0xb8226b81   str    w1, [x28, x2]

After:
0xb9404fbb   ldr    w27, [x29, #0x4c]
0x12881862   mov    w2, #-0x40c4              ; =-16580
0x0b020362   add    w2, w27, w2
0xb8226b9f   str    wzr, [x28, x2]
2024-11-02 23:15:22 +01:00
mitaclaw 3d0d03b871 Modernize `std::partition` with ranges
The new return value is `std::ranges::subrange`.
2024-10-17 18:39:13 -07:00
mitaclaw 5f3a8ff0de Modernize `std::unique` with ranges
The new return value is `std::ranges::subrange`.
2024-10-17 18:39:12 -07:00
mitaclaw be0b13da97 Simplify `std::remove` with `std::erase`
`std::erase` is a replacement for the remove-erase idiom.

Changes to `OpenModeToAndroid` inadvertently revealed that the prior implementation had UB (potentially deleting the end iterator). This is now fixed.
2024-10-17 18:38:34 -07:00
mitaclaw 4fde0f2868 Modernize `std::search` with ranges
The new return value is `std::ranges::subrange`.
2024-10-17 18:38:34 -07:00
mitaclaw 0352f24a8e Modernize `std::mismatch` with ranges
The new return value is `std::ranges::mismatch_result`, an alias for the pair-like type `std::ranges::in_in_result`.
2024-10-17 18:38:34 -07:00
JosJuice f58abf59c0 Jit: Skip discarded registers when flushing for interpreter fallback
Normally, the asserts added in 34b0a6ea90 are only triggered when
something actually went wrong in Dolphin. But there is one exception:
In FallBackToInterpreter, we flush all registers regardless of whether
they're discarded. This is fine as long as none of the discarded
registers are inputs to the instruction that the interpreter will run.

To avoid false positive asserts, this change adds a parameter to Flush
that controls whether to skip the asserts for discarded registers.

Additionally, an assert for discarded registers is added to
Arm64FPRCache::Flush. (Previously JitArm64 asserted for GPRs (and CRs)
only, whereas Jit64 asserted both for GPRs and FPRs. I most likely
didn't think of FPRs when writing 34b0a6ea90.)
2024-04-01 16:24:26 +02:00
121 changed files with 1768 additions and 1172 deletions

2
.gitignore vendored
View File

@ -41,3 +41,5 @@ CMakeLists.txt.user
.idea/
# Ignore Visual Studio Code's working dir
/.vscode/
# Ignore flatpak-builder's cache dir
.flatpak-builder

View File

@ -2,7 +2,6 @@
[Core]
# Values set here will override the main Dolphin settings.
CPUThread = False
[OnFrame]
# Add memory patches to be applied every frame here.

View File

@ -10,3 +10,6 @@ MMU = True
[ActionReplay]
# Add action replay cheats here.
[Video_Hacks]
# Needed for loading screens to show up.
EFBAccessEnable = True

View File

@ -62,7 +62,7 @@ bool IsPathAndroidContent(std::string_view uri)
std::string OpenModeToAndroid(std::string mode)
{
// The 'b' specifier is not supported by Android. Since we're on POSIX, it's fine to just skip it.
mode.erase(std::remove(mode.begin(), mode.end(), 'b'));
std::erase(mode, 'b');
if (mode == "r")
return "r";

View File

@ -51,10 +51,9 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsModGroup_getMods(JN
for (GraphicsModConfig& mod : mod_group->GetMods())
{
// If no group matches the mod's features, or if the mod has no features, skip it
if (std::none_of(mod.m_features.begin(), mod.m_features.end(),
[&groups](const GraphicsModFeatureConfig& feature) {
return groups.contains(feature.m_group);
}))
if (std::ranges::none_of(mod.m_features, [&groups](const GraphicsModFeatureConfig& feature) {
return groups.contains(feature.m_group);
}))
{
continue;
}

View File

@ -24,7 +24,7 @@ struct Elt
{
bool IsZero() const
{
return std::all_of(data.begin(), data.end(), [](u8 b) { return b == 0; });
return std::ranges::all_of(data, [](u8 b) { return b == 0; });
}
void MulX()

View File

@ -91,7 +91,7 @@ void MemoryPatches::DisablePatch(const Core::CPUThreadGuard& guard, std::size_t
bool MemoryPatches::HasEnabledPatch(u32 address) const
{
return std::any_of(m_patches.begin(), m_patches.end(), [address](const MemoryPatch& patch) {
return std::ranges::any_of(m_patches, [address](const MemoryPatch& patch) {
return patch.address == address && patch.is_enabled == MemoryPatch::State::Enabled;
});
}

View File

@ -79,7 +79,7 @@ void Watches::DisableWatch(std::size_t index)
bool Watches::HasEnabledWatch(u32 address) const
{
return std::any_of(m_watches.begin(), m_watches.end(), [address](const auto& watch) {
return std::ranges::any_of(m_watches, [address](const auto& watch) {
return watch.address == address && watch.is_enabled == Watch::State::Enabled;
});
}

View File

@ -729,7 +729,7 @@ static bool Unpack(const std::function<bool()>& cancelled, const std::string pat
const bool is_path_traversal_attack =
(childname.find("\\") != std::string_view::npos) ||
(childname.find('/') != std::string_view::npos) ||
std::all_of(childname.begin(), childname.end(), [](char c) { return c == '.'; });
std::ranges::all_of(childname, [](char c) { return c == '.'; });
if (is_path_traversal_attack)
{
ERROR_LOG_FMT(

View File

@ -41,7 +41,7 @@ std::vector<std::string> DoFileSearch(const std::vector<std::string>& directorie
// N.B. This avoids doing any copies
auto ext_matches = [&native_exts](const fs::path& path) {
const std::basic_string_view<fs::path::value_type> native_path = path.native();
return std::any_of(native_exts.cbegin(), native_exts.cend(), [&native_path](const auto& ext) {
return std::ranges::any_of(native_exts, [&native_path](const auto& ext) {
const auto compare_len = ext.native().length();
if (native_path.length() < compare_len)
return false;
@ -98,7 +98,8 @@ std::vector<std::string> DoFileSearch(const std::vector<std::string>& directorie
// not because std::filesystem returns duplicates). Also note that this pathname-based uniqueness
// isn't as thorough as std::filesystem::equivalent.
std::ranges::sort(result);
result.erase(std::unique(result.begin(), result.end()), result.end());
const auto unique_result = std::ranges::unique(result);
result.erase(unique_result.begin(), unique_result.end());
// Dolphin expects to be able to use "/" (DIR_SEP) everywhere.
// std::filesystem uses the OS separator.

View File

@ -250,9 +250,17 @@ bool IniFile::Load(const std::string& filename, bool keep_current_data)
Section* current_section = nullptr;
bool first_line = true;
std::string line_str;
while (std::getline(in, line_str))
while (!in.eof())
{
std::string line_str;
if (!std::getline(in, line_str))
{
if (in.eof())
return true;
else
return false;
}
std::string_view line = line_str;
// Skips the UTF-8 BOM at the start of files. Notepad likes to add this.
@ -303,7 +311,8 @@ bool IniFile::Load(const std::string& filename, bool keep_current_data)
}
}
return in.good();
in.close();
return true;
}
bool IniFile::Save(const std::string& filename)

View File

@ -113,7 +113,7 @@ static bool IsIllegalCharacter(char c)
std::string EscapeFileName(const std::string& filename)
{
// Prevent paths from containing special names like ., .., ..., ...., and so on
if (std::all_of(filename.begin(), filename.end(), [](char c) { return c == '.'; }))
if (std::ranges::all_of(filename, [](char c) { return c == '.'; }))
return ReplaceAll(filename, ".", "__2e__");
// Escape all double underscores since we will use double underscores for our escape sequences
@ -170,8 +170,7 @@ std::string UnescapeFileName(const std::string& filename)
bool IsFileNameSafe(const std::string_view filename)
{
return !filename.empty() &&
!std::all_of(filename.begin(), filename.end(), [](char c) { return c == '.'; }) &&
std::none_of(filename.begin(), filename.end(), IsIllegalCharacter);
return !filename.empty() && !std::ranges::all_of(filename, [](char c) { return c == '.'; }) &&
std::ranges::none_of(filename, IsIllegalCharacter);
}
} // namespace Common

View File

@ -275,6 +275,21 @@ inline bool IsAlpha(char c)
return std::isalpha(c, std::locale::classic());
}
inline bool IsAlnum(char c)
{
return std::isalnum(c, std::locale::classic());
}
inline bool IsUpper(char c)
{
return std::isupper(c, std::locale::classic());
}
inline bool IsXDigit(char c)
{
return std::isxdigit(c /* no locale needed */) != 0;
}
inline char ToLower(char ch)
{
return std::tolower(ch, std::locale::classic());

View File

@ -44,6 +44,7 @@ struct Symbol
std::string name;
std::string function_name; // stripped function name
std::string object_name; // name of object/source file symbol belongs to
std::vector<SCall> callers; // addresses of functions that call this function
std::vector<SCall> calls; // addresses of functions that are called by this function
u32 hash = 0; // use for HLE function finding

View File

@ -132,7 +132,6 @@ public:
void DoIdle();
std::recursive_mutex& GetLock();
void SetHardcoreMode();
bool IsHardcoreModeActive() const;
void SetGameIniId(const std::string& game_ini_id) { m_game_ini_id = game_ini_id; }
@ -199,6 +198,8 @@ private:
void* userdata);
void DisplayWelcomeMessage();
void SetHardcoreMode();
template <typename T>
void FilterApprovedIni(std::vector<T>& codes, const std::string& game_ini_id) const;
template <typename T>
@ -275,11 +276,21 @@ private:
#include <string>
namespace ActionReplay
{
struct ARCode;
}
namespace DiscIO
{
class Volume;
}
namespace Gecko
{
class GeckoCode;
}
class AchievementManager
{
public:

View File

@ -605,7 +605,9 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
ppc_state.pc = executable.reader->GetEntryPoint();
if (executable.reader->LoadSymbols(guard, system.GetPPCSymbolDB()))
const std::string filename = PathToFileName(executable.path);
if (executable.reader->LoadSymbols(guard, system.GetPPCSymbolDB(), filename))
{
Host_PPCSymbolsChanged();
HLE::PatchFunctions(system);

View File

@ -215,7 +215,8 @@ public:
virtual bool IsValid() const = 0;
virtual bool IsWii() const = 0;
virtual bool LoadIntoMemory(Core::System& system, bool only_in_mem1 = false) const = 0;
virtual bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const = 0;
virtual bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db,
const std::string& filename) const = 0;
protected:
std::vector<u8> m_bytes;

View File

@ -27,7 +27,8 @@ public:
bool IsAncast() const { return m_is_ancast; }
u32 GetEntryPoint() const override { return m_dolheader.entryPoint; }
bool LoadIntoMemory(Core::System& system, bool only_in_mem1 = false) const override;
bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const override
bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db,
const std::string& filename) const override
{
return false;
}

View File

@ -180,7 +180,8 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const
return -1;
}
bool ElfReader::LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const
bool ElfReader::LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db,
const std::string& filename) const
{
bool hasSymbols = false;
SectionID sec = GetSectionByName(".symtab");
@ -218,7 +219,7 @@ bool ElfReader::LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_
default:
continue;
}
ppc_symbol_db.AddKnownSymbol(guard, value, size, name, symtype);
ppc_symbol_db.AddKnownSymbol(guard, value, size, name, filename, symtype);
hasSymbols = true;
}
}

View File

@ -36,7 +36,8 @@ public:
u32 GetEntryPoint() const override { return entryPoint; }
u32 GetFlags() const { return (u32)(header->e_flags); }
bool LoadIntoMemory(Core::System& system, bool only_in_mem1 = false) const override;
bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const override;
bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db,
const std::string& filename) const override;
// TODO: actually check for validity.
bool IsValid() const override { return true; }
bool IsWii() const override;

View File

@ -79,11 +79,9 @@ static DiscIO::Language ComputeDefaultLanguage()
static std::optional<std::string> TryParseCountryCode(const std::string& locale)
{
const auto is_upper = [](char c) { return std::isupper(c, std::locale::classic()); };
for (const std::string& part : SplitString(locale, '-'))
{
if (part.size() == 2 && is_upper(part[0]) && is_upper(part[1]))
if (part.size() == 2 && Common::IsUpper(part[0]) && Common::IsUpper(part[1]))
return part;
}

View File

@ -114,9 +114,8 @@ public:
for (const auto& value : section_map)
{
const Config::Location location{system.first, section_name, value.first};
const bool load_disallowed =
std::any_of(begin(s_setting_disallowed), end(s_setting_disallowed),
[&location](const Config::Location* l) { return *l == location; });
const bool load_disallowed = std::ranges::any_of(
s_setting_disallowed, [&location](const auto* l) { return *l == location; });
if (load_disallowed)
continue;

View File

@ -27,9 +27,8 @@ bool IsSettingSaveable(const Config::Location& config_location)
&Config::WIIMOTE_BB_SOURCE.GetLocation(),
};
return std::any_of(begin(s_setting_saveable), end(s_setting_saveable),
[&config_location](const Config::Location* location) {
return *location == config_location;
});
return std::ranges::any_of(s_setting_saveable, [&config_location](const auto* location) {
return *location == config_location;
});
}
} // namespace ConfigLoaders

View File

@ -194,7 +194,7 @@ void DisplayMessage(std::string message, int time_in_ms)
return;
// Actually displaying non-ASCII could cause things to go pear-shaped
if (!std::all_of(message.begin(), message.end(), Common::IsPrintableCharacter))
if (!std::ranges::all_of(message, Common::IsPrintableCharacter))
return;
OSD::AddMessage(std::move(message), time_in_ms);

View File

@ -267,8 +267,8 @@ HitType CodeTrace::TraceLogic(const TraceOutput& current_instr, bool first_hit)
// Checks if the intstruction is a type that needs special handling.
const auto CompareInstruction = [](std::string_view instruction, const auto& type_compare) {
return std::any_of(type_compare.begin(), type_compare.end(),
[&instruction](std::string_view s) { return instruction.starts_with(s); });
return std::ranges::any_of(
type_compare, [&instruction](std::string_view s) { return instruction.starts_with(s); });
};
// Exclusions from updating tracking logic. mt operations are too complex and specialized.

View File

@ -375,6 +375,7 @@ void RSOView::LoadAll(const Core::CPUThreadGuard& guard, u32 address)
void RSOView::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const
{
const std::string rso_name = GetName();
for (const RSOExport& rso_export : GetExports())
{
u32 address = GetExportAddress(rso_export);
@ -389,15 +390,17 @@ void RSOView::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) c
{
// Function symbol
symbol->Rename(export_name);
symbol->object_name = rso_name;
}
else
{
// Data symbol
symbol_db->AddKnownSymbol(guard, address, 0, export_name, Common::Symbol::Type::Data);
symbol_db->AddKnownSymbol(guard, address, 0, export_name, rso_name,
Common::Symbol::Type::Data);
}
}
}
DEBUG_LOG_FMT(SYMBOLS, "RSO({}): {} symbols applied", GetName(), GetExportsCount());
DEBUG_LOG_FMT(SYMBOLS, "RSO({}): {} symbols applied", rso_name, GetExportsCount());
}
u32 RSOView::GetNextEntry() const

View File

@ -101,8 +101,8 @@ std::pair<GCMemcardErrorCode, std::optional<GCMemcard>> GCMemcard::Open(std::str
MBIT_SIZE_MEMORY_CARD_2043,
}};
if (!std::any_of(valid_megabits.begin(), valid_megabits.end(),
[filesize_megabits](u64 mbits) { return mbits == filesize_megabits; }))
if (!std::ranges::any_of(valid_megabits,
[filesize_megabits](u64 mbits) { return mbits == filesize_megabits; }))
{
error_code.Set(GCMemcardValidityIssues::INVALID_CARD_SIZE);
return std::make_pair(error_code, std::nullopt);
@ -1296,8 +1296,8 @@ GCMemcardErrorCode Header::CheckForErrors(u16 card_size_mbits) const
error_code.Set(GCMemcardValidityIssues::MISMATCHED_CARD_SIZE);
// unused areas, should always be filled with 0xFF
if (std::any_of(m_unused_1.begin(), m_unused_1.end(), [](u8 val) { return val != 0xFF; }) ||
std::any_of(m_unused_2.begin(), m_unused_2.end(), [](u8 val) { return val != 0xFF; }))
if (std::ranges::any_of(m_unused_1, [](u8 val) { return val != 0xFF; }) ||
std::ranges::any_of(m_unused_2, [](u8 val) { return val != 0xFF; }))
{
error_code.Set(GCMemcardValidityIssues::DATA_IN_UNUSED_AREA);
}
@ -1361,7 +1361,7 @@ GCMemcardErrorCode Directory::CheckForErrors() const
error_code.Set(GCMemcardValidityIssues::INVALID_CHECKSUM);
// unused area, should always be filled with 0xFF
if (std::any_of(m_padding.begin(), m_padding.end(), [](u8 val) { return val != 0xFF; }))
if (std::ranges::any_of(m_padding, [](u8 val) { return val != 0xFF; }))
error_code.Set(GCMemcardValidityIssues::DATA_IN_UNUSED_AREA);
return error_code;

View File

@ -613,8 +613,7 @@ void WiimoteScanner::SetScanMode(WiimoteScanMode scan_mode)
bool WiimoteScanner::IsReady() const
{
std::lock_guard lg(m_backends_mutex);
return std::any_of(m_backends.begin(), m_backends.end(),
[](const auto& backend) { return backend->IsReady(); });
return std::ranges::any_of(m_backends, &WiimoteScannerBackend::IsReady);
}
static void CheckForDisconnectedWiimotes()

View File

@ -98,8 +98,8 @@ bool IOCtlVRequest::HasNumberOfValidVectors(const size_t in_count, const size_t
return false;
auto IsValidVector = [](const auto& vector) { return vector.size == 0 || vector.address != 0; };
return std::all_of(in_vectors.begin(), in_vectors.end(), IsValidVector) &&
std::all_of(io_vectors.begin(), io_vectors.end(), IsValidVector);
return std::ranges::all_of(in_vectors, IsValidVector) &&
std::ranges::all_of(io_vectors, IsValidVector);
}
void IOCtlRequest::Log(std::string_view device_name, Common::Log::LogType type,

View File

@ -298,7 +298,7 @@ std::string TMDReader::GetGameID() const
std::memcpy(game_id, m_bytes.data() + offsetof(TMDHeader, title_id) + 4, 4);
std::memcpy(game_id + 4, m_bytes.data() + offsetof(TMDHeader, group_id), 2);
if (std::all_of(std::begin(game_id), std::end(game_id), Common::IsPrintableCharacter))
if (std::ranges::all_of(game_id, Common::IsPrintableCharacter))
return std::string(game_id, sizeof(game_id));
return fmt::format("{:016x}", GetTitleId());

View File

@ -79,8 +79,7 @@ static bool IsValidPartOfTitleID(const std::string& string)
{
if (string.length() != 8)
return false;
return std::all_of(string.begin(), string.end(),
[](const auto character) { return std::isxdigit(character) != 0; });
return std::ranges::all_of(string, Common::IsXDigit);
}
static std::vector<u64> GetTitlesInTitleOrImport(FS::FileSystem* fs, const std::string& titles_dir)

View File

@ -464,7 +464,7 @@ static bool HasAllRequiredContents(Kernel& ios, const ES::TMDReader& tmd)
const u64 title_id = tmd.GetTitleId();
const std::vector<ES::Content> contents = tmd.GetContents();
const ES::SharedContentMap shared_content_map{ios.GetFSCore()};
return std::all_of(contents.cbegin(), contents.cend(), [&](const ES::Content& content) {
return std::ranges::all_of(contents, [&](const ES::Content& content) {
if (content.IsOptional())
return true;
@ -878,7 +878,7 @@ ReturnCode ESCore::DeleteSharedContent(const std::array<u8, 20>& sha1) const
// Check whether the shared content is used by a system title.
const std::vector<u64> titles = GetInstalledTitles();
const bool is_used_by_system_title = std::any_of(titles.begin(), titles.end(), [&](u64 id) {
const bool is_used_by_system_title = std::ranges::any_of(titles, [&](u64 id) {
if (!ES::IsTitleType(id, ES::TitleType::System))
return false;
@ -887,8 +887,8 @@ ReturnCode ESCore::DeleteSharedContent(const std::array<u8, 20>& sha1) const
return true;
const auto contents = tmd.GetContents();
return std::any_of(contents.begin(), contents.end(),
[&sha1](const auto& content) { return content.sha1 == sha1; });
return std::ranges::any_of(contents,
[&sha1](const auto& content) { return content.sha1 == sha1; });
});
// Any shared content used by a system title cannot be deleted.

View File

@ -26,7 +26,7 @@ bool IsValidNonRootPath(std::string_view path)
bool IsValidFilename(std::string_view filename)
{
return filename.length() <= MaxFilenameLength &&
!std::any_of(filename.begin(), filename.end(), [](char c) { return c == '/'; });
!std::ranges::any_of(filename, [](char c) { return c == '/'; });
}
SplitPathResult SplitPathAndBasename(std::string_view path)

View File

@ -461,8 +461,7 @@ ResultCode HostFileSystem::Format(Uid uid)
ResultCode HostFileSystem::CreateFileOrDirectory(Uid uid, Gid gid, const std::string& path,
FileAttribute attr, Modes modes, bool is_file)
{
if (!IsValidNonRootPath(path) ||
!std::all_of(path.begin(), path.end(), Common::IsPrintableCharacter))
if (!IsValidNonRootPath(path) || !std::ranges::all_of(path, Common::IsPrintableCharacter))
{
return ResultCode::Invalid;
}
@ -516,14 +515,14 @@ ResultCode HostFileSystem::CreateDirectory(Uid uid, Gid gid, const std::string&
bool HostFileSystem::IsFileOpened(const std::string& path) const
{
return std::any_of(m_handles.begin(), m_handles.end(), [&path](const Handle& handle) {
return std::ranges::any_of(m_handles, [&path](const Handle& handle) {
return handle.opened && handle.wii_path == path;
});
}
bool HostFileSystem::IsDirectoryInUse(const std::string& path) const
{
return std::any_of(m_handles.begin(), m_handles.end(), [&path](const Handle& handle) {
return std::ranges::any_of(m_handles, [&path](const Handle& handle) {
return handle.opened && handle.wii_path.starts_with(path);
});
}

View File

@ -58,8 +58,8 @@ bool WC24FriendList::CheckFriendList() const
bool WC24FriendList::DoesFriendExist(u64 friend_id) const
{
return std::any_of(m_data.friend_codes.cbegin(), m_data.friend_codes.cend(),
[&friend_id](const u64 v) { return v == friend_id; });
return std::ranges::any_of(m_data.friend_codes,
[&friend_id](const u64 v) { return v == friend_id; });
}
std::vector<u64> WC24FriendList::GetUnconfirmedFriends() const

View File

@ -1085,10 +1085,10 @@ void WiiSockMan::UpdatePollCommands()
std::vector<int> original_order(pfds.size());
std::iota(original_order.begin(), original_order.end(), 0);
// Select indices with valid fds
auto mid = std::partition(original_order.begin(), original_order.end(), [&](auto i) {
const auto partition_result = std::ranges::partition(original_order, [&](auto i) {
return GetHostSocket(memory.Read_U32(pcmd.buffer_out + 0xc * i)) >= 0;
});
const auto n_valid = std::distance(original_order.begin(), mid);
const auto n_valid = std::distance(original_order.begin(), partition_result.begin());
// Move all the valid pollfds to the front of the vector
for (auto i = 0; i < n_valid; ++i)

View File

@ -74,7 +74,7 @@ bool Device::HasClass(const u8 device_class) const
if (GetDeviceDescriptor().bDeviceClass == device_class)
return true;
const auto interfaces = GetInterfaces(0);
return std::any_of(interfaces.begin(), interfaces.end(), [device_class](const auto& interface) {
return std::ranges::any_of(interfaces, [device_class](const auto& interface) {
return interface.bInterfaceClass == device_class;
});
}

View File

@ -234,7 +234,7 @@ std::optional<IPCReply> OH0::RegisterClassChangeHook(const IOCtlVRequest& reques
bool OH0::HasDeviceWithVidPid(const u16 vid, const u16 pid) const
{
return std::any_of(m_devices.begin(), m_devices.end(), [=](const auto& device) {
return std::ranges::any_of(m_devices, [=](const auto& device) {
return device.second->GetVid() == vid && device.second->GetPid() == pid;
});
}

View File

@ -380,9 +380,9 @@ bool IsEmulated(u32 major_version)
if (major_version == static_cast<u32>(Titles::BC & 0xffffffff))
return true;
return std::any_of(
ios_memory_values.begin(), ios_memory_values.end(),
[major_version](const MemoryValues& values) { return values.ios_number == major_version; });
return std::ranges::any_of(ios_memory_values, [major_version](const MemoryValues& values) {
return values.ios_number == major_version;
});
}
bool IsEmulated(u64 title_id)

View File

@ -375,8 +375,8 @@ s32 WFSSRVDevice::Rename(std::string source, std::string dest) const
INFO_LOG_FMT(IOS_WFS, "IOCTL_WFS_RENAME: {} to {}", source, dest);
const bool opened = std::any_of(m_fds.begin(), m_fds.end(),
[&](const auto& fd) { return fd.in_use && fd.path == source; });
const bool opened =
std::ranges::any_of(m_fds, [&](const auto& fd) { return fd.in_use && fd.path == source; });
if (opened)
return WFS_FILE_IS_OPENED;

View File

@ -93,7 +93,7 @@ static std::array<u8, 20> ConvertGitRevisionToBytes(const std::string& revision)
{
std::array<u8, 20> revision_bytes{};
if (revision.size() % 2 == 0 && std::all_of(revision.begin(), revision.end(), ::isxdigit))
if (revision.size() % 2 == 0 && std::ranges::all_of(revision, Common::IsXDigit))
{
// The revision string normally contains a git commit hash,
// which is 40 hexadecimal digits long. In DTM files, each pair of
@ -1081,11 +1081,11 @@ void MovieManager::LoadInput(const std::string& movie_path)
std::vector<u8> movInput(m_current_byte);
t_record.ReadArray(movInput.data(), movInput.size());
const auto result = std::mismatch(movInput.begin(), movInput.end(), m_temp_input.begin());
const auto mismatch_result = std::ranges::mismatch(movInput, m_temp_input);
if (result.first != movInput.end())
if (mismatch_result.in1 != movInput.end())
{
const ptrdiff_t mismatch_index = std::distance(movInput.begin(), result.first);
const ptrdiff_t mismatch_index = std::distance(movInput.begin(), mismatch_result.in1);
// this is a "you did something wrong" alert for the user's benefit.
// we'll try to say what's going on in excruciating detail, otherwise the user might not

View File

@ -2486,8 +2486,8 @@ bool NetPlayClient::PlayerHasControllerMapped(const PlayerId pid) const
{
const auto mapping_matches_player_id = [pid](const PlayerId& mapping) { return mapping == pid; };
return std::any_of(m_pad_map.begin(), m_pad_map.end(), mapping_matches_player_id) ||
std::any_of(m_wiimote_map.begin(), m_wiimote_map.end(), mapping_matches_player_id);
return std::ranges::any_of(m_pad_map, mapping_matches_player_id) ||
std::ranges::any_of(m_wiimote_map, mapping_matches_player_id);
}
bool NetPlayClient::IsLocalPlayer(const PlayerId pid) const
@ -2543,7 +2543,7 @@ bool NetPlayClient::DoAllPlayersHaveGame()
{
std::lock_guard lkp(m_crit.players);
return std::all_of(std::begin(m_players), std::end(m_players), [](auto entry) {
return std::ranges::all_of(m_players, [](const auto& entry) {
return entry.second.game_status == SyncIdentifierComparison::SameGame;
});
}

View File

@ -237,7 +237,7 @@ static bool DecompressPacketIntoFolderInternal(sf::Packet& packet, const std::st
if (name.find('\\') != std::string::npos)
return false;
#endif
if (std::all_of(name.begin(), name.end(), [](char c) { return c == '.'; }))
if (std::ranges::all_of(name, [](char c) { return c == '.'; }))
return false;
bool is_folder;

View File

@ -1060,14 +1060,14 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
{
// we have all records for this frame
if (!std::all_of(timebases.begin(), timebases.end(), [&](std::pair<PlayerId, u64> pair) {
if (!std::ranges::all_of(timebases, [&](std::pair<PlayerId, u64> pair) {
return pair.second == timebases[0].second;
}))
{
int pid_to_blame = 0;
for (auto pair : timebases)
{
if (std::all_of(timebases.begin(), timebases.end(), [&](std::pair<PlayerId, u64> other) {
if (std::ranges::all_of(timebases, [&](std::pair<PlayerId, u64> other) {
return other.first == pair.first || other.second != pair.second;
}))
{
@ -1467,14 +1467,12 @@ bool NetPlayServer::SetupNetSettings()
bool NetPlayServer::DoAllPlayersHaveIPLDump() const
{
return std::all_of(m_players.begin(), m_players.end(),
[](const auto& p) { return p.second.has_ipl_dump; });
return std::ranges::all_of(m_players, [](const auto& p) { return p.second.has_ipl_dump; });
}
bool NetPlayServer::DoAllPlayersHaveHardwareFMA() const
{
return std::all_of(m_players.begin(), m_players.end(),
[](const auto& p) { return p.second.has_hardware_fma; });
return std::ranges::all_of(m_players, [](const auto& p) { return p.second.has_hardware_fma; });
}
struct SaveSyncInfo
@ -2234,8 +2232,8 @@ bool NetPlayServer::PlayerHasControllerMapped(const PlayerId pid) const
{
const auto mapping_matches_player_id = [pid](const PlayerId& mapping) { return mapping == pid; };
return std::any_of(m_pad_map.begin(), m_pad_map.end(), mapping_matches_player_id) ||
std::any_of(m_wiimote_map.begin(), m_wiimote_map.end(), mapping_matches_player_id);
return std::ranges::any_of(m_pad_map, mapping_matches_player_id) ||
std::ranges::any_of(m_wiimote_map, mapping_matches_player_id);
}
void NetPlayServer::AssignNewUserAPad(const Client& player)

View File

@ -371,7 +371,7 @@ bool MemChecks::OverlapsMemcheck(u32 address, u32 length) const
const u32 page_end_suffix = length - 1;
const u32 page_end_address = address | page_end_suffix;
return std::any_of(m_mem_checks.cbegin(), m_mem_checks.cend(), [&](const auto& mc) {
return std::ranges::any_of(m_mem_checks, [&](const auto& mc) {
return ((mc.start_address | page_end_suffix) == page_end_address ||
(mc.end_address | page_end_suffix) == page_end_address) ||
((mc.start_address | page_end_suffix) < page_end_address &&

View File

@ -136,15 +136,14 @@ static double CallstackFunc(expr_func* f, vec_expr_t* args, void* c)
if (!std::isnan(num))
{
u32 address = static_cast<u32>(num);
return std::any_of(stack.begin(), stack.end(),
[address](const auto& s) { return s.vAddress == address; });
return std::ranges::any_of(stack, [address](const auto& s) { return s.vAddress == address; });
}
const char* cstr = expr_get_str(&vec_nth(args, 0));
if (cstr != nullptr)
{
return std::any_of(stack.begin(), stack.end(),
[cstr](const auto& s) { return s.Name.find(cstr) != std::string::npos; });
return std::ranges::any_of(
stack, [cstr](const auto& s) { return s.Name.find(cstr) != std::string::npos; });
}
return 0;

View File

@ -350,8 +350,8 @@ void Jit64::Shutdown()
void Jit64::FallBackToInterpreter(UGeckoInstruction inst)
{
gpr.Flush();
fpr.Flush();
gpr.Flush(BitSet32(0xFFFFFFFF), RegCache::IgnoreDiscardedRegisters::Yes);
fpr.Flush(BitSet32(0xFFFFFFFF), RegCache::IgnoreDiscardedRegisters::Yes);
if (js.op->canEndBlock)
{

View File

@ -644,6 +644,20 @@ void Jit64::fselx(UGeckoInstruction inst)
if (cpu_info.bAVX)
{
// Prefer BLENDVPD over VBLENDVPD if the latter doesn't save any
// instructions.
//
// VBLENDVPD allows separate source and destination registers, which can
// eliminate a MOVAPD/MOVSD. However, on Intel since Skylake, VBLENDVPD
// takes additional uops to execute compared to BLENDVPD (according to
// https://uops.info). On AMD and older Intel microarchitectures there is no
// difference.
if (d == c)
{
BLENDVPD(Rd, Rb);
return;
}
X64Reg src1 = XMM1;
if (Rc.IsSimpleReg())
{
@ -654,7 +668,7 @@ void Jit64::fselx(UGeckoInstruction inst)
MOVAPD(XMM1, Rc);
}
if (d == c || packed)
if (packed)
{
VBLENDVPD(Rd, src1, Rb, XMM0);
return;

View File

@ -369,10 +369,8 @@ RCForkGuard RegCache::Fork()
void RegCache::Discard(BitSet32 pregs)
{
ASSERT_MSG(
DYNA_REC,
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }),
"Someone forgot to unlock a X64 reg");
ASSERT_MSG(DYNA_REC, std::ranges::none_of(m_xregs, &X64CachedReg::IsLocked),
"Someone forgot to unlock a X64 reg");
for (preg_t i : pregs)
{
@ -391,12 +389,10 @@ void RegCache::Discard(BitSet32 pregs)
}
}
void RegCache::Flush(BitSet32 pregs)
void RegCache::Flush(BitSet32 pregs, IgnoreDiscardedRegisters ignore_discarded_registers)
{
ASSERT_MSG(
DYNA_REC,
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }),
"Someone forgot to unlock a X64 reg");
ASSERT_MSG(DYNA_REC, std::ranges::none_of(m_xregs, &X64CachedReg::IsLocked),
"Someone forgot to unlock a X64 reg");
for (preg_t i : pregs)
{
@ -410,7 +406,8 @@ void RegCache::Flush(BitSet32 pregs)
case PPCCachedReg::LocationType::Default:
break;
case PPCCachedReg::LocationType::Discarded:
ASSERT_MSG(DYNA_REC, false, "Attempted to flush discarded PPC reg {}", i);
ASSERT_MSG(DYNA_REC, ignore_discarded_registers != IgnoreDiscardedRegisters::No,
"Attempted to flush discarded PPC reg {}", i);
break;
case PPCCachedReg::LocationType::SpeculativeImmediate:
// We can have a cached value without a host register through speculative constants.
@ -458,9 +455,8 @@ void RegCache::Commit()
bool RegCache::IsAllUnlocked() const
{
return std::none_of(m_regs.begin(), m_regs.end(), [](const auto& r) { return r.IsLocked(); }) &&
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }) &&
!IsAnyConstraintActive();
return std::ranges::none_of(m_regs, &PPCCachedReg::IsLocked) &&
std::ranges::none_of(m_xregs, &X64CachedReg::IsLocked) && !IsAnyConstraintActive();
}
void RegCache::PreloadRegisters(BitSet32 to_preload)
@ -753,6 +749,5 @@ void RegCache::Realize(preg_t preg)
bool RegCache::IsAnyConstraintActive() const
{
return std::any_of(m_constraints.begin(), m_constraints.end(),
[](const auto& c) { return c.IsActive(); });
return std::ranges::any_of(m_constraints, &RCConstraint::IsActive);
}

View File

@ -126,6 +126,12 @@ public:
MaintainState,
};
enum class IgnoreDiscardedRegisters
{
No,
Yes,
};
explicit RegCache(Jit64& jit);
virtual ~RegCache() = default;
@ -168,7 +174,8 @@ public:
RCForkGuard Fork();
void Discard(BitSet32 pregs);
void Flush(BitSet32 pregs = BitSet32::AllTrue(32));
void Flush(BitSet32 pregs = BitSet32::AllTrue(32),
IgnoreDiscardedRegisters ignore_discarded_registers = IgnoreDiscardedRegisters::No);
void Reset(BitSet32 pregs);
void Revert();
void Commit();

View File

@ -256,8 +256,8 @@ void JitArm64::Shutdown()
void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
{
FlushCarry();
gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG, IgnoreDiscardedRegisters::Yes);
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG, IgnoreDiscardedRegisters::Yes);
if (js.op->canEndBlock)
{

View File

@ -1394,23 +1394,34 @@ void JitArm64::subfic(UGeckoInstruction inst)
else
{
const bool will_read = d == a;
const bool is_zero = imm == 0;
gpr.BindToRegister(d, will_read);
// d = imm - a
ARM64Reg RD = gpr.R(d);
if (imm == -1)
{
Arm64GPRCache::ScopedARM64Reg WA(ARM64Reg::WZR);
if (!is_zero)
// d = -1 - a = ~a
MVN(RD, gpr.R(a));
// CA is always set in this case
ComputeCarry(true);
}
else
{
const bool is_zero = imm == 0;
// d = imm - a
{
WA = will_read ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(RD);
MOVI2R(WA, imm);
Arm64GPRCache::ScopedARM64Reg WA(ARM64Reg::WZR);
if (!is_zero)
{
WA = will_read ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(RD);
MOVI2R(WA, imm);
}
CARRY_IF_NEEDED(SUB, SUBS, RD, WA, gpr.R(a));
}
CARRY_IF_NEEDED(SUB, SUBS, RD, WA, gpr.R(a));
ComputeCarry();
}
ComputeCarry();
}
}

View File

@ -181,7 +181,8 @@ void JitArm64::SafeStoreFromReg(s32 dest, u32 value, s32 regOffset, u32 flags, s
if (!jo.fastmem)
gpr.Lock(ARM64Reg::W0);
ARM64Reg RS = gpr.R(value);
// Don't materialize zero.
ARM64Reg RS = gpr.IsImm(value, 0) ? ARM64Reg::WZR : gpr.R(value);
ARM64Reg reg_dest = ARM64Reg::INVALID_REG;
ARM64Reg reg_off = ARM64Reg::INVALID_REG;

View File

@ -7,6 +7,7 @@
#include "Common/CommonTypes.h"
#include "Common/Config/Config.h"
#include "Common/StringUtil.h"
#include "Common/Unreachable.h"
#include "Core/Config/SessionSettings.h"
#include "Core/ConfigManager.h"
@ -337,8 +338,12 @@ void JitArm64::ps_sel(UGeckoInstruction inst)
const auto V0Q = fpr.GetScopedReg();
const ARM64Reg V0 = reg_encoder(V0Q);
m_float_emit.FCMGE(size, V0, VA);
m_float_emit.BSL(V0, VC, VB);
m_float_emit.MOV(VD, V0);
if (d == b)
m_float_emit.BIT(VD, VC, V0);
else if (d == c)
m_float_emit.BIF(VD, VB, V0);
else
Common::Unreachable();
}
ASSERT_MSG(DYNA_REC, singles == (fpr.IsSingle(a) && fpr.IsSingle(b) && fpr.IsSingle(c)),

View File

@ -241,12 +241,16 @@ void Arm64GPRCache::FlushRegister(size_t index, FlushMode mode, ARM64Reg tmp_reg
}
}
void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_reg)
void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers)
{
for (auto iter = regs.begin(); iter != regs.end(); ++iter)
{
const int i = *iter;
ASSERT_MSG(DYNA_REC, m_guest_registers[GUEST_GPR_OFFSET + i].GetType() != RegType::Discarded,
ASSERT_MSG(DYNA_REC,
ignore_discarded_registers != IgnoreDiscardedRegisters::No ||
m_guest_registers[GUEST_GPR_OFFSET + i].GetType() != RegType::Discarded,
"Attempted to flush discarded register");
if (i + 1 < int(GUEST_GPR_COUNT) && regs[i + 1])
@ -288,11 +292,14 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_r
}
}
void Arm64GPRCache::FlushCRRegisters(BitSet8 regs, FlushMode mode, ARM64Reg tmp_reg)
void Arm64GPRCache::FlushCRRegisters(BitSet8 regs, FlushMode mode, ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers)
{
for (int i : regs)
{
ASSERT_MSG(DYNA_REC, m_guest_registers[GUEST_CR_OFFSET + i].GetType() != RegType::Discarded,
ASSERT_MSG(DYNA_REC,
ignore_discarded_registers != IgnoreDiscardedRegisters::No ||
m_guest_registers[GUEST_CR_OFFSET + i].GetType() != RegType::Discarded,
"Attempted to flush discarded register");
FlushRegister(GUEST_CR_OFFSET + i, mode, tmp_reg);
@ -318,10 +325,11 @@ void Arm64GPRCache::ResetCRRegisters(BitSet8 regs)
}
}
void Arm64GPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg)
void Arm64GPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers)
{
FlushRegisters(BitSet32(0xFFFFFFFF), mode, tmp_reg);
FlushCRRegisters(BitSet8(0xFF), mode, tmp_reg);
FlushRegisters(BitSet32(0xFFFFFFFF), mode, tmp_reg, ignore_discarded_registers);
FlushCRRegisters(BitSet8(0xFF), mode, tmp_reg, ignore_discarded_registers);
}
ARM64Reg Arm64GPRCache::R(const GuestRegInfo& guest_reg)
@ -498,14 +506,19 @@ Arm64FPRCache::Arm64FPRCache() : Arm64RegCache(GUEST_FPR_COUNT)
{
}
void Arm64FPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg)
void Arm64FPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers)
{
for (size_t i = 0; i < m_guest_registers.size(); ++i)
{
const RegType reg_type = m_guest_registers[i].GetType();
if (reg_type != RegType::NotLoaded && reg_type != RegType::Discarded &&
reg_type != RegType::Immediate)
if (reg_type == RegType::Discarded)
{
ASSERT_MSG(DYNA_REC, ignore_discarded_registers != IgnoreDiscardedRegisters::No,
"Attempted to flush discarded register");
}
else if (reg_type != RegType::NotLoaded && reg_type != RegType::Immediate)
{
FlushRegister(i, mode, tmp_reg);
}

View File

@ -81,6 +81,12 @@ enum class FlushMode : bool
MaintainState,
};
enum class IgnoreDiscardedRegisters
{
No,
Yes,
};
class OpArg
{
public:
@ -169,7 +175,8 @@ public:
// Flushes the register cache in different ways depending on the mode.
// A temporary register must be supplied when flushing GPRs with FlushMode::MaintainState,
// but in other cases it can be set to ARM64Reg::INVALID_REG when convenient for the caller.
virtual void Flush(FlushMode mode, Arm64Gen::ARM64Reg tmp_reg) = 0;
virtual void Flush(FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers) = 0;
virtual BitSet32 GetCallerSavedUsed() const = 0;
@ -315,7 +322,9 @@ public:
// Flushes the register cache in different ways depending on the mode.
// A temporary register must be supplied when flushing GPRs with FlushMode::MaintainState,
// but in other cases it can be set to ARM64Reg::INVALID_REG when convenient for the caller.
void Flush(FlushMode mode, Arm64Gen::ARM64Reg tmp_reg) override;
void Flush(
FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers = IgnoreDiscardedRegisters::No) override;
// Returns a guest GPR inside of a host register.
// Will dump an immediate to the host register as well.
@ -381,12 +390,12 @@ public:
void StoreRegisters(BitSet32 regs, Arm64Gen::ARM64Reg tmp_reg = Arm64Gen::ARM64Reg::INVALID_REG)
{
FlushRegisters(regs, FlushMode::All, tmp_reg);
FlushRegisters(regs, FlushMode::All, tmp_reg, IgnoreDiscardedRegisters::No);
}
void StoreCRRegisters(BitSet8 regs, Arm64Gen::ARM64Reg tmp_reg = Arm64Gen::ARM64Reg::INVALID_REG)
{
FlushCRRegisters(regs, FlushMode::All, tmp_reg);
FlushCRRegisters(regs, FlushMode::All, tmp_reg, IgnoreDiscardedRegisters::No);
}
void DiscardCRRegisters(BitSet8 regs);
@ -421,8 +430,10 @@ private:
void SetImmediate(const GuestRegInfo& guest_reg, u32 imm, bool dirty);
void BindToRegister(const GuestRegInfo& guest_reg, bool will_read, bool will_write = true);
void FlushRegisters(BitSet32 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg);
void FlushCRRegisters(BitSet8 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg);
void FlushRegisters(BitSet32 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers);
void FlushCRRegisters(BitSet8 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers);
};
class Arm64FPRCache : public Arm64RegCache
@ -432,7 +443,9 @@ public:
// Flushes the register cache in different ways depending on the mode.
// The temporary register can be set to ARM64Reg::INVALID_REG when convenient for the caller.
void Flush(FlushMode mode, Arm64Gen::ARM64Reg tmp_reg) override;
void Flush(
FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers = IgnoreDiscardedRegisters::No) override;
// Returns a guest register inside of a host register
// Will dump an immediate to the host register as well

View File

@ -257,6 +257,12 @@ void ByteswapAfterLoad(ARM64XEmitter* emit, ARM64FloatEmitter* float_emit, ARM64
ARM64Reg ByteswapBeforeStore(ARM64XEmitter* emit, ARM64FloatEmitter* float_emit, ARM64Reg tmp_reg,
ARM64Reg src_reg, u32 flags, bool want_reversed)
{
// Byteswapping zero is still zero.
// We'd typically expect a writable register to be passed in, but recognize
// WZR for optimization purposes.
if ((flags & BackPatchInfo::FLAG_FLOAT) == 0 && src_reg == ARM64Reg::WZR)
return ARM64Reg::WZR;
ARM64Reg dst_reg = src_reg;
if (want_reversed == !(flags & BackPatchInfo::FLAG_REVERSE))

View File

@ -112,7 +112,7 @@ JitBase::~JitBase()
bool JitBase::DoesConfigNeedRefresh()
{
return std::any_of(JIT_SETTINGS.begin(), JIT_SETTINGS.end(), [this](const auto& pair) {
return std::ranges::any_of(JIT_SETTINGS, [this](const auto& pair) {
return this->*pair.first != Config::Get(*pair.second);
});
}

View File

@ -191,7 +191,7 @@ static void AnalyzeFunction2(PPCSymbolDB* func_db, Common::Symbol* func)
{
u32 flags = func->flags;
bool nonleafcall = std::any_of(func->calls.begin(), func->calls.end(), [&](const auto& call) {
bool nonleafcall = std::ranges::any_of(func->calls, [&](const auto& call) {
const Common::Symbol* const called_func = func_db->GetSymbolFromAddr(call.function);
return called_func && (called_func->flags & Common::FFLAG_LEAF) == 0;
});

View File

@ -49,7 +49,8 @@ Common::Symbol* PPCSymbolDB::AddFunction(const Core::CPUThreadGuard& guard, u32
}
void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name, Common::Symbol::Type type)
const std::string& name, const std::string& object_name,
Common::Symbol::Type type)
{
auto iter = m_functions.find(startAddr);
if (iter != m_functions.end())
@ -57,6 +58,7 @@ void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAdd
// already got it, let's just update name, checksum & size to be sure.
Common::Symbol* tempfunc = &iter->second;
tempfunc->Rename(name);
tempfunc->object_name = object_name;
tempfunc->hash = HashSignatureDB::ComputeCodeChecksum(guard, startAddr, startAddr + size - 4);
tempfunc->type = type;
tempfunc->size = size;
@ -65,6 +67,7 @@ void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAdd
{
// new symbol. run analyze.
auto& new_symbol = m_functions.emplace(startAddr, name).first->second;
new_symbol.object_name = object_name;
new_symbol.type = type;
new_symbol.address = startAddr;
@ -399,6 +402,13 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
if (name[strlen(name) - 1] == '\r')
name[strlen(name) - 1] = 0;
// Split the current name string into separate parts, and get the object name
// if it exists.
const std::vector<std::string> parts = SplitString(name, '\t');
const std::string name_string(StripWhitespace(parts[0]));
const std::string object_filename_string =
parts.size() > 1 ? std::string(StripWhitespace(parts[1])) : "";
// Check if this is a valid entry.
if (strlen(name) > 0)
{
@ -435,7 +445,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
if (good)
{
++good_count;
AddKnownSymbol(guard, vaddress, size, name, type);
AddKnownSymbol(guard, vaddress, size, name_string, object_filename_string, type);
}
else
{
@ -473,8 +483,13 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
for (const auto& symbol : function_symbols)
{
// Write symbol address, size, virtual address, alignment, name
f.WriteString(fmt::format("{0:08x} {1:08x} {2:08x} {3} {4}\n", symbol->address, symbol->size,
symbol->address, 0, symbol->name));
std::string line = fmt::format("{0:08x} {1:06x} {2:08x} {3} {4}", symbol->address, symbol->size,
symbol->address, 0, symbol->name);
// Also write the object name if it exists
if (!symbol->object_name.empty())
line += fmt::format(" \t{0}", symbol->object_name);
line += "\n";
f.WriteString(line);
}
// Write .data section
@ -482,8 +497,13 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
for (const auto& symbol : data_symbols)
{
// Write symbol address, size, virtual address, alignment, name
f.WriteString(fmt::format("{0:08x} {1:08x} {2:08x} {3} {4}\n", symbol->address, symbol->size,
symbol->address, 0, symbol->name));
std::string line = fmt::format("{0:08x} {1:06x} {2:08x} {3} {4}", symbol->address, symbol->size,
symbol->address, 0, symbol->name);
// Also write the object name if it exists
if (!symbol->object_name.empty())
line += fmt::format(" \t{0}", symbol->object_name);
line += "\n";
f.WriteString(line);
}
return true;

View File

@ -23,7 +23,7 @@ public:
Common::Symbol* AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr) override;
void AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name,
const std::string& name, const std::string& object_name,
Common::Symbol::Type type = Common::Symbol::Type::Function);
Common::Symbol* GetSymbolFromAddr(u32 addr) override;

View File

@ -223,15 +223,14 @@ bool IsTitleInstalled(u64 title_id)
// Since this isn't IOS and we only need a simple way to figure out if a title is installed,
// we make the (reasonable) assumption that having more than just the TMD in the content
// directory means that the title is installed.
return std::any_of(entries->begin(), entries->end(),
[](const std::string& file) { return file != "title.tmd"; });
return std::ranges::any_of(*entries, [](const std::string& file) { return file != "title.tmd"; });
}
bool IsTMDImported(IOS::HLE::FS::FileSystem& fs, u64 title_id)
{
const auto entries = fs.ReadDirectory(0, 0, Common::GetTitleContentPath(title_id));
return entries && std::any_of(entries->begin(), entries->end(),
[](const std::string& file) { return file == "title.tmd"; });
return entries &&
std::ranges::any_of(*entries, [](const std::string& file) { return file == "title.tmd"; });
}
IOS::ES::TMDReader FindBackupTMD(IOS::HLE::FS::FileSystem& fs, u64 title_id)
@ -947,8 +946,8 @@ static NANDCheckResult CheckNAND(IOS::HLE::Kernel& ios, bool repair)
}
const auto installed_contents = es.GetStoredContentsFromTMD(tmd);
const bool is_installed = std::any_of(installed_contents.begin(), installed_contents.end(),
[](const auto& content) { return !content.IsShared(); });
const bool is_installed = std::ranges::any_of(
installed_contents, [](const auto& content) { return !content.IsShared(); });
if (is_installed && installed_contents != tmd.GetContents() &&
(tmd.GetTitleFlags() & IOS::ES::TitleFlags::TITLE_TYPE_DATA) == 0)

View File

@ -13,6 +13,7 @@
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "Common/StringUtil.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h"
@ -39,8 +40,7 @@ std::string NameForPartitionType(u32 partition_type, bool include_prefix)
static_cast<char>((partition_type >> 16) & 0xFF),
static_cast<char>((partition_type >> 8) & 0xFF),
static_cast<char>(partition_type & 0xFF)};
if (std::all_of(type_as_game_id.cbegin(), type_as_game_id.cend(),
[](char c) { return std::isalnum(c, std::locale::classic()); }))
if (std::ranges::all_of(type_as_game_id, Common::IsAlnum))
{
return include_prefix ? "P-" + type_as_game_id : type_as_game_id;
}

View File

@ -240,11 +240,9 @@ bool NANDImporter::ExtractCertificates()
for (const PEMCertificate& certificate : certificates)
{
const auto search_result =
std::search(content_bytes.begin(), content_bytes.end(), certificate.search_bytes.begin(),
certificate.search_bytes.end());
const auto search_result = std::ranges::search(content_bytes, certificate.search_bytes);
if (search_result == content_bytes.end())
if (search_result.empty())
{
ERROR_LOG_FMT(DISCIO, "ExtractCertificates: Could not find offset for certficate '{}'",
certificate.filename);
@ -252,7 +250,8 @@ bool NANDImporter::ExtractCertificates()
}
const std::string pem_file_path = m_nand_root + std::string(certificate.filename);
const ptrdiff_t certificate_offset = std::distance(content_bytes.begin(), search_result);
const ptrdiff_t certificate_offset =
std::distance(content_bytes.begin(), search_result.begin());
constexpr int min_offset = 2;
if (certificate_offset < min_offset)
{

View File

@ -104,7 +104,7 @@ FileDataLoaderHostFS::MakeAbsoluteFromRelative(std::string_view external_relativ
result.pop_back();
result.pop_back();
}
else if (std::all_of(element.begin(), element.end(), [](char c) { return c == '.'; }))
else if (std::ranges::all_of(element, [](char c) { return c == '.'; }))
{
// This is a triple, quadruple, etc. dot.
// Some file systems treat this as several 'up' path traversals, but Riivolution does not.

View File

@ -729,16 +729,15 @@ bool VolumeVerifier::ShouldHaveInstallPartition() const
static constexpr std::array<std::string_view, 4> dragon_quest_x = {"S4MJGD", "S4SJGD", "S6TJGD",
"SDQJGD"};
const std::string& game_id = m_volume.GetGameID();
return std::any_of(dragon_quest_x.cbegin(), dragon_quest_x.cend(),
[&game_id](std::string_view x) { return x == game_id; });
return std::ranges::any_of(dragon_quest_x,
[&game_id](std::string_view x) { return x == game_id; });
}
bool VolumeVerifier::ShouldHaveMasterpiecePartitions() const
{
static constexpr std::array<std::string_view, 4> ssbb = {"RSBE01", "RSBJ01", "RSBK01", "RSBP01"};
const std::string& game_id = m_volume.GetGameID();
return std::any_of(ssbb.cbegin(), ssbb.cend(),
[&game_id](std::string_view x) { return x == game_id; });
return std::ranges::any_of(ssbb, [&game_id](std::string_view x) { return x == game_id; });
}
bool VolumeVerifier::ShouldBeDualLayer() const
@ -1039,7 +1038,7 @@ void VolumeVerifier::CheckSuperPaperMario()
if (!m_volume.Read(offset, length, data.data(), partition))
return;
if (std::any_of(data.cbegin(), data.cend(), [](u8 x) { return x != 0; }))
if (std::ranges::any_of(data, [](u8 x) { return x != 0; }))
{
AddProblem(Severity::High,
Common::GetStringT("Some padding data that should be zero is not zero. "

View File

@ -176,7 +176,7 @@ IOS::ES::TicketReader VolumeWAD::GetTicketWithFixedCommonKey() const
return m_ticket;
const std::vector<u8> sig = m_ticket.GetSignatureData();
if (!std::all_of(sig.cbegin(), sig.cend(), [](u8 a) { return a == 0; }))
if (!std::ranges::all_of(sig, [](u8 a) { return a == 0; }))
{
// This does not look like a typical "invalid common key index" ticket, so let's assume
// the index is correct. This saves some time when reading properly signed titles.

View File

@ -1126,7 +1126,7 @@ bool WIARVZFileReader<RVZ>::TryReuse(std::map<ReuseID, GroupEntry>* reusable_gro
static bool AllAre(const std::vector<u8>& data, u8 x)
{
return std::all_of(data.begin(), data.end(), [x](u8 y) { return x == y; });
return std::ranges::all_of(data, [x](u8 y) { return x == y; });
}
static bool AllAre(const u8* begin, const u8* end, u8 x)
@ -1379,8 +1379,8 @@ WIARVZFileReader<RVZ>::ProcessAndCompress(CompressThreadState* state, CompressPa
}
}
if (!std::all_of(output_entries.begin(), output_entries.end(),
[](const OutputParametersEntry& entry) { return entry.reused_group; }))
if (!std::ranges::all_of(output_entries,
[](const auto& entry) { return entry.reused_group.has_value(); }))
{
const u64 number_of_exception_lists =
chunks_per_wii_group == 1 ? exception_lists_per_chunk : chunks;

View File

@ -52,6 +52,7 @@ add_executable(dolphin-emu
Config/ConfigControls/ConfigBool.h
Config/ConfigControls/ConfigChoice.cpp
Config/ConfigControls/ConfigChoice.h
Config/ConfigControls/ConfigControl.h
Config/ConfigControls/ConfigInteger.cpp
Config/ConfigControls/ConfigInteger.h
Config/ConfigControls/ConfigRadio.cpp

View File

@ -3,31 +3,28 @@
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include <QEvent>
#include <QFont>
#include <QSignalBlocker>
#include "Common/Config/Config.h"
#include "DolphinQt/Settings.h"
ConfigBool::ConfigBool(const QString& label, const Config::Info<bool>& setting, bool reverse)
: ToolTipCheckBox(label), m_setting(setting), m_reverse(reverse)
: ConfigBool(label, setting, nullptr, reverse)
{
}
ConfigBool::ConfigBool(const QString& label, const Config::Info<bool>& setting,
Config::Layer* layer, bool reverse)
: ConfigControl(label, setting.GetLocation(), layer), m_setting(setting), m_reverse(reverse)
{
setChecked(ReadValue(setting) ^ reverse);
connect(this, &QCheckBox::toggled, this, &ConfigBool::Update);
setChecked(Config::Get(m_setting) ^ reverse);
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
QFont bf = font();
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
setFont(bf);
const QSignalBlocker blocker(this);
setChecked(Config::Get(m_setting) ^ m_reverse);
});
}
void ConfigBool::Update()
{
Config::SetBaseOrCurrent(m_setting, static_cast<bool>(isChecked() ^ m_reverse));
const bool value = static_cast<bool>(isChecked() ^ m_reverse);
SaveValue(m_setting, value);
}
void ConfigBool::OnConfigChanged()
{
setChecked(ReadValue(m_setting) ^ m_reverse);
}

View File

@ -3,23 +3,25 @@
#pragma once
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
namespace Config
{
template <typename T>
class Info;
}
#include "Common/Config/ConfigInfo.h"
class ConfigBool : public ToolTipCheckBox
class ConfigBool final : public ConfigControl<ToolTipCheckBox>
{
Q_OBJECT
public:
ConfigBool(const QString& label, const Config::Info<bool>& setting, bool reverse = false);
ConfigBool(const QString& label, const Config::Info<bool>& setting, Config::Layer* layer,
bool reverse = false);
protected:
void OnConfigChanged() override;
private:
void Update();
const Config::Info<bool>& m_setting;
const Config::Info<bool> m_setting;
bool m_reverse;
};

View File

@ -5,85 +5,168 @@
#include <QSignalBlocker>
#include "Common/Config/Config.h"
#include "DolphinQt/Settings.h"
ConfigChoice::ConfigChoice(const QStringList& options, const Config::Info<int>& setting)
: m_setting(setting)
ConfigChoice::ConfigChoice(const QStringList& options, const Config::Info<int>& setting,
Config::Layer* layer)
: ConfigControl(setting.GetLocation(), layer), m_setting(setting)
{
addItems(options);
setCurrentIndex(ReadValue(setting));
connect(this, &QComboBox::currentIndexChanged, this, &ConfigChoice::Update);
setCurrentIndex(Config::Get(m_setting));
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
QFont bf = font();
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
setFont(bf);
const QSignalBlocker blocker(this);
setCurrentIndex(Config::Get(m_setting));
});
}
void ConfigChoice::Update(int choice)
{
Config::SetBaseOrCurrent(m_setting, choice);
SaveValue(m_setting, choice);
}
void ConfigChoice::OnConfigChanged()
{
setCurrentIndex(ReadValue(m_setting));
}
ConfigStringChoice::ConfigStringChoice(const std::vector<std::string>& options,
const Config::Info<std::string>& setting)
: m_setting(setting), m_text_is_data(true)
const Config::Info<std::string>& setting,
Config::Layer* layer)
: ConfigControl(setting.GetLocation(), layer), m_setting(setting), m_text_is_data(true)
{
for (const auto& op : options)
addItem(QString::fromStdString(op));
Connect();
Load();
connect(this, &QComboBox::currentIndexChanged, this, &ConfigStringChoice::Update);
}
ConfigStringChoice::ConfigStringChoice(const std::vector<std::pair<QString, QString>>& options,
const Config::Info<std::string>& setting)
: m_setting(setting), m_text_is_data(false)
const Config::Info<std::string>& setting,
Config::Layer* layer)
: ConfigControl(setting.GetLocation(), layer), m_setting(setting), m_text_is_data(false)
{
for (const auto& [option_text, option_data] : options)
addItem(option_text, option_data);
Connect();
Load();
}
void ConfigStringChoice::Connect()
{
const auto on_config_changed = [this]() {
QFont bf = font();
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
setFont(bf);
Load();
};
connect(&Settings::Instance(), &Settings::ConfigChanged, this, on_config_changed);
connect(this, &QComboBox::currentIndexChanged, this, &ConfigStringChoice::Update);
Load();
}
void ConfigStringChoice::Update(int index)
{
if (m_text_is_data)
{
Config::SetBaseOrCurrent(m_setting, itemText(index).toStdString());
}
SaveValue(m_setting, itemText(index).toStdString());
else
{
Config::SetBaseOrCurrent(m_setting, itemData(index).toString().toStdString());
}
SaveValue(m_setting, itemData(index).toString().toStdString());
}
void ConfigStringChoice::Load()
{
const QString setting_value = QString::fromStdString(Config::Get(m_setting));
const QString setting_value = QString::fromStdString(ReadValue(m_setting));
const int index = m_text_is_data ? findText(setting_value) : findData(setting_value);
// This can be called publicly.
const QSignalBlocker block(this);
setCurrentIndex(index);
}
void ConfigStringChoice::OnConfigChanged()
{
Load();
}
ConfigComplexChoice::ConfigComplexChoice(const InfoVariant setting1, const InfoVariant setting2,
Config::Layer* layer)
: m_setting1(setting1), m_setting2(setting2), m_layer(layer)
{
connect(&Settings::Instance(), &Settings::ConfigChanged, this, &ConfigComplexChoice::Refresh);
connect(this, &QComboBox::currentIndexChanged, this, &ConfigComplexChoice::SaveValue);
}
void ConfigComplexChoice::Refresh()
{
auto& location = GetLocation();
QFont bf = font();
if (m_layer != nullptr)
{
bf.setBold(m_layer->Exists(location.first) || m_layer->Exists(location.second));
}
else
{
bf.setBold(Config::GetActiveLayerForConfig(location.first) != Config::LayerType::Base ||
Config::GetActiveLayerForConfig(location.second) != Config::LayerType::Base);
}
setFont(bf);
UpdateComboIndex();
}
void ConfigComplexChoice::Add(const QString& name, const OptionVariant option1,
const OptionVariant option2)
{
const QSignalBlocker blocker(this);
addItem(name);
m_options.push_back(std::make_pair(option1, option2));
}
void ConfigComplexChoice::Reset()
{
clear();
m_options.clear();
}
void ConfigComplexChoice::SaveValue(int choice)
{
auto Set = [this, choice](auto& setting, auto& value) {
if (m_layer != nullptr)
{
m_layer->Set(setting.GetLocation(), value);
Config::OnConfigChanged();
return;
}
Config::SetBaseOrCurrent(setting, value);
};
std::visit(Set, m_setting1, m_options[choice].first);
std::visit(Set, m_setting2, m_options[choice].second);
}
void ConfigComplexChoice::UpdateComboIndex()
{
auto Get = [this](auto& setting) {
if (m_layer != nullptr)
return static_cast<OptionVariant>(m_layer->Get(setting));
return static_cast<OptionVariant>(Config::Get(setting));
};
std::pair<OptionVariant, OptionVariant> values =
std::make_pair(std::visit(Get, m_setting1), std::visit(Get, m_setting2));
auto it = std::find(m_options.begin(), m_options.end(), values);
int index = static_cast<int>(std::distance(m_options.begin(), it));
const QSignalBlocker blocker(this);
setCurrentIndex(index);
}
const std::pair<Config::Location, Config::Location> ConfigComplexChoice::GetLocation() const
{
auto visit = [](auto& v) { return v.GetLocation(); };
return {std::visit(visit, m_setting1), std::visit(visit, m_setting2)};
}
void ConfigComplexChoice::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::RightButton && m_layer != nullptr)
{
auto& location = GetLocation();
m_layer->DeleteKey(location.first);
m_layer->DeleteKey(location.second);
Config::OnConfigChanged();
}
else
{
QComboBox::mousePressEvent(event);
}
};

View File

@ -7,15 +7,20 @@
#include <utility>
#include <vector>
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h"
#include "Common/Config/Config.h"
#include "Common/Config/ConfigInfo.h"
class ConfigChoice : public ToolTipComboBox
class ConfigChoice final : public ConfigControl<ToolTipComboBox>
{
Q_OBJECT
public:
ConfigChoice(const QStringList& options, const Config::Info<int>& setting);
ConfigChoice(const QStringList& options, const Config::Info<int>& setting,
Config::Layer* layer = nullptr);
protected:
void OnConfigChanged() override;
private:
void Update(int choice);
@ -23,20 +28,49 @@ private:
Config::Info<int> m_setting;
};
class ConfigStringChoice : public ToolTipComboBox
class ConfigStringChoice final : public ConfigControl<ToolTipComboBox>
{
Q_OBJECT
public:
ConfigStringChoice(const std::vector<std::string>& options,
const Config::Info<std::string>& setting);
const Config::Info<std::string>& setting, Config::Layer* layer = nullptr);
ConfigStringChoice(const std::vector<std::pair<QString, QString>>& options,
const Config::Info<std::string>& setting);
private:
void Connect();
void Update(int index);
const Config::Info<std::string>& setting, Config::Layer* layer = nullptr);
void Load();
Config::Info<std::string> m_setting;
protected:
void OnConfigChanged() override;
private:
void Update(int index);
const Config::Info<std::string> m_setting;
bool m_text_is_data = false;
};
class ConfigComplexChoice final : public ToolTipComboBox
{
Q_OBJECT
using InfoVariant = std::variant<Config::Info<u32>, Config::Info<int>, Config::Info<bool>>;
using OptionVariant = std::variant<u32, int, bool>;
public:
ConfigComplexChoice(const InfoVariant setting1, const InfoVariant setting2,
Config::Layer* layer = nullptr);
void Add(const QString& name, const OptionVariant option1, const OptionVariant option2);
void Refresh();
void Reset();
const std::pair<Config::Location, Config::Location> GetLocation() const;
private:
void SaveValue(int choice);
void UpdateComboIndex();
void mousePressEvent(QMouseEvent* event) override;
Config::Layer* m_layer;
const InfoVariant m_setting1;
const InfoVariant m_setting2;
std::vector<std::pair<OptionVariant, OptionVariant>> m_options;
};

View File

@ -0,0 +1,105 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QFont>
#include <QMouseEvent>
#include <QSignalBlocker>
#include "Common/Config/Enums.h"
#include "Common/Config/Layer.h"
#include "DolphinQt/Settings.h"
namespace Config
{
template <typename T>
class Info;
struct Location;
} // namespace Config
template <class Derived>
class ConfigControl : public Derived
{
public:
ConfigControl(const Config::Location& location, Config::Layer* layer)
: m_location(location), m_layer(layer)
{
ConnectConfig();
}
ConfigControl(const QString& label, const Config::Location& location, Config::Layer* layer)
: Derived(label), m_location(location), m_layer(layer)
{
ConnectConfig();
}
ConfigControl(const Qt::Orientation& orient, const Config::Location& location,
Config::Layer* layer)
: Derived(orient), m_location(location), m_layer(layer)
{
ConnectConfig();
}
const Config::Location GetLocation() const { return m_location; }
protected:
void ConnectConfig()
{
Derived::connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
QFont bf = Derived::font();
bf.setBold(IsConfigLocal());
Derived::setFont(bf);
const QSignalBlocker blocker(this);
OnConfigChanged();
});
}
template <typename T>
void SaveValue(const Config::Info<T>& setting, const T& value)
{
if (m_layer != nullptr)
{
m_layer->Set(m_location, value);
Config::OnConfigChanged();
return;
}
Config::SetBaseOrCurrent(setting, value);
}
template <typename T>
const T ReadValue(const Config::Info<T>& setting) const
{
if (m_layer != nullptr)
return m_layer->Get(setting);
return Config::Get(setting);
}
virtual void OnConfigChanged(){};
private:
bool IsConfigLocal() const
{
if (m_layer != nullptr)
return m_layer->Exists(m_location);
else
return Config::GetActiveLayerForConfig(m_location) != Config::LayerType::Base;
}
void mousePressEvent(QMouseEvent* event) override
{
if (m_layer != nullptr && event->button() == Qt::RightButton)
{
m_layer->DeleteKey(m_location);
Config::OnConfigChanged();
}
else
{
Derived::mousePressEvent(event);
}
}
const Config::Location m_location;
Config::Layer* m_layer;
};

View File

@ -3,20 +3,16 @@
#include "DolphinQt/Config/ConfigControls/ConfigFloatSlider.h"
#include <QSignalBlocker>
#include "Common/Config/Config.h"
#include "DolphinQt/Settings.h"
ConfigFloatSlider::ConfigFloatSlider(float minimum, float maximum,
const Config::Info<float>& setting, float step)
: ToolTipSlider(Qt::Horizontal), m_minimum(minimum), m_step(step), m_setting(setting)
const Config::Info<float>& setting, float step,
Config::Layer* layer)
: ConfigControl(Qt::Horizontal, setting.GetLocation(), layer), m_minimum(minimum), m_step(step),
m_setting(setting)
{
const float range = maximum - minimum;
const int steps = std::round(range / step);
const int interval = std::round(range / steps);
const int current_value = std::round((Config::Get(m_setting) - minimum) / step);
const int current_value = std::round((ReadValue(setting) - minimum) / step);
setMinimum(0);
setMaximum(steps);
@ -24,25 +20,21 @@ ConfigFloatSlider::ConfigFloatSlider(float minimum, float maximum,
setValue(current_value);
connect(this, &ConfigFloatSlider::valueChanged, this, &ConfigFloatSlider::Update);
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
QFont bf = font();
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
setFont(bf);
const QSignalBlocker blocker(this);
const int value = std::round((Config::Get(m_setting) - m_minimum) / m_step);
setValue(value);
});
}
void ConfigFloatSlider::Update(int value)
{
const float current_value = (m_step * value) + m_minimum;
Config::SetBaseOrCurrent(m_setting, current_value);
SaveValue(m_setting, current_value);
}
float ConfigFloatSlider::GetValue() const
{
return (m_step * value()) + m_minimum;
}
void ConfigFloatSlider::OnConfigChanged()
{
setValue(std::round((ReadValue(m_setting) - m_minimum) / m_step));
}

View File

@ -3,28 +3,29 @@
#pragma once
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h"
namespace Config
{
template <typename T>
class Info;
}
#include "Common/Config/ConfigInfo.h"
// Automatically converts an int slider into a float one.
// Do not read the int values or ranges directly from it.
class ConfigFloatSlider : public ToolTipSlider
class ConfigFloatSlider final : public ConfigControl<ToolTipSlider>
{
Q_OBJECT
public:
ConfigFloatSlider(float minimum, float maximum, const Config::Info<float>& setting, float step);
ConfigFloatSlider(float minimum, float maximum, const Config::Info<float>& setting, float step,
Config::Layer* layer = nullptr);
void Update(int value);
// Returns the adjusted float value
float GetValue() const;
protected:
void OnConfigChanged() override;
private:
float m_minimum;
float m_step;
const Config::Info<float>& m_setting;
const Config::Info<float> m_setting;
};

View File

@ -3,33 +3,39 @@
#include "DolphinQt/Config/ConfigControls/ConfigInteger.h"
#include <QSignalBlocker>
#include "Common/Config/Config.h"
#include "DolphinQt/Settings.h"
ConfigInteger::ConfigInteger(int minimum, int maximum, const Config::Info<int>& setting, int step)
: ToolTipSpinBox(), m_setting(setting)
: ConfigInteger(minimum, maximum, setting, nullptr, step)
{
}
ConfigInteger::ConfigInteger(int minimum, int maximum, const Config::Info<int>& setting,
Config::Layer* layer, int step)
: ConfigControl(setting.GetLocation(), layer), m_setting(setting)
{
setMinimum(minimum);
setMaximum(maximum);
setSingleStep(step);
setValue(Config::Get(setting));
setValue(ReadValue(setting));
connect(this, &ConfigInteger::valueChanged, this, &ConfigInteger::Update);
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
QFont bf = font();
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
setFont(bf);
const QSignalBlocker blocker(this);
setValue(Config::Get(m_setting));
});
}
void ConfigInteger::Update(int value)
{
Config::SetBaseOrCurrent(m_setting, value);
SaveValue(m_setting, value);
}
void ConfigInteger::OnConfigChanged()
{
setValue(ReadValue(m_setting));
}
ConfigIntegerLabel::ConfigIntegerLabel(const QString& text, ConfigInteger* widget)
: QLabel(text), m_widget(QPointer<ConfigInteger>(widget))
{
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this]() {
// Label shares font changes with ConfigInteger to mark game ini settings.
if (m_widget)
setFont(m_widget->font());
});
}

View File

@ -3,21 +3,38 @@
#pragma once
#include <QLabel>
#include <QPointer>
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipSpinBox.h"
namespace Config
{
template <typename T>
class Info;
}
#include "Common/Config/ConfigInfo.h"
class ConfigInteger : public ToolTipSpinBox
class ConfigInteger final : public ConfigControl<ToolTipSpinBox>
{
Q_OBJECT
public:
ConfigInteger(int minimum, int maximum, const Config::Info<int>& setting, int step = 1);
ConfigInteger(int minimum, int maximum, const Config::Info<int>& setting, Config::Layer* layer,
int step = 1);
void Update(int value);
protected:
void OnConfigChanged() override;
private:
const Config::Info<int>& m_setting;
const Config::Info<int> m_setting;
};
class ConfigIntegerLabel final : public QLabel
{
Q_OBJECT
public:
ConfigIntegerLabel(const QString& text, ConfigInteger* widget);
private:
QPointer<ConfigInteger> m_widget;
};

View File

@ -3,33 +3,21 @@
#include "DolphinQt/Config/ConfigControls/ConfigRadio.h"
#include <QSignalBlocker>
#include "Common/Config/Config.h"
#include "DolphinQt/Settings.h"
ConfigRadioInt::ConfigRadioInt(const QString& label, const Config::Info<int>& setting, int value)
: ToolTipRadioButton(label), m_setting(setting), m_value(value)
ConfigRadioInt::ConfigRadioInt(const QString& label, const Config::Info<int>& setting, int value,
Config::Layer* layer)
: ConfigControl(label, setting.GetLocation(), layer), m_setting(setting), m_value(value)
{
setChecked(Config::Get(m_setting) == m_value);
setChecked(ReadValue(setting) == value);
connect(this, &QRadioButton::toggled, this, &ConfigRadioInt::Update);
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
QFont bf = font();
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
setFont(bf);
const QSignalBlocker blocker(this);
setChecked(Config::Get(m_setting) == m_value);
});
}
void ConfigRadioInt::Update()
{
if (isChecked())
{
Config::SetBaseOrCurrent(m_setting, m_value);
SaveValue(m_setting, m_value);
emit OnSelected(m_value);
}
else
@ -37,3 +25,8 @@ void ConfigRadioInt::Update()
emit OnDeselected(m_value);
}
}
void ConfigRadioInt::OnConfigChanged()
{
setChecked(ReadValue(m_setting) == m_value);
}

View File

@ -3,15 +3,17 @@
#pragma once
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipRadioButton.h"
#include "Common/Config/Config.h"
#include "Common/Config/ConfigInfo.h"
class ConfigRadioInt : public ToolTipRadioButton
class ConfigRadioInt final : public ConfigControl<ToolTipRadioButton>
{
Q_OBJECT
public:
ConfigRadioInt(const QString& label, const Config::Info<int>& setting, int value);
ConfigRadioInt(const QString& label, const Config::Info<int>& setting, int value,
Config::Layer* layer = nullptr);
signals:
// Since selecting a new radio button deselects the old one, ::toggled will generate two signals.
@ -19,9 +21,12 @@ signals:
void OnSelected(int new_value);
void OnDeselected(int old_value);
protected:
void OnConfigChanged() override;
private:
void Update();
Config::Info<int> m_setting;
const Config::Info<int> m_setting;
int m_value;
};

View File

@ -1,36 +1,45 @@
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QFont>
#include "DolphinQt/Config/ConfigControls/ConfigSlider.h"
#include <QSignalBlocker>
#include "Common/Config/Config.h"
#include "DolphinQt/Settings.h"
ConfigSlider::ConfigSlider(int minimum, int maximum, const Config::Info<int>& setting, int tick)
: ToolTipSlider(Qt::Horizontal), m_setting(setting)
: ConfigSlider(minimum, maximum, setting, nullptr, tick)
{
}
ConfigSlider::ConfigSlider(int minimum, int maximum, const Config::Info<int>& setting,
Config::Layer* layer, int tick)
: ConfigControl(Qt::Horizontal, setting.GetLocation(), layer), m_setting(setting)
{
setMinimum(minimum);
setMaximum(maximum);
setTickInterval(tick);
setValue(Config::Get(setting));
setValue(ReadValue(setting));
connect(this, &ConfigSlider::valueChanged, this, &ConfigSlider::Update);
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
QFont bf = font();
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
setFont(bf);
const QSignalBlocker blocker(this);
setValue(Config::Get(m_setting));
});
}
void ConfigSlider::Update(int value)
{
Config::SetBaseOrCurrent(m_setting, value);
SaveValue(m_setting, value);
}
void ConfigSlider::OnConfigChanged()
{
setValue(ReadValue(m_setting));
}
ConfigSliderLabel::ConfigSliderLabel(const QString& text, ConfigSlider* slider)
: QLabel(text), m_slider(QPointer<ConfigSlider>(slider))
{
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this]() {
// Label shares font changes with slider to mark game ini settings.
if (m_slider)
setFont(m_slider->font());
});
}

View File

@ -3,21 +3,38 @@
#pragma once
#include <QLabel>
#include <QPointer>
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h"
namespace Config
{
template <typename T>
class Info;
}
#include "Common/Config/ConfigInfo.h"
class ConfigSlider : public ToolTipSlider
class ConfigSlider final : public ConfigControl<ToolTipSlider>
{
Q_OBJECT
public:
ConfigSlider(int minimum, int maximum, const Config::Info<int>& setting, int tick = 0);
ConfigSlider(int minimum, int maximum, const Config::Info<int>& setting, Config::Layer* layer,
int tick = 0);
void Update(int value);
protected:
void OnConfigChanged() override;
private:
const Config::Info<int>& m_setting;
const Config::Info<int> m_setting;
};
class ConfigSliderLabel final : public QLabel
{
Q_OBJECT
public:
ConfigSliderLabel(const QString& text, ConfigSlider* slider);
private:
QPointer<ConfigSlider> m_slider;
};

View File

@ -3,37 +3,39 @@
#include "DolphinQt/Config/GameConfigWidget.h"
#include <QCheckBox>
#include <QComboBox>
#include <QFont>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel>
#include <QPushButton>
#include <QSlider>
#include <QSpinBox>
#include <QTabWidget>
#include <QTimer>
#include <QToolTip>
#include <QVBoxLayout>
#include "Common/CommonPaths.h"
#include "Common/Config/Config.h"
#include "Common/Config/Layer.h"
#include "Common/FileUtil.h"
#include "Core/Config/GraphicsSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/ConfigLoaders/GameConfigLoader.h"
#include "Core/ConfigManager.h"
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include "DolphinQt/Config/ConfigControls/ConfigChoice.h"
#include "DolphinQt/Config/ConfigControls/ConfigInteger.h"
#include "DolphinQt/Config/ConfigControls/ConfigRadio.h"
#include "DolphinQt/Config/ConfigControls/ConfigSlider.h"
#include "DolphinQt/Config/GameConfigEdit.h"
#include "DolphinQt/Config/Graphics/AdvancedWidget.h"
#include "DolphinQt/Config/Graphics/EnhancementsWidget.h"
#include "DolphinQt/Config/Graphics/GeneralWidget.h"
#include "DolphinQt/Config/Graphics/HacksWidget.h"
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
#include "UICommon/GameFile.h"
constexpr int DETERMINISM_NOT_SET_INDEX = 0;
constexpr int DETERMINISM_AUTO_INDEX = 1;
constexpr int DETERMINISM_NONE_INDEX = 2;
constexpr int DETERMINISM_FAKE_COMPLETION_INDEX = 3;
constexpr const char* DETERMINISM_NOT_SET_STRING = "";
constexpr const char* DETERMINISM_AUTO_STRING = "auto";
constexpr const char* DETERMINISM_NONE_STRING = "none";
constexpr const char* DETERMINISM_FAKE_COMPLETION_STRING = "fake-completion";
static void PopulateTab(QTabWidget* tab, const std::string& path, std::string& game_id,
u16 revision, bool read_only)
{
@ -55,46 +57,62 @@ GameConfigWidget::GameConfigWidget(const UICommon::GameFile& game) : m_game(game
m_gameini_local_path =
QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini");
m_layer = std::make_unique<Config::Layer>(
ConfigLoaders::GenerateLocalGameConfigLoader(m_game_id, m_game.GetRevision()));
m_global_layer = std::make_unique<Config::Layer>(
ConfigLoaders::GenerateGlobalGameConfigLoader(m_game_id, m_game.GetRevision()));
CreateWidgets();
LoadSettings();
ConnectWidgets();
connect(&Settings::Instance(), &Settings::ConfigChanged, this, &GameConfigWidget::LoadSettings);
PopulateTab(m_default_tab, File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP, m_game_id,
m_game.GetRevision(), true);
PopulateTab(m_local_tab, File::GetUserPath(D_GAMESETTINGS_IDX), m_game_id, m_game.GetRevision(),
false);
// Always give the user the opportunity to create a new INI
if (m_local_tab->count() == 0)
bool game_id_tab = false;
for (int i = 0; i < m_local_tab->count(); i++)
{
if (m_local_tab->tabText(i).toStdString() == m_game_id + ".ini")
game_id_tab = true;
}
if (game_id_tab == false)
{
// Create new local game ini tab if none exists.
auto* edit = new GameConfigEdit(
nullptr, QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini"),
false);
m_local_tab->addTab(edit, QString::fromStdString(m_game_id + ".ini"));
}
// Fails to change font if it's directly called at this time. Is there a better workaround?
QTimer::singleShot(100, this, [this]() {
SetItalics();
Config::OnConfigChanged();
});
}
void GameConfigWidget::CreateWidgets()
{
// General
m_refresh_config = new QPushButton(tr("Refresh"));
Config::Layer* layer = m_layer.get();
// Core
auto* core_box = new QGroupBox(tr("Core"));
auto* core_layout = new QGridLayout;
core_box->setLayout(core_layout);
m_enable_dual_core = new QCheckBox(tr("Enable Dual Core"));
m_enable_mmu = new QCheckBox(tr("Enable MMU"));
m_enable_fprf = new QCheckBox(tr("Enable FPRF"));
m_sync_gpu = new QCheckBox(tr("Synchronize GPU thread"));
m_emulate_disc_speed = new QCheckBox(tr("Emulate Disc Speed"));
m_use_dsp_hle = new QCheckBox(tr("DSP HLE (fast)"));
m_manual_texture_sampling = new QCheckBox(tr("Manual Texture Sampling"));
m_deterministic_dual_core = new QComboBox;
m_enable_dual_core = new ConfigBool(tr("Enable Dual Core"), Config::MAIN_CPU_THREAD, layer);
m_enable_mmu = new ConfigBool(tr("Enable MMU"), Config::MAIN_MMU, layer);
m_enable_fprf = new ConfigBool(tr("Enable FPRF"), Config::MAIN_FPRF, layer);
m_sync_gpu = new ConfigBool(tr("Synchronize GPU thread"), Config::MAIN_SYNC_GPU, layer);
m_emulate_disc_speed =
new ConfigBool(tr("Emulate Disc Speed"), Config::MAIN_FAST_DISC_SPEED, layer, true);
m_use_dsp_hle = new ConfigBool(tr("DSP HLE (fast)"), Config::MAIN_DSP_HLE, layer);
for (const auto& item : {tr("Not Set"), tr("auto"), tr("none"), tr("fake-completion")})
m_deterministic_dual_core->addItem(item);
const std::vector<std::string> choice{tr("auto").toStdString(), tr("none").toStdString(),
tr("fake-completion").toStdString()};
m_deterministic_dual_core =
new ConfigStringChoice(choice, Config::MAIN_GPU_DETERMINISM_MODE, layer);
m_enable_mmu->setToolTip(tr(
"Enables the Memory Management Unit, needed for some games. (ON = Compatible, OFF = Fast)"));
@ -114,24 +132,18 @@ void GameConfigWidget::CreateWidgets()
core_layout->addWidget(m_sync_gpu, 3, 0);
core_layout->addWidget(m_emulate_disc_speed, 4, 0);
core_layout->addWidget(m_use_dsp_hle, 5, 0);
core_layout->addWidget(m_manual_texture_sampling, 6, 0);
core_layout->addWidget(new QLabel(tr("Deterministic dual core:")), 7, 0);
core_layout->addWidget(m_deterministic_dual_core, 7, 1);
core_layout->addWidget(new QLabel(tr("Deterministic dual core:")), 6, 0);
core_layout->addWidget(m_deterministic_dual_core, 6, 1);
// Stereoscopy
auto* stereoscopy_box = new QGroupBox(tr("Stereoscopy"));
auto* stereoscopy_layout = new QGridLayout;
stereoscopy_box->setLayout(stereoscopy_layout);
m_depth_slider = new QSlider(Qt::Horizontal);
m_depth_slider->setMinimum(100);
m_depth_slider->setMaximum(200);
m_convergence_spin = new QSpinBox;
m_convergence_spin->setMinimum(0);
m_convergence_spin->setMaximum(INT32_MAX);
m_use_monoscopic_shadows = new QCheckBox(tr("Monoscopic Shadows"));
m_depth_slider = new ConfigSlider(100, 200, Config::GFX_STEREO_DEPTH_PERCENTAGE, layer);
m_convergence_spin = new ConfigInteger(0, INT32_MAX, Config::GFX_STEREO_CONVERGENCE, layer);
m_use_monoscopic_shadows =
new ConfigBool(tr("Monoscopic Shadows"), Config::GFX_STEREO_EFB_MONO_DEPTH, layer);
m_depth_slider->setToolTip(
tr("This value is multiplied with the depth set in the graphics configuration."));
@ -140,37 +152,23 @@ void GameConfigWidget::CreateWidgets()
m_use_monoscopic_shadows->setToolTip(
tr("Use a single depth buffer for both eyes. Needed for a few games."));
stereoscopy_layout->addWidget(new QLabel(tr("Depth Percentage:")), 0, 0);
stereoscopy_layout->addWidget(new ConfigSliderLabel(tr("Depth Percentage:"), m_depth_slider), 0,
0);
stereoscopy_layout->addWidget(m_depth_slider, 0, 1);
stereoscopy_layout->addWidget(new QLabel(tr("Convergence:")), 1, 0);
stereoscopy_layout->addWidget(new ConfigIntegerLabel(tr("Convergence:"), m_convergence_spin), 1,
0);
stereoscopy_layout->addWidget(m_convergence_spin, 1, 1);
stereoscopy_layout->addWidget(m_use_monoscopic_shadows, 2, 0);
auto* settings_box = new QGroupBox(tr("Game-Specific Settings"));
auto* settings_layout = new QVBoxLayout;
settings_box->setLayout(settings_layout);
settings_layout->addWidget(
new QLabel(tr("These settings override core Dolphin settings.\nUndetermined means the game "
"uses Dolphin's setting.")));
settings_layout->addWidget(core_box);
settings_layout->addWidget(stereoscopy_box);
auto* general_layout = new QGridLayout;
general_layout->addWidget(settings_box, 0, 0, 1, -1);
general_layout->addWidget(m_refresh_config, 1, 0, 1, -1);
for (QCheckBox* item :
{m_enable_dual_core, m_enable_mmu, m_enable_fprf, m_sync_gpu, m_emulate_disc_speed,
m_use_dsp_hle, m_manual_texture_sampling, m_use_monoscopic_shadows})
item->setTristate(true);
auto* general_layout = new QVBoxLayout;
general_layout->addWidget(core_box);
general_layout->addWidget(stereoscopy_box);
general_layout->addStretch();
auto* general_widget = new QWidget;
general_widget->setLayout(general_layout);
// Advanced
// Editor tab
auto* advanced_layout = new QVBoxLayout;
auto* default_group = new QGroupBox(tr("Default Config (Read Only)"));
@ -196,207 +194,186 @@ void GameConfigWidget::CreateWidgets()
auto* layout = new QVBoxLayout;
auto* tab_widget = new QTabWidget;
tab_widget->addTab(general_widget, tr("General"));
tab_widget->addTab(advanced_widget, tr("Editor"));
// GFX settings tabs. Placed in a QWidget for consistent margins.
auto* gfx_tab_holder = new QWidget;
auto* gfx_layout = new QVBoxLayout;
gfx_tab_holder->setLayout(gfx_layout);
tab_widget->addTab(gfx_tab_holder, tr("Graphics"));
auto* gfx_tabs = new QTabWidget;
gfx_tabs->addTab(GetWrappedWidget(new GeneralWidget(this, m_layer.get()), this, 125, 100),
tr("General"));
gfx_tabs->addTab(GetWrappedWidget(new EnhancementsWidget(this, m_layer.get()), this, 125, 100),
tr("Enhancements"));
gfx_tabs->addTab(GetWrappedWidget(new HacksWidget(this, m_layer.get()), this, 125, 100),
tr("Hacks"));
gfx_tabs->addTab(GetWrappedWidget(new AdvancedWidget(this, m_layer.get()), this, 125, 100),
tr("Advanced"));
const int editor_index = tab_widget->addTab(advanced_widget, tr("Editor"));
gfx_layout->addWidget(gfx_tabs);
connect(tab_widget, &QTabWidget::currentChanged, this, [this, editor_index](int index) {
// Update the ini editor after editing other tabs.
if (index == editor_index)
{
// Layer only auto-saves when it is destroyed.
m_layer->Save();
// There can be multiple ini loaded for a game, only replace the one related to the game
// ini being edited.
for (int i = 0; i < m_local_tab->count(); i++)
{
if (m_local_tab->tabText(i).toStdString() == m_game_id + ".ini")
{
m_local_tab->removeTab(i);
auto* edit = new GameConfigEdit(
nullptr,
QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini"),
false);
m_local_tab->insertTab(i, edit, QString::fromStdString(m_game_id + ".ini"));
break;
}
}
}
// Update other tabs after using ini editor.
if (m_prev_tab_index == editor_index)
{
// Load won't clear deleted keys, so everything is wiped before loading.
m_layer->DeleteAllKeys();
m_layer->Load();
Config::OnConfigChanged();
}
m_prev_tab_index = index;
});
const QString help_msg = tr(
"Italics mark default game settings, bold marks user settings.\nRight-click to remove user "
"settings.\nGraphics tabs don't display the value of a default game setting.\nAnti-Aliasing "
"settings are disabled when the global graphics backend doesn't "
"match the game setting.");
auto help_icon = style()->standardIcon(QStyle::SP_MessageBoxQuestion);
auto* help_label = new QLabel(tr("These settings override core Dolphin settings."));
help_label->setToolTip(help_msg);
auto help_label_icon = new QLabel();
help_label_icon->setPixmap(help_icon.pixmap(12, 12));
help_label_icon->setToolTip(help_msg);
auto* help_layout = new QHBoxLayout();
help_layout->addWidget(help_label);
help_layout->addWidget(help_label_icon);
help_layout->addStretch();
layout->addLayout(help_layout);
layout->addWidget(tab_widget);
setLayout(layout);
}
void GameConfigWidget::ConnectWidgets()
GameConfigWidget::~GameConfigWidget()
{
// Buttons
connect(m_refresh_config, &QPushButton::clicked, this, &GameConfigWidget::LoadSettings);
// Destructor saves the layer to file.
m_layer.reset();
for (QCheckBox* box :
{m_enable_dual_core, m_enable_mmu, m_enable_fprf, m_sync_gpu, m_emulate_disc_speed,
m_use_dsp_hle, m_manual_texture_sampling, m_use_monoscopic_shadows})
connect(box, &QCheckBox::stateChanged, this, &GameConfigWidget::SaveSettings);
connect(m_deterministic_dual_core, &QComboBox::currentIndexChanged, this,
&GameConfigWidget::SaveSettings);
connect(m_depth_slider, &QSlider::valueChanged, this, &GameConfigWidget::SaveSettings);
connect(m_convergence_spin, &QSpinBox::valueChanged, this, &GameConfigWidget::SaveSettings);
}
void GameConfigWidget::LoadCheckBox(QCheckBox* checkbox, const std::string& section,
const std::string& key, bool reverse)
{
bool checked;
if (m_gameini_local.GetOrCreateSection(section)->Get(key, &checked))
return checkbox->setCheckState(checked ^ reverse ? Qt::Checked : Qt::Unchecked);
if (m_gameini_default.GetOrCreateSection(section)->Get(key, &checked))
return checkbox->setCheckState(checked ^ reverse ? Qt::Checked : Qt::Unchecked);
checkbox->setCheckState(Qt::PartiallyChecked);
}
void GameConfigWidget::SaveCheckBox(QCheckBox* checkbox, const std::string& section,
const std::string& key, bool reverse)
{
// Delete any existing entries from the local gameini if checkbox is undetermined.
// Otherwise, write the current value to the local gameini if the value differs from the default
// gameini values.
// Delete any existing entry from the local gameini if the value does not differ from the default
// gameini value.
if (checkbox->checkState() == Qt::PartiallyChecked)
// If a game is running and the game properties window is closed, update local game layer with
// any new changes. Not sure if doing it more frequently is safe.
auto local_layer = Config::GetLayer(Config::LayerType::LocalGame);
if (local_layer && SConfig::GetInstance().GetGameID() == m_game_id)
{
m_gameini_local.DeleteKey(section, key);
return;
local_layer->DeleteAllKeys();
local_layer->Load();
Config::OnConfigChanged();
}
bool checked = (checkbox->checkState() == Qt::Checked) ^ reverse;
if (m_gameini_default.Exists(section, key))
{
bool default_value;
m_gameini_default.GetOrCreateSection(section)->Get(key, &default_value);
if (default_value != checked)
m_gameini_local.GetOrCreateSection(section)->Set(key, checked);
else
m_gameini_local.DeleteKey(section, key);
return;
}
m_gameini_local.GetOrCreateSection(section)->Set(key, checked);
// Delete empty configs
if (File::GetSize(m_gameini_local_path.toStdString()) == 0)
File::Delete(m_gameini_local_path.toStdString());
}
void GameConfigWidget::LoadSettings()
{
// Reload config
m_gameini_local = SConfig::LoadLocalGameIni(m_game_id, m_game.GetRevision());
m_gameini_default = SConfig::LoadDefaultGameIni(m_game_id, m_game.GetRevision());
// Load globals
auto update_bool = [this](auto config, bool reverse = false) {
const Config::Location& setting = config->GetLocation();
// Load game-specific settings
// Don't overwrite local with global
if (m_layer->Exists(setting) || !m_global_layer->Exists(setting))
return;
// Core
LoadCheckBox(m_enable_dual_core, "Core", "CPUThread");
LoadCheckBox(m_enable_mmu, "Core", "MMU");
LoadCheckBox(m_enable_fprf, "Core", "FPRF");
LoadCheckBox(m_sync_gpu, "Core", "SyncGPU");
LoadCheckBox(m_emulate_disc_speed, "Core", "FastDiscSpeed", true);
LoadCheckBox(m_use_dsp_hle, "Core", "DSPHLE");
LoadCheckBox(m_manual_texture_sampling, "Video_Hacks", "FastTextureSampling", true);
std::optional<bool> value = m_global_layer->Get<bool>(config->GetLocation());
std::string determinism_mode;
if (value.has_value())
{
const QSignalBlocker blocker(config);
config->setChecked(value.value() ^ reverse);
}
};
int determinism_index = DETERMINISM_NOT_SET_INDEX;
auto update_int = [this](auto config) {
const Config::Location& setting = config->GetLocation();
m_gameini_default.GetIfExists("Core", "GPUDeterminismMode", &determinism_mode);
m_gameini_local.GetIfExists("Core", "GPUDeterminismMode", &determinism_mode);
if (m_layer->Exists(setting) || !m_global_layer->Exists(setting))
return;
if (determinism_mode == DETERMINISM_AUTO_STRING)
std::optional<int> value = m_global_layer->Get<int>(setting);
if (value.has_value())
{
const QSignalBlocker blocker(config);
config->setValue(value.value());
}
};
for (ConfigBool* config : {m_enable_dual_core, m_enable_mmu, m_enable_fprf, m_sync_gpu,
m_use_dsp_hle, m_use_monoscopic_shadows})
{
determinism_index = DETERMINISM_AUTO_INDEX;
}
else if (determinism_mode == DETERMINISM_NONE_STRING)
{
determinism_index = DETERMINISM_NONE_INDEX;
}
else if (determinism_mode == DETERMINISM_FAKE_COMPLETION_STRING)
{
determinism_index = DETERMINISM_FAKE_COMPLETION_INDEX;
update_bool(config);
}
m_deterministic_dual_core->setCurrentIndex(determinism_index);
update_bool(m_emulate_disc_speed, true);
// Stereoscopy
int depth_percentage = 100;
m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage", &depth_percentage);
m_gameini_local.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage", &depth_percentage);
m_depth_slider->setValue(depth_percentage);
int convergence = 0;
m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoConvergence", &convergence);
m_gameini_local.GetIfExists("Video_Stereoscopy", "StereoConvergence", &convergence);
m_convergence_spin->setValue(convergence);
LoadCheckBox(m_use_monoscopic_shadows, "Video_Stereoscopy", "StereoEFBMonoDepth");
update_int(m_depth_slider);
update_int(m_convergence_spin);
}
void GameConfigWidget::SaveSettings()
void GameConfigWidget::SetItalics()
{
// Core
SaveCheckBox(m_enable_dual_core, "Core", "CPUThread");
SaveCheckBox(m_enable_mmu, "Core", "MMU");
SaveCheckBox(m_enable_fprf, "Core", "FPRF");
SaveCheckBox(m_sync_gpu, "Core", "SyncGPU");
SaveCheckBox(m_emulate_disc_speed, "Core", "FastDiscSpeed", true);
SaveCheckBox(m_use_dsp_hle, "Core", "DSPHLE");
SaveCheckBox(m_manual_texture_sampling, "Video_Hacks", "FastTextureSampling", true);
// Mark system game settings with italics. Called once because it should never change.
auto italics = [this](auto config) {
if (!m_global_layer->Exists(config->GetLocation()))
return;
int determinism_num = m_deterministic_dual_core->currentIndex();
QFont ifont = config->font();
ifont.setItalic(true);
config->setFont(ifont);
};
std::string determinism_mode = DETERMINISM_NOT_SET_STRING;
for (auto* config : findChildren<ConfigBool*>())
italics(config);
for (auto* config : findChildren<ConfigSlider*>())
italics(config);
for (auto* config : findChildren<ConfigInteger*>())
italics(config);
for (auto* config : findChildren<ConfigRadioInt*>())
italics(config);
for (auto* config : findChildren<ConfigChoice*>())
italics(config);
for (auto* config : findChildren<ConfigStringChoice*>())
italics(config);
switch (determinism_num)
for (auto* config : findChildren<ConfigComplexChoice*>())
{
case DETERMINISM_AUTO_INDEX:
determinism_mode = DETERMINISM_AUTO_STRING;
break;
case DETERMINISM_NONE_INDEX:
determinism_mode = DETERMINISM_NONE_STRING;
break;
case DETERMINISM_FAKE_COMPLETION_INDEX:
determinism_mode = DETERMINISM_FAKE_COMPLETION_STRING;
break;
}
if (determinism_mode != DETERMINISM_NOT_SET_STRING)
{
std::string default_mode = DETERMINISM_NOT_SET_STRING;
if (!(m_gameini_default.GetIfExists("Core", "GPUDeterminismMode", &default_mode) &&
default_mode == determinism_mode))
std::pair<Config::Location, Config::Location> location = config->GetLocation();
if (m_global_layer->Exists(location.first) || m_global_layer->Exists(location.second))
{
m_gameini_local.GetOrCreateSection("Core")->Set("GPUDeterminismMode", determinism_mode);
QFont ifont = config->font();
ifont.setItalic(true);
config->setFont(ifont);
}
}
else
{
m_gameini_local.DeleteKey("Core", "GPUDeterminismMode");
}
// Stereoscopy
int depth_percentage = m_depth_slider->value();
if (depth_percentage != 100)
{
int default_value = 0;
if (!(m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage",
&default_value) &&
default_value == depth_percentage))
{
m_gameini_local.GetOrCreateSection("Video_Stereoscopy")
->Set("StereoDepthPercentage", depth_percentage);
}
}
int convergence = m_convergence_spin->value();
if (convergence != 0)
{
int default_value = 0;
if (!(m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoConvergence", &default_value) &&
default_value == convergence))
{
m_gameini_local.GetOrCreateSection("Video_Stereoscopy")
->Set("StereoConvergence", convergence);
}
}
SaveCheckBox(m_use_monoscopic_shadows, "Video_Stereoscopy", "StereoEFBMonoDepth");
bool success = m_gameini_local.Save(m_gameini_local_path.toStdString());
// If the resulting file is empty, delete it. Kind of a hack, but meh.
if (success && File::GetSize(m_gameini_local_path.toStdString()) == 0)
File::Delete(m_gameini_local_path.toStdString());
}

View File

@ -7,19 +7,21 @@
#include <QString>
#include <QWidget>
#include "Common/IniFile.h"
namespace UICommon
{
class GameFile;
}
class QCheckBox;
class QComboBox;
namespace Config
{
class Layer;
} // namespace Config
class ConfigBool;
class ConfigInteger;
class ConfigSlider;
class ConfigStringChoice;
class QPushButton;
class QSlider;
class QSpinBox;
class QTabWidget;
class GameConfigWidget : public QWidget
@ -27,45 +29,33 @@ class GameConfigWidget : public QWidget
Q_OBJECT
public:
explicit GameConfigWidget(const UICommon::GameFile& game);
~GameConfigWidget();
private:
void CreateWidgets();
void ConnectWidgets();
void LoadSettings();
void SaveSettings();
void SetItalics();
void SaveCheckBox(QCheckBox* checkbox, const std::string& section, const std::string& key,
bool reverse = false);
void LoadCheckBox(QCheckBox* checkbox, const std::string& section, const std::string& key,
bool reverse = false);
QString m_gameini_sys_path;
QString m_gameini_local_path;
QTabWidget* m_default_tab;
QTabWidget* m_local_tab;
QCheckBox* m_enable_dual_core;
QCheckBox* m_enable_mmu;
QCheckBox* m_enable_fprf;
QCheckBox* m_sync_gpu;
QCheckBox* m_emulate_disc_speed;
QCheckBox* m_use_dsp_hle;
QCheckBox* m_use_monoscopic_shadows;
QCheckBox* m_manual_texture_sampling;
ConfigBool* m_enable_dual_core;
ConfigBool* m_enable_mmu;
ConfigBool* m_enable_fprf;
ConfigBool* m_sync_gpu;
ConfigBool* m_emulate_disc_speed;
ConfigBool* m_use_dsp_hle;
ConfigBool* m_use_monoscopic_shadows;
QPushButton* m_refresh_config;
QComboBox* m_deterministic_dual_core;
QSlider* m_depth_slider;
QSpinBox* m_convergence_spin;
ConfigStringChoice* m_deterministic_dual_core;
ConfigSlider* m_depth_slider;
ConfigInteger* m_convergence_spin;
const UICommon::GameFile& m_game;
std::string m_game_id;
Common::IniFile m_gameini_local;
Common::IniFile m_gameini_default;
std::unique_ptr<Config::Layer> m_layer;
std::unique_ptr<Config::Layer> m_global_layer;
int m_prev_tab_index = 0;
};

View File

@ -3,11 +3,9 @@
#include "DolphinQt/Config/Graphics/AdvancedWidget.h"
#include <QCheckBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QSpinBox>
#include <QVBoxLayout>
#include "Core/Config/GraphicsSettings.h"
@ -19,6 +17,7 @@
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include "DolphinQt/Config/ConfigControls/ConfigChoice.h"
#include "DolphinQt/Config/ConfigControls/ConfigInteger.h"
#include "DolphinQt/Config/GameConfigWidget.h"
#include "DolphinQt/Config/Graphics/GraphicsWindow.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
#include "DolphinQt/QtUtils/SignalBlocking.h"
@ -29,7 +28,6 @@
AdvancedWidget::AdvancedWidget(GraphicsWindow* parent)
{
CreateWidgets();
LoadSettings();
ConnectWidgets();
AddDescriptions();
@ -37,17 +35,30 @@ AdvancedWidget::AdvancedWidget(GraphicsWindow* parent)
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) {
OnEmulationStateChanged(state != Core::State::Uninitialized);
});
connect(m_manual_texture_sampling, &QCheckBox::toggled, [this, parent] {
SaveSettings();
emit parent->UseFastTextureSamplingChanged();
});
connect(m_manual_texture_sampling, &QCheckBox::toggled,
[this, parent] { emit parent->UseFastTextureSamplingChanged(); });
OnBackendChanged();
OnEmulationStateChanged(!Core::IsUninitialized(Core::System::GetInstance()));
}
AdvancedWidget::AdvancedWidget(GameConfigWidget* parent, Config::Layer* layer) : m_game_layer(layer)
{
CreateWidgets();
ConnectWidgets();
AddDescriptions();
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) {
OnEmulationStateChanged(state != Core::State::Uninitialized);
});
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()) !=
Core::State::Uninitialized);
}
void AdvancedWidget::CreateWidgets()
{
const bool local_edit = m_game_layer != nullptr;
auto* main_layout = new QVBoxLayout;
// Performance
@ -55,17 +66,19 @@ void AdvancedWidget::CreateWidgets()
auto* performance_layout = new QGridLayout();
performance_box->setLayout(performance_layout);
m_show_fps = new ConfigBool(tr("Show FPS"), Config::GFX_SHOW_FPS);
m_show_ftimes = new ConfigBool(tr("Show Frame Times"), Config::GFX_SHOW_FTIMES);
m_show_vps = new ConfigBool(tr("Show VPS"), Config::GFX_SHOW_VPS);
m_show_vtimes = new ConfigBool(tr("Show VBlank Times"), Config::GFX_SHOW_VTIMES);
m_show_graphs = new ConfigBool(tr("Show Performance Graphs"), Config::GFX_SHOW_GRAPHS);
m_show_speed = new ConfigBool(tr("Show % Speed"), Config::GFX_SHOW_SPEED);
m_show_speed_colors = new ConfigBool(tr("Show Speed Colors"), Config::GFX_SHOW_SPEED_COLORS);
m_perf_samp_window = new ConfigInteger(0, 10000, Config::GFX_PERF_SAMP_WINDOW, 100);
m_show_fps = new ConfigBool(tr("Show FPS"), Config::GFX_SHOW_FPS, m_game_layer);
m_show_ftimes = new ConfigBool(tr("Show Frame Times"), Config::GFX_SHOW_FTIMES, m_game_layer);
m_show_vps = new ConfigBool(tr("Show VPS"), Config::GFX_SHOW_VPS, m_game_layer);
m_show_vtimes = new ConfigBool(tr("Show VBlank Times"), Config::GFX_SHOW_VTIMES, m_game_layer);
m_show_graphs =
new ConfigBool(tr("Show Performance Graphs"), Config::GFX_SHOW_GRAPHS, m_game_layer);
m_show_speed = new ConfigBool(tr("Show % Speed"), Config::GFX_SHOW_SPEED, m_game_layer);
m_show_speed_colors =
new ConfigBool(tr("Show Speed Colors"), Config::GFX_SHOW_SPEED_COLORS, m_game_layer);
m_perf_samp_window = new ConfigInteger(0, 10000, Config::GFX_PERF_SAMP_WINDOW, m_game_layer, 100);
m_perf_samp_window->SetTitle(tr("Performance Sample Window (ms)"));
m_log_render_time =
new ConfigBool(tr("Log Render Time to File"), Config::GFX_LOG_RENDER_TIME_TO_FILE);
m_log_render_time = new ConfigBool(tr("Log Render Time to File"),
Config::GFX_LOG_RENDER_TIME_TO_FILE, m_game_layer);
performance_layout->addWidget(m_show_fps, 0, 0);
performance_layout->addWidget(m_show_ftimes, 0, 1);
@ -83,14 +96,16 @@ void AdvancedWidget::CreateWidgets()
auto* debugging_layout = new QGridLayout();
debugging_box->setLayout(debugging_layout);
m_enable_wireframe = new ConfigBool(tr("Enable Wireframe"), Config::GFX_ENABLE_WIREFRAME);
m_show_statistics = new ConfigBool(tr("Show Statistics"), Config::GFX_OVERLAY_STATS);
m_show_proj_statistics =
new ConfigBool(tr("Show Projection Statistics"), Config::GFX_OVERLAY_PROJ_STATS);
m_enable_wireframe =
new ConfigBool(tr("Enable Wireframe"), Config::GFX_ENABLE_WIREFRAME, m_game_layer);
m_show_statistics =
new ConfigBool(tr("Show Statistics"), Config::GFX_OVERLAY_STATS, m_game_layer);
m_show_proj_statistics = new ConfigBool(tr("Show Projection Statistics"),
Config::GFX_OVERLAY_PROJ_STATS, m_game_layer);
m_enable_format_overlay =
new ConfigBool(tr("Texture Format Overlay"), Config::GFX_TEXFMT_OVERLAY_ENABLE);
m_enable_api_validation =
new ConfigBool(tr("Enable API Validation Layers"), Config::GFX_ENABLE_VALIDATION_LAYER);
new ConfigBool(tr("Texture Format Overlay"), Config::GFX_TEXFMT_OVERLAY_ENABLE, m_game_layer);
m_enable_api_validation = new ConfigBool(tr("Enable API Validation Layers"),
Config::GFX_ENABLE_VALIDATION_LAYER, m_game_layer);
debugging_layout->addWidget(m_enable_wireframe, 0, 0);
debugging_layout->addWidget(m_show_statistics, 0, 1);
@ -103,14 +118,25 @@ void AdvancedWidget::CreateWidgets()
auto* utility_layout = new QGridLayout();
utility_box->setLayout(utility_layout);
m_load_custom_textures = new ConfigBool(tr("Load Custom Textures"), Config::GFX_HIRES_TEXTURES);
m_prefetch_custom_textures =
new ConfigBool(tr("Prefetch Custom Textures"), Config::GFX_CACHE_HIRES_TEXTURES);
m_load_custom_textures =
new ConfigBool(tr("Load Custom Textures"), Config::GFX_HIRES_TEXTURES, m_game_layer);
m_prefetch_custom_textures = new ConfigBool(tr("Prefetch Custom Textures"),
Config::GFX_CACHE_HIRES_TEXTURES, m_game_layer);
m_prefetch_custom_textures->setEnabled(m_load_custom_textures->isChecked());
m_dump_efb_target = new ConfigBool(tr("Dump EFB Target"), Config::GFX_DUMP_EFB_TARGET);
m_dump_xfb_target = new ConfigBool(tr("Dump XFB Target"), Config::GFX_DUMP_XFB_TARGET);
m_disable_vram_copies =
new ConfigBool(tr("Disable EFB VRAM Copies"), Config::GFX_HACK_DISABLE_COPY_TO_VRAM);
m_enable_graphics_mods = new ToolTipCheckBox(tr("Enable Graphics Mods"));
if (local_edit)
{
// It's hazardous to accidentally set these in a game ini.
m_dump_efb_target->setEnabled(false);
m_dump_xfb_target->setEnabled(false);
}
m_disable_vram_copies = new ConfigBool(tr("Disable EFB VRAM Copies"),
Config::GFX_HACK_DISABLE_COPY_TO_VRAM, m_game_layer);
m_enable_graphics_mods =
new ConfigBool(tr("Enable Graphics Mods"), Config::GFX_MODS_ENABLE, m_game_layer);
utility_layout->addWidget(m_load_custom_textures, 0, 0);
utility_layout->addWidget(m_prefetch_custom_textures, 0, 1);
@ -128,6 +154,16 @@ void AdvancedWidget::CreateWidgets()
m_dump_textures = new ConfigBool(tr("Enable"), Config::GFX_DUMP_TEXTURES);
m_dump_base_textures = new ConfigBool(tr("Dump Base Textures"), Config::GFX_DUMP_BASE_TEXTURES);
m_dump_mip_textures = new ConfigBool(tr("Dump Mip Maps"), Config::GFX_DUMP_MIP_TEXTURES);
m_dump_mip_textures->setEnabled(m_dump_textures->isChecked());
m_dump_base_textures->setEnabled(m_dump_textures->isChecked());
if (local_edit)
{
// It's hazardous to accidentally set dumping in a game ini.
m_dump_textures->setEnabled(false);
m_dump_base_textures->setEnabled(false);
m_dump_mip_textures->setEnabled(false);
}
texture_dump_layout->addWidget(m_dump_textures, 0, 0);
@ -142,18 +178,22 @@ void AdvancedWidget::CreateWidgets()
m_frame_dumps_resolution_type =
new ConfigChoice({tr("Window Resolution"), tr("Aspect Ratio Corrected Internal Resolution"),
tr("Raw Internal Resolution")},
Config::GFX_FRAME_DUMPS_RESOLUTION_TYPE);
m_dump_use_ffv1 = new ConfigBool(tr("Use Lossless Codec (FFV1)"), Config::GFX_USE_FFV1);
m_dump_bitrate = new ConfigInteger(0, 1000000, Config::GFX_BITRATE_KBPS, 1000);
m_png_compression_level = new ConfigInteger(0, 9, Config::GFX_PNG_COMPRESSION_LEVEL);
Config::GFX_FRAME_DUMPS_RESOLUTION_TYPE, m_game_layer);
m_png_compression_level =
new ConfigInteger(0, 9, Config::GFX_PNG_COMPRESSION_LEVEL, m_game_layer);
dump_layout->addWidget(new QLabel(tr("Resolution Type:")), 0, 0);
dump_layout->addWidget(m_frame_dumps_resolution_type, 0, 1);
#if defined(HAVE_FFMPEG)
m_dump_use_ffv1 =
new ConfigBool(tr("Use Lossless Codec (FFV1)"), Config::GFX_USE_FFV1, m_game_layer);
m_dump_bitrate = new ConfigInteger(0, 1000000, Config::GFX_BITRATE_KBPS, m_game_layer, 1000);
m_dump_bitrate->setEnabled(!m_dump_use_ffv1->isChecked());
dump_layout->addWidget(m_dump_use_ffv1, 1, 0);
dump_layout->addWidget(new QLabel(tr("Bitrate (kbps):")), 2, 0);
dump_layout->addWidget(m_dump_bitrate, 2, 1);
#endif
dump_layout->addWidget(new QLabel(tr("PNG Compression Level:")), 3, 0);
m_png_compression_level->SetTitle(tr("PNG Compression Level"));
dump_layout->addWidget(m_png_compression_level, 3, 1);
@ -163,14 +203,16 @@ void AdvancedWidget::CreateWidgets()
auto* misc_layout = new QGridLayout();
misc_box->setLayout(misc_layout);
m_enable_cropping = new ConfigBool(tr("Crop"), Config::GFX_CROP);
m_enable_prog_scan = new ToolTipCheckBox(tr("Enable Progressive Scan"));
m_backend_multithreading =
new ConfigBool(tr("Backend Multithreading"), Config::GFX_BACKEND_MULTITHREADING);
m_enable_cropping = new ConfigBool(tr("Crop"), Config::GFX_CROP, m_game_layer);
m_enable_prog_scan =
new ConfigBool(tr("Enable Progressive Scan"), Config::SYSCONF_PROGRESSIVE_SCAN, m_game_layer);
m_backend_multithreading = new ConfigBool(tr("Backend Multithreading"),
Config::GFX_BACKEND_MULTITHREADING, m_game_layer);
m_prefer_vs_for_point_line_expansion = new ConfigBool(
// i18n: VS is short for vertex shaders.
tr("Prefer VS for Point/Line Expansion"), Config::GFX_PREFER_VS_FOR_LINE_POINT_EXPANSION);
m_cpu_cull = new ConfigBool(tr("Cull Vertices on the CPU"), Config::GFX_CPU_CULL);
tr("Prefer VS for Point/Line Expansion"), Config::GFX_PREFER_VS_FOR_LINE_POINT_EXPANSION,
m_game_layer);
m_cpu_cull = new ConfigBool(tr("Cull Vertices on the CPU"), Config::GFX_CPU_CULL, m_game_layer);
misc_layout->addWidget(m_enable_cropping, 0, 0);
misc_layout->addWidget(m_enable_prog_scan, 0, 1);
@ -179,7 +221,7 @@ void AdvancedWidget::CreateWidgets()
misc_layout->addWidget(m_cpu_cull, 2, 0);
#ifdef _WIN32
m_borderless_fullscreen =
new ConfigBool(tr("Borderless Fullscreen"), Config::GFX_BORDERLESS_FULLSCREEN);
new ConfigBool(tr("Borderless Fullscreen"), Config::GFX_BORDERLESS_FULLSCREEN, m_game_layer);
misc_layout->addWidget(m_borderless_fullscreen, 2, 1);
#endif
@ -189,10 +231,10 @@ void AdvancedWidget::CreateWidgets()
auto* experimental_layout = new QGridLayout();
experimental_box->setLayout(experimental_layout);
m_defer_efb_access_invalidation =
new ConfigBool(tr("Defer EFB Cache Invalidation"), Config::GFX_HACK_EFB_DEFER_INVALIDATION);
m_manual_texture_sampling =
new ConfigBool(tr("Manual Texture Sampling"), Config::GFX_HACK_FAST_TEXTURE_SAMPLING, true);
m_defer_efb_access_invalidation = new ConfigBool(
tr("Defer EFB Cache Invalidation"), Config::GFX_HACK_EFB_DEFER_INVALIDATION, m_game_layer);
m_manual_texture_sampling = new ConfigBool(
tr("Manual Texture Sampling"), Config::GFX_HACK_FAST_TEXTURE_SAMPLING, m_game_layer, true);
experimental_layout->addWidget(m_defer_efb_access_invalidation, 0, 0);
experimental_layout->addWidget(m_manual_texture_sampling, 0, 1);
@ -211,34 +253,19 @@ void AdvancedWidget::CreateWidgets()
void AdvancedWidget::ConnectWidgets()
{
connect(m_load_custom_textures, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings);
connect(m_dump_use_ffv1, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings);
connect(m_enable_prog_scan, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings);
connect(m_dump_textures, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings);
connect(m_enable_graphics_mods, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings);
}
connect(m_load_custom_textures, &QCheckBox::toggled, this,
[this](bool checked) { m_prefetch_custom_textures->setEnabled(checked); });
connect(m_dump_textures, &QCheckBox::toggled, this, [this](bool checked) {
m_dump_mip_textures->setEnabled(checked);
m_dump_base_textures->setEnabled(checked);
});
connect(m_enable_graphics_mods, &QCheckBox::toggled, this,
[this](bool checked) { emit Settings::Instance().EnableGfxModsChanged(checked); });
void AdvancedWidget::LoadSettings()
{
m_prefetch_custom_textures->setEnabled(Config::Get(Config::GFX_HIRES_TEXTURES));
m_dump_bitrate->setEnabled(!Config::Get(Config::GFX_USE_FFV1));
m_enable_prog_scan->setChecked(Config::Get(Config::SYSCONF_PROGRESSIVE_SCAN));
m_dump_mip_textures->setEnabled(Config::Get(Config::GFX_DUMP_TEXTURES));
m_dump_base_textures->setEnabled(Config::Get(Config::GFX_DUMP_TEXTURES));
SignalBlocking(m_enable_graphics_mods)->setChecked(Settings::Instance().GetGraphicModsEnabled());
}
void AdvancedWidget::SaveSettings()
{
m_prefetch_custom_textures->setEnabled(Config::Get(Config::GFX_HIRES_TEXTURES));
m_dump_bitrate->setEnabled(!Config::Get(Config::GFX_USE_FFV1));
Config::SetBase(Config::SYSCONF_PROGRESSIVE_SCAN, m_enable_prog_scan->isChecked());
m_dump_mip_textures->setEnabled(Config::Get(Config::GFX_DUMP_TEXTURES));
m_dump_base_textures->setEnabled(Config::Get(Config::GFX_DUMP_TEXTURES));
Settings::Instance().SetGraphicModsEnabled(m_enable_graphics_mods->isChecked());
#if defined(HAVE_FFMPEG)
connect(m_dump_use_ffv1, &QCheckBox::toggled, this,
[this](bool checked) { m_dump_bitrate->setEnabled(!checked); });
#endif
}
void AdvancedWidget::OnBackendChanged()

View File

@ -8,22 +8,22 @@
class ConfigBool;
class ConfigChoice;
class ConfigInteger;
class GameConfigWidget;
class GraphicsWindow;
class QCheckBox;
class QComboBox;
class QSpinBox;
class ToolTipCheckBox;
namespace Config
{
class Layer;
} // namespace Config
class AdvancedWidget final : public QWidget
{
Q_OBJECT
public:
explicit AdvancedWidget(GraphicsWindow* parent);
AdvancedWidget(GameConfigWidget* parent, Config::Layer* layer);
private:
void LoadSettings();
void SaveSettings();
void CreateWidgets();
void ConnectWidgets();
void AddDescriptions();
@ -52,7 +52,7 @@ private:
ConfigBool* m_dump_xfb_target;
ConfigBool* m_disable_vram_copies;
ConfigBool* m_load_custom_textures;
ToolTipCheckBox* m_enable_graphics_mods;
ConfigBool* m_enable_graphics_mods;
// Texture dumping
ConfigBool* m_dump_textures;
@ -67,7 +67,7 @@ private:
// Misc
ConfigBool* m_enable_cropping;
ToolTipCheckBox* m_enable_prog_scan;
ConfigBool* m_enable_prog_scan;
ConfigBool* m_backend_multithreading;
ConfigBool* m_prefer_vs_for_point_line_expansion;
ConfigBool* m_cpu_cull;
@ -76,4 +76,6 @@ private:
// Experimental
ConfigBool* m_defer_efb_access_invalidation;
ConfigBool* m_manual_texture_sampling;
Config::Layer* m_game_layer = nullptr;
};

View File

@ -11,6 +11,8 @@
#include <QPushButton>
#include <QVBoxLayout>
#include "Common/CommonTypes.h"
#include "Core/Config/GraphicsSettings.h"
#include "Core/ConfigManager.h"
@ -18,6 +20,7 @@
#include "DolphinQt/Config/ConfigControls/ConfigChoice.h"
#include "DolphinQt/Config/ConfigControls/ConfigRadio.h"
#include "DolphinQt/Config/ConfigControls/ConfigSlider.h"
#include "DolphinQt/Config/GameConfigWidget.h"
#include "DolphinQt/Config/Graphics/ColorCorrectionConfigWindow.h"
#include "DolphinQt/Config/Graphics/GraphicsWindow.h"
#include "DolphinQt/Config/Graphics/PostProcessingConfigWindow.h"
@ -31,31 +34,43 @@
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h"
EnhancementsWidget::EnhancementsWidget(GraphicsWindow* parent) : m_block_save(false)
EnhancementsWidget::EnhancementsWidget(GraphicsWindow* parent)
{
CreateWidgets();
LoadSettings();
LoadPPShaders();
ConnectWidgets();
AddDescriptions();
connect(parent, &GraphicsWindow::BackendChanged,
[this](const QString& backend) { LoadSettings(); });
connect(parent, &GraphicsWindow::UseFastTextureSamplingChanged, this,
&EnhancementsWidget::LoadSettings);
connect(parent, &GraphicsWindow::UseGPUTextureDecodingChanged, this,
&EnhancementsWidget::LoadSettings);
// BackendChanged is called by parent on window creation.
connect(parent, &GraphicsWindow::BackendChanged, this, &EnhancementsWidget::OnBackendChanged);
connect(parent, &GraphicsWindow::UseFastTextureSamplingChanged, this, [this]() {
m_texture_filtering_combo->setEnabled(ReadSetting(Config::GFX_HACK_FAST_TEXTURE_SAMPLING));
});
connect(parent, &GraphicsWindow::UseGPUTextureDecodingChanged, this, [this]() {
m_arbitrary_mipmap_detection->setEnabled(!ReadSetting(Config::GFX_ENABLE_GPU_TEXTURE_DECODING));
});
}
constexpr int TEXTURE_FILTERING_DEFAULT = 0;
constexpr int TEXTURE_FILTERING_ANISO_2X = 1;
constexpr int TEXTURE_FILTERING_ANISO_4X = 2;
constexpr int TEXTURE_FILTERING_ANISO_8X = 3;
constexpr int TEXTURE_FILTERING_ANISO_16X = 4;
constexpr int TEXTURE_FILTERING_FORCE_NEAREST = 5;
constexpr int TEXTURE_FILTERING_FORCE_LINEAR = 6;
constexpr int TEXTURE_FILTERING_FORCE_LINEAR_ANISO_2X = 7;
constexpr int TEXTURE_FILTERING_FORCE_LINEAR_ANISO_4X = 8;
constexpr int TEXTURE_FILTERING_FORCE_LINEAR_ANISO_8X = 9;
constexpr int TEXTURE_FILTERING_FORCE_LINEAR_ANISO_16X = 10;
EnhancementsWidget::EnhancementsWidget(GameConfigWidget* parent, Config::Layer* layer)
: m_game_layer(layer)
{
CreateWidgets();
LoadPPShaders();
ConnectWidgets();
AddDescriptions();
connect(&Settings::Instance(), &Settings::ConfigChanged, this,
&EnhancementsWidget::OnConfigChanged);
}
constexpr int ANISO_DEFAULT = 0;
constexpr int ANISO_2X = 1;
constexpr int ANISO_4X = 2;
constexpr int ANISO_8X = 3;
constexpr int ANISO_16X = 4;
constexpr int FILTERING_DEFAULT = 0;
constexpr int FILTERING_NEAREST = 1;
constexpr int FILTERING_LINEAR = 2;
void EnhancementsWidget::CreateWidgets()
{
@ -83,7 +98,7 @@ void EnhancementsWidget::CreateWidgets()
// If the current scale is greater than the max scale in the ini, add sufficient options so that
// when the settings are saved we don't lose the user-modified value from the ini.
const int max_efb_scale =
std::max(Config::Get(Config::GFX_EFB_SCALE), Config::Get(Config::GFX_MAX_EFB_SCALE));
std::max(ReadSetting(Config::GFX_EFB_SCALE), ReadSetting(Config::GFX_MAX_EFB_SCALE));
for (int scale = static_cast<int>(resolution_options.size()); scale <= max_efb_scale; scale++)
{
const QString scale_text = QString::number(scale);
@ -104,61 +119,60 @@ void EnhancementsWidget::CreateWidgets()
}
}
m_ir_combo = new ConfigChoice(resolution_options, Config::GFX_EFB_SCALE);
m_ir_combo = new ConfigChoice(resolution_options, Config::GFX_EFB_SCALE, m_game_layer);
m_ir_combo->setMaxVisibleItems(visible_resolution_option_count);
m_aa_combo = new ToolTipComboBox();
m_aa_combo = new ConfigComplexChoice(Config::GFX_MSAA, Config::GFX_SSAA, m_game_layer);
m_aa_combo->Add(tr("None"), (u32)1, false);
m_texture_filtering_combo = new ToolTipComboBox();
m_texture_filtering_combo->addItem(tr("Default"), TEXTURE_FILTERING_DEFAULT);
m_texture_filtering_combo->addItem(tr("2x Anisotropic"), TEXTURE_FILTERING_ANISO_2X);
m_texture_filtering_combo->addItem(tr("4x Anisotropic"), TEXTURE_FILTERING_ANISO_4X);
m_texture_filtering_combo->addItem(tr("8x Anisotropic"), TEXTURE_FILTERING_ANISO_8X);
m_texture_filtering_combo->addItem(tr("16x Anisotropic"), TEXTURE_FILTERING_ANISO_16X);
m_texture_filtering_combo->addItem(tr("Force Nearest"), TEXTURE_FILTERING_FORCE_NEAREST);
m_texture_filtering_combo->addItem(tr("Force Linear"), TEXTURE_FILTERING_FORCE_LINEAR);
m_texture_filtering_combo->addItem(tr("Force Linear and 2x Anisotropic"),
TEXTURE_FILTERING_FORCE_LINEAR_ANISO_2X);
m_texture_filtering_combo->addItem(tr("Force Linear and 4x Anisotropic"),
TEXTURE_FILTERING_FORCE_LINEAR_ANISO_4X);
m_texture_filtering_combo->addItem(tr("Force Linear and 8x Anisotropic"),
TEXTURE_FILTERING_FORCE_LINEAR_ANISO_8X);
m_texture_filtering_combo->addItem(tr("Force Linear and 16x Anisotropic"),
TEXTURE_FILTERING_FORCE_LINEAR_ANISO_16X);
m_texture_filtering_combo =
new ConfigComplexChoice(Config::GFX_ENHANCE_MAX_ANISOTROPY,
Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, m_game_layer);
m_output_resampling_combo = new ToolTipComboBox();
m_output_resampling_combo->addItem(tr("Default"),
static_cast<int>(OutputResamplingMode::Default));
m_output_resampling_combo->addItem(tr("Bilinear"),
static_cast<int>(OutputResamplingMode::Bilinear));
m_output_resampling_combo->addItem(tr("Bicubic: B-Spline"),
static_cast<int>(OutputResamplingMode::BSpline));
m_output_resampling_combo->addItem(tr("Bicubic: Mitchell-Netravali"),
static_cast<int>(OutputResamplingMode::MitchellNetravali));
m_output_resampling_combo->addItem(tr("Bicubic: Catmull-Rom"),
static_cast<int>(OutputResamplingMode::CatmullRom));
m_output_resampling_combo->addItem(tr("Sharp Bilinear"),
static_cast<int>(OutputResamplingMode::SharpBilinear));
m_output_resampling_combo->addItem(tr("Area Sampling"),
static_cast<int>(OutputResamplingMode::AreaSampling));
m_texture_filtering_combo->Add(tr("Default"), ANISO_DEFAULT, FILTERING_DEFAULT);
m_texture_filtering_combo->Add(tr("2x Anisotropic"), ANISO_2X, FILTERING_DEFAULT);
m_texture_filtering_combo->Add(tr("4x Anisotropic"), ANISO_4X, FILTERING_DEFAULT);
m_texture_filtering_combo->Add(tr("8x Anisotropic"), ANISO_8X, FILTERING_DEFAULT);
m_texture_filtering_combo->Add(tr("16x Anisotropic"), ANISO_16X, FILTERING_DEFAULT);
m_texture_filtering_combo->Add(tr("Force Nearest"), ANISO_DEFAULT, FILTERING_NEAREST);
m_texture_filtering_combo->Add(tr("Force Linear"), ANISO_DEFAULT, FILTERING_LINEAR);
m_texture_filtering_combo->Add(tr("Force Linear and 2x Anisotropic"), ANISO_2X, FILTERING_LINEAR);
m_texture_filtering_combo->Add(tr("Force Linear and 4x Anisotropic"), ANISO_4X, FILTERING_LINEAR);
m_texture_filtering_combo->Add(tr("Force Linear and 8x Anisotropic"), ANISO_8X, FILTERING_LINEAR);
m_texture_filtering_combo->Add(tr("Force Linear and 16x Anisotropic"), ANISO_16X,
FILTERING_LINEAR);
m_texture_filtering_combo->Refresh();
m_texture_filtering_combo->setEnabled(ReadSetting(Config::GFX_HACK_FAST_TEXTURE_SAMPLING));
m_output_resampling_combo = new ConfigChoice(
{tr("Default"), tr("Bilinear"), tr("Bicubic: B-Spline"), tr("Bicubic: Mitchell-Netravali"),
tr("Bicubic: Catmull-Rom"), tr("Sharp Bilinear"), tr("Area Sampling")},
Config::GFX_ENHANCE_OUTPUT_RESAMPLING, m_game_layer);
m_configure_color_correction = new ToolTipPushButton(tr("Configure"));
m_pp_effect = new ToolTipComboBox();
m_pp_effect = new ConfigStringChoice(VideoCommon::PostProcessing::GetShaderList(),
Config::GFX_ENHANCE_POST_SHADER, m_game_layer);
m_configure_pp_effect = new NonDefaultQPushButton(tr("Configure"));
m_scaled_efb_copy = new ConfigBool(tr("Scaled EFB Copy"), Config::GFX_HACK_COPY_EFB_SCALED);
m_per_pixel_lighting =
new ConfigBool(tr("Per-Pixel Lighting"), Config::GFX_ENABLE_PIXEL_LIGHTING);
m_configure_pp_effect->setDisabled(true);
m_widescreen_hack = new ConfigBool(tr("Widescreen Hack"), Config::GFX_WIDESCREEN_HACK);
m_disable_fog = new ConfigBool(tr("Disable Fog"), Config::GFX_DISABLE_FOG);
m_scaled_efb_copy =
new ConfigBool(tr("Scaled EFB Copy"), Config::GFX_HACK_COPY_EFB_SCALED, m_game_layer);
m_per_pixel_lighting =
new ConfigBool(tr("Per-Pixel Lighting"), Config::GFX_ENABLE_PIXEL_LIGHTING, m_game_layer);
m_widescreen_hack =
new ConfigBool(tr("Widescreen Hack"), Config::GFX_WIDESCREEN_HACK, m_game_layer);
m_disable_fog = new ConfigBool(tr("Disable Fog"), Config::GFX_DISABLE_FOG, m_game_layer);
m_force_24bit_color =
new ConfigBool(tr("Force 24-Bit Color"), Config::GFX_ENHANCE_FORCE_TRUE_COLOR);
m_disable_copy_filter =
new ConfigBool(tr("Disable Copy Filter"), Config::GFX_ENHANCE_DISABLE_COPY_FILTER);
m_arbitrary_mipmap_detection = new ConfigBool(tr("Arbitrary Mipmap Detection"),
Config::GFX_ENHANCE_ARBITRARY_MIPMAP_DETECTION);
m_hdr = new ConfigBool(tr("HDR Post-Processing"), Config::GFX_ENHANCE_HDR_OUTPUT);
new ConfigBool(tr("Force 24-Bit Color"), Config::GFX_ENHANCE_FORCE_TRUE_COLOR, m_game_layer);
m_disable_copy_filter = new ConfigBool(tr("Disable Copy Filter"),
Config::GFX_ENHANCE_DISABLE_COPY_FILTER, m_game_layer);
m_arbitrary_mipmap_detection =
new ConfigBool(tr("Arbitrary Mipmap Detection"),
Config::GFX_ENHANCE_ARBITRARY_MIPMAP_DETECTION, m_game_layer);
m_arbitrary_mipmap_detection->setEnabled(!ReadSetting(Config::GFX_ENABLE_GPU_TEXTURE_DECODING));
m_hdr = new ConfigBool(tr("HDR Post-Processing"), Config::GFX_ENHANCE_HDR_OUTPUT, m_game_layer);
int row = 0;
enhancements_layout->addWidget(new QLabel(tr("Internal Resolution:")), row, 0);
@ -209,26 +223,27 @@ void EnhancementsWidget::CreateWidgets()
m_3d_mode = new ConfigChoice({tr("Off"), tr("Side-by-Side"), tr("Top-and-Bottom"), tr("Anaglyph"),
tr("HDMI 3D"), tr("Passive")},
Config::GFX_STEREO_MODE);
m_3d_depth = new ConfigSlider(0, Config::GFX_STEREO_DEPTH_MAXIMUM, Config::GFX_STEREO_DEPTH);
Config::GFX_STEREO_MODE, m_game_layer);
m_3d_depth =
new ConfigSlider(0, Config::GFX_STEREO_DEPTH_MAXIMUM, Config::GFX_STEREO_DEPTH, m_game_layer);
m_3d_convergence = new ConfigSlider(0, Config::GFX_STEREO_CONVERGENCE_MAXIMUM,
Config::GFX_STEREO_CONVERGENCE, 100);
Config::GFX_STEREO_CONVERGENCE, m_game_layer, 100);
m_3d_swap_eyes = new ConfigBool(tr("Swap Eyes"), Config::GFX_STEREO_SWAP_EYES);
m_3d_swap_eyes = new ConfigBool(tr("Swap Eyes"), Config::GFX_STEREO_SWAP_EYES, m_game_layer);
m_3d_per_eye_resolution =
new ConfigBool(tr("Use Full Resolution Per Eye"), Config::GFX_STEREO_PER_EYE_RESOLUTION_FULL);
m_3d_per_eye_resolution = new ConfigBool(
tr("Use Full Resolution Per Eye"), Config::GFX_STEREO_PER_EYE_RESOLUTION_FULL, m_game_layer);
stereoscopy_layout->addWidget(new QLabel(tr("Stereoscopic 3D Mode:")), 0, 0);
stereoscopy_layout->addWidget(m_3d_mode, 0, 1);
stereoscopy_layout->addWidget(new QLabel(tr("Depth:")), 1, 0);
stereoscopy_layout->addWidget(new ConfigSliderLabel(tr("Depth:"), m_3d_depth), 1, 0);
stereoscopy_layout->addWidget(m_3d_depth, 1, 1);
stereoscopy_layout->addWidget(new QLabel(tr("Convergence:")), 2, 0);
stereoscopy_layout->addWidget(new ConfigSliderLabel(tr("Convergence:"), m_3d_convergence), 2, 0);
stereoscopy_layout->addWidget(m_3d_convergence, 2, 1);
stereoscopy_layout->addWidget(m_3d_swap_eyes, 3, 0);
stereoscopy_layout->addWidget(m_3d_per_eye_resolution, 4, 0);
auto current_stereo_mode = Config::Get(Config::GFX_STEREO_MODE);
auto current_stereo_mode = ReadSetting(Config::GFX_STEREO_MODE);
if (current_stereo_mode != StereoMode::SBS && current_stereo_mode != StereoMode::TAB)
m_3d_per_eye_resolution->hide();
@ -241,58 +256,53 @@ void EnhancementsWidget::CreateWidgets()
void EnhancementsWidget::ConnectWidgets()
{
connect(m_aa_combo, &QComboBox::currentIndexChanged, [this](int) { SaveSettings(); });
connect(m_texture_filtering_combo, &QComboBox::currentIndexChanged,
[this](int) { SaveSettings(); });
connect(m_output_resampling_combo, &QComboBox::currentIndexChanged,
[this](int) { SaveSettings(); });
connect(m_pp_effect, &QComboBox::currentIndexChanged, [this](int) { SaveSettings(); });
connect(m_3d_mode, &QComboBox::currentIndexChanged, [this] {
m_block_save = true;
m_configure_color_correction->setEnabled(g_Config.backend_info.bSupportsPostProcessing);
auto current_stereo_mode = Config::Get(Config::GFX_STEREO_MODE);
LoadPPShaders(current_stereo_mode);
auto current_stereo_mode = ReadSetting(Config::GFX_STEREO_MODE);
LoadPPShaders();
if (current_stereo_mode == StereoMode::SBS || current_stereo_mode == StereoMode::TAB)
m_3d_per_eye_resolution->show();
else
m_3d_per_eye_resolution->hide();
m_block_save = false;
SaveSettings();
});
connect(m_pp_effect, &QComboBox::currentIndexChanged, this, &EnhancementsWidget::ShaderChanged);
connect(m_configure_color_correction, &QPushButton::clicked, this,
&EnhancementsWidget::ConfigureColorCorrection);
connect(m_configure_pp_effect, &QPushButton::clicked, this,
&EnhancementsWidget::ConfigurePostProcessingShader);
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
const QSignalBlocker blocker(this);
m_block_save = true;
LoadPPShaders(Config::Get(Config::GFX_STEREO_MODE));
m_block_save = false;
});
}
void EnhancementsWidget::LoadPPShaders(StereoMode stereo_mode)
template <typename T>
T EnhancementsWidget::ReadSetting(const Config::Info<T>& setting) const
{
std::vector<std::string> shaders = VideoCommon::PostProcessing::GetShaderList();
if (stereo_mode == StereoMode::Anaglyph)
{
shaders = VideoCommon::PostProcessing::GetAnaglyphShaderList();
}
else if (stereo_mode == StereoMode::Passive)
{
shaders = VideoCommon::PostProcessing::GetPassiveShaderList();
}
if (m_game_layer != nullptr)
return m_game_layer->Get(setting);
else
return Config::Get(setting);
}
void EnhancementsWidget::LoadPPShaders()
{
auto stereo_mode = ReadSetting(Config::GFX_STEREO_MODE);
const QSignalBlocker blocker(m_pp_effect);
m_pp_effect->clear();
// Get shader list
std::vector<std::string> shaders = VideoCommon::PostProcessing::GetShaderList();
if (stereo_mode == StereoMode::Anaglyph)
shaders = VideoCommon::PostProcessing::GetAnaglyphShaderList();
else if (stereo_mode == StereoMode::Passive)
shaders = VideoCommon::PostProcessing::GetPassiveShaderList();
// Populate widget
if (stereo_mode != StereoMode::Anaglyph && stereo_mode != StereoMode::Passive)
m_pp_effect->addItem(tr("(off)"));
auto selected_shader = Config::Get(Config::GFX_ENHANCE_POST_SHADER);
auto selected_shader = ReadSetting(Config::GFX_ENHANCE_POST_SHADER);
bool found = false;
@ -306,6 +316,7 @@ void EnhancementsWidget::LoadPPShaders(StereoMode stereo_mode)
}
}
// Force a shader for StereoModes that require it.
if (!found)
{
if (stereo_mode == StereoMode::Anaglyph)
@ -321,103 +332,20 @@ void EnhancementsWidget::LoadPPShaders(StereoMode stereo_mode)
else
m_pp_effect->setCurrentIndex(0);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER, selected_shader);
// Save forced shader, but avoid forcing an option into a game ini layer.
if (m_game_layer == nullptr && ReadSetting(Config::GFX_ENHANCE_POST_SHADER) != selected_shader)
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER, selected_shader);
}
const bool supports_postprocessing = g_Config.backend_info.bSupportsPostProcessing;
m_pp_effect->setEnabled(supports_postprocessing);
m_pp_effect->setToolTip(supports_postprocessing ?
QString{} :
tr("%1 doesn't support this feature.")
.arg(tr(g_video_backend->GetDisplayName().c_str())));
VideoCommon::PostProcessingConfiguration pp_shader;
if (selected_shader != "" && supports_postprocessing)
{
pp_shader.LoadShader(selected_shader);
m_configure_pp_effect->setEnabled(pp_shader.HasOptions());
}
else
{
m_configure_pp_effect->setEnabled(false);
}
m_pp_effect->Load();
ShaderChanged();
}
void EnhancementsWidget::LoadSettings()
void EnhancementsWidget::OnBackendChanged()
{
m_block_save = true;
m_texture_filtering_combo->setEnabled(Config::Get(Config::GFX_HACK_FAST_TEXTURE_SAMPLING));
m_arbitrary_mipmap_detection->setEnabled(!Config::Get(Config::GFX_ENABLE_GPU_TEXTURE_DECODING));
// Anti-Aliasing
const u32 aa_selection = Config::Get(Config::GFX_MSAA);
const bool ssaa = Config::Get(Config::GFX_SSAA);
const int aniso = Config::Get(Config::GFX_ENHANCE_MAX_ANISOTROPY);
const TextureFilteringMode tex_filter_mode =
Config::Get(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING);
m_aa_combo->clear();
for (const u32 aa_mode : g_Config.backend_info.AAModes)
{
if (aa_mode == 1)
m_aa_combo->addItem(tr("None"), 1);
else
m_aa_combo->addItem(tr("%1x MSAA").arg(aa_mode), static_cast<int>(aa_mode));
if (aa_mode == aa_selection && !ssaa)
m_aa_combo->setCurrentIndex(m_aa_combo->count() - 1);
}
if (g_Config.backend_info.bSupportsSSAA)
{
for (const u32 aa_mode : g_Config.backend_info.AAModes)
{
if (aa_mode != 1) // don't show "None" twice
{
// Mark SSAA using negative values in the variant
m_aa_combo->addItem(tr("%1x SSAA").arg(aa_mode), -static_cast<int>(aa_mode));
if (aa_mode == aa_selection && ssaa)
m_aa_combo->setCurrentIndex(m_aa_combo->count() - 1);
}
}
}
m_aa_combo->setEnabled(m_aa_combo->count() > 1);
switch (tex_filter_mode)
{
case TextureFilteringMode::Default:
if (aniso >= 0 && aniso <= 4)
m_texture_filtering_combo->setCurrentIndex(aniso);
else
m_texture_filtering_combo->setCurrentIndex(TEXTURE_FILTERING_DEFAULT);
break;
case TextureFilteringMode::Nearest:
m_texture_filtering_combo->setCurrentIndex(TEXTURE_FILTERING_FORCE_NEAREST);
break;
case TextureFilteringMode::Linear:
if (aniso >= 0 && aniso <= 4)
m_texture_filtering_combo->setCurrentIndex(TEXTURE_FILTERING_FORCE_LINEAR + aniso);
else
m_texture_filtering_combo->setCurrentIndex(TEXTURE_FILTERING_FORCE_LINEAR);
break;
}
// Resampling
const OutputResamplingMode output_resampling_mode =
Config::Get(Config::GFX_ENHANCE_OUTPUT_RESAMPLING);
m_output_resampling_combo->setCurrentIndex(
m_output_resampling_combo->findData(static_cast<int>(output_resampling_mode)));
m_output_resampling_combo->setEnabled(g_Config.backend_info.bSupportsPostProcessing);
// Color Correction
m_configure_color_correction->setEnabled(g_Config.backend_info.bSupportsPostProcessing);
// Post Processing Shader
LoadPPShaders(Config::Get(Config::GFX_STEREO_MODE));
m_hdr->setEnabled(g_Config.backend_info.bSupportsHDROutput);
// Stereoscopy
const bool supports_stereoscopy = g_Config.backend_info.bSupportsGeometryShaders;
@ -426,105 +354,98 @@ void EnhancementsWidget::LoadSettings()
m_3d_depth->setEnabled(supports_stereoscopy);
m_3d_swap_eyes->setEnabled(supports_stereoscopy);
m_hdr->setEnabled(g_Config.backend_info.bSupportsHDROutput);
m_block_save = false;
}
void EnhancementsWidget::SaveSettings()
{
if (m_block_save)
return;
const u32 aa_value = static_cast<u32>(std::abs(m_aa_combo->currentData().toInt()));
const bool is_ssaa = m_aa_combo->currentData().toInt() < 0;
Config::SetBaseOrCurrent(Config::GFX_MSAA, aa_value);
Config::SetBaseOrCurrent(Config::GFX_SSAA, is_ssaa);
const int texture_filtering_selection = m_texture_filtering_combo->currentData().toInt();
switch (texture_filtering_selection)
// PostProcessing
const bool supports_postprocessing = g_Config.backend_info.bSupportsPostProcessing;
if (!supports_postprocessing)
{
case TEXTURE_FILTERING_DEFAULT:
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 0);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING,
TextureFilteringMode::Default);
break;
case TEXTURE_FILTERING_ANISO_2X:
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 1);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING,
TextureFilteringMode::Default);
break;
case TEXTURE_FILTERING_ANISO_4X:
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 2);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING,
TextureFilteringMode::Default);
break;
case TEXTURE_FILTERING_ANISO_8X:
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 3);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING,
TextureFilteringMode::Default);
break;
case TEXTURE_FILTERING_ANISO_16X:
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 4);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING,
TextureFilteringMode::Default);
break;
case TEXTURE_FILTERING_FORCE_NEAREST:
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 0);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING,
TextureFilteringMode::Nearest);
break;
case TEXTURE_FILTERING_FORCE_LINEAR:
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 0);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING,
TextureFilteringMode::Linear);
break;
case TEXTURE_FILTERING_FORCE_LINEAR_ANISO_2X:
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 1);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING,
TextureFilteringMode::Linear);
break;
case TEXTURE_FILTERING_FORCE_LINEAR_ANISO_4X:
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 2);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING,
TextureFilteringMode::Linear);
break;
case TEXTURE_FILTERING_FORCE_LINEAR_ANISO_8X:
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 3);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING,
TextureFilteringMode::Linear);
break;
case TEXTURE_FILTERING_FORCE_LINEAR_ANISO_16X:
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 4);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING,
TextureFilteringMode::Linear);
break;
m_configure_pp_effect->setEnabled(false);
m_pp_effect->setEnabled(false);
m_pp_effect->setToolTip(
tr("%1 doesn't support this feature.").arg(tr(g_video_backend->GetDisplayName().c_str())));
}
else if (!m_pp_effect->isEnabled() && supports_postprocessing)
{
m_configure_pp_effect->setEnabled(true);
m_pp_effect->setEnabled(true);
m_pp_effect->setToolTip(QString{});
LoadPPShaders();
}
const int output_resampling_selection = m_output_resampling_combo->currentData().toInt();
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_OUTPUT_RESAMPLING,
static_cast<OutputResamplingMode>(output_resampling_selection));
UpdateAAOptions();
}
const bool anaglyph = g_Config.stereo_mode == StereoMode::Anaglyph;
const bool passive = g_Config.stereo_mode == StereoMode::Passive;
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER,
(!anaglyph && !passive && m_pp_effect->currentIndex() == 0) ?
"" :
m_pp_effect->currentText().toStdString());
void EnhancementsWidget::ShaderChanged()
{
auto shader = ReadSetting(Config::GFX_ENHANCE_POST_SHADER);
VideoCommon::PostProcessingConfiguration pp_shader;
if (Config::Get(Config::GFX_ENHANCE_POST_SHADER) != "")
if (shader == "(off)" || shader == "")
{
pp_shader.LoadShader(Config::Get(Config::GFX_ENHANCE_POST_SHADER));
shader = "";
// Setting a shader to null in a game ini could be confusing, as it won't be bolded. Remove it
// instead.
if (m_game_layer != nullptr)
m_game_layer->DeleteKey(Config::GFX_ENHANCE_POST_SHADER.GetLocation());
else
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER, shader);
}
if (shader != "" && m_pp_effect->isEnabled())
{
VideoCommon::PostProcessingConfiguration pp_shader;
pp_shader.LoadShader(shader);
m_configure_pp_effect->setEnabled(pp_shader.HasOptions());
}
else
{
m_configure_pp_effect->setEnabled(false);
}
}
LoadSettings();
void EnhancementsWidget::OnConfigChanged()
{
// Only used for the GameConfigWidget. Bypasses graphics window signals and backend info due to it
// being global.
m_texture_filtering_combo->setEnabled(ReadSetting(Config::GFX_HACK_FAST_TEXTURE_SAMPLING));
m_arbitrary_mipmap_detection->setEnabled(!ReadSetting(Config::GFX_ENABLE_GPU_TEXTURE_DECODING));
UpdateAAOptions();
// Needs to update after deleting a key for 3d settings.
LoadPPShaders();
}
void EnhancementsWidget::UpdateAAOptions()
{
const QSignalBlocker blocker_aa(m_aa_combo);
m_aa_combo->Reset();
m_aa_combo->Add(tr("None"), (u32)1, false);
std::vector<u32> aa_modes = g_Config.backend_info.AAModes;
for (const u32 aa_mode : aa_modes)
{
if (aa_mode > 1)
m_aa_combo->Add(tr("%1x MSAA").arg(aa_mode), aa_mode, false);
}
if (g_Config.backend_info.bSupportsSSAA)
{
for (const u32 aa_mode : aa_modes)
{
if (aa_mode > 1)
m_aa_combo->Add(tr("%1x SSAA").arg(aa_mode), aa_mode, true);
}
}
m_aa_combo->Refresh();
// Backend info can't be populated in the local game settings window. Only enable local game AA
// edits when the backend info is correct - global and local have the same backend.
const bool good_info =
m_game_layer == nullptr || !m_game_layer->Exists(Config::MAIN_GFX_BACKEND.GetLocation()) ||
Config::Get(Config::MAIN_GFX_BACKEND) == m_game_layer->Get(Config::MAIN_GFX_BACKEND);
m_aa_combo->setEnabled(m_aa_combo->count() > 1 && good_info);
}
void EnhancementsWidget::AddDescriptions()
@ -713,7 +634,7 @@ void EnhancementsWidget::ConfigureColorCorrection()
void EnhancementsWidget::ConfigurePostProcessingShader()
{
const std::string shader = Config::Get(Config::GFX_ENHANCE_POST_SHADER);
const std::string shader = ReadSetting(Config::GFX_ENHANCE_POST_SHADER);
PostProcessingConfigWindow dialog(this, shader);
SetQWidgetWindowDecorations(&dialog);
dialog.exec();

View File

@ -9,39 +9,52 @@
class ConfigBool;
class ConfigChoice;
class ConfigComplexChoice;
class ConfigStringChoice;
class ConfigSlider;
class GameConfigWidget;
class GraphicsWindow;
class QCheckBox;
class QComboBox;
class QPushButton;
class QSlider;
class ToolTipComboBox;
class ToolTipPushButton;
enum class StereoMode : int;
namespace Config
{
template <typename T>
class Info;
class Layer;
} // namespace Config
class EnhancementsWidget final : public QWidget
{
Q_OBJECT
public:
explicit EnhancementsWidget(GraphicsWindow* parent);
EnhancementsWidget(GameConfigWidget* parent, Config::Layer* layer);
private:
void LoadSettings();
void SaveSettings();
template <typename T>
T ReadSetting(const Config::Info<T>& setting) const;
void CreateWidgets();
void ConnectWidgets();
void AddDescriptions();
void OnBackendChanged();
void UpdateAAOptions();
void LoadPPShaders();
void ShaderChanged();
void OnConfigChanged();
void ConfigureColorCorrection();
void ConfigurePostProcessingShader();
void LoadPPShaders(StereoMode stereo_mode);
// Enhancements
ConfigChoice* m_ir_combo;
ToolTipComboBox* m_aa_combo;
ToolTipComboBox* m_texture_filtering_combo;
ToolTipComboBox* m_output_resampling_combo;
ToolTipComboBox* m_pp_effect;
ConfigComplexChoice* m_aa_combo;
ConfigComplexChoice* m_texture_filtering_combo;
ConfigChoice* m_output_resampling_combo;
ConfigStringChoice* m_pp_effect;
ToolTipPushButton* m_configure_color_correction;
QPushButton* m_configure_pp_effect;
ConfigBool* m_scaled_efb_copy;
@ -60,6 +73,5 @@ private:
ConfigBool* m_3d_swap_eyes;
ConfigBool* m_3d_per_eye_resolution;
int m_msaa_modes;
bool m_block_save;
Config::Layer* m_game_layer = nullptr;
};

View File

@ -23,6 +23,7 @@
#include "DolphinQt/Config/ConfigControls/ConfigChoice.h"
#include "DolphinQt/Config/ConfigControls/ConfigInteger.h"
#include "DolphinQt/Config/ConfigControls/ConfigRadio.h"
#include "DolphinQt/Config/GameConfigWidget.h"
#include "DolphinQt/Config/Graphics/GraphicsWindow.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
@ -38,7 +39,6 @@ GeneralWidget::GeneralWidget(GraphicsWindow* parent)
LoadSettings();
ConnectWidgets();
AddDescriptions();
emit BackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND)));
connect(parent, &GraphicsWindow::BackendChanged, this, &GeneralWidget::OnBackendChanged);
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) {
@ -47,6 +47,14 @@ GeneralWidget::GeneralWidget(GraphicsWindow* parent)
OnEmulationStateChanged(!Core::IsUninitialized(Core::System::GetInstance()));
}
GeneralWidget::GeneralWidget(GameConfigWidget* parent, Config::Layer* layer) : m_game_layer(layer)
{
CreateWidgets();
LoadSettings();
ConnectWidgets();
AddDescriptions();
}
void GeneralWidget::CreateWidgets()
{
auto* main_layout = new QVBoxLayout;
@ -55,33 +63,36 @@ void GeneralWidget::CreateWidgets()
auto* m_video_box = new QGroupBox(tr("Basic"));
m_video_layout = new QGridLayout();
m_backend_combo = new ToolTipComboBox();
std::vector<std::pair<QString, QString>> options;
for (auto& backend : VideoBackendBase::GetAvailableBackends())
{
options.push_back(std::make_pair(tr(backend->GetDisplayName().c_str()),
QString::fromStdString(backend->GetName())));
}
m_backend_combo = new ConfigStringChoice(options, Config::MAIN_GFX_BACKEND, m_game_layer);
m_previous_backend = m_backend_combo->currentIndex();
m_aspect_combo = new ConfigChoice({tr("Auto"), tr("Force 16:9"), tr("Force 4:3"),
tr("Stretch to Window"), tr("Custom"), tr("Custom (Stretch)")},
Config::GFX_ASPECT_RATIO);
Config::GFX_ASPECT_RATIO, m_game_layer);
m_custom_aspect_label = new QLabel(tr("Custom Aspect Ratio:"));
m_custom_aspect_label->setHidden(true);
constexpr int MAX_CUSTOM_ASPECT_RATIO_RESOLUTION = 10000;
m_custom_aspect_width = new ConfigInteger(1, MAX_CUSTOM_ASPECT_RATIO_RESOLUTION,
Config::GFX_CUSTOM_ASPECT_RATIO_WIDTH);
Config::GFX_CUSTOM_ASPECT_RATIO_WIDTH, m_game_layer);
m_custom_aspect_width->setEnabled(false);
m_custom_aspect_width->setHidden(true);
m_custom_aspect_height = new ConfigInteger(1, MAX_CUSTOM_ASPECT_RATIO_RESOLUTION,
Config::GFX_CUSTOM_ASPECT_RATIO_HEIGHT);
Config::GFX_CUSTOM_ASPECT_RATIO_HEIGHT, m_game_layer);
m_custom_aspect_height->setEnabled(false);
m_custom_aspect_height->setHidden(true);
m_adapter_combo = new ToolTipComboBox;
m_enable_vsync = new ConfigBool(tr("V-Sync"), Config::GFX_VSYNC);
m_enable_fullscreen = new ConfigBool(tr("Start in Fullscreen"), Config::MAIN_FULLSCREEN);
m_enable_vsync = new ConfigBool(tr("V-Sync"), Config::GFX_VSYNC, m_game_layer);
m_enable_fullscreen =
new ConfigBool(tr("Start in Fullscreen"), Config::MAIN_FULLSCREEN, m_game_layer);
m_video_box->setLayout(m_video_layout);
for (auto& backend : VideoBackendBase::GetAvailableBackends())
{
m_backend_combo->addItem(tr(backend->GetDisplayName().c_str()),
QVariant(QString::fromStdString(backend->GetName())));
}
m_video_layout->addWidget(new QLabel(tr("Backend:")), 0, 0);
m_video_layout->addWidget(m_backend_combo, 0, 1, 1, -1);
@ -102,11 +113,14 @@ void GeneralWidget::CreateWidgets()
auto* m_options_box = new QGroupBox(tr("Other"));
auto* m_options_layout = new QGridLayout();
m_show_ping = new ConfigBool(tr("Show NetPlay Ping"), Config::GFX_SHOW_NETPLAY_PING);
m_autoadjust_window_size =
new ConfigBool(tr("Auto-Adjust Window Size"), Config::MAIN_RENDER_WINDOW_AUTOSIZE);
m_show_messages = new ConfigBool(tr("Show NetPlay Messages"), Config::GFX_SHOW_NETPLAY_MESSAGES);
m_render_main_window = new ConfigBool(tr("Render to Main Window"), Config::MAIN_RENDER_TO_MAIN);
m_show_ping =
new ConfigBool(tr("Show NetPlay Ping"), Config::GFX_SHOW_NETPLAY_PING, m_game_layer);
m_autoadjust_window_size = new ConfigBool(tr("Auto-Adjust Window Size"),
Config::MAIN_RENDER_WINDOW_AUTOSIZE, m_game_layer);
m_show_messages =
new ConfigBool(tr("Show NetPlay Messages"), Config::GFX_SHOW_NETPLAY_MESSAGES, m_game_layer);
m_render_main_window =
new ConfigBool(tr("Render to Main Window"), Config::MAIN_RENDER_TO_MAIN, m_game_layer);
m_options_box->setLayout(m_options_layout);
@ -128,13 +142,13 @@ void GeneralWidget::CreateWidgets()
}};
for (size_t i = 0; i < modes.size(); i++)
{
m_shader_compilation_mode[i] =
new ConfigRadioInt(tr(modes[i]), Config::GFX_SHADER_COMPILATION_MODE, static_cast<int>(i));
m_shader_compilation_mode[i] = new ConfigRadioInt(
tr(modes[i]), Config::GFX_SHADER_COMPILATION_MODE, static_cast<int>(i), m_game_layer);
shader_compilation_layout->addWidget(m_shader_compilation_mode[i], static_cast<int>(i / 2),
static_cast<int>(i % 2));
}
m_wait_for_shaders = new ConfigBool(tr("Compile Shaders Before Starting"),
Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING);
Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING, m_game_layer);
shader_compilation_layout->addWidget(m_wait_for_shaders);
shader_compilation_box->setLayout(shader_compilation_layout);
@ -149,7 +163,7 @@ void GeneralWidget::CreateWidgets()
void GeneralWidget::ConnectWidgets()
{
// Video Backend
connect(m_backend_combo, &QComboBox::currentIndexChanged, this, &GeneralWidget::SaveSettings);
connect(m_backend_combo, &QComboBox::currentIndexChanged, this, &GeneralWidget::BackendWarning);
connect(m_adapter_combo, &QComboBox::currentIndexChanged, this, [&](int index) {
g_Config.iAdapter = index;
Config::SetBaseOrCurrent(Config::GFX_ADAPTER, index);
@ -168,10 +182,6 @@ void GeneralWidget::ConnectWidgets()
void GeneralWidget::LoadSettings()
{
// Video Backend
m_backend_combo->setCurrentIndex(m_backend_combo->findData(
QVariant(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND)))));
const bool is_custom_aspect_ratio =
(Config::Get(Config::GFX_ASPECT_RATIO) == AspectMode::Custom) ||
(Config::Get(Config::GFX_ASPECT_RATIO) == AspectMode::CustomStretch);
@ -182,13 +192,8 @@ void GeneralWidget::LoadSettings()
m_custom_aspect_height->setHidden(!is_custom_aspect_ratio);
}
void GeneralWidget::SaveSettings()
void GeneralWidget::BackendWarning()
{
// Video Backend
const auto current_backend = m_backend_combo->currentData().toString().toStdString();
if (Config::Get(Config::MAIN_GFX_BACKEND) == current_backend)
return;
if (Config::GetActiveLayerForConfig(Config::MAIN_GFX_BACKEND) == Config::LayerType::Base)
{
auto warningMessage = VideoBackendBase::GetAvailableBackends()[m_backend_combo->currentIndex()]
@ -205,15 +210,14 @@ void GeneralWidget::SaveSettings()
SetQWidgetWindowDecorations(&confirm_sw);
if (confirm_sw.exec() != QMessageBox::Yes)
{
m_backend_combo->setCurrentIndex(m_backend_combo->findData(
QVariant(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND)))));
m_backend_combo->setCurrentIndex(m_previous_backend);
return;
}
}
}
Config::SetBaseOrCurrent(Config::MAIN_GFX_BACKEND, current_backend);
emit BackendChanged(QString::fromStdString(current_backend));
m_previous_backend = m_backend_combo->currentIndex();
emit BackendChanged(m_backend_combo->currentData().toString());
}
void GeneralWidget::OnEmulationStateChanged(bool running)
@ -227,7 +231,10 @@ void GeneralWidget::OnEmulationStateChanged(bool running)
std::string current_backend = m_backend_combo->currentData().toString().toStdString();
if (Config::Get(Config::MAIN_GFX_BACKEND) != current_backend)
{
m_backend_combo->Load();
emit BackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND)));
}
}
void GeneralWidget::AddDescriptions()
@ -348,8 +355,6 @@ void GeneralWidget::AddDescriptions()
void GeneralWidget::OnBackendChanged(const QString& backend_name)
{
m_backend_combo->setCurrentIndex(m_backend_combo->findData(QVariant(backend_name)));
const QSignalBlocker blocker(m_adapter_combo);
m_adapter_combo->clear();

View File

@ -11,6 +11,8 @@ class ConfigBool;
class ConfigChoice;
class ConfigInteger;
class ConfigRadioInt;
class ConfigStringChoice;
class GameConfigWidget;
class GraphicsWindow;
class QCheckBox;
class QComboBox;
@ -19,17 +21,24 @@ class QRadioButton;
class QGridLayout;
class ToolTipComboBox;
namespace Config
{
class Layer;
} // namespace Config
class GeneralWidget final : public QWidget
{
Q_OBJECT
public:
explicit GeneralWidget(GraphicsWindow* parent);
GeneralWidget(GameConfigWidget* parent, Config::Layer* layer);
signals:
void BackendChanged(const QString& backend);
private:
void LoadSettings();
void SaveSettings();
void BackendWarning();
void CreateWidgets();
void ConnectWidgets();
@ -40,7 +49,7 @@ private:
// Video
QGridLayout* m_video_layout;
ToolTipComboBox* m_backend_combo;
ConfigStringChoice* m_backend_combo;
ToolTipComboBox* m_adapter_combo;
ConfigChoice* m_aspect_combo;
QLabel* m_custom_aspect_label;
@ -56,4 +65,6 @@ private:
ConfigBool* m_render_main_window;
std::array<ConfigRadioInt*, 4> m_shader_compilation_mode{};
ConfigBool* m_wait_for_shaders;
int m_previous_backend = 0;
Config::Layer* m_game_layer = nullptr;
};

View File

@ -15,6 +15,7 @@
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include "DolphinQt/Config/ConfigControls/ConfigSlider.h"
#include "DolphinQt/Config/GameConfigWidget.h"
#include "DolphinQt/Config/Graphics/GraphicsWindow.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h"
#include "DolphinQt/Settings.h"
@ -37,6 +38,14 @@ HacksWidget::HacksWidget(GraphicsWindow* parent)
});
}
HacksWidget::HacksWidget(GameConfigWidget* parent, Config::Layer* layer) : m_game_layer(layer)
{
CreateWidgets();
LoadSettings();
ConnectWidgets();
AddDescriptions();
}
void HacksWidget::CreateWidgets()
{
auto* main_layout = new QVBoxLayout;
@ -45,14 +54,14 @@ void HacksWidget::CreateWidgets()
auto* efb_box = new QGroupBox(tr("Embedded Frame Buffer (EFB)"));
auto* efb_layout = new QGridLayout();
efb_box->setLayout(efb_layout);
m_skip_efb_cpu =
new ConfigBool(tr("Skip EFB Access from CPU"), Config::GFX_HACK_EFB_ACCESS_ENABLE, true);
m_ignore_format_changes = new ConfigBool(tr("Ignore Format Changes"),
Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES, true);
m_store_efb_copies =
new ConfigBool(tr("Store EFB Copies to Texture Only"), Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM);
m_defer_efb_copies =
new ConfigBool(tr("Defer EFB Copies to RAM"), Config::GFX_HACK_DEFER_EFB_COPIES);
m_skip_efb_cpu = new ConfigBool(tr("Skip EFB Access from CPU"),
Config::GFX_HACK_EFB_ACCESS_ENABLE, m_game_layer, true);
m_ignore_format_changes = new ConfigBool(
tr("Ignore Format Changes"), Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES, m_game_layer, true);
m_store_efb_copies = new ConfigBool(tr("Store EFB Copies to Texture Only"),
Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM, m_game_layer);
m_defer_efb_copies = new ConfigBool(tr("Defer EFB Copies to RAM"),
Config::GFX_HACK_DEFER_EFB_COPIES, m_game_layer);
efb_layout->addWidget(m_skip_efb_cpu, 0, 0);
efb_layout->addWidget(m_ignore_format_changes, 0, 1);
@ -69,8 +78,8 @@ void HacksWidget::CreateWidgets()
m_accuracy->setMaximum(2);
m_accuracy->setPageStep(1);
m_accuracy->setTickPosition(QSlider::TicksBelow);
m_gpu_texture_decoding =
new ConfigBool(tr("GPU Texture Decoding"), Config::GFX_ENABLE_GPU_TEXTURE_DECODING);
m_gpu_texture_decoding = new ConfigBool(tr("GPU Texture Decoding"),
Config::GFX_ENABLE_GPU_TEXTURE_DECODING, m_game_layer);
auto* safe_label = new QLabel(tr("Safe"));
safe_label->setAlignment(Qt::AlignRight);
@ -88,11 +97,12 @@ void HacksWidget::CreateWidgets()
auto* xfb_layout = new QVBoxLayout();
xfb_box->setLayout(xfb_layout);
m_store_xfb_copies =
new ConfigBool(tr("Store XFB Copies to Texture Only"), Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM);
m_immediate_xfb = new ConfigBool(tr("Immediately Present XFB"), Config::GFX_HACK_IMMEDIATE_XFB);
m_skip_duplicate_xfbs =
new ConfigBool(tr("Skip Presenting Duplicate Frames"), Config::GFX_HACK_SKIP_DUPLICATE_XFBS);
m_store_xfb_copies = new ConfigBool(tr("Store XFB Copies to Texture Only"),
Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM, m_game_layer);
m_immediate_xfb =
new ConfigBool(tr("Immediately Present XFB"), Config::GFX_HACK_IMMEDIATE_XFB, m_game_layer);
m_skip_duplicate_xfbs = new ConfigBool(tr("Skip Presenting Duplicate Frames"),
Config::GFX_HACK_SKIP_DUPLICATE_XFBS, m_game_layer);
xfb_layout->addWidget(m_store_xfb_copies);
xfb_layout->addWidget(m_immediate_xfb);
@ -104,13 +114,14 @@ void HacksWidget::CreateWidgets()
other_box->setLayout(other_layout);
m_fast_depth_calculation =
new ConfigBool(tr("Fast Depth Calculation"), Config::GFX_FAST_DEPTH_CALC);
new ConfigBool(tr("Fast Depth Calculation"), Config::GFX_FAST_DEPTH_CALC, m_game_layer);
m_disable_bounding_box =
new ConfigBool(tr("Disable Bounding Box"), Config::GFX_HACK_BBOX_ENABLE, true);
m_vertex_rounding = new ConfigBool(tr("Vertex Rounding"), Config::GFX_HACK_VERTEX_ROUNDING);
m_save_texture_cache_state =
new ConfigBool(tr("Save Texture Cache to State"), Config::GFX_SAVE_TEXTURE_CACHE_TO_STATE);
m_vi_skip = new ConfigBool(tr("VBI Skip"), Config::GFX_HACK_VI_SKIP);
new ConfigBool(tr("Disable Bounding Box"), Config::GFX_HACK_BBOX_ENABLE, m_game_layer, true);
m_vertex_rounding =
new ConfigBool(tr("Vertex Rounding"), Config::GFX_HACK_VERTEX_ROUNDING, m_game_layer);
m_save_texture_cache_state = new ConfigBool(
tr("Save Texture Cache to State"), Config::GFX_SAVE_TEXTURE_CACHE_TO_STATE, m_game_layer);
m_vi_skip = new ConfigBool(tr("VBI Skip"), Config::GFX_HACK_VI_SKIP, m_game_layer);
other_layout->addWidget(m_fast_depth_calculation, 0, 0);
other_layout->addWidget(m_disable_bounding_box, 0, 1);

View File

@ -6,15 +6,22 @@
#include <QWidget>
class ConfigBool;
class GameConfigWidget;
class GraphicsWindow;
class QLabel;
class ToolTipSlider;
namespace Config
{
class Layer;
} // namespace Config
class HacksWidget final : public QWidget
{
Q_OBJECT
public:
explicit HacksWidget(GraphicsWindow* parent);
HacksWidget(GameConfigWidget* parent, Config::Layer* layer);
private:
void LoadSettings();
@ -45,6 +52,8 @@ private:
ConfigBool* m_vi_skip;
ConfigBool* m_save_texture_cache_state;
Config::Layer* m_game_layer = nullptr;
void CreateWidgets();
void ConnectWidgets();
void AddDescriptions();

View File

@ -138,10 +138,9 @@ void GraphicsModListWidget::RefreshModList()
for (const GraphicsModConfig& mod : m_mod_group.GetMods())
{
// If no group matches the mod's features, or if the mod has no features, skip it
if (std::none_of(mod.m_features.begin(), mod.m_features.end(),
[&groups](const GraphicsModFeatureConfig& feature) {
return groups.contains(feature.m_group);
}))
if (std::ranges::none_of(mod.m_features, [&groups](const GraphicsModFeatureConfig& feature) {
return groups.contains(feature.m_group);
}))
{
continue;
}

View File

@ -598,9 +598,8 @@ void ShakeMappingIndicator::Update(float elapsed_seconds)
m_position_samples.push_front(ShakeSample{m_motion_state.position / MAX_DISTANCE});
const bool any_non_zero_samples =
std::any_of(m_position_samples.begin(), m_position_samples.end(),
[](const ShakeSample& s) { return s.state.LengthSquared() != 0.0; });
const bool any_non_zero_samples = std::ranges::any_of(
m_position_samples, [](const ShakeSample& s) { return s.state.LengthSquared() != 0.0; });
// Only start moving the line if there's non-zero data.
if (m_grid_line_position || any_non_zero_samples)

View File

@ -228,6 +228,8 @@ void MappingWidget::AddSettingWidgets(QFormLayout* layout, ControllerEmu::Contro
const auto hbox = new QHBoxLayout;
hbox->addWidget(setting_widget);
setting_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
hbox->addWidget(CreateSettingAdvancedMappingButton(*setting));
layout->addRow(tr(setting->GetUIName()), hbox);

View File

@ -52,8 +52,8 @@ ConvertDialog::ConvertDialog(QList<std::shared_ptr<const UICommon::GameFile>> fi
m_format->addItem(QStringLiteral("GCZ"), static_cast<int>(DiscIO::BlobType::GCZ));
m_format->addItem(QStringLiteral("WIA"), static_cast<int>(DiscIO::BlobType::WIA));
m_format->addItem(QStringLiteral("RVZ"), static_cast<int>(DiscIO::BlobType::RVZ));
if (std::all_of(m_files.begin(), m_files.end(),
[](const auto& file) { return file->GetBlobType() == DiscIO::BlobType::PLAIN; }))
if (std::ranges::all_of(
m_files, [](const auto& file) { return file->GetBlobType() == DiscIO::BlobType::PLAIN; }))
{
m_format->setCurrentIndex(m_format->count() - 1);
}
@ -153,7 +153,7 @@ void ConvertDialog::OnFormatChanged()
// To support legacy versions of dolphin, we have to check the GCZ block size
// See DiscIO::IsGCZBlockSizeLegacyCompatible() for details
const auto block_size_ok = [this](int block_size) {
return std::all_of(m_files.begin(), m_files.end(), [block_size](const auto& file) {
return std::ranges::all_of(m_files, [block_size](const auto& file) {
return DiscIO::IsGCZBlockSizeLegacyCompatible(block_size, file->GetVolumeSize());
});
};
@ -248,9 +248,8 @@ void ConvertDialog::OnFormatChanged()
m_compression->setEnabled(m_compression->count() > 1);
// Block scrubbing of RVZ containers and Datel discs
const bool scrubbing_allowed =
format != DiscIO::BlobType::RVZ &&
std::none_of(m_files.begin(), m_files.end(), std::mem_fn(&UICommon::GameFile::IsDatelDisc));
const bool scrubbing_allowed = format != DiscIO::BlobType::RVZ &&
std::ranges::none_of(m_files, &UICommon::GameFile::IsDatelDisc);
m_scrub->setEnabled(scrubbing_allowed);
if (!scrubbing_allowed)
@ -309,7 +308,7 @@ void ConvertDialog::Convert()
}
if (!scrub && format == DiscIO::BlobType::GCZ &&
std::any_of(m_files.begin(), m_files.end(), [](const auto& file) {
std::ranges::any_of(m_files, [](const auto& file) {
return file->GetPlatform() == DiscIO::Platform::WiiDisc && !file->IsDatelDisc();
}))
{
@ -321,7 +320,7 @@ void ConvertDialog::Convert()
}
}
if (std::any_of(m_files.begin(), m_files.end(), std::mem_fn(&UICommon::GameFile::IsNKit)))
if (std::ranges::any_of(m_files, &UICommon::GameFile::IsNKit))
{
if (!ShowAreYouSureDialog(
tr("Dolphin can't convert NKit files to non-NKit files. Converting an NKit file in "

View File

@ -379,6 +379,12 @@ void CodeWidget::UpdateSymbols()
{
QString name = QString::fromStdString(symbol.second.name);
// If the symbol has an object name, add it to the entry name.
if (!symbol.second.object_name.empty())
{
name += QString::fromStdString(fmt::format(" ({})", symbol.second.object_name));
}
auto* item = new QListWidgetItem(name);
if (name == selection)
item->setSelected(true);
@ -408,8 +414,17 @@ void CodeWidget::UpdateFunctionCalls(const Common::Symbol* symbol)
if (call_symbol)
{
const QString name =
QString::fromStdString(fmt::format("> {} ({:08x})", call_symbol->name, addr));
QString name;
if (!call_symbol->object_name.empty())
{
name = QString::fromStdString(
fmt::format("< {} ({}, {:08x})", call_symbol->name, call_symbol->object_name, addr));
}
else
{
name = QString::fromStdString(fmt::format("< {} ({:08x})", call_symbol->name, addr));
}
if (!name.contains(filter, Qt::CaseInsensitive))
continue;
@ -433,8 +448,17 @@ void CodeWidget::UpdateFunctionCallers(const Common::Symbol* symbol)
if (caller_symbol)
{
const QString name =
QString::fromStdString(fmt::format("< {} ({:08x})", caller_symbol->name, addr));
QString name;
if (!caller_symbol->object_name.empty())
{
name = QString::fromStdString(fmt::format("< {} ({}, {:08x})", caller_symbol->name,
caller_symbol->object_name, addr));
}
else
{
name = QString::fromStdString(fmt::format("< {} ({:08x})", caller_symbol->name, addr));
}
if (!name.contains(filter, Qt::CaseInsensitive))
continue;

View File

@ -241,6 +241,7 @@
-->
<ItemGroup>
<ClInclude Include="Config\CheatCodeEditor.h" />
<ClInclude Include="Config\ConfigControls\ConfigControl.h" />
<ClInclude Include="Config\GameConfigEdit.h" />
<ClInclude Include="Config\Mapping\MappingCommon.h" />
<ClInclude Include="Config\Mapping\MappingIndicator.h" />

View File

@ -377,15 +377,15 @@ void GameList::ShowContextMenu(const QPoint&)
{
const auto selected_games = GetSelectedGames();
if (std::all_of(selected_games.begin(), selected_games.end(),
[](const auto& game) { return game->ShouldAllowConversion(); }))
if (std::ranges::all_of(selected_games,
[](const auto& game) { return game->ShouldAllowConversion(); }))
{
menu->addAction(tr("Convert Selected Files..."), this, &GameList::ConvertFile);
menu->addSeparator();
}
if (std::all_of(selected_games.begin(), selected_games.end(),
[](const auto& game) { return DiscIO::IsWii(game->GetPlatform()); }))
if (std::ranges::all_of(selected_games,
[](const auto& game) { return DiscIO::IsWii(game->GetPlatform()); }))
{
menu->addAction(tr("Export Wii Saves"), this, &GameList::ExportWiiSave);
menu->addSeparator();
@ -546,13 +546,13 @@ void GameList::OpenProperties()
return;
PropertiesDialog* properties = new PropertiesDialog(this, *game);
// Since the properties dialog locks the game file, it's important to free it as soon as it's
// closed so that the file can be moved or deleted.
properties->setAttribute(Qt::WA_DeleteOnClose, true);
connect(properties, &PropertiesDialog::OpenGeneralSettings, this, &GameList::OpenGeneralSettings);
connect(properties, &PropertiesDialog::OpenGraphicsSettings, this,
&GameList::OpenGraphicsSettings);
connect(properties, &PropertiesDialog::finished, this,
[properties]() { properties->deleteLater(); });
#ifdef USE_RETRO_ACHIEVEMENTS
connect(properties, &PropertiesDialog::OpenAchievementSettings, this,
&GameList::OpenAchievementSettings);

View File

@ -309,11 +309,9 @@ void Settings::RemovePath(const QString& qpath)
std::string path = qpath.toStdString();
std::vector<std::string> paths = Config::GetIsoPaths();
auto new_end = std::remove(paths.begin(), paths.end(), path);
if (new_end == paths.end())
if (std::erase(paths, path) == 0)
return;
paths.erase(new_end, paths.end());
Config::SetIsoPaths(paths);
emit PathRemoved(qpath);
}

Some files were not shown because too many files have changed in this diff Show More