From b5da4e9d48978cb7cd2b27717c4878ad728440cf Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Fri, 24 Jun 2022 23:01:25 -0500 Subject: [PATCH 001/175] CMake: Don't omit frame pointer on RelWithDebInfo builds --- CMake/CheckAndAddFlag.cmake | 2 ++ CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CMake/CheckAndAddFlag.cmake b/CMake/CheckAndAddFlag.cmake index 226e570833..4fe7d5be18 100644 --- a/CMake/CheckAndAddFlag.cmake +++ b/CMake/CheckAndAddFlag.cmake @@ -21,6 +21,8 @@ function(check_and_add_flag var flag) set(genexp_config_test "1") if(ARGV2 STREQUAL "DEBUG_ONLY") set(genexp_config_test "$") + elseif(ARGV2 STREQUAL "NO_DEBINFO_ONLY") + set(genexp_config_test "$,$>>") elseif(ARGV2 STREQUAL "RELEASE_ONLY") set(genexp_config_test "$>") elseif(ARGV2) diff --git a/CMakeLists.txt b/CMakeLists.txt index 163ea2c943..9596373862 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -354,7 +354,7 @@ else() check_and_add_flag(VISIBILITY_INLINES_HIDDEN -fvisibility-inlines-hidden) check_and_add_flag(VISIBILITY_HIDDEN -fvisibility=hidden) - check_and_add_flag(FOMIT_FRAME_POINTER -fomit-frame-pointer RELEASE_ONLY) + check_and_add_flag(FOMIT_FRAME_POINTER -fomit-frame-pointer NO_DEBINFO_ONLY) dolphin_compile_definitions(_DEBUG DEBUG_ONLY) check_and_add_flag(GGDB -ggdb DEBUG_ONLY) From f2a074f4f8c047d3037249ae940a300f431af721 Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Fri, 24 Jun 2022 23:08:18 -0500 Subject: [PATCH 002/175] Common:X64ABI: Make proper stack frames --- Source/Core/Common/x64ABI.cpp | 15 +++++++++++++-- Source/Core/VideoCommon/VertexLoaderX64.cpp | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Source/Core/Common/x64ABI.cpp b/Source/Core/Common/x64ABI.cpp index 0c4f0770ca..424e8a483e 100644 --- a/Source/Core/Common/x64ABI.cpp +++ b/Source/Core/Common/x64ABI.cpp @@ -43,11 +43,18 @@ void XEmitter::ABI_CalculateFrameSize(BitSet32 mask, size_t rsp_alignment, size_ size_t XEmitter::ABI_PushRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size) { + mask[RSP] = false; // Stack pointer is never pushed size_t shadow, subtraction, xmm_offset; ABI_CalculateFrameSize(mask, rsp_alignment, needed_frame_size, &shadow, &subtraction, &xmm_offset); - for (int r : mask& ABI_ALL_GPRS) + if (mask[RBP]) + { + // Make a nice stack frame for any debuggers or profilers that might be looking at this + PUSH(RBP); + MOV(64, R(RBP), R(RSP)); + } + for (int r : mask& ABI_ALL_GPRS & ~BitSet32{RBP}) PUSH((X64Reg)r); if (subtraction) @@ -65,6 +72,7 @@ size_t XEmitter::ABI_PushRegistersAndAdjustStack(BitSet32 mask, size_t rsp_align void XEmitter::ABI_PopRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size) { + mask[RSP] = false; // Stack pointer is never pushed size_t shadow, subtraction, xmm_offset; ABI_CalculateFrameSize(mask, rsp_alignment, needed_frame_size, &shadow, &subtraction, &xmm_offset); @@ -80,9 +88,12 @@ void XEmitter::ABI_PopRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignmen for (int r = 15; r >= 0; r--) { - if (mask[r]) + if (r != RBP && mask[r]) POP((X64Reg)r); } + // RSP is pushed first and popped last to make debuggers/profilers happy + if (mask[RBP]) + POP(RBP); } void XEmitter::MOVTwo(int bits, Gen::X64Reg dst1, Gen::X64Reg src1, s32 offset1, Gen::X64Reg dst2, diff --git a/Source/Core/VideoCommon/VertexLoaderX64.cpp b/Source/Core/VideoCommon/VertexLoaderX64.cpp index 27e24e6f28..c92f94bed9 100644 --- a/Source/Core/VideoCommon/VertexLoaderX64.cpp +++ b/Source/Core/VideoCommon/VertexLoaderX64.cpp @@ -404,6 +404,7 @@ void VertexLoaderX64::GenerateVertexLoader() BitSet32 regs = {src_reg, dst_reg, scratch1, scratch2, scratch3, remaining_reg, skipped_reg, base_reg}; regs &= ABI_ALL_CALLEE_SAVED; + regs[RBP] = true; // Give us a stack frame ABI_PushRegistersAndAdjustStack(regs, 0); // Backup count since we're going to count it down. From 235b67707f71498d7a3848eed6f4b83491795839 Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Thu, 11 Aug 2022 23:57:28 -0500 Subject: [PATCH 003/175] Common:X64ABI: Work around clang-format being dumb --- Source/Core/Common/x64ABI.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Core/Common/x64ABI.cpp b/Source/Core/Common/x64ABI.cpp index 424e8a483e..9f45c132a6 100644 --- a/Source/Core/Common/x64ABI.cpp +++ b/Source/Core/Common/x64ABI.cpp @@ -54,13 +54,13 @@ size_t XEmitter::ABI_PushRegistersAndAdjustStack(BitSet32 mask, size_t rsp_align PUSH(RBP); MOV(64, R(RBP), R(RSP)); } - for (int r : mask& ABI_ALL_GPRS & ~BitSet32{RBP}) + for (int r : (mask & ABI_ALL_GPRS & ~BitSet32{RBP})) PUSH((X64Reg)r); if (subtraction) SUB(64, R(RSP), subtraction >= 0x80 ? Imm32((u32)subtraction) : Imm8((u8)subtraction)); - for (int x : mask& ABI_ALL_FPRS) + for (int x : (mask & ABI_ALL_FPRS)) { MOVAPD(MDisp(RSP, (int)xmm_offset), (X64Reg)(x - 16)); xmm_offset += 16; @@ -77,7 +77,7 @@ void XEmitter::ABI_PopRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignmen ABI_CalculateFrameSize(mask, rsp_alignment, needed_frame_size, &shadow, &subtraction, &xmm_offset); - for (int x : mask& ABI_ALL_FPRS) + for (int x : (mask & ABI_ALL_FPRS)) { MOVAPD((X64Reg)(x - 16), MDisp(RSP, (int)xmm_offset)); xmm_offset += 16; From f2be35c7cdc97e53f3d9f424ec29d3cbeb6989d5 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 19 Feb 2023 16:30:06 +0100 Subject: [PATCH 004/175] VideoCommon: Reword the unknown opcode error message When faced with this error, users often don't try disabling dual core, even though the error message suggests it. Perhaps the message is just too long and lists too many things? To try to improve the situation, I'm rewording the message and making it say different things depending on what settings you are using. --- Source/Core/Core/CoreTiming.cpp | 5 ++ Source/Core/Core/CoreTiming.h | 2 + Source/Core/VideoCommon/CommandProcessor.cpp | 54 ++++++++++++++++---- Source/Core/VideoCommon/CommandProcessor.h | 2 +- Source/Core/VideoCommon/Fifo.h | 1 + Source/Core/VideoCommon/OpcodeDecoding.cpp | 4 +- 6 files changed, 56 insertions(+), 12 deletions(-) diff --git a/Source/Core/Core/CoreTiming.cpp b/Source/Core/Core/CoreTiming.cpp index aa7c501c92..2e4e5112c1 100644 --- a/Source/Core/Core/CoreTiming.cpp +++ b/Source/Core/Core/CoreTiming.cpp @@ -417,6 +417,11 @@ bool CoreTimingManager::GetVISkip() const return m_throttle_disable_vi_int && g_ActiveConfig.bVISkip && !Core::WantsDeterminism(); } +bool CoreTimingManager::UseSyncOnSkipIdle() const +{ + return m_config_sync_on_skip_idle; +} + void CoreTimingManager::LogPendingEvents() const { auto clone = m_event_queue; diff --git a/Source/Core/Core/CoreTiming.h b/Source/Core/Core/CoreTiming.h index 1ddb13f948..525b7e8913 100644 --- a/Source/Core/Core/CoreTiming.h +++ b/Source/Core/Core/CoreTiming.h @@ -150,6 +150,8 @@ public: TimePoint GetCPUTimePoint(s64 cyclesLate) const; // Used by Dolphin Analytics bool GetVISkip() const; // Used By VideoInterface + bool UseSyncOnSkipIdle() const; + private: Globals m_globals; diff --git a/Source/Core/VideoCommon/CommandProcessor.cpp b/Source/Core/VideoCommon/CommandProcessor.cpp index 75cedcf96f..955bb337b9 100644 --- a/Source/Core/VideoCommon/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/CommandProcessor.cpp @@ -12,6 +12,7 @@ #include "Common/CommonTypes.h" #include "Common/Flag.h" #include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" #include "Core/ConfigManager.h" #include "Core/CoreTiming.h" #include "Core/HW/GPFifo.h" @@ -637,7 +638,8 @@ void CommandProcessorManager::SetCpClearRegister() { } -void CommandProcessorManager::HandleUnknownOpcode(u8 cmd_byte, const u8* buffer, bool preprocess) +void CommandProcessorManager::HandleUnknownOpcode(Core::System& system, u8 cmd_byte, + const u8* buffer, bool preprocess) { const auto& fifo = m_fifo; @@ -693,16 +695,50 @@ void CommandProcessorManager::HandleUnknownOpcode(u8 cmd_byte, const u8* buffer, { m_is_fifo_error_seen = true; - // TODO(Omega): Maybe dump FIFO to file on this error + // The panic alert contains an explanatory part that's worded differently depending on the + // user's settings, so as to offer the most relevant advice to the user. + const char* advice; + if (IsOnThread(system) && !system.GetFifo().UseDeterministicGPUThread()) + { + if (!system.GetCoreTiming().UseSyncOnSkipIdle() && !system.GetFifo().UseSyncGPU()) + { +// The SyncOnSkipIdle setting is only in the Android GUI, so we use the INI name on other platforms. +// +// TODO: Mark the Android string as translatable once we have translations on Android. It's +// currently untranslatable so translators won't try to look up how they translated "Synchronize +// GPU Thread" and "On Idle Skipping" and then not find those strings and become confused. +#ifdef ANDROID + advice = "Please change the \"Synchronize GPU Thread\" setting to \"On Idle Skipping\"! " + "It's currently set to \"Never\", which makes this problem very likely to happen."; +#else + // i18n: Please leave SyncOnSkipIdle and True untranslated. + // The user needs to enter these terms as-is in an INI file. + advice = _trans("Please change the \"SyncOnSkipIdle\" setting to \"True\"! " + "It's currently disabled, which makes this problem very likely to happen."); +#endif + } + else + { + advice = _trans( + "This error is usually caused by the emulated GPU desyncing with the emulated CPU. " + "Turn off the \"Dual Core\" setting to avoid this."); + } + } + else + { + advice = _trans( + "This error is usually caused by the emulated GPU desyncing with the emulated CPU, " + "but your current settings make this unlikely to happen. If this error is stopping the " + "game from working, please report it to the developers."); + } + PanicAlertFmtT("GFX FIFO: Unknown Opcode ({0:#04x} @ {1}, preprocess={2}).\n" - "This means one of the following:\n" - "* The emulated GPU got desynced, disabling dual core can help\n" - "* Command stream corrupted by some spurious memory bug\n" - "* This really is an unknown opcode (unlikely)\n" - "* Some other sort of bug\n\n" - "Further errors will be sent to the Video Backend log and\n" + "\n" + "{3}\n" + "\n" + "Further errors will be sent to the Video Backend log and " "Dolphin will now likely crash or hang.", - cmd_byte, fmt::ptr(buffer), preprocess); + cmd_byte, fmt::ptr(buffer), preprocess, Common::GetStringT(advice)); } } diff --git a/Source/Core/VideoCommon/CommandProcessor.h b/Source/Core/VideoCommon/CommandProcessor.h index 34b1e8701f..ccc381b535 100644 --- a/Source/Core/VideoCommon/CommandProcessor.h +++ b/Source/Core/VideoCommon/CommandProcessor.h @@ -177,7 +177,7 @@ public: void SetCpControlRegister(Core::System& system); void SetCpStatusRegister(Core::System& system); - void HandleUnknownOpcode(u8 cmd_byte, const u8* buffer, bool preprocess); + void HandleUnknownOpcode(Core::System& system, u8 cmd_byte, const u8* buffer, bool preprocess); // This one is shared between gfx thread and emulator thread. // It is only used by the Fifo and by the CommandProcessor. diff --git a/Source/Core/VideoCommon/Fifo.h b/Source/Core/VideoCommon/Fifo.h index 1db91eeb91..20648562b4 100644 --- a/Source/Core/VideoCommon/Fifo.h +++ b/Source/Core/VideoCommon/Fifo.h @@ -54,6 +54,7 @@ public: void PauseAndLock(Core::System& system, bool doLock, bool unpauseOnUnlock); void UpdateWantDeterminism(Core::System& system, bool want); bool UseDeterministicGPUThread() const { return m_use_deterministic_gpu_thread; } + bool UseSyncGPU() const { return m_config_sync_gpu; } // In deterministic GPU thread mode this waits for the GPU to be done with pending work. void SyncGPU(SyncGPUReason reason, bool may_move_read_ptr = true); diff --git a/Source/Core/VideoCommon/OpcodeDecoding.cpp b/Source/Core/VideoCommon/OpcodeDecoding.cpp index 9ffb59a277..4d43c6e66a 100644 --- a/Source/Core/VideoCommon/OpcodeDecoding.cpp +++ b/Source/Core/VideoCommon/OpcodeDecoding.cpp @@ -219,8 +219,8 @@ public: } else { - Core::System::GetInstance().GetCommandProcessor().HandleUnknownOpcode(opcode, data, - is_preprocess); + auto& system = Core::System::GetInstance(); + system.GetCommandProcessor().HandleUnknownOpcode(system, opcode, data, is_preprocess); m_cycles += 1; } } From 497e938c8ce8233cecd39fcec4201ea9ac3a84a0 Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Mon, 9 Jan 2023 18:46:25 -0800 Subject: [PATCH 005/175] Jit_LoadStoreFloating: Minor tidying The inst.SIMM_16 change is for readability (though it also fixes a warning about potentially unintended uses of `||`). The fallback change is because `b` is only meaningful for indexed instructions; this could theoretically lead to unintended fallbacks (but it seems unlikely). --- Source/Core/Core/PowerPC/Jit64/Jit_LoadStoreFloating.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Core/Core/PowerPC/Jit64/Jit_LoadStoreFloating.cpp b/Source/Core/Core/PowerPC/Jit64/Jit_LoadStoreFloating.cpp index 0af85a9f26..fd41ff0404 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit_LoadStoreFloating.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit_LoadStoreFloating.cpp @@ -22,7 +22,7 @@ void Jit64::lfXXX(UGeckoInstruction inst) bool indexed = inst.OPCD == 31; bool update = indexed ? !!(inst.SUBOP10 & 0x20) : !!(inst.OPCD & 1); bool single = indexed ? !(inst.SUBOP10 & 0x40) : !(inst.OPCD & 2); - update &= indexed || inst.SIMM_16; + update &= indexed || (inst.SIMM_16 != 0); int d = inst.RD; int a = inst.RA; @@ -93,7 +93,7 @@ void Jit64::stfXXX(UGeckoInstruction inst) bool indexed = inst.OPCD == 31; bool update = indexed ? !!(inst.SUBOP10 & 0x20) : !!(inst.OPCD & 1); bool single = indexed ? !(inst.SUBOP10 & 0x40) : !(inst.OPCD & 2); - update &= indexed || inst.SIMM_16; + update &= indexed || (inst.SIMM_16 != 0); int s = inst.RS; int a = inst.RA; @@ -101,7 +101,7 @@ void Jit64::stfXXX(UGeckoInstruction inst) s32 imm = (s16)inst.SIMM_16; int accessSize = single ? 32 : 64; - FALLBACK_IF(update && jo.memcheck && a == b); + FALLBACK_IF(update && jo.memcheck && indexed && a == b); if (single) { From c55e08ff738d58ce4b49988c6d9a6cf06e45c069 Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Mon, 9 Jan 2023 18:47:02 -0800 Subject: [PATCH 006/175] Jit64: Attempt to fix updating stores with an immediate value See https://bugs.dolphin-emu.org/issues/13144 --- Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp | 2 +- Source/Core/Core/PowerPC/Jit64/Jit_LoadStoreFloating.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp b/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp index 65ac5dab21..1e6019e0e0 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp @@ -510,7 +510,7 @@ void Jit64::stX(UGeckoInstruction inst) } else { - RCOpArg Ra = gpr.UseNoImm(a, RCMode::Write); + RCOpArg Ra = gpr.RevertableBind(a, RCMode::Write); RegCache::Realize(Ra); MemoryExceptionCheck(); MOV(32, Ra, Imm32(addr)); diff --git a/Source/Core/Core/PowerPC/Jit64/Jit_LoadStoreFloating.cpp b/Source/Core/Core/PowerPC/Jit64/Jit_LoadStoreFloating.cpp index fd41ff0404..7ac34f5d6e 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit_LoadStoreFloating.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit_LoadStoreFloating.cpp @@ -144,7 +144,7 @@ void Jit64::stfXXX(UGeckoInstruction inst) } else { - RCOpArg Ra = gpr.UseNoImm(a, RCMode::Write); + RCOpArg Ra = gpr.RevertableBind(a, RCMode::Write); RegCache::Realize(Ra); MemoryExceptionCheck(); MOV(32, Ra, Imm32(addr)); From d861b8caca1f4a8725ae72f936c492faf9c37502 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Sun, 26 Feb 2023 22:45:55 +0100 Subject: [PATCH 007/175] Config: Add setting for SD card file size when converting. --- Source/Core/Common/FatFsUtil.cpp | 12 +++++++++--- Source/Core/Core/Config/MainSettings.cpp | 2 ++ Source/Core/Core/Config/MainSettings.h | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Source/Core/Common/FatFsUtil.cpp b/Source/Core/Common/FatFsUtil.cpp index f194dc22a3..88d746e8ea 100644 --- a/Source/Core/Common/FatFsUtil.cpp +++ b/Source/Core/Common/FatFsUtil.cpp @@ -26,6 +26,8 @@ #include "Common/ScopeGuard.h" #include "Common/StringUtil.h" +#include "Core/Config/MainSettings.h" + enum : u32 { SECTOR_SIZE = 512, @@ -513,9 +515,13 @@ bool SyncSDFolderToSDImage(const std::function& cancelled, bool determin if (!CheckIfFATCompatible(root)) return false; - u64 size = GetSize(root); - // Allocate a reasonable amount of free space - size += std::clamp(size / 2, MebibytesToBytes(512), GibibytesToBytes(8)); + u64 size = Config::Get(Config::MAIN_WII_SD_CARD_FILESIZE); + if (size == 0) + { + size = GetSize(root); + // Allocate a reasonable amount of free space + size += std::clamp(size / 2, MebibytesToBytes(512), GibibytesToBytes(8)); + } size = AlignUp(size, MAX_CLUSTER_SIZE); std::lock_guard lk(s_fatfs_mutex); diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 1a7c523d6e..7023ec411b 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -10,6 +10,7 @@ #include "AudioCommon/AudioCommon.h" #include "Common/Assert.h" #include "Common/CommonPaths.h" +#include "Common/CommonTypes.h" #include "Common/Config/Config.h" #include "Common/EnumMap.h" #include "Common/FileUtil.h" @@ -175,6 +176,7 @@ const Info& GetInfoForSimulateKonga(int channel) const Info MAIN_WII_SD_CARD{{System::Main, "Core", "WiiSDCard"}, true}; const Info MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC{ {System::Main, "Core", "WiiSDCardEnableFolderSync"}, false}; +const Info MAIN_WII_SD_CARD_FILESIZE{{System::Main, "Core", "WiiSDCardFilesize"}, 0}; const Info MAIN_WII_KEYBOARD{{System::Main, "Core", "WiiKeyboard"}, false}; const Info MAIN_WIIMOTE_CONTINUOUS_SCANNING{ {System::Main, "Core", "WiimoteContinuousScanning"}, false}; diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index 546696476f..dd8f1632f8 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -9,6 +9,7 @@ #include #include "Common/Common.h" +#include "Common/CommonTypes.h" #include "Common/Config/Config.h" #include "DiscIO/Enums.h" @@ -98,6 +99,7 @@ const Info& GetInfoForAdapterRumble(int channel); const Info& GetInfoForSimulateKonga(int channel); extern const Info MAIN_WII_SD_CARD; extern const Info MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC; +extern const Info MAIN_WII_SD_CARD_FILESIZE; extern const Info MAIN_WII_KEYBOARD; extern const Info MAIN_WIIMOTE_CONTINUOUS_SCANNING; extern const Info MAIN_WIIMOTE_ENABLE_SPEAKER; From 9600bf1af98c090329d13c7193ff43cab5143f76 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Sun, 26 Feb 2023 22:46:05 +0100 Subject: [PATCH 008/175] Qt/WiiPane: Add setting for SD card file size when converting. --- Source/Core/DolphinQt/Settings/WiiPane.cpp | 56 ++++++++++++++++++++++ Source/Core/DolphinQt/Settings/WiiPane.h | 1 + 2 files changed, 57 insertions(+) diff --git a/Source/Core/DolphinQt/Settings/WiiPane.cpp b/Source/Core/DolphinQt/Settings/WiiPane.cpp index 40674ece3a..959a4aa1fc 100644 --- a/Source/Core/DolphinQt/Settings/WiiPane.cpp +++ b/Source/Core/DolphinQt/Settings/WiiPane.cpp @@ -3,7 +3,9 @@ #include "DolphinQt/Settings/WiiPane.h" +#include #include +#include #include #include @@ -53,6 +55,36 @@ static int TranslateSensorBarPosition(int position) return position; } +namespace +{ +struct SDSizeComboEntry +{ + u64 size; + const char* name; +}; +static constexpr u64 MebibytesToBytes(u64 mebibytes) +{ + return mebibytes * 1024u * 1024u; +} +static constexpr u64 GibibytesToBytes(u64 gibibytes) +{ + return MebibytesToBytes(gibibytes * 1024u); +} +constexpr std::array sd_size_combo_entries{ + SDSizeComboEntry{0, _trans("Auto")}, + SDSizeComboEntry{MebibytesToBytes(64), _trans("64 MiB")}, + SDSizeComboEntry{MebibytesToBytes(128), _trans("128 MiB")}, + SDSizeComboEntry{MebibytesToBytes(256), _trans("256 MiB")}, + SDSizeComboEntry{MebibytesToBytes(512), _trans("512 MiB")}, + SDSizeComboEntry{GibibytesToBytes(1), _trans("1 GiB")}, + SDSizeComboEntry{GibibytesToBytes(2), _trans("2 GiB")}, + SDSizeComboEntry{GibibytesToBytes(4), _trans("4 GiB (SDHC)")}, + SDSizeComboEntry{GibibytesToBytes(8), _trans("8 GiB (SDHC)")}, + SDSizeComboEntry{GibibytesToBytes(16), _trans("16 GiB (SDHC)")}, + SDSizeComboEntry{GibibytesToBytes(32), _trans("32 GiB (SDHC)")}, +}; +} // namespace + WiiPane::WiiPane(QWidget* parent) : QWidget(parent) { CreateLayout(); @@ -94,6 +126,8 @@ void WiiPane::ConnectLayout() connect(m_sd_card_checkbox, &QCheckBox::toggled, this, &WiiPane::OnSaveConfig); connect(m_allow_sd_writes_checkbox, &QCheckBox::toggled, this, &WiiPane::OnSaveConfig); connect(m_sync_sd_folder_checkbox, &QCheckBox::toggled, this, &WiiPane::OnSaveConfig); + connect(m_sd_card_size_combo, qOverload(&QComboBox::currentIndexChanged), this, + &WiiPane::OnSaveConfig); // Whitelisted USB Passthrough Devices connect(m_whitelist_usb_list, &QListWidget::itemClicked, this, &WiiPane::ValidateSelectionState); @@ -219,6 +253,13 @@ void WiiPane::CreateSDCard() ++row; } + m_sd_card_size_combo = new QComboBox(); + for (size_t i = 0; i < sd_size_combo_entries.size(); ++i) + m_sd_card_size_combo->addItem(tr(sd_size_combo_entries[i].name)); + sd_settings_group_layout->addWidget(new QLabel(tr("SD Card File Size:")), row, 0); + sd_settings_group_layout->addWidget(m_sd_card_size_combo, row, 1); + ++row; + m_sd_pack_button = new NonDefaultQPushButton(tr("Convert Folder to File Now")); m_sd_unpack_button = new NonDefaultQPushButton(tr("Convert File to Folder Now")); connect(m_sd_pack_button, &QPushButton::clicked, [this] { @@ -360,6 +401,13 @@ void WiiPane::LoadConfig() m_allow_sd_writes_checkbox->setChecked(Config::Get(Config::MAIN_ALLOW_SD_WRITES)); m_sync_sd_folder_checkbox->setChecked(Config::Get(Config::MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC)); + const u64 sd_card_size = Config::Get(Config::MAIN_WII_SD_CARD_FILESIZE); + for (size_t i = 0; i < sd_size_combo_entries.size(); ++i) + { + if (sd_size_combo_entries[i].size == sd_card_size) + m_sd_card_size_combo->setCurrentIndex(static_cast(i)); + } + PopulateUSBPassthroughListWidget(); m_wiimote_ir_sensor_position->setCurrentIndex( @@ -390,6 +438,14 @@ void WiiPane::OnSaveConfig() Config::SetBase(Config::MAIN_ALLOW_SD_WRITES, m_allow_sd_writes_checkbox->isChecked()); Config::SetBase(Config::MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC, m_sync_sd_folder_checkbox->isChecked()); + + const int sd_card_size_index = m_sd_card_size_combo->currentIndex(); + if (sd_card_size_index >= 0 && + static_cast(sd_card_size_index) < sd_size_combo_entries.size()) + { + Config::SetBase(Config::MAIN_WII_SD_CARD_FILESIZE, + sd_size_combo_entries[sd_card_size_index].size); + } } void WiiPane::ValidateSelectionState() diff --git a/Source/Core/DolphinQt/Settings/WiiPane.h b/Source/Core/DolphinQt/Settings/WiiPane.h index dbceeed907..6f4b3fe8de 100644 --- a/Source/Core/DolphinQt/Settings/WiiPane.h +++ b/Source/Core/DolphinQt/Settings/WiiPane.h @@ -62,6 +62,7 @@ private: QCheckBox* m_sd_card_checkbox; QCheckBox* m_allow_sd_writes_checkbox; QCheckBox* m_sync_sd_folder_checkbox; + QComboBox* m_sd_card_size_combo; QLineEdit* m_sd_raw_edit; QLineEdit* m_sd_sync_folder_edit; QPushButton* m_sd_pack_button; From 24a697514e69e8611b3865cfc2403b62bc8ee6f5 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 7 May 2023 18:40:28 +0200 Subject: [PATCH 009/175] ControllerInterface/Android: Use InputEvent.getDeviceId Instead of InputEvent.getDevice followed by InputDevice.getId. Should hopefully fix https://bugs.dolphin-emu.org/issues/13237, while also being simpler. --- .../ControllerInterface/Android/Android.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index a4bd7a3d46..45909f0bf8 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -37,7 +37,6 @@ jclass s_input_device_class; jmethodID s_input_device_get_device_ids; jmethodID s_input_device_get_device; jmethodID s_input_device_get_controller_number; -jmethodID s_input_device_get_id; jmethodID s_input_device_get_motion_ranges; jmethodID s_input_device_get_name; jmethodID s_input_device_get_sources; @@ -50,7 +49,7 @@ jmethodID s_motion_range_get_min; jmethodID s_motion_range_get_source; jclass s_input_event_class; -jmethodID s_input_event_get_device; +jmethodID s_input_event_get_device_id; jclass s_key_event_class; jmethodID s_key_event_get_action; @@ -798,7 +797,6 @@ void Init() env->GetStaticMethodID(s_input_device_class, "getDevice", "(I)Landroid/view/InputDevice;"); s_input_device_get_controller_number = env->GetMethodID(s_input_device_class, "getControllerNumber", "()I"); - s_input_device_get_id = env->GetMethodID(s_input_device_class, "getId", "()I"); s_input_device_get_motion_ranges = env->GetMethodID(s_input_device_class, "getMotionRanges", "()Ljava/util/List;"); s_input_device_get_name = @@ -817,8 +815,7 @@ void Init() const jclass input_event_class = env->FindClass("android/view/InputEvent"); s_input_event_class = reinterpret_cast(env->NewGlobalRef(input_event_class)); - s_input_event_get_device = - env->GetMethodID(s_input_event_class, "getDevice", "()Landroid/view/InputDevice;"); + s_input_event_get_device_id = env->GetMethodID(s_input_event_class, "getDeviceId", "()I"); const jclass key_event_class = env->FindClass("android/view/KeyEvent"); s_key_event_class = reinterpret_cast(env->NewGlobalRef(key_event_class)); @@ -990,9 +987,7 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch return JNI_FALSE; } - const jobject input_device = env->CallObjectMethod(key_event, s_input_event_get_device); - const jint device_id = env->CallIntMethod(input_device, s_input_device_get_id); - env->DeleteLocalRef(input_device); + const jint device_id = env->CallIntMethod(key_event, s_input_event_get_device_id); const std::shared_ptr device = FindDevice(device_id); if (!device) return JNI_FALSE; @@ -1022,9 +1017,7 @@ JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchGenericMotionEvent( JNIEnv* env, jclass, jobject motion_event) { - const jobject input_device = env->CallObjectMethod(motion_event, s_input_event_get_device); - const jint device_id = env->CallIntMethod(input_device, s_input_device_get_id); - env->DeleteLocalRef(input_device); + const jint device_id = env->CallIntMethod(motion_event, s_input_event_get_device_id); const std::shared_ptr device = FindDevice(device_id); if (!device) return JNI_FALSE; From 82f70cdf945697c71c5be1b4dd61be7a425659c0 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 7 May 2023 18:44:43 +0200 Subject: [PATCH 010/175] ControllerInterface/Android: Add some missing DeleteLocalRef calls --- .../InputCommon/ControllerInterface/Android/Android.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index 45909f0bf8..1fa4f1c42b 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -816,16 +816,19 @@ void Init() const jclass input_event_class = env->FindClass("android/view/InputEvent"); s_input_event_class = reinterpret_cast(env->NewGlobalRef(input_event_class)); s_input_event_get_device_id = env->GetMethodID(s_input_event_class, "getDeviceId", "()I"); + env->DeleteLocalRef(input_event_class); const jclass key_event_class = env->FindClass("android/view/KeyEvent"); s_key_event_class = reinterpret_cast(env->NewGlobalRef(key_event_class)); s_key_event_get_action = env->GetMethodID(s_key_event_class, "getAction", "()I"); s_key_event_get_keycode = env->GetMethodID(s_key_event_class, "getKeyCode", "()I"); + env->DeleteLocalRef(key_event_class); const jclass motion_event_class = env->FindClass("android/view/MotionEvent"); s_motion_event_class = reinterpret_cast(env->NewGlobalRef(motion_event_class)); s_motion_event_get_axis_value = env->GetMethodID(s_motion_event_class, "getAxisValue", "(I)F"); s_motion_event_get_source = env->GetMethodID(s_motion_event_class, "getSource", "()I"); + env->DeleteLocalRef(motion_event_class); const jclass controller_interface_class = env->FindClass("org/dolphinemu/dolphinemu/features/input/model/ControllerInterface"); @@ -844,6 +847,7 @@ void Init() "()Lorg/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager;"); s_controller_interface_vibrate = env->GetStaticMethodID(s_controller_interface_class, "vibrate", "(Landroid/os/Vibrator;)V"); + env->DeleteLocalRef(controller_interface_class); const jclass sensor_event_listener_class = env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener"); @@ -861,6 +865,7 @@ void Init() env->GetMethodID(s_sensor_event_listener_class, "getAxisNames", "()[Ljava/lang/String;"); s_sensor_event_listener_get_negative_axes = env->GetMethodID(s_sensor_event_listener_class, "getNegativeAxes", "()[Z"); + env->DeleteLocalRef(sensor_event_listener_class); const jclass dolphin_vibrator_manager_class = env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager"); @@ -870,6 +875,7 @@ void Init() env->GetMethodID(s_dolphin_vibrator_manager_class, "getVibrator", "(I)Landroid/os/Vibrator;"); s_dolphin_vibrator_manager_get_vibrator_ids = env->GetMethodID(s_dolphin_vibrator_manager_class, "getVibratorIds", "()[I"); + env->DeleteLocalRef(dolphin_vibrator_manager_class); jintArray keycodes_array = CreateKeyCodesArray(env); s_keycodes_array = reinterpret_cast(env->NewGlobalRef(keycodes_array)); From a902480cb053478af3f8df25aab8a36395685378 Mon Sep 17 00:00:00 2001 From: Jeffrey Bosboom Date: Sat, 15 Apr 2023 02:13:00 -0700 Subject: [PATCH 011/175] XInput2: Make button state a u32 Because we care how many bits it has, not its arithmetic range. No functional change on all supported platforms. --- .../Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp | 3 +-- Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h | 7 ++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index 07d8e4bfa5..db7052996c 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -441,8 +441,7 @@ ControlState KeyboardMouse::Key::GetState() const return (m_keyboard[m_keycode / 8] & (1 << (m_keycode % 8))) != 0; } -KeyboardMouse::Button::Button(unsigned int index, unsigned int* buttons) - : m_buttons(buttons), m_index(index) +KeyboardMouse::Button::Button(unsigned int index, u32* buttons) : m_buttons(buttons), m_index(index) { name = fmt::format("Click {}", m_index + 1); } diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h index ff2c787b1d..52c78ab752 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h @@ -13,6 +13,7 @@ extern "C" { #include } +#include "Common/CommonTypes.h" #include "Common/Matrix.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" @@ -26,7 +27,7 @@ private: struct State { std::array keyboard; - unsigned int buttons; + u32 buttons; Common::Vec2 cursor; Common::Vec3 axis; Common::Vec3 relative_mouse; @@ -52,11 +53,11 @@ private: { public: std::string GetName() const override { return name; } - Button(unsigned int index, unsigned int* buttons); + Button(unsigned int index, u32* buttons); ControlState GetState() const override; private: - const unsigned int* m_buttons; + const u32* m_buttons; const unsigned int m_index; std::string name; }; From 46540ea42babe23a6579d231dd3ea180b1fa39ad Mon Sep 17 00:00:00 2001 From: Jeffrey Bosboom Date: Sat, 15 Apr 2023 02:13:01 -0700 Subject: [PATCH 012/175] XInput2: Request XInput 2.1 We need XInput 2.1 to get raw events on the root window even while another client has a grab. We currently use raw events for relative mouse input, and upcoming commits will use raw events for buttons and keys. --- .../ControllerInterface/Xlib/XInput2.cpp | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index db7052996c..4f93509f15 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -5,12 +5,14 @@ #include +#include #include #include #include #include +#include "Common/Logging/Log.h" #include "Common/StringUtil.h" #include "Core/Host.h" @@ -54,6 +56,14 @@ // more cleanly separate each scroll wheel click, but risks dropping some inputs #define SCROLL_AXIS_DECAY 1.1f +namespace +{ +// We need XInput 2.1 to get raw events on the root window even while another +// client has a grab. If we request 2.2 or later, the server will not generate +// emulated button presses from touch events, so we want exactly 2.1. +constexpr int XINPUT_MAJOR = 2, XINPUT_MINOR = 1; +} // namespace + namespace ciface::XInput2 { // This function will add zero or more KeyboardMouse objects to devices. @@ -67,13 +77,18 @@ void PopulateDevices(void* const hwnd) // verify that the XInput extension is available if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) + { + WARN_LOG_FMT(CONTROLLERINTERFACE, "XInput extension not available (XQueryExtension)"); return; + } - // verify that the XInput extension is at at least version 2.0 - int major = 2, minor = 0; - - if (XIQueryVersion(dpy, &major, &minor) != Success) + int major = XINPUT_MAJOR, minor = XINPUT_MINOR; + if (XIQueryVersion(dpy, &major, &minor) != Success || major < XINPUT_MAJOR || + (major == XINPUT_MAJOR && minor < XINPUT_MINOR)) + { + WARN_LOG_FMT(CONTROLLERINTERFACE, "XInput extension not available (XIQueryVersion)"); return; + } // register all master devices with Dolphin @@ -160,6 +175,9 @@ KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboar // "context." m_display = XOpenDisplay(nullptr); + int major = XINPUT_MAJOR, minor = XINPUT_MINOR; + XIQueryVersion(m_display, &major, &minor); + // should always be 1 int unused; XIDeviceInfo* const pointer_device = XIQueryDevice(m_display, pointer_deviceid, &unused); From b2a98c41eef80a28519d87abeb28c8869af1e403 Mon Sep 17 00:00:00 2001 From: Jeffrey Bosboom Date: Sat, 15 Apr 2023 02:13:02 -0700 Subject: [PATCH 013/175] XInput2: Use raw events and queries for buttons and keys In X, the ButtonPress events generated when a mouse button is pressed have a special property: if they don't activate an existing passive grab, the X server automatically activates the "implicit passive grab" on behalf of the client the event is delivered to. This ensures the ButtonRelease event is delivered to the same client even if the pointer moves between windows, but it also causes all events from that pointer to be delivered exclusively to that client. As a consequence of the implicit passive grab, for each window, only one client can listen for ButtonPress events; any further listeners would never receive the event. XInput 1 made the implicit grab optional and explicit by allowing clients to listen for DeviceButtonPress events without DeviceButtonPressGrab events. XInput 2 does not have a separate grab event class, but multiple clients can listen for XI_ButtonPress on the same window. When a button is pressed, the X server first tries to deliver an XI_ButtonPress event; if no clients want it, then the server tries to deliver a DeviceButtonPress event; if no clients want it, then the server tries to deliver a ButtonPress event. Once an event has been delivered, event processing stops and earlier protocol levels are not considered. The reason for this rule is not obviously documented, but it is probably because of the implicit passive grab; a client receiving a ButtonPress event assumes it is the only client receiving that event, and later protocols maintain that property for backward compatibility. Before this commit, Dolphin listened for XI_ButtonPress events on the root window. This interferes with window managers that expect to receive ButtonPress events on the root window, such as awesome and Openbox. In Openbox, applications are often launched from a menu activated by clicking on the root window, and desktops are switched by scroll wheel input on the root window. This makes normal use of other applications difficult when Dolphin is open (though Openbox keyboard shortcuts still work). Conversely, Dolphin only receives XI_ButtonPress events for clicks on the root window or window decorations (title bars), not on Dolphin's windows' content or the render window. In window managers that use a "virtual root window" covering the actual root window, such as Mutter running in X, Dolphin and the window manager do not conflict, but clicks delivered to other applications using XInput2 (for testing, try xinput --test-xi2) are not seen by Dolphin, which is relevant when background input is enabled. This commit changes Dolphin to listen for XI_RawButtonPress (and the raw versions of other events); Dolphin was already listening to XI_RawMotion for raw mouse movement. Raw events are always and exclusively delivered to the root window and are delivered to every client listening for them, so Dolphin will not interfere with (or be interfered with by) other applications listening for events. As part of being raw, button numbers and keycodes in raw events have not had mapping applied. If a left-handed user swapped the left and right buttons on their mouse, raw events do not reflect that. It is possible to query the mappings for each device and apply them manually, but that would require a fair amount of code, including listening for mapping changes. Instead, Dolphin now uses the events only to set a "changed" flag, then queries the current button and key state after processing all events. Dolphin was already querying the pointer to get its absolute position and querying the keyboard to filter the key bitmap it created from events; now Dolphin also uses the button state from the pointer query and uses the keyboard query directly. Queries have a performance cost because they are synchronous requests to the X server (Dolphin waits for the result). Commit 2b640a4f made the pointer query conditional on receiving a motion event to "cut down on round trips", but commit bbb12a75 added an unconditional keyboard query, and there have apparently been no performance complaints. This commit queries the pointer slightly more often (on button events in addition to motion), but only queries the keyboard after key events, so the total rate of queries should be substantially reduced. Fixes: https://bugs.dolphin-emu.org/issues/10668 --- .../ControllerInterface/Xlib/XInput2.cpp | 90 ++++++++----------- 1 file changed, 37 insertions(+), 53 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index 4f93509f15..4c47da4fc5 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -190,8 +190,8 @@ KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboar { unsigned char mask_buf[(XI_LASTEVENT + 7) / 8] = {}; - XISetMask(mask_buf, XI_ButtonPress); - XISetMask(mask_buf, XI_ButtonRelease); + XISetMask(mask_buf, XI_RawButtonPress); + XISetMask(mask_buf, XI_RawButtonRelease); XISetMask(mask_buf, XI_RawMotion); XIEventMask mask; @@ -203,9 +203,8 @@ KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboar { unsigned char mask_buf[(XI_LASTEVENT + 7) / 8] = {}; - XISetMask(mask_buf, XI_KeyPress); - XISetMask(mask_buf, XI_KeyRelease); - XISetMask(mask_buf, XI_FocusOut); + XISetMask(mask_buf, XI_RawKeyPress); + XISetMask(mask_buf, XI_RawKeyRelease); XIEventMask mask; mask.mask = mask_buf; @@ -272,6 +271,25 @@ void KeyboardMouse::UpdateCursor(bool should_center_mouse) const auto win_width = std::max(win_attribs.width, 1); const auto win_height = std::max(win_attribs.height, 1); + { + XIButtonState button_state; + XIModifierState mods; + XIGroupState group; + + // Get the absolute position of the mouse pointer and the button state. + XIQueryPointer(m_display, pointer_deviceid, m_window, &root, &child, &root_x, &root_y, &win_x, + &win_y, &button_state, &mods, &group); + + // X buttons are 1-indexed, so to get 32 button bits we need a larger type + // for the shift. + u64 buttons_zero_indexed = 0; + std::memcpy(&buttons_zero_indexed, button_state.mask, + std::min(button_state.mask_len, sizeof(m_state.buttons))); + m_state.buttons = buttons_zero_indexed >> 1; + + free(button_state.mask); + } + if (should_center_mouse) { win_x = win_width / 2; @@ -281,19 +299,6 @@ void KeyboardMouse::UpdateCursor(bool should_center_mouse) g_controller_interface.SetMouseCenteringRequested(false); } - else - { - // unused-- we're not interested in button presses here, as those are - // updated using events - XIButtonState button_state; - XIModifierState mods; - XIGroupState group; - - XIQueryPointer(m_display, pointer_deviceid, m_window, &root, &child, &root_x, &root_y, &win_x, - &win_y, &button_state, &mods, &group); - - free(button_state.mask); - } const auto window_scale = g_controller_interface.GetWindowInputScale(); @@ -309,10 +314,10 @@ void KeyboardMouse::UpdateInput() // for the axis controls float delta_x = 0.0f, delta_y = 0.0f, delta_z = 0.0f; double delta_delta; - bool mouse_moved = false; + bool update_mouse = false, update_keyboard = false; - // Iterate through the event queue - update the axis controls, mouse - // button controls, and keyboard controls. + // Iterate through the event queue, processing raw pointer motion events and + // noting whether the button or key state has changed. XEvent event; while (XPending(m_display)) { @@ -325,28 +330,21 @@ void KeyboardMouse::UpdateInput() if (!XGetEventData(m_display, &event.xcookie)) continue; - // only one of these will get used - XIDeviceEvent* dev_event = (XIDeviceEvent*)event.xcookie.data; - XIRawEvent* raw_event = (XIRawEvent*)event.xcookie.data; - switch (event.xcookie.evtype) { - case XI_ButtonPress: - m_state.buttons |= 1 << (dev_event->detail - 1); + case XI_RawButtonPress: + case XI_RawButtonRelease: + update_mouse = true; break; - case XI_ButtonRelease: - m_state.buttons &= ~(1 << (dev_event->detail - 1)); - break; - case XI_KeyPress: - m_state.keyboard[dev_event->detail / 8] |= 1 << (dev_event->detail % 8); - break; - case XI_KeyRelease: - m_state.keyboard[dev_event->detail / 8] &= ~(1 << (dev_event->detail % 8)); + case XI_RawKeyPress: + case XI_RawKeyRelease: + update_keyboard = true; break; case XI_RawMotion: { - mouse_moved = true; + update_mouse = true; + XIRawEvent* raw_event = (XIRawEvent*)event.xcookie.data; float values[4] = {}; size_t value_idx = 0; @@ -377,10 +375,6 @@ void KeyboardMouse::UpdateInput() break; } - case XI_FocusOut: - // Clear keyboard state on FocusOut as we will not be receiving KeyRelease events. - m_state.keyboard.fill(0); - break; } XFreeEventData(m_display, &event.xcookie); @@ -400,23 +394,13 @@ void KeyboardMouse::UpdateInput() m_state.axis.z += delta_z; m_state.axis.z /= SCROLL_AXIS_DECAY; - // Get the absolute position of the mouse pointer const bool should_center_mouse = g_controller_interface.IsMouseCenteringRequested() && Host_RendererHasFocus(); - if (mouse_moved || should_center_mouse) + if (update_mouse || should_center_mouse) UpdateCursor(should_center_mouse); - // KeyRelease and FocusOut events are sometimes not received. - // Cycling Alt-Tab and landing on the same window results in a stuck "Alt" key. - // Unpressed keys are released here. - // Because we called XISetClientPointer in the constructor, XQueryKeymap - // will return the state of the associated keyboard, even if it isn't the - // first master keyboard. (XInput2 doesn't provide a function to query - // keyboard state.) - std::array keyboard; - XQueryKeymap(m_display, keyboard.data()); - for (size_t i = 0; i != keyboard.size(); ++i) - m_state.keyboard[i] &= keyboard[i]; + if (update_keyboard) + XQueryKeymap(m_display, m_state.keyboard.data()); } std::string KeyboardMouse::GetName() const From 620955d397d4c40aca06a0811294ccb90a6434f0 Mon Sep 17 00:00:00 2001 From: Jeffrey Bosboom Date: Sat, 15 Apr 2023 02:13:03 -0700 Subject: [PATCH 014/175] XInput2: Listen to master devices only A comment removed by this commit gives two reasons for listening to slave devices, both of which no longer apply: - "Only slaves emit raw motion events": perhaps this was true when the comment was written, but now master devices provide raw motion events along with the other raw events. - "Selecting slave keyboards avoids dealing with key focus": we get raw key events regardless of the focus. Listening to both master and slave devices results in duplicate raw events. For button and key events, that's a tiny waste of time setting the update flag a second time, but for raw mouse events the raw motion will be processed twice. That makes this commit a user-facing change. --- .../ControllerInterface/Xlib/XInput2.cpp | 40 ++----------------- .../ControllerInterface/Xlib/XInput2.h | 1 - 2 files changed, 4 insertions(+), 37 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index 4c47da4fc5..d90139fe03 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -130,39 +130,6 @@ void PopulateDevices(void* const hwnd) XIFreeDeviceInfo(all_masters); } -// Apply the event mask to the device and all its slaves. Only used in the -// constructor. Remember, each KeyboardMouse has its own copy of the event -// stream, which is how multiple event masks can "coexist." -void KeyboardMouse::SelectEventsForDevice(XIEventMask* mask, int deviceid) -{ - // Set the event mask for the master device. - mask->deviceid = deviceid; - XISelectEvents(m_display, DefaultRootWindow(m_display), mask, 1); - - // Query all the master device's slaves and set the same event mask for - // those too. There are two reasons we want to do this. For mouse devices, - // we want the raw motion events, and only slaves (i.e. physical hardware - // devices) emit those. For keyboard devices, selecting slaves avoids - // dealing with key focus. - - int num_slaves; - XIDeviceInfo* const all_slaves = XIQueryDevice(m_display, XIAllDevices, &num_slaves); - - for (int i = 0; i < num_slaves; i++) - { - XIDeviceInfo* const slave = &all_slaves[i]; - if ((slave->use != XISlavePointer && slave->use != XISlaveKeyboard) || - slave->attachment != deviceid) - { - continue; - } - mask->deviceid = slave->deviceid; - XISelectEvents(m_display, DefaultRootWindow(m_display), mask, 1); - } - - XIFreeDeviceInfo(all_slaves); -} - KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboard, double scroll_increment_) : m_window(window), xi_opcode(opcode), pointer_deviceid(pointer), keyboard_deviceid(keyboard), @@ -198,7 +165,8 @@ KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboar mask.mask = mask_buf; mask.mask_len = sizeof(mask_buf); - SelectEventsForDevice(&mask, pointer_deviceid); + mask.deviceid = pointer_deviceid; + XISelectEvents(m_display, DefaultRootWindow(m_display), &mask, 1); } { @@ -209,8 +177,8 @@ KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboar XIEventMask mask; mask.mask = mask_buf; mask.mask_len = sizeof(mask_buf); - - SelectEventsForDevice(&mask, keyboard_deviceid); + mask.deviceid = keyboard_deviceid; + XISelectEvents(m_display, DefaultRootWindow(m_display), &mask, 1); } // Keyboard Keys diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h index 52c78ab752..6a4f8436f1 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h @@ -108,7 +108,6 @@ private: }; private: - void SelectEventsForDevice(XIEventMask* mask, int deviceid); void UpdateCursor(bool should_center_mouse); public: From 8a78538b2dc1fc37e3574057100c88973be12def Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 22 May 2023 11:10:13 +0200 Subject: [PATCH 015/175] Android: Don't show analytics dialog for destroyed activity Should fix one of the reported crashes on Google Play. The issue can happen if you leave the activity during directory initialization. --- .../src/main/java/org/dolphinemu/dolphinemu/utils/Analytics.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Analytics.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Analytics.kt index c8aa483d58..5f3d2fc3cb 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Analytics.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Analytics.kt @@ -20,7 +20,7 @@ object Analytics { @JvmStatic fun checkAnalyticsInit(activity: FragmentActivity) { - AfterDirectoryInitializationRunner().runWithoutLifecycle { + AfterDirectoryInitializationRunner().runWithLifecycle(activity) { if (!BooleanSetting.MAIN_ANALYTICS_PERMISSION_ASKED.boolean) { AnalyticsDialog().show(activity.supportFragmentManager, AnalyticsDialog.TAG) } From a7b09413f9aa8a76c06f3371ca692ccac173f974 Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Mon, 22 May 2023 22:21:56 -0400 Subject: [PATCH 016/175] Added Notification Popup for Achievement Unlocked Added an OnScreenDisplay message to HandleAchievementTriggeredEvent to display a message when a player has unlocked an achievement. The message includes the title of the achievement and its point value based on the game data. To match RetroAchievements' website interface, the message is blue if the unlock is casual and gold for challenge. --- Source/Core/Core/AchievementManager.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index 9c8b3d1a4a..bcd275b10c 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -5,6 +5,8 @@ #include "Core/AchievementManager.h" +#include + #include #include "Common/HttpRequest.h" @@ -14,6 +16,7 @@ #include "Core/PowerPC/MMU.h" #include "Core/System.h" #include "DiscIO/Volume.h" +#include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/VideoEvents.h" static constexpr bool hardcore_mode_enabled = false; @@ -506,6 +509,11 @@ void AchievementManager::HandleAchievementTriggeredEvent(const rc_runtime_event_ return; it->second.session_unlock_count++; m_queue.EmplaceItem([this, runtime_event] { AwardAchievement(runtime_event->id); }); + AchievementId game_data_index = it->second.game_data_index; + OSD::AddMessage(fmt::format("Unlocked: {} ({})", m_game_data.achievements[game_data_index].title, + m_game_data.achievements[game_data_index].points), + OSD::Duration::VERY_LONG, + (hardcore_mode_enabled) ? OSD::Color::YELLOW : OSD::Color::CYAN); ActivateDeactivateAchievement(runtime_event->id, Config::Get(Config::RA_ACHIEVEMENTS_ENABLED), Config::Get(Config::RA_UNOFFICIAL_ENABLED), Config::Get(Config::RA_ENCORE_ENABLED)); From c20d0ae9e12d69b0b36f5d378979e4b1df7e27f2 Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Mon, 22 May 2023 22:41:32 -0400 Subject: [PATCH 017/175] Added Notification Popup for Leaderboard Scored Added an OnScreenDisplay message to HandleLeaderboardTriggeredEvent to display a message when a player has completed a leaderboard. The message includes the title of the achievement and the player's score/time. --- Source/Core/Core/AchievementManager.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index bcd275b10c..9599ebe619 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -523,6 +523,16 @@ void AchievementManager::HandleLeaderboardTriggeredEvent(const rc_runtime_event_ { m_queue.EmplaceItem( [this, runtime_event] { SubmitLeaderboard(runtime_event->id, runtime_event->value); }); + for (u32 ix = 0; ix < m_game_data.num_leaderboards; ix++) + { + if (m_game_data.leaderboards[ix].id == runtime_event->id) + { + OSD::AddMessage(fmt::format("Scored {} on leaderboard: {}", runtime_event->value, + m_game_data.leaderboards[ix].title), + OSD::Duration::VERY_LONG, OSD::Color::YELLOW); + break; + } + } } // Every RetroAchievements API call, with only a partial exception for fetch_image, follows From a4b7f43f2126ea82abc1548c7849b839208abec7 Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Tue, 23 May 2023 00:10:57 -0400 Subject: [PATCH 018/175] Added Notification Popup for Leaderboard Started Added HandleLeaderboardStartedEvent with an OnScreenDisplay message when a player has triggered a leaderboard's start conditions. The message includes the title of the achievement. --- Source/Core/Core/AchievementManager.cpp | 16 ++++++++++++++++ Source/Core/Core/AchievementManager.h | 1 + 2 files changed, 17 insertions(+) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index 9599ebe619..abf6d89367 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -266,6 +266,9 @@ void AchievementManager::AchievementEventHandler(const rc_runtime_event_t* runti case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED: HandleAchievementTriggeredEvent(runtime_event); break; + case RC_RUNTIME_EVENT_LBOARD_STARTED: + HandleLeaderboardStartedEvent(runtime_event); + break; case RC_RUNTIME_EVENT_LBOARD_TRIGGERED: HandleLeaderboardTriggeredEvent(runtime_event); break; @@ -519,6 +522,19 @@ void AchievementManager::HandleAchievementTriggeredEvent(const rc_runtime_event_ Config::Get(Config::RA_ENCORE_ENABLED)); } +void AchievementManager::HandleLeaderboardStartedEvent(const rc_runtime_event_t* runtime_event) +{ + for (u32 ix = 0; ix < m_game_data.num_leaderboards; ix++) + { + if (m_game_data.leaderboards[ix].id == runtime_event->id) + { + OSD::AddMessage(fmt::format("Attempting leaderboard: {}", m_game_data.leaderboards[ix].title), + OSD::Duration::VERY_LONG, OSD::Color::GREEN); + break; + } + } +} + void AchievementManager::HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event) { m_queue.EmplaceItem( diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h index b6cbcde732..3c5855c7c5 100644 --- a/Source/Core/Core/AchievementManager.h +++ b/Source/Core/Core/AchievementManager.h @@ -80,6 +80,7 @@ private: ResponseType PingRichPresence(const RichPresence& rich_presence); void HandleAchievementTriggeredEvent(const rc_runtime_event_t* runtime_event); + void HandleLeaderboardStartedEvent(const rc_runtime_event_t* runtime_event); void HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event); template From b0eb4ccb8012c308941ef1eba279f73622d29d60 Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Tue, 23 May 2023 20:45:04 -0400 Subject: [PATCH 019/175] Added Notification Popup for Leaderboard Canceled Added HandleLeaderboardStartedEvent with an OnScreenDisplay message when a player has triggered a leaderboard's failure conditions. The message includes the title of the achievement. --- Source/Core/Core/AchievementManager.cpp | 13 +++++++++++++ Source/Core/Core/AchievementManager.h | 1 + 2 files changed, 14 insertions(+) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index abf6d89367..2b6aa0b9a4 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -535,6 +535,19 @@ void AchievementManager::HandleLeaderboardStartedEvent(const rc_runtime_event_t* } } +void AchievementManager::HandleLeaderboardCanceledEvent(const rc_runtime_event_t* runtime_event) +{ + for (u32 ix = 0; ix < m_game_data.num_leaderboards; ix++) + { + if (m_game_data.leaderboards[ix].id == runtime_event->id) + { + OSD::AddMessage(fmt::format("Failed leaderboard: {}", m_game_data.leaderboards[ix].title), + OSD::Duration::VERY_LONG, OSD::Color::RED); + break; + } + } +} + void AchievementManager::HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event) { m_queue.EmplaceItem( diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h index 3c5855c7c5..7c89be7c59 100644 --- a/Source/Core/Core/AchievementManager.h +++ b/Source/Core/Core/AchievementManager.h @@ -81,6 +81,7 @@ private: void HandleAchievementTriggeredEvent(const rc_runtime_event_t* runtime_event); void HandleLeaderboardStartedEvent(const rc_runtime_event_t* runtime_event); + void HandleLeaderboardCanceledEvent(const rc_runtime_event_t* runtime_event); void HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event); template From 22af13f9e02f0177bbca560ea56bcd7d08346cfe Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Tue, 23 May 2023 20:47:38 -0400 Subject: [PATCH 020/175] Added Notification Popups for No Achievement Data Added OnScreenDisplay messages to LoadGameByFilenameAsync to notify the player when any of the potential failures occur. --- Source/Core/Core/AchievementManager.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index 2b6aa0b9a4..4420bec0cf 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -126,6 +126,8 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path, if (resolve_hash_response != ResponseType::SUCCESS || m_game_id == 0) { callback(resolve_hash_response); + OSD::AddMessage("No RetroAchievements data found for this game.", OSD::Duration::VERY_LONG, + OSD::Color::RED); return; } @@ -133,11 +135,18 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path, if (start_session_response != ResponseType::SUCCESS) { callback(start_session_response); + OSD::AddMessage("Failed to connect to RetroAchievements server.", OSD::Duration::VERY_LONG, + OSD::Color::RED); return; } const auto fetch_game_data_response = FetchGameData(); m_is_game_loaded = fetch_game_data_response == ResponseType::SUCCESS; + if (!m_is_game_loaded) + { + OSD::AddMessage("Unable to retrieve data from RetroAchievements server.", + OSD::Duration::VERY_LONG, OSD::Color::RED); + } // Claim the lock, then queue the fetch unlock data calls, then initialize the unlock map in // ActivateDeactiveAchievements. This allows the calls to process while initializing the From aa0224a8ab3f5e450de0fcc1e53906029e6b93b1 Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Tue, 23 May 2023 08:38:53 -0400 Subject: [PATCH 021/175] Added TallyScore method to AchievementManager Added a TallyScore method to AchievementManager to calculate the player's current scores (soft and hard) against the total number of points across all achievements. Includes a PointSpread structure to hold all points and counts. The Unlock Map now includes point values for each achievement to aid in this calculation; this is populated at insert time, and Unofficial achievements are tallied as zero. --- Source/Core/Core/AchievementManager.cpp | 31 +++++++++++++++++++++++-- Source/Core/Core/AchievementManager.h | 15 +++++++++++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index 4420bec0cf..b6a1879642 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -185,8 +185,11 @@ void AchievementManager::ActivateDeactivateAchievements() bool encore = Config::Get(Config::RA_ENCORE_ENABLED); for (u32 ix = 0; ix < m_game_data.num_achievements; ix++) { - auto iter = - m_unlock_map.insert({m_game_data.achievements[ix].id, UnlockStatus{.game_data_index = ix}}); + u32 points = (m_game_data.achievements[ix].category == RC_ACHIEVEMENT_CATEGORY_UNOFFICIAL) ? + 0 : + m_game_data.achievements[ix].points; + auto iter = m_unlock_map.insert( + {m_game_data.achievements[ix].id, UnlockStatus{.game_data_index = ix, .points = points}}); ActivateDeactivateAchievement(iter.first->first, enabled, unofficial, encore); } } @@ -573,6 +576,30 @@ void AchievementManager::HandleLeaderboardTriggeredEvent(const rc_runtime_event_ } } +AchievementManager::PointSpread AchievementManager::TallyScore() const +{ + PointSpread spread{}; + for (const auto& entry : m_unlock_map) + { + u32 points = entry.second.points; + spread.total_count++; + spread.total_points += points; + if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::HARDCORE || + (hardcore_mode_enabled && entry.second.session_unlock_count > 0)) + { + spread.hard_unlocks++; + spread.hard_points += points; + } + else if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::SOFTCORE || + entry.second.session_unlock_count > 0) + { + spread.soft_unlocks++; + spread.soft_points += points; + } + } + return spread; +} + // Every RetroAchievements API call, with only a partial exception for fetch_image, follows // the same design pattern (here, X is the name of the call): // Create a specific rc_api_X_request_t struct and populate with the necessary values diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h index 7c89be7c59..da11f930ce 100644 --- a/Source/Core/Core/AchievementManager.h +++ b/Source/Core/Core/AchievementManager.h @@ -41,6 +41,16 @@ public: }; using ResponseCallback = std::function; + struct PointSpread + { + u32 total_count; + u32 total_points; + u32 hard_unlocks; + u32 hard_points; + u32 soft_unlocks; + u32 soft_points; + }; + static AchievementManager* GetInstance(); void Init(); ResponseType Login(const std::string& password); @@ -84,6 +94,8 @@ private: void HandleLeaderboardCanceledEvent(const rc_runtime_event_t* runtime_event); void HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event); + PointSpread TallyScore() const; + template ResponseType Request(RcRequest rc_request, RcResponse* rc_response, const std::function& init_request, @@ -107,7 +119,8 @@ private: SOFTCORE, HARDCORE } remote_unlock_status = UnlockType::LOCKED; - int session_unlock_count = 0; + u32 session_unlock_count = 0; + u32 points = 0; }; std::unordered_map m_unlock_map; From 184108905425b3fc453dfc210ae966a69668bac3 Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Tue, 23 May 2023 08:51:54 -0400 Subject: [PATCH 022/175] Added Notification Popups for Game Start Added an OnScreenDisplay message to LoadGameByFilenameAsync to display a message when a player starts a game with achievements, notifying them of their current score. The score displayed is challenge points if the player is in challenge mode or challenge + casual if the player is in casual mode. A second message tells the player which mode they are in. To match RetroAchievements' website interface, the messages are blue for casual and gold for challenge. --- Source/Core/Core/AchievementManager.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index b6a1879642..10caf88387 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -156,6 +156,23 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path, std::lock_guard lg{m_lock}; LoadUnlockData([](ResponseType r_type) {}); ActivateDeactivateAchievements(); + PointSpread spread = TallyScore(); + if (hardcore_mode_enabled) + { + OSD::AddMessage(fmt::format("You have {}/{} achievements worth {}/{} points", + spread.hard_unlocks, spread.total_count, spread.hard_points, + spread.total_points), + OSD::Duration::VERY_LONG, OSD::Color::YELLOW); + OSD::AddMessage("Hardcore mode is ON", OSD::Duration::VERY_LONG, OSD::Color::YELLOW); + } + else + { + OSD::AddMessage(fmt::format("You have {}/{} achievements worth {}/{} points", + spread.hard_unlocks + spread.soft_unlocks, spread.total_count, + spread.hard_points + spread.soft_points, spread.total_points), + OSD::Duration::VERY_LONG, OSD::Color::CYAN); + OSD::AddMessage("Hardcore mode is OFF", OSD::Duration::VERY_LONG, OSD::Color::CYAN); + } } ActivateDeactivateLeaderboards(); ActivateDeactivateRichPresence(); @@ -591,7 +608,7 @@ AchievementManager::PointSpread AchievementManager::TallyScore() const spread.hard_points += points; } else if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::SOFTCORE || - entry.second.session_unlock_count > 0) + entry.second.session_unlock_count > 0) { spread.soft_unlocks++; spread.soft_points += points; From 57290a45e864dde336810089aec538adbb24f97f Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Tue, 23 May 2023 09:04:14 -0400 Subject: [PATCH 023/175] Added Notification Popups for Game Mastery Added OnScreenDisplay messages to HandleAchievementTriggeredEvent to display an extra congratulations message if the player has completed (unlocked all achievements in casual) or mastered (unlocked all achievements in challenge) the game. This also uses the display name retrieved when verifying credentials, which has now been added as a member field on AchievementManager. --- Source/Core/Core/AchievementManager.cpp | 14 ++++++++++++++ Source/Core/Core/AchievementManager.h | 1 + 2 files changed, 15 insertions(+) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index 10caf88387..81c3493dca 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -340,6 +340,7 @@ AchievementManager::ResponseType AchievementManager::VerifyCredentials(const std .username = username.c_str(), .api_token = api_token.c_str(), .password = password.c_str()}; ResponseType r_type = Request( login_request, &login_data, rc_api_init_login_request, rc_api_process_login_response); + m_display_name = login_data.display_name; if (r_type == ResponseType::SUCCESS) Config::SetBaseOrCurrent(Config::RA_API_TOKEN, login_data.api_token); rc_api_destroy_login_response(&login_data); @@ -546,6 +547,19 @@ void AchievementManager::HandleAchievementTriggeredEvent(const rc_runtime_event_ m_game_data.achievements[game_data_index].points), OSD::Duration::VERY_LONG, (hardcore_mode_enabled) ? OSD::Color::YELLOW : OSD::Color::CYAN); + PointSpread spread = TallyScore(); + if (spread.hard_points == spread.total_points) + { + OSD::AddMessage( + fmt::format("Congratulations! {} has mastered {}", m_display_name, m_game_data.title), + OSD::Duration::VERY_LONG, OSD::Color::YELLOW); + } + else if (spread.hard_points + spread.soft_points == spread.total_points) + { + OSD::AddMessage( + fmt::format("Congratulations! {} has completed {}", m_display_name, m_game_data.title), + OSD::Duration::VERY_LONG, OSD::Color::CYAN); + } ActivateDeactivateAchievement(runtime_event->id, Config::Get(Config::RA_ACHIEVEMENTS_ENABLED), Config::Get(Config::RA_UNOFFICIAL_ENABLED), Config::Get(Config::RA_ENCORE_ENABLED)); diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h index da11f930ce..8e01b9220e 100644 --- a/Source/Core/Core/AchievementManager.h +++ b/Source/Core/Core/AchievementManager.h @@ -104,6 +104,7 @@ private: rc_runtime_t m_runtime{}; Core::System* m_system{}; bool m_is_runtime_initialized = false; + std::string m_display_name; std::array m_game_hash{}; u32 m_game_id = 0; rc_api_fetch_game_data_response_t m_game_data{}; From 4d61ec1f4f5ae87f49ec85f30a3167d56c9706a4 Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Fri, 26 May 2023 01:59:51 +0000 Subject: [PATCH 024/175] CMake: Fix build with system Mbed TLS --- CMake/FindMBEDTLS.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMake/FindMBEDTLS.cmake b/CMake/FindMBEDTLS.cmake index 687994806f..8bca24e13b 100644 --- a/CMake/FindMBEDTLS.cmake +++ b/CMake/FindMBEDTLS.cmake @@ -1,8 +1,8 @@ -find_path(MBEDTLS_INCLUDE_DIR mbedtls/ssl.h) +find_path(MBEDTLS_INCLUDE_DIR mbedtls/ssl.h PATH_SUFFIXES mbedtls2) -find_library(MBEDTLS_LIBRARY mbedtls) -find_library(MBEDX509_LIBRARY mbedx509) -find_library(MBEDCRYPTO_LIBRARY mbedcrypto) +find_library(MBEDTLS_LIBRARY mbedtls PATH_SUFFIXES mbedtls2) +find_library(MBEDX509_LIBRARY mbedx509 PATH_SUFFIXES mbedtls2) +find_library(MBEDCRYPTO_LIBRARY mbedcrypto PATH_SUFFIXES mbedtls2) set(MBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) set(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY}) From 5eb1669573276d11474979145dd34dab3171406a Mon Sep 17 00:00:00 2001 From: jnaidu360 <128784914+jnaidu360@users.noreply.github.com> Date: Thu, 25 May 2023 15:27:00 -0700 Subject: [PATCH 025/175] Add Hotkeys for Skylanders Portal and Infinity Base Menus Adds two new hotkeys to open the menus for emulated USB devices- Skylanders Portal of Power and the Infinity Base. (Hotkeys only active when game is running). Portal menu: Default is . Infinity base: Default is --- Source/Core/Core/HotkeyManager.cpp | 9 ++++- Source/Core/Core/HotkeyManager.h | 4 ++ Source/Core/DolphinQt/CMakeLists.txt | 2 + .../DolphinQt/Config/Mapping/HotkeyUSBEmu.cpp | 39 +++++++++++++++++++ .../DolphinQt/Config/Mapping/HotkeyUSBEmu.h | 26 +++++++++++++ .../Config/Mapping/MappingWindow.cpp | 2 + Source/Core/DolphinQt/DolphinQt.vcxproj | 2 + Source/Core/DolphinQt/HotkeyScheduler.cpp | 7 ++++ Source/Core/DolphinQt/HotkeyScheduler.h | 3 ++ Source/Core/DolphinQt/MainWindow.cpp | 5 +++ 10 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 Source/Core/DolphinQt/Config/Mapping/HotkeyUSBEmu.cpp create mode 100644 Source/Core/DolphinQt/Config/Mapping/HotkeyUSBEmu.h diff --git a/Source/Core/Core/HotkeyManager.cpp b/Source/Core/Core/HotkeyManager.cpp index bfff976d0f..fee883fd0f 100644 --- a/Source/Core/Core/HotkeyManager.cpp +++ b/Source/Core/Core/HotkeyManager.cpp @@ -194,6 +194,9 @@ constexpr std::array s_hotkey_labels{{ _trans("2x"), _trans("3x"), _trans("4x"), + + _trans("Show Skylanders Portal"), + _trans("Show Infinity Base") }}; // clang-format on static_assert(NUM_HOTKEYS == s_hotkey_labels.size(), "Wrong count of hotkey_labels"); @@ -354,7 +357,8 @@ constexpr std::array s_groups_info = { {_trans("Other State Hotkeys"), HK_SAVE_FIRST_STATE, HK_DECREMENT_SELECTED_STATE_SLOT}, {_trans("GBA Core"), HK_GBA_LOAD, HK_GBA_RESET, true}, {_trans("GBA Volume"), HK_GBA_VOLUME_DOWN, HK_GBA_TOGGLE_MUTE, true}, - {_trans("GBA Window Size"), HK_GBA_1X, HK_GBA_4X, true}}}; + {_trans("GBA Window Size"), HK_GBA_1X, HK_GBA_4X, true}, + {_trans("USB Emulation Devices"), HK_SKYLANDERS_PORTAL, HK_INFINITY_BASE}}}; HotkeyManager::HotkeyManager() { @@ -490,4 +494,7 @@ void HotkeyManager::LoadDefaults(const ControllerInterface& ciface) set_key_expression(HK_GBA_3X, "`KP_3`"); set_key_expression(HK_GBA_4X, "`KP_4`"); #endif + + set_key_expression(HK_SKYLANDERS_PORTAL, hotkey_string({"Ctrl", "P"})); + set_key_expression(HK_INFINITY_BASE, hotkey_string({"Ctrl", "I"})); } diff --git a/Source/Core/Core/HotkeyManager.h b/Source/Core/Core/HotkeyManager.h index 134eba8e58..f675fd1a10 100644 --- a/Source/Core/Core/HotkeyManager.h +++ b/Source/Core/Core/HotkeyManager.h @@ -180,6 +180,9 @@ enum Hotkey HK_GBA_3X, HK_GBA_4X, + HK_SKYLANDERS_PORTAL, + HK_INFINITY_BASE, + NUM_HOTKEYS, }; @@ -211,6 +214,7 @@ enum HotkeyGroup : int HKGP_GBA_CORE, HKGP_GBA_VOLUME, HKGP_GBA_SIZE, + HKGP_USB_EMU, NUM_HOTKEY_GROUPS, }; diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index ac4d961ddf..116b2008f3 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -125,6 +125,8 @@ add_executable(dolphin-emu Config/Mapping/HotkeyStatesOther.h Config/Mapping/HotkeyTAS.cpp Config/Mapping/HotkeyTAS.h + Config/Mapping/HotkeyUSBEmu.cpp + Config/Mapping/HotkeyUSBEmu.h Config/Mapping/HotkeyWii.cpp Config/Mapping/HotkeyWii.h Config/Mapping/IOWindow.cpp diff --git a/Source/Core/DolphinQt/Config/Mapping/HotkeyUSBEmu.cpp b/Source/Core/DolphinQt/Config/Mapping/HotkeyUSBEmu.cpp new file mode 100644 index 0000000000..a92df83e4c --- /dev/null +++ b/Source/Core/DolphinQt/Config/Mapping/HotkeyUSBEmu.cpp @@ -0,0 +1,39 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "DolphinQt/Config/Mapping/HotkeyUSBEmu.h" + +#include +#include + +#include "Core/HotkeyManager.h" + +HotkeyUSBEmu::HotkeyUSBEmu(MappingWindow* window) : MappingWidget(window) +{ + CreateMainLayout(); +} + +void HotkeyUSBEmu::CreateMainLayout() +{ + m_main_layout = new QHBoxLayout(); + + m_main_layout->addWidget( + CreateGroupBox(tr("USB Device Emulation"), HotkeyManagerEmu::GetHotkeyGroup(HKGP_USB_EMU))); + + setLayout(m_main_layout); +} + +InputConfig* HotkeyUSBEmu::GetConfig() +{ + return HotkeyManagerEmu::GetConfig(); +} + +void HotkeyUSBEmu::LoadSettings() +{ + HotkeyManagerEmu::LoadConfig(); +} + +void HotkeyUSBEmu::SaveSettings() +{ + HotkeyManagerEmu::GetConfig()->SaveConfig(); +} diff --git a/Source/Core/DolphinQt/Config/Mapping/HotkeyUSBEmu.h b/Source/Core/DolphinQt/Config/Mapping/HotkeyUSBEmu.h new file mode 100644 index 0000000000..8f5390b440 --- /dev/null +++ b/Source/Core/DolphinQt/Config/Mapping/HotkeyUSBEmu.h @@ -0,0 +1,26 @@ +#pragma once +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "DolphinQt/Config/Mapping/MappingWidget.h" + +class QHBoxLayout; + +class HotkeyUSBEmu final : public MappingWidget +{ + Q_OBJECT +public: + explicit HotkeyUSBEmu(MappingWindow* window); + + InputConfig* GetConfig() override; + +private: + void LoadSettings() override; + void SaveSettings() override; + void CreateMainLayout(); + + // Main + QHBoxLayout* m_main_layout; +}; diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp index bb1a00d88d..dcb1cc83a1 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp @@ -39,6 +39,7 @@ #include "DolphinQt/Config/Mapping/HotkeyStates.h" #include "DolphinQt/Config/Mapping/HotkeyStatesOther.h" #include "DolphinQt/Config/Mapping/HotkeyTAS.h" +#include "DolphinQt/Config/Mapping/HotkeyUSBEmu.h" #include "DolphinQt/Config/Mapping/HotkeyWii.h" #include "DolphinQt/Config/Mapping/WiimoteEmuExtension.h" #include "DolphinQt/Config/Mapping/WiimoteEmuExtensionMotionInput.h" @@ -450,6 +451,7 @@ void MappingWindow::SetMappingType(MappingWindow::Type type) AddWidget(tr("Wii and Wii Remote"), new HotkeyWii(this)); AddWidget(tr("Controller Profile"), new HotkeyControllerProfile(this)); AddWidget(tr("Graphics"), new HotkeyGraphics(this)); + AddWidget(tr("USB Emulation"), new HotkeyUSBEmu(this)); // i18n: Stereoscopic 3D AddWidget(tr("3D"), new Hotkey3D(this)); AddWidget(tr("Save and Load State"), new HotkeyStates(this)); diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index b09a3828f2..55eedc5424 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -99,6 +99,7 @@ + @@ -298,6 +299,7 @@ + diff --git a/Source/Core/DolphinQt/HotkeyScheduler.cpp b/Source/Core/DolphinQt/HotkeyScheduler.cpp index 1e390c3796..3f42cd4a68 100644 --- a/Source/Core/DolphinQt/HotkeyScheduler.cpp +++ b/Source/Core/DolphinQt/HotkeyScheduler.cpp @@ -483,6 +483,13 @@ void HotkeyScheduler::Run() ShowEmulationSpeed(); } + // USB Device Emulation + if (IsHotkey(HK_SKYLANDERS_PORTAL)) + emit SkylandersPortalHotkey(); + + if (IsHotkey(HK_INFINITY_BASE)) + emit InfinityBaseHotkey(); + // Slot Saving / Loading if (IsHotkey(HK_SAVE_STATE_SLOT_SELECTED)) emit StateSaveSlotHotkey(); diff --git a/Source/Core/DolphinQt/HotkeyScheduler.h b/Source/Core/DolphinQt/HotkeyScheduler.h index 15f17fff93..97a43be72e 100644 --- a/Source/Core/DolphinQt/HotkeyScheduler.h +++ b/Source/Core/DolphinQt/HotkeyScheduler.h @@ -65,6 +65,9 @@ signals: void ToggleBreakpoint(); void AddBreakpoint(); + void SkylandersPortalHotkey(); + void InfinityBaseHotkey(); + private: void Run(); void CheckDebuggingHotkeys(); diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index aef0f94cc8..e0342a4f8e 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -642,6 +642,11 @@ void MainWindow::ConnectHotkeys() &CodeWidget::ToggleBreakpoint); connect(m_hotkey_scheduler, &HotkeyScheduler::AddBreakpoint, m_code_widget, &CodeWidget::AddBreakpoint); + + connect(m_hotkey_scheduler, &HotkeyScheduler::SkylandersPortalHotkey, this, + &MainWindow::ShowSkylanderPortal); + connect(m_hotkey_scheduler, &HotkeyScheduler::InfinityBaseHotkey, this, + &MainWindow::ShowInfinityBase); } void MainWindow::ConnectToolBar() From 1a8634fafc16c4d229cf5984f82de47335223606 Mon Sep 17 00:00:00 2001 From: Shawn Hoffman Date: Wed, 24 May 2023 12:50:35 -0700 Subject: [PATCH 026/175] windows: update to qt 6.5.1 --- Externals/Qt | 2 +- Source/Core/DolphinQt/CMakeLists.txt | 6 +++--- Source/VSProps/QtCompile.props | 8 +++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Externals/Qt b/Externals/Qt index 376baafde6..495517af2b 160000 --- a/Externals/Qt +++ b/Externals/Qt @@ -1 +1 @@ -Subproject commit 376baafde6cce2f8892c34c17ed397afa6c46d08 +Subproject commit 495517af2b922c10c24f543e0fd6ea3ddf774e50 diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index ac4d961ddf..8375f94fdd 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -5,15 +5,15 @@ endif() if (MSVC) if(_M_ARM_64) - list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/Externals/Qt/Qt6.3.0/ARM64") + list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/Externals/Qt/Qt6.5.1/ARM64") else() - list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/Externals/Qt/Qt6.3.0/x64") + list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/Externals/Qt/Qt6.5.1/x64") endif() endif() set(CMAKE_AUTOMOC ON) -find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Svg) message(STATUS "Found Qt version ${Qt6_VERSION}") set_property(TARGET Qt6::Core PROPERTY INTERFACE_COMPILE_FEATURES "") diff --git a/Source/VSProps/QtCompile.props b/Source/VSProps/QtCompile.props index c159b823e2..2b4ceed3c4 100644 --- a/Source/VSProps/QtCompile.props +++ b/Source/VSProps/QtCompile.props @@ -1,7 +1,7 @@ - $(ExternalsDir)Qt\Qt6.3.0\ + $(ExternalsDir)Qt\Qt6.5.1\ $(ExternalsQtDir)$(Platform)\ $(QtTargetDirDefault) $(QTDIR)\ @@ -78,13 +78,11 @@ - + - + From f8abc2c0e6f75e7475ef836234d14cd77b7cb244 Mon Sep 17 00:00:00 2001 From: Joshua de Reeper Date: Mon, 1 May 2023 22:30:31 +1200 Subject: [PATCH 027/175] Android: Infinity Base UI Add a UI option for the Infinity Base within the Android Emulation Activity --- .../activities/EmulationActivity.java | 145 +++++++++++++++-- .../features/infinitybase/InfinityConfig.kt | 24 +++ .../features/infinitybase/model/Figure.kt | 11 ++ .../features/infinitybase/ui/FigureSlot.kt | 5 + .../infinitybase/ui/FigureSlotAdapter.kt | 149 ++++++++++++++++++ .../features/settings/model/BooleanSetting.kt | 9 +- .../settings/ui/SettingsFragmentPresenter.kt | 21 +++ .../skylanders/ui/SkylanderSlotAdapter.kt | 16 +- .../dolphinemu/fragments/MenuFragment.java | 1 + .../layout/dialog_create_infinity_figure.xml | 56 +++++++ ...ger.xml => dialog_nfc_figures_manager.xml} | 6 +- .../main/res/layout/fragment_ingame_menu.xml | 5 + ...slot.xml => list_item_nfc_figure_slot.xml} | 26 +-- .../app/src/main/res/values/strings.xml | 22 ++- Source/Android/jni/CMakeLists.txt | 1 + Source/Android/jni/InfinityConfig.cpp | 126 +++++++++++++++ 16 files changed, 586 insertions(+), 37 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/InfinityConfig.kt create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/model/Figure.kt create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/ui/FigureSlot.kt create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/ui/FigureSlotAdapter.kt create mode 100644 Source/Android/app/src/main/res/layout/dialog_create_infinity_figure.xml rename Source/Android/app/src/main/res/layout/{dialog_skylanders_manager.xml => dialog_nfc_figures_manager.xml} (78%) rename Source/Android/app/src/main/res/layout/{list_item_skylander_slot.xml => list_item_nfc_figure_slot.xml} (72%) create mode 100644 Source/Android/jni/InfinityConfig.cpp diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index b9a9eab46e..f43da9bed3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -38,7 +38,11 @@ import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.databinding.ActivityEmulationBinding; import org.dolphinemu.dolphinemu.databinding.DialogInputAdjustBinding; -import org.dolphinemu.dolphinemu.databinding.DialogSkylandersManagerBinding; +import org.dolphinemu.dolphinemu.databinding.DialogNfcFiguresManagerBinding; +import org.dolphinemu.dolphinemu.features.infinitybase.InfinityConfig; +import org.dolphinemu.dolphinemu.features.infinitybase.model.Figure; +import org.dolphinemu.dolphinemu.features.infinitybase.ui.FigureSlot; +import org.dolphinemu.dolphinemu.features.infinitybase.ui.FigureSlotAdapter; import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface; import org.dolphinemu.dolphinemu.features.input.model.DolphinSensorEventListener; import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; @@ -78,6 +82,8 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP public static final int REQUEST_CHANGE_DISC = 1; public static final int REQUEST_SKYLANDER_FILE = 2; public static final int REQUEST_CREATE_SKYLANDER = 3; + public static final int REQUEST_INFINITY_FIGURE_FILE = 4; + public static final int REQUEST_CREATE_INFINITY_FIGURE = 5; private EmulationFragment mEmulationFragment; @@ -107,6 +113,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP public static final String EXTRA_SKYLANDER_ID = "SkylanderId"; public static final String EXTRA_SKYLANDER_VAR = "SkylanderVar"; public static final String EXTRA_SKYLANDER_NAME = "SkylanderName"; + public static final String EXTRA_INFINITY_POSITION = "FigurePosition"; + public static final String EXTRA_INFINITY_LIST_POSITION = "FigureListPosition"; + public static final String EXTRA_INFINITY_NUM = "FigureNum"; + public static final String EXTRA_INFINITY_NAME = "FigureName"; @Retention(SOURCE) @IntDef( @@ -121,7 +131,7 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP MENU_ACTION_RESET_OVERLAY, MENU_SET_IR_RECENTER, MENU_SET_IR_MODE, MENU_ACTION_CHOOSE_DOUBLETAP, MENU_ACTION_PAUSE_EMULATION, MENU_ACTION_UNPAUSE_EMULATION, MENU_ACTION_OVERLAY_CONTROLS, MENU_ACTION_SETTINGS, - MENU_ACTION_SKYLANDERS}) + MENU_ACTION_SKYLANDERS, MENU_ACTION_INFINITY_BASE}) public @interface MenuAction { } @@ -160,14 +170,20 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP public static final int MENU_ACTION_OVERLAY_CONTROLS = 34; public static final int MENU_ACTION_SETTINGS = 35; public static final int MENU_ACTION_SKYLANDERS = 36; + public static final int MENU_ACTION_INFINITY_BASE = 37; private Skylander mSkylanderData = new Skylander(-1, -1, "Slot"); + private Figure mInfinityFigureData = new Figure(-1, "Position"); private int mSkylanderSlot = -1; + private int mInfinityPosition = -1; + private int mInfinityListPosition = -1; - private DialogSkylandersManagerBinding mSkylandersBinding; + private DialogNfcFiguresManagerBinding mSkylandersBinding; + private DialogNfcFiguresManagerBinding mInfinityBinding; private static List sSkylanderSlots = new ArrayList<>(); + private static List sInfinityFigures = new ArrayList<>(); private static final SparseIntArray buttonsActionsMap = new SparseIntArray(); @@ -348,6 +364,17 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP sSkylanderSlots.add(new SkylanderSlot(getString(R.string.skylander_slot, i + 1), i)); } } + + if (sInfinityFigures.isEmpty()) + { + sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_hexagon_label), 0)); + sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_p1_label), 1)); + sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_p1a1_label), 3)); + sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_p1a2_label), 5)); + sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_p2_label), 2)); + sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_p2a1_label), 4)); + sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_p2a2_label), 6)); + } } @Override @@ -364,6 +391,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP outState.putInt(EXTRA_SKYLANDER_ID, mSkylanderData.getId()); outState.putInt(EXTRA_SKYLANDER_VAR, mSkylanderData.getVariant()); outState.putString(EXTRA_SKYLANDER_NAME, mSkylanderData.getName()); + outState.putInt(EXTRA_INFINITY_POSITION, mInfinityPosition); + outState.putInt(EXTRA_INFINITY_LIST_POSITION, mInfinityListPosition); + outState.putLong(EXTRA_INFINITY_NUM, mInfinityFigureData.getNumber()); + outState.putString(EXTRA_INFINITY_NAME, mInfinityFigureData.getName()); super.onSaveInstanceState(outState); } @@ -376,6 +407,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP mSkylanderData = new Skylander(savedInstanceState.getInt(EXTRA_SKYLANDER_ID), savedInstanceState.getInt(EXTRA_SKYLANDER_VAR), savedInstanceState.getString(EXTRA_SKYLANDER_NAME)); + mInfinityPosition = savedInstanceState.getInt(EXTRA_INFINITY_POSITION); + mInfinityListPosition = savedInstanceState.getInt(EXTRA_INFINITY_LIST_POSITION); + mInfinityFigureData = new Figure(savedInstanceState.getLong(EXTRA_INFINITY_NUM), + savedInstanceState.getString(EXTRA_INFINITY_NAME)); } @Override @@ -486,7 +521,7 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP clearSkylander(mSkylanderSlot); sSkylanderSlots.get(mSkylanderSlot).setPortalSlot(slot.first); sSkylanderSlots.get(mSkylanderSlot).setLabel(slot.second); - mSkylandersBinding.skylandersManager.getAdapter().notifyItemChanged(mSkylanderSlot); + mSkylandersBinding.figureManager.getAdapter().notifyItemChanged(mSkylanderSlot); mSkylanderSlot = -1; mSkylanderData = Skylander.BLANK_SKYLANDER; } @@ -500,11 +535,48 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP clearSkylander(mSkylanderSlot); sSkylanderSlots.get(mSkylanderSlot).setPortalSlot(slot.first); sSkylanderSlots.get(mSkylanderSlot).setLabel(slot.second); - mSkylandersBinding.skylandersManager.getAdapter().notifyItemChanged(mSkylanderSlot); + mSkylandersBinding.figureManager.getAdapter().notifyItemChanged(mSkylanderSlot); mSkylanderSlot = -1; mSkylanderData = Skylander.BLANK_SKYLANDER; } } + else if (requestCode == REQUEST_INFINITY_FIGURE_FILE) + { + String label = InfinityConfig.loadFigure(mInfinityPosition, result.getData().toString()); + if (label != null && !label.equals("Unknown Figure")) + { + clearInfinityFigure(mInfinityListPosition); + sInfinityFigures.get(mInfinityListPosition).setLabel(label); + mInfinityBinding.figureManager.getAdapter().notifyItemChanged(mInfinityListPosition); + mInfinityPosition = -1; + mInfinityListPosition = -1; + mInfinityFigureData = Figure.BLANK_FIGURE; + } + else + { + new MaterialAlertDialogBuilder(this) + .setTitle(R.string.incompatible_figure_selected) + .setMessage(R.string.select_compatible_figure) + .setPositiveButton(R.string.ok, null) + .show(); + } + } + else if (requestCode == REQUEST_CREATE_INFINITY_FIGURE) + { + if (!(mInfinityFigureData.getNumber() == -1)) + { + String label = + InfinityConfig.createFigure(mInfinityFigureData.getNumber(), + result.getData().toString(), + mInfinityPosition); + clearInfinityFigure(mInfinityListPosition); + sInfinityFigures.get(mInfinityListPosition).setLabel(label); + mInfinityBinding.figureManager.getAdapter().notifyItemChanged(mInfinityListPosition); + mInfinityPosition = -1; + mInfinityListPosition = -1; + mInfinityFigureData = Figure.BLANK_FIGURE; + } + } } } @@ -775,6 +847,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP showSkylanderPortalSettings(); break; + case MENU_ACTION_INFINITY_BASE: + showInfinityBaseSettings(); + break; + case MENU_ACTION_EXIT: mEmulationFragment.stopEmulation(); break; @@ -1036,10 +1112,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP private void showSkylanderPortalSettings() { mSkylandersBinding = - DialogSkylandersManagerBinding.inflate(getLayoutInflater()); - mSkylandersBinding.skylandersManager.setLayoutManager(new LinearLayoutManager(this)); + DialogNfcFiguresManagerBinding.inflate(getLayoutInflater()); + mSkylandersBinding.figureManager.setLayoutManager(new LinearLayoutManager(this)); - mSkylandersBinding.skylandersManager.setAdapter( + mSkylandersBinding.figureManager.setAdapter( new SkylanderSlotAdapter(sSkylanderSlots, this)); new MaterialAlertDialogBuilder(this) @@ -1048,6 +1124,21 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP .show(); } + private void showInfinityBaseSettings() + { + mInfinityBinding = + DialogNfcFiguresManagerBinding.inflate(getLayoutInflater()); + mInfinityBinding.figureManager.setLayoutManager(new LinearLayoutManager(this)); + + mInfinityBinding.figureManager.setAdapter( + new FigureSlotAdapter(sInfinityFigures, this)); + + new MaterialAlertDialogBuilder(this) + .setTitle(R.string.infinity_manager) + .setView(mInfinityBinding.getRoot()) + .show(); + } + public void setSkylanderData(int id, int var, String name, int slot) { mSkylanderData = new Skylander(id, var, name); @@ -1057,7 +1148,43 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP public void clearSkylander(int slot) { sSkylanderSlots.get(slot).setLabel(getString(R.string.skylander_slot, slot + 1)); - mSkylandersBinding.skylandersManager.getAdapter().notifyItemChanged(slot); + mSkylandersBinding.figureManager.getAdapter().notifyItemChanged(slot); + } + + public void setInfinityFigureData(Long num, String name, int position, int listPosition) + { + mInfinityFigureData = new Figure(num, name); + mInfinityPosition = position; + mInfinityListPosition = listPosition; + } + + public void clearInfinityFigure(int position) + { + switch (position) + { + case 0: + sInfinityFigures.get(position).setLabel(getString(R.string.infinity_hexagon_label)); + break; + case 1: + sInfinityFigures.get(position).setLabel(getString(R.string.infinity_p1_label)); + break; + case 2: + sInfinityFigures.get(position).setLabel(getString(R.string.infinity_p1a1_label)); + break; + case 3: + sInfinityFigures.get(position).setLabel(getString(R.string.infinity_p1a2_label)); + break; + case 4: + sInfinityFigures.get(position).setLabel(getString(R.string.infinity_p2_label)); + break; + case 5: + sInfinityFigures.get(position).setLabel(getString(R.string.infinity_p2a1_label)); + break; + case 6: + sInfinityFigures.get(position).setLabel(getString(R.string.infinity_p2a2_label)); + break; + } + mInfinityBinding.figureManager.getAdapter().notifyItemChanged(position); } private void resetOverlay() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/InfinityConfig.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/InfinityConfig.kt new file mode 100644 index 0000000000..0e2e54acb5 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/InfinityConfig.kt @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.infinitybase + +object InfinityConfig { + var LIST_FIGURES: Map = getFigureMap() + var REVERSE_LIST_FIGURES: Map = getInverseFigureMap() + + private external fun getFigureMap(): Map + private external fun getInverseFigureMap(): Map + + @JvmStatic + external fun removeFigure(position: Int) + + @JvmStatic + external fun loadFigure(position: Int, fileName: String): String? + + @JvmStatic + external fun createFigure( + figureNumber: Long, + fileName: String, + position: Int + ): String? +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/model/Figure.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/model/Figure.kt new file mode 100644 index 0000000000..b65b978e37 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/model/Figure.kt @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.infinitybase.model + +data class Figure(var number: Long, var name: String) { + + companion object { + @JvmField + val BLANK_FIGURE = Figure(-1, "Blank") + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/ui/FigureSlot.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/ui/FigureSlot.kt new file mode 100644 index 0000000000..7823fe1a92 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/ui/FigureSlot.kt @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.infinitybase.ui + +data class FigureSlot(var label: String, val position: Int) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/ui/FigureSlotAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/ui/FigureSlotAdapter.kt new file mode 100644 index 0000000000..8d88c37ccb --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/ui/FigureSlotAdapter.kt @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.infinitybase.ui + +import android.app.AlertDialog +import android.content.Intent +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.AdapterView.OnItemClickListener +import android.widget.ArrayAdapter +import android.widget.Toast +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.activities.EmulationActivity +import org.dolphinemu.dolphinemu.databinding.DialogCreateInfinityFigureBinding +import org.dolphinemu.dolphinemu.databinding.ListItemNfcFigureSlotBinding +import org.dolphinemu.dolphinemu.features.infinitybase.InfinityConfig +import org.dolphinemu.dolphinemu.features.infinitybase.InfinityConfig.removeFigure + +class FigureSlotAdapter( + private val figures: List, + private val activity: EmulationActivity +) : RecyclerView.Adapter(), + OnItemClickListener { + + class ViewHolder(var binding: ListItemNfcFigureSlotBinding) : + RecyclerView.ViewHolder(binding.getRoot()) + + private lateinit var binding: DialogCreateInfinityFigureBinding + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ViewHolder { + val inflater = LayoutInflater.from(parent.context) + val binding = ListItemNfcFigureSlotBinding.inflate(inflater, parent, false) + return ViewHolder(binding) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val figure = figures[position] + holder.binding.textFigureName.text = figure.label + + holder.binding.buttonClearFigure.setOnClickListener { + removeFigure(figure.position) + activity.clearInfinityFigure(position) + } + + holder.binding.buttonLoadFigure.setOnClickListener { + val loadFigure = Intent(Intent.ACTION_OPEN_DOCUMENT) + loadFigure.addCategory(Intent.CATEGORY_OPENABLE) + loadFigure.type = "*/*" + activity.setInfinityFigureData(0, "", figure.position, position) + activity.startActivityForResult( + loadFigure, + EmulationActivity.REQUEST_INFINITY_FIGURE_FILE + ) + } + + val inflater = LayoutInflater.from(activity) + binding = DialogCreateInfinityFigureBinding.inflate(inflater) + + binding.infinityDropdown.onItemClickListener = this + + holder.binding.buttonCreateFigure.setOnClickListener { + var validFigures = InfinityConfig.REVERSE_LIST_FIGURES + // Filter adapter list by position, either Hexagon Pieces, Characters or Abilities + validFigures = when (figure.position) { + 0 -> { + // Hexagon Pieces + validFigures.filter { (_, value) -> value in 2000000..2999999 || value in 4000000..4999999 } + } + + 1, 2 -> { + // Characters + validFigures.filter { (_, value) -> value in 1000000..1999999 } + } + + else -> { + // Abilities + validFigures.filter { (_, value) -> value in 3000000..3999999 } + } + } + val figureListKeys = validFigures.keys.toMutableList() + figureListKeys.sort() + val figureNames: ArrayList = ArrayList(figureListKeys) + binding.infinityDropdown.setAdapter( + ArrayAdapter( + activity, R.layout.support_simple_spinner_dropdown_item, + figureNames + ) + ) + + if (binding.getRoot().parent != null) { + (binding.getRoot().parent as ViewGroup).removeAllViews() + } + val createDialog = MaterialAlertDialogBuilder(activity) + .setTitle(R.string.create_figure_title) + .setView(binding.getRoot()) + .setPositiveButton(R.string.create_figure, null) + .setNegativeButton(R.string.cancel, null) + .show() + createDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { + if (binding.infinityNum.text.toString().isNotBlank()) { + val createFigure = Intent(Intent.ACTION_CREATE_DOCUMENT) + createFigure.addCategory(Intent.CATEGORY_OPENABLE) + createFigure.type = "*/*" + val num = binding.infinityNum.text.toString().toLong() + val name = InfinityConfig.LIST_FIGURES[num] + if (name != null) { + createFigure.putExtra( + Intent.EXTRA_TITLE, + "$name.bin" + ) + activity.setInfinityFigureData(num, name, figure.position, position) + } else { + createFigure.putExtra( + Intent.EXTRA_TITLE, + "Unknown(Number: $num).bin" + ) + activity.setInfinityFigureData(num, "Unknown", figure.position, position) + } + activity.startActivityForResult( + createFigure, + EmulationActivity.REQUEST_CREATE_INFINITY_FIGURE + ) + createDialog.dismiss() + } else { + Toast.makeText( + activity, R.string.invalid_infinity_figure, + Toast.LENGTH_SHORT + ).show() + } + } + } + } + + override fun getItemCount(): Int { + return figures.size + } + + override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) { + val figureNumber = InfinityConfig.REVERSE_LIST_FIGURES[parent.getItemAtPosition(position)] + binding.infinityNum.setText(figureNumber.toString()) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt index e59cb8a097..80060f61dd 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt @@ -217,6 +217,12 @@ enum class BooleanSetting( "EmulateSkylanderPortal", false ), + MAIN_EMULATE_INFINITY_BASE( + Settings.FILE_DOLPHIN, + Settings.SECTION_EMULATED_USB_DEVICES, + "EmulateInfinityBase", + false + ), MAIN_SHOW_GAME_TITLES( Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, @@ -719,7 +725,8 @@ enum class BooleanSetting( MAIN_RAM_OVERRIDE_ENABLE, MAIN_CUSTOM_RTC_ENABLE, MAIN_DSP_JIT, - MAIN_EMULATE_SKYLANDER_PORTAL + MAIN_EMULATE_SKYLANDER_PORTAL, + MAIN_EMULATE_INFINITY_BASE ) private val NOT_RUNTIME_EDITABLE: Set = HashSet(listOf(*NOT_RUNTIME_EDITABLE_ARRAY)) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt index 72649a39fd..e119813183 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt @@ -115,6 +115,7 @@ class SettingsFragmentPresenter( controllerNumber, controllerType ) + MenuTag.WIIMOTE_1, MenuTag.WIIMOTE_2, MenuTag.WIIMOTE_3, @@ -122,6 +123,7 @@ class SettingsFragmentPresenter( sl, controllerNumber ) + MenuTag.WIIMOTE_EXTENSION_1, MenuTag.WIIMOTE_EXTENSION_2, MenuTag.WIIMOTE_EXTENSION_3, @@ -130,6 +132,7 @@ class SettingsFragmentPresenter( controllerNumber, controllerType ) + MenuTag.WIIMOTE_GENERAL_1, MenuTag.WIIMOTE_GENERAL_2, MenuTag.WIIMOTE_GENERAL_3, @@ -137,6 +140,7 @@ class SettingsFragmentPresenter( sl, controllerNumber ) + MenuTag.WIIMOTE_MOTION_SIMULATION_1, MenuTag.WIIMOTE_MOTION_SIMULATION_2, MenuTag.WIIMOTE_MOTION_SIMULATION_3, @@ -144,6 +148,7 @@ class SettingsFragmentPresenter( sl, controllerNumber ) + MenuTag.WIIMOTE_MOTION_INPUT_1, MenuTag.WIIMOTE_MOTION_INPUT_2, MenuTag.WIIMOTE_MOTION_INPUT_3, @@ -151,6 +156,7 @@ class SettingsFragmentPresenter( sl, controllerNumber ) + else -> throw UnsupportedOperationException("Unimplemented menu") } @@ -454,10 +460,12 @@ class SettingsFragmentPresenter( BooleanSetting.MAIN_DSP_HLE.setBoolean(settings, true) BooleanSetting.MAIN_DSP_JIT.setBoolean(settings, true) } + DSP_LLE_RECOMPILER -> { BooleanSetting.MAIN_DSP_HLE.setBoolean(settings, false) BooleanSetting.MAIN_DSP_JIT.setBoolean(settings, true) } + DSP_LLE_INTERPRETER -> { BooleanSetting.MAIN_DSP_HLE.setBoolean(settings, false) BooleanSetting.MAIN_DSP_JIT.setBoolean(settings, false) @@ -834,6 +842,14 @@ class SettingsFragmentPresenter( 0 ) ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_EMULATE_INFINITY_BASE, + R.string.emulate_infinity_base, + 0 + ) + ) } private fun addAdvancedSettings(sl: ArrayList) { @@ -856,10 +872,12 @@ class SettingsFragmentPresenter( BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.setBoolean(settings, false) BooleanSetting.MAIN_SYNC_GPU.setBoolean(settings, false) } + SYNC_GPU_ON_IDLE_SKIP -> { BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.setBoolean(settings, true) BooleanSetting.MAIN_SYNC_GPU.setBoolean(settings, false) } + SYNC_GPU_ALWAYS -> { BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.setBoolean(settings, true) BooleanSetting.MAIN_SYNC_GPU.setBoolean(settings, true) @@ -893,10 +911,12 @@ class SettingsFragmentPresenter( emuCoresEntries = R.array.emuCoresEntriesX86_64 emuCoresValues = R.array.emuCoresValuesX86_64 } + 4 -> { emuCoresEntries = R.array.emuCoresEntriesARM64 emuCoresValues = R.array.emuCoresValuesARM64 } + else -> { emuCoresEntries = R.array.emuCoresEntriesGeneric emuCoresValues = R.array.emuCoresValuesGeneric @@ -2246,6 +2266,7 @@ class SettingsFragmentPresenter( setting.uiSuffix ) ) + NumericSetting.TYPE_BOOLEAN -> sl.add( SwitchSetting( InputMappingBooleanSetting(setting), diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/skylanders/ui/SkylanderSlotAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/skylanders/ui/SkylanderSlotAdapter.kt index bc23ca3f37..aa4157a54b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/skylanders/ui/SkylanderSlotAdapter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/skylanders/ui/SkylanderSlotAdapter.kt @@ -16,7 +16,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.dolphinemu.dolphinemu.R import org.dolphinemu.dolphinemu.activities.EmulationActivity import org.dolphinemu.dolphinemu.databinding.DialogCreateSkylanderBinding -import org.dolphinemu.dolphinemu.databinding.ListItemSkylanderSlotBinding +import org.dolphinemu.dolphinemu.databinding.ListItemNfcFigureSlotBinding import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig.removeSkylander import org.dolphinemu.dolphinemu.features.skylanders.model.SkylanderPair @@ -25,27 +25,27 @@ class SkylanderSlotAdapter( private val slots: List, private val activity: EmulationActivity ) : RecyclerView.Adapter(), OnItemClickListener { - class ViewHolder(var binding: ListItemSkylanderSlotBinding) : + class ViewHolder(var binding: ListItemNfcFigureSlotBinding) : RecyclerView.ViewHolder(binding.getRoot()) private lateinit var binding: DialogCreateSkylanderBinding override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val inflater = LayoutInflater.from(parent.context) - val binding = ListItemSkylanderSlotBinding.inflate(inflater, parent, false) + val binding = ListItemNfcFigureSlotBinding.inflate(inflater, parent, false) return ViewHolder(binding) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val slot = slots[position] - holder.binding.textSkylanderName.text = slot.label + holder.binding.textFigureName.text = slot.label - holder.binding.buttonClearSkylander.setOnClickListener { + holder.binding.buttonClearFigure.setOnClickListener { removeSkylander(slot.portalSlot) activity.clearSkylander(slot.slotNum) } - holder.binding.buttonLoadSkylander.setOnClickListener { + holder.binding.buttonLoadFigure.setOnClickListener { val loadSkylander = Intent(Intent.ACTION_OPEN_DOCUMENT) loadSkylander.addCategory(Intent.CATEGORY_OPENABLE) loadSkylander.type = "*/*" @@ -71,14 +71,14 @@ class SkylanderSlotAdapter( ) binding.skylanderDropdown.onItemClickListener = this - holder.binding.buttonCreateSkylander.setOnClickListener { + holder.binding.buttonCreateFigure.setOnClickListener { if (binding.getRoot().parent != null) { (binding.getRoot().parent as ViewGroup).removeAllViews() } val createDialog = MaterialAlertDialogBuilder(activity) .setTitle(R.string.create_skylander_title) .setView(binding.getRoot()) - .setPositiveButton(R.string.create_skylander, null) + .setPositiveButton(R.string.create_figure, null) .setNegativeButton(R.string.cancel, null) .show() createDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/MenuFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/MenuFragment.java index b30ba94b37..25263e1ffd 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/MenuFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/MenuFragment.java @@ -61,6 +61,7 @@ public final class MenuFragment extends Fragment implements View.OnClickListener buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT); buttonsActionsMap.append(R.id.menu_settings, EmulationActivity.MENU_ACTION_SETTINGS); buttonsActionsMap.append(R.id.menu_skylanders, EmulationActivity.MENU_ACTION_SKYLANDERS); + buttonsActionsMap.append(R.id.menu_infinitybase, EmulationActivity.MENU_ACTION_INFINITY_BASE); } private FragmentIngameMenuBinding mBinding; diff --git a/Source/Android/app/src/main/res/layout/dialog_create_infinity_figure.xml b/Source/Android/app/src/main/res/layout/dialog_create_infinity_figure.xml new file mode 100644 index 0000000000..75e7b0a2e0 --- /dev/null +++ b/Source/Android/app/src/main/res/layout/dialog_create_infinity_figure.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Android/app/src/main/res/layout/dialog_skylanders_manager.xml b/Source/Android/app/src/main/res/layout/dialog_nfc_figures_manager.xml similarity index 78% rename from Source/Android/app/src/main/res/layout/dialog_skylanders_manager.xml rename to Source/Android/app/src/main/res/layout/dialog_nfc_figures_manager.xml index 517399ff3a..c880f7c518 100644 --- a/Source/Android/app/src/main/res/layout/dialog_skylanders_manager.xml +++ b/Source/Android/app/src/main/res/layout/dialog_nfc_figures_manager.xml @@ -4,10 +4,10 @@ android:layout_height="wrap_content"> + android:fadeScrollbars="false" + android:scrollbars="vertical" /> diff --git a/Source/Android/app/src/main/res/layout/fragment_ingame_menu.xml b/Source/Android/app/src/main/res/layout/fragment_ingame_menu.xml index 0619f22087..07e3e42a6c 100644 --- a/Source/Android/app/src/main/res/layout/fragment_ingame_menu.xml +++ b/Source/Android/app/src/main/res/layout/fragment_ingame_menu.xml @@ -106,6 +106,11 @@ android:text="@string/emulate_skylander_portal" style="@style/InGameMenuOption" /> +