Compare commits

...

73 Commits

Author SHA1 Message Date
Narr the Reg bc8699a9fa service: hid: Fix gesture regression 2022-06-10 13:14:31 -05:00
bunnei 073714a762 Merge pull request #8428 from bunnei/nvflinger-fix-timing
Follow-up fixes for NVFlinger rewrite (Part 3)
2022-06-08 11:20:05 -07:00
bunnei 4ae75bec50 Merge pull request #8436 from liamwhite/asio-usage
core/debugger: fix asio write usage
2022-06-07 14:16:47 -07:00
Mai M 31527ccd25 Merge pull request #8435 from liamwhite/lambda-capture
core/debugger: fix crash due to incorrect lambda capture
2022-06-06 23:58:34 -04:00
Liam 268878f895 core/debugger: fix asio write usage 2022-06-06 23:50:56 -04:00
Liam d00b7be2d6 core/debugger: fix crash due to incorrect lambda capture 2022-06-06 23:39:48 -04:00
bunnei 708e5b027f Merge pull request #8367 from Docteh/say_win11
Logging: Report Post Windows 10 2004 versions, like Windows 11
2022-06-05 18:44:48 -07:00
bunnei c33c9c76bf Merge pull request #8426 from liamwhite/elf
common: consolidate ELF structure definitions
2022-06-05 16:52:06 -07:00
bunnei 888e814130 hle: service: nvflinger: buffer_queue_consumer: Always free released buffers. 2022-06-05 16:06:06 -07:00
Mai M cad53179ed Merge pull request #8419 from liamwhite/library-list
gdbstub: add missing library list query
2022-06-05 18:23:29 -04:00
Liam 3c313a43fd common: consolidate ELF structure definitions 2022-06-05 09:42:05 -04:00
bunnei 45bdbf538c Merge pull request #8395 from german77/ir_stub
service: hid: Improve stub of IRS
2022-06-04 01:26:08 -07:00
Liam 2f2e443858 gdbstub: add missing library list command 2022-06-03 20:42:13 -04:00
Fernando S 14db101148 Merge pull request #8414 from bylaws/master
Maxwell3D: Fix 3D semaphore counter type 0 handling
2022-06-03 18:02:10 +02:00
Billy Laws ea89cf8639 Maxwell3D: Fix 3D semaphore counter type 0 handling
Counter type 0 actually releases the semaphore payload rather than a constant zero as was previously thought. This is required by Skyrim.
2022-06-02 21:46:38 +01:00
Mai M 5c0a31e29f Merge pull request #8410 from liamwhite/thread-names
gdbstub: Support reading guest thread names
2022-06-02 16:34:41 -04:00
Liam 07922abffc core/debugger: Support reading guest thread names 2022-06-01 21:25:32 -04:00
Mai M 114a4562ed Merge pull request #8409 from liamwhite/tdesc-fix
gdbstub: fix target descriptions
2022-06-01 21:16:33 -04:00
Morph 858f8ac6d9 Merge pull request #8402 from liamwhite/better-step
core/debugger: Improved stepping mechanism and misc fixes
2022-06-01 20:46:10 -04:00
Liam b71130e6f1 gdbstub: fix target descriptions 2022-06-01 20:31:24 -04:00
bunnei af418eb666 Merge pull request #8400 from Docteh/fullscreen_glitch
fix UI opening fullscreen after certain crashes
2022-06-01 10:26:24 -07:00
liamwhite a2f6a2480d Merge pull request #8404 from Morph1984/virtual
core/debugger: Define defaulted virtual destructors
2022-06-01 12:30:47 -04:00
liamwhite 503feba7e4 Merge pull request #8403 from Morph1984/cast
gdbstub: Explicitly cast return type to u8
2022-06-01 12:30:32 -04:00
Morph 69511aed3d core/debugger: Define defaulted virtual destructors
Resolves an MSVC warning where a virtual destructor is not defined in the base class with virtual functions.
2022-06-01 02:28:34 -04:00
Liam 989d4a7a41 core/debugger: Improved stepping mechanism and misc fixes 2022-06-01 02:15:15 -04:00
Morph a32f6e9d8e gdbstub: Explicitly cast return type to u8
Otherwise, the addition promotes the returned value to an int instead of keeping it as a u8.
2022-06-01 01:40:18 -04:00
Kyle Kienapfel 36df3ce97e fix UI opening fullscreen after certain crashes
Sometimes when yuzu crashes, it restarts with the games list in fullscreen,
which would be fine, except there isn't an easy way to exit this.
It also doesn't occur often enough for qt-config.ini files to be in good supply.

UILayout\geometry value in qt-config.ini is the culprit,
 at least for the one provided.

Proposed fix is to simply check isFullScreen when yuzu is starting up,
and take it out of full screen immediately
2022-05-31 21:24:31 -07:00
Mai M de2f2e5140 Merge pull request #8394 from liamwhite/debugger
core/debugger: Implement new GDB stub debugger
2022-06-01 00:19:49 -04:00
Liam fb4b3c127f core/debugger: Implement new GDB stub debugger 2022-06-01 00:01:25 -04:00
Mai M 72b34650f9 Merge pull request #8401 from zhaobot/tx-update-20220601034505
Update translations (2022-06-01)
2022-05-31 23:53:39 -04:00
The yuzu Community 105c60b984 Update translations (2022-06-01) 2022-06-01 03:45:35 +00:00
Narr the Reg e609bc1c6a service: hid: Improve stub of IRS 2022-05-31 10:26:13 -05:00
bunnei f6c47df671 Merge pull request #8368 from german77/seventimes
Service: hid: Several improvements and implementations
2022-05-30 10:44:36 -07:00
Mai M be351c2b3d Merge pull request #8392 from Morph1984/boosted
CMakeLists: Update boost to 1.79.0
2022-05-30 00:35:25 -04:00
Morph f3d3528754 CMakeLists: Update boost to 1.79.0
This version of boost brings in a number of bug fixes, especially to the asio library. Details can be seen here: https://www.boost.org/users/history/version_1_79_0.html
2022-05-29 21:00:40 -04:00
bunnei ce961cb4c6 Merge pull request #8348 from JakobDev/metainfocontrol
Add control to metainfo
2022-05-29 14:16:27 -07:00
bunnei 1c8b509441 Merge pull request #8332 from Morph1984/reduce_exec_size
general: Use smaller array types where applicable
2022-05-29 02:33:24 -07:00
bunnei cc289a8ed0 Merge pull request #8339 from Docteh/about_icon
about dialog: Fix yuzu icon
2022-05-29 02:32:40 -07:00
Kyle K 017a18f42e Logging: Report Post Windows 10 2004 versions, like Windows 11
Qt5 and Qt6 don't really do a good job of reporting Windows versions past the 2004 version.

Current: Windows 10 Version 2009
This Patch: Windows 10 Version 21H1 (Build 19043.1706)
Also: Windows 11 Version 21H2 (Build 22000.675)

Fixes: #8362
2022-05-28 20:48:19 -07:00
Mai M 8a858c2623 Merge pull request #8385 from lat9nq/just-subsys-win
yuzu-qt: Call -Wl,--subsystem,windows directly
2022-05-28 14:30:15 -04:00
lat9nq 6e12eb80a0 yuzu-qt: Call -Wl,--subsystem,windows directly
-mwindows doesn't work with Clang. tpoechtrager/wclang resolves this by
just using MinGW-GCC to link the executable, however this prevents us
from using LLVM-exclusive tools when building yuzu.

Solution is to send the linker argument we need from -mwindows directly
to the linker.
From https://gcc-help.gcc.gnu.narkive.com/FogklN5J/gcc-wl-subsystem-windows-mwindows-options
2022-05-28 04:07:46 -04:00
bunnei d879741ec6 Merge pull request #8374 from german77/asnycvibrations
input_common: Make vibration request async
2022-05-28 00:55:53 -07:00
bunnei 439e621674 Merge pull request #8372 from german77/touch
input_common: touch: Rewrite touch driver to support multiple touch points
2022-05-27 12:16:03 -07:00
german77 a1f2610522 service: hid: Implement ResetIsSixAxisSensorDeviceNewlyAssigned
Needed by Nintendo Switch Sports
2022-05-27 10:22:37 -05:00
german77 240f59a4c8 service: hid: Implement LoadSixAxisSensorCalibrationParameter and GetSixAxisSensorIcInformation
Needed by Nintendo Switch Sports
2022-05-27 10:22:36 -05:00
german77 c889a5805e service: hid: Implement EnableSixAxisSensorUnalteredPassthrough and IsSixAxisSensorUnalteredPassthroughEnabled
Needed by Nintendo Switch Sports
2022-05-27 10:21:10 -05:00
german77 762a30d0db service: hid: Add error handling to sixaxis functions 2022-05-27 10:21:10 -05:00
german77 390d49c5f1 service: hid: Refractor sixaxis functions 2022-05-27 10:21:10 -05:00
german77 3cf15af31e service: hid: Implement MergeSingleJoyAsDualJoy according to RE 2022-05-27 10:21:09 -05:00
german77 7aa1d10655 service: hid: Add error handling to setNpadAssignment and variants 2022-05-27 10:21:09 -05:00
german77 74d1b9a254 service: hid: Quick RE fixes and comments 2022-05-27 10:21:08 -05:00
Mai M b01541f4e4 Merge pull request #8381 from lat9nq/path-util-pointer-bool
path_util: Resolve `-Wpointer-bool-conversion` warning
2022-05-26 19:20:47 -04:00
lat9nq 0e5a6676c3 path_util: Resolve -Wpointer-bool-conversion warning
Clang (rightfully) warns that we are checking for the existence of
pointer to something just allocated on the stack, which is always true.

Instead, check whether GetModuleFileNameW failed.

Co-authored-by: Mai M <mathew1800@gmail.com>
2022-05-26 19:01:26 -04:00
bunnei a9beb4746e Merge pull request #8379 from lat9nq/amd-push-desc-workaround
vulkan_device: Block AMDVLK's VK_KHR_push_descriptor
2022-05-25 13:07:39 -07:00
bunnei 79352ee9d5 Merge pull request #8369 from lat9nq/amd-wmel-workaround
vulkan_device: Workaround extension bug
2022-05-25 13:07:28 -07:00
lat9nq 3a8c2ae328 vulkan_device: Workaround extension bug
A bug occurs in yuzu when VK_KHR_workgroup_memory_explicit_layout is
available but 16-bit integers are not supported in the host driver.

Disable usage of the extension when this case arises.
2022-05-25 01:22:15 -04:00
lat9nq 7f53cd1e96 vulkan_device: Block AMDVLK's VK_KHR_push_descriptor
Recent AMD Vulkan drivers (22.5.2 or 2.0.226 for specifically Vulkan)
have a broken VK_KHR_push_descriptor implementation that causes a crash
in yuzu. Disable it for the time being.
2022-05-25 01:17:48 -04:00
bunnei 4118aad491 Merge pull request #8311 from asLody/fix-stencil-faces
vk_rasterizer: fix stencil test when two faces are disabled
2022-05-24 18:37:31 -07:00
Narr the Reg bf948b5790 input_common: Make vibration request async 2022-05-23 12:25:02 -05:00
german77 c82806f9cb input_common: touch: Rewrite touch driver to support multiple touch points 2022-05-23 11:01:14 -05:00
liamwhite 4eb7f6c044 Merge pull request #8342 from lat9nq/clang-latest-stdc++
general: Use Common::U16StringFromBuffer in place of QString::toStdU16String
2022-05-20 23:35:10 -04:00
Mai M b57df1dcb9 Merge pull request #8351 from abouvier/patch-2
video_core: Support new VkResult
2022-05-17 14:10:00 -04:00
JakobDev 775e29efb6 Add control to Metainfo 2022-05-17 08:40:06 +02:00
lat9nq 5035df27c3 qt_software_keyboard: Address review feedback
Use auto and a more descriptive variable name.

Secondly, fix some C++ misconceptions or constructing too many objects.

Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com>
Co-authored-by: Lioncash <mathew1800@gmail.com>
2022-05-16 14:44:01 -04:00
Kyle K 8f3098fc1e about dialog: Fix the logo in a multiplatform way
The Icon was renamed in #8283 for Linux builds, and the fix proposed in #8312 would in turn break
the icon for Windows users.

I've decided to fix the aboutdialog.ui file via qtcreator.

I'm not sure its important to have the yuzu icon inside the About dialog grabbed from the local Qt theme,
but I've reword how the code works for that, and we can just delete those lines.

I've also thrown the yuzu.png through pngcrush to remove this warning
libpng warning: iCCP: known incorrect sRGB profile

Credit to abouvier for bringing bug up.
2022-05-16 05:42:38 -07:00
lat9nq 3ca3254b9f main: Use Common::U16StringFromBuffer
See ffd3afcf2
2022-05-15 18:09:19 -04:00
lat9nq f7908eebb9 qt_software_keyboard: Use Common::U16StringFromBuffer
See ffd3afcf2
2022-05-15 18:08:08 -04:00
lat9nq ffd3afcf2f string_util: Add U16StringFromBuffer
Qt's QString::toStdU16String doesn't work when compiling against the
latest libstdc++, at least when using Clang. This function effectively
does the same thing as the aforementioned one.
2022-05-15 18:06:33 -04:00
Morph 36d44bf52f time_zone_manager: Use s8 for month length tables
Using this smaller type saves 512 bytes in the compiled executable.
2022-05-13 00:51:11 -04:00
Morph 370d3fa1e3 video_core/surface: Use u8 for PixelFormat block tables
Using this smaller type saves 33280 bytes in the compiled executable.
2022-05-13 00:51:11 -04:00
Morph b3f8c7ce58 codecs/vp9: Use u8 for norm and map luts
Using this smaller type saves 1536 bytes in the compiled executable.
2022-05-13 00:51:05 -04:00
Morph eea68eae18 command_generator: Use u8 for tap index lut
Using this smaller type saves 1024 bytes in the compiled executable.
2022-05-13 00:50:58 -04:00
Lody 535bc61b4c vk_rasterizer: fix stencil test when two faces are disabled 2022-05-06 14:47:55 +08:00
99 changed files with 8469 additions and 5769 deletions
+7 -2
View File
@@ -218,8 +218,13 @@ if (Boost_FOUND)
list(APPEND Boost_LIBRARIES Boost::context)
endif()
else()
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
list(APPEND CONAN_REQUIRED_LIBS "boost/1.78.0")
message(STATUS "Boost 1.79.0 or newer not found, falling back to Conan")
list(APPEND CONAN_REQUIRED_LIBS "boost/1.79.0")
endif()
# boost:asio has functions that require AcceptEx et al
if (MINGW)
find_library(MSWSOCK_LIBRARY mswsock REQUIRED)
endif()
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
+195 -200
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+207 -209
View File
File diff suppressed because it is too large Load Diff
+260 -265
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+204 -209
View File
File diff suppressed because it is too large Load Diff
+347 -346
View File
File diff suppressed because it is too large Load Diff
+197 -202
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+198 -203
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+209 -211
View File
File diff suppressed because it is too large Load Diff
+206 -211
View File
File diff suppressed because it is too large Load Diff
+204 -209
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+195 -200
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -32,6 +32,11 @@
<binary>yuzu</binary>
<binary>yuzu-cmd</binary>
</provides>
<supports>
<control>pointing</control>
<control>keyboard</control>
<control>gamepad</control>
</supports>
<requires>
<memory>8192</memory>
</requires>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

