Compare commits

...

38 Commits

Author SHA1 Message Date
Liam 5a64a77df3 glue: use multi wait API 2024-02-07 12:15:01 -05:00
Liam 6810929f6a server_manager: use multi wait API 2024-02-07 12:15:01 -05:00
Liam 9404633bfd service: add os types and multi wait API 2024-02-07 12:14:46 -05:00
liamwhite c10e720ba9 Merge pull request #12883 from FernandoS27/memory_manager_mem
MemoryManager: Reduce the page table size based on last big page address.
2024-02-06 10:25:03 -05:00
liamwhite 5016de3626 Merge pull request #12928 from german77/motion-mp
service: hid: Add multiprocess support to six axis input
2024-02-06 10:24:46 -05:00
liamwhite d5fb9fd12c Merge pull request #12933 from german77/irs-interface
service: irs: Migrate service to new interface
2024-02-06 10:24:30 -05:00
liamwhite c79b3af610 Merge pull request #12934 from german77/hid_debug_interface
service: hid: Migrate hid debug service to new interface
2024-02-06 10:24:20 -05:00
german77 b6106604c4 service: hid: Migrate hid debug service to new interface 2024-02-06 00:38:46 -06:00
german77 12b6162852 service: irs: Migrate service to new interface 2024-02-06 00:14:16 -06:00
german77 8f192b494a service: hid: Add multiprocess support to six axis input 2024-02-05 17:19:31 -06:00
german77 372897aac4 service: hid: Ensure aruid data is initialized 2024-02-05 17:17:21 -06:00
Charles Lombardo a2f23746c2 Merge pull request #12905 from liamwhite/hwc-release
nvnflinger: release buffers before presentation sleep
2024-02-05 13:43:22 -05:00
Charles Lombardo 215b13f2a2 Merge pull request #12924 from liamwhite/pedantic-unsigned
typed_address: test values are unsigned
2024-02-05 13:43:06 -05:00
liamwhite 35ed9425d7 Merge pull request #12925 from german77/linux-tab
yuzu: Fully hide linux tab
2024-02-05 13:41:31 -05:00
liamwhite 74cc8721c7 Merge pull request #12915 from german77/cheat
dmnt: cheats: Update cheat vm to latest version
2024-02-05 13:41:21 -05:00
liamwhite 8ef1db78b0 Merge pull request #12916 from liamwhite/float-fix
gdb: fix load/save of fp values in a32
2024-02-05 13:41:15 -05:00
Charles Lombardo 18c8f10ff2 Merge pull request #12922 from FearlessTobi/lang-mappins
.tx/config: Use language mappings for android "tx pull"
2024-02-05 13:40:53 -05:00
german77 96d881f087 yuzu: Fully hide linux tab 2024-02-05 11:58:20 -06:00
Liam 0e950baf41 typed_address: test values are unsigned 2024-02-05 12:47:10 -05:00
german77 8113f55f4b dmnt: cheats: Silence memory errors 2024-02-05 11:08:24 -06:00
FearlessTobi ddbefc71cb .tx/config: Use language mappings for android "tx pull"
The language names we are using in the android resources differ from those on Transifex.

We need to manually specify mappings for them, so Transifex is able to place the files in the correct folders.
2024-02-05 15:57:13 +01:00
Liam 85143e8376 gdb: fix load/save of fp values in a32 2024-02-04 20:28:43 -05:00
german77 504abbd6e0 dmnt: cheats: Update cheat vm to latest version 2024-02-04 17:46:20 -06:00
liamwhite 4cccbe7989 Merge pull request #12892 from liamwhite/serialization-stuff
cmif_serialization: enforce const for references
2024-02-04 09:48:33 -05:00
Liam 5eb5c96750 nvnflinger: release buffers before presentation sleep 2024-02-03 17:14:43 -05:00
liamwhite 5da55cbac9 Merge pull request #12901 from Kelebek1/timezone_firmware_fix
Fix firmware timezone boot load check.
2024-02-03 11:10:30 -05:00
liamwhite 81cc4df1f9 Merge pull request #12895 from german77/files
service: fs: Skip non user id folders
2024-02-03 11:10:24 -05:00
liamwhite 25f3d358b1 Merge pull request #12877 from german77/npad-fixed
service: hid: Multiple fixes
2024-02-03 11:10:14 -05:00
liamwhite a3c8bb251d Merge pull request #12852 from Calinou/multiplayer-color-player-counts
Color player counts in the multiplayer public lobby list
2024-02-03 11:10:00 -05:00
liamwhite 327533be1f Merge pull request #12851 from Calinou/multiplayer-persist-filters
Persist filters in multiplayer public lobby list
2024-02-03 11:09:51 -05:00
liamwhite 61ea2115c7 Merge pull request #12850 from Calinou/multiplayer-add-hotkeys
Add hotkeys for multiplayer actions
2024-02-03 11:09:41 -05:00
Narr the Reg fb3ef957bb service: fs: Skip non user id folders 2024-02-02 13:25:38 -06:00
Liam 78f72b3bf5 cmif_serialization: enforce const for references 2024-02-02 09:32:10 -05:00
Narr the Reg 818721d12d service: hid: Multiple fixes 2024-02-01 10:37:44 -06:00
Fernando Sahmkow f740d8b9be MemoryManager: Reduce the page table size based on last big page address. 2024-02-01 13:00:36 +01:00
Hugo Locurcio 442aad9b27 Persist filters in multiplayer public lobby list
After connecting to a room, the chosen filter text, "Games I Own",
"Hide Empty Rooms" and "Hide Full Rooms" values are persisted
to configuration so they are preserved across restarts.

