mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-17 08:37:43 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c066e49fd3 | |||
| 7b9210648f | |||
| c95773e20a | |||
| f57c210a5f | |||
| 6374f7f51f | |||
| 73e004de6e | |||
| f7b8ade40e | |||
| 8c1194474e | |||
| 90aafeedc1 | |||
| 4fe5f62c38 | |||
| c3f1e6562b | |||
| ac35358b3f | |||
| 7bf95be2c2 | |||
| 4ce45b3b37 | |||
| fd34024f0e | |||
| d76f8f91c4 | |||
| a7061eb4c8 | |||
| fa4e7c6992 | |||
| b77308ced6 | |||
| defddec47f | |||
| c5405250d1 | |||
| 10c07d700c | |||
| 505b157647 |
@@ -1,13 +0,0 @@
|
||||
diff --git a/libs/cobalt/include/boost/cobalt/concepts.hpp b/libs/cobalt/include/boost/cobalt/concepts.hpp
|
||||
index d49f2ec..a9bdb80 100644
|
||||
--- a/libs/cobalt/include/boost/cobalt/concepts.hpp
|
||||
+++ b/libs/cobalt/include/boost/cobalt/concepts.hpp
|
||||
@@ -62,7 +62,7 @@ struct enable_awaitables
|
||||
template <typename T>
|
||||
concept with_get_executor = requires (T& t)
|
||||
{
|
||||
- {t.get_executor()} -> asio::execution::executor;
|
||||
+ t.get_executor();
|
||||
};
|
||||
|
||||
|
||||
+3
-7
@@ -417,12 +417,9 @@ AddJsonPackage(boost)
|
||||
set(BOOST_NO_HEADERS ${Boost_ADDED})
|
||||
|
||||
if (Boost_ADDED)
|
||||
if (MSVC OR ANDROID)
|
||||
add_compile_definitions(YUZU_BOOST_v1)
|
||||
endif()
|
||||
|
||||
add_compile_definitions(YUZU_BOOST_v1)
|
||||
if (NOT MSVC OR CXX_CLANG)
|
||||
# boost sucks
|
||||
# solaris sucks
|
||||
if (SOLARIS)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthreads>)
|
||||
endif()
|
||||
@@ -431,8 +428,7 @@ if (Boost_ADDED)
|
||||
target_compile_options(boost_icl INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
|
||||
target_compile_options(boost_asio INTERFACE
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>
|
||||
)
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
+6
-9
@@ -6,22 +6,19 @@
|
||||
"version": "v0.19.0"
|
||||
},
|
||||
"boost": {
|
||||
"artifact": "%VERSION%-cmake.tar.xz",
|
||||
"artifact": "boost-%VERSION%.tar.zst",
|
||||
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
|
||||
"hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
|
||||
"hash": "681d97be4386767662e230b2e7c9754d45e709cc5db4e747c23c27d1f5b0e87770bdf19b0bc44dcb0e8349324887bbb8fffb432098f4d0ce74a5043021a90afc",
|
||||
"min_version": "1.57",
|
||||
"package": "Boost",
|
||||
"patches": [
|
||||
"0001-clang-cl.patch"
|
||||
],
|
||||
"repo": "boostorg/boost",
|
||||
"version": "boost-1.90.0"
|
||||
"repo": "eden-emulator/ext-boost",
|
||||
"version": "1.92.0"
|
||||
},
|
||||
"boost_headers": {
|
||||
"bundled": true,
|
||||
"hash": "4ef845775e2277a8104ded6ddf749aa262ce52cf8438042869a048f9a0156dd772fbbcfa74efa1378fecef339b7286f6fe4b4feb5c45d49966b35d08e3e83507",
|
||||
"hash": "c5fa2cd72f6e6666b7963b97bc359c75284b8fb540c30f3629a028b85270c9bc66c8a051383964f2bd4c1e005a4691593d15696e7ef39ea87cf6cff9e5691fb2",
|
||||
"repo": "boostorg/headers",
|
||||
"version": "boost-1.90.0"
|
||||
"version": "boost-1.91.0"
|
||||
},
|
||||
"catch2": {
|
||||
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
|
||||
|
||||
@@ -110,7 +110,6 @@ add_library(
|
||||
socket_types.h
|
||||
sparse_large_vector.cpp
|
||||
sparse_large_vector.h
|
||||
spin_lock.h
|
||||
stb.cpp
|
||||
stb.h
|
||||
steady_clock.cpp
|
||||
|
||||
+18
-20
@@ -17,35 +17,33 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
// glibc, mlibc, musl, and newlib all define their own variants of strerror_r
|
||||
// We don't need to use the preprocessor, we can just select depending on return type
|
||||
template<typename T> std::string HandleStrerrorR(T r, char *err_str);
|
||||
template<char*> std::string HandleStrerrorR(const char* r, char *) { return std::string{r}; }
|
||||
template<char const*> std::string HandleStrerrorR(const char* r, char *) { return std::string{r}; }
|
||||
template<int> std::string HandleStrerrorR(int r, char *err_str) {
|
||||
return std::string{r != 0
|
||||
? "(strerror_r failed to format error)"
|
||||
: err_str};
|
||||
}
|
||||
|
||||
std::string NativeErrorToString(int e) {
|
||||
#ifdef _WIN32
|
||||
LPSTR err_str;
|
||||
|
||||
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
|
||||
if (!res) {
|
||||
return "(FormatMessageA failed to format error)";
|
||||
LPSTR(&err_str), 1, nullptr);
|
||||
if (res) {
|
||||
std::string ret(err_str);
|
||||
LocalFree(err_str);
|
||||
return ret;
|
||||
}
|
||||
std::string ret(err_str);
|
||||
LocalFree(err_str);
|
||||
return ret;
|
||||
return "(FormatMessageA failed to format error)";
|
||||
#else
|
||||
char err_str[255];
|
||||
#if defined(__ANDROID__) || \
|
||||
(defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)))
|
||||
// Thread safe (GNU-specific)
|
||||
const char* str = strerror_r(e, err_str, sizeof(err_str));
|
||||
return std::string(str);
|
||||
#else
|
||||
// Thread safe (XSI-compliant)
|
||||
int second_err = strerror_r(e, err_str, sizeof(err_str));
|
||||
if (second_err != 0) {
|
||||
return "(strerror_r failed to format error)";
|
||||
}
|
||||
return std::string(err_str);
|
||||
#endif // GLIBC etc.
|
||||
return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str);
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
|
||||
+66
-13
@@ -5,6 +5,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include "common/container/unordered_map.h"
|
||||
@@ -480,28 +481,80 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
|
||||
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
||||
path.end());
|
||||
|
||||
const bool absolute = !path.empty() && path[0] == type2;
|
||||
std::vector<std::string_view> parts;
|
||||
std::string root;
|
||||
std::string_view components{path};
|
||||
bool drive_relative = false;
|
||||
|
||||
for (const auto part : SplitPathComponents(path))
|
||||
{
|
||||
if (part.empty() || part == ".")
|
||||
continue;
|
||||
if (part == ".." && !parts.empty() && parts.back() != "..")
|
||||
parts.pop_back();
|
||||
else if (part != "..") parts.push_back(part);
|
||||
#ifdef _WIN32
|
||||
const bool network = path.size() > 1 && path[0] == type2 && path[1] == type2;
|
||||
const bool drive =
|
||||
path.size() > 1 && std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':';
|
||||
|
||||
if (network) {
|
||||
root.assign(2, type2);
|
||||
components.remove_prefix(2);
|
||||
} else if (drive) {
|
||||
root.assign(path.data(), 2);
|
||||
components.remove_prefix(2);
|
||||
if (!components.empty() && components.front() == type2) {
|
||||
root += type2;
|
||||
components.remove_prefix(1);
|
||||
} else {
|
||||
drive_relative = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (root.empty() && !components.empty() && components.front() == type2) {
|
||||
root += type2;
|
||||
components.remove_prefix(1);
|
||||
}
|
||||
|
||||
std::string resolved = absolute ? std::string(1, type2) : std::string{};
|
||||
for (std::size_t i = 0; i < parts.size(); ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
const auto path_parts = SplitPathComponents(components);
|
||||
std::size_t root_component_count = 0;
|
||||
#ifdef _WIN32
|
||||
if (network) {
|
||||
root_component_count = 2;
|
||||
|
||||
const auto is_unc = [](std::string_view part) {
|
||||
return part.size() == 3 && (part[0] == 'U' || part[0] == 'u') &&
|
||||
(part[1] == 'N' || part[1] == 'n') && (part[2] == 'C' || part[2] == 'c');
|
||||
};
|
||||
if (path_parts.size() >= 2 && path_parts[0] == "?" && is_unc(path_parts[1])) {
|
||||
root_component_count = 4;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<std::string_view> parts;
|
||||
for (std::size_t i = 0; i < path_parts.size(); ++i) {
|
||||
const auto part = path_parts[i];
|
||||
if (i < root_component_count) {
|
||||
parts.push_back(part);
|
||||
} else if (part.empty() || part == ".") {
|
||||
continue;
|
||||
} else if (part == "..") {
|
||||
if (parts.size() > root_component_count) {
|
||||
parts.pop_back();
|
||||
}
|
||||
} else {
|
||||
parts.push_back(part);
|
||||
}
|
||||
}
|
||||
|
||||
const std::size_t root_length = root.size();
|
||||
std::string resolved = std::move(root);
|
||||
for (std::size_t i = 0; i < parts.size(); ++i) {
|
||||
if (i != 0 || (!resolved.empty() && resolved.back() != type2 && !drive_relative))
|
||||
resolved += type2;
|
||||
resolved.append(parts[i].data(), parts[i].size());
|
||||
}
|
||||
|
||||
path = std::move(resolved);
|
||||
|
||||
if (!path.empty() && path.size() == root_length) {
|
||||
return path;
|
||||
}
|
||||
return std::string(RemoveTrailingSlash(path));
|
||||
}
|
||||
|
||||
|
||||
@@ -347,8 +347,9 @@ enum class DirectorySeparator {
|
||||
// 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
|
||||
// Normalizes directory separators, removes duplicate and non-root trailing separators, and resolves
|
||||
// '.' and '..' components without traversing above the path root. Windows drive and UNC roots are
|
||||
// preserved.
|
||||
[[nodiscard]] std::string SanitizePath(
|
||||
std::string_view path,
|
||||
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
||||
|
||||
@@ -96,15 +96,15 @@ struct PageTable {
|
||||
}
|
||||
|
||||
/// Write page info atomically
|
||||
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
||||
inline void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
||||
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
||||
}
|
||||
|
||||
constexpr void MarkRasterizerCached() noexcept {
|
||||
inline void MarkRasterizerCached() noexcept {
|
||||
data_raw.fetch_or(0b111);
|
||||
}
|
||||
|
||||
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
|
||||
inline void MarkDebug(u64 ptr, u16 block) noexcept {
|
||||
Store(true, PageType::DebugMemory, block, ptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#elif defined(ARCHITECTURE_x86_64)
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
#include <atomic>
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// @brief A lock similar to mutex that forces a thread to spin wait instead calling the
|
||||
/// supervisor. Should be used on short sequences of code.
|
||||
struct SpinLock {
|
||||
SpinLock() noexcept = default;
|
||||
SpinLock(const SpinLock&) noexcept = delete;
|
||||
SpinLock& operator=(const SpinLock&) noexcept = delete;
|
||||
SpinLock(SpinLock&&) noexcept = delete;
|
||||
SpinLock& operator=(SpinLock&&) noexcept = delete;
|
||||
|
||||
inline void lock() noexcept {
|
||||
while (lck.test_and_set(std::memory_order_acquire)) {
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
_mm_pause();
|
||||
#elif defined(ARCHITECTURE_arm64) && defined(_MSC_VER)
|
||||
__yield();
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
asm("yield");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
inline void unlock() noexcept {
|
||||
lck.clear(std::memory_order_release);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool try_lock() noexcept {
|
||||
return !lck.test_and_set(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
std::atomic_flag lck = ATOMIC_FLAG_INIT;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
+2
-2
@@ -208,9 +208,9 @@ UUID UUID::MakeRandomRFC4122V4() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
UUID UUID::MakeRFC4122V5(std::span<u8, 20> sha1) {
|
||||
UUID UUID::MakeRFC4122V5(std::span<u8, 16> sha1) {
|
||||
UUID uuid{};
|
||||
std::memcpy(&uuid.uuid, sha1.data(), sizeof(UUID));
|
||||
std::memcpy(&uuid.uuid, sha1.data(), sha1.size());
|
||||
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
|
||||
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
|
||||
return uuid;
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ struct UUID {
|
||||
/// @returns A random UUID that is RFC 4122 Version 4 compliant.
|
||||
[[nodiscard]] static UUID MakeRandomRFC4122V4();
|
||||
|
||||
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 20> sha1);
|
||||
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 16> sha1);
|
||||
|
||||
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
|
||||
};
|
||||
|
||||
@@ -796,6 +796,8 @@ add_library(core STATIC
|
||||
hle/service/ns/application_manager_interface.h
|
||||
hle/service/ns/application_version_interface.cpp
|
||||
hle/service/ns/application_version_interface.h
|
||||
hle/service/ns/async_result.cpp
|
||||
hle/service/ns/async_result.h
|
||||
hle/service/ns/content_management_interface.cpp
|
||||
hle/service/ns/content_management_interface.h
|
||||
hle/service/ns/develop_interface.cpp
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "hid_core/frontend/emulated_controller.h"
|
||||
#include "hid_core/hid_core.h"
|
||||
#include "hid_core/hid_types.h"
|
||||
#include <array>
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
@@ -29,23 +30,39 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
|
||||
|
||||
const std::size_t min_supported_players =
|
||||
parameters.enable_single_mode ? 1 : parameters.min_players;
|
||||
using Core::HID::NpadStyleIndex;
|
||||
const std::size_t max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
|
||||
std::size_t num_selected_players = 0;
|
||||
std::array<bool, HID::HIDCore::available_controllers> keep_connected{};
|
||||
|
||||
// Disconnect Handheld first.
|
||||
// reserve existing AND valid players before filling slots. include Handheld, but not other
|
||||
for (std::size_t index = 0; index < hid_core.available_controllers - 1; ++index) {
|
||||
const auto* controller = hid_core.GetEmulatedControllerByIndex(index);
|
||||
if (!parameters.keep_controllers_connected || !controller->IsConnected() || num_selected_players >= max_supported_players) continue;
|
||||
|
||||
const auto style = controller->GetNpadStyleIndex();
|
||||
keep_connected[index] =
|
||||
(style == NpadStyleIndex::Fullkey && parameters.allow_pro_controller) ||
|
||||
(style == NpadStyleIndex::JoyconDual && parameters.allow_dual_joycons) ||
|
||||
(style == NpadStyleIndex::JoyconLeft && parameters.allow_left_joycon) ||
|
||||
(style == NpadStyleIndex::JoyconRight && parameters.allow_right_joycon) ||
|
||||
(style == NpadStyleIndex::Handheld && parameters.enable_single_mode && parameters.allow_handheld && !Settings::IsDockedMode()) ||
|
||||
(style == NpadStyleIndex::GameCube && parameters.allow_gamecube_controller);
|
||||
num_selected_players += keep_connected[index];
|
||||
}
|
||||
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||
handheld->Disconnect();
|
||||
if (!keep_connected[hid_core.available_controllers - 2]) handheld->Disconnect();
|
||||
|
||||
// Deduce the best configuration based on the input parameters.
|
||||
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
|
||||
auto* controller = hid_core.GetEmulatedControllerByIndex(index);
|
||||
|
||||
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
|
||||
// This makes it easy to connect the desired controllers.
|
||||
if (keep_connected[index]) continue;
|
||||
controller->Disconnect();
|
||||
|
||||
// Only connect the minimum number of required players.
|
||||
if (index >= min_supported_players) {
|
||||
continue;
|
||||
}
|
||||
// only add players still needed to reach the minimum
|
||||
if (num_selected_players >= min_supported_players) continue;
|
||||
++num_selected_players;
|
||||
|
||||
// Connect controllers based on the following priority list from highest to lowest priority:
|
||||
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
|
||||
|
||||
@@ -465,6 +465,10 @@ void KScheduler::ScheduleImplFiber(KernelCore& kernel) {
|
||||
// Check if we need scheduling. If we do, then we can't complete the switch and should
|
||||
// retry.
|
||||
if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
|
||||
// Some libc++ lazily init mutex
|
||||
[[maybe_unused]] auto const can_lock = highest_priority_thread->m_context_guard.try_lock();
|
||||
DEBUG_ASSERT(!can_lock);
|
||||
|
||||
// Our switch failed.
|
||||
// We should unlock the thread context, and then retry.
|
||||
highest_priority_thread->m_context_guard.unlock();
|
||||
@@ -496,6 +500,10 @@ void KScheduler::Unload(KernelCore& kernel, KThread* thread) {
|
||||
|
||||
// Check if the thread is terminated by checking the DPC flags.
|
||||
if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
|
||||
// Some libc++ lazily init mutex
|
||||
[[maybe_unused]] auto const can_lock = thread->m_context_guard.try_lock();
|
||||
DEBUG_ASSERT(!can_lock);
|
||||
|
||||
// The thread isn't terminated, so we want to unlock it.
|
||||
thread->m_context_guard.unlock();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/atomic_ops.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/spin_lock.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -30,7 +29,7 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
constexpr KSlabHeapImpl() = default;
|
||||
KSlabHeapImpl() = default;
|
||||
|
||||
void Initialize() {
|
||||
ASSERT(m_head == nullptr);
|
||||
@@ -68,7 +67,7 @@ public:
|
||||
|
||||
private:
|
||||
std::atomic<Node*> m_head{};
|
||||
Common::SpinLock m_lock;
|
||||
std::mutex m_lock;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "common/intrusive_red_black_tree.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/spin_lock.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/hle/kernel/k_affinity_mask.h"
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
@@ -920,7 +919,7 @@ private:
|
||||
bool m_resource_limit_release_hint{};
|
||||
bool m_is_kernel_address_key{};
|
||||
StackParameters m_stack_parameters{};
|
||||
Common::SpinLock m_context_guard{};
|
||||
std::mutex m_context_guard{};
|
||||
|
||||
// For emulation
|
||||
std::shared_ptr<Common::Fiber> m_host_context{};
|
||||
|
||||
@@ -23,6 +23,12 @@ constexpr std::size_t profile_username_size{32};
|
||||
using ProfileUsername = std::array<u8, profile_username_size>;
|
||||
using UserIDArray = std::array<Common::UUID, MAX_USERS>;
|
||||
|
||||
// This is nn::account::Uid
|
||||
struct Uid {
|
||||
std::array<u8, 0x10> unk0;
|
||||
};
|
||||
static_assert(sizeof(Uid) == 0x10);
|
||||
|
||||
/// Contains extra data related to a user.
|
||||
/// TODO: RE this structure
|
||||
struct UserData {
|
||||
|
||||
@@ -347,21 +347,22 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
|
||||
|
||||
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
||||
LOG_WARNING(Service_AM, "(stubbed)");
|
||||
R_UNLESS(out_pseudo_device_id, ResultUnknown);
|
||||
R_UNLESS(out_pseudo_device_id != nullptr, ResultUnknown);
|
||||
|
||||
// This should be hashed with the device specific hash
|
||||
// for now this will do
|
||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
||||
u8 hash[EVP_MAX_MD_SIZE];
|
||||
R_UNLESS(res.first != nullptr, ResultUnknown);
|
||||
std::array<u8, EVP_MAX_MD_SIZE> hash;
|
||||
unsigned int hash_len = 0;
|
||||
auto const seed = res.first->raw.seed_for_pseudo_device_id;
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
||||
auto const algorithm = EVP_sha1();
|
||||
EVP_DigestInit_ex(ctx, algorithm, nullptr);
|
||||
EVP_DigestUpdate(ctx, &seed, sizeof(seed));
|
||||
EVP_DigestFinal_ex(ctx, hash, &hash_len);
|
||||
EVP_DigestFinal_ex(ctx, hash.data(), &hash_len);
|
||||
EVP_MD_CTX_free(ctx);
|
||||
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 20>{hash, std::size(hash)});
|
||||
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 16>{hash.begin(), hash.begin() + 16});
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +154,9 @@ FSP_SRV::FSP_SRV(Core::System& system_)
|
||||
{720, nullptr, "AbandonAccessFailure"},
|
||||
{800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
|
||||
{810, nullptr, "RegisterProgramIndexMapInfo"},
|
||||
{820, nullptr, "GetContentStorageInfoIndex"},
|
||||
{830, nullptr, "EncryptStreamPlaySaveData"},
|
||||
{831, nullptr, "DecryptStreamPlaySaveData"},
|
||||
{1000, nullptr, "SetBisRootForHost"},
|
||||
{1001, nullptr, "SetSaveDataSize"},
|
||||
{1002, nullptr, "SetSaveDataRootPath"},
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
namespace IPC {
|
||||
|
||||
constexpr Result ResultNotSupported{ErrorModule::HIPC, 1};
|
||||
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
|
||||
|
||||
struct ResponseBuilder {
|
||||
|
||||
@@ -6,10 +6,16 @@
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_client_session.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/ngc/ngc.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "frontend_common/firmware_manager.h"
|
||||
|
||||
namespace Service::NGC {
|
||||
|
||||
@@ -166,12 +172,123 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct SaveDataHandle {
|
||||
u64 unk0;
|
||||
};
|
||||
static_assert(sizeof(SaveDataHandle) == 0x08);
|
||||
|
||||
class IUserShimScopedObject final : public ServiceFramework<IUserShimScopedObject> {
|
||||
public:
|
||||
explicit IUserShimScopedObject(Core::System& system_) : ServiceFramework(system_, "IUserShimScopedObject") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{450, nullptr, "InitializeForSaveData"},
|
||||
{451, nullptr, "FinalizeForSaveData"},
|
||||
{452, D<&IUserShimScopedObject::OpenSaveData>, "OpenSaveData"},
|
||||
{453, nullptr, "CloseSaveData"},
|
||||
{454, D<&IUserShimScopedObject::ReadSaveSlot>, "ReadSaveSlot"},
|
||||
{455, D<&IUserShimScopedObject::WriteSaveSlot>, "WriteSaveSlot"},
|
||||
{456, nullptr, "FlushSaveSlot"},
|
||||
{457, nullptr, "CommitSaveData"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
Result OpenSaveData(Account::Uid unk0, Out<SaveDataHandle> unk1) {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
R_THROW(IPC::ResultNotSupported);
|
||||
}
|
||||
|
||||
Result ReadSaveSlot(s32 offset, SaveDataHandle handle, OutBuffer<BufferAttr_HipcAutoSelect> out_data, Out<u32> out_size) {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
R_THROW(IPC::ResultNotSupported);
|
||||
}
|
||||
|
||||
Result WriteSaveSlot(s32 offset, SaveDataHandle handle, InBuffer<BufferAttr_HipcAutoSelect> out_data) {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
// to implement
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
class IUserService final : public ServiceFramework<IUserService> {
|
||||
public:
|
||||
explicit IUserService(Core::System& system_) : ServiceFramework(system_, "stpl:u") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0 , D<&IUserService::Cmd0>, "Cmd0"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
Result Cmd0(u32 unk0, OutInterface<IUserShimScopedObject> out_interface) {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
*out_interface = std::make_shared<IUserShimScopedObject>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
class ISystemShimScopedObject final : public ServiceFramework<ISystemShimScopedObject> {
|
||||
public:
|
||||
explicit ISystemShimScopedObject(Core::System& system_) : ServiceFramework(system_, "ISystemShimScopedObject") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{106, nullptr, "Cmd106"},
|
||||
{107, nullptr, "Cmd107"},
|
||||
{108, D<&ISystemShimScopedObject::Cmd108>, "Cmd108"},
|
||||
{207, nullptr, "Cmd207"},
|
||||
{208, D<&ISystemShimScopedObject::Cmd208>, "Cmd208"},
|
||||
{209, nullptr, "Cmd209"},
|
||||
{210, nullptr, "Cmd210"},
|
||||
{211, nullptr, "Cmd211"},
|
||||
{212, nullptr, "Cmd212"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
Result Cmd108() {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
R_THROW(IPC::ResultNotSupported);
|
||||
}
|
||||
|
||||
Result Cmd208(Out<std::array<u8, 0x20>> unk0) {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
R_THROW(IPC::ResultNotSupported);
|
||||
}
|
||||
};
|
||||
|
||||
class ISystemService final : public ServiceFramework<ISystemService> {
|
||||
public:
|
||||
explicit ISystemService(Core::System& system_) : ServiceFramework(system_, "stpl:sys") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0 , D<&ISystemService::Cmd0>, "Cmd0"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
Result Cmd0(OutInterface<ISystemShimScopedObject> out_interface) {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
*out_interface = std::make_shared<ISystemShimScopedObject>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
|
||||
server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system), 4);
|
||||
server_manager->RegisterNamedService("ngct:s", std::make_shared<IServiceWithManagementApi>(system), 4);
|
||||
server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system), 4);
|
||||
|
||||
// +23.0.0
|
||||
if (FirmwareManager::GetFirmwareVersion(system).first.major >= 23) {
|
||||
server_manager->RegisterNamedService("stpl:u", std::make_shared<IUserService>(system), 4);
|
||||
server_manager->RegisterNamedService("stpl:sys", std::make_shared<ISystemService>(system), 4);
|
||||
}
|
||||
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
|
||||
@@ -228,9 +228,9 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{930, nullptr, "Unknown930"}, //20.0.0+
|
||||
{931, nullptr, "Unknown931"}, //20.0.0+
|
||||
{933, nullptr, "Unknown933"}, //20.0.0+
|
||||
{934, nullptr, "Unknown934"}, //20.0.0+
|
||||
{935, nullptr, "Unknown935"}, //20.0.0+
|
||||
{936, nullptr, "Unknown936"}, //20.0.0+
|
||||
{934, nullptr, "Unknown934"}, //21.0.0+
|
||||
{935, nullptr, "Unknown935"}, //21.0.0+
|
||||
{936, D<&IApplicationManagerInterface::Unknown936>, "Unknown936"}, //21.0.0+
|
||||
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
|
||||
{1001, nullptr, "CorruptApplicationForDebug"},
|
||||
{1002, nullptr, "RequestVerifyAddOnContentsRights"},
|
||||
@@ -422,7 +422,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{4039, nullptr, "Unknown4039"}, //20.0.0+
|
||||
{4040, nullptr, "Unknown4040"}, //20.0.0+
|
||||
{4041, nullptr, "Unknown4041"}, //20.0.0+
|
||||
{4042, nullptr, "Unknown4042"}, //20.0.0+
|
||||
{4042, D<&IApplicationManagerInterface::Unknown4042>, "Unknown4042"}, //20.0.0+
|
||||
{4043, nullptr, "Unknown4043"}, //20.0.0+
|
||||
{4044, nullptr, "Unknown4044"}, //20.0.0+
|
||||
{4045, nullptr, "Unknown4045"}, //20.0.0+
|
||||
@@ -639,6 +639,12 @@ Result IApplicationManagerInterface::IsGameCardApplicationRunning(Out<bool> out_
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::Unknown936(Out<u64> out_result) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called.");
|
||||
*out_result = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
|
||||
Out<bool> out_is_any_application_entity_installed) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
@@ -862,6 +868,15 @@ Result IApplicationManagerInterface::Unknown4023(Out<u64> out_result) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::Unknown4042(OutInterface<IAsyncResult> out_interface,
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event,
|
||||
u64 arg1, u64 arg2) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called, arg1={:016X}, arg2={:016X}", arg1, arg2);
|
||||
*out_event = unknown_event.GetHandle();
|
||||
*out_interface = std::make_shared<IAsyncResult>(system, &unknown_event);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::Unknown4053() {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called.");
|
||||
R_SUCCEED();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/ns/async_result.h"
|
||||
#include "core/hle/service/ns/language.h"
|
||||
#include "core/hle/service/ns/ns_types.h"
|
||||
#include "core/hle/service/os/event.h"
|
||||
@@ -34,6 +35,7 @@ public:
|
||||
Result GetGameCardMountFailureEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetGameCardWakenReadyEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result IsGameCardApplicationRunning(Out<bool> out_is_running);
|
||||
Result Unknown936(Out<u64> out_result);
|
||||
Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
|
||||
Result GetApplicationViewDeprecated(
|
||||
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
|
||||
@@ -71,6 +73,9 @@ public:
|
||||
InBuffer<BufferAttr_HipcMapAlias> logo_path_buffer);
|
||||
Result Unknown4022(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result Unknown4023(Out<u64> out_result);
|
||||
Result Unknown4042(OutInterface<IAsyncResult> out_interface,
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event,
|
||||
u64 arg1, u64 arg2);
|
||||
Result Unknown4053();
|
||||
Result Unknown4105();
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ns/async_result.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
IAsyncResult::IAsyncResult(Core::System& system_, Service::Event* event_)
|
||||
: ServiceFramework{system_, "nn::ns::detail::IAsyncResult"}, event{event_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Get"},
|
||||
{1, D<&IAsyncResult::Cancel>, "Cancel"},
|
||||
{2, nullptr, "GetErrorContext"}, // 4.0.0+
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IAsyncResult::~IAsyncResult() = default;
|
||||
|
||||
Result IAsyncResult::Cancel() {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
if (event != nullptr) {
|
||||
event->Signal(system.Kernel());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::NS
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
class IAsyncResult final : public ServiceFramework<IAsyncResult> {
|
||||
public:
|
||||
explicit IAsyncResult(Core::System& system_, Service::Event* event_);
|
||||
~IAsyncResult() override;
|
||||
|
||||
private:
|
||||
Result Cancel();
|
||||
|
||||
Service::Event* event{};
|
||||
};
|
||||
|
||||
} // namespace Service::NS
|
||||
@@ -311,27 +311,30 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon
|
||||
// u64 - app count
|
||||
memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64));
|
||||
out_length += sizeof(u64);
|
||||
ASSERT(out_length <= t_mem->GetSize());
|
||||
// [list of u64] - size of icons
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
const u64 app_id = app_ids_buffer[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
u64 full_size = control.second->GetSize();
|
||||
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
|
||||
if (const auto control = pm.GetControlMetadata(); control.second) {
|
||||
u64 full_size = control.second->GetSize();
|
||||
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
|
||||
}
|
||||
out_length += sizeof(u64);
|
||||
ASSERT(out_length <= t_mem->GetSize());
|
||||
}
|
||||
// [list of raw icon data]
|
||||
std::vector<u8> full_icon_data;
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
const u64 app_id = app_ids_buffer[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
auto const full_size = control.second->GetSize();
|
||||
if (full_size > 0) {
|
||||
full_icon_data.resize(full_size);
|
||||
control.second->Read(full_icon_data.data(), full_size, 0);
|
||||
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
|
||||
out_length += full_size;
|
||||
if (const auto control = pm.GetControlMetadata(); control.second) {
|
||||
if (auto const full_size = control.second->GetSize(); full_size > 0) {
|
||||
std::vector<u8> full_icon_data(full_size);
|
||||
control.second->Read(full_icon_data.data(), full_size, 0);
|
||||
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
|
||||
out_length += full_size;
|
||||
ASSERT(out_length <= t_mem->GetSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,6 +348,12 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon
|
||||
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
||||
const auto app_ids_buffer = ctx.ReadBuffer();
|
||||
const size_t app_count = app_ids_buffer.size() / sizeof(u64);
|
||||
|
||||
std::vector<u64> application_ids(app_count);
|
||||
if (app_count > 0) {
|
||||
std::memcpy(application_ids.data(), app_ids_buffer.data(), app_count * sizeof(u64));
|
||||
}
|
||||
|
||||
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
|
||||
auto* t_mem = t_mem_obj.GetPointerUnsafe();
|
||||
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
|
||||
@@ -354,8 +363,9 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
|
||||
auto& memory = system.ApplicationMemory();
|
||||
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
const u64 app_id = app_ids_buffer[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
const u64 app_id = application_ids[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
FileSys::LanguageEntry entry{};
|
||||
if (control.first != nullptr) {
|
||||
|
||||
+218
-244
@@ -48,6 +48,12 @@ enum class OptionType : u32 {
|
||||
EnableAlpn = 3,
|
||||
};
|
||||
|
||||
// This is nn::ssl::sf::RenegotiationMode
|
||||
enum RenegotiationMode : u32 {
|
||||
None = 0, ///< None
|
||||
Secure = 1, ///< Secure
|
||||
};
|
||||
|
||||
// This is nn::ssl::sf::SslVersion
|
||||
struct SslVersion {
|
||||
union {
|
||||
@@ -75,34 +81,34 @@ public:
|
||||
shared_data{shared_data_in}, backend{std::move(backend_in)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISslConnection::SetSocketDescriptor, "SetSocketDescriptor"},
|
||||
{1, &ISslConnection::SetHostName, "SetHostName"},
|
||||
{2, &ISslConnection::SetVerifyOption, "SetVerifyOption"},
|
||||
{3, &ISslConnection::SetIoMode, "SetIoMode"},
|
||||
{4, nullptr, "GetSocketDescriptor"},
|
||||
{5, nullptr, "GetHostName"},
|
||||
{0, D<&ISslConnection::SetSocketDescriptor>, "SetSocketDescriptor"},
|
||||
{1, D<&ISslConnection::SetHostName>, "SetHostName"},
|
||||
{2, D<&ISslConnection::SetVerifyOption>, "SetVerifyOption"},
|
||||
{3, D<&ISslConnection::SetIoMode>, "SetIoMode"},
|
||||
{4, D<&ISslConnection::GetSocketDescriptor>, "GetSocketDescriptor"},
|
||||
{5, D<&ISslConnection::GetHostName>, "GetHostName"},
|
||||
{6, nullptr, "GetVerifyOption"},
|
||||
{7, nullptr, "GetIoMode"},
|
||||
{8, &ISslConnection::DoHandshake, "DoHandshake"},
|
||||
{7, D<&ISslConnection::GetIoMode>, "GetIoMode"},
|
||||
{8, D<&ISslConnection::DoHandshake>, "DoHandshake"},
|
||||
{9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"},
|
||||
{10, &ISslConnection::Read, "Read"},
|
||||
{11, &ISslConnection::Write, "Write"},
|
||||
{12, &ISslConnection::Pending, "Pending"},
|
||||
{13, nullptr, "Peek"},
|
||||
{14, nullptr, "Poll"},
|
||||
{15, nullptr, "GetVerifyCertError"},
|
||||
{16, nullptr, "GetNeededServerCertBufferSize"},
|
||||
{17, &ISslConnection::SetSessionCacheMode, "SetSessionCacheMode"},
|
||||
{18, nullptr, "GetSessionCacheMode"},
|
||||
{19, nullptr, "FlushSessionCache"},
|
||||
{20, nullptr, "SetRenegotiationMode"},
|
||||
{21, nullptr, "GetRenegotiationMode"},
|
||||
{22, &ISslConnection::SetOption, "SetOption"},
|
||||
{23, &ISslConnection::GetOption, "GetOption"},
|
||||
{10, D<&ISslConnection::Read>, "Read"},
|
||||
{11, D<&ISslConnection::Write>, "Write"},
|
||||
{12, D<&ISslConnection::Pending>, "Pending"},
|
||||
{13, D<&ISslConnection::Peek>, "Peek"},
|
||||
{14, D<&ISslConnection::Poll>, "Poll"},
|
||||
{15, D<&ISslConnection::GetVerifyCertError>, "GetVerifyCertError"},
|
||||
{16, D<&ISslConnection::GetNeededServerCertBufferSize>, "GetNeededServerCertBufferSize"},
|
||||
{17, D<&ISslConnection::SetSessionCacheMode>, "SetSessionCacheMode"},
|
||||
{18, D<&ISslConnection::GetSessionCacheMode>, "GetSessionCacheMode"},
|
||||
{19, D<&ISslConnection::FlushSessionCache>, "FlushSessionCache"},
|
||||
{20, D<&ISslConnection::SetRenegotiationMode>, "SetRenegotiationMode"},
|
||||
{21, D<&ISslConnection::GetRenegotiationMode>, "GetRenegotiationMode"},
|
||||
{22, D<&ISslConnection::SetOption>, "SetOption"},
|
||||
{23, D<&ISslConnection::GetOption>, "GetOption"},
|
||||
{24, nullptr, "GetVerifyCertErrors"},
|
||||
{25, nullptr, "GetCipherInfo"},
|
||||
{26, &ISslConnection::SetNextAlpnProto, "SetNextAlpnProto"},
|
||||
{27, &ISslConnection::GetNextAlpnProto, "GetNextAlpnProto"},
|
||||
{26, D<&ISslConnection::SetNextAlpnProto>, "SetNextAlpnProto"},
|
||||
{27, D<&ISslConnection::GetNextAlpnProto>, "GetNextAlpnProto"},
|
||||
{28, nullptr, "SetDtlsSocketDescriptor"},
|
||||
{29, nullptr, "GetDtlsHandshakeTimeout"},
|
||||
{30, nullptr, "SetPrivateOption"},
|
||||
@@ -141,80 +147,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
SslVersion ssl_version;
|
||||
std::shared_ptr<SslContextSharedData> shared_data;
|
||||
std::unique_ptr<SSLConnectionBackend> backend;
|
||||
std::optional<int> fd_to_close;
|
||||
bool do_not_close_socket = false;
|
||||
bool get_server_cert_chain = false;
|
||||
bool skip_default_verify = false;
|
||||
bool enable_alpn = false;
|
||||
std::shared_ptr<Network::SocketBase> socket;
|
||||
std::vector<u8> next_alpn_proto;
|
||||
bool did_handshake = false;
|
||||
u32 verify_option = 0;
|
||||
|
||||
Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
|
||||
LOG_DEBUG(Service_SSL, "called, fd={}", fd);
|
||||
ASSERT(!did_handshake);
|
||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
|
||||
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
|
||||
|
||||
auto const res_v = bsd->DuplicateSocketImpl(fd);
|
||||
if (auto *res = std::get_if<s32>(&res_v)) {
|
||||
const s32 duplicated_fd = *res;
|
||||
if (do_not_close_socket) {
|
||||
*out_fd = duplicated_fd;
|
||||
} else {
|
||||
*out_fd = -1;
|
||||
fd_to_close = duplicated_fd;
|
||||
}
|
||||
std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(duplicated_fd);
|
||||
if (!sock.has_value()) {
|
||||
LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", duplicated_fd);
|
||||
return ResultInvalidSocket;
|
||||
}
|
||||
socket = std::move(*sock);
|
||||
backend->SetSocket(socket);
|
||||
return ResultSuccess;
|
||||
}
|
||||
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd);
|
||||
return ResultInvalidSocket;
|
||||
}
|
||||
|
||||
Result SetHostNameImpl(const std::string& hostname) {
|
||||
LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
|
||||
ASSERT(!did_handshake);
|
||||
return backend->SetHostName(hostname);
|
||||
}
|
||||
|
||||
Result SetVerifyOptionImpl(u32 option) {
|
||||
ASSERT(!did_handshake);
|
||||
LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option);
|
||||
verify_option = 0;
|
||||
backend->SetVerifyOption(0);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SetIoModeImpl(u32 input_mode) {
|
||||
auto mode = static_cast<IoMode>(input_mode);
|
||||
ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking);
|
||||
ASSERT_OR_EXECUTE(socket, { return ResultNoSocket; });
|
||||
|
||||
const bool non_block = mode == IoMode::NonBlocking;
|
||||
const Network::Errno error = socket->SetNonBlock(non_block);
|
||||
if (error != Network::Errno::SUCCESS) {
|
||||
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SetSessionCacheModeImpl(u32 mode) {
|
||||
ASSERT(!did_handshake);
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called. value={}", mode);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result DoHandshakeImpl() {
|
||||
ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; });
|
||||
Result res = backend->DoHandshake();
|
||||
@@ -234,19 +166,17 @@ private:
|
||||
};
|
||||
if (!get_server_cert_chain) {
|
||||
// Just return the first one, unencoded.
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
!certs.empty(), { return {}; }, "Should be at least one server cert");
|
||||
ASSERT_OR_EXECUTE_MSG(!certs.empty(), { return {}; }, "Should be at least one server cert");
|
||||
return certs[0];
|
||||
}
|
||||
std::vector<u8> ret;
|
||||
Header header{0x4E4D684374726543, static_cast<u32>(certs.size()), 0};
|
||||
Header header{0x4E4D684374726543, u32(certs.size()), 0};
|
||||
ret.insert(ret.end(), reinterpret_cast<u8*>(&header), reinterpret_cast<u8*>(&header + 1));
|
||||
size_t data_offset = sizeof(Header) + certs.size() * sizeof(EntryHeader);
|
||||
for (auto& cert : certs) {
|
||||
EntryHeader entry_header{static_cast<u32>(cert.size()), static_cast<u32>(data_offset)};
|
||||
EntryHeader entry_header{u32(cert.size()), u32(data_offset)};
|
||||
data_offset += cert.size();
|
||||
ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header),
|
||||
reinterpret_cast<u8*>(&entry_header + 1));
|
||||
ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header), reinterpret_cast<u8*>(&entry_header + 1));
|
||||
}
|
||||
for (auto& cert : certs) {
|
||||
ret.insert(ret.end(), cert.begin(), cert.end());
|
||||
@@ -254,65 +184,77 @@ private:
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result ReadImpl(std::vector<u8>* out_data) {
|
||||
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
|
||||
size_t actual_size{};
|
||||
Result res = backend->Read(&actual_size, *out_data);
|
||||
if (res != ResultSuccess) {
|
||||
return res;
|
||||
Result SetSocketDescriptor(s32 in_fd, Out<s32> out_fd) {
|
||||
LOG_DEBUG(Service_SSL, "called, fd={}", in_fd);
|
||||
ASSERT(!did_handshake);
|
||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
|
||||
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
|
||||
|
||||
auto const res_v = bsd->DuplicateSocketImpl(in_fd);
|
||||
if (auto *res = std::get_if<s32>(&res_v)) {
|
||||
const s32 dup_fd = *res;
|
||||
*out_fd = do_not_close_socket ? dup_fd : -1;
|
||||
if (!do_not_close_socket)
|
||||
fd_to_close = dup_fd;
|
||||
auto const sock = bsd->GetSocket(dup_fd);
|
||||
if (!sock.has_value()) {
|
||||
LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", dup_fd);
|
||||
return ResultInvalidSocket;
|
||||
}
|
||||
socket = std::move(*sock);
|
||||
backend->SetSocket(std::move(socket));
|
||||
return ResultSuccess;
|
||||
}
|
||||
out_data->resize(actual_size);
|
||||
return res;
|
||||
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", in_fd);
|
||||
return ResultInvalidSocket;
|
||||
}
|
||||
|
||||
Result WriteImpl(size_t* out_size, std::span<const u8> data) {
|
||||
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
|
||||
return backend->Write(out_size, data);
|
||||
Result SetHostName(InBuffer<BufferAttr_HipcMapAlias> buf) {
|
||||
auto const hostname = Common::StringFromBuffer(buf);
|
||||
LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
|
||||
ASSERT(!did_handshake);
|
||||
return backend->SetHostName(hostname.c_str());
|
||||
}
|
||||
|
||||
Result PendingImpl(s32* out_pending) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called.");
|
||||
*out_pending = 0;
|
||||
return ResultSuccess;
|
||||
Result SetVerifyOption(u32 option) {
|
||||
LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option);
|
||||
ASSERT(!did_handshake);
|
||||
verify_option = 0;
|
||||
backend->SetVerifyOption(0);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetSocketDescriptor(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 in_fd = rp.Pop<s32>();
|
||||
s32 out_fd{-1};
|
||||
const Result res = SetSocketDescriptorImpl(&out_fd, in_fd);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push<s32>(out_fd);
|
||||
Result SetIoMode(u32 input_mode) {
|
||||
auto mode = IoMode(input_mode);
|
||||
ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking);
|
||||
R_UNLESS(socket, ResultNoSocket);
|
||||
const bool non_block = mode == IoMode::NonBlocking;
|
||||
const Network::Errno error = socket->SetNonBlock(non_block);
|
||||
if (error != Network::Errno::SUCCESS) {
|
||||
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetHostName(HLERequestContext& ctx) {
|
||||
const std::string hostname = Common::StringFromBuffer(ctx.ReadBuffer());
|
||||
const Result res = SetHostNameImpl(hostname);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
Result GetSocketDescriptor(Out<u32> out_fd) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
*out_fd = socket->GetFD();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetVerifyOption(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u32 option = rp.Pop<u32>();
|
||||
const Result res = SetVerifyOptionImpl(option);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
Result GetHostName(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
ASSERT(!did_handshake);
|
||||
return backend->GetHostName(std::span<u8>{data.begin(), data.end()}, out_size);
|
||||
}
|
||||
|
||||
void SetIoMode(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u32 mode = rp.Pop<u32>();
|
||||
const Result res = SetIoModeImpl(mode);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
Result GetIoMode(Out<u32> out_mode) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void DoHandshake(HLERequestContext& ctx) {
|
||||
const Result res = DoHandshakeImpl();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
Result DoHandshake() {
|
||||
return DoHandshakeImpl();
|
||||
}
|
||||
|
||||
void DoHandshakeGetServerCert(HLERequestContext& ctx) {
|
||||
@@ -351,131 +293,158 @@ private:
|
||||
rb.PushRaw(out);
|
||||
}
|
||||
|
||||
void Read(HLERequestContext& ctx) {
|
||||
std::vector<u8> output_bytes(ctx.GetWriteBufferSize());
|
||||
const Result res = ReadImpl(&output_bytes);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
if (res == ResultSuccess) {
|
||||
rb.Push(static_cast<u32>(output_bytes.size()));
|
||||
ctx.WriteBuffer(output_bytes);
|
||||
} else {
|
||||
rb.Push(static_cast<u32>(0));
|
||||
}
|
||||
Result Read(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||
R_UNLESS(did_handshake, ResultInternalError);
|
||||
size_t tmp{};
|
||||
auto const res = backend->Read(&tmp, data);
|
||||
*out_size = u32(tmp);
|
||||
return res;
|
||||
}
|
||||
|
||||
void Write(HLERequestContext& ctx) {
|
||||
size_t write_size{0};
|
||||
const Result res = WriteImpl(&write_size, ctx.ReadBuffer());
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push(static_cast<u32>(write_size));
|
||||
Result Write(InBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||
R_UNLESS(did_handshake, ResultInternalError);
|
||||
size_t tmp{};
|
||||
auto const res = backend->Write(&tmp, data);
|
||||
*out_size = u32(tmp);
|
||||
return res;
|
||||
}
|
||||
|
||||
void Pending(HLERequestContext& ctx) {
|
||||
s32 pending_size{0};
|
||||
const Result res = PendingImpl(&pending_size);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push<s32>(pending_size);
|
||||
Result Pending(Out<s32> out_pending_size) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
*out_pending_size = s32(backend->Pending());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetSessionCacheMode(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u32 mode = rp.Pop<u32>();
|
||||
const Result res = SetSessionCacheModeImpl(mode);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
Result Peek(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
size_t tmp{};
|
||||
auto const res = backend->Peek(&tmp, data);
|
||||
*out_size = u32(tmp);
|
||||
return res;
|
||||
}
|
||||
|
||||
void SetOption(HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
OptionType option;
|
||||
s32 value;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x8, "Parameters is an invalid size");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters = rp.PopRaw<Parameters>();
|
||||
|
||||
switch (parameters.option) {
|
||||
case OptionType::DoNotCloseSocket:
|
||||
do_not_close_socket = static_cast<bool>(parameters.value);
|
||||
break;
|
||||
case OptionType::GetServerCertChain:
|
||||
get_server_cert_chain = static_cast<bool>(parameters.value);
|
||||
break;
|
||||
case OptionType::SkipDefaultVerify:
|
||||
skip_default_verify = static_cast<bool>(parameters.value);
|
||||
break;
|
||||
case OptionType::EnableAlpn:
|
||||
enable_alpn = static_cast<bool>(parameters.value);
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", parameters.option,
|
||||
parameters.value);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
Result Poll(u32 in_pollevent, u32 timer, Out<u32> out_pollevent) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void GetOption(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto option = rp.PopRaw<OptionType>();
|
||||
Result GetVerifyCertError() {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u8 value = 0;
|
||||
Result GetNeededServerCertBufferSize(Out<u32> out_needed_buffer_size) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSessionCacheMode(u32 mode) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called. value={}", mode);
|
||||
R_UNLESS(!did_handshake, ResultInternalError);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetSessionCacheMode(Out<u32> mode) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_UNLESS(!did_handshake, ResultInternalError);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FlushSessionCache() {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_UNLESS(!did_handshake, ResultInternalError);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetRenegotiationMode(RenegotiationMode mode) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
backend->SetRenegotiationMode(u32(mode));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetRenegotiationMode(Out<RenegotiationMode> mode) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
u32 tmp{};
|
||||
auto const res = backend->GetRenegotiationMode(&tmp);
|
||||
*mode = RenegotiationMode(tmp);
|
||||
return res;
|
||||
}
|
||||
|
||||
Result SetOption(OptionType option, s32 value) {
|
||||
switch (option) {
|
||||
case OptionType::DoNotCloseSocket:
|
||||
value = static_cast<u8>(do_not_close_socket);
|
||||
do_not_close_socket = bool(value);
|
||||
break;
|
||||
case OptionType::GetServerCertChain:
|
||||
value = static_cast<u8>(get_server_cert_chain);
|
||||
get_server_cert_chain = bool(value);
|
||||
break;
|
||||
case OptionType::SkipDefaultVerify:
|
||||
value = static_cast<u8>(skip_default_verify);
|
||||
skip_default_verify = bool(value);
|
||||
break;
|
||||
case OptionType::EnableAlpn:
|
||||
value = static_cast<u8>(enable_alpn);
|
||||
enable_alpn = bool(value);
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(Service_SSL, "Unknown option={}", option);
|
||||
value = 0;
|
||||
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", option, value);
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetOption(OptionType option, Out<u8> value) {
|
||||
switch (option) {
|
||||
case OptionType::DoNotCloseSocket:
|
||||
*value = u8(do_not_close_socket);
|
||||
break;
|
||||
case OptionType::GetServerCertChain:
|
||||
*value = u8(get_server_cert_chain);
|
||||
break;
|
||||
case OptionType::SkipDefaultVerify:
|
||||
*value = u8(skip_default_verify);
|
||||
break;
|
||||
case OptionType::EnableAlpn:
|
||||
*value = u8(enable_alpn);
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(Service_SSL, "Unknown option={}", u32(option));
|
||||
*value = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", option, value);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u8>(value);
|
||||
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", u32(option), *value);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetNextAlpnProto(HLERequestContext& ctx) {
|
||||
const auto data = ctx.ReadBuffer(0);
|
||||
next_alpn_proto.assign(data.begin(), data.end());
|
||||
|
||||
Result SetNextAlpnProto(InBuffer<BufferAttr_HipcMapAlias> data) {
|
||||
auto const to_write = u32((std::min)(next_alpn_proto.size(), data.size()));
|
||||
next_alpn_proto.assign(data.begin(), data.begin() + to_write);
|
||||
LOG_DEBUG(Service_SSL, "SetNextAlpnProto called, size={}", next_alpn_proto.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void GetNextAlpnProto(HLERequestContext& ctx) {
|
||||
const size_t writable = ctx.GetWriteBufferSize();
|
||||
const size_t to_write = (std::min)(next_alpn_proto.size(), writable);
|
||||
|
||||
if (to_write != 0) {
|
||||
ctx.WriteBuffer(std::span<const u8>(next_alpn_proto.data(), to_write));
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", to_write);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(to_write));
|
||||
Result GetNextAlpnProto(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> to_write) {
|
||||
*to_write = u32((std::min)(next_alpn_proto.size(), data.size()));
|
||||
next_alpn_proto.assign(data.begin(), data.begin() + *to_write);
|
||||
LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", *to_write);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetVerifyCertErrors(OutBuffer<BufferAttr_HipcMapAlias> unk0, Out<u32> unk1, Out<u32> unk2) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
SslVersion ssl_version;
|
||||
std::shared_ptr<SslContextSharedData> shared_data;
|
||||
std::unique_ptr<SSLConnectionBackend> backend;
|
||||
std::optional<int> fd_to_close;
|
||||
std::shared_ptr<Network::SocketBase> socket;
|
||||
std::vector<u8> next_alpn_proto;
|
||||
u32 verify_option = 0;
|
||||
|
||||
bool do_not_close_socket = false;
|
||||
bool get_server_cert_chain = false;
|
||||
bool skip_default_verify = false;
|
||||
bool enable_alpn = false;
|
||||
bool did_handshake = false;
|
||||
};
|
||||
|
||||
class ISslContext final : public ServiceFramework<ISslContext> {
|
||||
@@ -492,7 +461,7 @@ public:
|
||||
{5, &ISslContext::ImportClientPki, "ImportClientPki"},
|
||||
{6, nullptr, "RemoveServerPki"},
|
||||
{7, nullptr, "RemoveClientPki"},
|
||||
{8, nullptr, "RegisterInternalPki"},
|
||||
{8, D<&ISslContext::RegisterInternalPki>, "RegisterInternalPki"},
|
||||
{9, nullptr, "AddPolicyOid"},
|
||||
{10, nullptr, "ImportCrl"},
|
||||
{11, nullptr, "RemoveCrl"},
|
||||
@@ -587,6 +556,11 @@ private:
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(client_id);
|
||||
}
|
||||
|
||||
Result RegisterInternalPki() {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
class ISslService final : public ServiceFramework<ISslService> {
|
||||
|
||||
@@ -36,12 +36,17 @@ class SSLConnectionBackend {
|
||||
public:
|
||||
virtual ~SSLConnectionBackend() {}
|
||||
virtual void SetSocket(std::shared_ptr<Network::SocketBase> socket) = 0;
|
||||
virtual Result SetHostName(const std::string& hostname) = 0;
|
||||
virtual void SetVerifyOption(u32 option) = 0;
|
||||
virtual Result DoHandshake() = 0;
|
||||
virtual Result Read(size_t* out_size, std::span<u8> data) = 0;
|
||||
virtual Result Peek(size_t* out_size, std::span<u8> data) = 0;
|
||||
virtual Result Write(size_t* out_size, std::span<const u8> data) = 0;
|
||||
virtual Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) = 0;
|
||||
virtual Result SetHostName(const char* hostname) = 0;
|
||||
virtual Result GetHostName(std::span<u8> hostname, u32* out_size) = 0;
|
||||
virtual int Pending() = 0;
|
||||
virtual Result SetRenegotiationMode(u32 mode) = 0;
|
||||
virtual Result GetRenegotiationMode(u32* mode) = 0;
|
||||
};
|
||||
|
||||
Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend);
|
||||
|
||||
@@ -157,18 +157,30 @@ public:
|
||||
socket = std::move(socket_in);
|
||||
}
|
||||
|
||||
Result SetHostName(const std::string& hostname) override {
|
||||
Result SetHostName(const char* hostname) override {
|
||||
if (!skip_cert_verification) {
|
||||
if (!SSL_set1_host(ssl, hostname.c_str())) {
|
||||
if (!SSL_set1_host(ssl, hostname)) {
|
||||
LOG_ERROR(Service_SSL, "SSL_set1_host({}) failed", hostname);
|
||||
return CheckOpenSSLErrors();
|
||||
}
|
||||
}
|
||||
if (!SSL_set_tlsext_host_name(ssl, hostname.c_str())) { // hostname for SNI
|
||||
if (!SSL_set_tlsext_host_name(ssl, hostname)) { // hostname for SNI
|
||||
LOG_ERROR(Service_SSL, "SSL_set_tlsext_host_name({}) failed", hostname);
|
||||
return CheckOpenSSLErrors();
|
||||
}
|
||||
return ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetHostName(std::span<u8> data, u32* out_size) override {
|
||||
auto const peer_name = SSL_get0_peername(ssl);
|
||||
if (peer_name == nullptr) {
|
||||
LOG_ERROR(Service_SSL, "SSL_get0_peername()");
|
||||
return CheckOpenSSLErrors();
|
||||
}
|
||||
auto const s = std::string{peer_name};
|
||||
*out_size = u32(s.size());
|
||||
std::memcpy(data.data(), s.data(), (std::min)(s.size(), data.size()));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetVerifyOption(u32 option) override {
|
||||
@@ -213,6 +225,13 @@ public:
|
||||
return HandleReturn("SSL_read_ex", out_size, ret);
|
||||
}
|
||||
|
||||
Result Peek(size_t* out_size, std::span<u8> data) override {
|
||||
auto const n = (std::min)(data.size(), *out_size);
|
||||
const int ret = SSL_peek(ssl, data.data(), int(n));
|
||||
*out_size = n;
|
||||
return HandleReturn("SSL_write_ex", out_size, ret);
|
||||
}
|
||||
|
||||
Result Write(size_t* out_size, std::span<const u8> data) override {
|
||||
const int ret = SSL_write_ex(ssl, data.data(), data.size(), out_size);
|
||||
return HandleReturn("SSL_write_ex", out_size, ret);
|
||||
@@ -263,7 +282,25 @@ public:
|
||||
out_certs->emplace_back(buf, buf + len);
|
||||
OPENSSL_free(buf);
|
||||
}
|
||||
return ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
int Pending() override {
|
||||
return SSL_pending(ssl);
|
||||
}
|
||||
|
||||
Result SetRenegotiationMode(u32 mode) override {
|
||||
if (mode == 0) {
|
||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_RENEGOTIATION);
|
||||
} else {
|
||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALLOW_CLIENT_RENEGOTIATION);
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetRenegotiationMode(u32* mode) override {
|
||||
*mode = SSL_get_secure_renegotiation_support(ssl) ? 1 : 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
~SSLConnectionBackendOpenSSL() {
|
||||
|
||||
@@ -90,7 +90,7 @@ template<>
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp.cvt64()]);
|
||||
} else {
|
||||
code.mov(page, qword[r14 + tmp.cvt64() * int(ctx.conf.page_table_log2_stride)]);
|
||||
code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]);
|
||||
}
|
||||
|
||||
// check for marked bit, use as unmapped if marked
|
||||
@@ -161,8 +161,12 @@ template<>
|
||||
code.jnz(abort, code.T_NEAR);
|
||||
}
|
||||
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp]);
|
||||
if (ctx.conf.page_table_log2_stride > 3) {
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp.cvt64()]);
|
||||
} else {
|
||||
code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]);
|
||||
}
|
||||
|
||||
// check for marked bit, use as unmapped if marked
|
||||
if (ctx.conf.page_table_marked_bit) {
|
||||
@@ -178,6 +182,7 @@ template<>
|
||||
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
||||
code.and_(page, tmp);
|
||||
}
|
||||
// check for sign bit, apply sign extension as needed
|
||||
if (ctx.conf.page_table_sign_extension) {
|
||||
code.shl(page, *ctx.conf.page_table_sign_extension);
|
||||
code.sar(page, *ctx.conf.page_table_sign_extension);
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace ConfigurationShared {
|
||||
|
||||
std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
||||
std::unique_ptr<TranslationMap> translations = std::make_unique<TranslationMap>();
|
||||
const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); };
|
||||
const auto& tr = [](const char* text) -> QString { return QCoreApplication::translate("ConfigurationShared", text); };
|
||||
|
||||
#define INSERT(SETTINGS, ID, NAME, TOOLTIP) \
|
||||
translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{(NAME), (TOOLTIP)}})
|
||||
@@ -151,6 +151,9 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
||||
tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA "
|
||||
"can produce a more stable picture in lower resolutions."));
|
||||
INSERT(Settings, post_shader_chain, QString(), QString());
|
||||
INSERT(Settings, post_shader_preset, QString(), QString());
|
||||
INSERT(Settings, post_shader_enabled, tr("Enable post-processing effects"),
|
||||
tr("Applies post-processing effects to the final image."));
|
||||
INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"),
|
||||
tr("The method used to render the window in fullscreen.\nBorderless offers the best "
|
||||
"compatibility with the on-screen keyboard that some games request for "
|
||||
@@ -368,8 +371,8 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
||||
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
|
||||
std::unique_ptr<ComboboxTranslationMap> translations =
|
||||
std::make_unique<ComboboxTranslationMap>();
|
||||
const auto& tr = [&](const char* text, const char* context = "") {
|
||||
return parent->tr(text, context);
|
||||
const auto& tr = [](const char* text, const char* context = "") {
|
||||
return QCoreApplication::translate("ConfigurationShared", text, context);
|
||||
};
|
||||
|
||||
#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)}
|
||||
|
||||
@@ -2850,8 +2850,6 @@ Sampler::VariantKey Sampler::MakeKey(const ImageView& image_view, bool is_depth)
|
||||
VariantKey key{};
|
||||
key.reduce_anisotropy = has_added_anisotropy && !image_view.SupportsAnisotropy();
|
||||
key.force_nearest = has_linear_filtering && IsPixelFormatInteger(image_view.format);
|
||||
key.drop_depth_comparison =
|
||||
is_depth && has_depth_comparison && !image_view.SupportsDepthComparison();
|
||||
key.drop_reduction = has_minmax_reduction && !image_view.SupportsMinmaxFilter();
|
||||
key.drop_custom_border = has_custom_border_colors && image_view.RequiresBorderColorFormat();
|
||||
key.srgb_border = has_srgb_border_color && IsPixelFormatSRGB(image_view.format);
|
||||
@@ -2929,9 +2927,6 @@ VkSampler Sampler::Emplace(VariantKey key) {
|
||||
create_info.anisotropyEnable = static_cast<VkBool32>(default_anisotropy > 1.0f);
|
||||
create_info.maxAnisotropy = default_anisotropy;
|
||||
}
|
||||
if (key.drop_depth_comparison) {
|
||||
create_info.compareEnable = VK_FALSE;
|
||||
}
|
||||
if (!custom_border) {
|
||||
create_info.borderColor = ConvertBorderColor(color);
|
||||
}
|
||||
|
||||
@@ -536,7 +536,6 @@ private:
|
||||
struct VariantKey {
|
||||
bool reduce_anisotropy;
|
||||
bool force_nearest;
|
||||
bool drop_depth_comparison;
|
||||
bool drop_reduction;
|
||||
bool drop_custom_border;
|
||||
bool srgb_border;
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
#include "yuzu/configuration/configure_graphics.h"
|
||||
#include "yuzu/configuration/shared_widget.h"
|
||||
|
||||
#ifdef HAS_RESHADE
|
||||
#include "yuzu/configuration/configure_post_processing.h"
|
||||
#endif
|
||||
|
||||
ConfigureGraphics::ConfigureGraphics(
|
||||
const Core::System& system_, std::vector<VkDeviceInfo::Record>& records_,
|
||||
const std::function<void()>& expose_compute_option_,
|
||||
@@ -216,6 +220,12 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
|
||||
std::vector<QWidget*> hold_api;
|
||||
|
||||
for (const auto setting : Settings::values.linkage.by_category[Settings::Category::Renderer]) {
|
||||
#ifndef HAS_RESHADE
|
||||
if (setting->Id() == Settings::values.post_shader_enabled.Id()) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
ConfigurationShared::Widget* widget = [&]() {
|
||||
if (setting->Id() == Settings::values.fsr_sharpening_slider.Id()) {
|
||||
// FSR needs a reversed slider and a 0.5 multiplier
|
||||
@@ -295,6 +305,23 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
|
||||
// Keep track of the resolution combobox to update other UI tabs that need it
|
||||
resolution_combobox = widget->combobox;
|
||||
hold_graphics.emplace(setting->Id(), widget);
|
||||
#ifdef HAS_RESHADE
|
||||
} else if (setting->Id() == Settings::values.post_shader_enabled.Id()) {
|
||||
QPushButton* post_shader_button = new QPushButton(tr("Configure Effects..."), widget);
|
||||
post_shader_button->setVisible(widget->checkbox->isChecked());
|
||||
|
||||
connect(post_shader_button, &QAbstractButton::clicked, this, [this]() {
|
||||
ConfigurePostProcessing dialog(this);
|
||||
dialog.exec();
|
||||
});
|
||||
connect(widget->checkbox, &QCheckBox::toggled, post_shader_button,
|
||||
&QWidget::setVisible);
|
||||
|
||||
QBoxLayout* row = qobject_cast<QBoxLayout*>(widget->layout());
|
||||
row->insertWidget(1, post_shader_button);
|
||||
|
||||
hold_graphics.emplace(setting->Id(), widget);
|
||||
#endif
|
||||
} else {
|
||||
hold_graphics.emplace(setting->Id(), widget);
|
||||
}
|
||||
|
||||
@@ -70,10 +70,8 @@ ConfigurePostProcessing::ConfigurePostProcessing(QWidget* parent) : QDialog(pare
|
||||
|
||||
auto* root = new QVBoxLayout(this);
|
||||
|
||||
auto* description = new QLabel(
|
||||
tr("ReShade FX effects are loaded from the post_shaders folder in the Eden data "
|
||||
"directory. Changes apply immediately while a game is running."),
|
||||
this);
|
||||
auto* description =
|
||||
new QLabel(tr("Changes apply immediately while a game is running."), this);
|
||||
description->setWordWrap(true);
|
||||
root->addWidget(description);
|
||||
|
||||
@@ -128,17 +126,15 @@ ConfigurePostProcessing::ConfigurePostProcessing(QWidget* parent) : QDialog(pare
|
||||
});
|
||||
preset_row->addWidget(save_button);
|
||||
|
||||
auto* delete_button = new QPushButton(tr("Delete"), this);
|
||||
connect(delete_button, &QPushButton::clicked, this, [this]() {
|
||||
const QString name = preset_combo->currentData().toString();
|
||||
if (name.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
VideoCore::DeleteFxPreset(name.toStdString());
|
||||
PopulatePresetCombo();
|
||||
RefreshPresetStatus();
|
||||
auto* clear_button = new QPushButton(tr("Clear"), this);
|
||||
clear_button->setToolTip(tr("Remove every effect in use and deselect the preset."));
|
||||
connect(clear_button, &QPushButton::clicked, this, [this]() {
|
||||
VideoCore::FxChain::Instance().Clear();
|
||||
VideoCore::SetActiveFxPreset(std::string());
|
||||
preset_combo->setCurrentIndex(0);
|
||||
ApplyStructuralChange();
|
||||
});
|
||||
preset_row->addWidget(delete_button);
|
||||
preset_row->addWidget(clear_button);
|
||||
|
||||
root->addLayout(preset_row);
|
||||
|
||||
@@ -203,6 +199,7 @@ void ConfigurePostProcessing::ApplyStructuralChange() {
|
||||
void ConfigurePostProcessing::PopulatePresetCombo() {
|
||||
const QString previous = preset_combo->currentData().toString();
|
||||
preset_combo->clear();
|
||||
preset_combo->addItem(tr("None"), QString());
|
||||
|
||||
for (const auto& preset : VideoCore::GetFxPresetCatalog()) {
|
||||
const QString name = QString::fromStdString(preset.name);
|
||||
@@ -212,7 +209,7 @@ void ConfigurePostProcessing::PopulatePresetCombo() {
|
||||
}
|
||||
|
||||
const int restored = preset_combo->findData(previous);
|
||||
if (restored >= 0) {
|
||||
if (!previous.isEmpty() && restored >= 0) {
|
||||
preset_combo->setCurrentIndex(restored);
|
||||
return;
|
||||
}
|
||||
@@ -220,12 +217,21 @@ void ConfigurePostProcessing::PopulatePresetCombo() {
|
||||
const int active = preset_combo->findData(QString::fromStdString(VideoCore::GetActiveFxPreset()));
|
||||
if (active >= 0) {
|
||||
preset_combo->setCurrentIndex(active);
|
||||
return;
|
||||
}
|
||||
|
||||
preset_combo->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
void ConfigurePostProcessing::RefreshPresetStatus() {
|
||||
const std::string active = VideoCore::GetActiveFxPreset();
|
||||
if (active.empty()) {
|
||||
if (VideoCore::FxChain::Instance().Size() == 0) {
|
||||
preset_status->setText(
|
||||
tr("No effects in use. Pick a preset and press Apply, or add effects one "
|
||||
"by one."));
|
||||
return;
|
||||
}
|
||||
preset_status->setText(tr("Custom chain. Pick a preset above and press Apply to replace it."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "render/performance_overlay.h"
|
||||
#ifdef HAS_RESHADE
|
||||
#include "configuration/configure_post_processing.h"
|
||||
#include "video_core/post_processing/fx_preset.h"
|
||||
#endif
|
||||
#include "updater/update_dialog.h"
|
||||
|
||||
@@ -1090,6 +1091,41 @@ void MainWindow::InitializeWidgets() {
|
||||
|
||||
statusBar()->insertPermanentWidget(0, volume_button);
|
||||
|
||||
#ifdef HAS_RESHADE
|
||||
post_shader_status_button = new QPushButton();
|
||||
post_shader_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
|
||||
post_shader_status_button->setFocusPolicy(Qt::NoFocus);
|
||||
post_shader_status_button->setCheckable(true);
|
||||
connect(post_shader_status_button, &QPushButton::clicked, this, [this] {
|
||||
const bool enabled = Settings::values.post_shader_enabled.GetValue();
|
||||
Settings::values.post_shader_enabled.SetValue(!enabled);
|
||||
UpdatePostShaderText();
|
||||
});
|
||||
UpdatePostShaderText();
|
||||
post_shader_status_button->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(post_shader_status_button, &QPushButton::customContextMenuRequested,
|
||||
[this](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
|
||||
for (auto const& preset : VideoCore::GetFxPresetCatalog()) {
|
||||
context_menu.addAction(QString::fromStdString(preset.name),
|
||||
[this, name = preset.name] {
|
||||
VideoCore::ApplyFxPreset(name);
|
||||
Settings::values.post_shader_enabled.SetValue(true);
|
||||
UpdatePostShaderText();
|
||||
});
|
||||
}
|
||||
|
||||
context_menu.addSeparator();
|
||||
context_menu.addAction(tr("Configure Effects..."), this,
|
||||
&MainWindow::OnPostProcessingShaders);
|
||||
|
||||
context_menu.exec(post_shader_status_button->mapToGlobal(menu_location));
|
||||
post_shader_status_button->repaint();
|
||||
});
|
||||
statusBar()->insertPermanentWidget(0, post_shader_status_button);
|
||||
#endif
|
||||
|
||||
// setup AA button
|
||||
aa_status_button = new QPushButton();
|
||||
aa_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
|
||||
@@ -3908,6 +3944,7 @@ void MainWindow::OnPostProcessingShaders() {
|
||||
connect(post_processing_dialog, &QDialog::finished, post_processing_dialog, [this]() {
|
||||
post_processing_dialog->deleteLater();
|
||||
post_processing_dialog = nullptr;
|
||||
UpdatePostShaderText();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4262,6 +4299,26 @@ void MainWindow::UpdateAAText() {
|
||||
: aa_text.toUpper());
|
||||
}
|
||||
|
||||
#ifdef HAS_RESHADE
|
||||
void MainWindow::UpdatePostShaderText() {
|
||||
const bool enabled = Settings::values.post_shader_enabled.GetValue();
|
||||
post_shader_status_button->setChecked(enabled);
|
||||
|
||||
if (!enabled) {
|
||||
post_shader_status_button->setText(tr("NO FX"));
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string preset = VideoCore::GetActiveFxPreset();
|
||||
if (preset.empty()) {
|
||||
post_shader_status_button->setText(tr("FX"));
|
||||
return;
|
||||
}
|
||||
|
||||
post_shader_status_button->setText(QString::fromStdString(preset).toUpper());
|
||||
}
|
||||
#endif
|
||||
|
||||
void MainWindow::UpdateVolumeUI() {
|
||||
const auto volume_value = static_cast<int>(Settings::values.volume.GetValue());
|
||||
volume_slider->setValue(volume_value);
|
||||
|
||||
@@ -448,6 +448,7 @@ private:
|
||||
void UpdateAPIText();
|
||||
void UpdateFilterText();
|
||||
void UpdateAAText();
|
||||
void UpdatePostShaderText();
|
||||
void UpdateVolumeUI();
|
||||
void UpdateStatusBar();
|
||||
void UpdateGPUAccuracyButton();
|
||||
@@ -524,6 +525,7 @@ private:
|
||||
QPushButton* dock_status_button = nullptr;
|
||||
QPushButton* filter_status_button = nullptr;
|
||||
QPushButton* aa_status_button = nullptr;
|
||||
QPushButton* post_shader_status_button = nullptr;
|
||||
VolumeButton* volume_button = nullptr;
|
||||
QWidget* volume_popup = nullptr;
|
||||
QSlider* volume_slider = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user