Compare commits
64 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2b567dfd6 | ||
|
|
a093f3d47a | ||
|
|
4f600f746a | ||
|
|
360418f1a1 | ||
|
|
3bc7575c47 | ||
|
|
fde8dc1652 | ||
|
|
b8f83aa4bf | ||
|
|
85b1e17df6 | ||
|
|
4144c517a5 | ||
|
|
8ad5f2c506 | ||
|
|
2a3f84aaf2 | ||
|
|
030e6b3980 | ||
|
|
e8ad603cd9 | ||
|
|
d10464de30 | ||
|
|
64f68e9635 | ||
|
|
462ba1b360 | ||
|
|
4a86a55174 | ||
|
|
d590cfb9d0 | ||
|
|
ded419ef2b | ||
|
|
4c3f898789 | ||
|
|
46c259bb20 | ||
|
|
15bebf1695 | ||
|
|
5c840334b8 | ||
|
|
a05c242429 | ||
|
|
bd59934350 | ||
|
|
11b123ba01 | ||
|
|
24e7ace876 | ||
|
|
62586c1676 | ||
|
|
108737fcc6 | ||
|
|
abfebe5cc4 | ||
|
|
a22a025c5b | ||
|
|
a529ef4c09 | ||
|
|
875568bb3e | ||
|
|
988e557ec8 | ||
|
|
6d2af32f29 | ||
|
|
8f9d5c3143 | ||
|
|
dc0fb56f3a | ||
|
|
7ba4a8f4a3 | ||
|
|
8ef1fdafa2 | ||
|
|
d597383ab2 | ||
|
|
5feda37688 | ||
|
|
34e4012998 | ||
|
|
c1924951ad | ||
|
|
5646e313a0 | ||
|
|
f447996080 | ||
|
|
42b34a0dc5 | ||
|
|
fe5e4bd846 | ||
|
|
a53cd2854e | ||
|
|
1d731dd1ff | ||
|
|
8225ac004e | ||
|
|
52e6b8a2d3 | ||
|
|
13131e602f | ||
|
|
7761f29892 | ||
|
|
e92b10f971 | ||
|
|
9268f265a1 | ||
|
|
e445ef9d60 | ||
|
|
40bb176c39 | ||
|
|
d5de9402ee | ||
|
|
4cd3f9f4f9 | ||
|
|
6b7dc587cf | ||
|
|
f05cb69d4f | ||
|
|
382cf087a0 | ||
|
|
0751488727 | ||
|
|
4bc932261b |
3
.github/workflows/verify.yml
vendored
3
.github/workflows/verify.yml
vendored
@@ -79,7 +79,8 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew install autoconf automake boost@1.83 ccache ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@5 sdl2 speexdsp zlib zlib zstd
|
||||
# workaround for https://github.com/actions/setup-python/issues/577
|
||||
brew install autoconf automake boost@1.83 ccache ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@5 sdl2 speexdsp zlib zlib zstd || brew link --overwrite python@3.12
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
|
||||
19
dist/72-yuzu-input.rules
vendored
Normal file
19
dist/72-yuzu-input.rules
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Allow systemd-logind to manage user access to hidraw with this file
|
||||
# On most systems, this file should be installed to /etc/udev/rules.d/72-yuzu-input.rules
|
||||
# Consult your distro if this is not the case
|
||||
|
||||
# Switch Pro Controller (USB/Bluetooth)
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="2009", MODE="0660", TAG+="uaccess"
|
||||
KERNEL=="hidraw*", KERNELS=="*057e:2009*", MODE="0660", TAG+="uaccess"
|
||||
|
||||
# Joy-Con L (Bluetooth)
|
||||
KERNEL=="hidraw*", KERNELS=="*057e:2006*", MODE="0660", TAG+="uaccess"
|
||||
|
||||
# Joy-Con R (Bluetooth)
|
||||
KERNEL=="hidraw*", KERNELS=="*057e:2007*", MODE="0660", TAG+="uaccess"
|
||||
|
||||
# Joy-Con Charging Grip (USB)
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="200e", MODE="0660", TAG+="uaccess"
|
||||
2
externals/vcpkg
vendored
2
externals/vcpkg
vendored
Submodule externals/vcpkg updated: ef2eef1734...a42af01b72
@@ -174,7 +174,8 @@ android {
|
||||
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
|
||||
"-DYUZU_USE_BUNDLED_VCPKG=ON",
|
||||
"-DYUZU_USE_BUNDLED_FFMPEG=ON",
|
||||
"-DYUZU_ENABLE_LTO=ON"
|
||||
"-DYUZU_ENABLE_LTO=ON",
|
||||
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
|
||||
)
|
||||
|
||||
abiFilters("arm64-v8a", "x86_64")
|
||||
|
||||
@@ -14,8 +14,10 @@ import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.DialogAddFolderBinding
|
||||
import org.yuzu.yuzu_emu.model.GameDir
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
|
||||
class AddGameFolderDialogFragment : DialogFragment() {
|
||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||
private val gamesViewModel: GamesViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
@@ -30,6 +32,7 @@ class AddGameFolderDialogFragment : DialogFragment() {
|
||||
.setTitle(R.string.add_game_folder)
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
|
||||
val newGameDir = GameDir(folderUriString!!, binding.deepScanSwitch.isChecked)
|
||||
homeViewModel.setGamesDirSelected(true)
|
||||
gamesViewModel.addFolder(newGameDir)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@@ -75,6 +76,8 @@ class SetupFragment : Fragment() {
|
||||
return binding.root
|
||||
}
|
||||
|
||||
// This is using the correct scope, lint is just acting up
|
||||
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
mainActivity = requireActivity() as MainActivity
|
||||
|
||||
@@ -206,12 +209,24 @@ class SetupFragment : Fragment() {
|
||||
)
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
homeViewModel.shouldPageForward.collect {
|
||||
if (it) {
|
||||
pageForward()
|
||||
homeViewModel.setShouldPageForward(false)
|
||||
viewLifecycleOwner.lifecycleScope.apply {
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
homeViewModel.shouldPageForward.collect {
|
||||
if (it) {
|
||||
pageForward()
|
||||
homeViewModel.setShouldPageForward(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
homeViewModel.gamesDirSelected.collect {
|
||||
if (it) {
|
||||
gamesDirCallback.onStepCompleted()
|
||||
homeViewModel.setGamesDirSelected(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,7 +354,6 @@ class SetupFragment : Fragment() {
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
||||
if (result != null) {
|
||||
mainActivity.processGamesDir(result)
|
||||
gamesDirCallback.onStepCompleted()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ class GamesViewModel : ViewModel() {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
NativeConfig.addGameDir(gameDir)
|
||||
getGameDirs()
|
||||
getGameDirs(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ package org.yuzu.yuzu_emu.model
|
||||
import androidx.lifecycle.ViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
class HomeViewModel : ViewModel() {
|
||||
val navigationVisible: StateFlow<Pair<Boolean, Boolean>> get() = _navigationVisible
|
||||
@@ -17,6 +18,9 @@ class HomeViewModel : ViewModel() {
|
||||
val shouldPageForward: StateFlow<Boolean> get() = _shouldPageForward
|
||||
private val _shouldPageForward = MutableStateFlow(false)
|
||||
|
||||
private val _gamesDirSelected = MutableStateFlow(false)
|
||||
val gamesDirSelected get() = _gamesDirSelected.asStateFlow()
|
||||
|
||||
var navigatedToSetup = false
|
||||
|
||||
fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
|
||||
@@ -36,4 +40,8 @@ class HomeViewModel : ViewModel() {
|
||||
fun setShouldPageForward(pageForward: Boolean) {
|
||||
_shouldPageForward.value = pageForward
|
||||
}
|
||||
|
||||
fun setGamesDirSelected(selected: Boolean) {
|
||||
_gamesDirSelected.value = selected
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,9 +291,6 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
||||
// Initialize filesystem.
|
||||
ConfigureFilesystemProvider(filepath);
|
||||
|
||||
// Initialize account manager
|
||||
m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
|
||||
|
||||
// Load the ROM.
|
||||
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
|
||||
if (m_load_result != Core::SystemResultStatus::Success) {
|
||||
@@ -736,8 +733,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
|
||||
auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory(
|
||||
Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
|
||||
|
||||
Service::Account::ProfileManager manager;
|
||||
const auto user_id = manager.GetUser(static_cast<std::size_t>(0));
|
||||
const auto user_id = EmulationSession::GetInstance().System().GetProfileManager().GetUser(
|
||||
static_cast<std::size_t>(0));
|
||||
ASSERT(user_id);
|
||||
|
||||
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
||||
|
||||
@@ -73,7 +73,6 @@ private:
|
||||
std::atomic<bool> m_is_running = false;
|
||||
std::atomic<bool> m_is_paused = false;
|
||||
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
|
||||
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||
|
||||
// GPU driver parameters
|
||||
|
||||
@@ -123,6 +123,12 @@ namespace Common {
|
||||
return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u64 MakeMagic(char a, char b, char c, char d, char e, char f, char g,
|
||||
char h) {
|
||||
return u64(a) << 0 | u64(b) << 8 | u64(c) << 16 | u64(d) << 24 | u64(e) << 32 | u64(f) << 40 |
|
||||
u64(g) << 48 | u64(h) << 56;
|
||||
}
|
||||
|
||||
// std::size() does not support zero-size C arrays. We're fixing that.
|
||||
template <class C>
|
||||
constexpr auto Size(const C& c) -> decltype(c.size()) {
|
||||
|
||||
@@ -354,18 +354,36 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitPathComponents(std::string_view filename) {
|
||||
std::string copy(filename);
|
||||
std::replace(copy.begin(), copy.end(), '\\', '/');
|
||||
std::vector<std::string> out;
|
||||
|
||||
std::stringstream stream(copy);
|
||||
std::string item;
|
||||
while (std::getline(stream, item, '/')) {
|
||||
out.push_back(std::move(item));
|
||||
template <typename F>
|
||||
static void ForEachPathComponent(std::string_view filename, F&& cb) {
|
||||
const char* component_begin = filename.data();
|
||||
const char* const end = component_begin + filename.size();
|
||||
for (const char* it = component_begin; it != end; ++it) {
|
||||
const char c = *it;
|
||||
if (c == '\\' || c == '/') {
|
||||
if (component_begin != it) {
|
||||
cb(std::string_view{component_begin, it});
|
||||
}
|
||||
component_begin = it + 1;
|
||||
}
|
||||
}
|
||||
if (component_begin != end) {
|
||||
cb(std::string_view{component_begin, end});
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
std::vector<std::string_view> SplitPathComponents(std::string_view filename) {
|
||||
std::vector<std::string_view> components;
|
||||
ForEachPathComponent(filename, [&](auto component) { components.emplace_back(component); });
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitPathComponentsCopy(std::string_view filename) {
|
||||
std::vector<std::string> components;
|
||||
ForEachPathComponent(filename, [&](auto component) { components.emplace_back(component); });
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
|
||||
|
||||
@@ -289,7 +289,11 @@ enum class DirectorySeparator {
|
||||
|
||||
// Splits the path on '/' or '\' and put the components into a vector
|
||||
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
|
||||
[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);
|
||||
[[nodiscard]] std::vector<std::string_view> SplitPathComponents(std::string_view filename);
|
||||
|
||||
// Splits the path on '/' or '\' and put the components into a vector
|
||||
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
|
||||
[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
|
||||
|
||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
|
||||
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
|
||||
|
||||
@@ -11,10 +11,6 @@
|
||||
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <android/sharedmem.h>
|
||||
#endif
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
@@ -193,6 +189,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool ClearBackingRegion(size_t physical_offset, size_t length) {
|
||||
// TODO: This does not seem to be possible on Windows.
|
||||
return false;
|
||||
}
|
||||
|
||||
void EnableDirectMappedAddress() {
|
||||
// TODO
|
||||
UNREACHABLE();
|
||||
@@ -442,9 +443,7 @@ public:
|
||||
}
|
||||
|
||||
// Backing memory initialization
|
||||
#ifdef ANDROID
|
||||
fd = ASharedMemory_create("HostMemory", backing_size);
|
||||
#elif defined(__FreeBSD__) && __FreeBSD__ < 13
|
||||
#if defined(__FreeBSD__) && __FreeBSD__ < 13
|
||||
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
|
||||
fd = shm_open(SHM_ANON, O_RDWR, 0600);
|
||||
#else
|
||||
@@ -455,7 +454,6 @@ public:
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
#ifndef ANDROID
|
||||
// Defined to extend the file with zeros
|
||||
int ret = ftruncate(fd, backing_size);
|
||||
if (ret != 0) {
|
||||
@@ -463,7 +461,6 @@ public:
|
||||
strerror(errno));
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
#endif
|
||||
|
||||
backing_base = static_cast<u8*>(
|
||||
mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||
@@ -552,6 +549,19 @@ public:
|
||||
ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
|
||||
}
|
||||
|
||||
bool ClearBackingRegion(size_t physical_offset, size_t length) {
|
||||
#ifdef __linux__
|
||||
// Set MADV_REMOVE on backing map to destroy it instantly.
|
||||
// This also deletes the area from the backing file.
|
||||
int ret = madvise(backing_base + physical_offset, length, MADV_REMOVE);
|
||||
ASSERT_MSG(ret == 0, "madvise failed: {}", strerror(errno));
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void EnableDirectMappedAddress() {
|
||||
virtual_base = nullptr;
|
||||
}
|
||||
@@ -623,6 +633,10 @@ public:
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {}
|
||||
|
||||
bool ClearBackingRegion(size_t physical_offset, size_t length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void EnableDirectMappedAddress() {}
|
||||
|
||||
u8* backing_base{nullptr};
|
||||
@@ -698,6 +712,12 @@ void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool w
|
||||
impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
|
||||
}
|
||||
|
||||
void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value) {
|
||||
if (!impl || fill_value != 0 || !impl->ClearBackingRegion(physical_offset, length)) {
|
||||
std::memset(backing_base + physical_offset, fill_value, length);
|
||||
}
|
||||
}
|
||||
|
||||
void HostMemory::EnableDirectMappedAddress() {
|
||||
if (impl) {
|
||||
impl->EnableDirectMappedAddress();
|
||||
|
||||
@@ -48,6 +48,8 @@ public:
|
||||
|
||||
void EnableDirectMappedAddress();
|
||||
|
||||
void ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value);
|
||||
|
||||
[[nodiscard]] u8* BackingBasePointer() noexcept {
|
||||
return backing_base;
|
||||
}
|
||||
|
||||
@@ -160,12 +160,16 @@ static bool is_nce_enabled = false;
|
||||
|
||||
void SetNceEnabled(bool is_39bit) {
|
||||
const bool is_nce_selected = values.cpu_backend.GetValue() == CpuBackend::Nce;
|
||||
is_nce_enabled = IsFastmemEnabled() && is_nce_selected && is_39bit;
|
||||
if (is_nce_selected && !is_nce_enabled) {
|
||||
if (is_nce_selected && !IsFastmemEnabled()) {
|
||||
LOG_WARNING(Common, "Fastmem is required to natively execute code in a performant manner, "
|
||||
"falling back to Dynarmic");
|
||||
}
|
||||
if (is_nce_selected && !is_39bit) {
|
||||
LOG_WARNING(
|
||||
Common,
|
||||
"Program does not utilize 39-bit address space, unable to natively execute code");
|
||||
}
|
||||
is_nce_enabled = IsFastmemEnabled() && is_nce_selected && is_39bit;
|
||||
}
|
||||
|
||||
bool IsNceEnabled() {
|
||||
|
||||
@@ -180,14 +180,20 @@ struct Values {
|
||||
&use_speed_limit};
|
||||
|
||||
// Cpu
|
||||
SwitchableSetting<CpuBackend, true> cpu_backend{
|
||||
linkage, CpuBackend::Dynarmic, CpuBackend::Dynarmic,
|
||||
SwitchableSetting<CpuBackend, true> cpu_backend{linkage,
|
||||
#ifdef HAS_NCE
|
||||
CpuBackend::Nce,
|
||||
CpuBackend::Nce,
|
||||
#else
|
||||
CpuBackend::Dynarmic,
|
||||
#endif
|
||||
"cpu_backend", Category::Cpu};
|
||||
CpuBackend::Dynarmic,
|
||||
#ifdef HAS_NCE
|
||||
CpuBackend::Nce,
|
||||
#else
|
||||
CpuBackend::Dynarmic,
|
||||
#endif
|
||||
"cpu_backend",
|
||||
Category::Cpu};
|
||||
SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
|
||||
CpuAccuracy::Auto, CpuAccuracy::Paranoid,
|
||||
"cpu_accuracy", Category::Cpu};
|
||||
|
||||
@@ -251,10 +251,16 @@ add_library(core STATIC
|
||||
hle/kernel/k_hardware_timer.h
|
||||
hle/kernel/k_interrupt_manager.cpp
|
||||
hle/kernel/k_interrupt_manager.h
|
||||
hle/kernel/k_light_client_session.cpp
|
||||
hle/kernel/k_light_client_session.h
|
||||
hle/kernel/k_light_condition_variable.cpp
|
||||
hle/kernel/k_light_condition_variable.h
|
||||
hle/kernel/k_light_lock.cpp
|
||||
hle/kernel/k_light_lock.h
|
||||
hle/kernel/k_light_server_session.cpp
|
||||
hle/kernel/k_light_server_session.h
|
||||
hle/kernel/k_light_session.cpp
|
||||
hle/kernel/k_light_session.h
|
||||
hle/kernel/k_memory_block.h
|
||||
hle/kernel/k_memory_block_manager.cpp
|
||||
hle/kernel/k_memory_block_manager.h
|
||||
@@ -543,6 +549,13 @@ add_library(core STATIC
|
||||
hle/service/hid/xcd.cpp
|
||||
hle/service/hid/xcd.h
|
||||
hle/service/hid/errors.h
|
||||
hle/service/hid/controllers/types/debug_pad_types.h
|
||||
hle/service/hid/controllers/types/keyboard_types.h
|
||||
hle/service/hid/controllers/types/mouse_types.h
|
||||
hle/service/hid/controllers/types/npad_types.h
|
||||
hle/service/hid/controllers/types/touch_types.h
|
||||
hle/service/hid/controllers/applet_resource.cpp
|
||||
hle/service/hid/controllers/applet_resource.h
|
||||
hle/service/hid/controllers/console_six_axis.cpp
|
||||
hle/service/hid/controllers/console_six_axis.h
|
||||
hle/service/hid/controllers/controller_base.cpp
|
||||
@@ -561,14 +574,15 @@ add_library(core STATIC
|
||||
hle/service/hid/controllers/palma.h
|
||||
hle/service/hid/controllers/seven_six_axis.cpp
|
||||
hle/service/hid/controllers/seven_six_axis.h
|
||||
hle/service/hid/controllers/shared_memory_format.h
|
||||
hle/service/hid/controllers/shared_memory_holder.cpp
|
||||
hle/service/hid/controllers/shared_memory_holder.h
|
||||
hle/service/hid/controllers/six_axis.cpp
|
||||
hle/service/hid/controllers/six_axis.h
|
||||
hle/service/hid/controllers/stubbed.cpp
|
||||
hle/service/hid/controllers/stubbed.h
|
||||
hle/service/hid/controllers/touchscreen.cpp
|
||||
hle/service/hid/controllers/touchscreen.h
|
||||
hle/service/hid/controllers/xpad.cpp
|
||||
hle/service/hid/controllers/xpad.h
|
||||
hle/service/hid/hidbus/hidbus_base.cpp
|
||||
hle/service/hid/hidbus/hidbus_base.h
|
||||
hle/service/hid/hidbus/ringcon.cpp
|
||||
@@ -764,12 +778,24 @@ add_library(core STATIC
|
||||
hle/service/kernel_helpers.h
|
||||
hle/service/mutex.cpp
|
||||
hle/service/mutex.h
|
||||
hle/service/ro/ro_nro_utils.cpp
|
||||
hle/service/ro/ro_nro_utils.h
|
||||
hle/service/ro/ro_results.h
|
||||
hle/service/ro/ro_types.h
|
||||
hle/service/ro/ro.cpp
|
||||
hle/service/ro/ro.h
|
||||
hle/service/server_manager.cpp
|
||||
hle/service/server_manager.h
|
||||
hle/service/service.cpp
|
||||
hle/service/service.h
|
||||
hle/service/set/set.cpp
|
||||
hle/service/set/set.h
|
||||
hle/service/set/appln_settings.cpp
|
||||
hle/service/set/appln_settings.h
|
||||
hle/service/set/device_settings.cpp
|
||||
hle/service/set/device_settings.h
|
||||
hle/service/set/private_settings.cpp
|
||||
hle/service/set/private_settings.h
|
||||
hle/service/set/set_cal.cpp
|
||||
hle/service/set/set_cal.h
|
||||
hle/service/set/set_fd.cpp
|
||||
@@ -778,6 +804,8 @@ add_library(core STATIC
|
||||
hle/service/set/set_sys.h
|
||||
hle/service/set/settings.cpp
|
||||
hle/service/set/settings.h
|
||||
hle/service/set/system_settings.cpp
|
||||
hle/service/set/system_settings.h
|
||||
hle/service/sm/sm.cpp
|
||||
hle/service/sm/sm.h
|
||||
hle/service/sm/sm_controller.cpp
|
||||
@@ -933,15 +961,19 @@ if (HAS_NCE)
|
||||
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
|
||||
|
||||
target_sources(core PRIVATE
|
||||
arm/nce/arm_nce_asm_definitions.h
|
||||
arm/nce/arm_nce.cpp
|
||||
arm/nce/arm_nce.h
|
||||
arm/nce/arm_nce.s
|
||||
arm/nce/guest_context.h
|
||||
arm/nce/instructions.h
|
||||
arm/nce/interpreter_visitor.cpp
|
||||
arm/nce/interpreter_visitor.h
|
||||
arm/nce/patcher.cpp
|
||||
arm/nce/patcher.h
|
||||
arm/nce/instructions.h
|
||||
arm/nce/visitor_base.h
|
||||
)
|
||||
target_link_libraries(core PRIVATE merry::oaknut)
|
||||
target_link_libraries(core PRIVATE merry::mcl merry::oaknut)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
|
||||
@@ -282,6 +282,8 @@ Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) {
|
||||
|
||||
// Ignore leading directories.
|
||||
char* path_pointer = module_path.path.data();
|
||||
char* path_end =
|
||||
path_pointer + std::min(PathLengthMax, module_path.path_length);
|
||||
|
||||
for (s32 i = 0; i < std::min(PathLengthMax, module_path.path_length) &&
|
||||
module_path.path[i] != '\0';
|
||||
@@ -292,7 +294,8 @@ Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) {
|
||||
}
|
||||
|
||||
// Insert output.
|
||||
modules.emplace(svc_mem_info.base_address, path_pointer);
|
||||
modules.emplace(svc_mem_info.base_address,
|
||||
std::string_view(path_pointer, path_end));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "common/signal_chain.h"
|
||||
#include "core/arm/nce/arm_nce.h"
|
||||
#include "core/arm/nce/guest_context.h"
|
||||
#include "core/arm/nce/interpreter_visitor.h"
|
||||
#include "core/arm/nce/patcher.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
@@ -21,7 +21,8 @@ namespace Core {
|
||||
|
||||
namespace {
|
||||
|
||||
struct sigaction g_orig_action;
|
||||
struct sigaction g_orig_bus_action;
|
||||
struct sigaction g_orig_segv_action;
|
||||
|
||||
// Verify assembly offsets.
|
||||
using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
|
||||
@@ -37,6 +38,9 @@ fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
|
||||
return reinterpret_cast<fpsimd_context*>(header);
|
||||
}
|
||||
|
||||
using namespace Common::Literals;
|
||||
constexpr u32 StackSize = 32_KiB;
|
||||
|
||||
} // namespace
|
||||
|
||||
void* ArmNce::RestoreGuestContext(void* raw_context) {
|
||||
@@ -104,19 +108,10 @@ void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) {
|
||||
host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0);
|
||||
}
|
||||
|
||||
bool ArmNce::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||
bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
auto* info = static_cast<siginfo_t*>(raw_info);
|
||||
|
||||
// Try to handle an invalid access.
|
||||
// TODO: handle accesses which split a page?
|
||||
const Common::ProcessAddress addr =
|
||||
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
|
||||
if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
|
||||
// We handled the access successfully and are returning to guest code.
|
||||
return true;
|
||||
}
|
||||
|
||||
// We can't handle the access, so determine why we crashed.
|
||||
const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
|
||||
|
||||
@@ -143,8 +138,44 @@ bool ArmNce::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw
|
||||
return false;
|
||||
}
|
||||
|
||||
void ArmNce::HandleHostFault(int sig, void* raw_info, void* raw_context) {
|
||||
return g_orig_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
|
||||
bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
auto* fpctx = GetFloatingPointState(host_ctx);
|
||||
auto& memory = guest_ctx->system->ApplicationMemory();
|
||||
|
||||
// Match and execute an instruction.
|
||||
auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx, fpctx);
|
||||
if (next_pc) {
|
||||
host_ctx.pc = *next_pc;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We couldn't handle the access.
|
||||
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
|
||||
}
|
||||
|
||||
bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||
auto* info = static_cast<siginfo_t*>(raw_info);
|
||||
|
||||
// Try to handle an invalid access.
|
||||
// TODO: handle accesses which split a page?
|
||||
const Common::ProcessAddress addr =
|
||||
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
|
||||
if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
|
||||
// We handled the access successfully and are returning to guest code.
|
||||
return true;
|
||||
}
|
||||
|
||||
// We couldn't handle the access.
|
||||
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
|
||||
}
|
||||
|
||||
void ArmNce::HandleHostAlignmentFault(int sig, void* raw_info, void* raw_context) {
|
||||
return g_orig_bus_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
|
||||
}
|
||||
|
||||
void ArmNce::HandleHostAccessFault(int sig, void* raw_info, void* raw_context) {
|
||||
return g_orig_segv_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
|
||||
}
|
||||
|
||||
void ArmNce::LockThread(Kernel::KThread* thread) {
|
||||
@@ -225,18 +256,31 @@ ArmNce::ArmNce(System& system, bool uses_wall_clock, std::size_t core_index)
|
||||
ArmNce::~ArmNce() = default;
|
||||
|
||||
void ArmNce::Initialize() {
|
||||
m_thread_id = gettid();
|
||||
if (m_thread_id == -1) {
|
||||
m_thread_id = gettid();
|
||||
}
|
||||
|
||||
// Setup our signals
|
||||
static std::once_flag signals;
|
||||
std::call_once(signals, [] {
|
||||
// Configure signal stack.
|
||||
if (!m_stack) {
|
||||
m_stack = std::make_unique<u8[]>(StackSize);
|
||||
|
||||
stack_t ss{};
|
||||
ss.ss_sp = m_stack.get();
|
||||
ss.ss_size = StackSize;
|
||||
sigaltstack(&ss, nullptr);
|
||||
}
|
||||
|
||||
// Set up signals.
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [] {
|
||||
using HandlerType = decltype(sigaction::sa_sigaction);
|
||||
|
||||
sigset_t signal_mask;
|
||||
sigemptyset(&signal_mask);
|
||||
sigaddset(&signal_mask, ReturnToRunCodeByExceptionLevelChangeSignal);
|
||||
sigaddset(&signal_mask, BreakFromRunCodeSignal);
|
||||
sigaddset(&signal_mask, GuestFaultSignal);
|
||||
sigaddset(&signal_mask, GuestAlignmentFaultSignal);
|
||||
sigaddset(&signal_mask, GuestAccessFaultSignal);
|
||||
|
||||
struct sigaction return_to_run_code_action {};
|
||||
return_to_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
@@ -253,18 +297,19 @@ void ArmNce::Initialize() {
|
||||
break_from_run_code_action.sa_mask = signal_mask;
|
||||
Common::SigAction(BreakFromRunCodeSignal, &break_from_run_code_action, nullptr);
|
||||
|
||||
struct sigaction fault_action {};
|
||||
fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
|
||||
fault_action.sa_sigaction = reinterpret_cast<HandlerType>(&ArmNce::GuestFaultSignalHandler);
|
||||
fault_action.sa_mask = signal_mask;
|
||||
Common::SigAction(GuestFaultSignal, &fault_action, &g_orig_action);
|
||||
struct sigaction alignment_fault_action {};
|
||||
alignment_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
alignment_fault_action.sa_sigaction =
|
||||
reinterpret_cast<HandlerType>(&ArmNce::GuestAlignmentFaultSignalHandler);
|
||||
alignment_fault_action.sa_mask = signal_mask;
|
||||
Common::SigAction(GuestAlignmentFaultSignal, &alignment_fault_action, nullptr);
|
||||
|
||||
// Simplify call for g_orig_action.
|
||||
// These fields occupy the same space in memory, so this should be a no-op in practice.
|
||||
if (!(g_orig_action.sa_flags & SA_SIGINFO)) {
|
||||
g_orig_action.sa_sigaction =
|
||||
reinterpret_cast<decltype(g_orig_action.sa_sigaction)>(g_orig_action.sa_handler);
|
||||
}
|
||||
struct sigaction access_fault_action {};
|
||||
access_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
|
||||
access_fault_action.sa_sigaction =
|
||||
reinterpret_cast<HandlerType>(&ArmNce::GuestAccessFaultSignalHandler);
|
||||
access_fault_action.sa_mask = signal_mask;
|
||||
Common::SigAction(GuestAccessFaultSignal, &access_fault_action, &g_orig_segv_action);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,8 @@ private:
|
||||
static void ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info,
|
||||
void* raw_context);
|
||||
static void BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context);
|
||||
static void GuestFaultSignalHandler(int sig, void* info, void* raw_context);
|
||||
static void GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context);
|
||||
static void GuestAccessFaultSignalHandler(int sig, void* info, void* raw_context);
|
||||
|
||||
static void LockThreadParameters(void* tpidr);
|
||||
static void UnlockThreadParameters(void* tpidr);
|
||||
@@ -70,8 +71,11 @@ private:
|
||||
// C++ implementation functions for assembly definitions.
|
||||
static void* RestoreGuestContext(void* raw_context);
|
||||
static void SaveGuestContext(GuestContext* ctx, void* raw_context);
|
||||
static bool HandleGuestFault(GuestContext* ctx, void* info, void* raw_context);
|
||||
static void HandleHostFault(int sig, void* info, void* raw_context);
|
||||
static bool HandleFailedGuestFault(GuestContext* ctx, void* info, void* raw_context);
|
||||
static bool HandleGuestAlignmentFault(GuestContext* ctx, void* info, void* raw_context);
|
||||
static bool HandleGuestAccessFault(GuestContext* ctx, void* info, void* raw_context);
|
||||
static void HandleHostAlignmentFault(int sig, void* info, void* raw_context);
|
||||
static void HandleHostAccessFault(int sig, void* info, void* raw_context);
|
||||
|
||||
public:
|
||||
Core::System& m_system;
|
||||
@@ -83,6 +87,9 @@ public:
|
||||
// Core context.
|
||||
GuestContext m_guest_ctx{};
|
||||
Kernel::KThread* m_running_thread{};
|
||||
|
||||
// Stack for signal processing.
|
||||
std::unique_ptr<u8[]> m_stack{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -130,11 +130,11 @@ _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
|
||||
ret
|
||||
|
||||
|
||||
/* static void Core::ArmNce::GuestFaultSignalHandler(int sig, void* info, void* raw_context) */
|
||||
.section .text._ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_, "ax", %progbits
|
||||
.global _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_
|
||||
.type _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_, %function
|
||||
_ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
|
||||
/* static void Core::ArmNce::GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context) */
|
||||
.section .text._ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, "ax", %progbits
|
||||
.global _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_
|
||||
.type _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, %function
|
||||
_ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
|
||||
/* Check to see if we have the correct TLS magic. */
|
||||
mrs x8, tpidr_el0
|
||||
ldr w9, [x8, #(TpidrEl0TlsMagic)]
|
||||
@@ -146,7 +146,7 @@ _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
|
||||
|
||||
/* Incorrect TLS magic, so this is a host fault. */
|
||||
/* Tail call the handler. */
|
||||
b _ZN4Core6ArmNce15HandleHostFaultEiPvS1_
|
||||
b _ZN4Core6ArmNce24HandleHostAlignmentFaultEiPvS1_
|
||||
|
||||
1:
|
||||
/* Correct TLS magic, so this is a guest fault. */
|
||||
@@ -163,7 +163,53 @@ _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
|
||||
msr tpidr_el0, x3
|
||||
|
||||
/* Call the handler. */
|
||||
bl _ZN4Core6ArmNce16HandleGuestFaultEPNS_12GuestContextEPvS3_
|
||||
bl _ZN4Core6ArmNce25HandleGuestAlignmentFaultEPNS_12GuestContextEPvS3_
|
||||
|
||||
/* If the handler returned false, we want to preserve the host tpidr_el0. */
|
||||
cbz x0, 2f
|
||||
|
||||
/* Otherwise, restore guest tpidr_el0. */
|
||||
msr tpidr_el0, x19
|
||||
|
||||
2:
|
||||
ldr x19, [sp, #0x10]
|
||||
ldp x29, x30, [sp], #0x20
|
||||
ret
|
||||
|
||||
/* static void Core::ArmNce::GuestAccessFaultSignalHandler(int sig, void* info, void* raw_context) */
|
||||
.section .text._ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_, "ax", %progbits
|
||||
.global _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_
|
||||
.type _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_, %function
|
||||
_ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
|
||||
/* Check to see if we have the correct TLS magic. */
|
||||
mrs x8, tpidr_el0
|
||||
ldr w9, [x8, #(TpidrEl0TlsMagic)]
|
||||
|
||||
LOAD_IMMEDIATE_32(w10, TlsMagic)
|
||||
|
||||
cmp w9, w10
|
||||
b.eq 1f
|
||||
|
||||
/* Incorrect TLS magic, so this is a host fault. */
|
||||
/* Tail call the handler. */
|
||||
b _ZN4Core6ArmNce21HandleHostAccessFaultEiPvS1_
|
||||
|
||||
1:
|
||||
/* Correct TLS magic, so this is a guest fault. */
|
||||
stp x29, x30, [sp, #-0x20]!
|
||||
str x19, [sp, #0x10]
|
||||
mov x29, sp
|
||||
|
||||
/* Save the old tpidr_el0. */
|
||||
mov x19, x8
|
||||
|
||||
/* Restore host tpidr_el0. */
|
||||
ldr x0, [x8, #(TpidrEl0NativeContext)]
|
||||
ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)]
|
||||
msr tpidr_el0, x3
|
||||
|
||||
/* Call the handler. */
|
||||
bl _ZN4Core6ArmNce22HandleGuestAccessFaultEPNS_12GuestContextEPvS3_
|
||||
|
||||
/* If the handler returned false, we want to preserve the host tpidr_el0. */
|
||||
cbz x0, 2f
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
|
||||
#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
|
||||
#define BreakFromRunCodeSignal SIGURG
|
||||
#define GuestFaultSignal SIGSEGV
|
||||
#define GuestAccessFaultSignal SIGSEGV
|
||||
#define GuestAlignmentFaultSignal SIGBUS
|
||||
|
||||
#define GuestContextSp 0xF8
|
||||
#define GuestContextHostContext 0x320
|
||||
|
||||
825
src/core/arm/nce/interpreter_visitor.cpp
Normal file
825
src/core/arm/nce/interpreter_visitor.cpp
Normal file
@@ -0,0 +1,825 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "core/arm/nce/interpreter_visitor.h"
|
||||
|
||||
#include <dynarmic/frontend/A64/decoder/a64.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
template <u32 BitSize>
|
||||
u64 SignExtendToLong(u64 value) {
|
||||
u64 mask = 1ULL << (BitSize - 1);
|
||||
value &= (1ULL << BitSize) - 1;
|
||||
return (value ^ mask) - mask;
|
||||
}
|
||||
|
||||
static u64 SignExtendToLong(u64 value, u64 bitsize) {
|
||||
switch (bitsize) {
|
||||
case 8:
|
||||
return SignExtendToLong<8>(value);
|
||||
case 16:
|
||||
return SignExtendToLong<16>(value);
|
||||
case 32:
|
||||
return SignExtendToLong<32>(value);
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
template <u64 BitSize>
|
||||
u32 SignExtendToWord(u32 value) {
|
||||
u32 mask = 1ULL << (BitSize - 1);
|
||||
value &= (1ULL << BitSize) - 1;
|
||||
return (value ^ mask) - mask;
|
||||
}
|
||||
|
||||
static u32 SignExtendToWord(u32 value, u64 bitsize) {
|
||||
switch (bitsize) {
|
||||
case 8:
|
||||
return SignExtendToWord<8>(value);
|
||||
case 16:
|
||||
return SignExtendToWord<16>(value);
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
static u64 SignExtend(u64 value, u64 bitsize, u64 regsize) {
|
||||
if (regsize == 64) {
|
||||
return SignExtendToLong(value, bitsize);
|
||||
} else {
|
||||
return SignExtendToWord(static_cast<u32>(value), bitsize);
|
||||
}
|
||||
}
|
||||
|
||||
static u128 VectorGetElement(u128 value, u64 bitsize) {
|
||||
switch (bitsize) {
|
||||
case 8:
|
||||
return {value[0] & ((1ULL << 8) - 1), 0};
|
||||
case 16:
|
||||
return {value[0] & ((1ULL << 16) - 1), 0};
|
||||
case 32:
|
||||
return {value[0] & ((1ULL << 32) - 1), 0};
|
||||
case 64:
|
||||
return {value[0], 0};
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
u64 InterpreterVisitor::ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift) {
|
||||
ASSERT(shift <= 4);
|
||||
ASSERT(bitsize == 32 || bitsize == 64);
|
||||
u64 val = this->GetReg(reg);
|
||||
size_t len;
|
||||
u64 extended;
|
||||
bool signed_extend;
|
||||
|
||||
switch (option.ZeroExtend()) {
|
||||
case 0b000: { // UXTB
|
||||
val &= ((1ULL << 8) - 1);
|
||||
len = 8;
|
||||
signed_extend = false;
|
||||
break;
|
||||
}
|
||||
case 0b001: { // UXTH
|
||||
val &= ((1ULL << 16) - 1);
|
||||
len = 16;
|
||||
signed_extend = false;
|
||||
break;
|
||||
}
|
||||
case 0b010: { // UXTW
|
||||
val &= ((1ULL << 32) - 1);
|
||||
len = 32;
|
||||
signed_extend = false;
|
||||
break;
|
||||
}
|
||||
case 0b011: { // UXTX
|
||||
len = 64;
|
||||
signed_extend = false;
|
||||
break;
|
||||
}
|
||||
case 0b100: { // SXTB
|
||||
val &= ((1ULL << 8) - 1);
|
||||
len = 8;
|
||||
signed_extend = true;
|
||||
break;
|
||||
}
|
||||
case 0b101: { // SXTH
|
||||
val &= ((1ULL << 16) - 1);
|
||||
len = 16;
|
||||
signed_extend = true;
|
||||
break;
|
||||
}
|
||||
case 0b110: { // SXTW
|
||||
val &= ((1ULL << 32) - 1);
|
||||
len = 32;
|
||||
signed_extend = true;
|
||||
break;
|
||||
}
|
||||
case 0b111: { // SXTX
|
||||
len = 64;
|
||||
signed_extend = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
if (len < bitsize && signed_extend) {
|
||||
extended = SignExtend(val, len, bitsize);
|
||||
} else {
|
||||
extended = val;
|
||||
}
|
||||
|
||||
return extended << shift;
|
||||
}
|
||||
|
||||
u128 InterpreterVisitor::GetVec(Vec v) {
|
||||
return m_fpsimd_regs[static_cast<u32>(v)];
|
||||
}
|
||||
|
||||
u64 InterpreterVisitor::GetReg(Reg r) {
|
||||
return m_regs[static_cast<u32>(r)];
|
||||
}
|
||||
|
||||
u64 InterpreterVisitor::GetSp() {
|
||||
return m_sp;
|
||||
}
|
||||
|
||||
u64 InterpreterVisitor::GetPc() {
|
||||
return m_pc;
|
||||
}
|
||||
|
||||
void InterpreterVisitor::SetVec(Vec v, u128 value) {
|
||||
m_fpsimd_regs[static_cast<u32>(v)] = value;
|
||||
}
|
||||
|
||||
void InterpreterVisitor::SetReg(Reg r, u64 value) {
|
||||
m_regs[static_cast<u32>(r)] = value;
|
||||
}
|
||||
|
||||
void InterpreterVisitor::SetSp(u64 value) {
|
||||
m_sp = value;
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::Ordered(size_t size, bool L, bool o0, Reg Rn, Reg Rt) {
|
||||
const auto memop = L ? MemOp::Load : MemOp::Store;
|
||||
const size_t elsize = 8 << size;
|
||||
const size_t datasize = elsize;
|
||||
|
||||
// Operation
|
||||
const size_t dbytes = datasize / 8;
|
||||
|
||||
u64 address;
|
||||
if (Rn == Reg::SP) {
|
||||
address = this->GetSp();
|
||||
} else {
|
||||
address = this->GetReg(Rn);
|
||||
}
|
||||
|
||||
switch (memop) {
|
||||
case MemOp::Store: {
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
u64 value = this->GetReg(Rt);
|
||||
m_memory.WriteBlock(address, &value, dbytes);
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
break;
|
||||
}
|
||||
case MemOp::Load: {
|
||||
u64 value = 0;
|
||||
m_memory.ReadBlock(address, &value, dbytes);
|
||||
this->SetReg(Rt, value);
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::STLLR(Imm<2> sz, Reg Rn, Reg Rt) {
|
||||
const size_t size = sz.ZeroExtend<size_t>();
|
||||
const bool L = 0;
|
||||
const bool o0 = 0;
|
||||
return this->Ordered(size, L, o0, Rn, Rt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::STLR(Imm<2> sz, Reg Rn, Reg Rt) {
|
||||
const size_t size = sz.ZeroExtend<size_t>();
|
||||
const bool L = 0;
|
||||
const bool o0 = 1;
|
||||
return this->Ordered(size, L, o0, Rn, Rt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::LDLAR(Imm<2> sz, Reg Rn, Reg Rt) {
|
||||
const size_t size = sz.ZeroExtend<size_t>();
|
||||
const bool L = 1;
|
||||
const bool o0 = 0;
|
||||
return this->Ordered(size, L, o0, Rn, Rt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::LDAR(Imm<2> sz, Reg Rn, Reg Rt) {
|
||||
const size_t size = sz.ZeroExtend<size_t>();
|
||||
const bool L = 1;
|
||||
const bool o0 = 1;
|
||||
return this->Ordered(size, L, o0, Rn, Rt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) {
|
||||
const size_t size = opc_0 == 0 ? 4 : 8;
|
||||
const s64 offset = Dynarmic::concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
|
||||
const u64 address = this->GetPc() + offset;
|
||||
|
||||
u64 data = 0;
|
||||
m_memory.ReadBlock(address, &data, size);
|
||||
|
||||
this->SetReg(Rt, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
|
||||
if (opc == 0b11) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
|
||||
const u64 size = 4 << opc.ZeroExtend();
|
||||
const u64 offset = imm19.SignExtend<u64>() << 2;
|
||||
const u64 address = this->GetPc() + offset;
|
||||
|
||||
u128 data{};
|
||||
m_memory.ReadBlock(address, &data, size);
|
||||
this->SetVec(Vt, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L,
|
||||
Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) {
|
||||
if ((L == 0 && opc.Bit<0>() == 1) || opc == 0b11) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto memop = L == 1 ? MemOp::Load : MemOp::Store;
|
||||
if (memop == MemOp::Load && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) {
|
||||
// Unpredictable instruction
|
||||
return false;
|
||||
}
|
||||
if (memop == MemOp::Store && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) {
|
||||
// Unpredictable instruction
|
||||
return false;
|
||||
}
|
||||
if (memop == MemOp::Load && Rt == Rt2) {
|
||||
// Unpredictable instruction
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 address;
|
||||
if (Rn == Reg::SP) {
|
||||
address = this->GetSp();
|
||||
} else {
|
||||
address = this->GetReg(Rn);
|
||||
}
|
||||
|
||||
const bool postindex = !not_postindex;
|
||||
const bool signed_ = opc.Bit<0>() != 0;
|
||||
const size_t scale = 2 + opc.Bit<1>();
|
||||
const size_t datasize = 8 << scale;
|
||||
const u64 offset = imm7.SignExtend<u64>() << scale;
|
||||
|
||||
if (!postindex) {
|
||||
address += offset;
|
||||
}
|
||||
|
||||
const size_t dbytes = datasize / 8;
|
||||
switch (memop) {
|
||||
case MemOp::Store: {
|
||||
u64 data1 = this->GetReg(Rt);
|
||||
u64 data2 = this->GetReg(Rt2);
|
||||
m_memory.WriteBlock(address, &data1, dbytes);
|
||||
m_memory.WriteBlock(address + dbytes, &data2, dbytes);
|
||||
break;
|
||||
}
|
||||
case MemOp::Load: {
|
||||
u64 data1 = 0, data2 = 0;
|
||||
m_memory.ReadBlock(address, &data1, dbytes);
|
||||
m_memory.ReadBlock(address + dbytes, &data2, dbytes);
|
||||
if (signed_) {
|
||||
this->SetReg(Rt, SignExtend(data1, datasize, 64));
|
||||
this->SetReg(Rt2, SignExtend(data2, datasize, 64));
|
||||
} else {
|
||||
this->SetReg(Rt, data1);
|
||||
this->SetReg(Rt2, data2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
if (wback) {
|
||||
if (postindex) {
|
||||
address += offset;
|
||||
}
|
||||
|
||||
if (Rn == Reg::SP) {
|
||||
this->SetSp(address);
|
||||
} else {
|
||||
this->SetReg(Rn, address);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L,
|
||||
Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) {
|
||||
if (opc == 0b11) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto memop = L == 1 ? MemOp::Load : MemOp::Store;
|
||||
if (memop == MemOp::Load && Vt == Vt2) {
|
||||
// Unpredictable instruction
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 address;
|
||||
if (Rn == Reg::SP) {
|
||||
address = this->GetSp();
|
||||
} else {
|
||||
address = this->GetReg(Rn);
|
||||
}
|
||||
|
||||
const bool postindex = !not_postindex;
|
||||
const size_t scale = 2 + opc.ZeroExtend<size_t>();
|
||||
const size_t datasize = 8 << scale;
|
||||
const u64 offset = imm7.SignExtend<u64>() << scale;
|
||||
const size_t dbytes = datasize / 8;
|
||||
|
||||
if (!postindex) {
|
||||
address += offset;
|
||||
}
|
||||
|
||||
switch (memop) {
|
||||
case MemOp::Store: {
|
||||
u128 data1 = VectorGetElement(this->GetVec(Vt), datasize);
|
||||
u128 data2 = VectorGetElement(this->GetVec(Vt2), datasize);
|
||||
m_memory.WriteBlock(address, &data1, dbytes);
|
||||
m_memory.WriteBlock(address + dbytes, &data2, dbytes);
|
||||
break;
|
||||
}
|
||||
case MemOp::Load: {
|
||||
u128 data1{}, data2{};
|
||||
m_memory.ReadBlock(address, &data1, dbytes);
|
||||
m_memory.ReadBlock(address + dbytes, &data2, dbytes);
|
||||
this->SetVec(Vt, data1);
|
||||
this->SetVec(Vt2, data2);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
if (wback) {
|
||||
if (postindex) {
|
||||
address += offset;
|
||||
}
|
||||
|
||||
if (Rn == Reg::SP) {
|
||||
this->SetSp(address);
|
||||
} else {
|
||||
this->SetReg(Rn, address);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::RegisterImmediate(bool wback, bool postindex, size_t scale, u64 offset,
|
||||
Imm<2> size, Imm<2> opc, Reg Rn, Reg Rt) {
|
||||
MemOp memop;
|
||||
bool signed_ = false;
|
||||
size_t regsize = 0;
|
||||
|
||||
if (opc.Bit<1>() == 0) {
|
||||
memop = opc.Bit<0>() ? MemOp::Load : MemOp::Store;
|
||||
regsize = size == 0b11 ? 64 : 32;
|
||||
signed_ = false;
|
||||
} else if (size == 0b11) {
|
||||
memop = MemOp::Prefetch;
|
||||
ASSERT(!opc.Bit<0>());
|
||||
} else {
|
||||
memop = MemOp::Load;
|
||||
ASSERT(!(size == 0b10 && opc.Bit<0>() == 1));
|
||||
regsize = opc.Bit<0>() ? 32 : 64;
|
||||
signed_ = true;
|
||||
}
|
||||
|
||||
if (memop == MemOp::Load && wback && Rn == Rt && Rn != Reg::R31) {
|
||||
// Unpredictable instruction
|
||||
return false;
|
||||
}
|
||||
if (memop == MemOp::Store && wback && Rn == Rt && Rn != Reg::R31) {
|
||||
// Unpredictable instruction
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 address;
|
||||
if (Rn == Reg::SP) {
|
||||
address = this->GetSp();
|
||||
} else {
|
||||
address = this->GetReg(Rn);
|
||||
}
|
||||
if (!postindex) {
|
||||
address += offset;
|
||||
}
|
||||
|
||||
const size_t datasize = 8 << scale;
|
||||
switch (memop) {
|
||||
case MemOp::Store: {
|
||||
u64 data = this->GetReg(Rt);
|
||||
m_memory.WriteBlock(address, &data, datasize / 8);
|
||||
break;
|
||||
}
|
||||
case MemOp::Load: {
|
||||
u64 data = 0;
|
||||
m_memory.ReadBlock(address, &data, datasize / 8);
|
||||
if (signed_) {
|
||||
this->SetReg(Rt, SignExtend(data, datasize, regsize));
|
||||
} else {
|
||||
this->SetReg(Rt, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MemOp::Prefetch:
|
||||
// this->Prefetch(address, Rt)
|
||||
break;
|
||||
}
|
||||
|
||||
if (wback) {
|
||||
if (postindex) {
|
||||
address += offset;
|
||||
}
|
||||
|
||||
if (Rn == Reg::SP) {
|
||||
this->SetSp(address);
|
||||
} else {
|
||||
this->SetReg(Rn, address);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex,
|
||||
Reg Rn, Reg Rt) {
|
||||
const bool wback = true;
|
||||
const bool postindex = !not_postindex;
|
||||
const size_t scale = size.ZeroExtend<size_t>();
|
||||
const u64 offset = imm9.SignExtend<u64>();
|
||||
|
||||
return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) {
|
||||
const bool wback = false;
|
||||
const bool postindex = false;
|
||||
const size_t scale = size.ZeroExtend<size_t>();
|
||||
const u64 offset = imm12.ZeroExtend<u64>() << scale;
|
||||
|
||||
return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
|
||||
const bool wback = false;
|
||||
const bool postindex = false;
|
||||
const size_t scale = size.ZeroExtend<size_t>();
|
||||
const u64 offset = imm9.SignExtend<u64>();
|
||||
|
||||
return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::SIMDImmediate(bool wback, bool postindex, size_t scale, u64 offset,
|
||||
MemOp memop, Reg Rn, Vec Vt) {
|
||||
const size_t datasize = 8 << scale;
|
||||
|
||||
u64 address;
|
||||
if (Rn == Reg::SP) {
|
||||
address = this->GetSp();
|
||||
} else {
|
||||
address = this->GetReg(Rn);
|
||||
}
|
||||
|
||||
if (!postindex) {
|
||||
address += offset;
|
||||
}
|
||||
|
||||
switch (memop) {
|
||||
case MemOp::Store: {
|
||||
u128 data = VectorGetElement(this->GetVec(Vt), datasize);
|
||||
m_memory.WriteBlock(address, &data, datasize / 8);
|
||||
break;
|
||||
}
|
||||
case MemOp::Load: {
|
||||
u128 data{};
|
||||
m_memory.ReadBlock(address, &data, datasize);
|
||||
this->SetVec(Vt, data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
if (wback) {
|
||||
if (postindex) {
|
||||
address += offset;
|
||||
}
|
||||
|
||||
if (Rn == Reg::SP) {
|
||||
this->SetSp(address);
|
||||
} else {
|
||||
this->SetReg(Rn, address);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9,
|
||||
bool not_postindex, Reg Rn, Vec Vt) {
|
||||
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||
if (scale > 4) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool wback = true;
|
||||
const bool postindex = !not_postindex;
|
||||
const u64 offset = imm9.SignExtend<u64>();
|
||||
|
||||
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn,
|
||||
Vec Vt) {
|
||||
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||
if (scale > 4) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool wback = false;
|
||||
const bool postindex = false;
|
||||
const u64 offset = imm12.ZeroExtend<u64>() << scale;
|
||||
|
||||
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9,
|
||||
bool not_postindex, Reg Rn, Vec Vt) {
|
||||
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||
if (scale > 4) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool wback = true;
|
||||
const bool postindex = !not_postindex;
|
||||
const u64 offset = imm9.SignExtend<u64>();
|
||||
|
||||
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn,
|
||||
Vec Vt) {
|
||||
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||
if (scale > 4) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool wback = false;
|
||||
const bool postindex = false;
|
||||
const u64 offset = imm12.ZeroExtend<u64>() << scale;
|
||||
|
||||
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
|
||||
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||
if (scale > 4) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool wback = false;
|
||||
const bool postindex = false;
|
||||
const u64 offset = imm9.SignExtend<u64>();
|
||||
|
||||
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
|
||||
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||
if (scale > 4) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool wback = false;
|
||||
const bool postindex = false;
|
||||
const u64 offset = imm9.SignExtend<u64>();
|
||||
|
||||
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::RegisterOffset(size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1,
|
||||
Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Reg Rt) {
|
||||
MemOp memop;
|
||||
size_t regsize = 64;
|
||||
bool signed_ = false;
|
||||
|
||||
if (opc_1 == 0) {
|
||||
memop = opc_0 == 1 ? MemOp::Load : MemOp::Store;
|
||||
regsize = size == 0b11 ? 64 : 32;
|
||||
signed_ = false;
|
||||
} else if (size == 0b11) {
|
||||
memop = MemOp::Prefetch;
|
||||
if (opc_0 == 1) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
memop = MemOp::Load;
|
||||
if (size == 0b10 && opc_0 == 1) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
regsize = opc_0 == 1 ? 32 : 64;
|
||||
signed_ = true;
|
||||
}
|
||||
|
||||
const size_t datasize = 8 << scale;
|
||||
|
||||
// Operation
|
||||
const u64 offset = this->ExtendReg(64, Rm, option, shift);
|
||||
|
||||
u64 address;
|
||||
if (Rn == Reg::SP) {
|
||||
address = this->GetSp();
|
||||
} else {
|
||||
address = this->GetReg(Rn);
|
||||
}
|
||||
address += offset;
|
||||
|
||||
switch (memop) {
|
||||
case MemOp::Store: {
|
||||
u64 data = this->GetReg(Rt);
|
||||
m_memory.WriteBlock(address, &data, datasize / 8);
|
||||
break;
|
||||
}
|
||||
case MemOp::Load: {
|
||||
u64 data = 0;
|
||||
m_memory.ReadBlock(address, &data, datasize / 8);
|
||||
if (signed_) {
|
||||
this->SetReg(Rt, SignExtend(data, datasize, regsize));
|
||||
} else {
|
||||
this->SetReg(Rt, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MemOp::Prefetch:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||
Reg Rt) {
|
||||
const Imm<1> opc_0{0};
|
||||
const size_t scale = size.ZeroExtend<size_t>();
|
||||
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||
if (!option.Bit<1>()) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
return this->RegisterOffset(scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||
Reg Rt) {
|
||||
const Imm<1> opc_0{1};
|
||||
const size_t scale = size.ZeroExtend<size_t>();
|
||||
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||
if (!option.Bit<1>()) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
return this->RegisterOffset(scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::SIMDOffset(size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option,
|
||||
Reg Rn, Vec Vt) {
|
||||
const auto memop = opc_0 == 1 ? MemOp::Load : MemOp::Store;
|
||||
const size_t datasize = 8 << scale;
|
||||
|
||||
// Operation
|
||||
const u64 offset = this->ExtendReg(64, Rm, option, shift);
|
||||
|
||||
u64 address;
|
||||
if (Rn == Reg::SP) {
|
||||
address = this->GetSp();
|
||||
} else {
|
||||
address = this->GetReg(Rn);
|
||||
}
|
||||
address += offset;
|
||||
|
||||
switch (memop) {
|
||||
case MemOp::Store: {
|
||||
u128 data = VectorGetElement(this->GetVec(Vt), datasize);
|
||||
m_memory.WriteBlock(address, &data, datasize / 8);
|
||||
break;
|
||||
}
|
||||
case MemOp::Load: {
|
||||
u128 data{};
|
||||
m_memory.ReadBlock(address, &data, datasize / 8);
|
||||
this->SetVec(Vt, data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S,
|
||||
Reg Rn, Vec Vt) {
|
||||
const Imm<1> opc_0{0};
|
||||
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||
if (scale > 4) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||
if (!option.Bit<1>()) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
|
||||
}
|
||||
|
||||
bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S,
|
||||
Reg Rn, Vec Vt) {
|
||||
const Imm<1> opc_0{1};
|
||||
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||
if (scale > 4) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||
if (!option.Bit<1>()) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
|
||||
}
|
||||
|
||||
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
|
||||
fpsimd_context* fpsimd_context) {
|
||||
// Construct the interpreter.
|
||||
std::span<u64, 31> regs(reinterpret_cast<u64*>(context->regs), 31);
|
||||
std::span<u128, 32> vregs(reinterpret_cast<u128*>(fpsimd_context->vregs), 32);
|
||||
u64& sp = *reinterpret_cast<u64*>(&context->sp);
|
||||
const u64& pc = *reinterpret_cast<u64*>(&context->pc);
|
||||
|
||||
InterpreterVisitor visitor(memory, regs, vregs, sp, pc);
|
||||
|
||||
// Read the instruction at the program counter.
|
||||
u32 instruction = memory.Read32(pc);
|
||||
bool was_executed = false;
|
||||
|
||||
// Interpret the instruction.
|
||||
if (auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction)) {
|
||||
was_executed = decoder->get().call(visitor, instruction);
|
||||
} else {
|
||||
LOG_ERROR(Core_ARM, "Unallocated encoding: {:#x}", instruction);
|
||||
}
|
||||
|
||||
if (was_executed) {
|
||||
return pc + 4;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
103
src/core/arm/nce/interpreter_visitor.h
Normal file
103
src/core/arm/nce/interpreter_visitor.h
Normal file
@@ -0,0 +1,103 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "core/arm/nce/visitor_base.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
class InterpreterVisitor final : public VisitorBase {
|
||||
public:
|
||||
explicit InterpreterVisitor(Core::Memory::Memory& memory, std::span<u64, 31> regs,
|
||||
std::span<u128, 32> fpsimd_regs, u64& sp, const u64& pc)
|
||||
: m_memory(memory), m_regs(regs), m_fpsimd_regs(fpsimd_regs), m_sp(sp), m_pc(pc) {}
|
||||
~InterpreterVisitor() override = default;
|
||||
|
||||
enum class MemOp {
|
||||
Load,
|
||||
Store,
|
||||
Prefetch,
|
||||
};
|
||||
|
||||
u128 GetVec(Vec v);
|
||||
u64 GetReg(Reg r);
|
||||
u64 GetSp();
|
||||
u64 GetPc();
|
||||
|
||||
void SetVec(Vec v, u128 value);
|
||||
void SetReg(Reg r, u64 value);
|
||||
void SetSp(u64 value);
|
||||
|
||||
u64 ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift);
|
||||
|
||||
// Loads and stores - Load/Store Exclusive
|
||||
bool Ordered(size_t size, bool L, bool o0, Reg Rn, Reg Rt);
|
||||
bool STLLR(Imm<2> size, Reg Rn, Reg Rt) override;
|
||||
bool STLR(Imm<2> size, Reg Rn, Reg Rt) override;
|
||||
bool LDLAR(Imm<2> size, Reg Rn, Reg Rt) override;
|
||||
bool LDAR(Imm<2> size, Reg Rn, Reg Rt) override;
|
||||
|
||||
// Loads and stores - Load register (literal)
|
||||
bool LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) override;
|
||||
bool LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) override;
|
||||
|
||||
// Loads and stores - Load/Store register pair
|
||||
bool STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Reg Rt2,
|
||||
Reg Rn, Reg Rt) override;
|
||||
bool STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Vec Vt2,
|
||||
Reg Rn, Vec Vt) override;
|
||||
|
||||
// Loads and stores - Load/Store register (immediate)
|
||||
bool RegisterImmediate(bool wback, bool postindex, size_t scale, u64 offset, Imm<2> size,
|
||||
Imm<2> opc, Reg Rn, Reg Rt);
|
||||
bool STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn,
|
||||
Reg Rt) override;
|
||||
bool STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) override;
|
||||
bool STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) override;
|
||||
|
||||
bool SIMDImmediate(bool wback, bool postindex, size_t scale, u64 offset, MemOp memop, Reg Rn,
|
||||
Vec Vt);
|
||||
bool STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn,
|
||||
Vec Vt) override;
|
||||
bool STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) override;
|
||||
bool LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn,
|
||||
Vec Vt) override;
|
||||
bool LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) override;
|
||||
bool STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) override;
|
||||
bool LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) override;
|
||||
|
||||
// Loads and stores - Load/Store register (register offset)
|
||||
bool RegisterOffset(size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1, Imm<1> opc_0, Reg Rm,
|
||||
Imm<3> option, Reg Rn, Reg Rt);
|
||||
bool STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||
Reg Rt) override;
|
||||
bool LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||
Reg Rt) override;
|
||||
|
||||
bool SIMDOffset(size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Vec Vt);
|
||||
bool STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||
Vec Vt) override;
|
||||
bool LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||
Vec Vt) override;
|
||||
|
||||
private:
|
||||
Core::Memory::Memory& m_memory;
|
||||
std::span<u64, 31> m_regs;
|
||||
std::span<u128, 32> m_fpsimd_regs;
|
||||
u64& m_sp;
|
||||
const u64& m_pc;
|
||||
};
|
||||
|
||||
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
|
||||
fpsimd_context* fpsimd_context);
|
||||
|
||||
} // namespace Core
|
||||
2777
src/core/arm/nce/visitor_base.h
Normal file
2777
src/core/arm/nce/visitor_base.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/apm/apm_controller.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
@@ -130,8 +131,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system)
|
||||
: kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
|
||||
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system},
|
||||
gpu_dirty_memory_write_manager{} {
|
||||
cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{},
|
||||
time_manager{system}, gpu_dirty_memory_write_manager{} {
|
||||
memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
|
||||
}
|
||||
|
||||
@@ -532,6 +533,7 @@ struct System::Impl {
|
||||
|
||||
/// Service State
|
||||
Service::Glue::ARPManager arp_manager;
|
||||
Service::Account::ProfileManager profile_manager;
|
||||
Service::Time::TimeManager time_manager;
|
||||
|
||||
/// Service manager
|
||||
@@ -921,6 +923,14 @@ const Service::APM::Controller& System::GetAPMController() const {
|
||||
return impl->apm_controller;
|
||||
}
|
||||
|
||||
Service::Account::ProfileManager& System::GetProfileManager() {
|
||||
return impl->profile_manager;
|
||||
}
|
||||
|
||||
const Service::Account::ProfileManager& System::GetProfileManager() const {
|
||||
return impl->profile_manager;
|
||||
}
|
||||
|
||||
Service::Time::TimeManager& System::GetTimeManager() {
|
||||
return impl->time_manager;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ class Memory;
|
||||
|
||||
namespace Service {
|
||||
|
||||
namespace Account {
|
||||
class ProfileManager;
|
||||
} // namespace Account
|
||||
|
||||
namespace AM::Applets {
|
||||
struct AppletFrontendSet;
|
||||
class AppletManager;
|
||||
@@ -383,6 +387,9 @@ public:
|
||||
[[nodiscard]] Service::APM::Controller& GetAPMController();
|
||||
[[nodiscard]] const Service::APM::Controller& GetAPMController() const;
|
||||
|
||||
[[nodiscard]] Service::Account::ProfileManager& GetProfileManager();
|
||||
[[nodiscard]] const Service::Account::ProfileManager& GetProfileManager() const;
|
||||
|
||||
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
|
||||
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
@@ -134,7 +135,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
|
||||
|
||||
child->size = child->source->GetSize();
|
||||
|
||||
AddFile(parent, child);
|
||||
AddFile(parent, std::move(child));
|
||||
}
|
||||
|
||||
for (auto& child_romfs_dir : romfs_dir->GetSubdirectories()) {
|
||||
@@ -163,36 +164,24 @@ void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
|
||||
|
||||
bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
|
||||
std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) {
|
||||
// Check whether it's already in the known directories.
|
||||
const auto [it, is_new] = directories.emplace(dir_ctx->path, nullptr);
|
||||
if (!is_new) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add a new directory.
|
||||
num_dirs++;
|
||||
dir_table_size +=
|
||||
sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4);
|
||||
dir_ctx->parent = parent_dir_ctx;
|
||||
it->second = dir_ctx;
|
||||
dir_ctx->parent = std::move(parent_dir_ctx);
|
||||
directories.emplace_back(std::move(dir_ctx));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
|
||||
std::shared_ptr<RomFSBuildFileContext> file_ctx) {
|
||||
// Check whether it's already in the known files.
|
||||
const auto [it, is_new] = files.emplace(file_ctx->path, nullptr);
|
||||
if (!is_new) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add a new file.
|
||||
num_files++;
|
||||
file_table_size +=
|
||||
sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4);
|
||||
file_ctx->parent = parent_dir_ctx;
|
||||
it->second = file_ctx;
|
||||
file_ctx->parent = std::move(parent_dir_ctx);
|
||||
files.emplace_back(std::move(file_ctx));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -201,7 +190,7 @@ RomFSBuildContext::RomFSBuildContext(VirtualDir base_, VirtualDir ext_)
|
||||
: base(std::move(base_)), ext(std::move(ext_)) {
|
||||
root = std::make_shared<RomFSBuildDirectoryContext>();
|
||||
root->path = "\0";
|
||||
directories.emplace(root->path, root);
|
||||
directories.emplace_back(root);
|
||||
num_dirs = 1;
|
||||
dir_table_size = 0x18;
|
||||
|
||||
@@ -210,28 +199,43 @@ RomFSBuildContext::RomFSBuildContext(VirtualDir base_, VirtualDir ext_)
|
||||
|
||||
RomFSBuildContext::~RomFSBuildContext() = default;
|
||||
|
||||
std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||
std::vector<std::pair<u64, VirtualFile>> RomFSBuildContext::Build() {
|
||||
const u64 dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
|
||||
const u64 file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
|
||||
dir_hash_table_size = 4 * dir_hash_table_entry_count;
|
||||
file_hash_table_size = 4 * file_hash_table_entry_count;
|
||||
|
||||
// Assign metadata pointers
|
||||
// Assign metadata pointers.
|
||||
RomFSHeader header{};
|
||||
|
||||
std::vector<u32> dir_hash_table(dir_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
|
||||
std::vector<u32> file_hash_table(file_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
|
||||
std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
|
||||
dir_table_size);
|
||||
u32* const dir_hash_table_pointer = reinterpret_cast<u32*>(metadata.data());
|
||||
u8* const dir_table_pointer = metadata.data() + dir_hash_table_size;
|
||||
u32* const file_hash_table_pointer =
|
||||
reinterpret_cast<u32*>(metadata.data() + dir_hash_table_size + dir_table_size);
|
||||
u8* const file_table_pointer =
|
||||
metadata.data() + dir_hash_table_size + dir_table_size + file_hash_table_size;
|
||||
|
||||
std::vector<u8> dir_table(dir_table_size);
|
||||
std::vector<u8> file_table(file_table_size);
|
||||
std::span<u32> dir_hash_table(dir_hash_table_pointer, dir_hash_table_entry_count);
|
||||
std::span<u32> file_hash_table(file_hash_table_pointer, file_hash_table_entry_count);
|
||||
std::span<u8> dir_table(dir_table_pointer, dir_table_size);
|
||||
std::span<u8> file_table(file_table_pointer, file_table_size);
|
||||
|
||||
std::shared_ptr<RomFSBuildFileContext> cur_file;
|
||||
// Initialize hash tables.
|
||||
std::memset(dir_hash_table.data(), 0xFF, dir_hash_table.size_bytes());
|
||||
std::memset(file_hash_table.data(), 0xFF, file_hash_table.size_bytes());
|
||||
|
||||
// Sort tables by name.
|
||||
std::sort(files.begin(), files.end(),
|
||||
[](const auto& a, const auto& b) { return a->path < b->path; });
|
||||
std::sort(directories.begin(), directories.end(),
|
||||
[](const auto& a, const auto& b) { return a->path < b->path; });
|
||||
|
||||
// Determine file offsets.
|
||||
u32 entry_offset = 0;
|
||||
std::shared_ptr<RomFSBuildFileContext> prev_file = nullptr;
|
||||
for (const auto& it : files) {
|
||||
cur_file = it.second;
|
||||
for (const auto& cur_file : files) {
|
||||
file_partition_size = Common::AlignUp(file_partition_size, 16);
|
||||
cur_file->offset = file_partition_size;
|
||||
file_partition_size += cur_file->size;
|
||||
@@ -243,34 +247,48 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||
}
|
||||
// Assign deferred parent/sibling ownership.
|
||||
for (auto it = files.rbegin(); it != files.rend(); ++it) {
|
||||
cur_file = it->second;
|
||||
auto& cur_file = *it;
|
||||
cur_file->sibling = cur_file->parent->file;
|
||||
cur_file->parent->file = cur_file;
|
||||
}
|
||||
|
||||
std::shared_ptr<RomFSBuildDirectoryContext> cur_dir;
|
||||
|
||||
// Determine directory offsets.
|
||||
entry_offset = 0;
|
||||
for (const auto& it : directories) {
|
||||
cur_dir = it.second;
|
||||
for (const auto& cur_dir : directories) {
|
||||
cur_dir->entry_offset = entry_offset;
|
||||
entry_offset +=
|
||||
static_cast<u32>(sizeof(RomFSDirectoryEntry) +
|
||||
Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4));
|
||||
}
|
||||
// Assign deferred parent/sibling ownership.
|
||||
for (auto it = directories.rbegin(); it->second != root; ++it) {
|
||||
cur_dir = it->second;
|
||||
for (auto it = directories.rbegin(); (*it) != root; ++it) {
|
||||
auto& cur_dir = *it;
|
||||
cur_dir->sibling = cur_dir->parent->child;
|
||||
cur_dir->parent->child = cur_dir;
|
||||
}
|
||||
|
||||
std::multimap<u64, VirtualFile> out;
|
||||
// Create output map.
|
||||
std::vector<std::pair<u64, VirtualFile>> out;
|
||||
out.reserve(num_files + 2);
|
||||
|
||||
// Set header fields.
|
||||
header.header_size = sizeof(RomFSHeader);
|
||||
header.file_hash_table_size = file_hash_table_size;
|
||||
header.file_table_size = file_table_size;
|
||||
header.dir_hash_table_size = dir_hash_table_size;
|
||||
header.dir_table_size = dir_table_size;
|
||||
header.file_partition_ofs = ROMFS_FILEPARTITION_OFS;
|
||||
header.dir_hash_table_ofs = Common::AlignUp(header.file_partition_ofs + file_partition_size, 4);
|
||||
header.dir_table_ofs = header.dir_hash_table_ofs + header.dir_hash_table_size;
|
||||
header.file_hash_table_ofs = header.dir_table_ofs + header.dir_table_size;
|
||||
header.file_table_ofs = header.file_hash_table_ofs + header.file_hash_table_size;
|
||||
|
||||
std::vector<u8> header_data(sizeof(RomFSHeader));
|
||||
std::memcpy(header_data.data(), &header, header_data.size());
|
||||
out.emplace_back(0, std::make_shared<VectorVfsFile>(std::move(header_data)));
|
||||
|
||||
// Populate file tables.
|
||||
for (const auto& it : files) {
|
||||
cur_file = it.second;
|
||||
for (const auto& cur_file : files) {
|
||||
RomFSFileEntry cur_entry{};
|
||||
|
||||
cur_entry.parent = cur_file->parent->entry_offset;
|
||||
@@ -287,7 +305,7 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||
|
||||
cur_entry.name_size = name_size;
|
||||
|
||||
out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, std::move(cur_file->source));
|
||||
out.emplace_back(cur_file->offset + ROMFS_FILEPARTITION_OFS, std::move(cur_file->source));
|
||||
std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
|
||||
std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
|
||||
Common::AlignUp(cur_entry.name_size, 4));
|
||||
@@ -296,8 +314,7 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||
}
|
||||
|
||||
// Populate dir tables.
|
||||
for (const auto& it : directories) {
|
||||
cur_dir = it.second;
|
||||
for (const auto& cur_dir : directories) {
|
||||
RomFSDirectoryEntry cur_entry{};
|
||||
|
||||
cur_entry.parent = cur_dir == root ? 0 : cur_dir->parent->entry_offset;
|
||||
@@ -323,34 +340,13 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||
cur_dir->path.data() + cur_dir->cur_path_ofs, name_size);
|
||||
}
|
||||
|
||||
// Set header fields.
|
||||
header.header_size = sizeof(RomFSHeader);
|
||||
header.file_hash_table_size = file_hash_table_size;
|
||||
header.file_table_size = file_table_size;
|
||||
header.dir_hash_table_size = dir_hash_table_size;
|
||||
header.dir_table_size = dir_table_size;
|
||||
header.file_partition_ofs = ROMFS_FILEPARTITION_OFS;
|
||||
header.dir_hash_table_ofs = Common::AlignUp(header.file_partition_ofs + file_partition_size, 4);
|
||||
header.dir_table_ofs = header.dir_hash_table_ofs + header.dir_hash_table_size;
|
||||
header.file_hash_table_ofs = header.dir_table_ofs + header.dir_table_size;
|
||||
header.file_table_ofs = header.file_hash_table_ofs + header.file_hash_table_size;
|
||||
// Write metadata.
|
||||
out.emplace_back(header.dir_hash_table_ofs,
|
||||
std::make_shared<VectorVfsFile>(std::move(metadata)));
|
||||
|
||||
std::vector<u8> header_data(sizeof(RomFSHeader));
|
||||
std::memcpy(header_data.data(), &header, header_data.size());
|
||||
out.emplace(0, std::make_shared<VectorVfsFile>(std::move(header_data)));
|
||||
|
||||
std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
|
||||
dir_table_size);
|
||||
std::size_t index = 0;
|
||||
std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32));
|
||||
index += dir_hash_table.size() * sizeof(u32);
|
||||
std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size());
|
||||
index += dir_table.size();
|
||||
std::memcpy(metadata.data() + index, file_hash_table.data(),
|
||||
file_hash_table.size() * sizeof(u32));
|
||||
index += file_hash_table.size() * sizeof(u32);
|
||||
std::memcpy(metadata.data() + index, file_table.data(), file_table.size());
|
||||
out.emplace(header.dir_hash_table_ofs, std::make_shared<VectorVfsFile>(std::move(metadata)));
|
||||
// Sort the output.
|
||||
std::sort(out.begin(), out.end(),
|
||||
[](const auto& a, const auto& b) { return a.first < b.first; });
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ public:
|
||||
~RomFSBuildContext();
|
||||
|
||||
// This finalizes the context.
|
||||
std::multimap<u64, VirtualFile> Build();
|
||||
std::vector<std::pair<u64, VirtualFile>> Build();
|
||||
|
||||
private:
|
||||
VirtualDir base;
|
||||
VirtualDir ext;
|
||||
std::shared_ptr<RomFSBuildDirectoryContext> root;
|
||||
std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories;
|
||||
std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files;
|
||||
std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> directories;
|
||||
std::vector<std::shared_ptr<RomFSBuildFileContext>> files;
|
||||
u64 num_dirs = 0;
|
||||
u64 num_files = 0;
|
||||
u64 dir_table_size = 0;
|
||||
|
||||
@@ -55,44 +55,68 @@ struct FileEntry {
|
||||
};
|
||||
static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
|
||||
|
||||
template <typename Entry>
|
||||
std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) {
|
||||
Entry entry{};
|
||||
if (file->ReadObject(&entry, offset) != sizeof(Entry))
|
||||
struct RomFSTraversalContext {
|
||||
RomFSHeader header;
|
||||
VirtualFile file;
|
||||
std::vector<u8> directory_meta;
|
||||
std::vector<u8> file_meta;
|
||||
};
|
||||
|
||||
template <typename EntryType, auto Member>
|
||||
std::pair<EntryType, std::string> GetEntry(const RomFSTraversalContext& ctx, size_t offset) {
|
||||
const size_t entry_end = offset + sizeof(EntryType);
|
||||
const std::vector<u8>& vec = ctx.*Member;
|
||||
const size_t size = vec.size();
|
||||
const u8* data = vec.data();
|
||||
EntryType entry{};
|
||||
|
||||
if (entry_end > size) {
|
||||
return {};
|
||||
std::string string(entry.name_length, '\0');
|
||||
if (file->ReadArray(&string[0], string.size(), offset + sizeof(Entry)) != string.size())
|
||||
return {};
|
||||
return {entry, string};
|
||||
}
|
||||
std::memcpy(&entry, data + offset, sizeof(EntryType));
|
||||
|
||||
const size_t name_length = std::min(entry_end + entry.name_length, size) - entry_end;
|
||||
std::string name(reinterpret_cast<const char*>(data + entry_end), name_length);
|
||||
|
||||
return {entry, std::move(name)};
|
||||
}
|
||||
|
||||
void ProcessFile(const VirtualFile& file, std::size_t file_offset, std::size_t data_offset,
|
||||
u32 this_file_offset, std::shared_ptr<VectorVfsDirectory>& parent) {
|
||||
while (this_file_offset != ROMFS_ENTRY_EMPTY) {
|
||||
auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
|
||||
std::pair<DirectoryEntry, std::string> GetDirectoryEntry(const RomFSTraversalContext& ctx,
|
||||
size_t directory_offset) {
|
||||
return GetEntry<DirectoryEntry, &RomFSTraversalContext::directory_meta>(ctx, directory_offset);
|
||||
}
|
||||
|
||||
parent->AddFile(std::make_shared<OffsetVfsFile>(
|
||||
file, entry.first.size, entry.first.offset + data_offset, entry.second));
|
||||
std::pair<FileEntry, std::string> GetFileEntry(const RomFSTraversalContext& ctx,
|
||||
size_t file_offset) {
|
||||
return GetEntry<FileEntry, &RomFSTraversalContext::file_meta>(ctx, file_offset);
|
||||
}
|
||||
|
||||
void ProcessFile(const RomFSTraversalContext& ctx, u32 this_file_offset,
|
||||
std::shared_ptr<VectorVfsDirectory>& parent) {
|
||||
while (this_file_offset != ROMFS_ENTRY_EMPTY) {
|
||||
auto entry = GetFileEntry(ctx, this_file_offset);
|
||||
|
||||
parent->AddFile(std::make_shared<OffsetVfsFile>(ctx.file, entry.first.size,
|
||||
entry.first.offset + ctx.header.data_offset,
|
||||
std::move(entry.second)));
|
||||
|
||||
this_file_offset = entry.first.sibling;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size_t file_offset,
|
||||
std::size_t data_offset, u32 this_dir_offset,
|
||||
void ProcessDirectory(const RomFSTraversalContext& ctx, u32 this_dir_offset,
|
||||
std::shared_ptr<VectorVfsDirectory>& parent) {
|
||||
while (this_dir_offset != ROMFS_ENTRY_EMPTY) {
|
||||
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
|
||||
auto entry = GetDirectoryEntry(ctx, this_dir_offset);
|
||||
auto current = std::make_shared<VectorVfsDirectory>(
|
||||
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second);
|
||||
|
||||
if (entry.first.child_file != ROMFS_ENTRY_EMPTY) {
|
||||
ProcessFile(file, file_offset, data_offset, entry.first.child_file, current);
|
||||
ProcessFile(ctx, entry.first.child_file, current);
|
||||
}
|
||||
|
||||
if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) {
|
||||
ProcessDirectory(file, dir_offset, file_offset, data_offset, entry.first.child_dir,
|
||||
current);
|
||||
ProcessDirectory(ctx, entry.first.child_dir, current);
|
||||
}
|
||||
|
||||
parent->AddDirectory(current);
|
||||
@@ -107,22 +131,25 @@ VirtualDir ExtractRomFS(VirtualFile file) {
|
||||
return root_container;
|
||||
}
|
||||
|
||||
RomFSHeader header{};
|
||||
if (file->ReadObject(&header) != sizeof(RomFSHeader)) {
|
||||
return root_container;
|
||||
RomFSTraversalContext ctx{};
|
||||
|
||||
if (file->ReadObject(&ctx.header) != sizeof(RomFSHeader)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (header.header_size != sizeof(RomFSHeader)) {
|
||||
return root_container;
|
||||
if (ctx.header.header_size != sizeof(RomFSHeader)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const u64 file_offset = header.file_meta.offset;
|
||||
const u64 dir_offset = header.directory_meta.offset;
|
||||
ctx.file = file;
|
||||
ctx.directory_meta =
|
||||
file->ReadBytes(ctx.header.directory_meta.size, ctx.header.directory_meta.offset);
|
||||
ctx.file_meta = file->ReadBytes(ctx.header.file_meta.size, ctx.header.file_meta.offset);
|
||||
|
||||
ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container);
|
||||
ProcessDirectory(ctx, 0, root_container);
|
||||
|
||||
if (auto root = root_container->GetSubdirectory(""); root) {
|
||||
return std::make_shared<CachedVfsDirectory>(std::move(root));
|
||||
return root;
|
||||
}
|
||||
|
||||
ASSERT(false);
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
|
||||
|
||||
namespace {
|
||||
|
||||
void PrintSaveDataAttributeWarnings(SaveDataAttribute meta) {
|
||||
@@ -197,7 +195,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
|
||||
GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
|
||||
|
||||
const auto size_file = relative_dir->GetFile(SAVE_DATA_SIZE_FILENAME);
|
||||
const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName());
|
||||
if (size_file == nullptr || size_file->GetSize() < sizeof(SaveDataSize)) {
|
||||
return {0, 0};
|
||||
}
|
||||
@@ -216,7 +214,7 @@ void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 us
|
||||
GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
|
||||
|
||||
const auto size_file = relative_dir->CreateFile(SAVE_DATA_SIZE_FILENAME);
|
||||
const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());
|
||||
if (size_file == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,10 @@ struct SaveDataSize {
|
||||
u64 journal;
|
||||
};
|
||||
|
||||
constexpr const char* GetSaveDataSizeFileName() {
|
||||
return ".yuzu_save_size";
|
||||
}
|
||||
|
||||
/// File system interface to the SaveData archive
|
||||
class SaveDataFactory {
|
||||
public:
|
||||
|
||||
@@ -201,8 +201,6 @@ std::string VfsFile::GetFullPath() const {
|
||||
|
||||
VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
|
||||
auto vec = Common::FS::SplitPathComponents(path);
|
||||
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
||||
vec.end());
|
||||
if (vec.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -237,8 +235,6 @@ VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const {
|
||||
|
||||
VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const {
|
||||
auto vec = Common::FS::SplitPathComponents(path);
|
||||
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
||||
vec.end());
|
||||
if (vec.empty()) {
|
||||
// TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently
|
||||
// because of const-ness
|
||||
@@ -303,8 +299,6 @@ std::size_t VfsDirectory::GetSize() const {
|
||||
|
||||
VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) {
|
||||
auto vec = Common::FS::SplitPathComponents(path);
|
||||
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
||||
vec.end());
|
||||
if (vec.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -334,8 +328,6 @@ VirtualFile VfsDirectory::CreateFileAbsolute(std::string_view path) {
|
||||
|
||||
VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) {
|
||||
auto vec = Common::FS::SplitPathComponents(path);
|
||||
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
||||
vec.end());
|
||||
if (vec.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name,
|
||||
return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
|
||||
}
|
||||
|
||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, std::string&& name,
|
||||
std::multimap<u64, VirtualFile>&& files) {
|
||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(
|
||||
u8 filler_byte, std::string&& name, std::vector<std::pair<u64, VirtualFile>>&& files) {
|
||||
// Fold trivial cases.
|
||||
if (files.empty()) {
|
||||
return nullptr;
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
/// Convenience function that turns a map of offsets to files into a concatenated file, filling
|
||||
/// gaps with a given filler byte.
|
||||
static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name,
|
||||
std::multimap<u64, VirtualFile>&& files);
|
||||
std::vector<std::pair<u64, VirtualFile>>&& files);
|
||||
|
||||
std::string GetName() const override;
|
||||
std::size_t GetSize() const override;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include "core/file_sys/vfs_layered.h"
|
||||
|
||||
@@ -59,13 +60,12 @@ std::string LayeredVfsDirectory::GetFullPath() const {
|
||||
|
||||
std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
||||
std::vector<VirtualFile> out;
|
||||
std::set<std::string, std::less<>> out_names;
|
||||
std::unordered_set<std::string> out_names;
|
||||
|
||||
for (const auto& layer : dirs) {
|
||||
for (auto& file : layer->GetFiles()) {
|
||||
auto file_name = file->GetName();
|
||||
if (!out_names.contains(file_name)) {
|
||||
out_names.emplace(std::move(file_name));
|
||||
const auto [it, is_new] = out_names.emplace(file->GetName());
|
||||
if (is_new) {
|
||||
out.emplace_back(std::move(file));
|
||||
}
|
||||
}
|
||||
@@ -75,18 +75,19 @@ std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
||||
}
|
||||
|
||||
std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
|
||||
std::vector<std::string> names;
|
||||
std::vector<VirtualDir> out;
|
||||
std::unordered_set<std::string> out_names;
|
||||
|
||||
for (const auto& layer : dirs) {
|
||||
for (const auto& sd : layer->GetSubdirectories()) {
|
||||
if (std::find(names.begin(), names.end(), sd->GetName()) == names.end())
|
||||
names.push_back(sd->GetName());
|
||||
out_names.emplace(sd->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<VirtualDir> out;
|
||||
out.reserve(names.size());
|
||||
for (const auto& subdir : names)
|
||||
out.reserve(out_names.size());
|
||||
for (const auto& subdir : out_names) {
|
||||
out.emplace_back(GetSubdirectory(subdir));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference)
|
||||
RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_,
|
||||
const std::string& path_, Mode perms_, std::optional<u64> size_)
|
||||
: base(base_), reference(std::move(reference_)), path(path_),
|
||||
parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponents(path_)),
|
||||
parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)),
|
||||
size(size_), perms(perms_) {}
|
||||
|
||||
RealVfsFile::~RealVfsFile() {
|
||||
@@ -276,7 +276,7 @@ RealVfsFile::~RealVfsFile() {
|
||||
}
|
||||
|
||||
std::string RealVfsFile::GetName() const {
|
||||
return path_components.back();
|
||||
return path_components.empty() ? "" : std::string(path_components.back());
|
||||
}
|
||||
|
||||
std::size_t RealVfsFile::GetSize() const {
|
||||
@@ -375,7 +375,7 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
|
||||
|
||||
RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
|
||||
: base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
|
||||
path_components(FS::SplitPathComponents(path)), perms(perms_) {
|
||||
path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) {
|
||||
if (!FS::Exists(path) && True(perms & Mode::Write)) {
|
||||
void(FS::CreateDirs(path));
|
||||
}
|
||||
@@ -464,7 +464,7 @@ bool RealVfsDirectory::IsReadable() const {
|
||||
}
|
||||
|
||||
std::string RealVfsDirectory::GetName() const {
|
||||
return path_components.back();
|
||||
return path_components.empty() ? "" : std::string(path_components.back());
|
||||
}
|
||||
|
||||
VirtualDir RealVfsDirectory::GetParentDirectory() const {
|
||||
|
||||
@@ -20,6 +20,9 @@ InputInterpreter::InputInterpreter(Core::System& system)
|
||||
InputInterpreter::~InputInterpreter() = default;
|
||||
|
||||
void InputInterpreter::PollInput() {
|
||||
if (npad == nullptr) {
|
||||
return;
|
||||
}
|
||||
const auto button_state = npad->GetAndResetPressState();
|
||||
|
||||
previous_index = current_index;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/hle/kernel/k_client_port.h"
|
||||
#include "core/hle/kernel/k_light_session.h"
|
||||
#include "core/hle/kernel/k_port.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||
@@ -63,6 +64,7 @@ Result KClientPort::CreateSession(KClientSession** out) {
|
||||
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Allocate a session normally.
|
||||
// TODO: Dynamic resource limits
|
||||
session = KSession::Create(m_kernel);
|
||||
|
||||
// Check that we successfully created a session.
|
||||
@@ -119,4 +121,71 @@ Result KClientPort::CreateSession(KClientSession** out) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KClientPort::CreateLightSession(KLightClientSession** out) {
|
||||
// Declare the session we're going to allocate.
|
||||
KLightSession* session{};
|
||||
|
||||
// Reserve a new session from the resource limit.
|
||||
KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel),
|
||||
Svc::LimitableResource::SessionCountMax);
|
||||
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Allocate a session normally.
|
||||
// TODO: Dynamic resource limits
|
||||
session = KLightSession::Create(m_kernel);
|
||||
|
||||
// Check that we successfully created a session.
|
||||
R_UNLESS(session != nullptr, ResultOutOfResource);
|
||||
|
||||
// Update the session counts.
|
||||
{
|
||||
ON_RESULT_FAILURE {
|
||||
session->Close();
|
||||
};
|
||||
|
||||
// Atomically increment the number of sessions.
|
||||
s32 new_sessions;
|
||||
{
|
||||
const auto max = m_max_sessions;
|
||||
auto cur_sessions = m_num_sessions.load(std::memory_order_acquire);
|
||||
do {
|
||||
R_UNLESS(cur_sessions < max, ResultOutOfSessions);
|
||||
new_sessions = cur_sessions + 1;
|
||||
} while (!m_num_sessions.compare_exchange_weak(cur_sessions, new_sessions,
|
||||
std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
// Atomically update the peak session tracking.
|
||||
{
|
||||
auto peak = m_peak_sessions.load(std::memory_order_acquire);
|
||||
do {
|
||||
if (peak >= new_sessions) {
|
||||
break;
|
||||
}
|
||||
} while (!m_peak_sessions.compare_exchange_weak(peak, new_sessions,
|
||||
std::memory_order_relaxed));
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the session.
|
||||
session->Initialize(this, m_parent->GetName());
|
||||
|
||||
// Commit the session reservation.
|
||||
session_reservation.Commit();
|
||||
|
||||
// Register the session.
|
||||
KLightSession::Register(m_kernel, session);
|
||||
ON_RESULT_FAILURE {
|
||||
session->GetClientSession().Close();
|
||||
session->GetServerSession().Close();
|
||||
};
|
||||
|
||||
// Enqueue the session with our parent.
|
||||
R_TRY(m_parent->EnqueueSession(std::addressof(session->GetServerSession())));
|
||||
|
||||
// We succeeded, so set the output.
|
||||
*out = std::addressof(session->GetClientSession());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KLightClientSession;
|
||||
class KClientSession;
|
||||
class KernelCore;
|
||||
class KPort;
|
||||
@@ -51,6 +52,7 @@ public:
|
||||
bool IsSignaled() const override;
|
||||
|
||||
Result CreateSession(KClientSession** out);
|
||||
Result CreateLightSession(KLightClientSession** out);
|
||||
|
||||
private:
|
||||
std::atomic<s32> m_num_sessions{};
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
static constexpr u32 MessageBufferSize = 0x100;
|
||||
|
||||
KClientSession::KClientSession(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel} {}
|
||||
KClientSession::KClientSession(KernelCore& kernel) : KAutoObject{kernel} {}
|
||||
KClientSession::~KClientSession() = default;
|
||||
|
||||
void KClientSession::Destroy() {
|
||||
@@ -22,18 +20,30 @@ void KClientSession::Destroy() {
|
||||
|
||||
void KClientSession::OnServerClosed() {}
|
||||
|
||||
Result KClientSession::SendSyncRequest() {
|
||||
Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) {
|
||||
// Create a session request.
|
||||
KSessionRequest* request = KSessionRequest::Create(m_kernel);
|
||||
R_UNLESS(request != nullptr, ResultOutOfResource);
|
||||
SCOPE_EXIT({ request->Close(); });
|
||||
|
||||
// Initialize the request.
|
||||
request->Initialize(nullptr, GetInteger(GetCurrentThread(m_kernel).GetTlsAddress()),
|
||||
MessageBufferSize);
|
||||
request->Initialize(nullptr, address, size);
|
||||
|
||||
// Send the request.
|
||||
R_RETURN(m_parent->GetServerSession().OnRequest(request));
|
||||
R_RETURN(m_parent->OnRequest(request));
|
||||
}
|
||||
|
||||
Result KClientSession::SendAsyncRequest(KEvent* event, uintptr_t address, size_t size) {
|
||||
// Create a session request.
|
||||
KSessionRequest* request = KSessionRequest::Create(m_kernel);
|
||||
R_UNLESS(request != nullptr, ResultOutOfResource);
|
||||
SCOPE_EXIT({ request->Close(); });
|
||||
|
||||
// Initialize the request.
|
||||
request->Initialize(event, address, size);
|
||||
|
||||
// Send the request.
|
||||
R_RETURN(m_parent->OnRequest(request));
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -9,24 +9,12 @@
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
union Result;
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class KSession;
|
||||
class KThread;
|
||||
|
||||
class KClientSession final
|
||||
: public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
|
||||
class KClientSession final : public KAutoObject {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject);
|
||||
|
||||
public:
|
||||
@@ -39,13 +27,13 @@ public:
|
||||
}
|
||||
|
||||
void Destroy() override;
|
||||
static void PostDestroy(uintptr_t arg) {}
|
||||
|
||||
KSession* GetParent() const {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
Result SendSyncRequest();
|
||||
Result SendSyncRequest(uintptr_t address, size_t size);
|
||||
Result SendAsyncRequest(KEvent* event, uintptr_t address, size_t size);
|
||||
|
||||
void OnServerClosed();
|
||||
|
||||
|
||||
31
src/core/hle/kernel/k_light_client_session.cpp
Normal file
31
src/core/hle/kernel/k_light_client_session.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/kernel/k_light_client_session.h"
|
||||
#include "core/hle/kernel/k_light_session.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KLightClientSession::KLightClientSession(KernelCore& kernel) : KAutoObject(kernel) {}
|
||||
|
||||
KLightClientSession::~KLightClientSession() = default;
|
||||
|
||||
void KLightClientSession::Destroy() {
|
||||
m_parent->OnClientClosed();
|
||||
}
|
||||
|
||||
void KLightClientSession::OnServerClosed() {}
|
||||
|
||||
Result KLightClientSession::SendSyncRequest(u32* data) {
|
||||
// Get the request thread.
|
||||
KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
|
||||
|
||||
// Set the light data.
|
||||
cur_thread->SetLightSessionData(data);
|
||||
|
||||
// Send the request.
|
||||
R_RETURN(m_parent->OnRequest(cur_thread));
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
39
src/core/hle/kernel/k_light_client_session.h
Normal file
39
src/core/hle/kernel/k_light_client_session.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KLightSession;
|
||||
|
||||
class KLightClientSession final : public KAutoObject {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KLightClientSession, KAutoObject);
|
||||
|
||||
public:
|
||||
explicit KLightClientSession(KernelCore& kernel);
|
||||
~KLightClientSession();
|
||||
|
||||
void Initialize(KLightSession* parent) {
|
||||
// Set member variables.
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
virtual void Destroy() override;
|
||||
|
||||
const KLightSession* GetParent() const {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
Result SendSyncRequest(u32* data);
|
||||
|
||||
void OnServerClosed();
|
||||
|
||||
private:
|
||||
KLightSession* m_parent;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
247
src/core/hle/kernel/k_light_server_session.cpp
Normal file
247
src/core/hle/kernel/k_light_server_session.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/kernel/k_light_server_session.h"
|
||||
#include "core/hle/kernel/k_light_session.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u64 InvalidThreadId = std::numeric_limits<u64>::max();
|
||||
|
||||
class ThreadQueueImplForKLightServerSessionRequest final : public KThreadQueue {
|
||||
private:
|
||||
KThread::WaiterList* m_wait_list;
|
||||
|
||||
public:
|
||||
ThreadQueueImplForKLightServerSessionRequest(KernelCore& kernel, KThread::WaiterList* wl)
|
||||
: KThreadQueue(kernel), m_wait_list(wl) {}
|
||||
|
||||
virtual void EndWait(KThread* waiting_thread, Result wait_result) override {
|
||||
// Remove the thread from our wait list.
|
||||
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
|
||||
|
||||
// Invoke the base end wait handler.
|
||||
KThreadQueue::EndWait(waiting_thread, wait_result);
|
||||
}
|
||||
|
||||
virtual void CancelWait(KThread* waiting_thread, Result wait_result,
|
||||
bool cancel_timer_task) override {
|
||||
// Remove the thread from our wait list.
|
||||
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
};
|
||||
|
||||
class ThreadQueueImplForKLightServerSessionReceive final : public KThreadQueue {
|
||||
private:
|
||||
KThread** m_server_thread;
|
||||
|
||||
public:
|
||||
ThreadQueueImplForKLightServerSessionReceive(KernelCore& kernel, KThread** st)
|
||||
: KThreadQueue(kernel), m_server_thread(st) {}
|
||||
|
||||
virtual void EndWait(KThread* waiting_thread, Result wait_result) override {
|
||||
// Clear the server thread.
|
||||
*m_server_thread = nullptr;
|
||||
|
||||
// Set the waiting thread as not cancelable.
|
||||
waiting_thread->ClearCancellable();
|
||||
|
||||
// Invoke the base end wait handler.
|
||||
KThreadQueue::EndWait(waiting_thread, wait_result);
|
||||
}
|
||||
|
||||
virtual void CancelWait(KThread* waiting_thread, Result wait_result,
|
||||
bool cancel_timer_task) override {
|
||||
// Clear the server thread.
|
||||
*m_server_thread = nullptr;
|
||||
|
||||
// Set the waiting thread as not cancelable.
|
||||
waiting_thread->ClearCancellable();
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
KLightServerSession::KLightServerSession(KernelCore& kernel) : KAutoObject(kernel) {}
|
||||
KLightServerSession::~KLightServerSession() = default;
|
||||
|
||||
void KLightServerSession::Destroy() {
|
||||
this->CleanupRequests();
|
||||
|
||||
m_parent->OnServerClosed();
|
||||
}
|
||||
|
||||
void KLightServerSession::OnClientClosed() {
|
||||
this->CleanupRequests();
|
||||
}
|
||||
|
||||
Result KLightServerSession::OnRequest(KThread* request_thread) {
|
||||
ThreadQueueImplForKLightServerSessionRequest wait_queue(m_kernel,
|
||||
std::addressof(m_request_list));
|
||||
|
||||
// Send the request.
|
||||
{
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl(m_kernel);
|
||||
|
||||
// Check that the server isn't closed.
|
||||
R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
|
||||
|
||||
// Check that the request thread isn't terminating.
|
||||
R_UNLESS(!request_thread->IsTerminationRequested(), ResultTerminationRequested);
|
||||
|
||||
// Add the request thread to our list.
|
||||
m_request_list.push_back(*request_thread);
|
||||
|
||||
// Begin waiting on the request.
|
||||
request_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
||||
request_thread->BeginWait(std::addressof(wait_queue));
|
||||
|
||||
// If we have a server thread, end its wait.
|
||||
if (m_server_thread != nullptr) {
|
||||
m_server_thread->EndWait(ResultSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Nintendo returns GetCurrentThread().GetWaitResult() here.
|
||||
// This is technically incorrect, although it doesn't cause problems in practice
|
||||
// because this is only ever called with request_thread = GetCurrentThreadPointer().
|
||||
R_RETURN(request_thread->GetWaitResult());
|
||||
}
|
||||
|
||||
Result KLightServerSession::ReplyAndReceive(u32* data) {
|
||||
// Set the server context.
|
||||
GetCurrentThread(m_kernel).SetLightSessionData(data);
|
||||
|
||||
// Reply, if we need to.
|
||||
if (data[0] & KLightSession::ReplyFlag) {
|
||||
KScopedSchedulerLock sl(m_kernel);
|
||||
|
||||
// Check that we're open.
|
||||
R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
|
||||
R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
|
||||
|
||||
// Check that we have a request to reply to.
|
||||
R_UNLESS(m_current_request != nullptr, ResultInvalidState);
|
||||
|
||||
// Check that the server thread id is correct.
|
||||
R_UNLESS(m_server_thread_id == GetCurrentThread(m_kernel).GetId(), ResultInvalidState);
|
||||
|
||||
// If we can reply, do so.
|
||||
if (!m_current_request->IsTerminationRequested()) {
|
||||
std::memcpy(m_current_request->GetLightSessionData(),
|
||||
GetCurrentThread(m_kernel).GetLightSessionData(), KLightSession::DataSize);
|
||||
m_current_request->EndWait(ResultSuccess);
|
||||
}
|
||||
|
||||
// Close our current request.
|
||||
m_current_request->Close();
|
||||
|
||||
// Clear our current request.
|
||||
m_current_request = nullptr;
|
||||
m_server_thread_id = InvalidThreadId;
|
||||
}
|
||||
|
||||
// Create the wait queue for our receive.
|
||||
ThreadQueueImplForKLightServerSessionReceive wait_queue(m_kernel,
|
||||
std::addressof(m_server_thread));
|
||||
|
||||
// Receive.
|
||||
while (true) {
|
||||
// Try to receive a request.
|
||||
{
|
||||
KScopedSchedulerLock sl(m_kernel);
|
||||
|
||||
// Check that we aren't already receiving.
|
||||
R_UNLESS(m_server_thread == nullptr, ResultInvalidState);
|
||||
R_UNLESS(m_server_thread_id == InvalidThreadId, ResultInvalidState);
|
||||
|
||||
// Check that we're open.
|
||||
R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
|
||||
R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
|
||||
|
||||
// Check that we're not terminating.
|
||||
R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(),
|
||||
ResultTerminationRequested);
|
||||
|
||||
// If we have a request available, use it.
|
||||
if (auto head = m_request_list.begin(); head != m_request_list.end()) {
|
||||
// Set our current request.
|
||||
m_current_request = std::addressof(*head);
|
||||
m_current_request->Open();
|
||||
|
||||
// Set our server thread id.
|
||||
m_server_thread_id = GetCurrentThread(m_kernel).GetId();
|
||||
|
||||
// Copy the client request data.
|
||||
std::memcpy(GetCurrentThread(m_kernel).GetLightSessionData(),
|
||||
m_current_request->GetLightSessionData(), KLightSession::DataSize);
|
||||
|
||||
// We successfully received.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// We need to wait for a request to come in.
|
||||
|
||||
// Check if we were cancelled.
|
||||
if (GetCurrentThread(m_kernel).IsWaitCancelled()) {
|
||||
GetCurrentThread(m_kernel).ClearWaitCancelled();
|
||||
R_THROW(ResultCancelled);
|
||||
}
|
||||
|
||||
// Mark ourselves as cancellable.
|
||||
GetCurrentThread(m_kernel).SetCancellable();
|
||||
|
||||
// Wait for a request to come in.
|
||||
m_server_thread = GetCurrentThreadPointer(m_kernel);
|
||||
GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
||||
GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue));
|
||||
}
|
||||
|
||||
// We waited to receive a request; if our wait failed, return the failing result.
|
||||
R_TRY(GetCurrentThread(m_kernel).GetWaitResult());
|
||||
}
|
||||
}
|
||||
|
||||
void KLightServerSession::CleanupRequests() {
|
||||
// Cleanup all pending requests.
|
||||
{
|
||||
KScopedSchedulerLock sl(m_kernel);
|
||||
|
||||
// Handle the current request.
|
||||
if (m_current_request != nullptr) {
|
||||
// Reply to the current request.
|
||||
if (!m_current_request->IsTerminationRequested()) {
|
||||
m_current_request->EndWait(ResultSessionClosed);
|
||||
}
|
||||
|
||||
// Clear our current request.
|
||||
m_current_request->Close();
|
||||
m_current_request = nullptr;
|
||||
m_server_thread_id = InvalidThreadId;
|
||||
}
|
||||
|
||||
// Reply to all other requests.
|
||||
for (auto& thread : m_request_list) {
|
||||
thread.EndWait(ResultSessionClosed);
|
||||
}
|
||||
|
||||
// Wait up our server thread, if we have one.
|
||||
if (m_server_thread != nullptr) {
|
||||
m_server_thread->EndWait(ResultSessionClosed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
49
src/core/hle/kernel/k_light_server_session.h
Normal file
49
src/core/hle/kernel/k_light_server_session.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KLightSession;
|
||||
|
||||
class KLightServerSession final : public KAutoObject,
|
||||
public Common::IntrusiveListBaseNode<KLightServerSession> {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KLightServerSession, KAutoObject);
|
||||
|
||||
private:
|
||||
KLightSession* m_parent{};
|
||||
KThread::WaiterList m_request_list{};
|
||||
KThread* m_current_request{};
|
||||
u64 m_server_thread_id{std::numeric_limits<u64>::max()};
|
||||
KThread* m_server_thread{};
|
||||
|
||||
public:
|
||||
explicit KLightServerSession(KernelCore& kernel);
|
||||
~KLightServerSession();
|
||||
|
||||
void Initialize(KLightSession* parent) {
|
||||
// Set member variables. */
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
virtual void Destroy() override;
|
||||
|
||||
constexpr const KLightSession* GetParent() const {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
Result OnRequest(KThread* request_thread);
|
||||
Result ReplyAndReceive(u32* data);
|
||||
|
||||
void OnClientClosed();
|
||||
|
||||
private:
|
||||
void CleanupRequests();
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
81
src/core/hle/kernel/k_light_session.cpp
Normal file
81
src/core/hle/kernel/k_light_session.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/kernel/k_client_port.h"
|
||||
#include "core/hle/kernel/k_light_client_session.h"
|
||||
#include "core/hle/kernel/k_light_server_session.h"
|
||||
#include "core/hle/kernel/k_light_session.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KLightSession::KLightSession(KernelCore& kernel)
|
||||
: KAutoObjectWithSlabHeapAndContainer(kernel), m_server(kernel), m_client(kernel) {}
|
||||
KLightSession::~KLightSession() = default;
|
||||
|
||||
void KLightSession::Initialize(KClientPort* client_port, uintptr_t name) {
|
||||
// Increment reference count.
|
||||
// Because reference count is one on creation, this will result
|
||||
// in a reference count of two. Thus, when both server and client are closed
|
||||
// this object will be destroyed.
|
||||
this->Open();
|
||||
|
||||
// Create our sub sessions.
|
||||
KAutoObject::Create(std::addressof(m_server));
|
||||
KAutoObject::Create(std::addressof(m_client));
|
||||
|
||||
// Initialize our sub sessions.
|
||||
m_server.Initialize(this);
|
||||
m_client.Initialize(this);
|
||||
|
||||
// Set state and name.
|
||||
m_state = State::Normal;
|
||||
m_name = name;
|
||||
|
||||
// Set our owner process.
|
||||
m_process = GetCurrentProcessPointer(m_kernel);
|
||||
m_process->Open();
|
||||
|
||||
// Set our port.
|
||||
m_port = client_port;
|
||||
if (m_port != nullptr) {
|
||||
m_port->Open();
|
||||
}
|
||||
|
||||
// Mark initialized.
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void KLightSession::Finalize() {
|
||||
if (m_port != nullptr) {
|
||||
m_port->OnSessionFinalized();
|
||||
m_port->Close();
|
||||
}
|
||||
}
|
||||
|
||||
void KLightSession::OnServerClosed() {
|
||||
if (m_state == State::Normal) {
|
||||
m_state = State::ServerClosed;
|
||||
m_client.OnServerClosed();
|
||||
}
|
||||
|
||||
this->Close();
|
||||
}
|
||||
|
||||
void KLightSession::OnClientClosed() {
|
||||
if (m_state == State::Normal) {
|
||||
m_state = State::ClientClosed;
|
||||
m_server.OnClientClosed();
|
||||
}
|
||||
|
||||
this->Close();
|
||||
}
|
||||
|
||||
void KLightSession::PostDestroy(uintptr_t arg) {
|
||||
// Release the session count resource the owner process holds.
|
||||
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
||||
owner->ReleaseResource(Svc::LimitableResource::SessionCountMax, 1);
|
||||
owner->Close();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
86
src/core/hle/kernel/k_light_session.h
Normal file
86
src/core/hle/kernel/k_light_session.h
Normal file
@@ -0,0 +1,86 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_light_client_session.h"
|
||||
#include "core/hle/kernel/k_light_server_session.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KClientPort;
|
||||
class KProcess;
|
||||
|
||||
// TODO: SupportDynamicExpansion for SlabHeap
|
||||
class KLightSession final
|
||||
: public KAutoObjectWithSlabHeapAndContainer<KLightSession, KAutoObjectWithList> {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KLightSession, KAutoObject);
|
||||
|
||||
private:
|
||||
enum class State : u8 {
|
||||
Invalid = 0,
|
||||
Normal = 1,
|
||||
ClientClosed = 2,
|
||||
ServerClosed = 3,
|
||||
};
|
||||
|
||||
public:
|
||||
static constexpr size_t DataSize = sizeof(u32) * 7;
|
||||
static constexpr u32 ReplyFlag = (1U << 31);
|
||||
|
||||
private:
|
||||
KLightServerSession m_server;
|
||||
KLightClientSession m_client;
|
||||
State m_state{State::Invalid};
|
||||
KClientPort* m_port{};
|
||||
uintptr_t m_name{};
|
||||
KProcess* m_process{};
|
||||
bool m_initialized{};
|
||||
|
||||
public:
|
||||
explicit KLightSession(KernelCore& kernel);
|
||||
~KLightSession();
|
||||
|
||||
void Initialize(KClientPort* client_port, uintptr_t name);
|
||||
void Finalize() override;
|
||||
|
||||
bool IsInitialized() const override {
|
||||
return m_initialized;
|
||||
}
|
||||
uintptr_t GetPostDestroyArgument() const override {
|
||||
return reinterpret_cast<uintptr_t>(m_process);
|
||||
}
|
||||
|
||||
static void PostDestroy(uintptr_t arg);
|
||||
|
||||
void OnServerClosed();
|
||||
void OnClientClosed();
|
||||
|
||||
bool IsServerClosed() const {
|
||||
return m_state != State::Normal;
|
||||
}
|
||||
bool IsClientClosed() const {
|
||||
return m_state != State::Normal;
|
||||
}
|
||||
|
||||
Result OnRequest(KThread* request_thread) {
|
||||
R_RETURN(m_server.OnRequest(request_thread));
|
||||
}
|
||||
|
||||
KLightClientSession& GetClientSession() {
|
||||
return m_client;
|
||||
}
|
||||
KLightServerSession& GetServerSession() {
|
||||
return m_server;
|
||||
}
|
||||
const KLightClientSession& GetClientSession() const {
|
||||
return m_client;
|
||||
}
|
||||
const KLightServerSession& GetServerSession() const {
|
||||
return m_server;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -421,8 +421,9 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
|
||||
} else {
|
||||
// Set all the allocated memory.
|
||||
for (const auto& block : *out) {
|
||||
std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
|
||||
block.GetSize());
|
||||
m_system.DeviceMemory().buffer.ClearBackingRegion(GetInteger(block.GetAddress()) -
|
||||
Core::DramMemoryMap::Base,
|
||||
block.GetSize(), fill_pattern);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,11 @@ void InvalidateInstructionCache(KernelCore& kernel, AddressType addr, u64 size)
|
||||
}
|
||||
}
|
||||
|
||||
void ClearBackingRegion(Core::System& system, KPhysicalAddress addr, u64 size, u32 fill_value) {
|
||||
system.DeviceMemory().buffer.ClearBackingRegion(GetInteger(addr) - Core::DramMemoryMap::Base,
|
||||
size, fill_value);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
Result InvalidateDataCache(AddressType addr, u64 size) {
|
||||
R_SUCCEED();
|
||||
@@ -1363,8 +1368,7 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
|
||||
|
||||
// Clear all the newly allocated pages.
|
||||
for (const auto& it : pg) {
|
||||
std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()),
|
||||
static_cast<u32>(m_heap_fill_value), it.GetSize());
|
||||
ClearBackingRegion(m_system, it.GetAddress(), it.GetSize(), m_heap_fill_value);
|
||||
}
|
||||
|
||||
// Lock the table.
|
||||
@@ -1570,8 +1574,7 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce
|
||||
|
||||
// Clear all pages.
|
||||
for (const auto& it : pg) {
|
||||
std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()),
|
||||
static_cast<u32>(m_heap_fill_value), it.GetSize());
|
||||
ClearBackingRegion(m_system, it.GetAddress(), it.GetSize(), m_heap_fill_value);
|
||||
}
|
||||
|
||||
// Map the pages.
|
||||
@@ -2159,8 +2162,7 @@ Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) {
|
||||
|
||||
// Clear all the newly allocated pages.
|
||||
for (const auto& it : pg) {
|
||||
std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()), m_heap_fill_value,
|
||||
it.GetSize());
|
||||
ClearBackingRegion(m_system, it.GetAddress(), it.GetSize(), m_heap_fill_value);
|
||||
}
|
||||
|
||||
// Map the pages.
|
||||
|
||||
@@ -58,4 +58,13 @@ Result KPort::EnqueueSession(KServerSession* session) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KPort::EnqueueSession(KLightServerSession* session) {
|
||||
KScopedSchedulerLock sl{m_kernel};
|
||||
|
||||
R_UNLESS(m_state == State::Normal, ResultPortClosed);
|
||||
|
||||
m_server.EnqueueSession(session);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KLightServerSession;
|
||||
class KServerSession;
|
||||
|
||||
class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
|
||||
@@ -38,6 +39,7 @@ public:
|
||||
bool IsServerClosed() const;
|
||||
|
||||
Result EnqueueSession(KServerSession* session);
|
||||
Result EnqueueSession(KLightServerSession* session);
|
||||
|
||||
KClientPort& GetClientPort() {
|
||||
return m_client;
|
||||
|
||||
@@ -27,12 +27,14 @@ bool KServerPort::IsLight() const {
|
||||
void KServerPort::CleanupSessions() {
|
||||
// Ensure our preconditions are met.
|
||||
if (this->IsLight()) {
|
||||
UNIMPLEMENTED();
|
||||
ASSERT(m_session_list.empty());
|
||||
} else {
|
||||
ASSERT(m_light_session_list.empty());
|
||||
}
|
||||
|
||||
// Cleanup the session list.
|
||||
while (true) {
|
||||
// Get the last session in the list
|
||||
// Get the last session in the list.
|
||||
KServerSession* session = nullptr;
|
||||
{
|
||||
KScopedSchedulerLock sl{m_kernel};
|
||||
@@ -49,6 +51,26 @@ void KServerPort::CleanupSessions() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup the light session list.
|
||||
while (true) {
|
||||
// Get the last session in the list.
|
||||
KLightServerSession* session = nullptr;
|
||||
{
|
||||
KScopedSchedulerLock sl{m_kernel};
|
||||
if (!m_light_session_list.empty()) {
|
||||
session = std::addressof(m_light_session_list.front());
|
||||
m_light_session_list.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
// Close the session.
|
||||
if (session != nullptr) {
|
||||
session->Close();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KServerPort::Destroy() {
|
||||
@@ -64,8 +86,7 @@ void KServerPort::Destroy() {
|
||||
|
||||
bool KServerPort::IsSignaled() const {
|
||||
if (this->IsLight()) {
|
||||
UNIMPLEMENTED();
|
||||
return false;
|
||||
return !m_light_session_list.empty();
|
||||
} else {
|
||||
return !m_session_list.empty();
|
||||
}
|
||||
@@ -83,6 +104,18 @@ void KServerPort::EnqueueSession(KServerSession* session) {
|
||||
}
|
||||
}
|
||||
|
||||
void KServerPort::EnqueueSession(KLightServerSession* session) {
|
||||
ASSERT(this->IsLight());
|
||||
|
||||
KScopedSchedulerLock sl{m_kernel};
|
||||
|
||||
// Add the session to our queue.
|
||||
m_light_session_list.push_back(*session);
|
||||
if (m_light_session_list.size() == 1) {
|
||||
this->NotifyAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
KServerSession* KServerPort::AcceptSession() {
|
||||
ASSERT(!this->IsLight());
|
||||
|
||||
@@ -98,4 +131,19 @@ KServerSession* KServerPort::AcceptSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
KLightServerSession* KServerPort::AcceptLightSession() {
|
||||
ASSERT(this->IsLight());
|
||||
|
||||
KScopedSchedulerLock sl{m_kernel};
|
||||
|
||||
// Return the first session in the list.
|
||||
if (m_light_session_list.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KLightServerSession* session = std::addressof(m_light_session_list.front());
|
||||
m_light_session_list.pop_front();
|
||||
return session;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "common/intrusive_list.h"
|
||||
|
||||
#include "core/hle/kernel/k_light_server_session.h"
|
||||
#include "core/hle/kernel/k_server_session.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
|
||||
@@ -28,8 +29,10 @@ public:
|
||||
void Initialize(KPort* parent);
|
||||
|
||||
void EnqueueSession(KServerSession* session);
|
||||
void EnqueueSession(KLightServerSession* session);
|
||||
|
||||
KServerSession* AcceptSession();
|
||||
KLightServerSession* AcceptLightSession();
|
||||
|
||||
const KPort* GetParent() const {
|
||||
return m_parent;
|
||||
@@ -43,10 +46,12 @@ public:
|
||||
|
||||
private:
|
||||
using SessionList = Common::IntrusiveListBaseTraits<KServerSession>::ListType;
|
||||
using LightSessionList = Common::IntrusiveListBaseTraits<KLightServerSession>::ListType;
|
||||
|
||||
void CleanupSessions();
|
||||
|
||||
SessionList m_session_list{};
|
||||
LightSessionList m_light_session_list{};
|
||||
KPort* m_parent{};
|
||||
};
|
||||
|
||||
|
||||
@@ -453,6 +453,11 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext
|
||||
size_t client_buffer_size = request->GetSize();
|
||||
// bool recv_list_broken = false;
|
||||
|
||||
if (!client_message) {
|
||||
client_message = GetInteger(client_thread->GetTlsAddress());
|
||||
client_buffer_size = MessageBufferSize;
|
||||
}
|
||||
|
||||
// Receive the message.
|
||||
Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
|
||||
if (out_context != nullptr) {
|
||||
@@ -462,8 +467,7 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext
|
||||
std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread);
|
||||
(*out_context)->SetSessionRequestManager(manager);
|
||||
(*out_context)
|
||||
->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(),
|
||||
cmd_buf);
|
||||
->PopulateFromIncomingCommandBuffer(*client_thread->GetOwnerProcess(), cmd_buf);
|
||||
} else {
|
||||
KThread* server_thread = GetCurrentThreadPointer(m_kernel);
|
||||
KProcess& src_process = *client_thread->GetOwnerProcess();
|
||||
|
||||
@@ -46,6 +46,10 @@ public:
|
||||
return this->GetState() != State::Normal;
|
||||
}
|
||||
|
||||
Result OnRequest(KSessionRequest* request) {
|
||||
R_RETURN(m_server.OnRequest(request));
|
||||
}
|
||||
|
||||
KClientSession& GetClientSession() {
|
||||
return m_client;
|
||||
}
|
||||
|
||||
@@ -385,6 +385,13 @@ public:
|
||||
m_cancellable = false;
|
||||
}
|
||||
|
||||
u32* GetLightSessionData() const {
|
||||
return m_light_ipc_data;
|
||||
}
|
||||
void SetLightSessionData(u32* data) {
|
||||
m_light_ipc_data = data;
|
||||
}
|
||||
|
||||
bool IsTerminationRequested() const {
|
||||
return m_termination_requested || GetRawState() == ThreadState::Terminated;
|
||||
}
|
||||
|
||||
@@ -135,7 +135,6 @@ struct KernelCore::Impl {
|
||||
obj = nullptr;
|
||||
}
|
||||
};
|
||||
CleanupObject(hid_shared_mem);
|
||||
CleanupObject(font_shared_mem);
|
||||
CleanupObject(irs_shared_mem);
|
||||
CleanupObject(time_shared_mem);
|
||||
@@ -744,22 +743,16 @@ struct KernelCore::Impl {
|
||||
void InitializeHackSharedMemory(KernelCore& kernel) {
|
||||
// Setup memory regions for emulated processes
|
||||
// TODO(bunnei): These should not be hardcoded regions initialized within the kernel
|
||||
constexpr std::size_t hid_size{0x40000};
|
||||
constexpr std::size_t font_size{0x1100000};
|
||||
constexpr std::size_t irs_size{0x8000};
|
||||
constexpr std::size_t time_size{0x1000};
|
||||
constexpr std::size_t hidbus_size{0x1000};
|
||||
|
||||
hid_shared_mem = KSharedMemory::Create(system.Kernel());
|
||||
font_shared_mem = KSharedMemory::Create(system.Kernel());
|
||||
irs_shared_mem = KSharedMemory::Create(system.Kernel());
|
||||
time_shared_mem = KSharedMemory::Create(system.Kernel());
|
||||
hidbus_shared_mem = KSharedMemory::Create(system.Kernel());
|
||||
|
||||
hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
|
||||
Svc::MemoryPermission::Read, hid_size);
|
||||
KSharedMemory::Register(kernel, hid_shared_mem);
|
||||
|
||||
font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
|
||||
Svc::MemoryPermission::Read, font_size);
|
||||
KSharedMemory::Register(kernel, font_shared_mem);
|
||||
@@ -1190,14 +1183,6 @@ const KSystemResource& KernelCore::GetSystemSystemResource() const {
|
||||
return *impl->sys_system_resource;
|
||||
}
|
||||
|
||||
Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
|
||||
return *impl->hid_shared_mem;
|
||||
}
|
||||
|
||||
const Kernel::KSharedMemory& KernelCore::GetHidSharedMem() const {
|
||||
return *impl->hid_shared_mem;
|
||||
}
|
||||
|
||||
Kernel::KSharedMemory& KernelCore::GetFontSharedMem() {
|
||||
return *impl->font_shared_mem;
|
||||
}
|
||||
@@ -1340,6 +1325,7 @@ struct KernelCore::SlabHeapContainer {
|
||||
KSlabHeap<KProcess> process;
|
||||
KSlabHeap<KResourceLimit> resource_limit;
|
||||
KSlabHeap<KSession> session;
|
||||
KSlabHeap<KLightSession> light_session;
|
||||
KSlabHeap<KSharedMemory> shared_memory;
|
||||
KSlabHeap<KSharedMemoryInfo> shared_memory_info;
|
||||
KSlabHeap<KThread> thread;
|
||||
@@ -1370,6 +1356,8 @@ KSlabHeap<T>& KernelCore::SlabHeap() {
|
||||
return slab_heap_container->resource_limit;
|
||||
} else if constexpr (std::is_same_v<T, KSession>) {
|
||||
return slab_heap_container->session;
|
||||
} else if constexpr (std::is_same_v<T, KLightSession>) {
|
||||
return slab_heap_container->light_session;
|
||||
} else if constexpr (std::is_same_v<T, KSharedMemory>) {
|
||||
return slab_heap_container->shared_memory;
|
||||
} else if constexpr (std::is_same_v<T, KSharedMemoryInfo>) {
|
||||
@@ -1407,6 +1395,7 @@ template KSlabHeap<KPort>& KernelCore::SlabHeap();
|
||||
template KSlabHeap<KProcess>& KernelCore::SlabHeap();
|
||||
template KSlabHeap<KResourceLimit>& KernelCore::SlabHeap();
|
||||
template KSlabHeap<KSession>& KernelCore::SlabHeap();
|
||||
template KSlabHeap<KLightSession>& KernelCore::SlabHeap();
|
||||
template KSlabHeap<KSharedMemory>& KernelCore::SlabHeap();
|
||||
template KSlabHeap<KSharedMemoryInfo>& KernelCore::SlabHeap();
|
||||
template KSlabHeap<KThread>& KernelCore::SlabHeap();
|
||||
|
||||
@@ -239,12 +239,6 @@ public:
|
||||
/// Gets the system resource manager.
|
||||
const KSystemResource& GetSystemSystemResource() const;
|
||||
|
||||
/// Gets the shared memory object for HID services.
|
||||
Kernel::KSharedMemory& GetHidSharedMem();
|
||||
|
||||
/// Gets the shared memory object for HID services.
|
||||
const Kernel::KSharedMemory& GetHidSharedMem() const;
|
||||
|
||||
/// Gets the shared memory object for font services.
|
||||
Kernel::KSharedMemory& GetFontSharedMem();
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
|
||||
}
|
||||
|
||||
// Handle external interrupt sources.
|
||||
if (interrupt || !m_is_single_core) {
|
||||
if (interrupt || m_is_single_core) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,71 +7,39 @@
|
||||
#include "core/hle/kernel/k_client_session.h"
|
||||
#include "core/hle/kernel/k_hardware_timer.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||
#include "core/hle/kernel/k_server_session.h"
|
||||
#include "core/hle/kernel/k_session.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
/// Makes a blocking IPC call to a service.
|
||||
Result SendSyncRequest(Core::System& system, Handle handle) {
|
||||
// Get the client session from its handle.
|
||||
namespace {
|
||||
|
||||
Result SendSyncRequestImpl(KernelCore& kernel, uintptr_t message, size_t buffer_size,
|
||||
Handle session_handle) {
|
||||
// Get the client session.
|
||||
KScopedAutoObject session =
|
||||
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KClientSession>(handle);
|
||||
GetCurrentProcess(kernel).GetHandleTable().GetObject<KClientSession>(session_handle);
|
||||
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}", handle);
|
||||
// Get the parent, and persist a reference to it until we're done.
|
||||
KScopedAutoObject parent = session->GetParent();
|
||||
ASSERT(parent.IsNotNull());
|
||||
|
||||
R_RETURN(session->SendSyncRequest());
|
||||
// Send the request.
|
||||
R_RETURN(session->SendSyncRequest(message, buffer_size));
|
||||
}
|
||||
|
||||
Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message_buffer,
|
||||
uint64_t message_buffer_size, Handle session_handle) {
|
||||
UNIMPLEMENTED();
|
||||
R_THROW(ResultNotImplemented);
|
||||
}
|
||||
|
||||
Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle,
|
||||
uint64_t message_buffer, uint64_t message_buffer_size,
|
||||
Handle session_handle) {
|
||||
UNIMPLEMENTED();
|
||||
R_THROW(ResultNotImplemented);
|
||||
}
|
||||
|
||||
Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles,
|
||||
Handle reply_target, s64 timeout_ns) {
|
||||
// Ensure number of handles is valid.
|
||||
R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
|
||||
|
||||
// Get the synchronization context.
|
||||
auto& kernel = system.Kernel();
|
||||
auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
|
||||
auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer();
|
||||
auto handles = GetCurrentThread(kernel).GetHandleBuffer();
|
||||
|
||||
// Copy user handles.
|
||||
if (num_handles > 0) {
|
||||
// Get the handles.
|
||||
R_UNLESS(GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(),
|
||||
sizeof(Handle) * num_handles),
|
||||
ResultInvalidPointer);
|
||||
|
||||
// Convert the handles to objects.
|
||||
R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(
|
||||
objs.data(), handles.data(), num_handles),
|
||||
ResultInvalidHandle);
|
||||
}
|
||||
|
||||
// Ensure handles are closed when we're done.
|
||||
SCOPE_EXIT({
|
||||
for (auto i = 0; i < num_handles; ++i) {
|
||||
objs[i]->Close();
|
||||
}
|
||||
});
|
||||
|
||||
Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
|
||||
size_t buffer_size, KPhysicalAddress message_paddr,
|
||||
KSynchronizationObject** objs, int32_t num_objects, Handle reply_target,
|
||||
int64_t timeout_ns) {
|
||||
// Reply to the target, if one is specified.
|
||||
if (reply_target != InvalidHandle) {
|
||||
KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
|
||||
KScopedAutoObject session =
|
||||
GetCurrentProcess(kernel).GetHandleTable().GetObject<KServerSession>(reply_target);
|
||||
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
// If we fail to reply, we want to set the output index to -1.
|
||||
@@ -81,57 +49,223 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad
|
||||
|
||||
// Send the reply.
|
||||
R_TRY(session->SendReply());
|
||||
// R_TRY(session->SendReply(message, buffer_size, message_paddr));
|
||||
}
|
||||
|
||||
// Convert the timeout from nanoseconds to ticks.
|
||||
// NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
|
||||
s64 timeout;
|
||||
if (timeout_ns > 0) {
|
||||
const s64 offset_tick(timeout_ns);
|
||||
if (offset_tick > 0) {
|
||||
timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
|
||||
if (timeout <= 0) {
|
||||
// Receive a message.
|
||||
{
|
||||
// Convert the timeout from nanoseconds to ticks.
|
||||
// NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
|
||||
s64 timeout;
|
||||
if (timeout_ns > 0) {
|
||||
const s64 offset_tick(timeout_ns);
|
||||
if (offset_tick > 0) {
|
||||
timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
|
||||
if (timeout <= 0) {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = timeout_ns;
|
||||
}
|
||||
|
||||
// Wait for a message.
|
||||
while (true) {
|
||||
// Wait for an object.
|
||||
s32 index;
|
||||
Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(),
|
||||
num_handles, timeout);
|
||||
if (result == ResultTimedOut) {
|
||||
R_RETURN(result);
|
||||
timeout = timeout_ns;
|
||||
}
|
||||
|
||||
// Receive the request.
|
||||
if (R_SUCCEEDED(result)) {
|
||||
KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
|
||||
if (session != nullptr) {
|
||||
result = session->ReceiveRequest();
|
||||
if (result == ResultNotFound) {
|
||||
continue;
|
||||
// Wait for a message.
|
||||
while (true) {
|
||||
// Wait for an object.
|
||||
s32 index;
|
||||
Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs,
|
||||
num_objects, timeout);
|
||||
if (ResultTimedOut == result) {
|
||||
R_THROW(result);
|
||||
}
|
||||
|
||||
// Receive the request.
|
||||
if (R_SUCCEEDED(result)) {
|
||||
KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
|
||||
if (session != nullptr) {
|
||||
// result = session->ReceiveRequest(message, buffer_size, message_paddr);
|
||||
result = session->ReceiveRequest();
|
||||
if (ResultNotFound == result) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_index = index;
|
||||
R_RETURN(result);
|
||||
*out_index = index;
|
||||
R_RETURN(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index,
|
||||
uint64_t message_buffer, uint64_t message_buffer_size,
|
||||
uint64_t handles, int32_t num_handles, Handle reply_target,
|
||||
int64_t timeout_ns) {
|
||||
UNIMPLEMENTED();
|
||||
R_THROW(ResultNotImplemented);
|
||||
Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
|
||||
size_t buffer_size, KPhysicalAddress message_paddr,
|
||||
KProcessAddress user_handles, int32_t num_handles, Handle reply_target,
|
||||
int64_t timeout_ns) {
|
||||
// Ensure number of handles is valid.
|
||||
R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange);
|
||||
|
||||
// Get the synchronization context.
|
||||
auto& process = GetCurrentProcess(kernel);
|
||||
auto& thread = GetCurrentThread(kernel);
|
||||
auto& handle_table = process.GetHandleTable();
|
||||
KSynchronizationObject** objs = thread.GetSynchronizationObjectBuffer().data();
|
||||
Handle* handles = thread.GetHandleBuffer().data();
|
||||
|
||||
// Copy user handles.
|
||||
if (num_handles > 0) {
|
||||
// Ensure that we can try to get the handles.
|
||||
R_UNLESS(process.GetPageTable().Contains(user_handles, num_handles * sizeof(Handle)),
|
||||
ResultInvalidPointer);
|
||||
|
||||
// Get the handles
|
||||
R_UNLESS(
|
||||
GetCurrentMemory(kernel).ReadBlock(user_handles, handles, sizeof(Handle) * num_handles),
|
||||
ResultInvalidPointer);
|
||||
|
||||
// Convert the handles to objects.
|
||||
R_UNLESS(
|
||||
handle_table.GetMultipleObjects<KSynchronizationObject>(objs, handles, num_handles),
|
||||
ResultInvalidHandle);
|
||||
}
|
||||
|
||||
// Ensure handles are closed when we're done.
|
||||
SCOPE_EXIT({
|
||||
for (auto i = 0; i < num_handles; ++i) {
|
||||
objs[i]->Close();
|
||||
}
|
||||
});
|
||||
|
||||
R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs,
|
||||
num_handles, reply_target, timeout_ns));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/// Makes a blocking IPC call to a service.
|
||||
Result SendSyncRequest(Core::System& system, Handle session_handle) {
|
||||
R_RETURN(SendSyncRequestImpl(system.Kernel(), 0, 0, session_handle));
|
||||
}
|
||||
|
||||
Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message, uint64_t buffer_size,
|
||||
Handle session_handle) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
// Validate that the message buffer is page aligned and does not overflow.
|
||||
R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress);
|
||||
R_UNLESS(buffer_size > 0, ResultInvalidSize);
|
||||
R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize);
|
||||
R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory);
|
||||
|
||||
// Get the process page table.
|
||||
auto& page_table = GetCurrentProcess(kernel).GetPageTable();
|
||||
|
||||
// Lock the message buffer.
|
||||
R_TRY(page_table.LockForIpcUserBuffer(nullptr, message, buffer_size));
|
||||
|
||||
{
|
||||
// If we fail to send the message, unlock the message buffer.
|
||||
ON_RESULT_FAILURE {
|
||||
page_table.UnlockForIpcUserBuffer(message, buffer_size);
|
||||
};
|
||||
|
||||
// Send the request.
|
||||
ASSERT(message != 0);
|
||||
R_TRY(SendSyncRequestImpl(kernel, message, buffer_size, session_handle));
|
||||
}
|
||||
|
||||
// We successfully processed, so try to unlock the message buffer.
|
||||
R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
|
||||
}
|
||||
|
||||
Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle,
|
||||
uint64_t message, uint64_t buffer_size,
|
||||
Handle session_handle) {
|
||||
// Get the process and handle table.
|
||||
auto& process = GetCurrentProcess(system.Kernel());
|
||||
auto& handle_table = process.GetHandleTable();
|
||||
|
||||
// Reserve a new event from the process resource limit.
|
||||
KScopedResourceReservation event_reservation(std::addressof(process),
|
||||
Svc::LimitableResource::EventCountMax);
|
||||
R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Get the client session.
|
||||
KScopedAutoObject session = process.GetHandleTable().GetObject<KClientSession>(session_handle);
|
||||
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
// Get the parent, and persist a reference to it until we're done.
|
||||
KScopedAutoObject parent = session->GetParent();
|
||||
ASSERT(parent.IsNotNull());
|
||||
|
||||
// Create a new event.
|
||||
KEvent* event = KEvent::Create(system.Kernel());
|
||||
R_UNLESS(event != nullptr, ResultOutOfResource);
|
||||
|
||||
// Initialize the event.
|
||||
event->Initialize(std::addressof(process));
|
||||
|
||||
// Commit our reservation.
|
||||
event_reservation.Commit();
|
||||
|
||||
// At end of scope, kill the standing references to the sub events.
|
||||
SCOPE_EXIT({
|
||||
event->GetReadableEvent().Close();
|
||||
event->Close();
|
||||
});
|
||||
|
||||
// Register the event.
|
||||
KEvent::Register(system.Kernel(), event);
|
||||
|
||||
// Add the readable event to the handle table.
|
||||
R_TRY(handle_table.Add(out_event_handle, std::addressof(event->GetReadableEvent())));
|
||||
|
||||
// Ensure that if we fail to send the request, we close the readable handle.
|
||||
ON_RESULT_FAILURE {
|
||||
handle_table.Remove(*out_event_handle);
|
||||
};
|
||||
|
||||
// Send the async request.
|
||||
R_RETURN(session->SendAsyncRequest(event, message, buffer_size));
|
||||
}
|
||||
|
||||
Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles, s32 num_handles,
|
||||
Handle reply_target, s64 timeout_ns) {
|
||||
R_RETURN(ReplyAndReceiveImpl(system.Kernel(), out_index, 0, 0, 0, handles, num_handles,
|
||||
reply_target, timeout_ns));
|
||||
}
|
||||
|
||||
Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index, uint64_t message,
|
||||
uint64_t buffer_size, uint64_t handles, int32_t num_handles,
|
||||
Handle reply_target, int64_t timeout_ns) {
|
||||
// Validate that the message buffer is page aligned and does not overflow.
|
||||
R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress);
|
||||
R_UNLESS(buffer_size > 0, ResultInvalidSize);
|
||||
R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize);
|
||||
R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory);
|
||||
|
||||
// Get the process page table.
|
||||
auto& page_table = GetCurrentProcess(system.Kernel()).GetPageTable();
|
||||
|
||||
// Lock the message buffer, getting its physical address.
|
||||
KPhysicalAddress message_paddr;
|
||||
R_TRY(page_table.LockForIpcUserBuffer(std::addressof(message_paddr), message, buffer_size));
|
||||
|
||||
{
|
||||
// If we fail to send the message, unlock the message buffer.
|
||||
ON_RESULT_FAILURE {
|
||||
page_table.UnlockForIpcUserBuffer(message, buffer_size);
|
||||
};
|
||||
|
||||
// Reply/Receive the request.
|
||||
ASSERT(message != 0);
|
||||
R_TRY(ReplyAndReceiveImpl(system.Kernel(), out_index, message, buffer_size, message_paddr,
|
||||
handles, num_handles, reply_target, timeout_ns));
|
||||
}
|
||||
|
||||
// We successfully processed, so try to unlock the message buffer.
|
||||
R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
|
||||
}
|
||||
|
||||
Result SendSyncRequest64(Core::System& system, Handle session_handle) {
|
||||
|
||||
@@ -1,21 +1,40 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_light_client_session.h"
|
||||
#include "core/hle/kernel/k_light_server_session.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
Result SendSyncRequestLight(Core::System& system, Handle session_handle, u32* args) {
|
||||
UNIMPLEMENTED();
|
||||
R_THROW(ResultNotImplemented);
|
||||
// Get the light client session from its handle.
|
||||
KScopedAutoObject session = GetCurrentProcess(system.Kernel())
|
||||
.GetHandleTable()
|
||||
.GetObject<KLightClientSession>(session_handle);
|
||||
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
// Send the request.
|
||||
R_TRY(session->SendSyncRequest(args));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ReplyAndReceiveLight(Core::System& system, Handle session_handle, u32* args) {
|
||||
UNIMPLEMENTED();
|
||||
R_THROW(ResultNotImplemented);
|
||||
// Get the light server session from its handle.
|
||||
KScopedAutoObject session = GetCurrentProcess(system.Kernel())
|
||||
.GetHandleTable()
|
||||
.GetObject<KLightServerSession>(session_handle);
|
||||
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
// Handle the request.
|
||||
R_TRY(session->ReplyAndReceive(args));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SendSyncRequestLight64(Core::System& system, Handle session_handle, u32* args) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_client_port.h"
|
||||
#include "core/hle/kernel/k_client_session.h"
|
||||
#include "core/hle/kernel/k_light_client_session.h"
|
||||
#include "core/hle/kernel/k_object_name.h"
|
||||
#include "core/hle/kernel/k_port.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
@@ -51,13 +52,73 @@ Result ConnectToNamedPort(Core::System& system, Handle* out, u64 user_name) {
|
||||
|
||||
Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client,
|
||||
int32_t max_sessions, bool is_light, uint64_t name) {
|
||||
UNIMPLEMENTED();
|
||||
R_THROW(ResultNotImplemented);
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
// Ensure max sessions is valid.
|
||||
R_UNLESS(max_sessions > 0, ResultOutOfRange);
|
||||
|
||||
// Get the current handle table.
|
||||
auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
|
||||
|
||||
// Create a new port.
|
||||
KPort* port = KPort::Create(kernel);
|
||||
R_UNLESS(port != nullptr, ResultOutOfResource);
|
||||
|
||||
// Initialize the port.
|
||||
port->Initialize(max_sessions, is_light, name);
|
||||
|
||||
// Ensure that we clean up the port (and its only references are handle table) on function end.
|
||||
SCOPE_EXIT({
|
||||
port->GetServerPort().Close();
|
||||
port->GetClientPort().Close();
|
||||
});
|
||||
|
||||
// Register the port.
|
||||
KPort::Register(kernel, port);
|
||||
|
||||
// Add the client to the handle table.
|
||||
R_TRY(handle_table.Add(out_client, std::addressof(port->GetClientPort())));
|
||||
|
||||
// Ensure that we maintain a clean handle state on exit.
|
||||
ON_RESULT_FAILURE {
|
||||
handle_table.Remove(*out_client);
|
||||
};
|
||||
|
||||
// Add the server to the handle table.
|
||||
R_RETURN(handle_table.Add(out_server, std::addressof(port->GetServerPort())));
|
||||
}
|
||||
|
||||
Result ConnectToPort(Core::System& system, Handle* out_handle, Handle port) {
|
||||
UNIMPLEMENTED();
|
||||
R_THROW(ResultNotImplemented);
|
||||
Result ConnectToPort(Core::System& system, Handle* out, Handle port) {
|
||||
// Get the current handle table.
|
||||
auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
|
||||
|
||||
// Get the client port.
|
||||
KScopedAutoObject client_port = handle_table.GetObject<KClientPort>(port);
|
||||
R_UNLESS(client_port.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
// Reserve a handle for the port.
|
||||
// NOTE: Nintendo really does write directly to the output handle here.
|
||||
R_TRY(handle_table.Reserve(out));
|
||||
ON_RESULT_FAILURE {
|
||||
handle_table.Unreserve(*out);
|
||||
};
|
||||
|
||||
// Create the session.
|
||||
KAutoObject* session;
|
||||
if (client_port->IsLight()) {
|
||||
R_TRY(client_port->CreateLightSession(
|
||||
reinterpret_cast<KLightClientSession**>(std::addressof(session))));
|
||||
} else {
|
||||
R_TRY(client_port->CreateSession(
|
||||
reinterpret_cast<KClientSession**>(std::addressof(session))));
|
||||
}
|
||||
|
||||
// Register the session.
|
||||
handle_table.Register(*out, session);
|
||||
session->Close();
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t user_name,
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_light_session.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||
#include "core/hle/kernel/k_server_port.h"
|
||||
#include "core/hle/kernel/k_session.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
|
||||
@@ -20,7 +22,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
|
||||
T* session;
|
||||
|
||||
// Reserve a new session from the process resource limit.
|
||||
// FIXME: LimitableResource_SessionCountMax
|
||||
// TODO: Dynamic resource limits
|
||||
KScopedResourceReservation session_reservation(std::addressof(process),
|
||||
LimitableResource::SessionCountMax);
|
||||
if (session_reservation.Succeeded()) {
|
||||
@@ -92,16 +94,42 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
|
||||
Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, bool is_light,
|
||||
u64 name) {
|
||||
if (is_light) {
|
||||
// return CreateSession<KLightSession>(system, out_server, out_client, name);
|
||||
R_THROW(ResultNotImplemented);
|
||||
R_RETURN(CreateSession<KLightSession>(system, out_server, out_client, name));
|
||||
} else {
|
||||
R_RETURN(CreateSession<KSession>(system, out_server, out_client, name));
|
||||
}
|
||||
}
|
||||
|
||||
Result AcceptSession(Core::System& system, Handle* out_handle, Handle port_handle) {
|
||||
UNIMPLEMENTED();
|
||||
R_THROW(ResultNotImplemented);
|
||||
Result AcceptSession(Core::System& system, Handle* out, Handle port_handle) {
|
||||
// Get the current handle table.
|
||||
auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
|
||||
|
||||
// Get the server port.
|
||||
KScopedAutoObject port = handle_table.GetObject<KServerPort>(port_handle);
|
||||
R_UNLESS(port.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
// Reserve an entry for the new session.
|
||||
R_TRY(handle_table.Reserve(out));
|
||||
ON_RESULT_FAILURE {
|
||||
handle_table.Unreserve(*out);
|
||||
};
|
||||
|
||||
// Accept the session.
|
||||
KAutoObject* session;
|
||||
if (port->IsLight()) {
|
||||
session = port->AcceptLightSession();
|
||||
} else {
|
||||
session = port->AcceptSession();
|
||||
}
|
||||
|
||||
// Ensure we accepted successfully.
|
||||
R_UNLESS(session != nullptr, ResultNotFound);
|
||||
|
||||
// Register the session.
|
||||
handle_table.Register(*out, session);
|
||||
session->Close();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateSession64(Core::System& system, Handle* out_server_session_handle,
|
||||
|
||||
@@ -104,11 +104,7 @@ Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) con
|
||||
const auto components = Common::FS::SplitPathComponents(path);
|
||||
std::string relative_path;
|
||||
for (const auto& component : components) {
|
||||
// Skip empty path components
|
||||
if (component.empty()) {
|
||||
continue;
|
||||
}
|
||||
relative_path = Common::FS::SanitizePath(relative_path + '/' + component);
|
||||
relative_path = Common::FS::SanitizePath(fmt::format("{}/{}", relative_path, component));
|
||||
auto new_dir = backing->CreateSubdirectory(relative_path);
|
||||
if (new_dir == nullptr) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
|
||||
@@ -246,7 +246,13 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
|
||||
entries.reserve(entries.size() + new_data.size());
|
||||
|
||||
for (const auto& new_entry : new_data) {
|
||||
entries.emplace_back(new_entry->GetName(), type,
|
||||
auto name = new_entry->GetName();
|
||||
|
||||
if (type == FileSys::EntryType::File && name == FileSys::GetSaveDataSizeFileName()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entries.emplace_back(name, type,
|
||||
type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize());
|
||||
}
|
||||
}
|
||||
|
||||
313
src/core/hle/service/hid/controllers/applet_resource.cpp
Normal file
313
src/core/hle/service/hid/controllers/applet_resource.cpp
Normal file
@@ -0,0 +1,313 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/service/hid/controllers/applet_resource.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_format.h"
|
||||
#include "core/hle/service/hid/errors.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
AppletResource::AppletResource(Core::System& system_) : system{system_} {}
|
||||
|
||||
AppletResource::~AppletResource() = default;
|
||||
|
||||
Result AppletResource::CreateAppletResource(u64 aruid) {
|
||||
const u64 index = GetIndexFromAruid(aruid);
|
||||
|
||||
if (index >= AruidIndexMax) {
|
||||
return ResultAruidNotRegistered;
|
||||
}
|
||||
|
||||
if (data[index].flag.is_assigned) {
|
||||
return ResultAruidAlreadyRegistered;
|
||||
}
|
||||
|
||||
auto& shared_memory = shared_memory_holder[index];
|
||||
if (!shared_memory.IsMapped()) {
|
||||
const Result result = shared_memory.Initialize(system);
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
if (shared_memory.GetAddress() == nullptr) {
|
||||
shared_memory.Finalize();
|
||||
return ResultSharedMemoryNotInitialized;
|
||||
}
|
||||
}
|
||||
|
||||
auto* shared_memory_format = shared_memory.GetAddress();
|
||||
if (shared_memory_format != nullptr) {
|
||||
shared_memory_format->Initialize();
|
||||
}
|
||||
|
||||
data[index].shared_memory_format = shared_memory_format;
|
||||
data[index].flag.is_assigned.Assign(true);
|
||||
// TODO: InitializeSixAxisControllerConfig(false);
|
||||
active_aruid = aruid;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result AppletResource::RegisterAppletResourceUserId(u64 aruid, bool enable_input) {
|
||||
const u64 index = GetIndexFromAruid(aruid);
|
||||
|
||||
if (index < AruidIndexMax) {
|
||||
return ResultAruidAlreadyRegistered;
|
||||
}
|
||||
|
||||
std::size_t data_index = AruidIndexMax;
|
||||
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
||||
if (!data[i].flag.is_initialized) {
|
||||
data_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data_index == AruidIndexMax) {
|
||||
return ResultAruidNoAvailableEntries;
|
||||
}
|
||||
|
||||
AruidData& aruid_data = data[data_index];
|
||||
|
||||
aruid_data.aruid = aruid;
|
||||
aruid_data.flag.is_initialized.Assign(true);
|
||||
if (enable_input) {
|
||||
aruid_data.flag.enable_pad_input.Assign(true);
|
||||
aruid_data.flag.enable_six_axis_sensor.Assign(true);
|
||||
aruid_data.flag.bit_18.Assign(true);
|
||||
aruid_data.flag.enable_touchscreen.Assign(true);
|
||||
}
|
||||
|
||||
data_index = AruidIndexMax;
|
||||
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
||||
if (registration_list.flag[i] == RegistrationStatus::Initialized) {
|
||||
if (registration_list.aruid[i] != aruid) {
|
||||
continue;
|
||||
}
|
||||
data_index = i;
|
||||
break;
|
||||
}
|
||||
if (registration_list.flag[i] == RegistrationStatus::None) {
|
||||
data_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data_index == AruidIndexMax) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
registration_list.flag[data_index] = RegistrationStatus::Initialized;
|
||||
registration_list.aruid[data_index] = aruid;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void AppletResource::UnregisterAppletResourceUserId(u64 aruid) {
|
||||
u64 index = GetIndexFromAruid(aruid);
|
||||
|
||||
if (index < AruidIndexMax) {
|
||||
if (data[index].flag.is_assigned) {
|
||||
data[index].shared_memory_format = nullptr;
|
||||
data[index].flag.is_assigned.Assign(false);
|
||||
}
|
||||
}
|
||||
|
||||
index = GetIndexFromAruid(aruid);
|
||||
if (index < AruidIndexMax) {
|
||||
DestroySevenSixAxisTransferMemory();
|
||||
data[index].flag.raw = 0;
|
||||
data[index].aruid = 0;
|
||||
|
||||
index = GetIndexFromAruid(aruid);
|
||||
if (index < AruidIndexMax) {
|
||||
registration_list.flag[index] = RegistrationStatus::PendingDelete;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AppletResource::FreeAppletResourceId(u64 aruid) {
|
||||
u64 index = GetIndexFromAruid(aruid);
|
||||
if (index >= AruidIndexMax) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& aruid_data = data[index];
|
||||
if (aruid_data.flag.is_assigned) {
|
||||
aruid_data.shared_memory_format = nullptr;
|
||||
aruid_data.flag.is_assigned.Assign(false);
|
||||
}
|
||||
}
|
||||
|
||||
u64 AppletResource::GetActiveAruid() {
|
||||
return active_aruid;
|
||||
}
|
||||
|
||||
Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) {
|
||||
u64 index = GetIndexFromAruid(aruid);
|
||||
if (index >= AruidIndexMax) {
|
||||
return ResultAruidNotRegistered;
|
||||
}
|
||||
|
||||
*out_handle = shared_memory_holder[index].GetHandle();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format,
|
||||
u64 aruid) {
|
||||
u64 index = GetIndexFromAruid(aruid);
|
||||
if (index >= AruidIndexMax) {
|
||||
return ResultAruidNotRegistered;
|
||||
}
|
||||
|
||||
*out_shared_memory_format = data[index].shared_memory_format;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
u64 AppletResource::GetIndexFromAruid(u64 aruid) {
|
||||
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
||||
if (registration_list.flag[i] == RegistrationStatus::Initialized &&
|
||||
registration_list.aruid[i] == aruid) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return AruidIndexMax;
|
||||
}
|
||||
|
||||
Result AppletResource::DestroySevenSixAxisTransferMemory() {
|
||||
// TODO
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void AppletResource::EnableInput(u64 aruid, bool is_enabled) {
|
||||
const u64 index = GetIndexFromAruid(aruid);
|
||||
if (index >= AruidIndexMax) {
|
||||
return;
|
||||
}
|
||||
|
||||
data[index].flag.enable_pad_input.Assign(is_enabled);
|
||||
data[index].flag.enable_touchscreen.Assign(is_enabled);
|
||||
}
|
||||
|
||||
void AppletResource::EnableSixAxisSensor(u64 aruid, bool is_enabled) {
|
||||
const u64 index = GetIndexFromAruid(aruid);
|
||||
if (index >= AruidIndexMax) {
|
||||
return;
|
||||
}
|
||||
|
||||
data[index].flag.enable_six_axis_sensor.Assign(is_enabled);
|
||||
}
|
||||
|
||||
void AppletResource::EnablePadInput(u64 aruid, bool is_enabled) {
|
||||
const u64 index = GetIndexFromAruid(aruid);
|
||||
if (index >= AruidIndexMax) {
|
||||
return;
|
||||
}
|
||||
|
||||
data[index].flag.enable_pad_input.Assign(is_enabled);
|
||||
}
|
||||
|
||||
void AppletResource::EnableTouchScreen(u64 aruid, bool is_enabled) {
|
||||
const u64 index = GetIndexFromAruid(aruid);
|
||||
if (index >= AruidIndexMax) {
|
||||
return;
|
||||
}
|
||||
|
||||
data[index].flag.enable_touchscreen.Assign(is_enabled);
|
||||
}
|
||||
|
||||
void AppletResource::SetIsPalmaConnectable(u64 aruid, bool is_connectable) {
|
||||
const u64 index = GetIndexFromAruid(aruid);
|
||||
if (index >= AruidIndexMax) {
|
||||
return;
|
||||
}
|
||||
|
||||
data[index].flag.is_palma_connectable.Assign(is_connectable);
|
||||
}
|
||||
|
||||
void AppletResource::EnablePalmaBoostMode(u64 aruid, bool is_enabled) {
|
||||
const u64 index = GetIndexFromAruid(aruid);
|
||||
if (index >= AruidIndexMax) {
|
||||
return;
|
||||
}
|
||||
|
||||
data[index].flag.enable_palma_boost_mode.Assign(is_enabled);
|
||||
}
|
||||
|
||||
Result AppletResource::RegisterCoreAppletResource() {
|
||||
if (ref_counter == std::numeric_limits<s32>::max() - 1) {
|
||||
return ResultAppletResourceOverflow;
|
||||
}
|
||||
if (ref_counter == 0) {
|
||||
const u64 index = GetIndexFromAruid(0);
|
||||
if (index < AruidIndexMax) {
|
||||
return ResultAruidAlreadyRegistered;
|
||||
}
|
||||
|
||||
std::size_t data_index = AruidIndexMax;
|
||||
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
||||
if (!data[i].flag.is_initialized) {
|
||||
data_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data_index == AruidIndexMax) {
|
||||
return ResultAruidNoAvailableEntries;
|
||||
}
|
||||
|
||||
AruidData& aruid_data = data[data_index];
|
||||
|
||||
aruid_data.aruid = 0;
|
||||
aruid_data.flag.is_initialized.Assign(true);
|
||||
aruid_data.flag.enable_pad_input.Assign(true);
|
||||
aruid_data.flag.enable_six_axis_sensor.Assign(true);
|
||||
aruid_data.flag.bit_18.Assign(true);
|
||||
aruid_data.flag.enable_touchscreen.Assign(true);
|
||||
|
||||
data_index = AruidIndexMax;
|
||||
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
||||
if (registration_list.flag[i] == RegistrationStatus::Initialized) {
|
||||
if (registration_list.aruid[i] != 0) {
|
||||
continue;
|
||||
}
|
||||
data_index = i;
|
||||
break;
|
||||
}
|
||||
if (registration_list.flag[i] == RegistrationStatus::None) {
|
||||
data_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Result result = ResultSuccess;
|
||||
|
||||
if (data_index == AruidIndexMax) {
|
||||
result = CreateAppletResource(0);
|
||||
} else {
|
||||
registration_list.flag[data_index] = RegistrationStatus::Initialized;
|
||||
registration_list.aruid[data_index] = 0;
|
||||
}
|
||||
|
||||
if (result.IsError()) {
|
||||
UnregisterAppletResourceUserId(0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
ref_counter++;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result AppletResource::UnregisterCoreAppletResource() {
|
||||
if (ref_counter == 0) {
|
||||
return ResultAppletResourceNotInitialized;
|
||||
}
|
||||
|
||||
if (--ref_counter == 0) {
|
||||
UnregisterAppletResourceUserId(0);
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
98
src/core/hle/service/hid/controllers/applet_resource.h
Normal file
98
src/core/hle/service/hid/controllers/applet_resource.h
Normal file
@@ -0,0 +1,98 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_holder.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KSharedMemory;
|
||||
}
|
||||
|
||||
namespace Service::HID {
|
||||
struct SharedMemoryFormat;
|
||||
|
||||
class AppletResource {
|
||||
public:
|
||||
explicit AppletResource(Core::System& system_);
|
||||
~AppletResource();
|
||||
|
||||
Result CreateAppletResource(u64 aruid);
|
||||
|
||||
Result RegisterAppletResourceUserId(u64 aruid, bool enable_input);
|
||||
void UnregisterAppletResourceUserId(u64 aruid);
|
||||
|
||||
void FreeAppletResourceId(u64 aruid);
|
||||
|
||||
u64 GetActiveAruid();
|
||||
Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
|
||||
Result GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, u64 aruid);
|
||||
|
||||
u64 GetIndexFromAruid(u64 aruid);
|
||||
|
||||
Result DestroySevenSixAxisTransferMemory();
|
||||
|
||||
void EnableInput(u64 aruid, bool is_enabled);
|
||||
void EnableSixAxisSensor(u64 aruid, bool is_enabled);
|
||||
void EnablePadInput(u64 aruid, bool is_enabled);
|
||||
void EnableTouchScreen(u64 aruid, bool is_enabled);
|
||||
void SetIsPalmaConnectable(u64 aruid, bool is_connectable);
|
||||
void EnablePalmaBoostMode(u64 aruid, bool is_enabled);
|
||||
|
||||
Result RegisterCoreAppletResource();
|
||||
Result UnregisterCoreAppletResource();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t AruidIndexMax = 0x20;
|
||||
|
||||
enum RegistrationStatus : u32 {
|
||||
None,
|
||||
Initialized,
|
||||
PendingDelete,
|
||||
};
|
||||
|
||||
struct DataStatusFlag {
|
||||
union {
|
||||
u32 raw{};
|
||||
|
||||
BitField<0, 1, u32> is_initialized;
|
||||
BitField<1, 1, u32> is_assigned;
|
||||
BitField<16, 1, u32> enable_pad_input;
|
||||
BitField<17, 1, u32> enable_six_axis_sensor;
|
||||
BitField<18, 1, u32> bit_18;
|
||||
BitField<19, 1, u32> is_palma_connectable;
|
||||
BitField<20, 1, u32> enable_palma_boost_mode;
|
||||
BitField<21, 1, u32> enable_touchscreen;
|
||||
};
|
||||
};
|
||||
|
||||
struct AruidRegisterList {
|
||||
std::array<RegistrationStatus, AruidIndexMax> flag{};
|
||||
std::array<u64, AruidIndexMax> aruid{};
|
||||
};
|
||||
static_assert(sizeof(AruidRegisterList) == 0x180, "AruidRegisterList is an invalid size");
|
||||
|
||||
struct AruidData {
|
||||
DataStatusFlag flag{};
|
||||
u64 aruid{};
|
||||
SharedMemoryFormat* shared_memory_format{nullptr};
|
||||
};
|
||||
|
||||
u64 active_aruid{};
|
||||
AruidRegisterList registration_list{};
|
||||
std::array<AruidData, AruidIndexMax> data{};
|
||||
std::array<SharedMemoryHolder, AruidIndexMax> shared_memory_holder{};
|
||||
s32 ref_counter{};
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
@@ -1,23 +1,18 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hid/emulated_console.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/console_six_axis.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_format.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
|
||||
|
||||
ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase{hid_core_} {
|
||||
ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_,
|
||||
ConsoleSixAxisSensorSharedMemoryFormat& console_shared_memory)
|
||||
: ControllerBase{hid_core_}, shared_memory{console_shared_memory} {
|
||||
console = hid_core.GetEmulatedConsole();
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
|
||||
"ConsoleSharedMemory is bigger than the shared memory");
|
||||
shared_memory = std::construct_at(
|
||||
reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
|
||||
}
|
||||
|
||||
ConsoleSixAxis::~ConsoleSixAxis() = default;
|
||||
@@ -33,10 +28,10 @@ void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
|
||||
const auto motion_status = console->GetMotion();
|
||||
|
||||
shared_memory->sampling_number++;
|
||||
shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
|
||||
shared_memory->verticalization_error = motion_status.verticalization_error;
|
||||
shared_memory->gyro_bias = motion_status.gyro_bias;
|
||||
shared_memory.sampling_number++;
|
||||
shared_memory.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
|
||||
shared_memory.verticalization_error = motion_status.verticalization_error;
|
||||
shared_memory.gyro_bias = motion_status.gyro_bias;
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/vector_math.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
|
||||
namespace Core::HID {
|
||||
@@ -11,9 +10,12 @@ class EmulatedConsole;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
struct ConsoleSixAxisSensorSharedMemoryFormat;
|
||||
|
||||
class ConsoleSixAxis final : public ControllerBase {
|
||||
public:
|
||||
explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_,
|
||||
ConsoleSixAxisSensorSharedMemoryFormat& console_shared_memory);
|
||||
~ConsoleSixAxis() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -26,18 +28,7 @@ public:
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
private:
|
||||
// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
|
||||
struct ConsoleSharedMemory {
|
||||
u64 sampling_number{};
|
||||
bool is_seven_six_axis_sensor_at_rest{};
|
||||
INSERT_PADDING_BYTES(3); // padding
|
||||
f32 verticalization_error{};
|
||||
Common::Vec3f gyro_bias{};
|
||||
INSERT_PADDING_BYTES(4); // padding
|
||||
};
|
||||
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
|
||||
|
||||
ConsoleSharedMemory* shared_memory = nullptr;
|
||||
ConsoleSixAxisSensorSharedMemoryFormat& shared_memory;
|
||||
Core::HID::EmulatedConsole* console = nullptr;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -39,9 +39,6 @@ public:
|
||||
|
||||
bool IsControllerActivated() const;
|
||||
|
||||
static const std::size_t hid_entry_count = 17;
|
||||
static const std::size_t shared_memory_size = 0x40000;
|
||||
|
||||
protected:
|
||||
bool is_activated{false};
|
||||
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/hid/controllers/debug_pad.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_format.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
|
||||
|
||||
DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase{hid_core_} {
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size,
|
||||
"DebugPadSharedMemory is bigger than the shared memory");
|
||||
shared_memory = std::construct_at(
|
||||
reinterpret_cast<DebugPadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
|
||||
DebugPad::DebugPad(Core::HID::HIDCore& hid_core_,
|
||||
DebugPadSharedMemoryFormat& debug_pad_shared_memory)
|
||||
: ControllerBase{hid_core_}, shared_memory{debug_pad_shared_memory} {
|
||||
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
|
||||
}
|
||||
|
||||
@@ -30,12 +25,12 @@ void DebugPad::OnRelease() {}
|
||||
|
||||
void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory->debug_pad_lifo.buffer_count = 0;
|
||||
shared_memory->debug_pad_lifo.buffer_tail = 0;
|
||||
shared_memory.debug_pad_lifo.buffer_count = 0;
|
||||
shared_memory.debug_pad_lifo.buffer_tail = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& last_entry = shared_memory->debug_pad_lifo.ReadCurrentEntry().state;
|
||||
const auto& last_entry = shared_memory.debug_pad_lifo.ReadCurrentEntry().state;
|
||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
||||
|
||||
if (Settings::values.debug_pad_enabled) {
|
||||
@@ -49,7 +44,7 @@ void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
next_state.r_stick = stick_state.right;
|
||||
}
|
||||
|
||||
shared_memory->debug_pad_lifo.WriteNextEntry(next_state);
|
||||
shared_memory.debug_pad_lifo.WriteNextEntry(next_state);
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -3,21 +3,24 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
#include "core/hle/service/hid/controllers/types/debug_pad_types.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedController;
|
||||
struct DebugPadButton;
|
||||
struct AnalogStickState;
|
||||
} // namespace Core::HID
|
||||
class HIDCore;
|
||||
}
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
||||
namespace Service::HID {
|
||||
struct DebugPadSharedMemoryFormat;
|
||||
|
||||
class DebugPad final : public ControllerBase {
|
||||
public:
|
||||
explicit DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
explicit DebugPad(Core::HID::HIDCore& hid_core_,
|
||||
DebugPadSharedMemoryFormat& debug_pad_shared_memory);
|
||||
~DebugPad() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -30,35 +33,8 @@ public:
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
private:
|
||||
// This is nn::hid::DebugPadAttribute
|
||||
struct DebugPadAttribute {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> connected;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
|
||||
|
||||
// This is nn::hid::DebugPadState
|
||||
struct DebugPadState {
|
||||
s64 sampling_number{};
|
||||
DebugPadAttribute attribute{};
|
||||
Core::HID::DebugPadButton pad_state{};
|
||||
Core::HID::AnalogStickState r_stick{};
|
||||
Core::HID::AnalogStickState l_stick{};
|
||||
};
|
||||
static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
|
||||
|
||||
struct DebugPadSharedMemory {
|
||||
// This is nn::hid::detail::DebugPadLifo
|
||||
Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{};
|
||||
static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
|
||||
INSERT_PADDING_WORDS(0x4E);
|
||||
};
|
||||
static_assert(sizeof(DebugPadSharedMemory) == 0x400, "DebugPadSharedMemory is an invalid size");
|
||||
|
||||
DebugPadState next_state{};
|
||||
DebugPadSharedMemory* shared_memory = nullptr;
|
||||
DebugPadSharedMemoryFormat& shared_memory;
|
||||
Core::HID::EmulatedController* controller = nullptr;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/hid/emulated_console.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/gesture.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_format.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
|
||||
|
||||
// HW is around 700, value is set to 400 to make it easier to trigger with mouse
|
||||
constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
|
||||
constexpr f32 angle_threshold = 0.015f; // Threshold in radians
|
||||
@@ -23,19 +21,15 @@ constexpr f32 Square(s32 num) {
|
||||
return static_cast<f32>(num * num);
|
||||
}
|
||||
|
||||
Gesture::Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase(hid_core_) {
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
|
||||
"GestureSharedMemory is bigger than the shared memory");
|
||||
shared_memory = std::construct_at(
|
||||
reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
|
||||
Gesture::Gesture(Core::HID::HIDCore& hid_core_, GestureSharedMemoryFormat& gesture_shared_memory)
|
||||
: ControllerBase(hid_core_), shared_memory{gesture_shared_memory} {
|
||||
console = hid_core.GetEmulatedConsole();
|
||||
}
|
||||
Gesture::~Gesture() = default;
|
||||
|
||||
void Gesture::OnInit() {
|
||||
shared_memory->gesture_lifo.buffer_count = 0;
|
||||
shared_memory->gesture_lifo.buffer_tail = 0;
|
||||
shared_memory.gesture_lifo.buffer_count = 0;
|
||||
shared_memory.gesture_lifo.buffer_tail = 0;
|
||||
force_update = true;
|
||||
}
|
||||
|
||||
@@ -43,8 +37,8 @@ void Gesture::OnRelease() {}
|
||||
|
||||
void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory->gesture_lifo.buffer_count = 0;
|
||||
shared_memory->gesture_lifo.buffer_tail = 0;
|
||||
shared_memory.gesture_lifo.buffer_count = 0;
|
||||
shared_memory.gesture_lifo.buffer_tail = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -52,7 +46,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
|
||||
GestureProperties gesture = GetGestureProperties();
|
||||
f32 time_difference =
|
||||
static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
|
||||
static_cast<f32>(shared_memory.gesture_lifo.timestamp - last_update_timestamp) /
|
||||
(1000 * 1000 * 1000);
|
||||
|
||||
// Only update if necessary
|
||||
@@ -60,7 +54,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
return;
|
||||
}
|
||||
|
||||
last_update_timestamp = shared_memory->gesture_lifo.timestamp;
|
||||
last_update_timestamp = shared_memory.gesture_lifo.timestamp;
|
||||
UpdateGestureSharedMemory(gesture, time_difference);
|
||||
}
|
||||
|
||||
@@ -103,7 +97,7 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
|
||||
GestureType type = GestureType::Idle;
|
||||
GestureAttribute attributes{};
|
||||
|
||||
const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
|
||||
const auto& last_entry = shared_memory.gesture_lifo.ReadCurrentEntry().state;
|
||||
|
||||
// Reset next state to default
|
||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
||||
@@ -133,7 +127,7 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
|
||||
next_state.points = gesture.points;
|
||||
last_gesture = gesture;
|
||||
|
||||
shared_memory->gesture_lifo.WriteNextEntry(next_state);
|
||||
shared_memory.gesture_lifo.WriteNextEntry(next_state);
|
||||
}
|
||||
|
||||
void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
|
||||
@@ -305,11 +299,11 @@ void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_
|
||||
next_state.direction = GestureDirection::Up;
|
||||
}
|
||||
|
||||
const Gesture::GestureState& Gesture::GetLastGestureEntry() const {
|
||||
return shared_memory->gesture_lifo.ReadCurrentEntry().state;
|
||||
const GestureState& Gesture::GetLastGestureEntry() const {
|
||||
return shared_memory.gesture_lifo.ReadCurrentEntry().state;
|
||||
}
|
||||
|
||||
Gesture::GestureProperties Gesture::GetGestureProperties() {
|
||||
GestureProperties Gesture::GetGestureProperties() {
|
||||
GestureProperties gesture;
|
||||
std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
|
||||
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
|
||||
|
||||
@@ -4,17 +4,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/bit_field.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/point.h"
|
||||
#include "core/hid/emulated_console.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
#include "core/hle/service/hid/controllers/types/touch_types.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedConsole;
|
||||
}
|
||||
|
||||
namespace Service::HID {
|
||||
struct GestureSharedMemoryFormat;
|
||||
|
||||
class Gesture final : public ControllerBase {
|
||||
public:
|
||||
explicit Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
explicit Gesture(Core::HID::HIDCore& hid_core_,
|
||||
GestureSharedMemoryFormat& gesture_shared_memory);
|
||||
~Gesture() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -27,79 +32,6 @@ public:
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t MAX_FINGERS = 16;
|
||||
static constexpr size_t MAX_POINTS = 4;
|
||||
|
||||
// This is nn::hid::GestureType
|
||||
enum class GestureType : u32 {
|
||||
Idle, // Nothing touching the screen
|
||||
Complete, // Set at the end of a touch event
|
||||
Cancel, // Set when the number of fingers change
|
||||
Touch, // A finger just touched the screen
|
||||
Press, // Set if last type is touch and the finger hasn't moved
|
||||
Tap, // Fast press then release
|
||||
Pan, // All points moving together across the screen
|
||||
Swipe, // Fast press movement and release of a single point
|
||||
Pinch, // All points moving away/closer to the midpoint
|
||||
Rotate, // All points rotating from the midpoint
|
||||
};
|
||||
|
||||
// This is nn::hid::GestureDirection
|
||||
enum class GestureDirection : u32 {
|
||||
None,
|
||||
Left,
|
||||
Up,
|
||||
Right,
|
||||
Down,
|
||||
};
|
||||
|
||||
// This is nn::hid::GestureAttribute
|
||||
struct GestureAttribute {
|
||||
union {
|
||||
u32 raw{};
|
||||
|
||||
BitField<4, 1, u32> is_new_touch;
|
||||
BitField<8, 1, u32> is_double_tap;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
|
||||
|
||||
// This is nn::hid::GestureState
|
||||
struct GestureState {
|
||||
s64 sampling_number{};
|
||||
s64 detection_count{};
|
||||
GestureType type{GestureType::Idle};
|
||||
GestureDirection direction{GestureDirection::None};
|
||||
Common::Point<s32> pos{};
|
||||
Common::Point<s32> delta{};
|
||||
f32 vel_x{};
|
||||
f32 vel_y{};
|
||||
GestureAttribute attributes{};
|
||||
f32 scale{};
|
||||
f32 rotation_angle{};
|
||||
s32 point_count{};
|
||||
std::array<Common::Point<s32>, 4> points{};
|
||||
};
|
||||
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
|
||||
|
||||
struct GestureProperties {
|
||||
std::array<Common::Point<s32>, MAX_POINTS> points{};
|
||||
std::size_t active_points{};
|
||||
Common::Point<s32> mid_point{};
|
||||
s64 detection_count{};
|
||||
u64 delta_time{};
|
||||
f32 average_distance{};
|
||||
f32 angle{};
|
||||
};
|
||||
|
||||
struct GestureSharedMemory {
|
||||
// This is nn::hid::detail::GestureLifo
|
||||
Lifo<GestureState, hid_entry_count> gesture_lifo{};
|
||||
static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
|
||||
INSERT_PADDING_WORDS(0x3E);
|
||||
};
|
||||
static_assert(sizeof(GestureSharedMemory) == 0x800, "GestureSharedMemory is an invalid size");
|
||||
|
||||
// Reads input from all available input engines
|
||||
void ReadTouchInput();
|
||||
|
||||
@@ -142,7 +74,7 @@ private:
|
||||
GestureProperties GetGestureProperties();
|
||||
|
||||
GestureState next_state{};
|
||||
GestureSharedMemory* shared_memory = nullptr;
|
||||
GestureSharedMemoryFormat& shared_memory;
|
||||
Core::HID::EmulatedConsole* console = nullptr;
|
||||
|
||||
std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hid/emulated_devices.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/keyboard.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_format.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
|
||||
|
||||
Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase{hid_core_} {
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size,
|
||||
"KeyboardSharedMemory is bigger than the shared memory");
|
||||
shared_memory = std::construct_at(
|
||||
reinterpret_cast<KeyboardSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
|
||||
Keyboard::Keyboard(Core::HID::HIDCore& hid_core_,
|
||||
KeyboardSharedMemoryFormat& keyboard_shared_memory)
|
||||
: ControllerBase{hid_core_}, shared_memory{keyboard_shared_memory} {
|
||||
emulated_devices = hid_core.GetEmulatedDevices();
|
||||
}
|
||||
|
||||
@@ -29,12 +24,12 @@ void Keyboard::OnRelease() {}
|
||||
|
||||
void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory->keyboard_lifo.buffer_count = 0;
|
||||
shared_memory->keyboard_lifo.buffer_tail = 0;
|
||||
shared_memory.keyboard_lifo.buffer_count = 0;
|
||||
shared_memory.keyboard_lifo.buffer_tail = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& last_entry = shared_memory->keyboard_lifo.ReadCurrentEntry().state;
|
||||
const auto& last_entry = shared_memory.keyboard_lifo.ReadCurrentEntry().state;
|
||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
||||
|
||||
if (Settings::values.keyboard_enabled) {
|
||||
@@ -46,7 +41,7 @@ void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
next_state.attribute.is_connected.Assign(1);
|
||||
}
|
||||
|
||||
shared_memory->keyboard_lifo.WriteNextEntry(next_state);
|
||||
shared_memory.keyboard_lifo.WriteNextEntry(next_state);
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -3,20 +3,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedDevices;
|
||||
struct KeyboardModifier;
|
||||
struct KeyboardKey;
|
||||
} // namespace Core::HID
|
||||
#include "core/hle/service/hid/controllers/types/keyboard_types.h"
|
||||
|
||||
namespace Service::HID {
|
||||
struct KeyboardSharedMemoryFormat;
|
||||
|
||||
class Keyboard final : public ControllerBase {
|
||||
public:
|
||||
explicit Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
explicit Keyboard(Core::HID::HIDCore& hid_core_,
|
||||
KeyboardSharedMemoryFormat& keyboard_shared_memory);
|
||||
~Keyboard() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -29,25 +25,8 @@ public:
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
private:
|
||||
// This is nn::hid::detail::KeyboardState
|
||||
struct KeyboardState {
|
||||
s64 sampling_number{};
|
||||
Core::HID::KeyboardModifier modifier{};
|
||||
Core::HID::KeyboardAttribute attribute{};
|
||||
Core::HID::KeyboardKey key{};
|
||||
};
|
||||
static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
|
||||
|
||||
struct KeyboardSharedMemory {
|
||||
// This is nn::hid::detail::KeyboardLifo
|
||||
Lifo<KeyboardState, hid_entry_count> keyboard_lifo{};
|
||||
static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
|
||||
INSERT_PADDING_WORDS(0xA);
|
||||
};
|
||||
static_assert(sizeof(KeyboardSharedMemory) == 0x400, "KeyboardSharedMemory is an invalid size");
|
||||
|
||||
KeyboardState next_state{};
|
||||
KeyboardSharedMemory* shared_memory = nullptr;
|
||||
KeyboardSharedMemoryFormat& shared_memory;
|
||||
Core::HID::EmulatedDevices* emulated_devices = nullptr;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/hid/emulated_devices.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/mouse.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_format.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
|
||||
|
||||
Mouse::Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size,
|
||||
"MouseSharedMemory is bigger than the shared memory");
|
||||
shared_memory = std::construct_at(
|
||||
reinterpret_cast<MouseSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
|
||||
Mouse::Mouse(Core::HID::HIDCore& hid_core_, MouseSharedMemoryFormat& mouse_shared_memory)
|
||||
: ControllerBase{hid_core_}, shared_memory{mouse_shared_memory} {
|
||||
emulated_devices = hid_core.GetEmulatedDevices();
|
||||
}
|
||||
|
||||
@@ -27,14 +22,14 @@ void Mouse::OnRelease() {}
|
||||
|
||||
void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory->mouse_lifo.buffer_count = 0;
|
||||
shared_memory->mouse_lifo.buffer_tail = 0;
|
||||
shared_memory.mouse_lifo.buffer_count = 0;
|
||||
shared_memory.mouse_lifo.buffer_tail = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
next_state = {};
|
||||
|
||||
const auto& last_entry = shared_memory->mouse_lifo.ReadCurrentEntry().state;
|
||||
const auto& last_entry = shared_memory.mouse_lifo.ReadCurrentEntry().state;
|
||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
||||
|
||||
if (Settings::values.mouse_enabled) {
|
||||
@@ -53,7 +48,7 @@ void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
next_state.button = mouse_button_state;
|
||||
}
|
||||
|
||||
shared_memory->mouse_lifo.WriteNextEntry(next_state);
|
||||
shared_memory.mouse_lifo.WriteNextEntry(next_state);
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedDevices;
|
||||
@@ -14,9 +12,11 @@ struct AnalogStickState;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
struct MouseSharedMemoryFormat;
|
||||
|
||||
class Mouse final : public ControllerBase {
|
||||
public:
|
||||
explicit Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
explicit Mouse(Core::HID::HIDCore& hid_core_, MouseSharedMemoryFormat& mouse_shared_memory);
|
||||
~Mouse() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -29,17 +29,9 @@ public:
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
private:
|
||||
struct MouseSharedMemory {
|
||||
// This is nn::hid::detail::MouseLifo
|
||||
Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{};
|
||||
static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
|
||||
INSERT_PADDING_WORDS(0x2C);
|
||||
};
|
||||
static_assert(sizeof(MouseSharedMemory) == 0x400, "MouseSharedMemory is an invalid size");
|
||||
|
||||
Core::HID::MouseState next_state{};
|
||||
Core::HID::AnalogStickState last_mouse_wheel_state{};
|
||||
MouseSharedMemory* shared_memory = nullptr;
|
||||
MouseSharedMemoryFormat& shared_memory;
|
||||
Core::HID::EmulatedDevices* emulated_devices = nullptr;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_format.h"
|
||||
#include "core/hle/service/hid/errors.h"
|
||||
#include "core/hle/service/hid/hid_util.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t NPAD_OFFSET = 0x9A00;
|
||||
constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
|
||||
Core::HID::NpadIdType::Player1, Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3,
|
||||
Core::HID::NpadIdType::Player4, Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6,
|
||||
@@ -30,14 +30,12 @@ constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
|
||||
Core::HID::NpadIdType::Handheld,
|
||||
};
|
||||
|
||||
NPad::NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
NPad::NPad(Core::HID::HIDCore& hid_core_, NpadSharedMemoryFormat& npad_shared_memory_format,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
: ControllerBase{hid_core_}, service_context{service_context_} {
|
||||
static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size);
|
||||
for (std::size_t i = 0; i < controller_data.size(); ++i) {
|
||||
auto& controller = controller_data[i];
|
||||
controller.shared_memory = std::construct_at(reinterpret_cast<NpadInternalState*>(
|
||||
raw_shared_memory_ + NPAD_OFFSET + (i * sizeof(NpadInternalState))));
|
||||
controller.shared_memory = &npad_shared_memory_format.npad_entry[i].internal_state;
|
||||
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
||||
controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
|
||||
Core::HID::DEFAULT_VIBRATION_VALUE;
|
||||
@@ -617,7 +615,7 @@ void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
|
||||
hold_type = joy_hold_type;
|
||||
}
|
||||
|
||||
NPad::NpadJoyHoldType NPad::GetHoldType() const {
|
||||
NpadJoyHoldType NPad::GetHoldType() const {
|
||||
return hold_type;
|
||||
}
|
||||
|
||||
@@ -630,7 +628,7 @@ void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_m
|
||||
handheld_activation_mode = activation_mode;
|
||||
}
|
||||
|
||||
NPad::NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const {
|
||||
NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const {
|
||||
return handheld_activation_mode;
|
||||
}
|
||||
|
||||
@@ -638,7 +636,7 @@ void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) {
|
||||
communication_mode = communication_mode_;
|
||||
}
|
||||
|
||||
NPad::NpadCommunicationMode NPad::GetNpadCommunicationMode() const {
|
||||
NpadCommunicationMode NPad::GetNpadCommunicationMode() const {
|
||||
return communication_mode;
|
||||
}
|
||||
|
||||
@@ -978,27 +976,27 @@ Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
NPad::SixAxisLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) {
|
||||
NpadSixAxisSensorLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) {
|
||||
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo;
|
||||
}
|
||||
|
||||
NPad::SixAxisLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
|
||||
NpadSixAxisSensorLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
|
||||
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo;
|
||||
}
|
||||
|
||||
NPad::SixAxisLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
|
||||
NpadSixAxisSensorLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
|
||||
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo;
|
||||
}
|
||||
|
||||
NPad::SixAxisLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
|
||||
NpadSixAxisSensorLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
|
||||
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo;
|
||||
}
|
||||
|
||||
NPad::SixAxisLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
|
||||
NpadSixAxisSensorLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
|
||||
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo;
|
||||
}
|
||||
|
||||
NPad::SixAxisLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
|
||||
NpadSixAxisSensorLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
|
||||
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo;
|
||||
}
|
||||
|
||||
@@ -1343,7 +1341,7 @@ const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
|
||||
}
|
||||
}
|
||||
|
||||
NPad::AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
|
||||
AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
|
||||
const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory;
|
||||
|
||||
return {
|
||||
|
||||
@@ -8,12 +8,10 @@
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
#include "core/hle/service/hid/controllers/types/npad_types.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedController;
|
||||
@@ -32,10 +30,13 @@ class ServiceContext;
|
||||
union Result;
|
||||
|
||||
namespace Service::HID {
|
||||
struct NpadInternalState;
|
||||
struct NpadSixAxisSensorLifo;
|
||||
struct NpadSharedMemoryFormat;
|
||||
|
||||
class NPad final : public ControllerBase {
|
||||
public:
|
||||
explicit NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
explicit NPad(Core::HID::HIDCore& hid_core_, NpadSharedMemoryFormat& npad_shared_memory_format,
|
||||
KernelHelpers::ServiceContext& service_context_);
|
||||
~NPad() override;
|
||||
|
||||
@@ -48,89 +49,6 @@ public:
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
// This is nn::hid::NpadJoyHoldType
|
||||
enum class NpadJoyHoldType : u64 {
|
||||
Vertical = 0,
|
||||
Horizontal = 1,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadJoyAssignmentMode
|
||||
enum class NpadJoyAssignmentMode : u32 {
|
||||
Dual = 0,
|
||||
Single = 1,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadJoyDeviceType
|
||||
enum class NpadJoyDeviceType : s64 {
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadHandheldActivationMode
|
||||
enum class NpadHandheldActivationMode : u64 {
|
||||
Dual = 0,
|
||||
Single = 1,
|
||||
None = 2,
|
||||
MaxActivationMode = 3,
|
||||
};
|
||||
|
||||
// This is nn::hid::system::AppletFooterUiAttributesSet
|
||||
struct AppletFooterUiAttributes {
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
};
|
||||
|
||||
// This is nn::hid::system::AppletFooterUiType
|
||||
enum class AppletFooterUiType : u8 {
|
||||
None = 0,
|
||||
HandheldNone = 1,
|
||||
HandheldJoyConLeftOnly = 2,
|
||||
HandheldJoyConRightOnly = 3,
|
||||
HandheldJoyConLeftJoyConRight = 4,
|
||||
JoyDual = 5,
|
||||
JoyDualLeftOnly = 6,
|
||||
JoyDualRightOnly = 7,
|
||||
JoyLeftHorizontal = 8,
|
||||
JoyLeftVertical = 9,
|
||||
JoyRightHorizontal = 10,
|
||||
JoyRightVertical = 11,
|
||||
SwitchProController = 12,
|
||||
CompatibleProController = 13,
|
||||
CompatibleJoyCon = 14,
|
||||
LarkHvc1 = 15,
|
||||
LarkHvc2 = 16,
|
||||
LarkNesLeft = 17,
|
||||
LarkNesRight = 18,
|
||||
Lucia = 19,
|
||||
Verification = 20,
|
||||
Lagon = 21,
|
||||
};
|
||||
|
||||
using AppletFooterUiVariant = u8;
|
||||
|
||||
// This is "nn::hid::system::AppletDetailedUiType".
|
||||
struct AppletDetailedUiType {
|
||||
AppletFooterUiVariant ui_variant;
|
||||
INSERT_PADDING_BYTES(0x2);
|
||||
AppletFooterUiType footer;
|
||||
};
|
||||
static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
|
||||
// This is nn::hid::NpadCommunicationMode
|
||||
enum class NpadCommunicationMode : u64 {
|
||||
Mode_5ms = 0,
|
||||
Mode_10ms = 1,
|
||||
Mode_15ms = 2,
|
||||
Default = 3,
|
||||
};
|
||||
|
||||
enum class NpadRevision : u32 {
|
||||
Revision0 = 0,
|
||||
Revision1 = 1,
|
||||
Revision2 = 2,
|
||||
Revision3 = 3,
|
||||
};
|
||||
|
||||
using SixAxisLifo = Lifo<Core::HID::SixAxisSensorState, hid_entry_count>;
|
||||
|
||||
void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
|
||||
Core::HID::NpadStyleTag GetSupportedStyleSet() const;
|
||||
|
||||
@@ -188,12 +106,12 @@ public:
|
||||
Result ResetIsSixAxisSensorDeviceNewlyAssigned(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle);
|
||||
|
||||
SixAxisLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
|
||||
SixAxisLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
|
||||
SixAxisLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
|
||||
SixAxisLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
|
||||
SixAxisLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
|
||||
SixAxisLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
|
||||
NpadSixAxisSensorLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
|
||||
NpadSixAxisSensorLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
|
||||
NpadSixAxisSensorLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
|
||||
NpadSixAxisSensorLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
|
||||
NpadSixAxisSensorLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
|
||||
NpadSixAxisSensorLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
|
||||
|
||||
Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
|
||||
Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
|
||||
@@ -221,214 +139,6 @@ public:
|
||||
AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
|
||||
|
||||
private:
|
||||
static constexpr std::size_t NPAD_COUNT = 10;
|
||||
|
||||
// This is nn::hid::detail::ColorAttribute
|
||||
enum class ColorAttribute : u32 {
|
||||
Ok = 0,
|
||||
ReadError = 1,
|
||||
NoController = 2,
|
||||
};
|
||||
static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::NpadFullKeyColorState
|
||||
struct NpadFullKeyColorState {
|
||||
ColorAttribute attribute{ColorAttribute::NoController};
|
||||
Core::HID::NpadControllerColor fullkey{};
|
||||
};
|
||||
static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::NpadJoyColorState
|
||||
struct NpadJoyColorState {
|
||||
ColorAttribute attribute{ColorAttribute::NoController};
|
||||
Core::HID::NpadControllerColor left{};
|
||||
Core::HID::NpadControllerColor right{};
|
||||
};
|
||||
static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
|
||||
|
||||
// This is nn::hid::NpadAttribute
|
||||
struct NpadAttribute {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> is_connected;
|
||||
BitField<1, 1, u32> is_wired;
|
||||
BitField<2, 1, u32> is_left_connected;
|
||||
BitField<3, 1, u32> is_left_wired;
|
||||
BitField<4, 1, u32> is_right_connected;
|
||||
BitField<5, 1, u32> is_right_wired;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
|
||||
|
||||
// This is nn::hid::NpadFullKeyState
|
||||
// This is nn::hid::NpadHandheldState
|
||||
// This is nn::hid::NpadJoyDualState
|
||||
// This is nn::hid::NpadJoyLeftState
|
||||
// This is nn::hid::NpadJoyRightState
|
||||
// This is nn::hid::NpadPalmaState
|
||||
// This is nn::hid::NpadSystemExtState
|
||||
struct NPadGenericState {
|
||||
s64_le sampling_number{};
|
||||
Core::HID::NpadButtonState npad_buttons{};
|
||||
Core::HID::AnalogStickState l_stick{};
|
||||
Core::HID::AnalogStickState r_stick{};
|
||||
NpadAttribute connection_status{};
|
||||
INSERT_PADDING_BYTES(4); // Reserved
|
||||
};
|
||||
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
|
||||
|
||||
// This is nn::hid::server::NpadGcTriggerState
|
||||
struct NpadGcTriggerState {
|
||||
s64 sampling_number{};
|
||||
s32 l_analog{};
|
||||
s32 r_analog{};
|
||||
};
|
||||
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
|
||||
|
||||
// This is nn::hid::NpadSystemProperties
|
||||
struct NPadSystemProperties {
|
||||
union {
|
||||
s64 raw{};
|
||||
BitField<0, 1, s64> is_charging_joy_dual;
|
||||
BitField<1, 1, s64> is_charging_joy_left;
|
||||
BitField<2, 1, s64> is_charging_joy_right;
|
||||
BitField<3, 1, s64> is_powered_joy_dual;
|
||||
BitField<4, 1, s64> is_powered_joy_left;
|
||||
BitField<5, 1, s64> is_powered_joy_right;
|
||||
BitField<9, 1, s64> is_system_unsupported_button;
|
||||
BitField<10, 1, s64> is_system_ext_unsupported_button;
|
||||
BitField<11, 1, s64> is_vertical;
|
||||
BitField<12, 1, s64> is_horizontal;
|
||||
BitField<13, 1, s64> use_plus;
|
||||
BitField<14, 1, s64> use_minus;
|
||||
BitField<15, 1, s64> use_directional_buttons;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
|
||||
|
||||
// This is nn::hid::NpadSystemButtonProperties
|
||||
struct NpadSystemButtonProperties {
|
||||
union {
|
||||
s32 raw{};
|
||||
BitField<0, 1, s32> is_home_button_protection_enabled;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NpadSystemButtonProperties) == 0x4,
|
||||
"NPadButtonProperties is an invalid size");
|
||||
|
||||
// This is nn::hid::system::DeviceType
|
||||
struct DeviceType {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, s32> fullkey;
|
||||
BitField<1, 1, s32> debug_pad;
|
||||
BitField<2, 1, s32> handheld_left;
|
||||
BitField<3, 1, s32> handheld_right;
|
||||
BitField<4, 1, s32> joycon_left;
|
||||
BitField<5, 1, s32> joycon_right;
|
||||
BitField<6, 1, s32> palma;
|
||||
BitField<7, 1, s32> lark_hvc_left;
|
||||
BitField<8, 1, s32> lark_hvc_right;
|
||||
BitField<9, 1, s32> lark_nes_left;
|
||||
BitField<10, 1, s32> lark_nes_right;
|
||||
BitField<11, 1, s32> handheld_lark_hvc_left;
|
||||
BitField<12, 1, s32> handheld_lark_hvc_right;
|
||||
BitField<13, 1, s32> handheld_lark_nes_left;
|
||||
BitField<14, 1, s32> handheld_lark_nes_right;
|
||||
BitField<15, 1, s32> lucia;
|
||||
BitField<16, 1, s32> lagon;
|
||||
BitField<17, 1, s32> lager;
|
||||
BitField<31, 1, s32> system;
|
||||
};
|
||||
};
|
||||
|
||||
// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
|
||||
struct NfcXcdDeviceHandleStateImpl {
|
||||
u64 handle{};
|
||||
bool is_available{};
|
||||
bool is_activated{};
|
||||
INSERT_PADDING_BYTES(0x6); // Reserved
|
||||
u64 sampling_number{};
|
||||
};
|
||||
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
|
||||
"NfcXcdDeviceHandleStateImpl is an invalid size");
|
||||
|
||||
// This is nn::hid::NpadLarkType
|
||||
enum class NpadLarkType : u32 {
|
||||
Invalid,
|
||||
H1,
|
||||
H2,
|
||||
NL,
|
||||
NR,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadLuciaType
|
||||
enum class NpadLuciaType : u32 {
|
||||
Invalid,
|
||||
J,
|
||||
E,
|
||||
U,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadLagonType
|
||||
enum class NpadLagonType : u32 {
|
||||
Invalid,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadLagerType
|
||||
enum class NpadLagerType : u32 {
|
||||
Invalid,
|
||||
J,
|
||||
E,
|
||||
U,
|
||||
};
|
||||
|
||||
// This is nn::hid::detail::NpadInternalState
|
||||
struct NpadInternalState {
|
||||
Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
|
||||
NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
|
||||
NpadFullKeyColorState fullkey_color{};
|
||||
NpadJoyColorState joycon_color{};
|
||||
Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
|
||||
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
|
||||
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
|
||||
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
|
||||
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
|
||||
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
|
||||
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
|
||||
DeviceType device_type{};
|
||||
INSERT_PADDING_BYTES(0x4); // Reserved
|
||||
NPadSystemProperties system_properties{};
|
||||
NpadSystemButtonProperties button_properties{};
|
||||
Core::HID::NpadBatteryLevel battery_level_dual{};
|
||||
Core::HID::NpadBatteryLevel battery_level_left{};
|
||||
Core::HID::NpadBatteryLevel battery_level_right{};
|
||||
AppletFooterUiAttributes applet_footer_attributes{};
|
||||
AppletFooterUiType applet_footer_type{AppletFooterUiType::None};
|
||||
INSERT_PADDING_BYTES(0x5B); // Reserved
|
||||
INSERT_PADDING_BYTES(0x20); // Unknown
|
||||
Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
|
||||
NpadLarkType lark_type_l_and_main{};
|
||||
NpadLarkType lark_type_r{};
|
||||
NpadLuciaType lucia_type{};
|
||||
NpadLagonType lagon_type{};
|
||||
NpadLagerType lager_type{};
|
||||
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");
|
||||
|
||||
struct VibrationData {
|
||||
bool device_mounted{};
|
||||
Core::HID::VibrationValue latest_vibration_value{};
|
||||
@@ -479,7 +189,7 @@ private:
|
||||
|
||||
std::atomic<u64> press_state{};
|
||||
|
||||
std::array<NpadControllerData, NPAD_COUNT> controller_data{};
|
||||
std::array<NpadControllerData, NpadCount> controller_data{};
|
||||
KernelHelpers::ServiceContext& service_context;
|
||||
std::mutex mutex;
|
||||
std::vector<Core::HID::NpadIdType> supported_npad_id_types{};
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
Palma::Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
Palma::Palma(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
|
||||
: ControllerBase{hid_core_}, service_context{service_context_} {
|
||||
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
|
||||
operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
|
||||
|
||||
@@ -97,8 +97,7 @@ public:
|
||||
static_assert(sizeof(PalmaConnectionHandle) == 0x8,
|
||||
"PalmaConnectionHandle has incorrect size.");
|
||||
|
||||
explicit Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
KernelHelpers::ServiceContext& service_context_);
|
||||
explicit Palma(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_);
|
||||
~Palma() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
|
||||
240
src/core/hle/service/hid/controllers/shared_memory_format.h
Normal file
240
src/core/hle/service/hid/controllers/shared_memory_format.h
Normal file
@@ -0,0 +1,240 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/hid//controllers/types/debug_pad_types.h"
|
||||
#include "core/hle/service/hid//controllers/types/keyboard_types.h"
|
||||
#include "core/hle/service/hid//controllers/types/mouse_types.h"
|
||||
#include "core/hle/service/hid//controllers/types/npad_types.h"
|
||||
#include "core/hle/service/hid//controllers/types/touch_types.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Service::HID {
|
||||
static const std::size_t HidEntryCount = 17;
|
||||
|
||||
struct CommonHeader {
|
||||
s64 timestamp{};
|
||||
s64 total_entry_count{};
|
||||
s64 last_entry_index{};
|
||||
s64 entry_count{};
|
||||
};
|
||||
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::DebugPadSharedMemoryFormat
|
||||
struct DebugPadSharedMemoryFormat {
|
||||
// This is nn::hid::detail::DebugPadLifo
|
||||
Lifo<DebugPadState, HidEntryCount> debug_pad_lifo{};
|
||||
static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
|
||||
INSERT_PADDING_WORDS(0x4E);
|
||||
};
|
||||
static_assert(sizeof(DebugPadSharedMemoryFormat) == 0x400,
|
||||
"DebugPadSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::TouchScreenSharedMemoryFormat
|
||||
struct TouchScreenSharedMemoryFormat {
|
||||
// This is nn::hid::detail::TouchScreenLifo
|
||||
Lifo<TouchScreenState, HidEntryCount> touch_screen_lifo{};
|
||||
static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
|
||||
INSERT_PADDING_WORDS(0xF2);
|
||||
};
|
||||
static_assert(sizeof(TouchScreenSharedMemoryFormat) == 0x3000,
|
||||
"TouchScreenSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::MouseSharedMemoryFormat
|
||||
struct MouseSharedMemoryFormat {
|
||||
// This is nn::hid::detail::MouseLifo
|
||||
Lifo<Core::HID::MouseState, HidEntryCount> mouse_lifo{};
|
||||
static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
|
||||
INSERT_PADDING_WORDS(0x2C);
|
||||
};
|
||||
static_assert(sizeof(MouseSharedMemoryFormat) == 0x400,
|
||||
"MouseSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::KeyboardSharedMemoryFormat
|
||||
struct KeyboardSharedMemoryFormat {
|
||||
// This is nn::hid::detail::KeyboardLifo
|
||||
Lifo<KeyboardState, HidEntryCount> keyboard_lifo{};
|
||||
static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
|
||||
INSERT_PADDING_WORDS(0xA);
|
||||
};
|
||||
static_assert(sizeof(KeyboardSharedMemoryFormat) == 0x400,
|
||||
"KeyboardSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::DigitizerSharedMemoryFormat
|
||||
struct DigitizerSharedMemoryFormat {
|
||||
CommonHeader header;
|
||||
INSERT_PADDING_BYTES(0xFE0);
|
||||
};
|
||||
static_assert(sizeof(DigitizerSharedMemoryFormat) == 0x1000,
|
||||
"DigitizerSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::HomeButtonSharedMemoryFormat
|
||||
struct HomeButtonSharedMemoryFormat {
|
||||
CommonHeader header;
|
||||
INSERT_PADDING_BYTES(0x1E0);
|
||||
};
|
||||
static_assert(sizeof(HomeButtonSharedMemoryFormat) == 0x200,
|
||||
"HomeButtonSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::SleepButtonSharedMemoryFormat
|
||||
struct SleepButtonSharedMemoryFormat {
|
||||
CommonHeader header;
|
||||
INSERT_PADDING_BYTES(0x1E0);
|
||||
};
|
||||
static_assert(sizeof(SleepButtonSharedMemoryFormat) == 0x200,
|
||||
"SleepButtonSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::CaptureButtonSharedMemoryFormat
|
||||
struct CaptureButtonSharedMemoryFormat {
|
||||
CommonHeader header;
|
||||
INSERT_PADDING_BYTES(0x1E0);
|
||||
};
|
||||
static_assert(sizeof(CaptureButtonSharedMemoryFormat) == 0x200,
|
||||
"CaptureButtonSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::InputDetectorSharedMemoryFormat
|
||||
struct InputDetectorSharedMemoryFormat {
|
||||
CommonHeader header;
|
||||
INSERT_PADDING_BYTES(0x7E0);
|
||||
};
|
||||
static_assert(sizeof(InputDetectorSharedMemoryFormat) == 0x800,
|
||||
"InputDetectorSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::UniquePadSharedMemoryFormat
|
||||
struct UniquePadSharedMemoryFormat {
|
||||
CommonHeader header;
|
||||
INSERT_PADDING_BYTES(0x3FE0);
|
||||
};
|
||||
static_assert(sizeof(UniquePadSharedMemoryFormat) == 0x4000,
|
||||
"UniquePadSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::NpadSixAxisSensorLifo
|
||||
struct NpadSixAxisSensorLifo {
|
||||
Lifo<Core::HID::SixAxisSensorState, HidEntryCount> lifo;
|
||||
};
|
||||
|
||||
// This is nn::hid::detail::NpadInternalState
|
||||
struct NpadInternalState {
|
||||
Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
|
||||
NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
|
||||
NpadFullKeyColorState fullkey_color{};
|
||||
NpadJoyColorState joycon_color{};
|
||||
Lifo<NPadGenericState, HidEntryCount> fullkey_lifo{};
|
||||
Lifo<NPadGenericState, HidEntryCount> handheld_lifo{};
|
||||
Lifo<NPadGenericState, HidEntryCount> joy_dual_lifo{};
|
||||
Lifo<NPadGenericState, HidEntryCount> joy_left_lifo{};
|
||||
Lifo<NPadGenericState, HidEntryCount> joy_right_lifo{};
|
||||
Lifo<NPadGenericState, HidEntryCount> palma_lifo{};
|
||||
Lifo<NPadGenericState, HidEntryCount> system_ext_lifo{};
|
||||
NpadSixAxisSensorLifo sixaxis_fullkey_lifo{};
|
||||
NpadSixAxisSensorLifo sixaxis_handheld_lifo{};
|
||||
NpadSixAxisSensorLifo sixaxis_dual_left_lifo{};
|
||||
NpadSixAxisSensorLifo sixaxis_dual_right_lifo{};
|
||||
NpadSixAxisSensorLifo sixaxis_left_lifo{};
|
||||
NpadSixAxisSensorLifo sixaxis_right_lifo{};
|
||||
DeviceType device_type{};
|
||||
INSERT_PADDING_BYTES(0x4); // Reserved
|
||||
NPadSystemProperties system_properties{};
|
||||
NpadSystemButtonProperties button_properties{};
|
||||
Core::HID::NpadBatteryLevel battery_level_dual{};
|
||||
Core::HID::NpadBatteryLevel battery_level_left{};
|
||||
Core::HID::NpadBatteryLevel battery_level_right{};
|
||||
AppletFooterUiAttributes applet_footer_attributes{};
|
||||
AppletFooterUiType applet_footer_type{AppletFooterUiType::None};
|
||||
INSERT_PADDING_BYTES(0x5B); // Reserved
|
||||
INSERT_PADDING_BYTES(0x20); // Unknown
|
||||
Lifo<NpadGcTriggerState, HidEntryCount> gc_trigger_lifo{};
|
||||
NpadLarkType lark_type_l_and_main{};
|
||||
NpadLarkType lark_type_r{};
|
||||
NpadLuciaType lucia_type{};
|
||||
NpadLagerType lager_type{};
|
||||
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;
|
||||
};
|
||||
static_assert(sizeof(NpadInternalState) == 0x43F8, "NpadInternalState is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::NpadSharedMemoryEntry
|
||||
struct NpadSharedMemoryEntry {
|
||||
NpadInternalState internal_state;
|
||||
INSERT_PADDING_BYTES(0xC08);
|
||||
};
|
||||
static_assert(sizeof(NpadSharedMemoryEntry) == 0x5000, "NpadSharedMemoryEntry is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::NpadSharedMemoryFormat
|
||||
struct NpadSharedMemoryFormat {
|
||||
std::array<NpadSharedMemoryEntry, NpadCount> npad_entry;
|
||||
};
|
||||
static_assert(sizeof(NpadSharedMemoryFormat) == 0x32000,
|
||||
"NpadSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::GestureSharedMemoryFormat
|
||||
struct GestureSharedMemoryFormat {
|
||||
// This is nn::hid::detail::GestureLifo
|
||||
Lifo<GestureState, HidEntryCount> gesture_lifo{};
|
||||
static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
|
||||
INSERT_PADDING_WORDS(0x3E);
|
||||
};
|
||||
static_assert(sizeof(GestureSharedMemoryFormat) == 0x800,
|
||||
"GestureSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
|
||||
struct ConsoleSixAxisSensorSharedMemoryFormat {
|
||||
u64 sampling_number{};
|
||||
bool is_seven_six_axis_sensor_at_rest{};
|
||||
INSERT_PADDING_BYTES(3); // padding
|
||||
f32 verticalization_error{};
|
||||
Common::Vec3f gyro_bias{};
|
||||
INSERT_PADDING_BYTES(4); // padding
|
||||
};
|
||||
static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20,
|
||||
"ConsoleSixAxisSensorSharedMemoryFormat is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::SharedMemoryFormat
|
||||
struct SharedMemoryFormat {
|
||||
void Initialize() {}
|
||||
|
||||
DebugPadSharedMemoryFormat debug_pad;
|
||||
TouchScreenSharedMemoryFormat touch_screen;
|
||||
MouseSharedMemoryFormat mouse;
|
||||
KeyboardSharedMemoryFormat keyboard;
|
||||
DigitizerSharedMemoryFormat digitizer;
|
||||
HomeButtonSharedMemoryFormat home_button;
|
||||
SleepButtonSharedMemoryFormat sleep_button;
|
||||
CaptureButtonSharedMemoryFormat capture_button;
|
||||
InputDetectorSharedMemoryFormat input_detector;
|
||||
UniquePadSharedMemoryFormat unique_pad;
|
||||
NpadSharedMemoryFormat npad;
|
||||
GestureSharedMemoryFormat gesture;
|
||||
ConsoleSixAxisSensorSharedMemoryFormat console;
|
||||
INSERT_PADDING_BYTES(0x19E0);
|
||||
MouseSharedMemoryFormat debug_mouse;
|
||||
INSERT_PADDING_BYTES(0x2000);
|
||||
};
|
||||
static_assert(offsetof(SharedMemoryFormat, debug_pad) == 0x0, "debug_pad has wrong offset");
|
||||
static_assert(offsetof(SharedMemoryFormat, touch_screen) == 0x400, "touch_screen has wrong offset");
|
||||
static_assert(offsetof(SharedMemoryFormat, mouse) == 0x3400, "mouse has wrong offset");
|
||||
static_assert(offsetof(SharedMemoryFormat, keyboard) == 0x3800, "keyboard has wrong offset");
|
||||
static_assert(offsetof(SharedMemoryFormat, digitizer) == 0x3C00, "digitizer has wrong offset");
|
||||
static_assert(offsetof(SharedMemoryFormat, home_button) == 0x4C00, "home_button has wrong offset");
|
||||
static_assert(offsetof(SharedMemoryFormat, sleep_button) == 0x4E00,
|
||||
"sleep_button has wrong offset");
|
||||
static_assert(offsetof(SharedMemoryFormat, capture_button) == 0x5000,
|
||||
"capture_button has wrong offset");
|
||||
static_assert(offsetof(SharedMemoryFormat, input_detector) == 0x5200,
|
||||
"input_detector has wrong offset");
|
||||
static_assert(offsetof(SharedMemoryFormat, npad) == 0x9A00, "npad has wrong offset");
|
||||
static_assert(offsetof(SharedMemoryFormat, gesture) == 0x3BA00, "gesture has wrong offset");
|
||||
static_assert(offsetof(SharedMemoryFormat, console) == 0x3C200, "console has wrong offset");
|
||||
static_assert(offsetof(SharedMemoryFormat, debug_mouse) == 0x3DC00, "debug_mouse has wrong offset");
|
||||
static_assert(sizeof(SharedMemoryFormat) == 0x40000, "SharedMemoryFormat is an invalid size");
|
||||
|
||||
} // namespace Service::HID
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_format.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_holder.h"
|
||||
#include "core/hle/service/hid/errors.h"
|
||||
|
||||
namespace Service::HID {
|
||||
SharedMemoryHolder::SharedMemoryHolder() {}
|
||||
|
||||
SharedMemoryHolder::~SharedMemoryHolder() {
|
||||
Finalize();
|
||||
}
|
||||
|
||||
Result SharedMemoryHolder::Initialize(Core::System& system) {
|
||||
shared_memory = Kernel::KSharedMemory::Create(system.Kernel());
|
||||
const Result result = shared_memory->Initialize(
|
||||
system.DeviceMemory(), nullptr, Kernel::Svc::MemoryPermission::None,
|
||||
Kernel::Svc::MemoryPermission::Read, sizeof(SharedMemoryFormat));
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
Kernel::KSharedMemory::Register(system.Kernel(), shared_memory);
|
||||
|
||||
is_created = true;
|
||||
is_mapped = true;
|
||||
address = std::construct_at(reinterpret_cast<SharedMemoryFormat*>(shared_memory->GetPointer()));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void SharedMemoryHolder::Finalize() {
|
||||
if (address != nullptr) {
|
||||
shared_memory->Close();
|
||||
}
|
||||
is_created = false;
|
||||
is_mapped = false;
|
||||
address = nullptr;
|
||||
}
|
||||
|
||||
bool SharedMemoryHolder::IsMapped() {
|
||||
return is_mapped;
|
||||
}
|
||||
|
||||
SharedMemoryFormat* SharedMemoryHolder::GetAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
Kernel::KSharedMemory* SharedMemoryHolder::GetHandle() {
|
||||
return shared_memory;
|
||||
}
|
||||
} // namespace Service::HID
|
||||
44
src/core/hle/service/hid/controllers/shared_memory_holder.h
Normal file
44
src/core/hle/service/hid/controllers/shared_memory_holder.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KSharedMemory;
|
||||
}
|
||||
|
||||
namespace Service::HID {
|
||||
struct SharedMemoryFormat;
|
||||
|
||||
// This is nn::hid::detail::SharedMemoryHolder
|
||||
class SharedMemoryHolder {
|
||||
public:
|
||||
SharedMemoryHolder();
|
||||
~SharedMemoryHolder();
|
||||
|
||||
Result Initialize(Core::System& system);
|
||||
void Finalize();
|
||||
|
||||
bool IsMapped();
|
||||
SharedMemoryFormat* GetAddress();
|
||||
Kernel::KSharedMemory* GetHandle();
|
||||
|
||||
private:
|
||||
bool is_owner{};
|
||||
bool is_created{};
|
||||
bool is_mapped{};
|
||||
INSERT_PADDING_BYTES(0x5);
|
||||
Kernel::KSharedMemory* shared_memory;
|
||||
INSERT_PADDING_BYTES(0x38);
|
||||
SharedMemoryFormat* address = nullptr;
|
||||
};
|
||||
// Correct size is 0x50 bytes
|
||||
static_assert(sizeof(SharedMemoryHolder) == 0x50, "SharedMemoryHolder is an invalid size");
|
||||
|
||||
} // namespace Service::HID
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_format.h"
|
||||
#include "core/hle/service/hid/controllers/six_axis.h"
|
||||
#include "core/hle/service/hid/errors.h"
|
||||
#include "core/hle/service/hid/hid_util.h"
|
||||
@@ -132,30 +133,30 @@ void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
}
|
||||
|
||||
sixaxis_fullkey_state.sampling_number =
|
||||
sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_handheld_state.sampling_number =
|
||||
sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_dual_left_state.sampling_number =
|
||||
sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_dual_right_state.sampling_number =
|
||||
sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_left_lifo_state.sampling_number =
|
||||
sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_right_lifo_state.sampling_number =
|
||||
sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
|
||||
if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
|
||||
// This buffer only is updated on handheld on HW
|
||||
sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
|
||||
sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state);
|
||||
} else {
|
||||
// Handheld doesn't update this buffer on HW
|
||||
sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
|
||||
sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state);
|
||||
}
|
||||
|
||||
sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
|
||||
sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
|
||||
sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
|
||||
sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
|
||||
sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state);
|
||||
sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state);
|
||||
sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state);
|
||||
sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_format.h"
|
||||
#include "core/hle/service/hid/controllers/stubbed.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase{hid_core_} {
|
||||
raw_shared_memory = raw_shared_memory_;
|
||||
}
|
||||
Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_,
|
||||
CommonHeader& ring_lifo_header)
|
||||
: ControllerBase{hid_core_}, header{ring_lifo_header} {}
|
||||
|
||||
Controller_Stubbed::~Controller_Stubbed() = default;
|
||||
|
||||
@@ -25,18 +22,10 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
return;
|
||||
}
|
||||
|
||||
CommonHeader header{};
|
||||
header.timestamp = core_timing.GetGlobalTimeNs().count();
|
||||
header.total_entry_count = 17;
|
||||
header.entry_count = 0;
|
||||
header.last_entry_index = 0;
|
||||
|
||||
std::memcpy(raw_shared_memory + common_offset, &header, sizeof(CommonHeader));
|
||||
}
|
||||
|
||||
void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
|
||||
common_offset = off;
|
||||
smart_update = true;
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
|
||||
namespace Service::HID {
|
||||
struct CommonHeader;
|
||||
|
||||
class Controller_Stubbed final : public ControllerBase {
|
||||
public:
|
||||
explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, CommonHeader& ring_lifo_header);
|
||||
~Controller_Stubbed() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -21,19 +22,8 @@ public:
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
void SetCommonHeaderOffset(std::size_t off);
|
||||
|
||||
private:
|
||||
struct CommonHeader {
|
||||
s64 timestamp{};
|
||||
s64 total_entry_count{};
|
||||
s64 last_entry_index{};
|
||||
s64 entry_count{};
|
||||
};
|
||||
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
|
||||
|
||||
u8* raw_shared_memory = nullptr;
|
||||
CommonHeader& header;
|
||||
bool smart_update{};
|
||||
std::size_t common_offset{};
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -2,26 +2,22 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/hid/emulated_console.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/shared_memory_format.h"
|
||||
#include "core/hle/service/hid/controllers/touchscreen.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
|
||||
|
||||
TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width),
|
||||
TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_,
|
||||
TouchScreenSharedMemoryFormat& touch_shared_memory)
|
||||
: ControllerBase{hid_core_}, shared_memory{touch_shared_memory},
|
||||
touchscreen_width(Layout::ScreenUndocked::Width),
|
||||
touchscreen_height(Layout::ScreenUndocked::Height) {
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
|
||||
"TouchSharedMemory is bigger than the shared memory");
|
||||
shared_memory = std::construct_at(
|
||||
reinterpret_cast<TouchSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
|
||||
console = hid_core.GetEmulatedConsole();
|
||||
}
|
||||
|
||||
@@ -32,11 +28,11 @@ void TouchScreen::OnInit() {}
|
||||
void TouchScreen::OnRelease() {}
|
||||
|
||||
void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
|
||||
shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory->touch_screen_lifo.buffer_count = 0;
|
||||
shared_memory->touch_screen_lifo.buffer_tail = 0;
|
||||
shared_memory.touch_screen_lifo.buffer_count = 0;
|
||||
shared_memory.touch_screen_lifo.buffer_tail = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -86,7 +82,7 @@ void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
|
||||
|
||||
const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count());
|
||||
const auto& last_entry = shared_memory->touch_screen_lifo.ReadCurrentEntry().state;
|
||||
const auto& last_entry = shared_memory.touch_screen_lifo.ReadCurrentEntry().state;
|
||||
|
||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
||||
next_state.entry_count = static_cast<s32>(active_fingers_count);
|
||||
@@ -118,7 +114,7 @@ void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
}
|
||||
}
|
||||
|
||||
shared_memory->touch_screen_lifo.WriteNextEntry(next_state);
|
||||
shared_memory.touch_screen_lifo.WriteNextEntry(next_state);
|
||||
}
|
||||
|
||||
void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) {
|
||||
|
||||
@@ -3,20 +3,23 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include <array>
|
||||
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
#include "core/hle/service/hid/controllers/types/touch_types.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedConsole;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
struct TouchScreenSharedMemoryFormat;
|
||||
|
||||
class TouchScreen final : public ControllerBase {
|
||||
public:
|
||||
explicit TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
explicit TouchScreen(Core::HID::HIDCore& hid_core_,
|
||||
TouchScreenSharedMemoryFormat& touch_shared_memory);
|
||||
~TouchScreen() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -31,27 +34,8 @@ public:
|
||||
void SetTouchscreenDimensions(u32 width, u32 height);
|
||||
|
||||
private:
|
||||
static constexpr std::size_t MAX_FINGERS = 16;
|
||||
|
||||
// This is nn::hid::TouchScreenState
|
||||
struct TouchScreenState {
|
||||
s64 sampling_number{};
|
||||
s32 entry_count{};
|
||||
INSERT_PADDING_BYTES(4); // Reserved
|
||||
std::array<Core::HID::TouchState, MAX_FINGERS> states{};
|
||||
};
|
||||
static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
|
||||
|
||||
struct TouchSharedMemory {
|
||||
// This is nn::hid::detail::TouchScreenLifo
|
||||
Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{};
|
||||
static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
|
||||
INSERT_PADDING_WORDS(0xF2);
|
||||
};
|
||||
static_assert(sizeof(TouchSharedMemory) == 0x3000, "TouchSharedMemory is an invalid size");
|
||||
|
||||
TouchScreenState next_state{};
|
||||
TouchSharedMemory* shared_memory = nullptr;
|
||||
TouchScreenSharedMemoryFormat& shared_memory;
|
||||
Core::HID::EmulatedConsole* console = nullptr;
|
||||
|
||||
std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
|
||||
|
||||
31
src/core/hle/service/hid/controllers/types/debug_pad_types.h
Normal file
31
src/core/hle/service/hid/controllers/types/debug_pad_types.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
// This is nn::hid::DebugPadAttribute
|
||||
struct DebugPadAttribute {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> connected;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
|
||||
|
||||
// This is nn::hid::DebugPadState
|
||||
struct DebugPadState {
|
||||
s64 sampling_number{};
|
||||
DebugPadAttribute attribute{};
|
||||
Core::HID::DebugPadButton pad_state{};
|
||||
Core::HID::AnalogStickState r_stick{};
|
||||
Core::HID::AnalogStickState l_stick{};
|
||||
};
|
||||
static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
|
||||
|
||||
} // namespace Service::HID
|
||||
77
src/core/hle/service/hid/controllers/types/gesture_types.h
Normal file
77
src/core/hle/service/hid/controllers/types/gesture_types.h
Normal file
@@ -0,0 +1,77 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/point.h"
|
||||
|
||||
namespace Service::HID {
|
||||
static constexpr size_t MAX_FINGERS = 16;
|
||||
static constexpr size_t MAX_POINTS = 4;
|
||||
|
||||
// This is nn::hid::GestureType
|
||||
enum class GestureType : u32 {
|
||||
Idle, // Nothing touching the screen
|
||||
Complete, // Set at the end of a touch event
|
||||
Cancel, // Set when the number of fingers change
|
||||
Touch, // A finger just touched the screen
|
||||
Press, // Set if last type is touch and the finger hasn't moved
|
||||
Tap, // Fast press then release
|
||||
Pan, // All points moving together across the screen
|
||||
Swipe, // Fast press movement and release of a single point
|
||||
Pinch, // All points moving away/closer to the midpoint
|
||||
Rotate, // All points rotating from the midpoint
|
||||
};
|
||||
|
||||
// This is nn::hid::GestureDirection
|
||||
enum class GestureDirection : u32 {
|
||||
None,
|
||||
Left,
|
||||
Up,
|
||||
Right,
|
||||
Down,
|
||||
};
|
||||
|
||||
// This is nn::hid::GestureAttribute
|
||||
struct GestureAttribute {
|
||||
union {
|
||||
u32 raw{};
|
||||
|
||||
BitField<4, 1, u32> is_new_touch;
|
||||
BitField<8, 1, u32> is_double_tap;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
|
||||
|
||||
// This is nn::hid::GestureState
|
||||
struct GestureState {
|
||||
s64 sampling_number{};
|
||||
s64 detection_count{};
|
||||
GestureType type{GestureType::Idle};
|
||||
GestureDirection direction{GestureDirection::None};
|
||||
Common::Point<s32> pos{};
|
||||
Common::Point<s32> delta{};
|
||||
f32 vel_x{};
|
||||
f32 vel_y{};
|
||||
GestureAttribute attributes{};
|
||||
f32 scale{};
|
||||
f32 rotation_angle{};
|
||||
s32 point_count{};
|
||||
std::array<Common::Point<s32>, 4> points{};
|
||||
};
|
||||
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
|
||||
|
||||
struct GestureProperties {
|
||||
std::array<Common::Point<s32>, MAX_POINTS> points{};
|
||||
std::size_t active_points{};
|
||||
Common::Point<s32> mid_point{};
|
||||
s64 detection_count{};
|
||||
u64 delta_time{};
|
||||
f32 average_distance{};
|
||||
f32 angle{};
|
||||
};
|
||||
|
||||
} // namespace Service::HID
|
||||
20
src/core/hle/service/hid/controllers/types/keyboard_types.h
Normal file
20
src/core/hle/service/hid/controllers/types/keyboard_types.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
// This is nn::hid::detail::KeyboardState
|
||||
struct KeyboardState {
|
||||
s64 sampling_number{};
|
||||
Core::HID::KeyboardModifier modifier{};
|
||||
Core::HID::KeyboardAttribute attribute{};
|
||||
Core::HID::KeyboardKey key{};
|
||||
};
|
||||
static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
|
||||
|
||||
} // namespace Service::HID
|
||||
8
src/core/hle/service/hid/controllers/types/mouse_types.h
Normal file
8
src/core/hle/service/hid/controllers/types/mouse_types.h
Normal file
@@ -0,0 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::HID {} // namespace Service::HID
|
||||
254
src/core/hle/service/hid/controllers/types/npad_types.h
Normal file
254
src/core/hle/service/hid/controllers/types/npad_types.h
Normal file
@@ -0,0 +1,254 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
|
||||
namespace Service::HID {
|
||||
static constexpr std::size_t NpadCount = 10;
|
||||
|
||||
// This is nn::hid::NpadJoyHoldType
|
||||
enum class NpadJoyHoldType : u64 {
|
||||
Vertical = 0,
|
||||
Horizontal = 1,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadJoyAssignmentMode
|
||||
enum class NpadJoyAssignmentMode : u32 {
|
||||
Dual = 0,
|
||||
Single = 1,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadJoyDeviceType
|
||||
enum class NpadJoyDeviceType : s64 {
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadHandheldActivationMode
|
||||
enum class NpadHandheldActivationMode : u64 {
|
||||
Dual = 0,
|
||||
Single = 1,
|
||||
None = 2,
|
||||
MaxActivationMode = 3,
|
||||
};
|
||||
|
||||
// This is nn::hid::system::AppletFooterUiAttributesSet
|
||||
struct AppletFooterUiAttributes {
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
};
|
||||
|
||||
// This is nn::hid::system::AppletFooterUiType
|
||||
enum class AppletFooterUiType : u8 {
|
||||
None = 0,
|
||||
HandheldNone = 1,
|
||||
HandheldJoyConLeftOnly = 2,
|
||||
HandheldJoyConRightOnly = 3,
|
||||
HandheldJoyConLeftJoyConRight = 4,
|
||||
JoyDual = 5,
|
||||
JoyDualLeftOnly = 6,
|
||||
JoyDualRightOnly = 7,
|
||||
JoyLeftHorizontal = 8,
|
||||
JoyLeftVertical = 9,
|
||||
JoyRightHorizontal = 10,
|
||||
JoyRightVertical = 11,
|
||||
SwitchProController = 12,
|
||||
CompatibleProController = 13,
|
||||
CompatibleJoyCon = 14,
|
||||
LarkHvc1 = 15,
|
||||
LarkHvc2 = 16,
|
||||
LarkNesLeft = 17,
|
||||
LarkNesRight = 18,
|
||||
Lucia = 19,
|
||||
Verification = 20,
|
||||
Lagon = 21,
|
||||
};
|
||||
|
||||
using AppletFooterUiVariant = u8;
|
||||
|
||||
// This is "nn::hid::system::AppletDetailedUiType".
|
||||
struct AppletDetailedUiType {
|
||||
AppletFooterUiVariant ui_variant;
|
||||
INSERT_PADDING_BYTES(0x2);
|
||||
AppletFooterUiType footer;
|
||||
};
|
||||
static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
|
||||
// This is nn::hid::NpadCommunicationMode
|
||||
enum class NpadCommunicationMode : u64 {
|
||||
Mode_5ms = 0,
|
||||
Mode_10ms = 1,
|
||||
Mode_15ms = 2,
|
||||
Default = 3,
|
||||
};
|
||||
|
||||
enum class NpadRevision : u32 {
|
||||
Revision0 = 0,
|
||||
Revision1 = 1,
|
||||
Revision2 = 2,
|
||||
Revision3 = 3,
|
||||
};
|
||||
|
||||
// This is nn::hid::detail::ColorAttribute
|
||||
enum class ColorAttribute : u32 {
|
||||
Ok = 0,
|
||||
ReadError = 1,
|
||||
NoController = 2,
|
||||
};
|
||||
static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::NpadFullKeyColorState
|
||||
struct NpadFullKeyColorState {
|
||||
ColorAttribute attribute{ColorAttribute::NoController};
|
||||
Core::HID::NpadControllerColor fullkey{};
|
||||
};
|
||||
static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::NpadJoyColorState
|
||||
struct NpadJoyColorState {
|
||||
ColorAttribute attribute{ColorAttribute::NoController};
|
||||
Core::HID::NpadControllerColor left{};
|
||||
Core::HID::NpadControllerColor right{};
|
||||
};
|
||||
static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
|
||||
|
||||
// This is nn::hid::NpadAttribute
|
||||
struct NpadAttribute {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> is_connected;
|
||||
BitField<1, 1, u32> is_wired;
|
||||
BitField<2, 1, u32> is_left_connected;
|
||||
BitField<3, 1, u32> is_left_wired;
|
||||
BitField<4, 1, u32> is_right_connected;
|
||||
BitField<5, 1, u32> is_right_wired;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
|
||||
|
||||
// This is nn::hid::NpadFullKeyState
|
||||
// This is nn::hid::NpadHandheldState
|
||||
// This is nn::hid::NpadJoyDualState
|
||||
// This is nn::hid::NpadJoyLeftState
|
||||
// This is nn::hid::NpadJoyRightState
|
||||
// This is nn::hid::NpadPalmaState
|
||||
// This is nn::hid::NpadSystemExtState
|
||||
struct NPadGenericState {
|
||||
s64_le sampling_number{};
|
||||
Core::HID::NpadButtonState npad_buttons{};
|
||||
Core::HID::AnalogStickState l_stick{};
|
||||
Core::HID::AnalogStickState r_stick{};
|
||||
NpadAttribute connection_status{};
|
||||
INSERT_PADDING_BYTES(4); // Reserved
|
||||
};
|
||||
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
|
||||
|
||||
// This is nn::hid::server::NpadGcTriggerState
|
||||
struct NpadGcTriggerState {
|
||||
s64 sampling_number{};
|
||||
s32 l_analog{};
|
||||
s32 r_analog{};
|
||||
};
|
||||
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
|
||||
|
||||
// This is nn::hid::NpadSystemProperties
|
||||
struct NPadSystemProperties {
|
||||
union {
|
||||
s64 raw{};
|
||||
BitField<0, 1, s64> is_charging_joy_dual;
|
||||
BitField<1, 1, s64> is_charging_joy_left;
|
||||
BitField<2, 1, s64> is_charging_joy_right;
|
||||
BitField<3, 1, s64> is_powered_joy_dual;
|
||||
BitField<4, 1, s64> is_powered_joy_left;
|
||||
BitField<5, 1, s64> is_powered_joy_right;
|
||||
BitField<9, 1, s64> is_system_unsupported_button;
|
||||
BitField<10, 1, s64> is_system_ext_unsupported_button;
|
||||
BitField<11, 1, s64> is_vertical;
|
||||
BitField<12, 1, s64> is_horizontal;
|
||||
BitField<13, 1, s64> use_plus;
|
||||
BitField<14, 1, s64> use_minus;
|
||||
BitField<15, 1, s64> use_directional_buttons;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
|
||||
|
||||
// This is nn::hid::NpadSystemButtonProperties
|
||||
struct NpadSystemButtonProperties {
|
||||
union {
|
||||
s32 raw{};
|
||||
BitField<0, 1, s32> is_home_button_protection_enabled;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NpadSystemButtonProperties) == 0x4, "NPadButtonProperties is an invalid size");
|
||||
|
||||
// This is nn::hid::system::DeviceType
|
||||
struct DeviceType {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, s32> fullkey;
|
||||
BitField<1, 1, s32> debug_pad;
|
||||
BitField<2, 1, s32> handheld_left;
|
||||
BitField<3, 1, s32> handheld_right;
|
||||
BitField<4, 1, s32> joycon_left;
|
||||
BitField<5, 1, s32> joycon_right;
|
||||
BitField<6, 1, s32> palma;
|
||||
BitField<7, 1, s32> lark_hvc_left;
|
||||
BitField<8, 1, s32> lark_hvc_right;
|
||||
BitField<9, 1, s32> lark_nes_left;
|
||||
BitField<10, 1, s32> lark_nes_right;
|
||||
BitField<11, 1, s32> handheld_lark_hvc_left;
|
||||
BitField<12, 1, s32> handheld_lark_hvc_right;
|
||||
BitField<13, 1, s32> handheld_lark_nes_left;
|
||||
BitField<14, 1, s32> handheld_lark_nes_right;
|
||||
BitField<15, 1, s32> lucia;
|
||||
BitField<16, 1, s32> lagon;
|
||||
BitField<17, 1, s32> lager;
|
||||
BitField<31, 1, s32> system;
|
||||
};
|
||||
};
|
||||
|
||||
// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
|
||||
struct NfcXcdDeviceHandleStateImpl {
|
||||
u64 handle{};
|
||||
bool is_available{};
|
||||
bool is_activated{};
|
||||
INSERT_PADDING_BYTES(0x6); // Reserved
|
||||
u64 sampling_number{};
|
||||
};
|
||||
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
|
||||
"NfcXcdDeviceHandleStateImpl is an invalid size");
|
||||
|
||||
// This is nn::hid::NpadLarkType
|
||||
enum class NpadLarkType : u32 {
|
||||
Invalid,
|
||||
H1,
|
||||
H2,
|
||||
NL,
|
||||
NR,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadLuciaType
|
||||
enum class NpadLuciaType : u32 {
|
||||
Invalid,
|
||||
J,
|
||||
E,
|
||||
U,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadLagonType
|
||||
enum class NpadLagonType : u32 {
|
||||
Invalid,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadLagerType
|
||||
enum class NpadLagerType : u32 {
|
||||
Invalid,
|
||||
J,
|
||||
E,
|
||||
U,
|
||||
};
|
||||
|
||||
} // namespace Service::HID
|
||||
90
src/core/hle/service/hid/controllers/types/touch_types.h
Normal file
90
src/core/hle/service/hid/controllers/types/touch_types.h
Normal file
@@ -0,0 +1,90 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <array>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/point.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
|
||||
namespace Service::HID {
|
||||
static constexpr std::size_t MAX_FINGERS = 16;
|
||||
static constexpr size_t MAX_POINTS = 4;
|
||||
|
||||
// This is nn::hid::GestureType
|
||||
enum class GestureType : u32 {
|
||||
Idle, // Nothing touching the screen
|
||||
Complete, // Set at the end of a touch event
|
||||
Cancel, // Set when the number of fingers change
|
||||
Touch, // A finger just touched the screen
|
||||
Press, // Set if last type is touch and the finger hasn't moved
|
||||
Tap, // Fast press then release
|
||||
Pan, // All points moving together across the screen
|
||||
Swipe, // Fast press movement and release of a single point
|
||||
Pinch, // All points moving away/closer to the midpoint
|
||||
Rotate, // All points rotating from the midpoint
|
||||
};
|
||||
|
||||
// This is nn::hid::GestureDirection
|
||||
enum class GestureDirection : u32 {
|
||||
None,
|
||||
Left,
|
||||
Up,
|
||||
Right,
|
||||
Down,
|
||||
};
|
||||
|
||||
// This is nn::hid::GestureAttribute
|
||||
struct GestureAttribute {
|
||||
union {
|
||||
u32 raw{};
|
||||
|
||||
BitField<4, 1, u32> is_new_touch;
|
||||
BitField<8, 1, u32> is_double_tap;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
|
||||
|
||||
// This is nn::hid::GestureState
|
||||
struct GestureState {
|
||||
s64 sampling_number{};
|
||||
s64 detection_count{};
|
||||
GestureType type{GestureType::Idle};
|
||||
GestureDirection direction{GestureDirection::None};
|
||||
Common::Point<s32> pos{};
|
||||
Common::Point<s32> delta{};
|
||||
f32 vel_x{};
|
||||
f32 vel_y{};
|
||||
GestureAttribute attributes{};
|
||||
f32 scale{};
|
||||
f32 rotation_angle{};
|
||||
s32 point_count{};
|
||||
std::array<Common::Point<s32>, 4> points{};
|
||||
};
|
||||
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
|
||||
|
||||
struct GestureProperties {
|
||||
std::array<Common::Point<s32>, MAX_POINTS> points{};
|
||||
std::size_t active_points{};
|
||||
Common::Point<s32> mid_point{};
|
||||
s64 detection_count{};
|
||||
u64 delta_time{};
|
||||
f32 average_distance{};
|
||||
f32 angle{};
|
||||
};
|
||||
|
||||
// This is nn::hid::TouchScreenState
|
||||
struct TouchScreenState {
|
||||
s64 sampling_number{};
|
||||
s32 entry_count{};
|
||||
INSERT_PADDING_BYTES(4); // Reserved
|
||||
std::array<Core::HID::TouchState, MAX_FINGERS> states{};
|
||||
};
|
||||
static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
|
||||
|
||||
} // namespace Service::HID
|
||||
@@ -1,39 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/xpad.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
|
||||
|
||||
XPad::XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size,
|
||||
"XpadSharedMemory is bigger than the shared memory");
|
||||
shared_memory = std::construct_at(
|
||||
reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
|
||||
}
|
||||
XPad::~XPad() = default;
|
||||
|
||||
void XPad::OnInit() {}
|
||||
|
||||
void XPad::OnRelease() {}
|
||||
|
||||
void XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory->basic_xpad_lifo.buffer_count = 0;
|
||||
shared_memory->basic_xpad_lifo.buffer_tail = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& last_entry = shared_memory->basic_xpad_lifo.ReadCurrentEntry().state;
|
||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
||||
// TODO(ogniK): Update xpad states
|
||||
|
||||
shared_memory->basic_xpad_lifo.WriteNextEntry(next_state);
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
@@ -1,112 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Service::HID {
|
||||
class XPad final : public ControllerBase {
|
||||
public:
|
||||
explicit XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~XPad() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
// When the controller is released
|
||||
void OnRelease() override;
|
||||
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
private:
|
||||
// This is nn::hid::BasicXpadAttributeSet
|
||||
struct BasicXpadAttributeSet {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> is_connected;
|
||||
BitField<1, 1, u32> is_wired;
|
||||
BitField<2, 1, u32> is_left_connected;
|
||||
BitField<3, 1, u32> is_left_wired;
|
||||
BitField<4, 1, u32> is_right_connected;
|
||||
BitField<5, 1, u32> is_right_wired;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size");
|
||||
|
||||
// This is nn::hid::BasicXpadButtonSet
|
||||
struct BasicXpadButtonSet {
|
||||
union {
|
||||
u32 raw{};
|
||||
// Button states
|
||||
BitField<0, 1, u32> a;
|
||||
BitField<1, 1, u32> b;
|
||||
BitField<2, 1, u32> x;
|
||||
BitField<3, 1, u32> y;
|
||||
BitField<4, 1, u32> l_stick;
|
||||
BitField<5, 1, u32> r_stick;
|
||||
BitField<6, 1, u32> l;
|
||||
BitField<7, 1, u32> r;
|
||||
BitField<8, 1, u32> zl;
|
||||
BitField<9, 1, u32> zr;
|
||||
BitField<10, 1, u32> plus;
|
||||
BitField<11, 1, u32> minus;
|
||||
|
||||
// D-Pad
|
||||
BitField<12, 1, u32> d_left;
|
||||
BitField<13, 1, u32> d_up;
|
||||
BitField<14, 1, u32> d_right;
|
||||
BitField<15, 1, u32> d_down;
|
||||
|
||||
// Left JoyStick
|
||||
BitField<16, 1, u32> l_stick_left;
|
||||
BitField<17, 1, u32> l_stick_up;
|
||||
BitField<18, 1, u32> l_stick_right;
|
||||
BitField<19, 1, u32> l_stick_down;
|
||||
|
||||
// Right JoyStick
|
||||
BitField<20, 1, u32> r_stick_left;
|
||||
BitField<21, 1, u32> r_stick_up;
|
||||
BitField<22, 1, u32> r_stick_right;
|
||||
BitField<23, 1, u32> r_stick_down;
|
||||
|
||||
// Not always active?
|
||||
BitField<24, 1, u32> left_sl;
|
||||
BitField<25, 1, u32> left_sr;
|
||||
|
||||
BitField<26, 1, u32> right_sl;
|
||||
BitField<27, 1, u32> right_sr;
|
||||
|
||||
BitField<28, 1, u32> palma;
|
||||
BitField<30, 1, u32> handheld_left_b;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::BasicXpadState
|
||||
struct BasicXpadState {
|
||||
s64 sampling_number{};
|
||||
BasicXpadAttributeSet attributes{};
|
||||
BasicXpadButtonSet pad_states{};
|
||||
Core::HID::AnalogStickState l_stick{};
|
||||
Core::HID::AnalogStickState r_stick{};
|
||||
};
|
||||
static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
|
||||
|
||||
struct XpadSharedMemory {
|
||||
// This is nn::hid::detail::BasicXpadLifo
|
||||
Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{};
|
||||
static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
|
||||
INSERT_PADDING_WORDS(0x4E);
|
||||
};
|
||||
static_assert(sizeof(XpadSharedMemory) == 0x400, "XpadSharedMemory is an invalid size");
|
||||
|
||||
BasicXpadState next_state{};
|
||||
XpadSharedMemory* shared_memory = nullptr;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user