This makes it easier to rejoin a room if you regularly play the same
game, or after a crash.
2024-01-30 17:40:29 +01:00
Hugo Locurcio 8e0f97ac96 Color player counts in the multiplayer public lobby list
- Full lobbies have their player count displayed in red.
- Lobbies with one slot left have their player count displayed in orange.
- Empty lobbies have their player count grayed out.
2024-01-30 17:38:21 +01:00
Hugo Locurcio 345d691328 Add hotkeys for multiplayer actions
Default shortcuts were chosen as to be intuitive (use the first letter
of the action, or the second word's first letter) and work on all
types of keyboards. The hotkeys can be used while playing a game too,
as they are application-wide.
2024-01-30 01:32:14 +01:00
56 changed files with 1411 additions and 1228 deletions
+1
View File
@@ -11,3 +11,4 @@ type = QT
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
source_file = ../../src/android/app/src/main/res/values/strings.xml
type = ANDROID
lang_map = ja_JP:ja, ko_KR:ko, pt_BR:pt-rBR, pt_PT:pt-rPT, ru_RU:ru, vi_VN:vi, zh_CN:zh-rCN, zh_TW:zh-rTW
+3
View File
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
#include <fmt/format.h>
#include "common/assert.h"
#include "common/common_types.h"
namespace Common {
@@ -29,6 +30,8 @@ namespace Common {
template <std::size_t Size, bool le = false>
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
std::array<u8, Size> out{};
if constexpr (le) {
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
+40 -40
View File
@@ -186,68 +186,68 @@ static_assert(std::is_trivially_destructible_v<PhysicalAddress>);
static_assert(std::is_trivially_destructible_v<VirtualAddress>);
static_assert(std::is_trivially_destructible_v<ProcessAddress>);
static_assert(Null<uint64_t> == 0);
static_assert(Null<uint64_t> == 0U);
static_assert(Null<PhysicalAddress> == Null<uint64_t>);
static_assert(Null<VirtualAddress> == Null<uint64_t>);
static_assert(Null<ProcessAddress> == Null<uint64_t>);
// Constructor/assignment validations.
static_assert([] {
const PhysicalAddress a(5);
const PhysicalAddress a(5U);
PhysicalAddress b(a);
return b;
}() == PhysicalAddress(5));
}() == PhysicalAddress(5U));
static_assert([] {
const PhysicalAddress a(5);
PhysicalAddress b(10);
const PhysicalAddress a(5U);
PhysicalAddress b(10U);
b = a;
return b;
}() == PhysicalAddress(5));
}() == PhysicalAddress(5U));
// Arithmetic validations.
static_assert(PhysicalAddress(10) + 5 == PhysicalAddress(15));
static_assert(PhysicalAddress(10) - 5 == PhysicalAddress(5));
static_assert(PhysicalAddress(10U) + 5U == PhysicalAddress(15U));
static_assert(PhysicalAddress(10U) - 5U == PhysicalAddress(5U));
static_assert([] {
PhysicalAddress v(10);
v += 5;
PhysicalAddress v(10U);
v += 5U;
return v;
}() == PhysicalAddress(15));
}() == PhysicalAddress(15U));
static_assert([] {
PhysicalAddress v(10);
v -= 5;
PhysicalAddress v(10U);
v -= 5U;
return v;
}() == PhysicalAddress(5));
static_assert(PhysicalAddress(10)++ == PhysicalAddress(10));
static_assert(++PhysicalAddress(10) == PhysicalAddress(11));
static_assert(PhysicalAddress(10)-- == PhysicalAddress(10));
static_assert(--PhysicalAddress(10) == PhysicalAddress(9));
}() == PhysicalAddress(5U));
static_assert(PhysicalAddress(10U)++ == PhysicalAddress(10U));
static_assert(++PhysicalAddress(10U) == PhysicalAddress(11U));
static_assert(PhysicalAddress(10U)-- == PhysicalAddress(10U));
static_assert(--PhysicalAddress(10U) == PhysicalAddress(9U));
// Logical validations.
static_assert((PhysicalAddress(0b11111111) >> 1) == 0b01111111);
static_assert((PhysicalAddress(0b10101010) >> 1) == 0b01010101);
static_assert((PhysicalAddress(0b11111111) << 1) == 0b111111110);
static_assert((PhysicalAddress(0b01010101) << 1) == 0b10101010);
static_assert((PhysicalAddress(0b11111111) & 0b01010101) == 0b01010101);
static_assert((PhysicalAddress(0b11111111) & 0b10101010) == 0b10101010);
static_assert((PhysicalAddress(0b01010101) & 0b10101010) == 0b00000000);
static_assert((PhysicalAddress(0b00000000) | 0b01010101) == 0b01010101);
static_assert((PhysicalAddress(0b11111111) | 0b01010101) == 0b11111111);
static_assert((PhysicalAddress(0b10101010) | 0b01010101) == 0b11111111);
static_assert((PhysicalAddress(0b11111111U) >> 1) == 0b01111111U);
static_assert((PhysicalAddress(0b10101010U) >> 1) == 0b01010101U);
static_assert((PhysicalAddress(0b11111111U) << 1) == 0b111111110U);
static_assert((PhysicalAddress(0b01010101U) << 1) == 0b10101010U);
static_assert((PhysicalAddress(0b11111111U) & 0b01010101U) == 0b01010101U);
static_assert((PhysicalAddress(0b11111111U) & 0b10101010U) == 0b10101010U);
static_assert((PhysicalAddress(0b01010101U) & 0b10101010U) == 0b00000000U);
static_assert((PhysicalAddress(0b00000000U) | 0b01010101U) == 0b01010101U);
static_assert((PhysicalAddress(0b11111111U) | 0b01010101U) == 0b11111111U);
static_assert((PhysicalAddress(0b10101010U) | 0b01010101U) == 0b11111111U);
// Comparisons.
static_assert(PhysicalAddress(0) == PhysicalAddress(0));
static_assert(PhysicalAddress(0) != PhysicalAddress(1));
static_assert(PhysicalAddress(0) < PhysicalAddress(1));
static_assert(PhysicalAddress(0) <= PhysicalAddress(1));
static_assert(PhysicalAddress(1) > PhysicalAddress(0));
static_assert(PhysicalAddress(1) >= PhysicalAddress(0));
static_assert(PhysicalAddress(0U) == PhysicalAddress(0U));
static_assert(PhysicalAddress(0U) != PhysicalAddress(1U));
static_assert(PhysicalAddress(0U) < PhysicalAddress(1U));
static_assert(PhysicalAddress(0U) <= PhysicalAddress(1U));
static_assert(PhysicalAddress(1U) > PhysicalAddress(0U));
static_assert(PhysicalAddress(1U) >= PhysicalAddress(0U));
static_assert(!(PhysicalAddress(0) == PhysicalAddress(1)));
static_assert(!(PhysicalAddress(0) != PhysicalAddress(0)));
static_assert(!(PhysicalAddress(1) < PhysicalAddress(0)));
static_assert(!(PhysicalAddress(1) <= PhysicalAddress(0)));
static_assert(!(PhysicalAddress(0) > PhysicalAddress(1)));
static_assert(!(PhysicalAddress(0) >= PhysicalAddress(1)));
static_assert(!(PhysicalAddress(0U) == PhysicalAddress(1U)));
static_assert(!(PhysicalAddress(0U) != PhysicalAddress(0U)));
static_assert(!(PhysicalAddress(1U) < PhysicalAddress(0U)));
static_assert(!(PhysicalAddress(1U) <= PhysicalAddress(0U)));
static_assert(!(PhysicalAddress(0U) > PhysicalAddress(1U)));
static_assert(!(PhysicalAddress(0U) >= PhysicalAddress(1U)));
} // namespace Common
+9 -4
View File
@@ -548,8 +548,6 @@ add_library(core STATIC
hle/service/es/es.h
hle/service/eupld/eupld.cpp
hle/service/eupld/eupld.h
hle/service/event.cpp
hle/service/event.h
hle/service/fatal/fatal.cpp
hle/service/fatal/fatal.h
hle/service/fatal/fatal_p.cpp
@@ -676,8 +674,6 @@ add_library(core STATIC
hle/service/mm/mm_u.h
hle/service/mnpp/mnpp_app.cpp
hle/service/mnpp/mnpp_app.h
hle/service/mutex.cpp
hle/service/mutex.h
hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h
hle/service/nfc/common/amiibo_crypto.cpp
@@ -790,6 +786,15 @@ add_library(core STATIC
hle/service/nvnflinger/window.h
hle/service/olsc/olsc.cpp
hle/service/olsc/olsc.h
hle/service/os/event.cpp
hle/service/os/event.h
hle/service/os/multi_wait_holder.cpp
hle/service/os/multi_wait_holder.h
hle/service/os/multi_wait_utils.h
hle/service/os/multi_wait.cpp
hle/service/os/multi_wait.h
hle/service/os/mutex.cpp
hle/service/os/mutex.h
hle/service/pcie/pcie.cpp
hle/service/pcie/pcie.h
hle/service/pctl/pctl.cpp
+2 -2
View File
@@ -383,7 +383,7 @@ std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const
} else if (id == CPSR_REGISTER) {
return ValueToHex(context.pstate);
} else if (id >= D0_REGISTER && id < Q0_REGISTER) {
return ValueToHex(fprs[id - D0_REGISTER][0]);
return ValueToHex(fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2]);
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
return ValueToHex(fprs[id - Q0_REGISTER]);
} else if (id == FPSCR_REGISTER) {
@@ -406,7 +406,7 @@ void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v
} else if (id == CPSR_REGISTER) {
context.pstate = HexToValue<u32>(value);
} else if (id >= D0_REGISTER && id < Q0_REGISTER) {
fprs[id - D0_REGISTER] = {HexToValue<u64>(value), 0};
fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2] = HexToValue<u64>(value);
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
fprs[id - Q0_REGISTER] = HexToValue<u128>(value);
} else if (id == FPSCR_REGISTER) {
+1 -1
View File
@@ -9,8 +9,8 @@
#include "common/math_util.h"
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/os/event.h"
#include "core/hle/service/service.h"
#include "core/hle/service/am/am_types.h"
+1 -1
View File
@@ -7,8 +7,8 @@
#include <memory>
#include <mutex>
#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/os/event.h"
union Result;
@@ -115,6 +115,11 @@ struct ArgumentTraits {
static constexpr ArgumentType Type = ArgumentType::InData;
};
template <typename... Ts>
consteval bool ConstIfReference() {
return ((!std::is_reference_v<Ts> || std::is_const_v<std::remove_reference_t<Ts>>) && ... && true);
}
struct RequestLayout {
u32 copy_handle_count;
u32 move_handle_count;
@@ -435,6 +440,7 @@ void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
}
const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false;
static_assert(ConstIfReference<A...>(), "Arguments taken by reference must be const");
using MethodArguments = std::tuple<std::remove_cvref_t<A>...>;
OutTemporaryBuffers buffers{};
+25 -2
View File
@@ -4,10 +4,9 @@
#pragma once
#include <memory>
#include <span>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/hle_ipc.h"
namespace Service {
@@ -22,8 +21,10 @@ class Out {
public:
using Type = T;
/* implicit */ Out(const Out& t) : raw(t.raw) {}
/* implicit */ Out(AutoOut<Type>& t) : raw(&t.raw) {}
/* implicit */ Out(Type* t) : raw(t) {}
Out& operator=(const Out&) = delete;
Type* Get() const {
return raw;
@@ -37,6 +38,10 @@ public:
return raw;
}
operator Type*() const {
return raw;
}
private:
Type* raw;
};
@@ -113,8 +118,10 @@ class OutCopyHandle {
public:
using Type = T*;
/* implicit */ OutCopyHandle(const OutCopyHandle& t) : raw(t.raw) {}
/* implicit */ OutCopyHandle(AutoOut<Type>& t) : raw(&t.raw) {}
/* implicit */ OutCopyHandle(Type* t) : raw(t) {}
OutCopyHandle& operator=(const OutCopyHandle&) = delete;
Type* Get() const {
return raw;
@@ -128,6 +135,10 @@ public:
return raw;
}
operator Type*() const {
return raw;
}
private:
Type* raw;
};
@@ -137,8 +148,10 @@ class OutMoveHandle {
public:
using Type = T*;
/* implicit */ OutMoveHandle(const OutMoveHandle& t) : raw(t.raw) {}
/* implicit */ OutMoveHandle(AutoOut<Type>& t) : raw(&t.raw) {}
/* implicit */ OutMoveHandle(Type* t) : raw(t) {}
OutMoveHandle& operator=(const OutMoveHandle&) = delete;
Type* Get() const {
return raw;
@@ -152,6 +165,10 @@ public:
return raw;
}
operator Type*() const {
return raw;
}
private:
Type* raw;
};
@@ -248,8 +265,10 @@ public:
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_In | BufferAttr_FixedSize);
using Type = T;
/* implicit */ OutLargeData(const OutLargeData& t) : raw(t.raw) {}
/* implicit */ OutLargeData(Type* t) : raw(t) {}
/* implicit */ OutLargeData(AutoOut<T>& t) : raw(&t.raw) {}
OutLargeData& operator=(const OutLargeData&) = delete;
Type* Get() const {
return raw;
@@ -263,6 +282,10 @@ public:
return raw;
}
operator Type*() const {
return raw;
}
private:
Type* raw;
};
@@ -115,6 +115,11 @@ private:
if (type->GetName() == "save") {
for (const auto& save_id : type->GetSubdirectories()) {
for (const auto& user_id : save_id->GetSubdirectories()) {
// Skip non user id subdirectories
if (user_id->GetName().size() != 0x20) {
continue;
}
const auto save_id_numeric = stoull_be(save_id->GetName());
auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
@@ -160,6 +165,10 @@ private:
} else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
// Temporary Storage
for (const auto& user_id : type->GetSubdirectories()) {
// Skip non user id subdirectories
if (user_id->GetName().size() != 0x20) {
continue;
}
for (const auto& title_id : user_id->GetSubdirectories()) {
if (!title_id->GetFiles().empty() ||
!title_id->GetSubdirectories().empty()) {
+48 -82
View File
@@ -7,6 +7,7 @@
#include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
#include "core/hle/service/glue/time/worker.h"
#include "core/hle/service/os/multi_wait_utils.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/psc/time/static.h"
@@ -143,82 +144,46 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
Common::SetCurrentThreadName("TimeWorker");
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
enum class EventType {
Exit = 0,
IpmModuleService_GetEvent = 1,
PowerStateChange = 2,
SignalAlarms = 3,
UpdateLocalSystemClock = 4,
UpdateNetworkSystemClock = 5,
UpdateEphemeralSystemClock = 6,
UpdateSteadyClock = 7,
UpdateFileTimestamp = 8,
AutoCorrect = 9,
Max = 10,
};
s32 num_objs{};
std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{};
std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{};
const auto AddWaiter{
[&](Kernel::KSynchronizationObject* synchronization_object, EventType type) {
// Open a new reference to the object.
synchronization_object->Open();
// Insert into the list.
wait_indices[num_objs] = type;
wait_objs[num_objs++] = synchronization_object;
}};
while (!stop_token.stop_requested()) {
SCOPE_EXIT({
for (s32 i = 0; i < num_objs; i++) {
wait_objs[i]->Close();
}
});
enum class EventType : s32 {
Exit = 0,
PowerStateChange = 1,
SignalAlarms = 2,
UpdateLocalSystemClock = 3,
UpdateNetworkSystemClock = 4,
UpdateEphemeralSystemClock = 5,
UpdateSteadyClock = 6,
UpdateFileTimestamp = 7,
AutoCorrect = 8,
};
s32 index{};
num_objs = {};
wait_objs = {};
if (m_pm_state_change_handler.m_priority != 0) {
AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
// TODO
// AddWaiter(gIPmModuleService::GetEvent(), 1);
AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange);
// TODO: gIPmModuleService::GetEvent() 1
index = WaitAny(m_system.Kernel(),
&m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent() // 1
);
} else {
AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
// TODO
// AddWaiter(gIPmModuleService::GetEvent(), 1);
AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange);
AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms);
AddWaiter(m_local_clock_event, EventType::UpdateLocalSystemClock);
AddWaiter(m_network_clock_event, EventType::UpdateNetworkSystemClock);
AddWaiter(m_ephemeral_clock_event, EventType::UpdateEphemeralSystemClock);
AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock);
AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp);
AddWaiter(m_standard_user_auto_correct_clock_event, EventType::AutoCorrect);
// TODO: gIPmModuleService::GetEvent() 1
index = WaitAny(m_system.Kernel(),
&m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent(), // 1
&m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
m_local_clock_event, // 3
m_network_clock_event, // 4
m_ephemeral_clock_event, // 5
&m_timer_steady_clock->GetReadableEvent(), // 6
&m_timer_file_system->GetReadableEvent(), // 7
m_standard_user_auto_correct_clock_event // 8
);
}
s32 out_index{-1};
Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(),
num_objs, -1);
ASSERT(out_index >= 0 && out_index < num_objs);
if (stop_token.stop_requested()) {
return;
}
switch (wait_indices[out_index]) {
switch (static_cast<EventType>(index)) {
case EventType::Exit:
return;
case EventType::IpmModuleService_GetEvent:
// TODO
// IPmModuleService::GetEvent()
// clear the event
// Handle power state change event
break;
case EventType::PowerStateChange:
m_alarm_worker.GetEvent().Clear();
if (m_pm_state_change_handler.m_priority <= 1) {
@@ -235,19 +200,19 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
m_local_clock_event->Clear();
Service::PSC::Time::SystemClockContext context{};
auto res = m_local_clock->GetSystemClockContext(&context);
ASSERT(res == ResultSuccess);
R_ASSERT(m_local_clock->GetSystemClockContext(&context));
m_set_sys->SetUserSystemClockContext(context);
m_file_timestamp_worker.SetFilesystemPosixTime();
} break;
break;
}
case EventType::UpdateNetworkSystemClock: {
m_network_clock_event->Clear();
Service::PSC::Time::SystemClockContext context{};
auto res = m_network_clock->GetSystemClockContext(&context);
ASSERT(res == ResultSuccess);
R_ASSERT(m_network_clock->GetSystemClockContext(&context));
m_set_sys->SetNetworkSystemClockContext(context);
s64 time{};
@@ -267,7 +232,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
}
m_file_timestamp_worker.SetFilesystemPosixTime();
} break;
break;
}
case EventType::UpdateEphemeralSystemClock: {
m_ephemeral_clock_event->Clear();
@@ -295,7 +261,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
if (!g_ig_report_ephemeral_clock_context_set) {
g_ig_report_ephemeral_clock_context_set = true;
}
} break;
break;
}
case EventType::UpdateSteadyClock:
m_timer_steady_clock->Clear();
@@ -314,21 +281,20 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
m_standard_user_auto_correct_clock_event->Clear();
bool automatic_correction{};
auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
&automatic_correction);
ASSERT(res == ResultSuccess);
R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
&automatic_correction));
Service::PSC::Time::SteadyClockTimePoint time_point{};
res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point);
ASSERT(res == ResultSuccess);
R_ASSERT(
m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
} break;
break;
}
default:
UNREACHABLE();
break;
}
}
}
+63 -110
View File
@@ -3,6 +3,7 @@
#include <algorithm>
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/hid_debug_server.h"
#include "core/hle/service/ipc_helpers.h"
#include "hid_core/hid_types.h"
@@ -11,7 +12,6 @@
#include "hid_core/resources/touch_screen/gesture.h"
#include "hid_core/resources/touch_screen/touch_screen.h"
#include "hid_core/resources/touch_screen/touch_types.h"
namespace Service::HID {
@@ -24,14 +24,14 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{0, nullptr, "DeactivateDebugPad"},
{1, nullptr, "SetDebugPadAutoPilotState"},
{2, nullptr, "UnsetDebugPadAutoPilotState"},
{10, &IHidDebugServer::DeactivateTouchScreen, "DeactivateTouchScreen"},
{11, &IHidDebugServer::SetTouchScreenAutoPilotState, "SetTouchScreenAutoPilotState"},
{12, &IHidDebugServer::UnsetTouchScreenAutoPilotState, "UnsetTouchScreenAutoPilotState"},
{13, &IHidDebugServer::GetTouchScreenConfiguration, "GetTouchScreenConfiguration"},
{14, &IHidDebugServer::ProcessTouchScreenAutoTune, "ProcessTouchScreenAutoTune"},
{15, &IHidDebugServer::ForceStopTouchScreenManagement, "ForceStopTouchScreenManagement"},
{16, &IHidDebugServer::ForceRestartTouchScreenManagement, "ForceRestartTouchScreenManagement"},
{17, &IHidDebugServer::IsTouchScreenManaged, "IsTouchScreenManaged"},
{10, C<&IHidDebugServer::DeactivateTouchScreen>, "DeactivateTouchScreen"},
{11, C<&IHidDebugServer::SetTouchScreenAutoPilotState>, "SetTouchScreenAutoPilotState"},
{12, C<&IHidDebugServer::UnsetTouchScreenAutoPilotState>, "UnsetTouchScreenAutoPilotState"},
{13, C<&IHidDebugServer::GetTouchScreenConfiguration>, "GetTouchScreenConfiguration"},
{14, C<&IHidDebugServer::ProcessTouchScreenAutoTune>, "ProcessTouchScreenAutoTune"},
{15, C<&IHidDebugServer::ForceStopTouchScreenManagement>, "ForceStopTouchScreenManagement"},
{16, C<&IHidDebugServer::ForceRestartTouchScreenManagement>, "ForceRestartTouchScreenManagement"},
{17, C<&IHidDebugServer::IsTouchScreenManaged>, "IsTouchScreenManaged"},
{20, nullptr, "DeactivateMouse"},
{21, nullptr, "SetMouseAutoPilotState"},
{22, nullptr, "UnsetMouseAutoPilotState"},
@@ -47,7 +47,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{60, nullptr, "ClearNpadSystemCommonPolicy"},
{61, nullptr, "DeactivateNpad"},
{62, nullptr, "ForceDisconnectNpad"},
{91, &IHidDebugServer::DeactivateGesture, "DeactivateGesture"},
{91, C<&IHidDebugServer::DeactivateGesture>, "DeactivateGesture"},
{110, nullptr, "DeactivateHomeButton"},
{111, nullptr, "SetHomeButtonAutoPilotState"},
{112, nullptr, "UnsetHomeButtonAutoPilotState"},
@@ -160,169 +160,122 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
}
IHidDebugServer::~IHidDebugServer() = default;
void IHidDebugServer::DeactivateTouchScreen(HLERequestContext& ctx) {
Result IHidDebugServer::DeactivateTouchScreen() {
LOG_INFO(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetTouchScreen()->Deactivate();
R_RETURN(GetResourceManager()->GetTouchScreen()->Deactivate());
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IHidDebugServer::SetTouchScreenAutoPilotState(HLERequestContext& ctx) {
Result IHidDebugServer::SetTouchScreenAutoPilotState(
InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer) {
AutoPilotState auto_pilot{};
auto_pilot.count = ctx.GetReadBufferNumElements<TouchState>();
const auto buffer = ctx.ReadBuffer();
auto_pilot.count = std::min(auto_pilot.count, static_cast<u64>(auto_pilot.state.size()));
memcpy(auto_pilot.state.data(), buffer.data(), auto_pilot.count * sizeof(TouchState));
auto_pilot.count =
static_cast<u64>(std::min(auto_pilot_buffer.size(), auto_pilot.state.size()));
memcpy(auto_pilot.state.data(), auto_pilot_buffer.data(),
auto_pilot.count * sizeof(TouchState));
LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count);
const Result result =
GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_RETURN(GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot));
}
void IHidDebugServer::UnsetTouchScreenAutoPilotState(HLERequestContext& ctx) {
Result IHidDebugServer::UnsetTouchScreenAutoPilotState() {
LOG_INFO(Service_HID, "called");
const Result result = GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_RETURN(GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState());
}
void IHidDebugServer::GetTouchScreenConfiguration(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result IHidDebugServer::GetTouchScreenConfiguration(
Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config,
ClientAppletResourceUserId aruid) {
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", aruid.pid);
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
R_TRY(GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration(
*out_touchscreen_config, aruid.pid));
Core::HID::TouchScreenConfigurationForNx touchscreen_config{};
const Result result = GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration(
touchscreen_config, applet_resource_user_id);
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
if (out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Heat2 &&
out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Finger) {
out_touchscreen_config->mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(result);
rb.PushRaw(touchscreen_config);
R_SUCCEED();
}
void IHidDebugServer::ProcessTouchScreenAutoTune(HLERequestContext& ctx) {
Result IHidDebugServer::ProcessTouchScreenAutoTune() {
LOG_INFO(Service_HID, "called");
Result result = GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_RETURN(GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune());
}
void IHidDebugServer::ForceStopTouchScreenManagement(HLERequestContext& ctx) {
Result IHidDebugServer::ForceStopTouchScreenManagement() {
LOG_INFO(Service_HID, "called");
if (!firmware_settings->IsDeviceManaged()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
R_SUCCEED();
}
Result result = ResultSuccess;
bool is_touch_active{};
bool is_gesture_active{};
auto touch_screen = GetResourceManager()->GetTouchScreen();
auto gesture = GetResourceManager()->GetGesture();
if (firmware_settings->IsTouchI2cManaged()) {
result = touch_screen->IsActive(is_touch_active);
if (result.IsSuccess()) {
result = gesture->IsActive(is_gesture_active);
bool is_touch_active{};
bool is_gesture_active{};
R_TRY(touch_screen->IsActive(is_touch_active));
R_TRY(gesture->IsActive(is_gesture_active));
if (is_touch_active) {
R_TRY(touch_screen->Deactivate());
}
if (result.IsSuccess() && is_touch_active) {
result = touch_screen->Deactivate();
}
if (result.IsSuccess() && is_gesture_active) {
result = gesture->Deactivate();
if (is_gesture_active) {
R_TRY(gesture->Deactivate());
}
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IHidDebugServer::ForceRestartTouchScreenManagement(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
u32 basic_gesture_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IHidDebugServer::ForceRestartTouchScreenManagement(u32 basic_gesture_id,
ClientAppletResourceUserId aruid) {
LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
parameters.basic_gesture_id, parameters.applet_resource_user_id);
basic_gesture_id, aruid.pid);
Result result = ResultSuccess;
auto touch_screen = GetResourceManager()->GetTouchScreen();
auto gesture = GetResourceManager()->GetGesture();
if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) {
result = gesture->Activate();
if (result.IsSuccess()) {
result =
gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id);
}
if (result.IsSuccess()) {
result = touch_screen->Activate();
}
if (result.IsSuccess()) {
result = touch_screen->Activate(parameters.applet_resource_user_id);
}
R_TRY(gesture->Activate());
R_TRY(gesture->Activate(aruid.pid, basic_gesture_id));
R_TRY(touch_screen->Activate());
R_TRY(touch_screen->Activate(aruid.pid));
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IHidDebugServer::IsTouchScreenManaged(HLERequestContext& ctx) {
Result IHidDebugServer::IsTouchScreenManaged(Out<bool> out_is_managed) {
LOG_INFO(Service_HID, "called");
bool is_touch_active{};
bool is_gesture_active{};
R_TRY(GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active));
R_TRY(GetResourceManager()->GetGesture()->IsActive(is_gesture_active));
Result result = GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active);
if (result.IsSuccess()) {
result = GetResourceManager()->GetGesture()->IsActive(is_gesture_active);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push(is_touch_active | is_gesture_active);
*out_is_managed = is_touch_active || is_gesture_active;
R_SUCCEED();
}
void IHidDebugServer::DeactivateGesture(HLERequestContext& ctx) {
Result IHidDebugServer::DeactivateGesture() {
LOG_INFO(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetGesture()->Deactivate();
R_RETURN(GetResourceManager()->GetGesture()->Deactivate());
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
+15 -9
View File
@@ -3,7 +3,9 @@
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
#include "hid_core/resources/touch_screen/touch_types.h"
namespace Core {
class System;
@@ -20,15 +22,19 @@ public:
~IHidDebugServer() override;
private:
void DeactivateTouchScreen(HLERequestContext& ctx);
void SetTouchScreenAutoPilotState(HLERequestContext& ctx);
void UnsetTouchScreenAutoPilotState(HLERequestContext& ctx);
void GetTouchScreenConfiguration(HLERequestContext& ctx);
void ProcessTouchScreenAutoTune(HLERequestContext& ctx);
void ForceStopTouchScreenManagement(HLERequestContext& ctx);
void ForceRestartTouchScreenManagement(HLERequestContext& ctx);
void IsTouchScreenManaged(HLERequestContext& ctx);
void DeactivateGesture(HLERequestContext& ctx);
Result DeactivateTouchScreen();
Result SetTouchScreenAutoPilotState(
InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer);
Result UnsetTouchScreenAutoPilotState();
Result GetTouchScreenConfiguration(
Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config,
ClientAppletResourceUserId aruid);
Result ProcessTouchScreenAutoTune();
Result ForceStopTouchScreenManagement();
Result ForceRestartTouchScreenManagement(u32 basic_gesture_id,
ClientAppletResourceUserId aruid);
Result IsTouchScreenManaged(Out<bool> out_is_managed);
Result DeactivateGesture();
std::shared_ptr<ResourceManager> GetResourceManager();
+189 -385
View File
@@ -9,6 +9,7 @@
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/irs.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/memory.h"
@@ -28,24 +29,24 @@ namespace Service::IRS {
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
// clang-format off
static const FunctionInfo functions[] = {
{302, &IRS::ActivateIrsensor, "ActivateIrsensor"},
{303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"},
{304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"},
{305, &IRS::StopImageProcessor, "StopImageProcessor"},
{306, &IRS::RunMomentProcessor, "RunMomentProcessor"},
{307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"},
{308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"},
{309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"},
{310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"},
{311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"},
{312, &IRS::RunPointingProcessor, "RunPointingProcessor"},
{313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"},
{314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"},
{315, &IRS::SetFunctionLevel, "SetFunctionLevel"},
{316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"},
{317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"},
{318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"},
{319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"},
{302, C<&IRS::ActivateIrsensor>, "ActivateIrsensor"},
{303, C<&IRS::DeactivateIrsensor>, "DeactivateIrsensor"},
{304, C<&IRS::GetIrsensorSharedMemoryHandle>, "GetIrsensorSharedMemoryHandle"},
{305, C<&IRS::StopImageProcessor>, "StopImageProcessor"},
{306, C<&IRS::RunMomentProcessor>, "RunMomentProcessor"},
{307, C<&IRS::RunClusteringProcessor>, "RunClusteringProcessor"},
{308, C<&IRS::RunImageTransferProcessor>, "RunImageTransferProcessor"},
{309, C<&IRS::GetImageTransferProcessorState>, "GetImageTransferProcessorState"},
{310, C<&IRS::RunTeraPluginProcessor>, "RunTeraPluginProcessor"},
{311, C<&IRS::GetNpadIrCameraHandle>, "GetNpadIrCameraHandle"},
{312, C<&IRS::RunPointingProcessor>, "RunPointingProcessor"},
{313, C<&IRS::SuspendImageProcessor>, "SuspendImageProcessor"},
{314, C<&IRS::CheckFirmwareVersion>, "CheckFirmwareVersion"},
{315, C<&IRS::SetFunctionLevel>, "SetFunctionLevel"},
{316, C<&IRS::RunImageTransferExProcessor>, "RunImageTransferExProcessor"},
{317, C<&IRS::RunIrLedProcessor>, "RunIrLedProcessor"},
{318, C<&IRS::StopImageProcessorAsync>, "StopImageProcessorAsync"},
{319, C<&IRS::ActivateIrsensorWithFunctionLevel>, "ActivateIrsensorWithFunctionLevel"},
};
// clang-format on
@@ -57,489 +58,292 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
}
IRS::~IRS() = default;
void IRS::ActivateIrsensor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
Result IRS::ActivateIrsensor(ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
R_SUCCEED();
}
void IRS::DeactivateIrsensor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
Result IRS::DeactivateIrsensor(ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
R_SUCCEED();
}
void IRS::GetIrsensorSharedMemoryHandle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result IRS::GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory,
ClientAppletResourceUserId aruid) {
LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", aruid.pid);
LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(&system.Kernel().GetIrsSharedMem());
*out_shared_memory = &system.Kernel().GetIrsSharedMem();
R_SUCCEED();
}
void IRS::StopImageProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Stop Image processor
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
result = ResultSuccess;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
// TODO: Stop Image processor
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
R_SUCCEED();
}
void IRS::RunMomentProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedMomentProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::RunMomentProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedMomentProcessorConfig& processor_config) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessorWithCoreContext<MomentProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<MomentProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::RunClusteringProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedClusteringProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::RunClusteringProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedClusteringProcessorConfig& processor_config) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<ClusteringProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessorWithCoreContext<ClusteringProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<ClusteringProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::RunImageTransferProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
u32 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
Result IRS::RunImageTransferProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) {
const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size");
LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
"applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, t_mem->GetSize(),
aruid.pid);
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::GetImageTransferProcessorState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::GetImageTransferProcessorState(
Out<Core::IrSensor::ImageTransferProcessorState> out_state,
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data) {
LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
const auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(InvalidProcessorState);
return;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
R_UNLESS(device.mode == Core::IrSensor::IrSensorMode::ImageTransferProcessor,
InvalidProcessorState);
std::vector<u8> data{};
const auto& image_transfer_processor =
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
const auto& state = image_transfer_processor.GetState(data);
*out_state = GetProcessor<ImageTransferProcessor>(camera_handle).GetState(out_buffer_data);
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(state);
R_SUCCEED();
}
void IRS::RunTeraPluginProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
Result IRS::RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config,
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
"applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, aruid.pid);
const auto parameters{rp.PopRaw<Parameters>()};
R_TRY(IsIrCameraHandleValid(camera_handle));
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
"applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major,
parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id);
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<TeraPluginProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<TeraPluginProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
Result IRS::GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle,
Core::HID::NpadIdType npad_id) {
R_UNLESS(HID::IsNpadIdValid(npad_id), HID::ResultInvalidNpadId);
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
npad_id != Core::HID::NpadIdType::Handheld) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(Service::HID::ResultInvalidNpadId);
return;
}
Core::IrSensor::IrCameraHandle camera_handle{
*out_camera_handle = {
.npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)),
.npad_type = Core::HID::NpadStyleIndex::None,
};
LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
camera_handle.npad_id, camera_handle.npad_type);
out_camera_handle->npad_id, out_camera_handle->npad_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(camera_handle);
R_SUCCEED();
}
void IRS::RunPointingProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result IRS::RunPointingProcessor(
Core::IrSensor::IrCameraHandle camera_handle,
const Core::IrSensor::PackedPointingProcessorConfig& processor_config,
ClientAppletResourceUserId aruid) {
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
processor_config.required_mcu_version.minor, aruid.pid);
auto result = IsIrCameraHandleValid(camera_handle);
R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<PointingProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<PointingProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::SuspendImageProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Suspend image processor
result = ResultSuccess;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
// TODO: Suspend image processor
R_SUCCEED();
}
void IRS::CheckFirmwareVersion(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result IRS::CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedMcuVersion mcu_version,
ClientAppletResourceUserId aruid) {
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}",
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
camera_handle.npad_type, camera_handle.npad_id, aruid.pid, mcu_version.major,
mcu_version.minor);
auto result = IsIrCameraHandleValid(camera_handle);
if (result.IsSuccess()) {
// TODO: Check firmware version
result = ResultSuccess;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
// TODO: Check firmware version
R_SUCCEED();
}
void IRS::SetFunctionLevel(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result IRS::SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid) {
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, function_level.function_level,
applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, aruid.pid);
auto result = IsIrCameraHandleValid(camera_handle);
if (result.IsSuccess()) {
// TODO: Set Function level
result = ResultSuccess;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
// TODO: Set Function level
R_SUCCEED();
}
void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
u64 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
Result IRS::RunImageTransferExProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) {
const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size");
LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, "
"applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.transfer_memory_size, parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, aruid.pid);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::RunIrLedProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result IRS::RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedIrLedProcessorConfig processor_config,
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} "
"applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target,
processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
processor_config.required_mcu_version.minor, aruid.pid);
auto result = IsIrCameraHandleValid(camera_handle);
R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<IrLedProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<IrLedProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::StopImageProcessorAsync(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Stop image processor async
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
result = ResultSuccess;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
// TODO: Stop image processor async
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
R_SUCCEED();
}
void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::PackedFunctionLevel function_level;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}",
parameters.function_level.function_level, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
function_level.function_level, aruid.pid);
R_SUCCEED();
}
Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
+66 -18
View File
@@ -4,6 +4,7 @@
#pragma once
#include "core/core.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
#include "hid_core/hid_types.h"
#include "hid_core/irsensor/irs_types.h"
@@ -35,26 +36,73 @@ private:
};
static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
void ActivateIrsensor(HLERequestContext& ctx);
void DeactivateIrsensor(HLERequestContext& ctx);
void GetIrsensorSharedMemoryHandle(HLERequestContext& ctx);
void StopImageProcessor(HLERequestContext& ctx);
void RunMomentProcessor(HLERequestContext& ctx);
void RunClusteringProcessor(HLERequestContext& ctx);
void RunImageTransferProcessor(HLERequestContext& ctx);
void GetImageTransferProcessorState(HLERequestContext& ctx);
void RunTeraPluginProcessor(HLERequestContext& ctx);
void GetNpadIrCameraHandle(HLERequestContext& ctx);
void RunPointingProcessor(HLERequestContext& ctx);
void SuspendImageProcessor(HLERequestContext& ctx);
void CheckFirmwareVersion(HLERequestContext& ctx);
void SetFunctionLevel(HLERequestContext& ctx);
void RunImageTransferExProcessor(HLERequestContext& ctx);
void RunIrLedProcessor(HLERequestContext& ctx);
void StopImageProcessorAsync(HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx);
Result ActivateIrsensor(ClientAppletResourceUserId aruid);
Result DeactivateIrsensor(ClientAppletResourceUserId aruid);
Result GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory,
ClientAppletResourceUserId aruid);
Result StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid);
Result RunMomentProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedMomentProcessorConfig& processor_config);
Result RunClusteringProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedClusteringProcessorConfig& processor_config);
Result RunImageTransferProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem);
Result GetImageTransferProcessorState(
Out<Core::IrSensor::ImageTransferProcessorState> out_state,
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data);
Result RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config,
ClientAppletResourceUserId aruid);
Result GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle,
Core::HID::NpadIdType npad_id);
Result RunPointingProcessor(
Core::IrSensor::IrCameraHandle camera_handle,
const Core::IrSensor::PackedPointingProcessorConfig& processor_config,
ClientAppletResourceUserId aruid);
Result SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid);
Result CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedMcuVersion mcu_version,
ClientAppletResourceUserId aruid);
Result SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid);
Result RunImageTransferExProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem);
Result RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedIrLedProcessorConfig processor_config,
ClientAppletResourceUserId aruid);
Result StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid);
Result ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid);
Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
const Core::IrSensor::IrCameraHandle& camera_handle);
@@ -123,6 +123,8 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
vm.va_range_end = params.va_range_end;
}
const u64 max_big_page_bits = Common::Log2Ceil64(vm.va_range_end);
const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)};
const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)};
vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages);
@@ -132,8 +134,8 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages);
gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, vm.big_page_size_bits,
VM::PAGE_SIZE_BITS);
gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split,
vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
system.GPU().InitAddressSpace(*gmmu);
vm.initialised = true;
@@ -7,7 +7,6 @@
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
#include "core/hle/service/nvnflinger/hardware_composer.h"
#include "core/hle/service/nvnflinger/hwc_layer.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
@@ -46,31 +45,9 @@ HardwareComposer::HardwareComposer() = default;
HardwareComposer::~HardwareComposer() = default;
u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance) {
Nvidia::Devices::nvdisp_disp0& nvdisp) {
boost::container::small_vector<HwcLayer, 2> composition_stack;
m_frame_number += frame_advance;
// Release any necessary framebuffers.
for (auto& [layer_id, framebuffer] : m_framebuffers) {
if (framebuffer.release_frame_number > m_frame_number) {
// Not yet ready to release this framebuffer.
continue;
}
if (!framebuffer.is_acquired) {
// Already released.
continue;
}
if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
// TODO: support release fence
// This is needed to prevent screen tearing
layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
framebuffer.is_acquired = false;
}
}
// Set default speed limit to 100%.
*out_speed_scale = 1.0f;
@@ -142,7 +119,30 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
MicroProfileFlip();
// Advance by at least one frame.
return swap_interval.value_or(1);
const u32 frame_advance = swap_interval.value_or(1);
m_frame_number += frame_advance;
// Release any necessary framebuffers.
for (auto& [layer_id, framebuffer] : m_framebuffers) {
if (framebuffer.release_frame_number > m_frame_number) {
// Not yet ready to release this framebuffer.
continue;
}
if (!framebuffer.is_acquired) {
// Already released.
continue;
}
if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
// TODO: support release fence
// This is needed to prevent screen tearing
layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
framebuffer.is_acquired = false;
}
}
return frame_advance;
}
void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) {
@@ -27,7 +27,7 @@ public:
~HardwareComposer();
u32 ComposeLocked(f32* out_speed_scale, VI::Display& display,
Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance);
Nvidia::Devices::nvdisp_disp0& nvdisp);
void RemoveLayerLocked(VI::Display& display, LayerId layer_id);
private:
@@ -291,8 +291,7 @@ void Nvnflinger::Compose() {
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
ASSERT(nvdisp);
swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp,
swap_interval);
swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp);
}
}
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/os/event.h"
namespace Service {
+59
View File
@@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/service/os/multi_wait.h"
namespace Service {
MultiWait::MultiWait() = default;
MultiWait::~MultiWait() = default;
MultiWaitHolder* MultiWait::WaitAny(Kernel::KernelCore& kernel) {
return this->TimedWaitImpl(kernel, -1);
}
MultiWaitHolder* MultiWait::TryWaitAny(Kernel::KernelCore& kernel) {
return this->TimedWaitImpl(kernel, 0);
}
MultiWaitHolder* MultiWait::TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns) {
return this->TimedWaitImpl(kernel, kernel.HardwareTimer().GetTick() + timeout_ns);
}
MultiWaitHolder* MultiWait::TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick) {
std::array<MultiWaitHolder*, Kernel::Svc::ArgumentHandleCountMax> holders{};
std::array<Kernel::KSynchronizationObject*, Kernel::Svc::ArgumentHandleCountMax> objects{};
s32 out_index = -1;
s32 num_objects = 0;
for (auto it = m_wait_list.begin(); it != m_wait_list.end(); it++) {
ASSERT(num_objects < Kernel::Svc::ArgumentHandleCountMax);
holders[num_objects] = std::addressof(*it);
objects[num_objects] = it->GetNativeHandle();
num_objects++;
}
Kernel::KSynchronizationObject::Wait(kernel, std::addressof(out_index), objects.data(),
num_objects, timeout_tick);
if (out_index == -1) {
return nullptr;
} else {
return holders[out_index];
}
}
void MultiWait::MoveAll(MultiWait* other) {
while (!other->m_wait_list.empty()) {
MultiWaitHolder& holder = other->m_wait_list.front();
holder.UnlinkFromMultiWait();
holder.LinkToMultiWait(this);
}
}
} // namespace Service
+36
View File
@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/os/multi_wait_holder.h"
namespace Kernel {
class KernelCore;
}
namespace Service {
class MultiWait final {
public:
explicit MultiWait();
~MultiWait();
public:
MultiWaitHolder* WaitAny(Kernel::KernelCore& kernel);
MultiWaitHolder* TryWaitAny(Kernel::KernelCore& kernel);
MultiWaitHolder* TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns);
// TODO: SdkReplyAndReceive?
void MoveAll(MultiWait* other);
private:
MultiWaitHolder* TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick);
private:
friend class MultiWaitHolder;
using ListType = Common::IntrusiveListMemberTraits<&MultiWaitHolder::m_list_node>::ListType;
ListType m_wait_list{};
};
} // namespace Service
@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/os/multi_wait.h"
#include "core/hle/service/os/multi_wait_holder.h"
namespace Service {
void MultiWaitHolder::LinkToMultiWait(MultiWait* multi_wait) {
if (m_multi_wait != nullptr) {
UNREACHABLE();
}
m_multi_wait = multi_wait;
m_multi_wait->m_wait_list.push_back(*this);
}
void MultiWaitHolder::UnlinkFromMultiWait() {
if (m_multi_wait) {
m_multi_wait->m_wait_list.erase(m_multi_wait->m_wait_list.iterator_to(*this));
m_multi_wait = nullptr;
}
}
} // namespace Service
@@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/intrusive_list.h"
namespace Kernel {
class KSynchronizationObject;
} // namespace Kernel
namespace Service {
class MultiWait;
class MultiWaitHolder {
public:
explicit MultiWaitHolder(Kernel::KSynchronizationObject* native_handle)
: m_native_handle(native_handle) {}
void LinkToMultiWait(MultiWait* multi_wait);
void UnlinkFromMultiWait();
void SetUserData(uintptr_t user_data) {
m_user_data = user_data;
}
uintptr_t GetUserData() const {
return m_user_data;
}
Kernel::KSynchronizationObject* GetNativeHandle() const {
return m_native_handle;
}
private:
friend class MultiWait;
Common::IntrusiveListNode m_list_node{};
MultiWait* m_multi_wait{};
Kernel::KSynchronizationObject* m_native_handle{};
uintptr_t m_user_data{};
};
} // namespace Service
+109
View File
@@ -0,0 +1,109 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/os/multi_wait.h"
namespace Service {
namespace impl {
class AutoMultiWaitHolder {
private:
MultiWaitHolder m_holder;
public:
template <typename T>
explicit AutoMultiWaitHolder(MultiWait* multi_wait, T&& arg) : m_holder(arg) {
m_holder.LinkToMultiWait(multi_wait);
}
~AutoMultiWaitHolder() {
m_holder.UnlinkFromMultiWait();
}
std::pair<MultiWaitHolder*, int> ConvertResult(const std::pair<MultiWaitHolder*, int> result,
int index) {
if (result.first == std::addressof(m_holder)) {
return std::make_pair(static_cast<MultiWaitHolder*>(nullptr), index);
} else {
return result;
}
}
};
using WaitAnyFunction = decltype(&MultiWait::WaitAny);
inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
MultiWait* multi_wait, WaitAnyFunction func,
int) {
return std::pair<MultiWaitHolder*, int>((multi_wait->*func)(kernel), -1);
}
template <typename T, typename... Args>
inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
MultiWait* multi_wait, WaitAnyFunction func,
int index, T&& x, Args&&... args) {
AutoMultiWaitHolder holder(multi_wait, std::forward<T>(x));
return holder.ConvertResult(
WaitAnyImpl(kernel, multi_wait, func, index + 1, std::forward<Args>(args)...), index);
}
template <typename... Args>
inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
MultiWait* multi_wait, WaitAnyFunction func,
Args&&... args) {
return WaitAnyImpl(kernel, multi_wait, func, 0, std::forward<Args>(args)...);
}
template <typename... Args>
inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
WaitAnyFunction func, Args&&... args) {
MultiWait temp_multi_wait;
return WaitAnyImpl(kernel, std::addressof(temp_multi_wait), func, 0,
std::forward<Args>(args)...);
}
class NotBoolButInt {
public:
constexpr NotBoolButInt(int v) : m_value(v) {}
constexpr operator int() const {
return m_value;
}
explicit operator bool() const = delete;
private:
int m_value;
};
} // namespace impl
template <typename... Args>
requires(sizeof...(Args) > 0)
inline std::pair<MultiWaitHolder*, int> WaitAny(Kernel::KernelCore& kernel, MultiWait* multi_wait,
Args&&... args) {
return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, multi_wait, std::forward<Args>(args)...);
}
template <typename... Args>
requires(sizeof...(Args) > 0)
inline int WaitAny(Kernel::KernelCore& kernel, Args&&... args) {
return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, std::forward<Args>(args)...).second;
}
template <typename... Args>
requires(sizeof...(Args) > 0)
inline std::pair<MultiWaitHolder*, int> TryWaitAny(Kernel::KernelCore& kernel,
MultiWait* multi_wait, Args&&... args) {
return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, multi_wait,
std::forward<Args>(args)...);
}
template <typename... Args>
requires(sizeof...(Args) > 0)
inline impl::NotBoolButInt TryWaitAny(Kernel::KernelCore& kernel, Args&&... args) {
return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, std::forward<Args>(args)...).second;
}
} // namespace Service
@@ -4,7 +4,7 @@
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/service/mutex.h"
#include "core/hle/service/os/mutex.h"
namespace Service {
+213 -247
View File
@@ -20,50 +20,91 @@
namespace Service {
constexpr size_t MaximumWaitObjects = 0x40;
enum HandleType {
enum class UserDataTag {
Port,
Session,
DeferEvent,
Event,
};
ServerManager::ServerManager(Core::System& system) : m_system{system}, m_serve_mutex{system} {
class Port : public MultiWaitHolder, public Common::IntrusiveListBaseNode<Port> {
public:
explicit Port(Kernel::KServerPort* server_port, SessionRequestHandlerFactory&& handler_factory)
: MultiWaitHolder(server_port), m_handler_factory(std::move(handler_factory)) {
this->SetUserData(static_cast<uintptr_t>(UserDataTag::Port));
}
~Port() {
this->GetNativeHandle()->Close();
}
SessionRequestHandlerPtr CreateHandler() {
return m_handler_factory();
}
private:
const SessionRequestHandlerFactory m_handler_factory;
};
class Session : public MultiWaitHolder, public Common::IntrusiveListBaseNode<Session> {
public:
explicit Session(Kernel::KServerSession* server_session,
std::shared_ptr<SessionRequestManager>&& manager)
: MultiWaitHolder(server_session), m_manager(std::move(manager)) {
this->SetUserData(static_cast<uintptr_t>(UserDataTag::Session));
}
~Session() {
this->GetNativeHandle()->Close();
}
std::shared_ptr<SessionRequestManager>& GetManager() {
return m_manager;
}
std::shared_ptr<HLERequestContext>& GetContext() {
return m_context;
}
private:
std::shared_ptr<SessionRequestManager> m_manager;
std::shared_ptr<HLERequestContext> m_context;
};
ServerManager::ServerManager(Core::System& system) : m_system{system}, m_selection_mutex{system} {
// Initialize event.
m_event = Kernel::KEvent::Create(system.Kernel());
m_event->Initialize(nullptr);
m_wakeup_event = Kernel::KEvent::Create(system.Kernel());
m_wakeup_event->Initialize(nullptr);
// Register event.
Kernel::KEvent::Register(system.Kernel(), m_event);
Kernel::KEvent::Register(system.Kernel(), m_wakeup_event);
// Link to holder.
m_wakeup_holder.emplace(std::addressof(m_wakeup_event->GetReadableEvent()));
m_wakeup_holder->LinkToMultiWait(std::addressof(m_deferred_list));
}
ServerManager::~ServerManager() {
// Signal stop.
m_stop_source.request_stop();
m_event->Signal();
m_wakeup_event->Signal();
// Wait for processing to stop.
m_stopped.Wait();
m_threads.clear();
// Clean up server ports.
for (const auto& [port, handler] : m_ports) {
port->Close();
// Clean up ports.
for (auto it = m_servers.begin(); it != m_servers.end(); it = m_servers.erase(it)) {
delete std::addressof(*it);
}
// Clean up sessions.
for (const auto& [session, manager] : m_sessions) {
session->Close();
for (auto it = m_sessions.begin(); it != m_sessions.end(); it = m_sessions.erase(it)) {
delete std::addressof(*it);
}
for (const auto& request : m_deferrals) {
request.session->Close();
}
// Close event.
m_event->GetReadableEvent().Close();
m_event->Close();
// Close wakeup event.
m_wakeup_event->GetReadableEvent().Close();
m_wakeup_event->Close();
if (m_deferral_event) {
m_deferral_event->GetReadableEvent().Close();
@@ -75,19 +116,19 @@ void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
server_manager->m_system.RunServer(std::move(server_manager));
}
Result ServerManager::RegisterSession(Kernel::KServerSession* session,
Result ServerManager::RegisterSession(Kernel::KServerSession* server_session,
std::shared_ptr<SessionRequestManager> manager) {
ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
// We are taking ownership of the server session, so don't open it.
auto* session = new Session(server_session, std::move(manager));
// Begin tracking the server session.
{
std::scoped_lock ll{m_list_mutex};
m_sessions.emplace(session, std::move(manager));
std::scoped_lock ll{m_deferred_list_mutex};
m_sessions.push_back(*session);
}
// Signal the wakeup event.
m_event->Signal();
// Register to wait on the session.
this->LinkToDeferredList(session);
R_SUCCEED();
}
@@ -95,21 +136,22 @@ Result ServerManager::RegisterSession(Kernel::KServerSession* session,
Result ServerManager::RegisterNamedService(const std::string& service_name,
SessionRequestHandlerFactory&& handler_factory,
u32 max_sessions) {
ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
// Add the new server to sm: and get the moved server port.
Kernel::KServerPort* server_port{};
R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name,
max_sessions, handler_factory));
// We are taking ownership of the server port, so don't open it.
auto* server = new Port(server_port, std::move(handler_factory));
// Begin tracking the server port.
{
std::scoped_lock ll{m_list_mutex};
m_ports.emplace(server_port, std::move(handler_factory));
std::scoped_lock ll{m_deferred_list_mutex};
m_servers.push_back(*server);
}
// Signal the wakeup event.
m_event->Signal();
// Register to wait on the server port.
this->LinkToDeferredList(server);
R_SUCCEED();
}
@@ -127,8 +169,6 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
Result ServerManager::ManageNamedPort(const std::string& service_name,
SessionRequestHandlerFactory&& handler_factory,
u32 max_sessions) {
ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
// Create a new port.
auto* port = Kernel::KPort::Create(m_system.Kernel());
port->Initialize(max_sessions, false, 0);
@@ -149,12 +189,18 @@ Result ServerManager::ManageNamedPort(const std::string& service_name,
// Open a new reference to the server port.
port->GetServerPort().Open();
// Begin tracking the server port.
// Transfer ownership into a new port object.
auto* server = new Port(std::addressof(port->GetServerPort()), std::move(handler_factory));
// Begin tracking the port.
{
std::scoped_lock ll{m_list_mutex};
m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory));
std::scoped_lock ll{m_deferred_list_mutex};
m_servers.push_back(*server);
}
// Register to wait on the port.
this->LinkToDeferredList(server);
// We succeeded.
R_SUCCEED();
}
@@ -173,6 +219,11 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
// Set the output.
*out_event = m_deferral_event;
// Register to wait on the event.
m_deferral_holder.emplace(std::addressof(m_deferral_event->GetReadableEvent()));
m_deferral_holder->SetUserData(static_cast<uintptr_t>(UserDataTag::DeferEvent));
this->LinkToDeferredList(std::addressof(*m_deferral_holder));
// We succeeded.
R_SUCCEED();
}
@@ -191,270 +242,185 @@ Result ServerManager::LoopProcess() {
R_RETURN(this->LoopProcessImpl());
}
void ServerManager::LinkToDeferredList(MultiWaitHolder* holder) {
// Link.
{
std::scoped_lock lk{m_deferred_list_mutex};
holder->LinkToMultiWait(std::addressof(m_deferred_list));
}
// Signal the wakeup event.
m_wakeup_event->Signal();
}
void ServerManager::LinkDeferred() {
std::scoped_lock lk{m_deferred_list_mutex};
m_multi_wait.MoveAll(std::addressof(m_deferred_list));
}
MultiWaitHolder* ServerManager::WaitSignaled() {
// Ensure we are the only thread waiting for this server.
std::scoped_lock lk{m_selection_mutex};
while (true) {
this->LinkDeferred();
// If we're done, return before we start waiting.
if (m_stop_source.stop_requested()) {
return nullptr;
}
auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
if (selected == std::addressof(*m_wakeup_holder)) {
// Clear and restart if we were woken up.
m_wakeup_event->Clear();
} else {
// Unlink and handle the event.
selected->UnlinkFromMultiWait();
return selected;
}
}
}
Result ServerManager::Process(MultiWaitHolder* holder) {
switch (static_cast<UserDataTag>(holder->GetUserData())) {
case UserDataTag::Session:
R_RETURN(this->OnSessionEvent(static_cast<Session*>(holder)));
case UserDataTag::Port:
R_RETURN(this->OnPortEvent(static_cast<Port*>(holder)));
case UserDataTag::DeferEvent:
R_RETURN(this->OnDeferralEvent());
default:
UNREACHABLE();
}
}
bool ServerManager::WaitAndProcessImpl() {
if (auto* signaled_holder = this->WaitSignaled(); signaled_holder != nullptr) {
R_ASSERT(this->Process(signaled_holder));
return true;
} else {
return false;
}
}
Result ServerManager::LoopProcessImpl() {
while (!m_stop_source.stop_requested()) {
R_TRY(this->WaitAndProcessImpl());
this->WaitAndProcessImpl();
}
R_SUCCEED();
}
Result ServerManager::WaitAndProcessImpl() {
Kernel::KScopedAutoObject<Kernel::KSynchronizationObject> wait_obj;
HandleType wait_type{};
// Ensure we are the only thread waiting for this server.
std::unique_lock sl{m_serve_mutex};
// If we're done, return before we start waiting.
R_SUCCEED_IF(m_stop_source.stop_requested());
// Wait for a tracked object to become signaled.
{
s32 num_objs{};
std::array<HandleType, MaximumWaitObjects> wait_types{};
std::array<Kernel::KSynchronizationObject*, MaximumWaitObjects> wait_objs{};
const auto AddWaiter{
[&](Kernel::KSynchronizationObject* synchronization_object, HandleType type) {
// Open a new reference to the object.
synchronization_object->Open();
// Insert into the list.
wait_types[num_objs] = type;
wait_objs[num_objs++] = synchronization_object;
}};
{
std::scoped_lock ll{m_list_mutex};
// Add all of our ports.
for (const auto& [port, handler] : m_ports) {
AddWaiter(port, HandleType::Port);
}
// Add all of our sessions.
for (const auto& [session, manager] : m_sessions) {
AddWaiter(session, HandleType::Session);
}
}
// Add the deferral wakeup event.
if (m_deferral_event != nullptr) {
AddWaiter(std::addressof(m_deferral_event->GetReadableEvent()), HandleType::DeferEvent);
}
// Add the wakeup event.
AddWaiter(std::addressof(m_event->GetReadableEvent()), HandleType::Event);
// Clean up extra references on exit.
SCOPE_EXIT({
for (s32 i = 0; i < num_objs; i++) {
wait_objs[i]->Close();
}
});
// Wait for a signal.
s32 out_index{-1};
R_TRY_CATCH(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index,
wait_objs.data(), num_objs, -1)) {
R_CATCH(Kernel::ResultSessionClosed) {
// On session closed, index is updated and we don't want to return an error.
}
}
R_END_TRY_CATCH;
ASSERT(out_index >= 0 && out_index < num_objs);
// Set the output index.
wait_obj = wait_objs[out_index];
wait_type = wait_types[out_index];
}
// Process what we just received, temporarily removing the object so it is
// not processed concurrently by another thread.
{
switch (wait_type) {
case HandleType::Port: {
// Port signaled.
auto* port = wait_obj->DynamicCast<Kernel::KServerPort*>();
SessionRequestHandlerFactory handler_factory;
// Remove from tracking.
{
std::scoped_lock ll{m_list_mutex};
ASSERT(m_ports.contains(port));
m_ports.at(port).swap(handler_factory);
m_ports.erase(port);
}
// Allow other threads to serve.
sl.unlock();
// Finish.
R_RETURN(this->OnPortEvent(port, std::move(handler_factory)));
}
case HandleType::Session: {
// Session signaled.
auto* session = wait_obj->DynamicCast<Kernel::KServerSession*>();
std::shared_ptr<SessionRequestManager> manager;
// Remove from tracking.
{
std::scoped_lock ll{m_list_mutex};
ASSERT(m_sessions.contains(session));
m_sessions.at(session).swap(manager);
m_sessions.erase(session);
}
// Allow other threads to serve.
sl.unlock();
// Finish.
R_RETURN(this->OnSessionEvent(session, std::move(manager)));
}
case HandleType::DeferEvent: {
// Clear event.
ASSERT(R_SUCCEEDED(m_deferral_event->Clear()));
// Drain the list of deferrals while we process.
std::list<RequestState> deferrals;
{
std::scoped_lock ll{m_list_mutex};
m_deferrals.swap(deferrals);
}
// Allow other threads to serve.
sl.unlock();
// Finish.
R_RETURN(this->OnDeferralEvent(std::move(deferrals)));
}
case HandleType::Event: {
// Clear event and finish.
R_RETURN(m_event->Clear());
}
default: {
UNREACHABLE();
}
}
}
}
Result ServerManager::OnPortEvent(Kernel::KServerPort* port,
SessionRequestHandlerFactory&& handler_factory) {
Result ServerManager::OnPortEvent(Port* server) {
// Accept a new server session.
Kernel::KServerSession* session = port->AcceptSession();
ASSERT(session != nullptr);
auto* server_port = static_cast<Kernel::KServerPort*>(server->GetNativeHandle());
Kernel::KServerSession* server_session = server_port->AcceptSession();
ASSERT(server_session != nullptr);
// Create the session manager and install the handler.
auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this);
manager->SetSessionHandler(handler_factory());
manager->SetSessionHandler(server->CreateHandler());
// Track the server session.
{
std::scoped_lock ll{m_list_mutex};
m_ports.emplace(port, std::move(handler_factory));
m_sessions.emplace(session, std::move(manager));
}
// Create and register the new session.
this->RegisterSession(server_session, std::move(manager));
// Signal the wakeup event.
m_event->Signal();
// Resume tracking the port.
this->LinkToDeferredList(server);
// We succeeded.
R_SUCCEED();
}
Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
std::shared_ptr<SessionRequestManager>&& manager) {
Result rc{ResultSuccess};
Result ServerManager::OnSessionEvent(Session* session) {
Result res = ResultSuccess;
// Try to receive a message.
std::shared_ptr<HLERequestContext> context;
rc = session->ReceiveRequestHLE(&context, manager);
auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle());
res = server_session->ReceiveRequestHLE(&session->GetContext(), session->GetManager());
// If the session has been closed, we're done.
if (rc == Kernel::ResultSessionClosed) {
// Close the session.
session->Close();
// Finish.
if (res == Kernel::ResultSessionClosed) {
this->DestroySession(session);
R_SUCCEED();
}
ASSERT(R_SUCCEEDED(rc));
RequestState request{
.session = session,
.context = std::move(context),
.manager = std::move(manager),
};
R_ASSERT(res);
// Complete the sync request with deferral handling.
R_RETURN(this->CompleteSyncRequest(std::move(request)));
R_RETURN(this->CompleteSyncRequest(session));
}
Result ServerManager::CompleteSyncRequest(RequestState&& request) {
Result rc{ResultSuccess};
Result service_rc{ResultSuccess};
Result ServerManager::CompleteSyncRequest(Session* session) {
Result res = ResultSuccess;
Result service_res = ResultSuccess;
// Mark the request as not deferred.
request.context->SetIsDeferred(false);
session->GetContext()->SetIsDeferred(false);
// Complete the request. We have exclusive access to this session.
service_rc = request.manager->CompleteSyncRequest(request.session, *request.context);
auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle());
service_res =
session->GetManager()->CompleteSyncRequest(server_session, *session->GetContext());
// If we've been deferred, we're done.
if (request.context->GetIsDeferred()) {
// Insert into deferral list.
std::scoped_lock ll{m_list_mutex};
m_deferrals.emplace_back(std::move(request));
if (session->GetContext()->GetIsDeferred()) {
// Insert into deferred session list.
std::scoped_lock ll{m_deferred_list_mutex};
m_deferred_sessions.push_back(session);
// Finish.
R_SUCCEED();
}
// Send the reply.
rc = request.session->SendReplyHLE();
res = server_session->SendReplyHLE();
// If the session has been closed, we're done.
if (rc == Kernel::ResultSessionClosed || service_rc == IPC::ResultSessionClosed) {
// Close the session.
request.session->Close();
// Finish.
if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) {
this->DestroySession(session);
R_SUCCEED();
}
ASSERT(R_SUCCEEDED(rc));
ASSERT(R_SUCCEEDED(service_rc));
R_ASSERT(res);
R_ASSERT(service_res);
// Reinsert the session.
{
std::scoped_lock ll{m_list_mutex};
m_sessions.emplace(request.session, std::move(request.manager));
}
// We succeeded, so we can process future messages on this session.
this->LinkToDeferredList(session);
// Signal the wakeup event.
m_event->Signal();
// We succeeded.
R_SUCCEED();
}
Result ServerManager::OnDeferralEvent(std::list<RequestState>&& deferrals) {
ON_RESULT_FAILURE {
std::scoped_lock ll{m_list_mutex};
m_deferrals.splice(m_deferrals.end(), deferrals);
};
Result ServerManager::OnDeferralEvent() {
// Clear event before grabbing the list.
m_deferral_event->Clear();
while (!deferrals.empty()) {
RequestState request = deferrals.front();
deferrals.pop_front();
// Get and clear list.
const auto deferrals = [&] {
std::scoped_lock lk{m_deferred_list_mutex};
return std::move(m_deferred_sessions);
}();
// Try again to complete the request.
R_TRY(this->CompleteSyncRequest(std::move(request)));
// Relink deferral event.
this->LinkToDeferredList(std::addressof(*m_deferral_holder));
// For each session, try again to complete the request.
for (auto* session : deferrals) {
R_ASSERT(this->CompleteSyncRequest(session));
}
R_SUCCEED();
}
void ServerManager::DestroySession(Session* session) {
// Unlink.
{
std::scoped_lock lk{m_deferred_list_mutex};
m_sessions.erase(m_sessions.iterator_to(*session));
}
// Free the session.
delete session;
}
} // namespace Service
+33 -26
View File
@@ -3,18 +3,17 @@
#pragma once
#include <functional>
#include <list>
#include <map>
#include <mutex>
#include <string_view>
#include <optional>
#include <vector>
#include "common/polyfill_thread.h"
#include "common/thread.h"
#include "core/hle/result.h"
#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/mutex.h"
#include "core/hle/service/os/multi_wait.h"
#include "core/hle/service/os/mutex.h"
namespace Core {
class System;
@@ -24,11 +23,13 @@ namespace Kernel {
class KEvent;
class KServerPort;
class KServerSession;
class KSynchronizationObject;
} // namespace Kernel
namespace Service {
class Port;
class Session;
class ServerManager {
public:
explicit ServerManager(Core::System& system);
@@ -52,34 +53,40 @@ public:
static void RunServer(std::unique_ptr<ServerManager>&& server);
private:
struct RequestState;
void LinkToDeferredList(MultiWaitHolder* holder);
void LinkDeferred();
MultiWaitHolder* WaitSignaled();
Result Process(MultiWaitHolder* holder);
bool WaitAndProcessImpl();
Result LoopProcessImpl();
Result WaitAndProcessImpl();
Result OnPortEvent(Kernel::KServerPort* port, SessionRequestHandlerFactory&& handler_factory);
Result OnSessionEvent(Kernel::KServerSession* session,
std::shared_ptr<SessionRequestManager>&& manager);
Result OnDeferralEvent(std::list<RequestState>&& deferrals);
Result CompleteSyncRequest(RequestState&& state);
Result OnPortEvent(Port* port);
Result OnSessionEvent(Session* session);
Result OnDeferralEvent();
Result CompleteSyncRequest(Session* session);
private:
void DestroySession(Session* session);
private:
Core::System& m_system;
Mutex m_serve_mutex;
std::mutex m_list_mutex;
Mutex m_selection_mutex;
// Guest state tracking
std::map<Kernel::KServerPort*, SessionRequestHandlerFactory> m_ports{};
std::map<Kernel::KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions{};
Kernel::KEvent* m_event{};
// Events
Kernel::KEvent* m_wakeup_event{};
Kernel::KEvent* m_deferral_event{};
// Deferral tracking
struct RequestState {
Kernel::KServerSession* session;
std::shared_ptr<HLERequestContext> context;
std::shared_ptr<SessionRequestManager> manager;
};
std::list<RequestState> m_deferrals{};
// Deferred wait list
std::mutex m_deferred_list_mutex{};
MultiWait m_deferred_list{};
// Guest state tracking
MultiWait m_multi_wait{};
Common::IntrusiveListBaseTraits<Port>::ListType m_servers{};
Common::IntrusiveListBaseTraits<Session>::ListType m_sessions{};
std::list<Session*> m_deferred_sessions{};
std::optional<MultiWaitHolder> m_wakeup_holder{};
std::optional<MultiWaitHolder> m_deferral_holder{};
// Host state tracking
Common::Event m_stopped{};
+30 -16
View File
@@ -9,6 +9,7 @@
#include "core/core_timing.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/sm/sm.h"
#include "core/memory.h"
@@ -46,12 +47,23 @@ StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMeta
StandardVmCallbacks::~StandardVmCallbacks() = default;
void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
system.ApplicationMemory().ReadBlock(SanitizeAddress(address), data, size);
void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) {
// Return zero on invalid address
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
std::memset(data, 0, size);
return;
}
system.ApplicationMemory().ReadBlock(address, data, size);
}
void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) {
system.ApplicationMemory().WriteBlock(SanitizeAddress(address), data, size);
void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) {
// Skip invalid memory write address
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
return;
}
system.ApplicationMemory().WriteBlock(address, data, size);
}
u64 StandardVmCallbacks::HidKeysDown() {
@@ -81,21 +93,25 @@ void StandardVmCallbacks::CommandLog(std::string_view data) {
data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
}
VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const {
bool StandardVmCallbacks::IsAddressInRange(VAddr in) const {
if ((in < metadata.main_nso_extents.base ||
in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
(in < metadata.heap_extents.base ||
in >= metadata.heap_extents.base + metadata.heap_extents.size)) {
LOG_ERROR(CheatEngine,
in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
(in < metadata.alias_extents.base ||
in >= metadata.heap_extents.base + metadata.alias_extents.size) &&
(in < metadata.aslr_extents.base ||
in >= metadata.heap_extents.base + metadata.aslr_extents.size)) {
LOG_DEBUG(CheatEngine,
"Cheat attempting to access memory at invalid address={:016X}, if this "
"persists, "
"the cheat may be incorrect. However, this may be normal early in execution if "
"the game has not properly set up yet.",
in);
return 0; ///< Invalid addresses will hard crash
return false; ///< Invalid addresses will hard crash
}
return in;
return true;
}
CheatParser::~CheatParser() = default;
@@ -211,16 +227,14 @@ void CheatEngine::Initialize() {
.base = GetInteger(page_table.GetHeapRegionStart()),
.size = page_table.GetHeapRegionSize(),
};
metadata.address_space_extents = {
.base = GetInteger(page_table.GetAddressSpaceStart()),
.size = page_table.GetAddressSpaceSize(),
};
metadata.alias_extents = {
metadata.aslr_extents = {
.base = GetInteger(page_table.GetAliasCodeRegionStart()),
.size = page_table.GetAliasCodeRegionSize(),
};
metadata.alias_extents = {
.base = GetInteger(page_table.GetAliasRegionStart()),
.size = page_table.GetAliasRegionSize(),
};
is_pending_reload.exchange(true);
}
+4 -4
View File
@@ -27,17 +27,17 @@ public:
StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_);
~StandardVmCallbacks() override;
void MemoryRead(VAddr address, void* data, u64 size) override;
void MemoryWrite(VAddr address, const void* data, u64 size) override;
void MemoryReadUnsafe(VAddr address, void* data, u64 size) override;
void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override;
u64 HidKeysDown() override;
void DebugLog(u8 id, u64 value) override;
void CommandLog(std::string_view data) override;
private:
VAddr SanitizeAddress(VAddr address) const;
bool IsAddressInRange(VAddr address) const;
const CheatProcessMetadata& metadata;
System& system;
Core::System& system;
};
// Intermediary class that parses a text file or other disk format for storing cheats into a
+1 -1
View File
@@ -18,7 +18,7 @@ struct CheatProcessMetadata {
MemoryRegionExtents main_nso_extents{};
MemoryRegionExtents heap_extents{};
MemoryRegionExtents alias_extents{};
MemoryRegionExtents address_space_extents{};
MemoryRegionExtents aslr_extents{};
std::array<u8, 0x20> main_nso_build_id{};
};
+52 -22
View File
@@ -322,8 +322,9 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
} break;
case CheatVmOpcodeType::EndConditionalBlock: {
// 20000000
// There's actually nothing left to process here!
opcode.opcode = EndConditionalOpcode{};
opcode.opcode = EndConditionalOpcode{
.is_else = ((first_dword >> 24) & 0xf) == 1,
};
} break;
case CheatVmOpcodeType::ControlLoop: {
// 300R0000 VVVVVVVV
@@ -555,6 +556,18 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
.idx = first_dword & 0xF,
};
} break;
case CheatVmOpcodeType::PauseProcess: {
/* FF0????? */
/* FF0 = opcode 0xFF0 */
/* Pauses the current process. */
opcode.opcode = PauseProcessOpcode{};
} break;
case CheatVmOpcodeType::ResumeProcess: {
/* FF0????? */
/* FF0 = opcode 0xFF0 */
/* Pauses the current process. */
opcode.opcode = ResumeProcessOpcode{};
} break;
case CheatVmOpcodeType::DebugLog: {
// FFFTIX##
// FFFTI0Ma aaaaaaaa
@@ -621,7 +634,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
return valid;
}
void DmntCheatVm::SkipConditionalBlock() {
void DmntCheatVm::SkipConditionalBlock(bool is_if) {
if (condition_depth > 0) {
// We want to continue until we're out of the current block.
const std::size_t desired_depth = condition_depth - 1;
@@ -637,8 +650,12 @@ void DmntCheatVm::SkipConditionalBlock() {
// We also support nesting of conditional blocks, and Gateway does not.
if (skip_opcode.begin_conditional_block) {
condition_depth++;
} else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) {
condition_depth--;
} else if (auto end_cond = std::get_if<EndConditionalOpcode>(&skip_opcode.opcode)) {
if (!end_cond->is_else) {
condition_depth--;
} else if (is_if && condition_depth - 1 == desired_depth) {
break;
}
}
}
} else {
@@ -675,6 +692,10 @@ u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
return metadata.main_nso_extents.base + rel_address;
case MemoryAccessType::Heap:
return metadata.heap_extents.base + rel_address;
case MemoryAccessType::Alias:
return metadata.alias_extents.base + rel_address;
case MemoryAccessType::Aslr:
return metadata.aslr_extents.base + rel_address;
}
}
@@ -682,7 +703,6 @@ void DmntCheatVm::ResetState() {
registers.fill(0);
saved_values.fill(0);
loop_tops.fill(0);
static_registers.fill(0);
instruction_ptr = 0;
condition_depth = 0;
decode_success = true;
@@ -753,7 +773,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width);
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, store_static->bit_width);
break;
}
} else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
@@ -766,7 +786,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width);
callbacks->MemoryReadUnsafe(src_address, &src_value, begin_cond->bit_width);
break;
}
// Check against condition.
@@ -794,13 +814,18 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
}
// Skip conditional block if condition not met.
if (!cond_met) {
SkipConditionalBlock();
SkipConditionalBlock(true);
}
} else if (std::holds_alternative<EndConditionalOpcode>(cur_opcode.opcode)) {
// Decrement the condition depth.
// We will assume, graciously, that mismatched conditional block ends are a nop.
if (condition_depth > 0) {
condition_depth--;
} else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) {
if (end_cond->is_else) {
/* Skip to the end of the conditional block. */
this->SkipConditionalBlock(false);
} else {
/* Decrement the condition depth. */
/* We will assume, graciously, that mismatched conditional block ends are a nop. */
if (condition_depth > 0) {
condition_depth--;
}
}
} else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
if (ctrl_loop->start_loop) {
@@ -832,8 +857,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryRead(src_address, &registers[ldr_memory->reg_index],
ldr_memory->bit_width);
callbacks->MemoryReadUnsafe(src_address, &registers[ldr_memory->reg_index],
ldr_memory->bit_width);
break;
}
} else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) {
@@ -849,7 +874,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width);
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_static->bit_width);
break;
}
// Increment register if relevant.
@@ -908,7 +933,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
// Check for keypress.
if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
// Keys not pressed. Skip conditional block.
SkipConditionalBlock();
SkipConditionalBlock(true);
}
} else if (auto perform_math_reg =
std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
@@ -1007,7 +1032,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width);
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_register->bit_width);
break;
}
@@ -1086,7 +1111,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width);
callbacks->MemoryReadUnsafe(cond_address, &cond_value,
begin_reg_cond->bit_width);
break;
}
}
@@ -1116,7 +1142,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
// Skip conditional block if condition not met.
if (!cond_met) {
SkipConditionalBlock();
SkipConditionalBlock(true);
}
} else if (auto save_restore_reg =
std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
@@ -1178,6 +1204,10 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
// Store a register to a static register.
static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
}
} else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) {
// TODO: Pause cheat process
} else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) {
// TODO: Resume cheat process
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
// Read value from memory.
u64 log_value = 0;
@@ -1224,7 +1254,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width);
callbacks->MemoryReadUnsafe(val_address, &log_value, debug_log->bit_width);
break;
}
}
+16 -6
View File
@@ -42,12 +42,16 @@ enum class CheatVmOpcodeType : u32 {
DoubleExtendedWidth = 0xF0,
// Double-extended width opcodes.
PauseProcess = 0xFF0,
ResumeProcess = 0xFF1,
DebugLog = 0xFFF,
};
enum class MemoryAccessType : u32 {
MainNso = 0,
Heap = 1,
Alias = 2,
Aslr = 3,
};
enum class ConditionalComparisonType : u32 {
@@ -131,7 +135,9 @@ struct BeginConditionalOpcode {
VmInt value{};
};
struct EndConditionalOpcode {};
struct EndConditionalOpcode {
bool is_else;
};
struct ControlLoopOpcode {
bool start_loop{};
@@ -222,6 +228,10 @@ struct ReadWriteStaticRegisterOpcode {
u32 idx{};
};
struct PauseProcessOpcode {};
struct ResumeProcessOpcode {};
struct DebugLogOpcode {
u32 bit_width{};
u32 log_id{};
@@ -244,8 +254,8 @@ struct CheatVmOpcode {
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode,
UnrecognizedInstruction>
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
opcode{};
};
@@ -256,8 +266,8 @@ public:
public:
virtual ~Callbacks();
virtual void MemoryRead(VAddr address, void* data, u64 size) = 0;
virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0;
virtual void MemoryReadUnsafe(VAddr address, void* data, u64 size) = 0;
virtual void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) = 0;
virtual u64 HidKeysDown() = 0;
@@ -296,7 +306,7 @@ private:
std::array<std::size_t, NumRegisters> loop_tops{};
bool DecodeNextOpcode(CheatVmOpcode& out);
void SkipConditionalBlock();
void SkipConditionalBlock(bool is_if);
void ResetState();
// For implementing the DebugLog opcode.
@@ -145,9 +145,8 @@ void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_m
}
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
std::vector<u8>& data) const {
const auto size = GetDataSize(current_config.trimming_format);
data.resize(size);
std::span<u8> data) const {
const auto size = std::min(GetDataSize(current_config.trimming_format), data.size());
system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size);
return processor_state;
}
@@ -3,6 +3,8 @@
#pragma once
#include <span>
#include "common/typed_address.h"
#include "hid_core/irsensor/irs_types.h"
#include "hid_core/irsensor/processor_base.h"
@@ -39,7 +41,7 @@ public:
// Transfer memory where the image data will be stored
void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const;
Core::IrSensor::ImageTransferProcessorState GetState(std::span<u8> data) const;
private:
// This is nn::irsensor::ImageTransferProcessorConfig
+42 -46
View File
@@ -52,9 +52,42 @@ ResourceManager::ResourceManager(Core::System& system_,
std::shared_ptr<HidFirmwareSettings> settings)
: firmware_settings{settings}, system{system_}, service_context{system_, "hid"} {
applet_resource = std::make_shared<AppletResource>(system);
// Register update callbacks
npad_update_event = Core::Timing::CreateEvent("HID::UpdatePadCallback",
[this](s64 time, std::chrono::nanoseconds ns_late)
-> std::optional<std::chrono::nanoseconds> {
UpdateNpad(ns_late);
return std::nullopt;
});
default_update_event = Core::Timing::CreateEvent(
"HID::UpdateDefaultCallback",
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
UpdateControllers(ns_late);
return std::nullopt;
});
mouse_keyboard_update_event = Core::Timing::CreateEvent(
"HID::UpdateMouseKeyboardCallback",
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
UpdateMouseKeyboard(ns_late);
return std::nullopt;
});
motion_update_event = Core::Timing::CreateEvent(
"HID::UpdateMotionCallback",
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
UpdateMotion(ns_late);
return std::nullopt;
});
}
ResourceManager::~ResourceManager() {
system.CoreTiming().UnscheduleEvent(npad_update_event);
system.CoreTiming().UnscheduleEvent(default_update_event);
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event);
system.CoreTiming().UnscheduleEvent(motion_update_event);
system.CoreTiming().UnscheduleEvent(touch_update_event);
input_event->Finalize();
};
@@ -201,6 +234,7 @@ void ResourceManager::InitializeHidCommonSampler() {
debug_pad->SetAppletResource(applet_resource, &shared_mutex);
digitizer->SetAppletResource(applet_resource, &shared_mutex);
unique_pad->SetAppletResource(applet_resource, &shared_mutex);
keyboard->SetAppletResource(applet_resource, &shared_mutex);
const auto settings =
@@ -214,6 +248,14 @@ void ResourceManager::InitializeHidCommonSampler() {
home_button->SetAppletResource(applet_resource, &shared_mutex);
sleep_button->SetAppletResource(applet_resource, &shared_mutex);
capture_button->SetAppletResource(applet_resource, &shared_mutex);
system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
default_update_event);
system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
mouse_keyboard_update_event);
system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
motion_update_event);
}
void ResourceManager::InitializeTouchScreenSampler() {
@@ -465,55 +507,9 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
};
RegisterHandlers(functions);
// Register update callbacks
npad_update_event = Core::Timing::CreateEvent(
"HID::UpdatePadCallback",
[this, resource](
s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
resource->UpdateNpad(ns_late);
return std::nullopt;
});
default_update_event = Core::Timing::CreateEvent(
"HID::UpdateDefaultCallback",
[this, resource](
s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
resource->UpdateControllers(ns_late);
return std::nullopt;
});
mouse_keyboard_update_event = Core::Timing::CreateEvent(
"HID::UpdateMouseKeyboardCallback",
[this, resource](
s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
resource->UpdateMouseKeyboard(ns_late);
return std::nullopt;
});
motion_update_event = Core::Timing::CreateEvent(
"HID::UpdateMotionCallback",
[this, resource](
s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
resource->UpdateMotion(ns_late);
return std::nullopt;
});
system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
default_update_event);
system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
mouse_keyboard_update_event);
system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
motion_update_event);
}
IAppletResource::~IAppletResource() {
system.CoreTiming().UnscheduleEvent(npad_update_event);
system.CoreTiming().UnscheduleEvent(default_update_event);
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event);
system.CoreTiming().UnscheduleEvent(motion_update_event);
resource_manager->FreeAppletResourceId(aruid);
}
+4 -5
View File
@@ -147,6 +147,10 @@ private:
std::shared_ptr<SixAxis> six_axis{nullptr};
std::shared_ptr<SleepButton> sleep_button{nullptr};
std::shared_ptr<UniquePad> unique_pad{nullptr};
std::shared_ptr<Core::Timing::EventType> npad_update_event;
std::shared_ptr<Core::Timing::EventType> default_update_event;
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event;
// TODO: Create these resources
// std::shared_ptr<AudioControl> audio_control{nullptr};
@@ -179,11 +183,6 @@ public:
private:
void GetSharedMemoryHandle(HLERequestContext& ctx);
std::shared_ptr<Core::Timing::EventType> npad_update_event{nullptr};
std::shared_ptr<Core::Timing::EventType> default_update_event{nullptr};
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event{nullptr};
std::shared_ptr<Core::Timing::EventType> motion_update_event{nullptr};
u64 aruid{};
std::shared_ptr<ResourceManager> resource_manager;
};
@@ -57,7 +57,7 @@ Result NpadAbstractSixAxisHandler::UpdateSixAxisState() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
if (data->flag.is_assigned) {
if (data == nullptr || !data->flag.is_assigned) {
continue;
}
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
@@ -17,10 +17,6 @@ void Digitizer::OnInit() {}
void Digitizer::OnRelease() {}
void Digitizer::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!smart_update) {
return;
}
std::scoped_lock shared_lock{*shared_mutex};
const u64 aruid = applet_resource->GetActiveAruid();
auto* data = applet_resource->GetAruidData(aruid);
@@ -20,8 +20,5 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
bool smart_update{};
};
} // namespace Service::HID
+20 -4
View File
@@ -102,6 +102,8 @@ Result NPad::Activate(u64 aruid) {
for (std::size_t i = 0; i < 19; ++i) {
WriteEmptyEntry(npad);
}
controller.is_active = true;
}
return ResultSuccess;
@@ -129,7 +131,7 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c
auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
if (!data->flag.is_assigned) {
if (data == nullptr || !data->flag.is_assigned) {
continue;
}
@@ -461,9 +463,16 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
std::scoped_lock lock{*applet_resource_holder.shared_mutex};
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
const auto aruid = data->aruid;
if (!data->flag.is_assigned) {
if (data == nullptr || !data->flag.is_assigned) {
continue;
}
bool is_set{};
const auto aruid = data->aruid;
npad_resource.IsSupportedNpadStyleSet(is_set, aruid);
// Wait until style is defined
if (!is_set) {
continue;
}
@@ -484,6 +493,10 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
continue;
}
if (!controller.is_active) {
continue;
}
RequestPadStateUpdate(aruid, controller.device->GetNpadIdType());
auto& pad_state = controller.npad_pad_state;
auto& libnx_state = controller.npad_libnx_state;
@@ -592,7 +605,9 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;
libnx_state.l_stick = pad_state.l_stick;
libnx_state.r_stick = pad_state.r_stick;
npad->system_ext_lifo.WriteNextEntry(pad_state);
libnx_state.sampling_number =
npad->system_ext_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad->system_ext_lifo.WriteNextEntry(libnx_state);
press_state |= static_cast<u64>(pad_state.npad_buttons.raw);
}
@@ -1060,6 +1075,7 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) {
// TODO: Remove this once abstract pad is emulated properly
const auto aruid_index = npad_resource.GetIndexFromAruid(aruid);
for (auto& controller : controller_data[aruid_index]) {
controller.is_active = false;
controller.is_connected = false;
controller.shared_memory = nullptr;
}
+1
View File
@@ -164,6 +164,7 @@ private:
NpadInternalState* shared_memory = nullptr;
Core::HID::EmulatedController* device = nullptr;
bool is_active{};
bool is_connected{};
// Dual joycons can have only one side connected
+130 -124
View File
@@ -28,142 +28,148 @@ void SixAxis::OnRelease() {}
void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
std::scoped_lock shared_lock{*shared_mutex};
const u64 aruid = applet_resource->GetActiveAruid();
auto* data = applet_resource->GetAruidData(aruid);
if (data == nullptr || !data->flag.is_assigned) {
return;
}
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
const auto* data = applet_resource->GetAruidDataByIndex(aruid_index);
if (!IsControllerActivated()) {
return;
}
for (std::size_t i = 0; i < controller_data.size(); ++i) {
NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i];
auto& controller = controller_data[i];
const auto& controller_type = controller.device->GetNpadStyleIndex();
if (controller_type == Core::HID::NpadStyleIndex::None ||
!controller.device->IsConnected()) {
if (data == nullptr || !data->flag.is_assigned) {
continue;
}
const auto& motion_state = controller.device->GetMotions();
auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo;
auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo;
auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo;
auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo;
auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo;
auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo;
// Clear previous state
sixaxis_fullkey_state = {};
sixaxis_handheld_state = {};
sixaxis_dual_left_state = {};
sixaxis_dual_right_state = {};
sixaxis_left_lifo_state = {};
sixaxis_right_lifo_state = {};
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
controller.sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_state.size(); ++e) {
controller.sixaxis_at_rest =
controller.sixaxis_at_rest && motion_state[e].is_at_rest;
}
if (!IsControllerActivated()) {
return;
}
const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state,
const Core::HID::ControllerMotion& hid_state) {
using namespace std::literals::chrono_literals;
static constexpr Core::HID::SixAxisSensorState default_motion_state = {
.delta_time = std::chrono::nanoseconds(5ms).count(),
.accel = {0, 0, -1.0f},
.orientation =
{
Common::Vec3f{1.0f, 0, 0},
Common::Vec3f{0, 1.0f, 0},
Common::Vec3f{0, 0, 1.0f},
},
.attribute = {1},
for (std::size_t i = 0; i < controller_data.size(); ++i) {
NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i];
auto& controller = controller_data[i];
const auto& controller_type = controller.device->GetNpadStyleIndex();
if (!data->flag.enable_six_axis_sensor) {
continue;
}
if (controller_type == Core::HID::NpadStyleIndex::None ||
!controller.device->IsConnected()) {
continue;
}
const auto& motion_state = controller.device->GetMotions();
auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo;
auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo;
auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo;
auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo;
auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo;
auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo;
// Clear previous state
sixaxis_fullkey_state = {};
sixaxis_handheld_state = {};
sixaxis_dual_left_state = {};
sixaxis_dual_right_state = {};
sixaxis_left_lifo_state = {};
sixaxis_right_lifo_state = {};
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
controller.sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_state.size(); ++e) {
controller.sixaxis_at_rest =
controller.sixaxis_at_rest && motion_state[e].is_at_rest;
}
}
const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state,
const Core::HID::ControllerMotion& hid_state) {
using namespace std::literals::chrono_literals;
static constexpr Core::HID::SixAxisSensorState default_motion_state = {
.delta_time = std::chrono::nanoseconds(5ms).count(),
.accel = {0, 0, -1.0f},
.orientation =
{
Common::Vec3f{1.0f, 0, 0},
Common::Vec3f{0, 1.0f, 0},
Common::Vec3f{0, 0, 1.0f},
},
.attribute = {1},
};
if (!controller.sixaxis_sensor_enabled) {
state = default_motion_state;
return;
}
if (!Settings::values.motion_enabled.GetValue()) {
state = default_motion_state;
return;
}
state.attribute.is_connected.Assign(1);
state.delta_time = std::chrono::nanoseconds(5ms).count();
state.accel = hid_state.accel;
state.gyro = hid_state.gyro;
state.rotation = hid_state.rotation;
state.orientation = hid_state.orientation;
};
if (!controller.sixaxis_sensor_enabled) {
state = default_motion_state;
return;
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::Fullkey:
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::Handheld:
set_motion_state(sixaxis_handheld_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
set_motion_state(sixaxis_dual_left_state, motion_state[0]);
set_motion_state(sixaxis_dual_right_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::Pokeball:
using namespace std::literals::chrono_literals;
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
break;
default:
break;
}
if (!Settings::values.motion_enabled.GetValue()) {
state = default_motion_state;
return;
sixaxis_fullkey_state.sampling_number =
sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_state.sampling_number =
sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_state.sampling_number =
sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_state.sampling_number =
sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo_state.sampling_number =
sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo_state.sampling_number =
sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
// This buffer only is updated on handheld on HW
sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
// Handheld doesn't update this buffer on HW
sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state);
}
state.attribute.is_connected.Assign(1);
state.delta_time = std::chrono::nanoseconds(5ms).count();
state.accel = hid_state.accel;
state.gyro = hid_state.gyro;
state.rotation = hid_state.rotation;
state.orientation = hid_state.orientation;
};
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::Fullkey:
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::Handheld:
set_motion_state(sixaxis_handheld_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
set_motion_state(sixaxis_dual_left_state, motion_state[0]);
set_motion_state(sixaxis_dual_right_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::Pokeball:
using namespace std::literals::chrono_literals;
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
break;
default:
break;
sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state);
sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state);
sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state);
sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state);
}
sixaxis_fullkey_state.sampling_number =
sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_state.sampling_number =
sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_state.sampling_number =
sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_state.sampling_number =
sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo_state.sampling_number =
sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo_state.sampling_number =
sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
// This buffer only is updated on handheld on HW
sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
// Handheld doesn't update this buffer on HW
sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state);
}
sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state);
sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state);
sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state);
sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state);
}
}
@@ -63,7 +63,7 @@ Result TouchResource::ActivateTouch(u64 aruid) {
auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
TouchAruidData& touch_data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) {
if (applet_data == nullptr || !applet_data->flag.is_assigned) {
touch_data = {};
continue;
}
@@ -124,7 +124,7 @@ Result TouchResource::ActivateGesture(u64 aruid, u32 basic_gesture_id) {
auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
TouchAruidData& touch_data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) {
if (applet_data == nullptr || !applet_data->flag.is_assigned) {
touch_data = {};
continue;
}
@@ -324,7 +324,7 @@ Result TouchResource::SetTouchScreenConfiguration(
const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
TouchAruidData& data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) {
if (applet_data == nullptr || !applet_data->flag.is_assigned) {
continue;
}
if (aruid != data.aruid) {
@@ -344,7 +344,7 @@ Result TouchResource::GetTouchScreenConfiguration(
const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
const TouchAruidData& data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) {
if (applet_data == nullptr || !applet_data->flag.is_assigned) {
continue;
}
if (aruid != data.aruid) {
@@ -17,10 +17,6 @@ void UniquePad::OnInit() {}
void UniquePad::OnRelease() {}
void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!smart_update) {
return;
}
const u64 aruid = applet_resource->GetActiveAruid();
auto* data = applet_resource->GetAruidData(aruid);
@@ -20,8 +20,5 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
bool smart_update{};
};
} // namespace Service::HID
+1 -1
View File
@@ -10,7 +10,7 @@ namespace Host1x {
Host1x::Host1x(Core::System& system_)
: system{system_}, syncpoint_manager{},
memory_manager(system.DeviceMemory()), gmmu_manager{system, memory_manager, 32, 12},
memory_manager(system.DeviceMemory()), gmmu_manager{system, memory_manager, 32, 0, 12},
allocator{std::make_unique<Common::FlatAllocator<u32, 0, 32>>(1 << 12)} {}
Host1x::~Host1x() = default;
+9 -8
View File
@@ -22,11 +22,12 @@ using Tegra::Memory::GuestMemoryFlags;
std::atomic<size_t> MemoryManager::unique_identifier_generator{};
MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_,
u64 address_space_bits_, u64 big_page_bits_, u64 page_bits_)
u64 address_space_bits_, GPUVAddr split_address_, u64 big_page_bits_,
u64 page_bits_)
: system{system_}, memory{memory_}, address_space_bits{address_space_bits_},
page_bits{page_bits_}, big_page_bits{big_page_bits_}, entries{}, big_entries{},
page_table{address_space_bits, address_space_bits + page_bits - 38,
page_bits != big_page_bits ? page_bits : 0},
split_address{split_address_}, page_bits{page_bits_}, big_page_bits{big_page_bits_},
entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38,
page_bits != big_page_bits ? page_bits : 0},
kind_map{PTEKind::INVALID}, unique_identifier{unique_identifier_generator.fetch_add(
1, std::memory_order_acq_rel)},
accumulator{std::make_unique<VideoCommon::InvalidationAccumulator>()} {
@@ -48,10 +49,10 @@ MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager&
entries.resize(page_table_size / 32, 0);
}
MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 big_page_bits_,
u64 page_bits_)
: MemoryManager(system_, system_.Host1x().MemoryManager(), address_space_bits_, big_page_bits_,
page_bits_) {}
MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_,
GPUVAddr split_address_, u64 big_page_bits_, u64 page_bits_)
: MemoryManager(system_, system_.Host1x().MemoryManager(), address_space_bits_, split_address_,
big_page_bits_, page_bits_) {}
MemoryManager::~MemoryManager() = default;
+5 -3
View File
@@ -36,10 +36,11 @@ namespace Tegra {
class MemoryManager final {
public:
explicit MemoryManager(Core::System& system_, u64 address_space_bits_ = 40,
u64 big_page_bits_ = 16, u64 page_bits_ = 12);
explicit MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_,
u64 address_space_bits_ = 40, u64 big_page_bits_ = 16,
GPUVAddr split_address = 1ULL << 34, u64 big_page_bits_ = 16,
u64 page_bits_ = 12);
explicit MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_,
u64 address_space_bits_ = 40, GPUVAddr split_address = 1ULL << 34,
u64 big_page_bits_ = 16, u64 page_bits_ = 12);
~MemoryManager();
size_t GetID() const {
@@ -192,6 +193,7 @@ private:
MaxwellDeviceMemoryManager& memory;
const u64 address_space_bits;
GPUVAddr split_address;
const u64 page_bits;
u64 address_space_size;
u64 page_size;
@@ -73,8 +73,11 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
ui->tabWidget->addTab(graphics_advanced_tab.get(), tr("Adv. Graphics"));
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles"));
// Only show Linux tab on Unix
linux_tab->setVisible(false);
#ifdef __unix__
linux_tab->setVisible(true);
ui->tabWidget->addTab(linux_tab.get(), tr("Linux"));
#endif
+7
View File
@@ -1353,6 +1353,13 @@ void GMainWindow::InitializeHotkeys() {
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);
LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true);
LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true);
LinkActionShortcut(ui->action_View_Lobby,
QStringLiteral("Multiplayer Browse Public Game Lobby"));
LinkActionShortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room"));
LinkActionShortcut(ui->action_Connect_To_Room,
QStringLiteral("Multiplayer Direct Connect to Room"));
LinkActionShortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room"));
LinkActionShortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room"));
static const QString main_window = QStringLiteral("Main Window");
const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
+12 -1
View File
@@ -77,16 +77,23 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
// UI Buttons
connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom);
// Actions
connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this,
&Lobby::OnRefreshLobby);
// Load persistent filters after events are connected to make sure they apply
ui->search->setText(
QString::fromStdString(UISettings::values.multiplayer_filter_text.GetValue()));
ui->games_owned->setChecked(UISettings::values.multiplayer_filter_games_owned.GetValue());
ui->hide_empty->setChecked(UISettings::values.multiplayer_filter_hide_empty.GetValue());
ui->hide_full->setChecked(UISettings::values.multiplayer_filter_hide_full.GetValue());
}
Lobby::~Lobby() = default;
@@ -204,6 +211,10 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
// Save settings
UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString();
UISettings::values.multiplayer_filter_text = ui->search->text().toStdString();
UISettings::values.multiplayer_filter_games_owned = ui->games_owned->isChecked();
UISettings::values.multiplayer_filter_hide_empty = ui->hide_empty->isChecked();
UISettings::values.multiplayer_filter_hide_full = ui->hide_full->isChecked();
UISettings::values.multiplayer_ip =
proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString();
UISettings::values.multiplayer_port =
+21 -4
View File
@@ -193,12 +193,29 @@ public:
}
QVariant data(int role) const override {
if (role != Qt::DisplayRole) {
switch (role) {
case Qt::DisplayRole: {
auto members = data(MemberListRole).toList();
return QStringLiteral("%1 / %2 ")
.arg(QString::number(members.size()), data(MaxPlayerRole).toString());
}
case Qt::ForegroundRole: {
auto members = data(MemberListRole).toList();
auto max_players = data(MaxPlayerRole).toInt();
if (members.size() >= max_players) {
return QBrush(QColor(255, 48, 32));
} else if (members.size() == (max_players - 1)) {
return QBrush(QColor(255, 140, 32));
} else if (members.size() == 0) {
return QBrush(QColor(128, 128, 128));
}
// FIXME: How to return a value that tells Qt not to modify the
// text color from the default (as if Qt::ForegroundRole wasn't overridden)?
return QBrush(nullptr);
}
default:
return LobbyItem::data(role);
}
auto members = data(MemberListRole).toList();
return QStringLiteral("%1 / %2 ")
.arg(QString::number(members.size()), data(MaxPlayerRole).toString());
}
bool operator<(const QStandardItem& other) const override {
+13 -1
View File
@@ -169,6 +169,13 @@ struct Values {
// multiplayer settings
Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer};
Setting<std::string> multiplayer_filter_text{linkage, {}, "filter_text", Category::Multiplayer};
Setting<bool> multiplayer_filter_games_owned{linkage, false, "filter_games_owned",
Category::Multiplayer};
Setting<bool> multiplayer_filter_hide_empty{linkage, false, "filter_games_hide_empty",
Category::Multiplayer};
Setting<bool> multiplayer_filter_hide_full{linkage, false, "filter_games_hide_full",
Category::Multiplayer};
Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer};
Setting<u16, true> multiplayer_port{linkage, 24872, 0,
UINT16_MAX, "port", Category::Multiplayer};
@@ -222,7 +229,7 @@ void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig);
// This must be in alphabetical order according to action name as it must have the same order as
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<Shortcut, 23> default_hotkeys{{
const std::array<Shortcut, 28> default_hotkeys{{
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
@@ -236,6 +243,11 @@ const std::array<Shortcut, 23> default_hotkeys{{
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Browse Public Game Lobby")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+B"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Create Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+N"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Direct Connect to Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+C"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Leave Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+L"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Show Current Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+R"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}},