+8 -8
View File
@@ -129,17 +129,17 @@ s32 ToS32(float sample) {
return static_cast<s32>(rescaled_sample);
}
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
constexpr std::array<u8, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
constexpr std::array<u8, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
constexpr std::array<u8, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
constexpr std::array<u8, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
template <std::size_t CHANNEL_COUNT>
void ApplyReverbGeneric(
+1
View File
@@ -58,6 +58,7 @@ add_library(common STATIC
div_ceil.h
dynamic_library.cpp
dynamic_library.h
elf.h
error.cpp
error.h
expected.h
+333
View File
@@ -0,0 +1,333 @@
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <cstddef>
#include "common_types.h"
namespace Common {
namespace ELF {
/* Type for a 16-bit quantity. */
using Elf32_Half = u16;
using Elf64_Half = u16;
/* Types for signed and unsigned 32-bit quantities. */
using Elf32_Word = u32;
using Elf32_Sword = s32;
using Elf64_Word = u32;
using Elf64_Sword = s32;
/* Types for signed and unsigned 64-bit quantities. */
using Elf32_Xword = u64;
using Elf32_Sxword = s64;
using Elf64_Xword = u64;
using Elf64_Sxword = s64;
/* Type of addresses. */
using Elf32_Addr = u32;
using Elf64_Addr = u64;
/* Type of file offsets. */
using Elf32_Off = u32;
using Elf64_Off = u64;
/* Type for section indices, which are 16-bit quantities. */
using Elf32_Section = u16;
using Elf64_Section = u16;
/* Type for version symbol information. */
using Elf32_Versym = Elf32_Half;
using Elf64_Versym = Elf64_Half;
constexpr size_t ElfIdentSize = 16;
/* The ELF file header. This appears at the start of every ELF file. */
struct Elf32_Ehdr {
std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
};
struct Elf64_Ehdr {
std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
Elf64_Half e_type; /* Object file type */
Elf64_Half e_machine; /* Architecture */
Elf64_Word e_version; /* Object file version */
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags; /* Processor-specific flags */
Elf64_Half e_ehsize; /* ELF header size in bytes */
Elf64_Half e_phentsize; /* Program header table entry size */
Elf64_Half e_phnum; /* Program header table entry count */
Elf64_Half e_shentsize; /* Section header table entry size */
Elf64_Half e_shnum; /* Section header table entry count */
Elf64_Half e_shstrndx; /* Section header string table index */
};
constexpr u8 ElfClass32 = 1; /* 32-bit objects */
constexpr u8 ElfClass64 = 2; /* 64-bit objects */
constexpr u8 ElfData2Lsb = 1; /* 2's complement, little endian */
constexpr u8 ElfVersionCurrent = 1; /* EV_CURRENT */
constexpr u8 ElfOsAbiNone = 0; /* System V ABI */
constexpr u16 ElfTypeNone = 0; /* No file type */
constexpr u16 ElfTypeRel = 0; /* Relocatable file */
constexpr u16 ElfTypeExec = 0; /* Executable file */
constexpr u16 ElfTypeDyn = 0; /* Shared object file */
constexpr u16 ElfMachineArm = 40; /* ARM */
constexpr u16 ElfMachineAArch64 = 183; /* ARM AARCH64 */
constexpr std::array<u8, ElfIdentSize> Elf32Ident{
0x7f, 'E', 'L', 'F', ElfClass32, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
constexpr std::array<u8, ElfIdentSize> Elf64Ident{
0x7f, 'E', 'L', 'F', ElfClass64, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
/* Section header. */
struct Elf32_Shdr {
Elf32_Word sh_name; /* Section name (string tbl index) */
Elf32_Word sh_type; /* Section type */
Elf32_Word sh_flags; /* Section flags */
Elf32_Addr sh_addr; /* Section virtual addr at execution */
Elf32_Off sh_offset; /* Section file offset */
Elf32_Word sh_size; /* Section size in bytes */
Elf32_Word sh_link; /* Link to another section */
Elf32_Word sh_info; /* Additional section information */
Elf32_Word sh_addralign; /* Section alignment */
Elf32_Word sh_entsize; /* Entry size if section holds table */
};
struct Elf64_Shdr {
Elf64_Word sh_name; /* Section name (string tbl index) */
Elf64_Word sh_type; /* Section type */
Elf64_Xword sh_flags; /* Section flags */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Section size in bytes */
Elf64_Word sh_link; /* Link to another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
};
constexpr u32 ElfShnUndef = 0; /* Undefined section */
constexpr u32 ElfShtNull = 0; /* Section header table entry unused */
constexpr u32 ElfShtProgBits = 1; /* Program data */
constexpr u32 ElfShtSymtab = 2; /* Symbol table */
constexpr u32 ElfShtStrtab = 3; /* String table */
constexpr u32 ElfShtRela = 4; /* Relocation entries with addends */
constexpr u32 ElfShtDynamic = 6; /* Dynamic linking information */
constexpr u32 ElfShtNobits = 7; /* Program space with no data (bss) */
constexpr u32 ElfShtRel = 9; /* Relocation entries, no addends */
constexpr u32 ElfShtDynsym = 11; /* Dynamic linker symbol table */
/* Symbol table entry. */
struct Elf32_Sym {
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
u8 st_info; /* Symbol type and binding */
u8 st_other; /* Symbol visibility */
Elf32_Section st_shndx; /* Section index */
};
struct Elf64_Sym {
Elf64_Word st_name; /* Symbol name (string tbl index) */
u8 st_info; /* Symbol type and binding */
u8 st_other; /* Symbol visibility */
Elf64_Section st_shndx; /* Section index */
Elf64_Addr st_value; /* Symbol value */
Elf64_Xword st_size; /* Symbol size */
};
/* How to extract and insert information held in the st_info field. */
static inline u8 ElfStBind(u8 st_info) {
return st_info >> 4;
}
static inline u8 ElfStType(u8 st_info) {
return st_info & 0xf;
}
static inline u8 ElfStInfo(u8 st_bind, u8 st_type) {
return static_cast<u8>((st_bind << 4) + (st_type & 0xf));
}
constexpr u8 ElfBindLocal = 0; /* Local symbol */
constexpr u8 ElfBindGlobal = 1; /* Global symbol */
constexpr u8 ElfBindWeak = 2; /* Weak symbol */
constexpr u8 ElfTypeUnspec = 0; /* Symbol type is unspecified */
constexpr u8 ElfTypeObject = 1; /* Symbol is a data object */
constexpr u8 ElfTypeFunc = 2; /* Symbol is a code object */
static inline u8 ElfStVisibility(u8 st_other) {
return static_cast<u8>(st_other & 0x3);
}
constexpr u8 ElfVisibilityDefault = 0; /* Default symbol visibility rules */
constexpr u8 ElfVisibilityInternal = 1; /* Processor specific hidden class */
constexpr u8 ElfVisibilityHidden = 2; /* Sym unavailable in other modules */
constexpr u8 ElfVisibilityProtected = 3; /* Not preemptible, not exported */
/* Relocation table entry without addend (in section of type ShtRel). */
struct Elf32_Rel {
Elf32_Addr r_offset; /* Address */
Elf32_Word r_info; /* Relocation type and symbol index */
};
/* Relocation table entry with addend (in section of type ShtRela). */
struct Elf32_Rela {
Elf32_Addr r_offset; /* Address */
Elf32_Word r_info; /* Relocation type and symbol index */
Elf32_Sword r_addend; /* Addend */
};
struct Elf64_Rela {
Elf64_Addr r_offset; /* Address */
Elf64_Xword r_info; /* Relocation type and symbol index */
Elf64_Sxword r_addend; /* Addend */
};
/* How to extract and insert information held in the r_info field. */
static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
return r_info >> 8;
}
static inline u8 Elf32RelType(Elf32_Word r_info) {
return static_cast<u8>(r_info & 0xff);
}
static inline Elf32_Word Elf32RelInfo(u32 sym_index, u8 type) {
return (sym_index << 8) + type;
}
static inline u32 Elf64RelSymIndex(Elf64_Xword r_info) {
return static_cast<u32>(r_info >> 32);
}
static inline u32 Elf64RelType(Elf64_Xword r_info) {
return r_info & 0xffffffff;
}
static inline Elf64_Xword Elf64RelInfo(u32 sym_index, u32 type) {
return (static_cast<Elf64_Xword>(sym_index) << 32) + type;
}
constexpr u32 ElfArmCopy = 20; /* Copy symbol at runtime */
constexpr u32 ElfArmGlobDat = 21; /* Create GOT entry */
constexpr u32 ElfArmJumpSlot = 22; /* Create PLT entry */
constexpr u32 ElfArmRelative = 23; /* Adjust by program base */
constexpr u32 ElfAArch64Copy = 1024; /* Copy symbol at runtime */
constexpr u32 ElfAArch64GlobDat = 1025; /* Create GOT entry */
constexpr u32 ElfAArch64JumpSlot = 1026; /* Create PLT entry */
constexpr u32 ElfAArch64Relative = 1027; /* Adjust by program base */
/* Program segment header. */
struct Elf32_Phdr {
Elf32_Word p_type; /* Segment type */
Elf32_Off p_offset; /* Segment file offset */
Elf32_Addr p_vaddr; /* Segment virtual address */
Elf32_Addr p_paddr; /* Segment physical address */
Elf32_Word p_filesz; /* Segment size in file */
Elf32_Word p_memsz; /* Segment size in memory */
Elf32_Word p_flags; /* Segment flags */
Elf32_Word p_align; /* Segment alignment */
};
struct Elf64_Phdr {
Elf64_Word p_type; /* Segment type */
Elf64_Word p_flags; /* Segment flags */
Elf64_Off p_offset; /* Segment file offset */
Elf64_Addr p_vaddr; /* Segment virtual address */
Elf64_Addr p_paddr; /* Segment physical address */
Elf64_Xword p_filesz; /* Segment size in file */
Elf64_Xword p_memsz; /* Segment size in memory */
Elf64_Xword p_align; /* Segment alignment */
};
/* Legal values for p_type (segment type). */
constexpr u32 ElfPtNull = 0; /* Program header table entry unused */
constexpr u32 ElfPtLoad = 1; /* Loadable program segment */
constexpr u32 ElfPtDynamic = 2; /* Dynamic linking information */
constexpr u32 ElfPtInterp = 3; /* Program interpreter */
constexpr u32 ElfPtNote = 4; /* Auxiliary information */
constexpr u32 ElfPtPhdr = 6; /* Entry for header table itself */
constexpr u32 ElfPtTls = 7; /* Thread-local storage segment */
/* Legal values for p_flags (segment flags). */
constexpr u32 ElfPfExec = 0; /* Segment is executable */
constexpr u32 ElfPfWrite = 1; /* Segment is writable */
constexpr u32 ElfPfRead = 2; /* Segment is readable */
/* Dynamic section entry. */
struct Elf32_Dyn {
Elf32_Sword d_tag; /* Dynamic entry type */
union {
Elf32_Word d_val; /* Integer value */
Elf32_Addr d_ptr; /* Address value */
} d_un;
};
struct Elf64_Dyn {
Elf64_Sxword d_tag; /* Dynamic entry type */
union {
Elf64_Xword d_val; /* Integer value */
Elf64_Addr d_ptr; /* Address value */
} d_un;
};
/* Legal values for d_tag (dynamic entry type). */
constexpr u32 ElfDtNull = 0; /* Marks end of dynamic section */
constexpr u32 ElfDtNeeded = 1; /* Name of needed library */
constexpr u32 ElfDtPltRelSz = 2; /* Size in bytes of PLT relocs */
constexpr u32 ElfDtPltGot = 3; /* Processor defined value */
constexpr u32 ElfDtHash = 4; /* Address of symbol hash table */
constexpr u32 ElfDtStrtab = 5; /* Address of string table */
constexpr u32 ElfDtSymtab = 6; /* Address of symbol table */
constexpr u32 ElfDtRela = 7; /* Address of Rela relocs */
constexpr u32 ElfDtRelasz = 8; /* Total size of Rela relocs */
constexpr u32 ElfDtRelaent = 9; /* Size of one Rela reloc */
constexpr u32 ElfDtStrsz = 10; /* Size of string table */
constexpr u32 ElfDtSyment = 11; /* Size of one symbol table entry */
constexpr u32 ElfDtInit = 12; /* Address of init function */
constexpr u32 ElfDtFini = 13; /* Address of termination function */
constexpr u32 ElfDtRel = 17; /* Address of Rel relocs */
constexpr u32 ElfDtRelsz = 18; /* Total size of Rel relocs */
constexpr u32 ElfDtRelent = 19; /* Size of one Rel reloc */
constexpr u32 ElfDtPltRel = 20; /* Type of reloc in PLT */
constexpr u32 ElfDtTextRel = 22; /* Reloc might modify .text */
constexpr u32 ElfDtJmpRel = 23; /* Address of PLT relocs */
constexpr u32 ElfDtBindNow = 24; /* Process relocations of object */
constexpr u32 ElfDtInitArray = 25; /* Array with addresses of init fct */
constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */
constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
} // namespace ELF
} // namespace Common
+1 -3
View File
@@ -232,9 +232,7 @@ void SetYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
fs::path GetExeDirectory() {
wchar_t exe_path[MAX_PATH];
GetModuleFileNameW(nullptr, exe_path, MAX_PATH);
if (!exe_path) {
if (GetModuleFileNameW(nullptr, exe_path, MAX_PATH) == 0) {
LOG_ERROR(Common_Filesystem,
"Failed to get the path to the executable of the current process");
}
+1
View File
@@ -72,6 +72,7 @@ enum class PollingError {
enum class VibrationAmplificationType {
Linear,
Exponential,
Test,
};
// Analog properties for calibration
+1
View File
@@ -70,6 +70,7 @@ void LogSettings() {
log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue());
log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
+1 -1
View File
@@ -601,7 +601,7 @@ struct Values {
// Debugging
bool record_frame_times;
BasicSetting<bool> use_gdbstub{false, "use_gdbstub"};
BasicSetting<u16> gdbstub_port{0, "gdbstub_port"};
BasicSetting<u16> gdbstub_port{6543, "gdbstub_port"};
BasicSetting<std::string> program_args{std::string(), "program_args"};
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
BasicSetting<bool> dump_nso{false, "dump_nso"};
+4
View File
@@ -178,6 +178,10 @@ std::wstring UTF8ToUTF16W(const std::string& input) {
#endif
std::u16string U16StringFromBuffer(const u16* input, std::size_t length) {
return std::u16string(reinterpret_cast<const char16_t*>(input), length);
}
std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) {
std::size_t len = 0;
while (len < buffer.length() && len < max_len && buffer[len] != '\0') {
+2
View File
@@ -44,6 +44,8 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
#endif
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
/**
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
* `other` for equality.
+10
View File
@@ -36,6 +36,13 @@ add_library(core STATIC
crypto/ctr_encryption_layer.h
crypto/xts_encryption_layer.cpp
crypto/xts_encryption_layer.h
debugger/debugger_interface.h
debugger/debugger.cpp
debugger/debugger.h
debugger/gdbstub_arch.cpp
debugger/gdbstub_arch.h
debugger/gdbstub.cpp
debugger/gdbstub.h
device_memory.cpp
device_memory.h
file_sys/bis_factory.cpp
@@ -761,6 +768,9 @@ create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
if (MINGW)
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
endif()
if (ENABLE_WEB_SERVICE)
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
+46
View File
@@ -9,7 +9,9 @@
#include "core/arm/arm_interface.h"
#include "core/arm/symbols.h"
#include "core/core.h"
#include "core/debugger/debugger.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"
#include "core/loader/loader.h"
#include "core/memory.h"
@@ -88,4 +90,48 @@ void ARM_Interface::LogBacktrace() const {
}
}
void ARM_Interface::Run() {
using Kernel::StepState;
using Kernel::SuspendType;
while (true) {
Kernel::KThread* current_thread{system.Kernel().CurrentScheduler()->GetCurrentThread()};
Dynarmic::HaltReason hr{};
// Notify the debugger and go to sleep if a step was performed
// and this thread has been scheduled again.
if (current_thread->GetStepState() == StepState::StepPerformed) {
system.GetDebugger().NotifyThreadStopped(current_thread);
current_thread->RequestSuspend(SuspendType::Debug);
break;
}
// Otherwise, run the thread.
if (current_thread->GetStepState() == StepState::StepPending) {
hr = StepJit();
if (Has(hr, step_thread)) {
current_thread->SetStepState(StepState::StepPerformed);
}
} else {
hr = RunJit();
}
// Notify the debugger and go to sleep if a breakpoint was hit.
if (Has(hr, breakpoint)) {
system.GetDebugger().NotifyThreadStopped(current_thread);
current_thread->RequestSuspend(Kernel::SuspendType::Debug);
break;
}
// Handle syscalls and scheduling (this may change the current thread)
if (Has(hr, svc_call)) {
Kernel::Svc::Call(system, GetSvcNumber());
}
if (Has(hr, break_loop) || !uses_wall_clock) {
break;
}
}
}
} // namespace Core
+13 -4
View File
@@ -6,6 +6,9 @@
#include <array>
#include <vector>
#include <dynarmic/interface/halt_reason.h>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hardware_properties.h"
@@ -64,10 +67,7 @@ public:
static_assert(sizeof(ThreadContext64) == 0x320);
/// Runs the CPU until an event happens
virtual void Run() = 0;
/// Step CPU by one instruction
virtual void Step() = 0;
void Run();
/// Clear all instruction cache
virtual void ClearInstructionCache() = 0;
@@ -194,6 +194,11 @@ public:
void LogBacktrace() const;
static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step;
static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
protected:
/// System context that this ARM interface is running under.
System& system;
@@ -201,6 +206,10 @@ protected:
bool uses_wall_clock;
static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
virtual Dynarmic::HaltReason RunJit() = 0;
virtual Dynarmic::HaltReason StepJit() = 0;
virtual u32 GetSvcNumber() const = 0;
};
} // namespace Core
+17 -17
View File
@@ -17,6 +17,8 @@
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/debugger/debugger.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
@@ -24,9 +26,6 @@ namespace Core {
using namespace Common::Literals;
constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
@@ -78,16 +77,21 @@ public:
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
if (parent.system.DebuggerEnabled()) {
parent.jit.load()->Regs()[15] = pc;
parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
return;
}
parent.LogBacktrace();
LOG_CRITICAL(Core_ARM,
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
UNIMPLEMENTED();
}
void CallSVC(u32 swi) override {
parent.svc_swi = swi;
parent.jit.load()->HaltExecution(svc_call);
parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
}
void AddTicks(u64 ticks) override {
@@ -232,20 +236,16 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
return std::make_unique<Dynarmic::A32::Jit>(config);
}
void ARM_Dynarmic_32::Run() {
while (true) {
const auto hr = jit.load()->Run();
if (Has(hr, svc_call)) {
Kernel::Svc::Call(system, svc_swi);
}
if (Has(hr, break_loop) || !uses_wall_clock) {
break;
}
}
Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() {
return jit.load()->Run();
}
void ARM_Dynarmic_32::Step() {
jit.load()->Step();
Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() {
return jit.load()->Step();
}
u32 ARM_Dynarmic_32::GetSvcNumber() const {
return svc_swi;
}
ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
+5 -2
View File
@@ -41,8 +41,6 @@ public:
void SetVectorReg(int index, u128 value) override;
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
void Run() override;
void Step() override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
@@ -70,6 +68,11 @@ public:
std::vector<BacktraceEntry> GetBacktrace() const override;
protected:
Dynarmic::HaltReason RunJit() override;
Dynarmic::HaltReason StepJit() override;
u32 GetSvcNumber() const override;
private:
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
+16 -17
View File
@@ -15,6 +15,7 @@
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/debugger/debugger.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"
@@ -25,9 +26,6 @@ namespace Core {
using Vector = Dynarmic::A64::Vector;
using namespace Common::Literals;
constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
@@ -119,8 +117,13 @@ public:
case Dynarmic::A64::Exception::SendEventLocal:
case Dynarmic::A64::Exception::Yield:
return;
case Dynarmic::A64::Exception::Breakpoint:
default:
if (parent.system.DebuggerEnabled()) {
parent.jit.load()->SetPC(pc);
parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
return;
}
parent.LogBacktrace();
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
@@ -129,7 +132,7 @@ public:
void CallSVC(u32 swi) override {
parent.svc_swi = swi;
parent.jit.load()->HaltExecution(svc_call);
parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
}
void AddTicks(u64 ticks) override {
@@ -293,20 +296,16 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
return std::make_shared<Dynarmic::A64::Jit>(config);
}
void ARM_Dynarmic_64::Run() {
while (true) {
const auto hr = jit.load()->Run();
if (Has(hr, svc_call)) {
Kernel::Svc::Call(system, svc_swi);
}
if (Has(hr, break_loop) || !uses_wall_clock) {
break;
}
}
Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() {
return jit.load()->Run();
}
void ARM_Dynarmic_64::Step() {
jit.load()->Step();
Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() {
return jit.load()->Step();
}
u32 ARM_Dynarmic_64::GetSvcNumber() const {
return svc_swi;
}
ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
+5 -2
View File
@@ -39,8 +39,6 @@ public:
void SetVectorReg(int index, u128 value) override;
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
void Run() override;
void Step() override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
@@ -64,6 +62,11 @@ public:
std::vector<BacktraceEntry> GetBacktrace() const override;
protected:
Dynarmic::HaltReason RunJit() override;
Dynarmic::HaltReason StepJit() override;
u32 GetSvcNumber() const override;
private:
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
std::size_t address_space_bits) const;
+13 -72
View File
@@ -3,73 +3,14 @@
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/elf.h"
#include "core/arm/symbols.h"
#include "core/core.h"
#include "core/memory.h"
using namespace Common::ELF;
namespace Core {
namespace {
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
enum class ELFSymbolType : u8 {
None = 0,
Object = 1,
Function = 2,
Section = 3,
File = 4,
Common = 5,
TLS = 6,
};
enum class ELFSymbolBinding : u8 {
Local = 0,
Global = 1,
Weak = 2,
};
enum class ELFSymbolVisibility : u8 {
Default = 0,
Internal = 1,
Hidden = 2,
Protected = 3,
};
struct ELF64Symbol {
u32 name_index;
union {
u8 info;
BitField<0, 4, ELFSymbolType> type;
BitField<4, 4, ELFSymbolBinding> binding;
};
ELFSymbolVisibility visibility;
u16 sh_index;
u64 value;
u64 size;
};
static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
struct ELF32Symbol {
u32 name_index;
u32 value;
u32 size;
union {
u8 info;
BitField<0, 4, ELFSymbolType> type;
BitField<4, 4, ELFSymbolBinding> binding;
};
ELFSymbolVisibility visibility;
u16 sh_index;
};
static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
} // Anonymous namespace
namespace Symbols {
template <typename Word, typename ELFSymbol, typename ByteReader>
@@ -110,15 +51,15 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
const Word value = ReadWord(dynamic_index + sizeof(Word));
dynamic_index += 2 * sizeof(Word);
if (tag == ELF_DYNAMIC_TAG_NULL) {
if (tag == ElfDtNull) {
break;
}
if (tag == ELF_DYNAMIC_TAG_STRTAB) {
if (tag == ElfDtStrtab) {
string_table_offset = value;
} else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
} else if (tag == ElfDtSymtab) {
symbol_table_offset = value;
} else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
} else if (tag == ElfDtSyment) {
symbol_entry_size = value;
}
}
@@ -134,14 +75,14 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
ELFSymbol symbol{};
ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
VAddr string_offset = string_table_offset + symbol.name_index;
VAddr string_offset = string_table_offset + symbol.st_name;
std::string name;
for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
name += static_cast<char>(c);
}
symbol_index += symbol_entry_size;
out[name] = std::make_pair(symbol.value, symbol.size);
out[name] = std::make_pair(symbol.st_value, symbol.st_size);
}
return out;
@@ -152,9 +93,9 @@ Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
[&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
if (is_64) {
return GetSymbols<u64, ELF64Symbol>(ReadBytes);
return GetSymbols<u64, Elf64_Sym>(ReadBytes);
} else {
return GetSymbols<u32, ELF32Symbol>(ReadBytes);
return GetSymbols<u32, Elf32_Sym>(ReadBytes);
}
}
@@ -164,9 +105,9 @@ Symbols GetSymbols(std::span<const u8> data, bool is_64) {
}};
if (is_64) {
return GetSymbols<u64, ELF64Symbol>(ReadBytes);
return GetSymbols<u64, Elf64_Sym>(ReadBytes);
} else {
return GetSymbols<u32, ELF32Symbol>(ReadBytes);
return GetSymbols<u32, Elf32_Sym>(ReadBytes);
}
}
+25 -4
View File
@@ -17,6 +17,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/cpu_manager.h"
#include "core/debugger/debugger.h"
#include "core/device_memory.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/mode.h"
@@ -171,6 +172,10 @@ struct System::Impl {
}
}
void InitializeDebugger(System& system, u16 port) {
debugger = std::make_unique<Debugger>(system, port);
}
SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
LOG_DEBUG(Core, "initialized OK");
@@ -329,6 +334,7 @@ struct System::Impl {
gpu_core->NotifyShutdown();
}
debugger.reset();
services.reset();
service_manager.reset();
cheat_engine.reset();
@@ -436,6 +442,9 @@ struct System::Impl {
/// Network instance
Network::NetworkInstance network_instance;
/// Debugger
std::unique_ptr<Core::Debugger> debugger;
SystemResultStatus status = SystemResultStatus::Success;
std::string status_details = "";
@@ -472,10 +481,6 @@ SystemResultStatus System::Pause() {
return impl->Pause();
}
SystemResultStatus System::SingleStep() {
return SystemResultStatus::Success;
}
void System::InvalidateCpuInstructionCaches() {
impl->kernel.InvalidateAllInstructionCaches();
}
@@ -496,6 +501,10 @@ void System::UnstallCPU() {
impl->UnstallCPU();
}
void System::InitializeDebugger() {
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
}
SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
u64 program_id, std::size_t program_index) {
return impl->Load(*this, emu_window, filepath, program_id, program_index);
@@ -809,6 +818,18 @@ bool System::IsMulticore() const {
return impl->is_multicore;
}
bool System::DebuggerEnabled() const {
return Settings::values.use_gdbstub.GetValue();
}
Core::Debugger& System::GetDebugger() {
return *impl->debugger;
}
const Core::Debugger& System::GetDebugger() const {
return *impl->debugger;
}
void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
impl->execute_program_callback = std::move(callback);
}
+12 -6
View File
@@ -97,6 +97,7 @@ namespace Core {
class ARM_Interface;
class CpuManager;
class Debugger;
class DeviceMemory;
class ExclusiveMonitor;
class SpeedLimiter;
@@ -147,12 +148,6 @@ public:
*/
[[nodiscard]] SystemResultStatus Pause();
/**
* Step the CPU one instruction
* @return Result status, indicating whether or not the operation succeeded.
*/
[[nodiscard]] SystemResultStatus SingleStep();
/**
* Invalidate the CPU instruction caches
* This function should only be used by GDB Stub to support breakpoints, memory updates and
@@ -168,6 +163,11 @@ public:
std::unique_lock<std::mutex> StallCPU();
void UnstallCPU();
/**
* Initialize the debugger.
*/
void InitializeDebugger();
/**
* Load an executable application.
* @param emu_window Reference to the host-system window used for video output and keyboard
@@ -354,6 +354,9 @@ public:
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
[[nodiscard]] Core::Debugger& GetDebugger();
[[nodiscard]] const Core::Debugger& GetDebugger() const;
void SetExitLock(bool locked);
[[nodiscard]] bool GetExitLock() const;
@@ -375,6 +378,9 @@ public:
/// Tells if system is running on multicore.
[[nodiscard]] bool IsMulticore() const;
/// Tells if the system debugger is enabled.
[[nodiscard]] bool DebuggerEnabled() const;
/// Type used for the frontend to designate a callback for System to re-launch the application
/// using a specified program index.
using ExecuteProgramCallback = std::function<void(std::size_t)>;
+269
View File
@@ -0,0 +1,269 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <mutex>
#include <thread>
#include <boost/asio.hpp>
#include <boost/process/async_pipe.hpp>
#include "common/logging/log.h"
#include "common/thread.h"
#include "core/core.h"
#include "core/debugger/debugger.h"
#include "core/debugger/debugger_interface.h"
#include "core/debugger/gdbstub.h"
#include "core/hle/kernel/global_scheduler_context.h"
template <typename Readable, typename Buffer, typename Callback>
static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
static_assert(std::is_trivial_v<Buffer>);
auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
r.async_read_some(
boost_buffer, [&, c](const boost::system::error_code& error, size_t bytes_read) {
if (!error.failed()) {
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
c(received_data);
}
AsyncReceiveInto(r, buffer, c);
});
}
template <typename Readable, typename Buffer>
static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
static_assert(std::is_trivial_v<Buffer>);
auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
size_t bytes_read = r.read_some(boost_buffer);
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
return received_data;
}
namespace Core {
class DebuggerImpl : public DebuggerBackend {
public:
explicit DebuggerImpl(Core::System& system_, u16 port)
: system{system_}, signal_pipe{io_context}, client_socket{io_context} {
frontend = std::make_unique<GDBStub>(*this, system);
InitializeServer(port);
}
~DebuggerImpl() override {
ShutdownServer();
}
bool NotifyThreadStopped(Kernel::KThread* thread) {
std::scoped_lock lk{connection_lock};
if (stopped) {
// Do not notify the debugger about another event.
// It should be ignored.
return false;
}
stopped = true;
boost::asio::write(signal_pipe, boost::asio::buffer(&thread, sizeof(thread)));
return true;
}
std::span<const u8> ReadFromClient() override {
return ReceiveInto(client_socket, client_data);
}
void WriteToClient(std::span<const u8> data) override {
boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
}
void SetActiveThread(Kernel::KThread* thread) override {
active_thread = thread;
}
Kernel::KThread* GetActiveThread() override {
return active_thread;
}
private:
void InitializeServer(u16 port) {
using boost::asio::ip::tcp;
LOG_INFO(Debug_GDBStub, "Starting server on port {}...", port);
// Run the connection thread.
connection_thread = std::jthread([&, port](std::stop_token stop_token) {
try {
// Initialize the listening socket and accept a new client.
tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port};
tcp::acceptor acceptor{io_context, endpoint};
acceptor.async_accept(client_socket, [](const auto&) {});
io_context.run_one();
io_context.restart();
if (stop_token.stop_requested()) {
return;
}
ThreadLoop(stop_token);
} catch (const std::exception& ex) {
LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
}
});
}
void ShutdownServer() {
connection_thread.request_stop();
io_context.stop();
connection_thread.join();
}
void ThreadLoop(std::stop_token stop_token) {
Common::SetCurrentThreadName("yuzu:Debugger");
// Set up the client signals for new data.
AsyncReceiveInto(signal_pipe, active_thread, [&](auto d) { PipeData(d); });
AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
// Stop the emulated CPU.
AllCoreStop();
// Set the active thread.
UpdateActiveThread();
// Set up the frontend.
frontend->Connected();
// Main event loop.
while (!stop_token.stop_requested() && io_context.run()) {
}
}
void PipeData(std::span<const u8> data) {
AllCoreStop();
UpdateActiveThread();
frontend->Stopped(active_thread);
}
void ClientData(std::span<const u8> data) {
const auto actions{frontend->ClientData(data)};
for (const auto action : actions) {
switch (action) {
case DebuggerAction::Interrupt: {
{
std::scoped_lock lk{connection_lock};
stopped = true;
}
AllCoreStop();
UpdateActiveThread();
frontend->Stopped(active_thread);
break;
}
case DebuggerAction::Continue:
active_thread->SetStepState(Kernel::StepState::NotStepping);
ResumeInactiveThreads();
AllCoreResume();
break;
case DebuggerAction::StepThreadUnlocked:
active_thread->SetStepState(Kernel::StepState::StepPending);
ResumeInactiveThreads();
AllCoreResume();
break;
case DebuggerAction::StepThreadLocked:
active_thread->SetStepState(Kernel::StepState::StepPending);
SuspendInactiveThreads();
AllCoreResume();
break;
case DebuggerAction::ShutdownEmulation: {
// Suspend all threads and release any locks held
active_thread->RequestSuspend(Kernel::SuspendType::Debug);
SuspendInactiveThreads();
AllCoreResume();
// Spawn another thread that will exit after shutdown,
// to avoid a deadlock
Core::System* system_ref{&system};
std::thread t([system_ref] { system_ref->Exit(); });
t.detach();
break;
}
}
}
}
void AllCoreStop() {
if (!suspend) {
suspend = system.StallCPU();
}
}
void AllCoreResume() {
stopped = false;
system.UnstallCPU();
suspend.reset();
}
void SuspendInactiveThreads() {
for (auto* thread : ThreadList()) {
if (thread != active_thread) {
thread->RequestSuspend(Kernel::SuspendType::Debug);
}
}
}
void ResumeInactiveThreads() {
for (auto* thread : ThreadList()) {
if (thread != active_thread) {
thread->Resume(Kernel::SuspendType::Debug);
thread->SetStepState(Kernel::StepState::NotStepping);
}
}
}
void UpdateActiveThread() {
const auto& threads{ThreadList()};
if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
active_thread = threads[0];
}
active_thread->Resume(Kernel::SuspendType::Debug);
active_thread->SetStepState(Kernel::StepState::NotStepping);
}
const std::vector<Kernel::KThread*>& ThreadList() {
return system.GlobalSchedulerContext().GetThreadList();
}
private:
System& system;
std::unique_ptr<DebuggerFrontend> frontend;
std::jthread connection_thread;
std::mutex connection_lock;
boost::asio::io_context io_context;
boost::process::async_pipe signal_pipe;
boost::asio::ip::tcp::socket client_socket;
std::optional<std::unique_lock<std::mutex>> suspend;
Kernel::KThread* active_thread;
bool stopped;
std::array<u8, 4096> client_data;
};
Debugger::Debugger(Core::System& system, u16 port) {
try {
impl = std::make_unique<DebuggerImpl>(system, port);
} catch (const std::exception& ex) {
LOG_CRITICAL(Debug_GDBStub, "Failed to initialize debugger: {}", ex.what());
}
}
Debugger::~Debugger() = default;
bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
return impl && impl->NotifyThreadStopped(thread);
}
} // namespace Core
+41
View File
@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "common/common_types.h"
namespace Kernel {
class KThread;
}
namespace Core {
class System;
class DebuggerImpl;
class Debugger {
public:
/**
* Blocks and waits for a connection on localhost, port `server_port`.
* Does not create the debugger if the port is already in use.
*/
explicit Debugger(Core::System& system, u16 server_port);
~Debugger();
/**
* Notify the debugger that the given thread is stopped
* (due to a breakpoint, or due to stopping after a successful step).
*
* The debugger will asynchronously halt emulation after the notification has
* occurred. If another thread attempts to notify before emulation has stopped,
* it is ignored and this method will return false. Otherwise it will return true.
*/
bool NotifyThreadStopped(Kernel::KThread* thread);
private:
std::unique_ptr<DebuggerImpl> impl;
};
} // namespace Core
+79
View File
@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <functional>
#include <span>
#include <vector>
#include "common/common_types.h"
namespace Kernel {
class KThread;
}
namespace Core {
enum class DebuggerAction {
Interrupt, ///< Stop emulation as soon as possible.
Continue, ///< Resume emulation.
StepThreadLocked, ///< Step the currently-active thread without resuming others.
StepThreadUnlocked, ///< Step the currently-active thread and resume others.
ShutdownEmulation, ///< Shut down the emulator.
};
class DebuggerBackend {
public:
virtual ~DebuggerBackend() = default;
/**
* Can be invoked from a callback to synchronously wait for more data.
* Will return as soon as least one byte is received. Reads up to 4096 bytes.
*/
virtual std::span<const u8> ReadFromClient() = 0;
/**
* Can be invoked from a callback to write data to the client.
* Returns immediately after the data is sent.
*/
virtual void WriteToClient(std::span<const u8> data) = 0;
/**
* Gets the currently active thread when the debugger is stopped.
*/
virtual Kernel::KThread* GetActiveThread() = 0;
/**
* Sets the currently active thread when the debugger is stopped.
*/
virtual void SetActiveThread(Kernel::KThread* thread) = 0;
};
class DebuggerFrontend {
public:
explicit DebuggerFrontend(DebuggerBackend& backend_) : backend{backend_} {}
virtual ~DebuggerFrontend() = default;
/**
* Called after the client has successfully connected to the port.
*/
virtual void Connected() = 0;
/**
* Called when emulation has stopped.
*/
virtual void Stopped(Kernel::KThread* thread) = 0;
/**
* Called when new data is asynchronously received on the client socket.
* A list of actions to perform is returned.
*/
[[nodiscard]] virtual std::vector<DebuggerAction> ClientData(std::span<const u8> data) = 0;
protected:
DebuggerBackend& backend;
};
} // namespace Core
+618
View File
@@ -0,0 +1,618 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <atomic>
#include <numeric>
#include <optional>
#include <thread>
#include <boost/algorithm/string.hpp>
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/debugger/gdbstub.h"
#include "core/debugger/gdbstub_arch.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h"
#include "core/loader/loader.h"
#include "core/memory.h"
namespace Core {
constexpr char GDB_STUB_START = '$';
constexpr char GDB_STUB_END = '#';
constexpr char GDB_STUB_ACK = '+';
constexpr char GDB_STUB_NACK = '-';
constexpr char GDB_STUB_INT3 = 0x03;
constexpr int GDB_STUB_SIGTRAP = 5;
constexpr char GDB_STUB_REPLY_ERR[] = "E01";
constexpr char GDB_STUB_REPLY_OK[] = "OK";
constexpr char GDB_STUB_REPLY_EMPTY[] = "";
static u8 CalculateChecksum(std::string_view data) {
return std::accumulate(data.begin(), data.end(), u8{0},
[](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); });
}
static std::string EscapeGDB(std::string_view data) {
std::string escaped;
escaped.reserve(data.size());
for (char c : data) {
switch (c) {
case '#':
escaped += "}\x03";
break;
case '$':
escaped += "}\x04";
break;
case '*':
escaped += "}\x0a";
break;
case '}':
escaped += "}\x5d";
break;
default:
escaped += c;
break;
}
}
return escaped;
}
static std::string EscapeXML(std::string_view data) {
std::string escaped;
escaped.reserve(data.size());
for (char c : data) {
switch (c) {
case '&':
escaped += "&amp;";
break;
case '"':
escaped += "&quot;";
break;
case '<':
escaped += "&lt;";
break;
case '>':
escaped += "&gt;";
break;
default:
escaped += c;
break;
}
}
return escaped;
}
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
: DebuggerFrontend(backend_), system{system_} {
if (system.CurrentProcess()->Is64BitProcess()) {
arch = std::make_unique<GDBStubA64>();
} else {
arch = std::make_unique<GDBStubA32>();
}
}
GDBStub::~GDBStub() = default;
void GDBStub::Connected() {}
void GDBStub::Stopped(Kernel::KThread* thread) {
SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
}
std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
std::vector<DebuggerAction> actions;
current_command.insert(current_command.end(), data.begin(), data.end());
while (current_command.size() != 0) {
ProcessData(actions);
}
return actions;
}
void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) {
const char c{current_command[0]};
// Acknowledgement
if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) {
current_command.erase(current_command.begin());
return;
}
// Interrupt
if (c == GDB_STUB_INT3) {
LOG_INFO(Debug_GDBStub, "Received interrupt");
current_command.erase(current_command.begin());
actions.push_back(DebuggerAction::Interrupt);
SendStatus(GDB_STUB_ACK);
return;
}
// Otherwise, require the data to be the start of a command
if (c != GDB_STUB_START) {
LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data());
current_command.clear();
SendStatus(GDB_STUB_NACK);
return;
}
// Continue reading until command is complete
while (CommandEnd() == current_command.end()) {
const auto new_data{backend.ReadFromClient()};
current_command.insert(current_command.end(), new_data.begin(), new_data.end());
}
// Execute and respond to GDB
const auto command{DetachCommand()};
if (command) {
SendStatus(GDB_STUB_ACK);
ExecuteCommand(*command, actions);
} else {
SendStatus(GDB_STUB_NACK);
}
}
void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions) {
LOG_TRACE(Debug_GDBStub, "Executing command: {}", packet);
if (packet.length() == 0) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
if (packet.starts_with("vCont")) {
HandleVCont(packet.substr(5), actions);
return;
}
std::string_view command{packet.substr(1, packet.size())};
switch (packet[0]) {
case 'H': {
Kernel::KThread* thread{nullptr};
s64 thread_id{strtoll(command.data() + 1, nullptr, 16)};
if (thread_id >= 1) {
thread = GetThreadByID(thread_id);
} else {
thread = backend.GetActiveThread();
}
if (thread) {
SendReply(GDB_STUB_REPLY_OK);
backend.SetActiveThread(thread);
} else {
SendReply(GDB_STUB_REPLY_ERR);
}
break;
}
case 'T': {
s64 thread_id{strtoll(command.data(), nullptr, 16)};
if (GetThreadByID(thread_id)) {
SendReply(GDB_STUB_REPLY_OK);
} else {
SendReply(GDB_STUB_REPLY_ERR);
}
break;
}
case 'Q':
case 'q':
HandleQuery(command);
break;
case '?':
SendReply(arch->ThreadStatus(backend.GetActiveThread(), GDB_STUB_SIGTRAP));
break;
case 'k':
LOG_INFO(Debug_GDBStub, "Shutting down emulation");
actions.push_back(DebuggerAction::ShutdownEmulation);
break;
case 'g':
SendReply(arch->ReadRegisters(backend.GetActiveThread()));
break;
case 'G':
arch->WriteRegisters(backend.GetActiveThread(), command);
SendReply(GDB_STUB_REPLY_OK);
break;
case 'p': {
const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
SendReply(arch->RegRead(backend.GetActiveThread(), reg));
break;
}
case 'P': {
const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1};
const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep));
break;
}
case 'm': {
const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
std::vector<u8> mem(size);
system.Memory().ReadBlock(addr, mem.data(), size);
SendReply(Common::HexToString(mem));
} else {
SendReply(GDB_STUB_REPLY_ERR);
}
break;
}
case 'M': {
const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1};
const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
const auto mem_substr{std::string_view(command).substr(mem_sep)};
const auto mem{Common::HexStringToVector(mem_substr, false)};
if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
system.Memory().WriteBlock(addr, mem.data(), size);
system.InvalidateCpuInstructionCacheRange(addr, size);
SendReply(GDB_STUB_REPLY_OK);
} else {
SendReply(GDB_STUB_REPLY_ERR);
}
break;
}
case 's':
actions.push_back(DebuggerAction::StepThreadLocked);
break;
case 'C':
case 'c':
actions.push_back(DebuggerAction::Continue);
break;
case 'Z': {
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
if (system.Memory().IsValidVirtualAddress(addr)) {
replaced_instructions[addr] = system.Memory().Read32(addr);
system.Memory().Write32(addr, arch->BreakpointInstruction());
system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
SendReply(GDB_STUB_REPLY_OK);
} else {
SendReply(GDB_STUB_REPLY_ERR);
}
break;
}
case 'z': {
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
const auto orig_insn{replaced_instructions.find(addr)};
if (system.Memory().IsValidVirtualAddress(addr) &&
orig_insn != replaced_instructions.end()) {
system.Memory().Write32(addr, orig_insn->second);
system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
replaced_instructions.erase(addr);
SendReply(GDB_STUB_REPLY_OK);
} else {
SendReply(GDB_STUB_REPLY_ERR);
}
break;
}
default:
SendReply(GDB_STUB_REPLY_EMPTY);
break;
}
}
// Structure offsets are from Atmosphere
// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
const Kernel::KThread* thread) {
// Read thread type from TLS
const VAddr tls_thread_type{memory.Read32(thread->GetTLSAddress() + 0x1fc)};
const VAddr argument_thread_type{thread->GetArgument()};
if (argument_thread_type && tls_thread_type != argument_thread_type) {
// Probably not created by nnsdk, no name available.
return std::nullopt;
}
if (!tls_thread_type) {
return std::nullopt;
}
const u16 version{memory.Read16(tls_thread_type + 0x26)};
VAddr name_pointer{};
if (version == 1) {
name_pointer = memory.Read32(tls_thread_type + 0xe4);
} else {
name_pointer = memory.Read32(tls_thread_type + 0xe8);
}
if (!name_pointer) {
// No name provided.
return std::nullopt;
}
return memory.ReadCString(name_pointer, 256);
}
static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
const Kernel::KThread* thread) {
// Read thread type from TLS
const VAddr tls_thread_type{memory.Read64(thread->GetTLSAddress() + 0x1f8)};
const VAddr argument_thread_type{thread->GetArgument()};
if (argument_thread_type && tls_thread_type != argument_thread_type) {
// Probably not created by nnsdk, no name available.
return std::nullopt;
}
if (!tls_thread_type) {
return std::nullopt;
}
const u16 version{memory.Read16(tls_thread_type + 0x46)};
VAddr name_pointer{};
if (version == 1) {
name_pointer = memory.Read64(tls_thread_type + 0x1a0);
} else {
name_pointer = memory.Read64(tls_thread_type + 0x1a8);
}
if (!name_pointer) {
// No name provided.
return std::nullopt;
}
return memory.ReadCString(name_pointer, 256);
}
static std::optional<std::string> GetThreadName(Core::System& system,
const Kernel::KThread* thread) {
if (system.CurrentProcess()->Is64BitProcess()) {
return GetNameFromThreadType64(system.Memory(), thread);
} else {
return GetNameFromThreadType32(system.Memory(), thread);
}
}
static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
switch (thread->GetWaitReasonForDebugging()) {
case Kernel::ThreadWaitReasonForDebugging::Sleep:
return "Sleep";
case Kernel::ThreadWaitReasonForDebugging::IPC:
return "IPC";
case Kernel::ThreadWaitReasonForDebugging::Synchronization:
return "Synchronization";
case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
return "ConditionVar";
case Kernel::ThreadWaitReasonForDebugging::Arbitration:
return "Arbitration";
case Kernel::ThreadWaitReasonForDebugging::Suspended:
return "Suspended";
default:
return "Unknown";
}
}
static std::string GetThreadState(const Kernel::KThread* thread) {
switch (thread->GetState()) {
case Kernel::ThreadState::Initialized:
return "Initialized";
case Kernel::ThreadState::Waiting:
return fmt::format("Waiting ({})", GetThreadWaitReason(thread));
case Kernel::ThreadState::Runnable:
return "Runnable";
case Kernel::ThreadState::Terminated:
return "Terminated";
default:
return "Unknown";
}
}
static std::string PaginateBuffer(std::string_view buffer, std::string_view request) {
const auto amount{request.substr(request.find(',') + 1)};
const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))};
const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
if (offset_val + amount_val > buffer.size()) {
return fmt::format("l{}", buffer.substr(offset_val));
} else {
return fmt::format("m{}", buffer.substr(offset_val, amount_val));
}
}
void GDBStub::HandleQuery(std::string_view command) {
if (command.starts_with("TStatus")) {
// no tracepoint support
SendReply("T0");
} else if (command.starts_with("Supported")) {
SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
"vContSupported+;QStartNoAckMode+");
} else if (command.starts_with("Xfer:features:read:target.xml:")) {
const auto target_xml{arch->GetTargetXML()};
SendReply(PaginateBuffer(target_xml, command.substr(30)));
} else if (command.starts_with("Offsets")) {
Loader::AppLoader::Modules modules;
system.GetAppLoader().ReadNSOModules(modules);
const auto main = std::find_if(modules.begin(), modules.end(),
[](const auto& key) { return key.second == "main"; });
if (main != modules.end()) {
SendReply(fmt::format("TextSeg={:x}", main->first));
} else {
SendReply(fmt::format("TextSeg={:x}",
system.CurrentProcess()->PageTable().GetCodeRegionStart()));
}
} else if (command.starts_with("Xfer:libraries:read::")) {
Loader::AppLoader::Modules modules;
system.GetAppLoader().ReadNSOModules(modules);
std::string buffer;
buffer += R"(<?xml version="1.0"?>)";
buffer += "<library-list>";
for (const auto& [base, name] : modules) {
buffer += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)",
EscapeXML(name), base);
}
buffer += "</library-list>";
SendReply(PaginateBuffer(buffer, command.substr(21)));
} else if (command.starts_with("fThreadInfo")) {
// beginning of list
const auto& threads = system.GlobalSchedulerContext().GetThreadList();
std::vector<std::string> thread_ids;
for (const auto& thread : threads) {
thread_ids.push_back(fmt::format("{:x}", thread->GetThreadID()));
}
SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
} else if (command.starts_with("sThreadInfo")) {
// end of list
SendReply("l");
} else if (command.starts_with("Xfer:threads:read::")) {
std::string buffer;
buffer += R"(<?xml version="1.0"?>)";
buffer += "<threads>";
const auto& threads = system.GlobalSchedulerContext().GetThreadList();
for (const auto* thread : threads) {
auto thread_name{GetThreadName(system, thread)};
if (!thread_name) {
thread_name = fmt::format("Thread {:d}", thread->GetThreadID());
}
buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
thread->GetThreadID(), thread->GetActiveCore(),
EscapeXML(*thread_name), GetThreadState(thread));
}
buffer += "</threads>";
SendReply(PaginateBuffer(buffer, command.substr(19)));
} else if (command.starts_with("Attached")) {
SendReply("0");
} else if (command.starts_with("StartNoAckMode")) {
no_ack = true;
SendReply(GDB_STUB_REPLY_OK);
} else {
SendReply(GDB_STUB_REPLY_EMPTY);
}
}
void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) {
if (command == "?") {
// Continuing and stepping are supported
// (signal is ignored, but required for GDB to use vCont)
SendReply("vCont;c;C;s;S");
return;
}
Kernel::KThread* stepped_thread{nullptr};
bool lock_execution{true};
std::vector<std::string> entries;
boost::split(entries, command.substr(1), boost::is_any_of(";"));
for (const auto& thread_action : entries) {
std::vector<std::string> parts;
boost::split(parts, thread_action, boost::is_any_of(":"));
if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) {
lock_execution = false;
}
if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) {
stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
}
}
if (stepped_thread) {
backend.SetActiveThread(stepped_thread);
actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked
: DebuggerAction::StepThreadUnlocked);
} else {
actions.push_back(DebuggerAction::Continue);
}
}
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
for (auto* thread : threads) {
if (thread->GetThreadID() == thread_id) {
return thread;
}
}
return nullptr;
}
std::vector<char>::const_iterator GDBStub::CommandEnd() const {
// Find the end marker
const auto end{std::find(current_command.begin(), current_command.end(), GDB_STUB_END)};
// Require the checksum to be present
return std::min(end + 2, current_command.end());
}
std::optional<std::string> GDBStub::DetachCommand() {
// Slice the string part from the beginning to the end marker
const auto end{CommandEnd()};
// Extract possible command data
std::string data(current_command.data(), end - current_command.begin() + 1);
// Shift over the remaining contents
current_command.erase(current_command.begin(), end + 1);
// Validate received command
if (data[0] != GDB_STUB_START) {
LOG_ERROR(Debug_GDBStub, "Invalid start data: {}", data[0]);
return std::nullopt;
}
u8 calculated = CalculateChecksum(std::string_view(data).substr(1, data.size() - 4));
u8 received = static_cast<u8>(strtoll(data.data() + data.size() - 2, nullptr, 16));
// Verify checksum
if (calculated != received) {
LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}",
calculated, received);
return std::nullopt;
}
return data.substr(1, data.size() - 4);
}
void GDBStub::SendReply(std::string_view data) {
const auto escaped{EscapeGDB(data)};
const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END,
CalculateChecksum(escaped))};
LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
// C++ string support is complete rubbish
const u8* output_begin = reinterpret_cast<const u8*>(output.data());
const u8* output_end = output_begin + output.size();
backend.WriteToClient(std::span<const u8>(output_begin, output_end));
}
void GDBStub::SendStatus(char status) {
if (no_ack) {
return;
}
std::array<u8, 1> buf = {static_cast<u8>(status)};
LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
backend.WriteToClient(buf);
}
} // namespace Core
+48
View File
@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <map>
#include <memory>
#include <optional>
#include <string_view>
#include <vector>
#include "core/debugger/debugger_interface.h"
#include "core/debugger/gdbstub_arch.h"
namespace Core {
class System;
class GDBStub : public DebuggerFrontend {
public:
explicit GDBStub(DebuggerBackend& backend, Core::System& system);
~GDBStub() override;
void Connected() override;
void Stopped(Kernel::KThread* thread) override;
std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
private:
void ProcessData(std::vector<DebuggerAction>& actions);
void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
void HandleQuery(std::string_view command);
std::vector<char>::const_iterator CommandEnd() const;
std::optional<std::string> DetachCommand();
Kernel::KThread* GetThreadByID(u64 thread_id);
void SendReply(std::string_view data);
void SendStatus(char status);
private:
Core::System& system;
std::unique_ptr<GDBStubArch> arch;
std::vector<char> current_command;
std::map<VAddr, u32> replaced_instructions;
bool no_ack{};
};
} // namespace Core
+483
View File
@@ -0,0 +1,483 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/hex_util.h"
#include "core/debugger/gdbstub_arch.h"
#include "core/hle/kernel/k_thread.h"
namespace Core {
template <typename T>
static T HexToValue(std::string_view hex) {
static_assert(std::is_trivially_copyable_v<T>);
T value{};
const auto mem{Common::HexStringToVector(hex, false)};
std::memcpy(&value, mem.data(), std::min(mem.size(), sizeof(T)));
return value;
}
template <typename T>
static std::string ValueToHex(const T value) {
static_assert(std::is_trivially_copyable_v<T>);
std::array<u8, sizeof(T)> mem{};
std::memcpy(mem.data(), &value, sizeof(T));
return Common::HexToString(mem);
}
template <typename T>
static T GetSIMDRegister(const std::array<u32, 64>& simd_regs, size_t offset) {
static_assert(std::is_trivially_copyable_v<T>);
T value{};
std::memcpy(&value, reinterpret_cast<const u8*>(simd_regs.data()) + sizeof(T) * offset,
sizeof(T));
return value;
}
template <typename T>
static void PutSIMDRegister(std::array<u32, 64>& simd_regs, size_t offset, const T value) {
static_assert(std::is_trivially_copyable_v<T>);
std::memcpy(reinterpret_cast<u8*>(simd_regs.data()) + sizeof(T) * offset, &value, sizeof(T));
}
// For sample XML files see the GDB source /gdb/features
// This XML defines what the registers are for this specific ARM device
std::string GDBStubA64::GetTargetXML() const {
constexpr const char* target_xml =
R"(<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
<architecture>aarch64</architecture>
<feature name="org.gnu.gdb.aarch64.core">
<reg name="x0" bitsize="64"/>
<reg name="x1" bitsize="64"/>
<reg name="x2" bitsize="64"/>
<reg name="x3" bitsize="64"/>
<reg name="x4" bitsize="64"/>
<reg name="x5" bitsize="64"/>
<reg name="x6" bitsize="64"/>
<reg name="x7" bitsize="64"/>
<reg name="x8" bitsize="64"/>
<reg name="x9" bitsize="64"/>
<reg name="x10" bitsize="64"/>
<reg name="x11" bitsize="64"/>
<reg name="x12" bitsize="64"/>
<reg name="x13" bitsize="64"/>
<reg name="x14" bitsize="64"/>
<reg name="x15" bitsize="64"/>
<reg name="x16" bitsize="64"/>
<reg name="x17" bitsize="64"/>
<reg name="x18" bitsize="64"/>
<reg name="x19" bitsize="64"/>
<reg name="x20" bitsize="64"/>
<reg name="x21" bitsize="64"/>
<reg name="x22" bitsize="64"/>
<reg name="x23" bitsize="64"/>
<reg name="x24" bitsize="64"/>
<reg name="x25" bitsize="64"/>
<reg name="x26" bitsize="64"/>
<reg name="x27" bitsize="64"/>
<reg name="x28" bitsize="64"/>
<reg name="x29" bitsize="64"/>
<reg name="x30" bitsize="64"/>
<reg name="sp" bitsize="64" type="data_ptr"/>
<reg name="pc" bitsize="64" type="code_ptr"/>
<flags id="cpsr_flags" size="4">
<field name="SP" start="0" end="0"/>
<field name="" start="1" end="1"/>
<field name="EL" start="2" end="3"/>
<field name="nRW" start="4" end="4"/>
<field name="" start="5" end="5"/>
<field name="F" start="6" end="6"/>
<field name="I" start="7" end="7"/>
<field name="A" start="8" end="8"/>
<field name="D" start="9" end="9"/>
<field name="IL" start="20" end="20"/>
<field name="SS" start="21" end="21"/>
<field name="V" start="28" end="28"/>
<field name="C" start="29" end="29"/>
<field name="Z" start="30" end="30"/>
<field name="N" start="31" end="31"/>
</flags>
<reg name="cpsr" bitsize="32" type="cpsr_flags"/>
</feature>
<feature name="org.gnu.gdb.aarch64.fpu">
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v2u" type="uint64" count="2"/>
<vector id="v2i" type="int64" count="2"/>
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v4u" type="uint32" count="4"/>
<vector id="v4i" type="int32" count="4"/>
<vector id="v8u" type="uint16" count="8"/>
<vector id="v8i" type="int16" count="8"/>
<vector id="v16u" type="uint8" count="16"/>
<vector id="v16i" type="int8" count="16"/>
<vector id="v1u" type="uint128" count="1"/>
<vector id="v1i" type="int128" count="1"/>
<union id="vnd">
<field name="f" type="v2d"/>
<field name="u" type="v2u"/>
<field name="s" type="v2i"/>
</union>
<union id="vns">
<field name="f" type="v4f"/>
<field name="u" type="v4u"/>
<field name="s" type="v4i"/>
</union>
<union id="vnh">
<field name="u" type="v8u"/>
<field name="s" type="v8i"/>
</union>
<union id="vnb">
<field name="u" type="v16u"/>
<field name="s" type="v16i"/>
</union>
<union id="vnq">
<field name="u" type="v1u"/>
<field name="s" type="v1i"/>
</union>
<union id="aarch64v">
<field name="d" type="vnd"/>
<field name="s" type="vns"/>
<field name="h" type="vnh"/>
<field name="b" type="vnb"/>
<field name="q" type="vnq"/>
</union>
<reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
<reg name="v1" bitsize="128" type="aarch64v" />
<reg name="v2" bitsize="128" type="aarch64v" />
<reg name="v3" bitsize="128" type="aarch64v" />
<reg name="v4" bitsize="128" type="aarch64v" />
<reg name="v5" bitsize="128" type="aarch64v" />
<reg name="v6" bitsize="128" type="aarch64v" />
<reg name="v7" bitsize="128" type="aarch64v" />
<reg name="v8" bitsize="128" type="aarch64v" />
<reg name="v9" bitsize="128" type="aarch64v" />
<reg name="v10" bitsize="128" type="aarch64v"/>
<reg name="v11" bitsize="128" type="aarch64v"/>
<reg name="v12" bitsize="128" type="aarch64v"/>
<reg name="v13" bitsize="128" type="aarch64v"/>
<reg name="v14" bitsize="128" type="aarch64v"/>
<reg name="v15" bitsize="128" type="aarch64v"/>
<reg name="v16" bitsize="128" type="aarch64v"/>
<reg name="v17" bitsize="128" type="aarch64v"/>
<reg name="v18" bitsize="128" type="aarch64v"/>
<reg name="v19" bitsize="128" type="aarch64v"/>
<reg name="v20" bitsize="128" type="aarch64v"/>
<reg name="v21" bitsize="128" type="aarch64v"/>
<reg name="v22" bitsize="128" type="aarch64v"/>
<reg name="v23" bitsize="128" type="aarch64v"/>
<reg name="v24" bitsize="128" type="aarch64v"/>
<reg name="v25" bitsize="128" type="aarch64v"/>
<reg name="v26" bitsize="128" type="aarch64v"/>
<reg name="v27" bitsize="128" type="aarch64v"/>
<reg name="v28" bitsize="128" type="aarch64v"/>
<reg name="v29" bitsize="128" type="aarch64v"/>
<reg name="v30" bitsize="128" type="aarch64v"/>
<reg name="v31" bitsize="128" type="aarch64v"/>
<reg name="fpsr" bitsize="32"/>
<reg name="fpcr" bitsize="32"/>
</feature>
</target>)";
return target_xml;
}
std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const {
if (!thread) {
return "";
}
const auto& context{thread->GetContext64()};
const auto& gprs{context.cpu_registers};
const auto& fprs{context.vector_registers};
if (id <= SP_REGISTER) {
return ValueToHex(gprs[id]);
} else if (id == PC_REGISTER) {
return ValueToHex(context.pc);
} else if (id == PSTATE_REGISTER) {
return ValueToHex(context.pstate);
} else if (id >= Q0_REGISTER && id < FPSR_REGISTER) {
return ValueToHex(fprs[id - Q0_REGISTER]);
} else if (id == FPSR_REGISTER) {
return ValueToHex(context.fpsr);
} else if (id == FPCR_REGISTER) {
return ValueToHex(context.fpcr);
} else {
return "";
}
}
void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const {
if (!thread) {
return;
}
auto& context{thread->GetContext64()};
if (id <= SP_REGISTER) {
context.cpu_registers[id] = HexToValue<u64>(value);
} else if (id == PC_REGISTER) {
context.pc = HexToValue<u64>(value);
} else if (id == PSTATE_REGISTER) {
context.pstate = HexToValue<u32>(value);
} else if (id >= Q0_REGISTER && id < FPSR_REGISTER) {
context.vector_registers[id - Q0_REGISTER] = HexToValue<u128>(value);
} else if (id == FPSR_REGISTER) {
context.fpsr = HexToValue<u32>(value);
} else if (id == FPCR_REGISTER) {
context.fpcr = HexToValue<u32>(value);
}
}
std::string GDBStubA64::ReadRegisters(const Kernel::KThread* thread) const {
std::string output;
for (size_t reg = 0; reg <= FPCR_REGISTER; reg++) {
output += RegRead(thread, reg);
}
return output;
}
void GDBStubA64::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const {
for (size_t i = 0, reg = 0; reg <= FPCR_REGISTER; reg++) {
if (reg <= SP_REGISTER || reg == PC_REGISTER) {
RegWrite(thread, reg, register_data.substr(i, 16));
i += 16;
} else if (reg == PSTATE_REGISTER || reg == FPCR_REGISTER || reg == FPSR_REGISTER) {
RegWrite(thread, reg, register_data.substr(i, 8));
i += 8;
} else if (reg >= Q0_REGISTER && reg < FPCR_REGISTER) {
RegWrite(thread, reg, register_data.substr(i, 32));
i += 32;
}
}
}
std::string GDBStubA64::ThreadStatus(const Kernel::KThread* thread, u8 signal) const {
return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER,
RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER),
LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID());
}
u32 GDBStubA64::BreakpointInstruction() const {
// A64: brk #0
return 0xd4200000;
}
std::string GDBStubA32::GetTargetXML() const {
constexpr const char* target_xml =
R"(<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
<architecture>arm</architecture>
<feature name="org.gnu.gdb.arm.core">
<reg name="r0" bitsize="32" type="uint32"/>
<reg name="r1" bitsize="32" type="uint32"/>
<reg name="r2" bitsize="32" type="uint32"/>
<reg name="r3" bitsize="32" type="uint32"/>
<reg name="r4" bitsize="32" type="uint32"/>
<reg name="r5" bitsize="32" type="uint32"/>
<reg name="r6" bitsize="32" type="uint32"/>
<reg name="r7" bitsize="32" type="uint32"/>
<reg name="r8" bitsize="32" type="uint32"/>
<reg name="r9" bitsize="32" type="uint32"/>
<reg name="r10" bitsize="32" type="uint32"/>
<reg name="r11" bitsize="32" type="uint32"/>
<reg name="r12" bitsize="32" type="uint32"/>
<reg name="sp" bitsize="32" type="data_ptr"/>
<reg name="lr" bitsize="32" type="code_ptr"/>
<reg name="pc" bitsize="32" type="code_ptr"/>
<!-- The CPSR is register 25, rather than register 16, because
the FPA registers historically were placed between the PC
and the CPSR in the "g" packet. -->
<reg name="cpsr" bitsize="32" regnum="25"/>
</feature>
<feature name="org.gnu.gdb.arm.vfp">
<vector id="neon_uint8x8" type="uint8" count="8"/>
<vector id="neon_uint16x4" type="uint16" count="4"/>
<vector id="neon_uint32x2" type="uint32" count="2"/>
<vector id="neon_float32x2" type="ieee_single" count="2"/>
<union id="neon_d">
<field name="u8" type="neon_uint8x8"/>
<field name="u16" type="neon_uint16x4"/>
<field name="u32" type="neon_uint32x2"/>
<field name="u64" type="uint64"/>
<field name="f32" type="neon_float32x2"/>
<field name="f64" type="ieee_double"/>
</union>
<vector id="neon_uint8x16" type="uint8" count="16"/>
<vector id="neon_uint16x8" type="uint16" count="8"/>
<vector id="neon_uint32x4" type="uint32" count="4"/>
<vector id="neon_uint64x2" type="uint64" count="2"/>
<vector id="neon_float32x4" type="ieee_single" count="4"/>
<vector id="neon_float64x2" type="ieee_double" count="2"/>
<union id="neon_q">
<field name="u8" type="neon_uint8x16"/>
<field name="u16" type="neon_uint16x8"/>
<field name="u32" type="neon_uint32x4"/>
<field name="u64" type="neon_uint64x2"/>
<field name="f32" type="neon_float32x4"/>
<field name="f64" type="neon_float64x2"/>
</union>
<reg name="d0" bitsize="64" type="neon_d" regnum="32"/>
<reg name="d1" bitsize="64" type="neon_d"/>
<reg name="d2" bitsize="64" type="neon_d"/>
<reg name="d3" bitsize="64" type="neon_d"/>
<reg name="d4" bitsize="64" type="neon_d"/>
<reg name="d5" bitsize="64" type="neon_d"/>
<reg name="d6" bitsize="64" type="neon_d"/>
<reg name="d7" bitsize="64" type="neon_d"/>
<reg name="d8" bitsize="64" type="neon_d"/>
<reg name="d9" bitsize="64" type="neon_d"/>
<reg name="d10" bitsize="64" type="neon_d"/>
<reg name="d11" bitsize="64" type="neon_d"/>
<reg name="d12" bitsize="64" type="neon_d"/>
<reg name="d13" bitsize="64" type="neon_d"/>
<reg name="d14" bitsize="64" type="neon_d"/>
<reg name="d15" bitsize="64" type="neon_d"/>
<reg name="d16" bitsize="64" type="neon_d"/>
<reg name="d17" bitsize="64" type="neon_d"/>
<reg name="d18" bitsize="64" type="neon_d"/>
<reg name="d19" bitsize="64" type="neon_d"/>
<reg name="d20" bitsize="64" type="neon_d"/>
<reg name="d21" bitsize="64" type="neon_d"/>
<reg name="d22" bitsize="64" type="neon_d"/>
<reg name="d23" bitsize="64" type="neon_d"/>
<reg name="d24" bitsize="64" type="neon_d"/>
<reg name="d25" bitsize="64" type="neon_d"/>
<reg name="d26" bitsize="64" type="neon_d"/>
<reg name="d27" bitsize="64" type="neon_d"/>
<reg name="d28" bitsize="64" type="neon_d"/>
<reg name="d29" bitsize="64" type="neon_d"/>
<reg name="d30" bitsize="64" type="neon_d"/>
<reg name="d31" bitsize="64" type="neon_d"/>
<reg name="q0" bitsize="128" type="neon_q" regnum="64"/>
<reg name="q1" bitsize="128" type="neon_q"/>
<reg name="q2" bitsize="128" type="neon_q"/>
<reg name="q3" bitsize="128" type="neon_q"/>
<reg name="q4" bitsize="128" type="neon_q"/>
<reg name="q5" bitsize="128" type="neon_q"/>
<reg name="q6" bitsize="128" type="neon_q"/>
<reg name="q7" bitsize="128" type="neon_q"/>
<reg name="q8" bitsize="128" type="neon_q"/>
<reg name="q9" bitsize="128" type="neon_q"/>
<reg name="q10" bitsize="128" type="neon_q"/>
<reg name="q10" bitsize="128" type="neon_q"/>
<reg name="q12" bitsize="128" type="neon_q"/>
<reg name="q13" bitsize="128" type="neon_q"/>
<reg name="q14" bitsize="128" type="neon_q"/>
<reg name="q15" bitsize="128" type="neon_q"/>
<reg name="fpscr" bitsize="32" type="int" group="float" regnum="80"/>
</feature>
</target>)";
return target_xml;
}
std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const {
if (!thread) {
return "";
}
const auto& context{thread->GetContext32()};
const auto& gprs{context.cpu_registers};
const auto& fprs{context.extension_registers};
if (id <= PC_REGISTER) {
return ValueToHex(gprs[id]);
} else if (id == CPSR_REGISTER) {
return ValueToHex(context.cpsr);
} else if (id >= D0_REGISTER && id < Q0_REGISTER) {
const u64 dN{GetSIMDRegister<u64>(fprs, id - D0_REGISTER)};
return ValueToHex(dN);
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
const u128 qN{GetSIMDRegister<u128>(fprs, id - Q0_REGISTER)};
return ValueToHex(qN);
} else if (id == FPSCR_REGISTER) {
return ValueToHex(context.fpscr);
} else {
return "";
}
}
void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const {
if (!thread) {
return;
}
auto& context{thread->GetContext32()};
auto& fprs{context.extension_registers};
if (id <= PC_REGISTER) {
context.cpu_registers[id] = HexToValue<u32>(value);
} else if (id == CPSR_REGISTER) {
context.cpsr = HexToValue<u32>(value);
} else if (id >= D0_REGISTER && id < Q0_REGISTER) {
PutSIMDRegister(fprs, id - D0_REGISTER, HexToValue<u64>(value));
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
PutSIMDRegister(fprs, id - Q0_REGISTER, HexToValue<u128>(value));
} else if (id == FPSCR_REGISTER) {
context.fpscr = HexToValue<u32>(value);
}
}
std::string GDBStubA32::ReadRegisters(const Kernel::KThread* thread) const {
std::string output;
for (size_t reg = 0; reg <= FPSCR_REGISTER; reg++) {
const bool gpr{reg <= PC_REGISTER};
const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER};
const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER};
if (!(gpr || dfpr || qfpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER)) {
continue;
}
output += RegRead(thread, reg);
}
return output;
}
void GDBStubA32::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const {
for (size_t i = 0, reg = 0; reg <= FPSCR_REGISTER; reg++) {
const bool gpr{reg <= PC_REGISTER};
const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER};
const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER};
if (gpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER) {
RegWrite(thread, reg, register_data.substr(i, 8));
i += 8;
} else if (dfpr) {
RegWrite(thread, reg, register_data.substr(i, 16));
i += 16;
} else if (qfpr) {
RegWrite(thread, reg, register_data.substr(i, 32));
i += 32;
}
if (reg == PC_REGISTER) {
reg = CPSR_REGISTER - 1;
} else if (reg == CPSR_REGISTER) {
reg = D0_REGISTER - 1;
}
}
}
std::string GDBStubA32::ThreadStatus(const Kernel::KThread* thread, u8 signal) const {
return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER,
RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER),
LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID());
}
u32 GDBStubA32::BreakpointInstruction() const {
// A32: trap
// T32: trap + b #4
return 0xe7ffdefe;
}
} // namespace Core
+67
View File
@@ -0,0 +1,67 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include "common/common_types.h"
namespace Kernel {
class KThread;
}
namespace Core {
class GDBStubArch {
public:
virtual std::string GetTargetXML() const = 0;
virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0;
virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0;
virtual std::string ReadRegisters(const Kernel::KThread* thread) const = 0;
virtual void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const = 0;
virtual std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const = 0;
virtual u32 BreakpointInstruction() const = 0;
};
class GDBStubA64 final : public GDBStubArch {
public:
std::string GetTargetXML() const override;
std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
std::string ReadRegisters(const Kernel::KThread* thread) const override;
void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override;
std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override;
u32 BreakpointInstruction() const override;
private:
static constexpr u32 LR_REGISTER = 30;
static constexpr u32 SP_REGISTER = 31;
static constexpr u32 PC_REGISTER = 32;
static constexpr u32 PSTATE_REGISTER = 33;
static constexpr u32 Q0_REGISTER = 34;
static constexpr u32 FPSR_REGISTER = 66;
static constexpr u32 FPCR_REGISTER = 67;
};
class GDBStubA32 final : public GDBStubArch {
public:
std::string GetTargetXML() const override;
std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
std::string ReadRegisters(const Kernel::KThread* thread) const override;
void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override;
std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override;
u32 BreakpointInstruction() const override;
private:
static constexpr u32 SP_REGISTER = 13;
static constexpr u32 LR_REGISTER = 14;
static constexpr u32 PC_REGISTER = 15;
static constexpr u32 CPSR_REGISTER = 25;
static constexpr u32 D0_REGISTER = 32;
static constexpr u32 Q0_REGISTER = 64;
static constexpr u32 FPSCR_REGISTER = 80;
};
} // namespace Core
+11 -4
View File
@@ -27,12 +27,19 @@ void EmulatedConsole::SetTouchParams() {
// We can't use mouse as touch if native mouse is enabled
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
}
touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
touch_params[index++] =
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
touch_params[index++] =
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
touch_params[index++] =
Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
touch_params[index++] =
Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
touch_params[index++] =
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
touch_params[index++] =
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
const auto button_index =
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
+29 -5
View File
@@ -884,18 +884,42 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
}
bool EmulatedController::TestVibration(std::size_t device_index) {
static constexpr VibrationValue test_vibration = {
if (device_index >= output_devices.size()) {
return false;
}
if (!output_devices[device_index]) {
return false;
}
const auto player_index = NpadIdTypeToIndex(npad_id_type);
const auto& player = Settings::values.players.GetValue()[player_index];
if (!player.vibration_enabled) {
return false;
}
const Common::Input::VibrationStatus test_vibration = {
.low_amplitude = 0.001f,
.low_frequency = 160.0f,
.low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
.high_amplitude = 0.001f,
.high_frequency = 320.0f,
.high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
.type = Common::Input::VibrationAmplificationType::Test,
};
const Common::Input::VibrationStatus zero_vibration = {
.low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude,
.low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
.high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude,
.high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
.type = Common::Input::VibrationAmplificationType::Test,
};
// Send a slight vibration to test for rumble support
SetVibration(device_index, test_vibration);
output_devices[device_index]->SetVibration(test_vibration);
// Stop any vibration and return the result
return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
return output_devices[device_index]->SetVibration(zero_vibration) ==
Common::Input::VibrationError::None;
}
bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
+43
View File
@@ -498,6 +498,49 @@ struct SixAxisSensorFusionParameters {
static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
"SixAxisSensorFusionParameters is an invalid size");
// This is nn::hid::server::SixAxisSensorProperties
struct SixAxisSensorProperties {
union {
u8 raw{};
BitField<0, 1, u8> is_newly_assigned;
BitField<1, 1, u8> is_firmware_update_available;
};
};
static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size");
// This is nn::hid::SixAxisSensorCalibrationParameter
struct SixAxisSensorCalibrationParameter {
std::array<u8, 0x744> unknown_data{};
};
static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744,
"SixAxisSensorCalibrationParameter is an invalid size");
// This is nn::hid::SixAxisSensorIcInformation
struct SixAxisSensorIcInformation {
f32 angular_rate{2000.0f}; // dps
std::array<f32, 6> unknown_gyro_data1{
-10.0f, -10.0f, -10.0f, 10.0f, 10.0f, 10.0f,
}; // dps
std::array<f32, 9> unknown_gyro_data2{
0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
};
std::array<f32, 9> unknown_gyro_data3{
1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
};
f32 acceleration_range{8.0f}; // g force
std::array<f32, 6> unknown_accel_data1{
-0.0612f, -0.0612f, -0.0612f, 0.0612f, 0.0612f, 0.0612f,
}; // g force
std::array<f32, 9> unknown_accel_data2{
0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
};
std::array<f32, 9> unknown_accel_data3{
1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
};
};
static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
"SixAxisSensorIcInformation is an invalid size");
// This is nn::hid::VibrationDeviceHandle
struct VibrationDeviceHandle {
NpadStyleIndex npad_type{NpadStyleIndex::None};
+4
View File
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <random>
#include "common/input.h"
@@ -196,6 +197,9 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
x = std::clamp(x, 0.0f, 1.0f);
y = std::clamp(y, 0.0f, 1.0f);
// Limit id to maximum number of fingers
status.id = std::clamp(status.id, 0, 16);
if (status.pressed.inverted) {
status.pressed.value = !status.pressed.value;
}
+4
View File
@@ -64,6 +64,10 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
{
KScopedSchedulerLock lock{kernel};
thread->SetState(ThreadState::Runnable);
if (system.DebuggerEnabled()) {
thread->RequestSuspend(SuspendType::Debug);
}
}
}
} // Anonymous namespace
+4
View File
@@ -198,6 +198,10 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
resource_limit_release_hint = false;
cpu_time = 0;
// Set debug context.
stack_top = user_stack_top;
argument = arg;
// Clear our stack parameters.
std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0,
sizeof(StackParameters));
+25
View File
@@ -100,6 +100,12 @@ enum class ThreadWaitReasonForDebugging : u32 {
Suspended, ///< Thread is waiting due to process suspension
};
enum class StepState : u32 {
NotStepping, ///< Thread is not currently stepping
StepPending, ///< Thread will step when next scheduled
StepPerformed, ///< Thread has stepped, waiting to be scheduled again
};
[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
@@ -267,6 +273,14 @@ public:
void SetState(ThreadState state);
[[nodiscard]] StepState GetStepState() const {
return step_state;
}
void SetStepState(StepState state) {
step_state = state;
}
[[nodiscard]] s64 GetLastScheduledTick() const {
return last_scheduled_tick;
}
@@ -646,6 +660,14 @@ public:
void IfDummyThreadTryWait();
void IfDummyThreadEndWait();
[[nodiscard]] uintptr_t GetArgument() const {
return argument;
}
[[nodiscard]] VAddr GetUserStackTop() const {
return stack_top;
}
private:
static constexpr size_t PriorityInheritanceCountMax = 10;
union SyncObjectBuffer {
@@ -769,6 +791,7 @@ private:
std::shared_ptr<Common::Fiber> host_context{};
bool is_single_core{};
ThreadType thread_type{};
StepState step_state{};
std::mutex dummy_wait_lock;
std::condition_variable dummy_wait_cv;
@@ -776,6 +799,8 @@ private:
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
VAddr mutex_wait_address_for_debugging{};
ThreadWaitReasonForDebugging wait_reason_for_debugging{};
uintptr_t argument;
VAddr stack_top;
public:
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
@@ -61,6 +61,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
}
last_update_timestamp = shared_memory->gesture_lifo.timestamp;
UpdateGestureSharedMemory(gesture, time_difference);
}
void Controller_Gesture::ReadTouchInput() {
@@ -94,8 +95,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
return false;
}
void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
GestureProperties& gesture,
void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
f32 time_difference) {
GestureType type = GestureType::Idle;
GestureAttribute attributes{};
@@ -107,8 +107,7 @@ private:
bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
// Updates the shared memory to the next state
void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture,
f32 time_difference);
void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
// Initializes new gesture
void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
+348 -298
View File
@@ -56,11 +56,22 @@ bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle
return npad_id && npad_type && device_index;
}
bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
ResultCode Controller_NPad::VerifyValidSixAxisSensorHandle(
const Core::HID::SixAxisSensorHandle& device_handle) {
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
if (!npad_id) {
return InvalidNpadId;
}
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
return npad_id && npad_type && device_index;
if (!device_index) {
return NpadDeviceIndexOutOfRange;
}
// This doesn't get validated on nnsdk
const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
if (!npad_type) {
return NpadInvalidHandle;
}
return ResultSuccess;
}
Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
@@ -158,6 +169,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController;
shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::Handheld:
shared_memory->style_tag.handheld.Assign(1);
@@ -170,16 +182,19 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
shared_memory->applet_nfc_xcd.applet_footer.type =
AppletFooterUiType::HandheldJoyConLeftJoyConRight;
shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
shared_memory->style_tag.joycon_dual.Assign(1);
if (controller.is_dual_left_connected) {
shared_memory->device_type.joycon_left.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1);
}
if (controller.is_dual_right_connected) {
shared_memory->device_type.joycon_right.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1);
}
shared_memory->system_properties.use_directional_buttons.Assign(1);
shared_memory->system_properties.is_vertical.Assign(1);
@@ -198,6 +213,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->system_properties.is_horizontal.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
shared_memory->style_tag.joycon_right.Assign(1);
@@ -205,6 +221,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->system_properties.is_horizontal.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::GameCube:
shared_memory->style_tag.gamecube.Assign(1);
@@ -215,6 +232,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
case Core::HID::NpadStyleIndex::Pokeball:
shared_memory->style_tag.palma.Assign(1);
shared_memory->device_type.palma.Assign(1);
shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::NES:
shared_memory->style_tag.lark.Assign(1);
@@ -582,6 +600,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
UNREACHABLE();
break;
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Pokeball:
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::Handheld:
@@ -672,6 +691,12 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
}
void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
if (joy_hold_type != NpadJoyHoldType::Horizontal &&
joy_hold_type != NpadJoyHoldType::Vertical) {
LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
joy_hold_type);
return;
}
hold_type = joy_hold_type;
}
@@ -695,11 +720,12 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
return communication_mode;
}
void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
NpadJoyAssignmentMode assignment_mode) {
ResultCode Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
NpadJoyDeviceType npad_device_type,
NpadJoyAssignmentMode assignment_mode) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return;
return InvalidNpadId;
}
auto& controller = GetControllerFromNpadIdType(npad_id);
@@ -708,7 +734,7 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
}
if (!controller.device->IsConnected()) {
return;
return ResultSuccess;
}
if (assignment_mode == NpadJoyAssignmentMode::Dual) {
@@ -717,34 +743,34 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
controller.is_dual_left_connected = true;
controller.is_dual_right_connected = false;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
return;
return ResultSuccess;
}
if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
DisconnectNpad(npad_id);
controller.is_dual_left_connected = false;
controller.is_dual_right_connected = true;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
return;
return ResultSuccess;
}
return;
return ResultSuccess;
}
// This is for NpadJoyAssignmentMode::Single
// Only JoyconDual get affected by this function
if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
return;
return ResultSuccess;
}
if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
DisconnectNpad(npad_id);
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
return;
return ResultSuccess;
}
if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
DisconnectNpad(npad_id);
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
return;
return ResultSuccess;
}
// We have two controllers connected to the same npad_id we need to split them
@@ -762,6 +788,7 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
controller_2.is_dual_right_connected = false;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
}
return ResultSuccess;
}
bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
@@ -957,10 +984,10 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
InitNewlyAddedController(npad_id);
}
void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
ResultCode Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return;
return InvalidNpadId;
}
LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
@@ -977,6 +1004,12 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
shared_memory->device_type.raw = 0;
shared_memory->system_properties.raw = 0;
shared_memory->button_properties.raw = 0;
shared_memory->sixaxis_fullkey_properties.raw = 0;
shared_memory->sixaxis_handheld_properties.raw = 0;
shared_memory->sixaxis_dual_left_properties.raw = 0;
shared_memory->sixaxis_dual_right_properties.raw = 0;
shared_memory->sixaxis_left_properties.raw = 0;
shared_memory->sixaxis_right_properties.raw = 0;
shared_memory->battery_level_dual = 0;
shared_memory->battery_level_left = 0;
shared_memory->battery_level_right = 0;
@@ -997,346 +1030,268 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
controller.device->Disconnect();
SignalStyleSetChangedEvent(npad_id);
WriteEmptyEntry(shared_memory);
return ResultSuccess;
}
ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
GyroscopeZeroDriftMode drift_mode) {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
return NpadInvalidHandle;
ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, GyroscopeZeroDriftMode drift_mode) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
controller.sixaxis_fullkey.gyroscope_zero_drift_mode = drift_mode;
break;
case Core::HID::NpadStyleIndex::Handheld:
controller.sixaxis_handheld.gyroscope_zero_drift_mode = drift_mode;
break;
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::GameCube:
case Core::HID::NpadStyleIndex::Pokeball:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
controller.sixaxis_dual_left.gyroscope_zero_drift_mode = drift_mode;
break;
}
controller.sixaxis_dual_right.gyroscope_zero_drift_mode = drift_mode;
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
controller.sixaxis_left.gyroscope_zero_drift_mode = drift_mode;
break;
case Core::HID::NpadStyleIndex::JoyconRight:
controller.sixaxis_right.gyroscope_zero_drift_mode = drift_mode;
break;
default:
LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
return NpadInvalidHandle;
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
sixaxis.gyroscope_zero_drift_mode = drift_mode;
return ResultSuccess;
}
ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
GyroscopeZeroDriftMode& drift_mode) const {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
return NpadInvalidHandle;
ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
GyroscopeZeroDriftMode& drift_mode) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
drift_mode = controller.sixaxis_fullkey.gyroscope_zero_drift_mode;
break;
case Core::HID::NpadStyleIndex::Handheld:
drift_mode = controller.sixaxis_handheld.gyroscope_zero_drift_mode;
break;
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::GameCube:
case Core::HID::NpadStyleIndex::Pokeball:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
drift_mode = controller.sixaxis_dual_left.gyroscope_zero_drift_mode;
break;
}
drift_mode = controller.sixaxis_dual_right.gyroscope_zero_drift_mode;
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
drift_mode = controller.sixaxis_left.gyroscope_zero_drift_mode;
break;
case Core::HID::NpadStyleIndex::JoyconRight:
drift_mode = controller.sixaxis_right.gyroscope_zero_drift_mode;
break;
default:
LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
return NpadInvalidHandle;
}
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
drift_mode = sixaxis.gyroscope_zero_drift_mode;
return ResultSuccess;
}
ResultCode Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
bool& is_at_rest) const {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
return NpadInvalidHandle;
ResultCode Controller_NPad::IsSixAxisSensorAtRest(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_at_rest) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& controller = GetControllerFromHandle(sixaxis_handle);
is_at_rest = controller.sixaxis_at_rest;
return ResultSuccess;
}
ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
return NpadInvalidHandle;
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
// We don't support joycon firmware updates
is_firmware_available = false;
const auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
is_firmware_available = sixaxis_properties.is_firmware_update_available != 0;
return ResultSuccess;
}
ResultCode Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
bool sixaxis_status) {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
return NpadInvalidHandle;
ResultCode Controller_NPad::EnableSixAxisSensorUnalteredPassthrough(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
sixaxis.unaltered_passtrough = is_enabled;
return ResultSuccess;
}
ResultCode Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
is_enabled = sixaxis.unaltered_passtrough;
return ResultSuccess;
}
ResultCode Controller_NPad::LoadSixAxisSensorCalibrationParameter(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
// TODO: Request this data to the controller. On error return 0xd8ca
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
calibration = sixaxis.calibration;
return ResultSuccess;
}
ResultCode Controller_NPad::GetSixAxisSensorIcInformation(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorIcInformation& ic_information) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
// TODO: Request this data to the controller. On error return 0xd8ca
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
ic_information = sixaxis.ic_information;
return ResultSuccess;
}
ResultCode Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
sixaxis_properties.is_newly_assigned.Assign(0);
return ResultSuccess;
}
ResultCode Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool sixaxis_status) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& controller = GetControllerFromHandle(sixaxis_handle);
controller.sixaxis_sensor_enabled = sixaxis_status;
return ResultSuccess;
}
ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled(
Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_fusion_enabled) const {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
return NpadInvalidHandle;
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
is_fusion_enabled = controller.sixaxis_fullkey.is_fusion_enabled;
break;
case Core::HID::NpadStyleIndex::Handheld:
is_fusion_enabled = controller.sixaxis_handheld.is_fusion_enabled;
break;
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::GameCube:
case Core::HID::NpadStyleIndex::Pokeball:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
is_fusion_enabled = controller.sixaxis_dual_left.is_fusion_enabled;
break;
}
is_fusion_enabled = controller.sixaxis_dual_right.is_fusion_enabled;
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
is_fusion_enabled = controller.sixaxis_left.is_fusion_enabled;
break;
case Core::HID::NpadStyleIndex::JoyconRight:
is_fusion_enabled = controller.sixaxis_right.is_fusion_enabled;
break;
default:
LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
return NpadInvalidHandle;
}
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
is_fusion_enabled = sixaxis.is_fusion_enabled;
return ResultSuccess;
}
ResultCode Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
bool is_fusion_enabled) {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
return NpadInvalidHandle;
ResultCode Controller_NPad::SetSixAxisFusionEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
controller.sixaxis_fullkey.is_fusion_enabled = is_fusion_enabled;
break;
case Core::HID::NpadStyleIndex::Handheld:
controller.sixaxis_handheld.is_fusion_enabled = is_fusion_enabled;
break;
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::GameCube:
case Core::HID::NpadStyleIndex::Pokeball:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
controller.sixaxis_dual_left.is_fusion_enabled = is_fusion_enabled;
break;
}
controller.sixaxis_dual_right.is_fusion_enabled = is_fusion_enabled;
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
controller.sixaxis_left.is_fusion_enabled = is_fusion_enabled;
break;
case Core::HID::NpadStyleIndex::JoyconRight:
controller.sixaxis_right.is_fusion_enabled = is_fusion_enabled;
break;
default:
LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
return NpadInvalidHandle;
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
sixaxis.is_fusion_enabled = is_fusion_enabled;
return ResultSuccess;
}
ResultCode Controller_NPad::SetSixAxisFusionParameters(
Core::HID::SixAxisSensorHandle sixaxis_handle,
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
return NpadInvalidHandle;
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto param1 = sixaxis_fusion_parameters.parameter1;
if (param1 < 0.0f || param1 > 1.0f) {
return InvalidSixAxisFusionRange;
}
auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
controller.sixaxis_fullkey.fusion = sixaxis_fusion_parameters;
break;
case Core::HID::NpadStyleIndex::Handheld:
controller.sixaxis_handheld.fusion = sixaxis_fusion_parameters;
break;
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::GameCube:
case Core::HID::NpadStyleIndex::Pokeball:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
controller.sixaxis_dual_left.fusion = sixaxis_fusion_parameters;
break;
}
controller.sixaxis_dual_right.fusion = sixaxis_fusion_parameters;
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
controller.sixaxis_left.fusion = sixaxis_fusion_parameters;
break;
case Core::HID::NpadStyleIndex::JoyconRight:
controller.sixaxis_right.fusion = sixaxis_fusion_parameters;
break;
default:
LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
return NpadInvalidHandle;
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
sixaxis.fusion = sixaxis_fusion_parameters;
return ResultSuccess;
}
ResultCode Controller_NPad::GetSixAxisFusionParameters(
Core::HID::SixAxisSensorHandle sixaxis_handle,
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters& parameters) const {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
return NpadInvalidHandle;
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
parameters = controller.sixaxis_fullkey.fusion;
break;
case Core::HID::NpadStyleIndex::Handheld:
parameters = controller.sixaxis_handheld.fusion;
break;
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::GameCube:
case Core::HID::NpadStyleIndex::Pokeball:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
parameters = controller.sixaxis_dual_left.fusion;
break;
}
parameters = controller.sixaxis_dual_right.fusion;
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
parameters = controller.sixaxis_left.fusion;
break;
case Core::HID::NpadStyleIndex::JoyconRight:
parameters = controller.sixaxis_right.fusion;
break;
default:
LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
return NpadInvalidHandle;
}
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
parameters = sixaxis.fusion;
return ResultSuccess;
}
void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
Core::HID::NpadIdType npad_id_2) {
ResultCode Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
Core::HID::NpadIdType npad_id_2) {
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
npad_id_2);
return;
return InvalidNpadId;
}
auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
const auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
const auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
bool merge_controllers = false;
auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
// If the controllers at both npad indices form a pair of left and right joycons, merge them.
// Otherwise, do nothing.
// Simplify this code by converting dualjoycon with only a side connected to single joycons
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual) {
if (controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
controller_style_1 = Core::HID::NpadStyleIndex::JoyconLeft;
}
if (!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
controller_style_1 = Core::HID::NpadStyleIndex::JoyconRight;
}
}
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
if (controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
controller_style_2 = Core::HID::NpadStyleIndex::JoyconLeft;
}
if (!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
controller_style_2 = Core::HID::NpadStyleIndex::JoyconRight;
}
}
// Invalid merge errors
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual ||
controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
return NpadIsDualJoycon;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft) {
return NpadIsSameType;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
merge_controllers = true;
}
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) {
merge_controllers = true;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight &&
controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
merge_controllers = true;
return NpadIsSameType;
}
if (merge_controllers) {
// Disconnect the joycon at the second id and connect the dual joycon at the first index.
DisconnectNpad(npad_id_2);
controller_1.is_dual_left_connected = true;
controller_1.is_dual_right_connected = true;
AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
return;
// These exceptions are handled as if they where dual joycon
if (controller_style_1 != Core::HID::NpadStyleIndex::JoyconLeft &&
controller_style_1 != Core::HID::NpadStyleIndex::JoyconRight) {
return NpadIsDualJoycon;
}
LOG_WARNING(Service_HID,
"Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, "
"dual_1(left/right):{}/{}, dual_2(left/right):{}/{}",
npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(),
controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected,
controller_1.is_dual_right_connected, controller_2.is_dual_left_connected,
controller_2.is_dual_right_connected);
if (controller_style_2 != Core::HID::NpadStyleIndex::JoyconLeft &&
controller_style_2 != Core::HID::NpadStyleIndex::JoyconRight) {
return NpadIsDualJoycon;
}
// Disconnect the joycon at the second id and connect the dual joycon at the first index.
DisconnectNpad(npad_id_2);
controller_1.is_dual_left_connected = true;
controller_1.is_dual_right_connected = true;
AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
return ResultSuccess;
}
void Controller_NPad::StartLRAssignmentMode() {
@@ -1349,17 +1304,17 @@ void Controller_NPad::StopLRAssignmentMode() {
is_in_lr_assignment_mode = false;
}
bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
Core::HID::NpadIdType npad_id_2) {
ResultCode Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
Core::HID::NpadIdType npad_id_2) {
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
npad_id_2);
return false;
return InvalidNpadId;
}
if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
npad_id_2 == Core::HID::NpadIdType::Other) {
return true;
return ResultSuccess;
}
const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
@@ -1369,46 +1324,49 @@ bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
const auto is_connected_2 = controller_2->IsConnected();
if (!IsControllerSupported(type_index_1) && is_connected_1) {
return false;
return NpadNotConnected;
}
if (!IsControllerSupported(type_index_2) && is_connected_2) {
return false;
return NpadNotConnected;
}
UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
return true;
return ResultSuccess;
}
Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) {
ResultCode Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
Core::HID::LedPattern& pattern) const {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return Core::HID::LedPattern{0, 0, 0, 0};
return InvalidNpadId;
}
const auto& controller = GetControllerFromNpadIdType(npad_id).device;
return controller->GetLedPattern();
pattern = controller->GetLedPattern();
return ResultSuccess;
}
bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
Core::HID::NpadIdType npad_id) const {
ResultCode Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
Core::HID::NpadIdType npad_id, bool& is_valid) const {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
// Return the default value
return false;
return InvalidNpadId;
}
const auto& controller = GetControllerFromNpadIdType(npad_id);
return controller.unintended_home_button_input_protection;
is_valid = controller.unintended_home_button_input_protection;
return ResultSuccess;
}
void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
Core::HID::NpadIdType npad_id) {
ResultCode Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
bool is_protection_enabled, Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return;
return InvalidNpadId;
}
auto& controller = GetControllerFromNpadIdType(npad_id);
controller.unintended_home_button_input_protection = is_protection_enabled;
return ResultSuccess;
}
void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
@@ -1546,4 +1504,96 @@ const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpa
return controller_data[npad_index];
}
Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Pokeball:
return controller.shared_memory->sixaxis_fullkey_properties;
case Core::HID::NpadStyleIndex::Handheld:
return controller.shared_memory->sixaxis_handheld_properties;
case Core::HID::NpadStyleIndex::JoyconDual:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
return controller.shared_memory->sixaxis_dual_left_properties;
}
return controller.shared_memory->sixaxis_dual_right_properties;
case Core::HID::NpadStyleIndex::JoyconLeft:
return controller.shared_memory->sixaxis_left_properties;
case Core::HID::NpadStyleIndex::JoyconRight:
return controller.shared_memory->sixaxis_right_properties;
default:
return controller.shared_memory->sixaxis_fullkey_properties;
}
}
const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
const auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Pokeball:
return controller.shared_memory->sixaxis_fullkey_properties;
case Core::HID::NpadStyleIndex::Handheld:
return controller.shared_memory->sixaxis_handheld_properties;
case Core::HID::NpadStyleIndex::JoyconDual:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
return controller.shared_memory->sixaxis_dual_left_properties;
}
return controller.shared_memory->sixaxis_dual_right_properties;
case Core::HID::NpadStyleIndex::JoyconLeft:
return controller.shared_memory->sixaxis_left_properties;
case Core::HID::NpadStyleIndex::JoyconRight:
return controller.shared_memory->sixaxis_right_properties;
default:
return controller.shared_memory->sixaxis_fullkey_properties;
}
}
Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Pokeball:
return controller.sixaxis_fullkey;
case Core::HID::NpadStyleIndex::Handheld:
return controller.sixaxis_handheld;
case Core::HID::NpadStyleIndex::JoyconDual:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
return controller.sixaxis_dual_left;
}
return controller.sixaxis_dual_right;
case Core::HID::NpadStyleIndex::JoyconLeft:
return controller.sixaxis_left;
case Core::HID::NpadStyleIndex::JoyconRight:
return controller.sixaxis_right;
default:
return controller.sixaxis_unknown;
}
}
const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
const auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Pokeball:
return controller.sixaxis_fullkey;
case Core::HID::NpadStyleIndex::Handheld:
return controller.sixaxis_handheld;
case Core::HID::NpadStyleIndex::JoyconDual:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
return controller.sixaxis_dual_left;
}
return controller.sixaxis_dual_right;
case Core::HID::NpadStyleIndex::JoyconLeft:
return controller.sixaxis_left;
case Core::HID::NpadStyleIndex::JoyconRight:
return controller.sixaxis_right;
default:
return controller.sixaxis_unknown;
}
}
} // namespace Service::HID
+53 -22
View File
@@ -107,8 +107,8 @@ public:
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
NpadCommunicationMode GetNpadCommunicationMode() const;
void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
NpadJoyAssignmentMode assignment_mode);
ResultCode SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
NpadJoyAssignmentMode assignment_mode);
bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
const Core::HID::VibrationValue& vibration_value);
@@ -141,50 +141,65 @@ public:
void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
bool connected);
void DisconnectNpad(Core::HID::NpadIdType npad_id);
ResultCode DisconnectNpad(Core::HID::NpadIdType npad_id);
ResultCode SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
ResultCode SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
GyroscopeZeroDriftMode drift_mode);
ResultCode GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
ResultCode GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
GyroscopeZeroDriftMode& drift_mode) const;
ResultCode IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
ResultCode IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_at_rest) const;
ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(
Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const;
ResultCode SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
ResultCode EnableSixAxisSensorUnalteredPassthrough(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
ResultCode IsSixAxisSensorUnalteredPassthroughEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
ResultCode LoadSixAxisSensorCalibrationParameter(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
ResultCode GetSixAxisSensorIcInformation(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorIcInformation& ic_information) const;
ResultCode ResetIsSixAxisSensorDeviceNewlyAssigned(
const Core::HID::SixAxisSensorHandle& sixaxis_handle);
ResultCode SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool sixaxis_status);
ResultCode IsSixAxisSensorFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
ResultCode IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_fusion_enabled) const;
ResultCode SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
ResultCode SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool is_fusion_enabled);
ResultCode SetSixAxisFusionParameters(
Core::HID::SixAxisSensorHandle sixaxis_handle,
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
ResultCode GetSixAxisFusionParameters(
Core::HID::SixAxisSensorHandle sixaxis_handle,
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters& parameters) const;
Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
Core::HID::NpadIdType npad_id);
ResultCode GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
ResultCode IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
bool& is_enabled) const;
ResultCode SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
Core::HID::NpadIdType npad_id);
void SetAnalogStickUseCenterClamp(bool use_center_clamp);
void ClearAllConnectedControllers();
void DisconnectAllConnectedControllers();
void ConnectAllDisconnectedControllers();
void ClearAllControllers();
void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
ResultCode MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
Core::HID::NpadIdType npad_id_2);
void StartLRAssignmentMode();
void StopLRAssignmentMode();
bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
ResultCode SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
// Logical OR for all buttons presses on all controllers
// Specifically for cheat engine and other features.
Core::HID::NpadButton GetAndResetPressState();
static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle);
static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
static ResultCode VerifyValidSixAxisSensorHandle(
const Core::HID::SixAxisSensorHandle& device_handle);
private:
static constexpr std::size_t NPAD_COUNT = 10;
@@ -451,9 +466,13 @@ private:
NpadLuciaType lucia_type{};
NpadLagonType lagon_type{};
NpadLagerType lager_type{};
// FW 13.x Investigate there is some sort of bitflag related to joycons
INSERT_PADDING_BYTES(0x4);
INSERT_PADDING_BYTES(0xc08); // Unknown
Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties;
Core::HID::SixAxisSensorProperties sixaxis_handheld_properties;
Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties;
Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties;
Core::HID::SixAxisSensorProperties sixaxis_left_properties;
Core::HID::SixAxisSensorProperties sixaxis_right_properties;
INSERT_PADDING_BYTES(0xc06); // Unknown
};
static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
@@ -465,7 +484,10 @@ private:
struct SixaxisParameters {
bool is_fusion_enabled{true};
bool unaltered_passtrough{false};
Core::HID::SixAxisSensorFusionParameters fusion{};
Core::HID::SixAxisSensorCalibrationParameter calibration{};
Core::HID::SixAxisSensorIcInformation ic_information{};
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
};
@@ -491,6 +513,7 @@ private:
SixaxisParameters sixaxis_dual_right{};
SixaxisParameters sixaxis_left{};
SixaxisParameters sixaxis_right{};
SixaxisParameters sixaxis_unknown{};
// Current pad state
NPadGenericState npad_pad_state{};
@@ -522,6 +545,14 @@ private:
NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
const Core::HID::SixAxisSensorHandle& device_handle);
const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
const Core::HID::SixAxisSensorHandle& device_handle) const;
SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
const SixaxisParameters& GetSixaxisState(
const Core::HID::SixAxisSensorHandle& device_handle) const;
std::atomic<u64> press_state{};
std::array<NpadControllerData, NPAD_COUNT> controller_data{};
@@ -44,7 +44,6 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
for (std::size_t id = 0; id < MAX_FINGERS; id++) {
const auto& current_touch = touch_status[id];
auto& finger = fingers[id];
finger.position = current_touch.position;
finger.id = current_touch.id;
if (finger.attribute.start_touch) {
@@ -61,13 +60,18 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
if (!finger.pressed && current_touch.pressed) {
finger.attribute.start_touch.Assign(1);
finger.pressed = true;
finger.position = current_touch.position;
continue;
}
if (finger.pressed && !current_touch.pressed) {
finger.attribute.raw = 0;
finger.attribute.end_touch.Assign(1);
continue;
}
// Only update position if touch is not on a special frame
finger.position = current_touch.position;
}
std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
+4
View File
@@ -8,7 +8,11 @@
namespace Service::HID {
constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100};
constexpr ResultCode NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423};
constexpr ResultCode NpadIsDualJoycon{ErrorModule::HID, 601};
constexpr ResultCode NpadIsSameType{ErrorModule::HID, 602};
constexpr ResultCode InvalidNpadId{ErrorModule::HID, 709};
constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710};
} // namespace Service::HID
+189 -47
View File
@@ -257,12 +257,12 @@ Hid::Hid(Core::System& system_)
{81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
{82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
{83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
{84, nullptr, "EnableSixAxisSensorUnalteredPassthrough"},
{85, nullptr, "IsSixAxisSensorUnalteredPassthroughEnabled"},
{84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"},
{85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"},
{86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
{87, nullptr, "LoadSixAxisSensorCalibrationParameter"},
{88, nullptr, "GetSixAxisSensorIcInformation"},
{89, nullptr, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
{87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"},
{88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"},
{89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
{91, &Hid::ActivateGesture, "ActivateGesture"},
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -694,11 +694,7 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
rb.Push(result1);
return;
}
if (result2.IsError()) {
rb.Push(result2);
return;
}
rb.Push(ResultSuccess);
rb.Push(result2);
}
void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -821,6 +817,144 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
rb.Push(is_firmware_available);
}
void Hid::EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
bool enabled;
Core::HID::SixAxisSensorHandle sixaxis_handle;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result = controller.EnableSixAxisSensorUnalteredPassthrough(
parameters.sixaxis_handle, parameters.enabled);
LOG_WARNING(Service_HID,
"(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
"applet_resource_user_id={}",
parameters.enabled, parameters.sixaxis_handle.npad_type,
parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::HID::SixAxisSensorHandle sixaxis_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>()};
bool is_unaltered_sisxaxis_enabled{};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled(
parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
LOG_WARNING(
Service_HID,
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push(is_unaltered_sisxaxis_enabled);
}
void Hid::LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::HID::SixAxisSensorHandle sixaxis_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>()};
Core::HID::SixAxisSensorCalibrationParameter calibration{};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result =
controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
LOG_WARNING(
Service_HID,
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
if (result.IsSuccess()) {
ctx.WriteBuffer(calibration);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void Hid::GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::HID::SixAxisSensorHandle sixaxis_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>()};
Core::HID::SixAxisSensorIcInformation ic_information{};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result =
controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
LOG_WARNING(
Service_HID,
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
if (result.IsSuccess()) {
ctx.WriteBuffer(ic_information);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::HID::SixAxisSensorHandle sixaxis_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>()};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result =
controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
LOG_WARNING(
Service_HID,
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
@@ -948,27 +1082,29 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.DisconnectNpad(parameters.npad_id);
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result = controller.DisconnectNpad(parameters.npad_id);
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(result);
}
void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
Core::HID::LedPattern pattern{0, 0, 0, 0};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result = controller.GetLedPattern(npad_id, pattern);
LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
.GetLedPattern(npad_id)
.raw);
rb.Push(result);
rb.Push(pattern.raw);
}
void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
@@ -1028,15 +1164,16 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
Controller_NPad::NpadJoyAssignmentMode::Single);
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result =
controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(result);
}
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
@@ -1051,16 +1188,16 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
Controller_NPad::NpadJoyAssignmentMode::Single);
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result = controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
parameters.npad_id, parameters.applet_resource_user_id,
parameters.npad_joy_device_type);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(result);
}
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
@@ -1074,14 +1211,15 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result = controller.SetNpadMode(parameters.npad_id, {},
Controller_NPad::NpadJoyAssignmentMode::Dual);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(result);
}
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
@@ -1090,14 +1228,14 @@ void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
npad_id_1, npad_id_2, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(result);
}
void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
@@ -1157,19 +1295,14 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SwapNpadAssignment(npad_id_1, npad_id_2);
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2);
LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
npad_id_1, npad_id_2, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
if (res) {
rb.Push(ResultSuccess);
} else {
LOG_ERROR(Service_HID, "Npads are not connected!");
rb.Push(NpadNotConnected);
}
rb.Push(result);
}
void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
@@ -1183,13 +1316,17 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext
const auto parameters{rp.PopRaw<Parameters>()};
bool is_enabled = false;
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result =
controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
parameters.npad_id, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id));
rb.Push(result);
rb.Push(is_enabled);
}
void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) {
@@ -1204,9 +1341,9 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetUnintendedHomeButtonInputProtectionEnabled(
parameters.unintended_home_button_input_protection, parameters.npad_id);
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled(
parameters.unintended_home_button_input_protection, parameters.npad_id);
LOG_WARNING(Service_HID,
"(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
@@ -1215,7 +1352,7 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(result);
}
void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
@@ -1377,6 +1514,8 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto can_vibrate{rp.Pop<bool>()};
// nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
// by converting it to a bool
Settings::values.vibration_enabled.SetValue(can_vibrate);
LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
@@ -1388,9 +1527,12 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
// nnSDK checks if a float is greater than zero. We return the bool we stored earlier
const auto is_enabled = Settings::values.vibration_enabled.GetValue();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(Settings::values.vibration_enabled.GetValue());
rb.Push(is_enabled);
}
void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
+5
View File
@@ -113,6 +113,11 @@ private:
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx);
void EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx);
void LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx);
void GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx);
void ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx);
void ActivateGesture(Kernel::HLERequestContext& ctx);
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
+230 -19
View File
@@ -5,7 +5,9 @@
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#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/hid/errors.h"
#include "core/hle/service/hid/irs.h"
namespace Service::HID {
@@ -38,21 +40,32 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
}
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_IRS, "called");
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
@@ -60,35 +73,109 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
}
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
struct Parameters {
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>()};
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);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
struct Parameters {
IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
PackedMomentProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
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);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
struct Parameters {
IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
PackedClusteringProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
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);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
struct Parameters {
IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
PackedImageTransferProcessorConfig processor_config;
u32 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem =
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
LOG_WARNING(Service_IRS,
"(STUBBED) 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);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
struct Parameters {
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>()};
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);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
@@ -97,71 +184,195 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
}
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
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, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
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(InvalidNpadId);
return;
}
IrCameraHandle camera_handle{
.npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
.npad_type = Core::HID::NpadStyleIndex::None,
};
LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}",
npad_id, camera_handle.npad_id, camera_handle.npad_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw<u32>(device_handle);
rb.PushRaw(camera_handle);
}
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
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);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
struct Parameters {
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>()};
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);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
const auto mcu_version{rp.PopRaw<PackedMcuVersion>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
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,
mcu_version.minor);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
struct Parameters {
IrCameraHandle camera_handle;
PackedFunctionLevel function_level;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
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);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
struct Parameters {
IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
PackedImageTransferProcessorExConfig processor_config;
u64 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem =
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
LOG_WARNING(Service_IRS,
"(STUBBED) 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);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
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);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
struct Parameters {
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>()};
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);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_IRS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
struct Parameters {
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>()};
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);
+230 -2
View File
@@ -3,6 +3,7 @@
#pragma once
#include "core/hid/hid_types.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -17,6 +18,235 @@ public:
~IRS() override;
private:
// This is nn::irsensor::IrCameraStatus
enum IrCameraStatus : u32 {
Available,
Unsupported,
Unconnected,
};
// This is nn::irsensor::IrCameraInternalStatus
enum IrCameraInternalStatus : u32 {
Stopped,
FirmwareUpdateNeeded,
Unkown2,
Unkown3,
Unkown4,
FirmwareVersionRequested,
FirmwareVersionIsInvalid,
Ready,
Setting,
};
// This is nn::irsensor::detail::StatusManager::IrSensorMode
enum IrSensorMode : u64 {
None,
MomentProcessor,
ClusteringProcessor,
ImageTransferProcessor,
PointingProcessorMarker,
TeraPluginProcessor,
IrLedProcessor,
};
// This is nn::irsensor::ImageProcessorStatus
enum ImageProcessorStatus : u8 {
stopped,
running,
};
// This is nn::irsensor::ImageTransferProcessorFormat
enum ImageTransferProcessorFormat : u8 {
Size320x240,
Size160x120,
Size80x60,
Size40x30,
Size20x15,
};
// This is nn::irsensor::AdaptiveClusteringMode
enum AdaptiveClusteringMode : u8 {
StaticFov,
DynamicFov,
};
// This is nn::irsensor::AdaptiveClusteringTargetDistance
enum AdaptiveClusteringTargetDistance : u8 {
Near,
Middle,
Far,
};
// This is nn::irsensor::IrsHandAnalysisMode
enum IrsHandAnalysisMode : u8 {
Silhouette,
Image,
SilhoueteAndImage,
SilhuetteOnly,
};
// This is nn::irsensor::IrSensorFunctionLevel
enum IrSensorFunctionLevel : u8 {
unknown0,
unknown1,
unknown2,
unknown3,
unknown4,
};
// This is nn::irsensor::IrCameraHandle
struct IrCameraHandle {
u8 npad_id{};
Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
struct IrsRect {
s16 x;
s16 y;
s16 width;
s16 height;
};
// This is nn::irsensor::PackedMcuVersion
struct PackedMcuVersion {
u16 major;
u16 minor;
};
static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
// This is nn::irsensor::MomentProcessorConfig
struct MomentProcessorConfig {
u64 exposire_time;
u8 light_target;
u8 gain;
u8 is_negative_used;
INSERT_PADDING_BYTES(7);
IrsRect window_of_interest;
u8 preprocess;
u8 preprocess_intensity_threshold;
INSERT_PADDING_BYTES(5);
};
static_assert(sizeof(MomentProcessorConfig) == 0x28,
"MomentProcessorConfig is an invalid size");
// This is nn::irsensor::PackedMomentProcessorConfig
struct PackedMomentProcessorConfig {
u64 exposire_time;
u8 light_target;
u8 gain;
u8 is_negative_used;
INSERT_PADDING_BYTES(5);
IrsRect window_of_interest;
PackedMcuVersion required_mcu_version;
u8 preprocess;
u8 preprocess_intensity_threshold;
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
"PackedMomentProcessorConfig is an invalid size");
// This is nn::irsensor::ClusteringProcessorConfig
struct ClusteringProcessorConfig {
u64 exposire_time;
u32 light_target;
u32 gain;
u8 is_negative_used;
INSERT_PADDING_BYTES(7);
IrsRect window_of_interest;
u32 pixel_count_min;
u32 pixel_count_max;
u32 object_intensity_min;
u8 is_external_light_filter_enabled;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
"ClusteringProcessorConfig is an invalid size");
// This is nn::irsensor::PackedClusteringProcessorConfig
struct PackedClusteringProcessorConfig {
u64 exposire_time;
u8 light_target;
u8 gain;
u8 is_negative_used;
INSERT_PADDING_BYTES(5);
IrsRect window_of_interest;
PackedMcuVersion required_mcu_version;
u32 pixel_count_min;
u32 pixel_count_max;
u32 object_intensity_min;
u8 is_external_light_filter_enabled;
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30,
"PackedClusteringProcessorConfig is an invalid size");
// This is nn::irsensor::PackedImageTransferProcessorConfig
struct PackedImageTransferProcessorConfig {
u64 exposire_time;
u8 light_target;
u8 gain;
u8 is_negative_used;
INSERT_PADDING_BYTES(5);
PackedMcuVersion required_mcu_version;
u8 format;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
"PackedImageTransferProcessorConfig is an invalid size");
// This is nn::irsensor::PackedTeraPluginProcessorConfig
struct PackedTeraPluginProcessorConfig {
PackedMcuVersion required_mcu_version;
u8 mode;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
"PackedTeraPluginProcessorConfig is an invalid size");
// This is nn::irsensor::PackedPointingProcessorConfig
struct PackedPointingProcessorConfig {
IrsRect window_of_interest;
PackedMcuVersion required_mcu_version;
};
static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
"PackedPointingProcessorConfig is an invalid size");
// This is nn::irsensor::PackedFunctionLevel
struct PackedFunctionLevel {
IrSensorFunctionLevel function_level;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
// This is nn::irsensor::PackedImageTransferProcessorExConfig
struct PackedImageTransferProcessorExConfig {
u64 exposire_time;
u8 light_target;
u8 gain;
u8 is_negative_used;
INSERT_PADDING_BYTES(5);
PackedMcuVersion required_mcu_version;
ImageTransferProcessorFormat origin_format;
ImageTransferProcessorFormat trimming_format;
u16 trimming_start_x;
u16 trimming_start_y;
u8 is_external_light_filter_enabled;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
"PackedImageTransferProcessorExConfig is an invalid size");
// This is nn::irsensor::PackedIrLedProcessorConfig
struct PackedIrLedProcessorConfig {
PackedMcuVersion required_mcu_version;
u8 light_target;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
"PackedIrLedProcessorConfig is an invalid size");
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx);
@@ -35,8 +265,6 @@ private:
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
const u32 device_handle{0xABCD};
};
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
+8 -24
View File
@@ -11,10 +11,13 @@
#include "common/alignment.h"
#include "common/common_funcs.h"
#include "common/div_ceil.h"
#include "common/elf.h"
#include "common/logging/log.h"
#include "core/hle/service/jit/jit_context.h"
#include "core/memory.h"
using namespace Common::ELF;
namespace Service::JIT {
constexpr std::array<u8, 8> SVC0_ARM64 = {
@@ -26,25 +29,6 @@ constexpr std::array HELPER_FUNCTIONS{
"_stop", "_resolve", "_panic", "memcpy", "memmove", "memset",
};
struct Elf64_Dyn {
u64 d_tag;
u64 d_un;
};
struct Elf64_Rela {
u64 r_offset;
u64 r_info;
s64 r_addend;
};
static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) {
return static_cast<u32>(rela->r_info);
}
constexpr int DT_RELA = 7; /* Address of Rela relocs */
constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */
constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */
constexpr size_t STACK_ALIGN = 16;
class JITContextImpl;
@@ -206,17 +190,17 @@ public:
if (!dyn.d_tag) {
break;
}
if (dyn.d_tag == DT_RELA) {
rela_dyn = dyn.d_un;
if (dyn.d_tag == ElfDtRela) {
rela_dyn = dyn.d_un.d_ptr;
}
if (dyn.d_tag == DT_RELASZ) {
num_rela = dyn.d_un / sizeof(Elf64_Rela);
if (dyn.d_tag == ElfDtRelasz) {
num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela);
}
}
for (size_t i = 0; i < num_rela; i++) {
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) {
if (Elf64RelType(rela.r_info) != ElfAArch64Relative) {
continue;
}
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
@@ -89,14 +89,6 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
// If the front buffer is still being tracked, update its slot state
if (core->StillTracking(*front)) {
slots[slot].acquire_called = true;
slots[slot].needs_cleanup_on_release = false;
slots[slot].buffer_state = BufferState::Acquired;
slots[slot].fence = Fence::NoFence();
}
// If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
// avoid unnecessarily remapping this buffer on the consumer side.
if (out_buffer->acquire_called) {
@@ -139,26 +131,11 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
++current;
}
if (slots[slot].buffer_state == BufferState::Acquired) {
slots[slot].fence = release_fence;
slots[slot].buffer_state = BufferState::Free;
slots[slot].buffer_state = BufferState::Free;
listener = core->connected_producer_listener;
listener = core->connected_producer_listener;
LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
} else if (slots[slot].needs_cleanup_on_release) {
LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot,
slots[slot].buffer_state);
slots[slot].needs_cleanup_on_release = false;
return Status::StaleBufferSlot;
} else {
LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}",
slot, slots[slot].buffer_state);
return Status::BadValue;
}
LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
core->SignalDequeueCondition();
}
@@ -84,10 +84,6 @@ void BufferQueueCore::FreeBufferLocked(s32 slot) {
slots[slot].graphic_buffer.reset();
if (slots[slot].buffer_state == BufferState::Acquired) {
slots[slot].needs_cleanup_on_release = true;
}
slots[slot].buffer_state = BufferState::Free;
slots[slot].frame_number = UINT32_MAX;
slots[slot].acquire_called = false;
@@ -31,7 +31,6 @@ struct BufferSlot final {
u64 frame_number{};
Fence fence;
bool acquire_called{};
bool needs_cleanup_on_release{};
bool attached_by_consumer{};
bool is_preallocated{};
};
@@ -110,10 +110,9 @@ static constexpr s64 GetLeapDaysFromYear(s64 year) {
}
}
static constexpr int GetMonthLength(bool is_leap_year, int month) {
constexpr std::array<int, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
constexpr std::array<int, 12> month_lengths_leap{31, 29, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31};
static constexpr s8 GetMonthLength(bool is_leap_year, int month) {
constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
return is_leap_year ? month_lengths_leap[month] : month_lengths[month];
}
+16 -167
View File
@@ -6,6 +6,7 @@
#include <memory>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/elf.h"
#include "common/logging/log.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_page_table.h"
@@ -13,159 +14,7 @@
#include "core/loader/elf.h"
#include "core/memory.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// ELF Header Constants
// File type
enum ElfType {
ET_NONE = 0,
ET_REL = 1,
ET_EXEC = 2,
ET_DYN = 3,
ET_CORE = 4,
ET_LOPROC = 0xFF00,
ET_HIPROC = 0xFFFF,
};
// Machine/Architecture
enum ElfMachine {
EM_NONE = 0,
EM_M32 = 1,
EM_SPARC = 2,
EM_386 = 3,
EM_68K = 4,
EM_88K = 5,
EM_860 = 7,
EM_MIPS = 8
};
// File version
#define EV_NONE 0
#define EV_CURRENT 1
// Identification index
#define EI_MAG0 0
#define EI_MAG1 1
#define EI_MAG2 2
#define EI_MAG3 3
#define EI_CLASS 4
#define EI_DATA 5
#define EI_VERSION 6
#define EI_PAD 7
#define EI_NIDENT 16
// Sections constants
// Section types
#define SHT_NULL 0
#define SHT_PROGBITS 1
#define SHT_SYMTAB 2
#define SHT_STRTAB 3
#define SHT_RELA 4
#define SHT_HASH 5
#define SHT_DYNAMIC 6
#define SHT_NOTE 7
#define SHT_NOBITS 8
#define SHT_REL 9
#define SHT_SHLIB 10
#define SHT_DYNSYM 11
#define SHT_LOPROC 0x70000000
#define SHT_HIPROC 0x7FFFFFFF
#define SHT_LOUSER 0x80000000
#define SHT_HIUSER 0xFFFFFFFF
// Section flags
enum ElfSectionFlags {
SHF_WRITE = 0x1,
SHF_ALLOC = 0x2,
SHF_EXECINSTR = 0x4,
SHF_MASKPROC = 0xF0000000,
};
// Segment types
#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_SHLIB 5
#define PT_PHDR 6
#define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7FFFFFFF
// Segment flags
#define PF_X 0x1
#define PF_W 0x2
#define PF_R 0x4
#define PF_MASKPROC 0xF0000000
typedef unsigned int Elf32_Addr;
typedef unsigned short Elf32_Half;
typedef unsigned int Elf32_Off;
typedef signed int Elf32_Sword;
typedef unsigned int Elf32_Word;
////////////////////////////////////////////////////////////////////////////////////////////////////
// ELF file header
struct Elf32_Ehdr {
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
};
// Section header
struct Elf32_Shdr {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
};
// Segment header
struct Elf32_Phdr {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
};
// Symbol table entry
struct Elf32_Sym {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
};
// Relocation entries
struct Elf32_Rel {
Elf32_Addr r_offset;
Elf32_Word r_info;
};
using namespace Common::ELF;
////////////////////////////////////////////////////////////////////////////////////////////////////
// ElfReader class
@@ -193,11 +42,11 @@ public:
}
// Quick accessors
ElfType GetType() const {
return (ElfType)(header->e_type);
u16 GetType() const {
return header->e_type;
}
ElfMachine GetMachine() const {
return (ElfMachine)(header->e_machine);
u16 GetMachine() const {
return header->e_machine;
}
VAddr GetEntryPoint() const {
return entryPoint;
@@ -220,13 +69,13 @@ public:
const u8* GetSectionDataPtr(int section) const {
if (section < 0 || section >= header->e_shnum)
return nullptr;
if (sections[section].sh_type != SHT_NOBITS)
if (sections[section].sh_type != ElfShtNobits)
return GetPtr(sections[section].sh_offset);
else
return nullptr;
}
bool IsCodeSection(int section) const {
return sections[section].sh_type == SHT_PROGBITS;
return sections[section].sh_type == ElfShtProgBits;
}
const u8* GetSegmentPtr(int segment) {
return GetPtr(segments[segment].p_offset);
@@ -256,7 +105,7 @@ ElfReader::ElfReader(void* ptr) {
}
const char* ElfReader::GetSectionName(int section) const {
if (sections[section].sh_type == SHT_NULL)
if (sections[section].sh_type == ElfShtNull)
return nullptr;
int name_offset = sections[section].sh_name;
@@ -272,7 +121,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
// Should we relocate?
relocate = (header->e_type != ET_EXEC);
relocate = (header->e_type != ElfTypeExec);
if (relocate) {
LOG_DEBUG(Loader, "Relocatable module");
@@ -288,7 +137,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
u64 total_image_size = 0;
for (unsigned int i = 0; i < header->e_phnum; ++i) {
const Elf32_Phdr* p = &segments[i];
if (p->p_type == PT_LOAD) {
if (p->p_type == ElfPtLoad) {
total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
}
}
@@ -303,14 +152,14 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
p->p_vaddr, p->p_filesz, p->p_memsz);
if (p->p_type == PT_LOAD) {
if (p->p_type == ElfPtLoad) {
Kernel::CodeSet::Segment* codeset_segment;
u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X);
if (permission_flags == (PF_R | PF_X)) {
u32 permission_flags = p->p_flags & (ElfPfRead | ElfPfWrite | ElfPfExec);
if (permission_flags == (ElfPfRead | ElfPfExec)) {
codeset_segment = &codeset.CodeSegment();
} else if (permission_flags == (PF_R)) {
} else if (permission_flags == (ElfPfRead)) {
codeset_segment = &codeset.RODataSegment();
} else if (permission_flags == (PF_R | PF_W)) {
} else if (permission_flags == (ElfPfRead | ElfPfWrite)) {
codeset_segment = &codeset.DataSegment();
} else {
LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
+13
View File
@@ -594,6 +594,19 @@ bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
}
bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const {
VAddr end = base + size;
VAddr page = Common::AlignDown(base, PAGE_SIZE);
for (; page < end; page += PAGE_SIZE) {
if (!IsValidVirtualAddress(page)) {
return false;
}
}
return true;
}
u8* Memory::GetPointer(VAddr vaddr) {
return impl->GetPointer(vaddr);
}
+11
View File
@@ -95,6 +95,17 @@ public:
*/
[[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const;
/**
* Checks whether or not the supplied range of addresses are all valid
* virtual addresses for the current process.
*
* @param base The address to begin checking.
* @param size The amount of bytes to check.
*
* @returns True if all bytes in the given range are valid, false otherwise.
*/
[[nodiscard]] bool IsValidVirtualAddressRange(VAddr base, u64 size) const;
/**
* Gets a pointer to the given address.
*
+21 -2
View File
@@ -434,6 +434,7 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
using namespace std::chrono_literals;
while (initialized) {
SDL_PumpEvents();
SendVibrations();
std::this_thread::sleep_for(1ms);
}
});
@@ -531,13 +532,31 @@ Common::Input::VibrationError SDLDriver::SetRumble(
.type = Common::Input::VibrationAmplificationType::Exponential,
};
if (!joystick->RumblePlay(new_vibration)) {
return Common::Input::VibrationError::Unknown;
if (vibration.type == Common::Input::VibrationAmplificationType::Test) {
if (!joystick->RumblePlay(new_vibration)) {
return Common::Input::VibrationError::Unknown;
}
return Common::Input::VibrationError::None;
}
vibration_queue.Push(VibrationRequest{
.identifier = identifier,
.vibration = new_vibration,
});
return Common::Input::VibrationError::None;
}
void SDLDriver::SendVibrations() {
while (!vibration_queue.Empty()) {
VibrationRequest request;
vibration_queue.Pop(request);
const auto joystick = GetSDLJoystickByGUID(request.identifier.guid.RawString(),
static_cast<int>(request.identifier.port));
joystick->RumblePlay(request.vibration);
}
}
Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, std::string guid,
s32 axis, float value) const {
Common::ParamPackage params{};
+12
View File
@@ -12,6 +12,7 @@
#include <SDL.h>
#include "common/common_types.h"
#include "common/threadsafe_queue.h"
#include "input_common/input_engine.h"
union SDL_Event;
@@ -64,12 +65,20 @@ public:
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
private:
struct VibrationRequest {
PadIdentifier identifier;
Common::Input::VibrationStatus vibration;
};
void InitJoystick(int joystick_index);
void CloseJoystick(SDL_Joystick* sdl_joystick);
/// Needs to be called before SDL_QuitSubSystem.
void CloseJoysticks();
/// Takes all vibrations from the queue and sends the command to the controller
void SendVibrations();
Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
float value = 0.1f) const;
Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid,
@@ -107,6 +116,9 @@ private:
/// Returns true if the button is on the left joycon
bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const;
/// Queue of vibration request to controllers
Common::SPSCQueue<VibrationRequest> vibration_queue;
/// Map of GUID of a list of corresponding virtual Joysticks
std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
std::mutex joystick_map_mutex;
+72 -17
View File
@@ -14,38 +14,93 @@ constexpr PadIdentifier identifier = {
TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
PreSetController(identifier);
ReleaseAllTouch();
}
void TouchScreen::TouchMoved(float x, float y, std::size_t finger) {
if (finger >= 16) {
void TouchScreen::TouchMoved(float x, float y, std::size_t finger_id) {
const auto index = GetIndexFromFingerId(finger_id);
if (!index) {
// Touch doesn't exist handle it as a new one
TouchPressed(x, y, finger_id);
return;
}
TouchPressed(x, y, finger);
const auto i = index.value();
fingers[i].is_active = true;
SetButton(identifier, static_cast<int>(i), true);
SetAxis(identifier, static_cast<int>(i * 2), x);
SetAxis(identifier, static_cast<int>(i * 2 + 1), y);
}
void TouchScreen::TouchPressed(float x, float y, std::size_t finger) {
if (finger >= 16) {
void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) {
if (GetIndexFromFingerId(finger_id)) {
// Touch already exist. Just update the data
TouchMoved(x, y, finger_id);
return;
}
SetButton(identifier, static_cast<int>(finger), true);
SetAxis(identifier, static_cast<int>(finger * 2), x);
SetAxis(identifier, static_cast<int>(finger * 2 + 1), y);
const auto index = GetNextFreeIndex();
if (!index) {
// No free entries. Ignore input
return;
}
const auto i = index.value();
fingers[i].is_enabled = true;
fingers[i].finger_id = finger_id;
TouchMoved(x, y, finger_id);
}
void TouchScreen::TouchReleased(std::size_t finger) {
if (finger >= 16) {
void TouchScreen::TouchReleased(std::size_t finger_id) {
const auto index = GetIndexFromFingerId(finger_id);
if (!index) {
return;
}
SetButton(identifier, static_cast<int>(finger), false);
SetAxis(identifier, static_cast<int>(finger * 2), 0.0f);
SetAxis(identifier, static_cast<int>(finger * 2 + 1), 0.0f);
const auto i = index.value();
fingers[i].is_enabled = false;
SetButton(identifier, static_cast<int>(i), false);
SetAxis(identifier, static_cast<int>(i * 2), 0.0f);
SetAxis(identifier, static_cast<int>(i * 2 + 1), 0.0f);
}
std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const {
for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
const auto& finger = fingers[index];
if (!finger.is_enabled) {
continue;
}
if (finger.finger_id == finger_id) {
return index;
}
}
return std::nullopt;
}
std::optional<std::size_t> TouchScreen::GetNextFreeIndex() const {
for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
if (!fingers[index].is_enabled) {
return index;
}
}
return std::nullopt;
}
void TouchScreen::ClearActiveFlag() {
for (auto& finger : fingers) {
finger.is_active = false;
}
}
void TouchScreen::ReleaseInactiveTouch() {
for (const auto& finger : fingers) {
if (!finger.is_active) {
TouchReleased(finger.finger_id);
}
}
}
void TouchScreen::ReleaseAllTouch() {
for (int index = 0; index < 16; ++index) {
SetButton(identifier, index, false);
SetAxis(identifier, index * 2, 0.0f);
SetAxis(identifier, index * 2 + 1, 0.0f);
for (const auto& finger : fingers) {
if (finger.is_enabled) {
TouchReleased(finger.finger_id);
}
}
}
+38 -14
View File
@@ -3,41 +3,65 @@
#pragma once
#include <optional>
#include "input_common/input_engine.h"
namespace InputCommon {
/**
* A button device factory representing a keyboard. It receives keyboard events and forward them
* to all button devices it created.
* A touch device factory representing a touch screen. It receives touch events and forward them
* to all touch devices it created.
*/
class TouchScreen final : public InputEngine {
public:
explicit TouchScreen(std::string input_engine_);
/**
* Signals that mouse has moved.
* @param x the x-coordinate of the cursor
* @param y the y-coordinate of the cursor
* @param center_x the x-coordinate of the middle of the screen
* @param center_y the y-coordinate of the middle of the screen
* Signals that touch has moved and marks this touch point as active
* @param x new horizontal position
* @param y new vertical position
* @param finger_id of the touch point to be updated
*/
void TouchMoved(float x, float y, std::size_t finger);
void TouchMoved(float x, float y, std::size_t finger_id);
/**
* Sets the status of all buttons bound with the key to pressed
* @param key_code the code of the key to press
* Signals and creates a new touch point with this finger id
* @param x starting horizontal position
* @param y starting vertical position
* @param finger_id to be assigned to the new touch point
*/
void TouchPressed(float x, float y, std::size_t finger);
void TouchPressed(float x, float y, std::size_t finger_id);
/**
* Sets the status of all buttons bound with the key to released
* @param key_code the code of the key to release
* Signals and resets the touch point related to the this finger id
* @param finger_id to be released
*/
void TouchReleased(std::size_t finger);
void TouchReleased(std::size_t finger_id);
/// Resets the active flag for each touch point
void ClearActiveFlag();
/// Releases all touch that haven't been marked as active
void ReleaseInactiveTouch();
/// Resets all inputs to their initial value
void ReleaseAllTouch();
private:
static constexpr std::size_t MAX_FINGER_COUNT = 16;
struct TouchStatus {
std::size_t finger_id{};
bool is_enabled{};
bool is_active{};
};
std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
std::optional<std::size_t> GetNextFreeIndex() const;
std::array<TouchStatus, MAX_FINGER_COUNT> fingers{};
};
} // namespace InputCommon
+4
View File
@@ -277,3 +277,7 @@ else()
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
)
endif()
if (ARCHITECTURE_x86_64)
target_link_libraries(video_core PRIVATE dynarmic)
endif()
@@ -153,7 +153,7 @@ constexpr Vp9EntropyProbs default_probs{
.high_precision{128, 128},
};
constexpr std::array<s32, 256> norm_lut{
constexpr std::array<u8, 256> norm_lut{
0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -164,7 +164,7 @@ constexpr std::array<s32, 256> norm_lut{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
constexpr std::array<s32, 254> map_lut{
constexpr std::array<u8, 254> map_lut{
20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
@@ -232,7 +232,7 @@ constexpr std::array<s32, 254> map_lut{
std::max(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1));
}
return map_lut[index];
return static_cast<s32>(map_lut[index]);
}
} // Anonymous namespace
@@ -819,7 +819,7 @@ void VpxRangeEncoder::Write(bool bit, s32 probability) {
local_range = range - split;
}
s32 shift = norm_lut[local_range];
s32 shift = static_cast<s32>(norm_lut[local_range]);
local_range <<= shift;
count += shift;
+2 -2
View File
@@ -595,8 +595,8 @@ void Maxwell3D::DrawArrays() {
std::optional<u64> Maxwell3D::GetQueryResult() {
switch (regs.query.query_get.select) {
case Regs::QuerySelect::Zero:
return 0;
case Regs::QuerySelect::Payload:
return regs.query.query_sequence;
case Regs::QuerySelect::SamplesPassed:
// Deferred.
rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed,
+1 -1
View File
@@ -93,7 +93,7 @@ public:
};
enum class QuerySelect : u32 {
Zero = 0,
Payload = 0,
TimeElapsed = 2,
TransformFeedbackPrimitivesGenerated = 11,
PrimitivesGenerated = 18,
@@ -784,8 +784,8 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
});
} else {
// Front face defines both faces
scheduler.Record([ref = regs.stencil_back_func_ref, write_mask = regs.stencil_back_mask,
test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) {
scheduler.Record([ref = regs.stencil_front_func_ref, write_mask = regs.stencil_front_mask,
test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) {
cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref);
cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask);
cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, test_mask);
+3 -3
View File
@@ -147,7 +147,7 @@ enum class SurfaceTarget {
TextureCubeArray,
};
constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
1, // A8B8G8R8_UNORM
1, // A8B8G8R8_SNORM
1, // A8B8G8R8_SINT
@@ -249,7 +249,7 @@ constexpr u32 DefaultBlockWidth(PixelFormat format) {
return BLOCK_WIDTH_TABLE[static_cast<std::size_t>(format)];
}
constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
1, // A8B8G8R8_UNORM
1, // A8B8G8R8_SNORM
1, // A8B8G8R8_SINT
@@ -351,7 +351,7 @@ constexpr u32 DefaultBlockHeight(PixelFormat format) {
return BLOCK_HEIGHT_TABLE[static_cast<std::size_t>(format)];
}
constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
32, // A8B8G8R8_UNORM
32, // A8B8G8R8_SNORM
32, // A8B8G8R8_SINT
+17 -1
View File
@@ -566,7 +566,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
}
VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR workgroup_layout;
if (khr_workgroup_memory_explicit_layout) {
if (khr_workgroup_memory_explicit_layout && is_shader_int16_supported) {
workgroup_layout = {
.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR,
@@ -577,6 +577,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
.workgroupMemoryExplicitLayout16BitAccess = VK_TRUE,
};
SetNext(next, workgroup_layout);
} else if (khr_workgroup_memory_explicit_layout) {
// TODO(lat9nq): Find a proper fix for this
LOG_WARNING(Render_Vulkan, "Disabling VK_KHR_workgroup_memory_explicit_layout due to a "
"yuzu bug when host driver does not support 16-bit integers");
khr_workgroup_memory_explicit_layout = false;
}
VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
@@ -664,6 +669,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
const bool is_amd =
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
if (is_amd) {
// TODO(lat9nq): Add an upper bound when AMD fixes their VK_KHR_push_descriptor
const bool has_broken_push_descriptor = VK_VERSION_MAJOR(properties.driverVersion) == 2 &&
VK_VERSION_MINOR(properties.driverVersion) == 0 &&
VK_VERSION_PATCH(properties.driverVersion) >= 226;
if (khr_push_descriptor && has_broken_push_descriptor) {
LOG_WARNING(
Render_Vulkan,
"Disabling AMD driver 2.0.226 and later from broken VK_KHR_push_descriptor");
khr_push_descriptor = false;
}
// AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2.
sets_per_pool = 96;
// Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken.
+5 -1
View File
@@ -240,7 +240,7 @@ elseif(WIN32)
if(MSVC)
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
elseif(MINGW)
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-mwindows")
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
endif()
endif()
@@ -319,3 +319,7 @@ endif()
if (NOT APPLE)
target_compile_definitions(yuzu PRIVATE HAS_OPENGL)
endif()
if (ARCHITECTURE_x86_64)
target_link_libraries(yuzu PRIVATE dynarmic)
endif()
+5 -1
View File
@@ -19,7 +19,11 @@ AboutDialog::AboutDialog(QWidget* parent)
const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
ui->setupUi(this);
ui->labelLogo->setPixmap(QIcon::fromTheme(QStringLiteral("yuzu")).pixmap(200));
// Try and request the icon from Qt theme (Linux?)
const QIcon yuzu_logo = QIcon::fromTheme(QStringLiteral("org.yuzu_emu.yuzu"));
if (!yuzu_logo.isNull()) {
ui->labelLogo->setPixmap(yuzu_logo.pixmap(200));
}
ui->labelBuildInfo->setText(
ui->labelBuildInfo->text().arg(QString::fromStdString(yuzu_build_version),
QString::fromUtf8(Common::g_build_date).left(10)));
+14 -2
View File
@@ -26,8 +26,20 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../dist/qt_themes/default/default.qrc">:/icons/default/256x256/yuzu.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
@@ -152,7 +164,7 @@ p, li { white-space: pre-wrap; }
</layout>
</widget>
<resources>
<include location="../../dist/icons/icons.qrc"/>
<include location="../../dist/qt_themes_default/default/default.qrc"/>
</resources>
<connections>
<connection>
+15 -14
View File
@@ -411,11 +411,11 @@ void QtSoftwareKeyboardDialog::ShowTextCheckDialog(
break;
}
auto text = ui->topOSK->currentIndex() == 1
? ui->text_edit_osk->toPlainText().toStdU16String()
: ui->line_edit_osk->text().toStdU16String();
const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
: ui->line_edit_osk->text();
auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
emit SubmitNormalText(SwkbdResult::Ok, std::move(text), true);
emit SubmitNormalText(SwkbdResult::Ok, std::move(text_str), true);
break;
}
}
@@ -562,7 +562,7 @@ void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) {
return;
}
InlineTextInsertString(entered_text.toStdU16String());
InlineTextInsertString(Common::U16StringFromBuffer(entered_text.utf16(), entered_text.size()));
}
void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) {
@@ -1119,11 +1119,11 @@ void QtSoftwareKeyboardDialog::NormalKeyboardButtonClicked(QPushButton* button)
}
if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) {
auto text = ui->topOSK->currentIndex() == 1
? ui->text_edit_osk->toPlainText().toStdU16String()
: ui->line_edit_osk->text().toStdU16String();
const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
: ui->line_edit_osk->text();
auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
emit SubmitNormalText(SwkbdResult::Ok, std::move(text));
emit SubmitNormalText(SwkbdResult::Ok, std::move(text_str));
return;
}
@@ -1189,7 +1189,8 @@ void QtSoftwareKeyboardDialog::InlineKeyboardButtonClicked(QPushButton* button)
return;
}
InlineTextInsertString(button->text().toStdU16String());
const auto button_text = button->text();
InlineTextInsertString(Common::U16StringFromBuffer(button_text.utf16(), button_text.size()));
// Revert the keyboard to lowercase if the shift key is active.
if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) {
@@ -1282,11 +1283,11 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button
if (is_inline) {
emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
} else {
auto text = ui->topOSK->currentIndex() == 1
? ui->text_edit_osk->toPlainText().toStdU16String()
: ui->line_edit_osk->text().toStdU16String();
const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
: ui->line_edit_osk->text();
auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
emit SubmitNormalText(SwkbdResult::Cancel, std::move(text));
emit SubmitNormalText(SwkbdResult::Cancel, std::move(text_str));
}
break;
case Core::HID::NpadButton::Y:
+17 -52
View File
@@ -50,6 +50,7 @@ void EmuThread::run() {
auto& gpu = system.GPU();
auto stop_token = stop_source.get_token();
bool debugger_should_start = system.DebuggerEnabled();
system.RegisterHostThread();
@@ -89,6 +90,12 @@ void EmuThread::run() {
this->SetRunning(false);
emit ErrorThrown(result, system.GetStatusDetails());
}
if (debugger_should_start) {
system.InitializeDebugger();
debugger_should_start = false;
}
running_wait.Wait();
result = system.Pause();
if (result != Core::SystemResultStatus::Success) {
@@ -102,11 +109,9 @@ void EmuThread::run() {
was_active = true;
emit DebugModeEntered();
}
} else if (exec_step) {
UNIMPLEMENTED();
} else {
std::unique_lock lock{running_mutex};
running_cv.wait(lock, stop_token, [this] { return IsRunning() || exec_step; });
running_cv.wait(lock, stop_token, [this] { return IsRunning(); });
}
}
@@ -772,65 +777,25 @@ void GRenderWindow::wheelEvent(QWheelEvent* event) {
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
for (const auto& touch_point : touch_points) {
if (!TouchUpdate(touch_point)) {
TouchStart(touch_point);
}
const auto [x, y] = ScaleTouch(touch_point.pos());
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, touch_point.id());
}
}
void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
input_subsystem->GetTouchScreen()->ClearActiveFlag();
for (const auto& touch_point : touch_points) {
if (!TouchUpdate(touch_point)) {
TouchStart(touch_point);
}
}
// Release all inactive points
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
if (!TouchExist(touch_ids[id], touch_points)) {
touch_ids[id] = 0;
input_subsystem->GetTouchScreen()->TouchReleased(id);
}
const auto [x, y] = ScaleTouch(touch_point.pos());
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, touch_point.id());
}
input_subsystem->GetTouchScreen()->ReleaseInactiveTouch();
}
void GRenderWindow::TouchEndEvent() {
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
if (touch_ids[id] != 0) {
touch_ids[id] = 0;
input_subsystem->GetTouchScreen()->TouchReleased(id);
}
}
}
void GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) {
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
if (touch_ids[id] == 0) {
touch_ids[id] = touch_point.id() + 1;
const auto [x, y] = ScaleTouch(touch_point.pos());
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id);
}
}
}
bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) {
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) {
const auto [x, y] = ScaleTouch(touch_point.pos());
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id);
return true;
}
}
return false;
}
bool GRenderWindow::TouchExist(std::size_t id,
const QList<QTouchEvent::TouchPoint>& touch_points) const {
return std::any_of(touch_points.begin(), touch_points.end(), [id](const auto& point) {
return id == static_cast<std::size_t>(point.id() + 1);
});
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
}
bool GRenderWindow::event(QEvent* event) {
-16
View File
@@ -54,15 +54,6 @@ public:
*/
void run() override;
/**
* Steps the emulation thread by a single CPU instruction (if the CPU is not already running)
* @note This function is thread-safe
*/
void ExecStep() {
exec_step = true;
running_cv.notify_all();
}
/**
* Sets whether the emulation thread is running or not
* @param running Boolean value, set the emulation thread to running if true
@@ -99,7 +90,6 @@ public:
}
private:
bool exec_step = false;
bool running = false;
std::stop_source stop_source;
std::mutex running_mutex;
@@ -217,10 +207,6 @@ private:
void TouchUpdateEvent(const QTouchEvent* event);
void TouchEndEvent();
void TouchStart(const QTouchEvent::TouchPoint& touch_point);
bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point);
bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const;
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
bool InitializeOpenGL();
@@ -246,8 +232,6 @@ private:
bool first_frame = false;
InputCommon::TasInput::TasState last_tas_state;
std::array<std::size_t, 16> touch_ids{};
Core::System& system;
protected:
+5
View File
@@ -525,6 +525,9 @@ void Config::ReadDebuggingValues() {
// Intentionally not using the QT default setting as this is intended to be changed in the ini
Settings::values.record_frame_times =
qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
ReadBasicSetting(Settings::values.use_gdbstub);
ReadBasicSetting(Settings::values.gdbstub_port);
ReadBasicSetting(Settings::values.program_args);
ReadBasicSetting(Settings::values.dump_exefs);
ReadBasicSetting(Settings::values.dump_nso);
@@ -1095,6 +1098,8 @@ void Config::SaveDebuggingValues() {
// Intentionally not using the QT default setting as this is intended to be changed in the ini
qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
WriteBasicSetting(Settings::values.use_gdbstub);
WriteBasicSetting(Settings::values.gdbstub_port);
WriteBasicSetting(Settings::values.program_args);
WriteBasicSetting(Settings::values.dump_exefs);
WriteBasicSetting(Settings::values.dump_nso);
+8 -1
View File
@@ -24,13 +24,18 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent)
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LogDir));
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
});
connect(ui->toggle_gdbstub, &QCheckBox::toggled,
[&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); });
}
ConfigureDebug::~ConfigureDebug() = default;
void ConfigureDebug::SetConfiguration() {
const bool runtime_lock = !system.IsPoweredOn();
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue());
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue());
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue());
ui->toggle_console->setEnabled(runtime_lock);
ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
@@ -71,6 +76,8 @@ void ConfigureDebug::SetConfiguration() {
}
void ConfigureDebug::ApplyConfiguration() {
Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked();
Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
UISettings::values.show_console = ui->toggle_console->isChecked();
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
+54
View File
@@ -3,6 +3,60 @@
<class>ConfigureDebug</class>
<widget class="QWidget" name="ConfigureDebug">
<layout class="QVBoxLayout" name="verticalLayout_1">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Debugger</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<widget class="QCheckBox" name="toggle_gdbstub">
<property name="text">
<string>Enable GDB Stub</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="gdbport_spinbox">
<property name="minimum">
<number>1024</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
+35 -4
View File
@@ -198,6 +198,31 @@ static void RemoveCachedContents() {
Common::FS::RemoveDirRecursively(offline_system_data);
}
static QString PrettyProductName() {
#ifdef _WIN32
// After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
// With that notation change they changed the registry key used to denote the current version
QSettings windows_registry(
QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
QSettings::NativeFormat);
const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
if (release_id == QStringLiteral("2009")) {
const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
const QString display_version =
windows_registry.value(QStringLiteral("DisplayVersion")).toString();
const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
u32 version = 10;
if (current_build >= 22000) {
version = 11;
}
return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
.arg(QString::number(version), display_version, QString::number(current_build),
QString::number(ubr));
}
#endif
return QSysInfo::prettyProductName();
}
GMainWindow::GMainWindow()
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
@@ -259,7 +284,7 @@ GMainWindow::GMainWindow()
}
LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
#endif
LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
@@ -1015,6 +1040,10 @@ void GMainWindow::SetDefaultUIGeometry() {
void GMainWindow::RestoreUIState() {
setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
restoreGeometry(UISettings::values.geometry);
// Work-around because the games list isn't supposed to be full screen
if (isFullScreen()) {
showNormal();
}
restoreState(UISettings::values.state);
render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint);
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
@@ -1401,7 +1430,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success &&
type == StartGameType::Normal) {
// Load per game settings
const auto file_path = std::filesystem::path{filename.toStdU16String()};
const auto file_path =
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())};
const auto config_file_name = title_id == 0
? Common::FS::PathToUTF8String(file_path.filename())
: fmt::format("{:016X}", title_id);
@@ -1482,7 +1512,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
}
if (res != Loader::ResultStatus::Success || title_name.empty()) {
title_name = Common::FS::PathToUTF8String(
std::filesystem::path{filename.toStdU16String()}.filename());
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())}
.filename());
}
const bool is_64bit = system->Kernel().CurrentProcess()->Is64BitProcess();
const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)");
@@ -3150,7 +3181,7 @@ void GMainWindow::OnTasStateChanged() {
}
void GMainWindow::UpdateStatusBar() {
if (emu_thread == nullptr) {
if (emu_thread == nullptr || !system->IsPoweredOn()) {
status_bar_update_timer.stop();
return;
}
+1 -1
View File
@@ -93,7 +93,7 @@ void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
}
void EmuWindow_SDL2::OnFingerUp() {
input_subsystem->GetTouchScreen()->TouchReleased(0);
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
}
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {