diff options
Diffstat (limited to '')
290 files changed, 14086 insertions, 3182 deletions
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp index fe3a898ad..20a756dce 100644 --- a/src/audio_core/audio_out.cpp +++ b/src/audio_core/audio_out.cpp @@ -7,7 +7,7 @@ #include "audio_core/sink_details.h" #include "common/assert.h" #include "common/logging/log.h" -#include "core/settings.h" +#include "common/settings.h" namespace AudioCore { diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index d2ce8c814..ae2201c36 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -11,8 +11,8 @@ #include "audio_core/info_updater.h" #include "audio_core/voice_context.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/memory.h" -#include "core/settings.h" namespace { [[nodiscard]] static constexpr s16 ClampToS16(s32 value) { diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index 043447eaa..93c35e785 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp @@ -11,7 +11,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/ring_buffer.h" -#include "core/settings.h" +#include "common/settings.h" #ifdef _WIN32 #include <objbase.h> diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index b0f6f0c34..ad6c587c2 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -11,8 +11,8 @@ #include "audio_core/stream.h" #include "common/assert.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core_timing.h" -#include "core/settings.h" namespace AudioCore { diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 788516ded..88644eeb6 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -97,6 +97,7 @@ add_custom_command(OUTPUT scm_rev.cpp add_library(common STATIC algorithm.h alignment.h + assert.cpp assert.h atomic_ops.h detached_tasks.cpp @@ -109,6 +110,7 @@ add_library(common STATIC cityhash.h common_funcs.h common_paths.h + common_sizes.h common_types.h concepts.h div_ceil.h @@ -150,6 +152,10 @@ add_library(common STATIC scm_rev.cpp scm_rev.h scope_exit.h + settings.cpp + settings.h + settings_input.cpp + settings_input.h spin_lock.cpp spin_lock.h stream.cpp diff --git a/src/common/assert.cpp b/src/common/assert.cpp new file mode 100644 index 000000000..72f1121aa --- /dev/null +++ b/src/common/assert.cpp @@ -0,0 +1,14 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_funcs.h" + +#include "common/settings.h" + +void assert_handle_failure() { + if (Settings::values.use_debug_asserts) { + Crash(); + } +} diff --git a/src/common/assert.h b/src/common/assert.h index 06d7b5612..b3ba35c0f 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -4,10 +4,13 @@ #pragma once -#include <cstdlib> -#include "common/common_funcs.h" #include "common/logging/log.h" +// Sometimes we want to try to continue even after hitting an assert. +// However touching this file yields a global recompilation as this header is included almost +// everywhere. So let's just move the handling of the failed assert to a single cpp file. +void assert_handle_failure(); + // For asserts we'd like to keep all the junk executed when an assert happens away from the // important code in the function. One way of doing this is to put all the relevant code inside a // lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to @@ -17,15 +20,14 @@ // enough for our purposes. template <typename Fn> #if defined(_MSC_VER) -[[msvc::noinline, noreturn]] +[[msvc::noinline]] #elif defined(__GNUC__) -[[gnu::cold, gnu::noinline, noreturn]] +[[gnu::cold, gnu::noinline]] #endif static void assert_noinline_call(const Fn& fn) { fn(); - Crash(); - exit(1); // Keeps GCC's mouth shut about this actually returning + assert_handle_failure(); } #define ASSERT(_a_) \ diff --git a/src/common/common_sizes.h b/src/common/common_sizes.h new file mode 100644 index 000000000..7e9fd968b --- /dev/null +++ b/src/common/common_sizes.h @@ -0,0 +1,43 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <limits> + +#include "common/common_types.h" + +namespace Common { + +enum : u64 { + Size_1_KB = 0x400ULL, + Size_64_KB = 64ULL * Size_1_KB, + Size_128_KB = 128ULL * Size_1_KB, + Size_1_MB = 0x100000ULL, + Size_2_MB = 2ULL * Size_1_MB, + Size_4_MB = 4ULL * Size_1_MB, + Size_5_MB = 5ULL * Size_1_MB, + Size_14_MB = 14ULL * Size_1_MB, + Size_32_MB = 32ULL * Size_1_MB, + Size_33_MB = 33ULL * Size_1_MB, + Size_128_MB = 128ULL * Size_1_MB, + Size_448_MB = 448ULL * Size_1_MB, + Size_507_MB = 507ULL * Size_1_MB, + Size_562_MB = 562ULL * Size_1_MB, + Size_1554_MB = 1554ULL * Size_1_MB, + Size_2048_MB = 2048ULL * Size_1_MB, + Size_2193_MB = 2193ULL * Size_1_MB, + Size_3285_MB = 3285ULL * Size_1_MB, + Size_4916_MB = 4916ULL * Size_1_MB, + Size_1_GB = 0x40000000ULL, + Size_2_GB = 2ULL * Size_1_GB, + Size_4_GB = 4ULL * Size_1_GB, + Size_6_GB = 6ULL * Size_1_GB, + Size_8_GB = 8ULL * Size_1_GB, + Size_64_GB = 64ULL * Size_1_GB, + Size_512_GB = 512ULL * Size_1_GB, + Size_Invalid = std::numeric_limits<u64>::max(), +}; + +} // namespace Common diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 2d4d2e9e7..bc82905c0 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -21,11 +21,11 @@ #include "common/logging/backend.h" #include "common/logging/log.h" #include "common/logging/text_formatter.h" +#include "common/settings.h" #include "common/string_util.h" #include "common/threadsafe_queue.h" -#include "core/settings.h" -namespace Log { +namespace Common::Log { /** * Static state as a singleton. @@ -37,8 +37,11 @@ public: return backend; } - Impl(Impl const&) = delete; - const Impl& operator=(Impl const&) = delete; + Impl(const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + + Impl(Impl&&) = delete; + Impl& operator=(Impl&&) = delete; void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, std::string message) { @@ -132,7 +135,7 @@ private: std::mutex writing_mutex; std::thread backend_thread; std::vector<std::unique_ptr<Backend>> backends; - Common::MPSCQueue<Log::Entry> message_queue; + MPSCQueue<Entry> message_queue; Filter filter; std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; }; @@ -146,16 +149,16 @@ void ColorConsoleBackend::Write(const Entry& entry) { } FileBackend::FileBackend(const std::string& filename) : bytes_written(0) { - if (Common::FS::Exists(filename + ".old.txt")) { - Common::FS::Delete(filename + ".old.txt"); + if (FS::Exists(filename + ".old.txt")) { + FS::Delete(filename + ".old.txt"); } - if (Common::FS::Exists(filename)) { - Common::FS::Rename(filename, filename + ".old.txt"); + if (FS::Exists(filename)) { + FS::Rename(filename, filename + ".old.txt"); } // _SH_DENYWR allows read only access to the file for other programs. // It is #defined to 0 on other platforms - file = Common::FS::IOFile(filename, "w", _SH_DENYWR); + file = FS::IOFile(filename, "w", _SH_DENYWR); } void FileBackend::Write(const Entry& entry) { @@ -182,7 +185,7 @@ void FileBackend::Write(const Entry& entry) { void DebuggerBackend::Write(const Entry& entry) { #ifdef _WIN32 - ::OutputDebugStringW(Common::UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); + ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); #endif } @@ -212,6 +215,7 @@ void DebuggerBackend::Write(const Entry& entry) { SUB(Service, ARP) \ SUB(Service, BCAT) \ SUB(Service, BPC) \ + SUB(Service, BGTC) \ SUB(Service, BTDRV) \ SUB(Service, BTM) \ SUB(Service, Capture) \ @@ -341,4 +345,4 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, instance.PushEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args)); } -} // namespace Log +} // namespace Common::Log diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index da1c2f185..84a544ea4 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h @@ -11,7 +11,7 @@ #include "common/logging/filter.h" #include "common/logging/log.h" -namespace Log { +namespace Common::Log { class Filter; @@ -135,4 +135,4 @@ const char* GetLevelName(Level log_level); * never get the message */ void SetGlobalFilter(const Filter& filter); -} // namespace Log
\ No newline at end of file +} // namespace Common::Log
\ No newline at end of file diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 2eccbcd8d..20a2dd106 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -7,7 +7,7 @@ #include "common/logging/filter.h" #include "common/string_util.h" -namespace Log { +namespace Common::Log { namespace { template <typename It> Level GetLevelByName(const It begin, const It end) { @@ -103,4 +103,4 @@ bool Filter::IsDebug() const { }); } -} // namespace Log +} // namespace Common::Log diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h index 773df6f2c..f5673a9f6 100644 --- a/src/common/logging/filter.h +++ b/src/common/logging/filter.h @@ -9,7 +9,7 @@ #include <string_view> #include "common/logging/log.h" -namespace Log { +namespace Common::Log { /** * Implements a log message filter which allows different log classes to have different minimum @@ -51,4 +51,4 @@ public: private: std::array<Level, static_cast<std::size_t>(Class::Count)> class_levels; }; -} // namespace Log +} // namespace Common::Log diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 835894918..1f0f8db52 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -7,7 +7,7 @@ #include <fmt/format.h> #include "common/common_types.h" -namespace Log { +namespace Common::Log { // trims up to and including the last of ../, ..\, src/, src\ in a string constexpr const char* TrimSourcePath(std::string_view source) { @@ -66,6 +66,7 @@ enum class Class : ClassType { Service_ARP, ///< The ARP service Service_Audio, ///< The Audio (Audio control) service Service_BCAT, ///< The BCAT service + Service_BGTC, ///< The BGTC (Background Task Controller) service Service_BPC, ///< The BPC service Service_BTDRV, ///< The Bluetooth driver service Service_BTM, ///< The BTM service @@ -147,28 +148,34 @@ void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsig fmt::make_format_args(args...)); } -} // namespace Log +} // namespace Common::Log #ifdef _DEBUG #define LOG_TRACE(log_class, ...) \ - ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, \ - ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) + Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Trace, \ + Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ + __VA_ARGS__) #else #define LOG_TRACE(log_class, fmt, ...) (void(0)) #endif #define LOG_DEBUG(log_class, ...) \ - ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, \ - ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) + Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Debug, \ + Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ + __VA_ARGS__) #define LOG_INFO(log_class, ...) \ - ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, \ - ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) + Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Info, \ + Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ + __VA_ARGS__) #define LOG_WARNING(log_class, ...) \ - ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, \ - ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) + Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Warning, \ + Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ + __VA_ARGS__) #define LOG_ERROR(log_class, ...) \ - ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, \ - ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) + Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Error, \ + Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ + __VA_ARGS__) #define LOG_CRITICAL(log_class, ...) \ - ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, \ - ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) + Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Critical, \ + Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ + __VA_ARGS__) diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index 6a0605c63..80ee2cca1 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp @@ -16,7 +16,7 @@ #include "common/logging/text_formatter.h" #include "common/string_util.h" -namespace Log { +namespace Common::Log { std::string FormatLogMessage(const Entry& entry) { unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000); @@ -108,4 +108,4 @@ void PrintColoredMessage(const Entry& entry) { #undef ESC #endif } -} // namespace Log +} // namespace Common::Log diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h index b6d9e57c8..171e74cfe 100644 --- a/src/common/logging/text_formatter.h +++ b/src/common/logging/text_formatter.h @@ -7,7 +7,7 @@ #include <cstddef> #include <string> -namespace Log { +namespace Common::Log { struct Entry; @@ -17,4 +17,4 @@ std::string FormatLogMessage(const Entry& entry); void PrintMessage(const Entry& entry); /// Prints the same message as `PrintMessage`, but colored according to the severity level. void PrintColoredMessage(const Entry& entry); -} // namespace Log +} // namespace Common::Log diff --git a/src/common/nvidia_flags.h b/src/common/nvidia_flags.h index 75a0233ac..8930efcec 100644 --- a/src/common/nvidia_flags.h +++ b/src/common/nvidia_flags.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + namespace Common { /// Configure platform specific flags for Nvidia's driver diff --git a/src/core/settings.cpp b/src/common/settings.cpp index 2ae5196e0..702b6598d 100644 --- a/src/core/settings.cpp +++ b/src/common/settings.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -7,10 +7,7 @@ #include "common/assert.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "core/core.h" -#include "core/hle/service/hid/hid.h" -#include "core/settings.h" -#include "video_core/renderer_base.h" +#include "common/settings.h" namespace Settings { @@ -32,14 +29,6 @@ std::string GetTimeZoneString() { return timezones[time_zone_index]; } -void Apply(Core::System& system) { - if (system.IsPoweredOn()) { - system.Renderer().RefreshBaseSettings(); - } - - Service::HID::ReloadInputDevices(); -} - void LogSettings() { const auto log_setting = [](std::string_view name, const auto& value) { LOG_INFO(Config, "{}: {}", name, value); diff --git a/src/core/settings.h b/src/common/settings.h index d849dded3..d39b4aa45 100644 --- a/src/core/settings.h +++ b/src/common/settings.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -11,16 +11,13 @@ #include <optional> #include <string> #include <vector> -#include "common/common_types.h" -#include "input_common/settings.h" -namespace Core { -class System; -} +#include "common/common_types.h" +#include "common/settings_input.h" namespace Settings { -enum class RendererBackend { +enum class RendererBackend : u32 { OpenGL = 0, Vulkan = 1, }; @@ -31,7 +28,7 @@ enum class GPUAccuracy : u32 { Extreme = 2, }; -enum class CPUAccuracy { +enum class CPUAccuracy : u32 { Accurate = 0, Unsafe = 1, DebugMode = 2, @@ -139,6 +136,7 @@ struct Values { Setting<int> vulkan_device; Setting<u16> resolution_factor{1}; + Setting<int> fullscreen_mode; Setting<int> aspect_ratio; Setting<int> max_anisotropy; Setting<bool> use_frame_limit; @@ -222,6 +220,8 @@ struct Values { bool quest_flag; bool disable_macro_jit; bool extended_logging; + bool use_debug_asserts; + bool use_auto_stub; // Miscellaneous std::string log_filter; @@ -253,7 +253,6 @@ float Volume(); std::string GetTimeZoneString(); -void Apply(Core::System& system); void LogSettings(); // Restore the global state of all applicable settings in the Values struct diff --git a/src/input_common/settings.cpp b/src/common/settings_input.cpp index 557e7a9a0..bea2b837b 100644 --- a/src/input_common/settings.cpp +++ b/src/common/settings_input.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "input_common/settings.h" +#include "common/settings_input.h" namespace Settings { namespace NativeButton { diff --git a/src/input_common/settings.h b/src/common/settings_input.h index a59f5d461..609600582 100644 --- a/src/input_common/settings.h +++ b/src/common/settings_input.h @@ -6,6 +6,7 @@ #include <array> #include <string> + #include "common/common_types.h" namespace Settings { diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index a4647314a..ad04df8ca 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -83,11 +83,15 @@ public: return true; } - T PopWait() { + void Wait() { if (Empty()) { std::unique_lock lock{cv_mutex}; cv.wait(lock, [this]() { return !Empty(); }); } + } + + T PopWait() { + Wait(); T t; Pop(t); return t; @@ -156,6 +160,10 @@ public: return spsc_queue.Pop(t); } + void Wait() { + spsc_queue.Wait(); + } + T PopWait() { return spsc_queue.PopWait(); } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 17f251c37..532e418b0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -141,6 +141,9 @@ add_library(core STATIC hardware_interrupt_manager.h hle/ipc.h hle/ipc_helpers.h + hle/kernel/board/nintendo/nx/k_system_control.cpp + hle/kernel/board/nintendo/nx/k_system_control.h + hle/kernel/board/nintendo/nx/secure_monitor.h hle/kernel/client_port.cpp hle/kernel/client_port.h hle/kernel/client_session.cpp @@ -169,9 +172,13 @@ add_library(core STATIC hle/kernel/k_memory_block.h hle/kernel/k_memory_block_manager.cpp hle/kernel/k_memory_block_manager.h + hle/kernel/k_memory_layout.cpp + hle/kernel/k_memory_layout.board.nintendo_nx.cpp hle/kernel/k_memory_layout.h hle/kernel/k_memory_manager.cpp hle/kernel/k_memory_manager.h + hle/kernel/k_memory_region.h + hle/kernel/k_memory_region_type.h hle/kernel/k_page_bitmap.h hle/kernel/k_page_heap.cpp hle/kernel/k_page_heap.h @@ -196,11 +203,11 @@ add_library(core STATIC hle/kernel/k_spin_lock.h hle/kernel/k_synchronization_object.cpp hle/kernel/k_synchronization_object.h - hle/kernel/k_system_control.cpp hle/kernel/k_system_control.h hle/kernel/k_thread.cpp hle/kernel/k_thread.h hle/kernel/k_thread_queue.h + hle/kernel/k_trace.h hle/kernel/k_writable_event.cpp hle/kernel/k_writable_event.h hle/kernel/kernel.cpp @@ -266,6 +273,7 @@ add_library(core STATIC hle/service/am/applets/profile_select.h hle/service/am/applets/software_keyboard.cpp hle/service/am/applets/software_keyboard.h + hle/service/am/applets/software_keyboard_types.h hle/service/am/applets/web_browser.cpp hle/service/am/applets/web_browser.h hle/service/am/applets/web_types.h @@ -614,8 +622,6 @@ add_library(core STATIC perf_stats.h reporter.cpp reporter.h - settings.cpp - settings.h telemetry_session.cpp telemetry_session.h tools/freezer.cpp @@ -666,7 +672,7 @@ endif() create_target_directory_groups(core) target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) -target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus zip) +target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus zip) if (YUZU_ENABLE_BOXCAT) target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT) diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 53d78de32..7aeb2a658 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/page_table.h" +#include "common/settings.h" #include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_32.h" #include "core/arm/dynarmic/arm_dynarmic_cp15.h" @@ -18,7 +19,6 @@ #include "core/core_timing.h" #include "core/hle/kernel/svc.h" #include "core/memory.h" -#include "core/settings.h" namespace Core { @@ -114,18 +114,17 @@ public: static constexpr u64 minimum_run_cycles = 1000U; }; -std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table, - std::size_t address_space_bits) const { +std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const { Dynarmic::A32::UserConfig config; config.callbacks = cb.get(); - // TODO(bunnei): Implement page table for 32-bit - // config.page_table = &page_table.pointers; config.coprocessors[15] = cp15; config.define_unpredictable_behaviour = true; static constexpr std::size_t PAGE_BITS = 12; static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS); - config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( - page_table.pointers.data()); + if (page_table) { + config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( + page_table->pointers.data()); + } config.absolute_offset_page_table = true; config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; @@ -138,6 +137,10 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& // Timing config.wall_clock_cntpct = uses_wall_clock; + // Code cache size + config.code_cache_size = 512 * 1024 * 1024; + config.far_code_offset = 256 * 1024 * 1024; + // Safe optimizations if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) { if (!Settings::values.cpuopt_page_tables) { @@ -201,7 +204,8 @@ ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handle : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, cb(std::make_unique<DynarmicCallbacks32>(*this)), cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index}, - exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} + exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)}, + jit(MakeJit(nullptr)) {} ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; @@ -256,9 +260,6 @@ void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) { } void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { - if (!jit) { - return; - } Dynarmic::A32::Context context; jit->SaveContext(context); ctx.cpu_registers = context.Regs(); @@ -268,9 +269,6 @@ void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { } void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { - if (!jit) { - return; - } Dynarmic::A32::Context context; context.Regs() = ctx.cpu_registers; context.ExtRegs() = ctx.extension_registers; @@ -284,23 +282,14 @@ void ARM_Dynarmic_32::PrepareReschedule() { } void ARM_Dynarmic_32::ClearInstructionCache() { - if (!jit) { - return; - } jit->ClearCache(); } void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) { - if (!jit) { - return; - } jit->InvalidateCacheRange(static_cast<u32>(addr), size); } void ARM_Dynarmic_32::ClearExclusiveState() { - if (!jit) { - return; - } jit->ClearExclusiveState(); } @@ -316,7 +305,7 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, LoadContext(ctx); return; } - jit = MakeJit(page_table, new_address_space_size_in_bits); + jit = MakeJit(&page_table); LoadContext(ctx); jit_cache.emplace(key, jit); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index f6c4d4db9..d40aef7a9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -68,8 +68,7 @@ public: std::size_t new_address_space_size_in_bits) override; private: - std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable& page_table, - std::size_t address_space_bits) const; + std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; using JitCacheType = @@ -80,10 +79,10 @@ private: std::unique_ptr<DynarmicCallbacks32> cb; JitCacheType jit_cache; - std::shared_ptr<Dynarmic::A32::Jit> jit; std::shared_ptr<DynarmicCP15> cp15; std::size_t core_index; DynarmicExclusiveMonitor& exclusive_monitor; + std::shared_ptr<Dynarmic::A32::Jit> jit; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index b36b7d918..040529f4d 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -9,6 +9,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/page_table.h" +#include "common/settings.h" #include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" #include "core/arm/dynarmic/arm_exclusive_monitor.h" @@ -19,7 +20,6 @@ #include "core/hle/kernel/process.h" #include "core/hle/kernel/svc.h" #include "core/memory.h" -#include "core/settings.h" namespace Core { @@ -142,7 +142,7 @@ public: static constexpr u64 minimum_run_cycles = 1000U; }; -std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table, +std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table, std::size_t address_space_bits) const { Dynarmic::A64::UserConfig config; @@ -150,13 +150,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& config.callbacks = cb.get(); // Memory - config.page_table = reinterpret_cast<void**>(page_table.pointers.data()); - config.page_table_address_space_bits = address_space_bits; - config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; - config.silently_mirror_page_table = false; - config.absolute_offset_page_table = true; - config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; - config.only_detect_misalignment_via_page_table_on_page_boundary = true; + if (page_table) { + config.page_table = reinterpret_cast<void**>(page_table->pointers.data()); + config.page_table_address_space_bits = address_space_bits; + config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; + config.silently_mirror_page_table = false; + config.absolute_offset_page_table = true; + config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; + config.only_detect_misalignment_via_page_table_on_page_boundary = true; + } // Multi-process state config.processor_id = core_index; @@ -175,6 +177,10 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& // Timing config.wall_clock_cntpct = uses_wall_clock; + // Code cache size + config.code_cache_size = 512 * 1024 * 1024; + config.far_code_offset = 256 * 1024 * 1024; + // Safe optimizations if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) { if (!Settings::values.cpuopt_page_tables) { @@ -237,7 +243,8 @@ ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handle std::size_t core_index) : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index}, - exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} + exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)}, + jit(MakeJit(nullptr, 48)) {} ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; @@ -294,9 +301,6 @@ void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) { } void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { - if (!jit) { - return; - } ctx.cpu_registers = jit->GetRegisters(); ctx.sp = jit->GetSP(); ctx.pc = jit->GetPC(); @@ -308,9 +312,6 @@ void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { } void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { - if (!jit) { - return; - } jit->SetRegisters(ctx.cpu_registers); jit->SetSP(ctx.sp); jit->SetPC(ctx.pc); @@ -326,23 +327,14 @@ void ARM_Dynarmic_64::PrepareReschedule() { } void ARM_Dynarmic_64::ClearInstructionCache() { - if (!jit) { - return; - } jit->ClearCache(); } void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) { - if (!jit) { - return; - } jit->InvalidateCacheRange(addr, size); } void ARM_Dynarmic_64::ClearExclusiveState() { - if (!jit) { - return; - } jit->ClearExclusiveState(); } @@ -358,7 +350,7 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, LoadContext(ctx); return; } - jit = MakeJit(page_table, new_address_space_size_in_bits); + jit = MakeJit(&page_table, new_address_space_size_in_bits); LoadContext(ctx); jit_cache.emplace(key, jit); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 329b59a32..edef04376 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -61,7 +61,7 @@ public: std::size_t new_address_space_size_in_bits) override; private: - std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table, + std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, std::size_t address_space_bits) const; using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; @@ -71,10 +71,11 @@ private: friend class DynarmicCallbacks64; std::unique_ptr<DynarmicCallbacks64> cb; JitCacheType jit_cache; - std::shared_ptr<Dynarmic::A64::Jit> jit; std::size_t core_index; DynarmicExclusiveMonitor& exclusive_monitor; + + std::shared_ptr<Dynarmic::A64::Jit> jit; }; } // namespace Core diff --git a/src/core/core.cpp b/src/core/core.cpp index 305f56ff1..d459d6c34 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -9,6 +9,7 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/microprofile.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" @@ -36,6 +37,7 @@ #include "core/hle/service/apm/controller.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/manager.h" +#include "core/hle/service/hid/hid.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/time/time_manager.h" @@ -45,7 +47,6 @@ #include "core/network/network.h" #include "core/perf_stats.h" #include "core/reporter.h" -#include "core/settings.h" #include "core/telemetry_session.h" #include "core/tools/freezer.h" #include "video_core/renderer_base.h" @@ -296,7 +297,7 @@ struct System::Impl { exit_lock = false; if (gpu_core) { - gpu_core->WaitIdle(); + gpu_core->ShutDown(); } services.reset(); @@ -774,4 +775,12 @@ void System::ExecuteProgram(std::size_t program_index) { } } +void System::ApplySettings() { + if (IsPoweredOn()) { + Renderer().RefreshBaseSettings(); + } + + Service::HID::ReloadInputDevices(); +} + } // namespace Core diff --git a/src/core/core.h b/src/core/core.h index 3a8e040c1..f1068d23f 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -388,6 +388,9 @@ public: */ void ExecuteProgram(std::size_t program_index); + /// Applies any changes to settings to this core instance. + void ApplySettings(); + private: System(); diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index ad116dcc0..070ed439e 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -22,6 +22,7 @@ #include "common/file_util.h" #include "common/hex_util.h" #include "common/logging/log.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/crypto/aes_util.h" #include "core/crypto/key_manager.h" @@ -32,7 +33,6 @@ #include "core/file_sys/registered_cache.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" -#include "core/settings.h" namespace Core::Crypto { namespace { diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index b0a130345..f66759815 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -100,6 +100,14 @@ u64 NACP::GetDeviceSaveDataSize() const { return raw.device_save_data_size; } +u32 NACP::GetParentalControlFlag() const { + return raw.parental_control; +} + +const std::array<u8, 0x20>& NACP::GetRatingAge() const { + return raw.rating_age; +} + std::vector<u8> NACP::GetRawBytes() const { std::vector<u8> out(sizeof(RawNACP)); std::memcpy(out.data(), &raw, sizeof(RawNACP)); diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 403c4219a..dd9837cf5 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -114,6 +114,8 @@ public: std::vector<u8> GetRawBytes() const; bool GetUserAccountSwitchLock() const; u64 GetDeviceSaveDataSize() const; + u32 GetParentalControlFlag() const; + const std::array<u8, 0x20>& GetRatingAge() const; private: RawNACP raw{}; diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 7c3284df8..cc9b4b637 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -10,6 +10,7 @@ #include "common/file_util.h" #include "common/hex_util.h" #include "common/logging/log.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/core.h" #include "core/file_sys/common_funcs.h" @@ -25,7 +26,6 @@ #include "core/loader/loader.h" #include "core/loader/nso.h" #include "core/memory/cheat_engine.h" -#include "core/settings.h" namespace FileSys { namespace { diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp index 4df3574d2..8d960d1ca 100644 --- a/src/core/frontend/applets/profile_select.cpp +++ b/src/core/frontend/applets/profile_select.cpp @@ -2,9 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/settings.h" #include "core/frontend/applets/profile_select.h" #include "core/hle/service/acc/profile_manager.h" -#include "core/settings.h" namespace Core::Frontend { diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index 856ed33da..12c76c9ee 100644 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -1,29 +1,149 @@ -// Copyright 2018 yuzu emulator team +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/logging/backend.h" +#include <thread> + +#include "common/logging/log.h" #include "common/string_util.h" #include "core/frontend/applets/software_keyboard.h" namespace Core::Frontend { + SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default; -void DefaultSoftwareKeyboardApplet::RequestText( - std::function<void(std::optional<std::u16string>)> out, - SoftwareKeyboardParameters parameters) const { - if (parameters.initial_text.empty()) - out(u"yuzu"); +DefaultSoftwareKeyboardApplet::~DefaultSoftwareKeyboardApplet() = default; + +void DefaultSoftwareKeyboardApplet::InitializeKeyboard( + bool is_inline, KeyboardInitializeParameters initialize_parameters, + std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)> submit_normal_callback_, + std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> + submit_inline_callback_) { + if (is_inline) { + LOG_WARNING( + Service_AM, + "(STUBBED) called, backend requested to initialize the inline software keyboard."); + + submit_inline_callback = std::move(submit_inline_callback_); + } else { + LOG_WARNING( + Service_AM, + "(STUBBED) called, backend requested to initialize the normal software keyboard."); + + submit_normal_callback = std::move(submit_normal_callback_); + } + + parameters = std::move(initialize_parameters); + + LOG_INFO(Service_AM, + "\nKeyboardInitializeParameters:" + "\nok_text={}" + "\nheader_text={}" + "\nsub_text={}" + "\nguide_text={}" + "\ninitial_text={}" + "\nmax_text_length={}" + "\nmin_text_length={}" + "\ninitial_cursor_position={}" + "\ntype={}" + "\npassword_mode={}" + "\ntext_draw_type={}" + "\nkey_disable_flags={}" + "\nuse_blur_background={}" + "\nenable_backspace_button={}" + "\nenable_return_button={}" + "\ndisable_cancel_button={}", + Common::UTF16ToUTF8(parameters.ok_text), Common::UTF16ToUTF8(parameters.header_text), + Common::UTF16ToUTF8(parameters.sub_text), Common::UTF16ToUTF8(parameters.guide_text), + Common::UTF16ToUTF8(parameters.initial_text), parameters.max_text_length, + parameters.min_text_length, parameters.initial_cursor_position, parameters.type, + parameters.password_mode, parameters.text_draw_type, parameters.key_disable_flags.raw, + parameters.use_blur_background, parameters.enable_backspace_button, + parameters.enable_return_button, parameters.disable_cancel_button); +} + +void DefaultSoftwareKeyboardApplet::ShowNormalKeyboard() const { + LOG_WARNING(Service_AM, + "(STUBBED) called, backend requested to show the normal software keyboard."); + + SubmitNormalText(u"yuzu"); +} + +void DefaultSoftwareKeyboardApplet::ShowTextCheckDialog( + Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const { + LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to show the text check dialog."); +} + +void DefaultSoftwareKeyboardApplet::ShowInlineKeyboard( + InlineAppearParameters appear_parameters) const { + LOG_WARNING(Service_AM, + "(STUBBED) called, backend requested to show the inline software keyboard."); + + LOG_INFO(Service_AM, + "\nInlineAppearParameters:" + "\nmax_text_length={}" + "\nmin_text_length={}" + "\nkey_top_scale_x={}" + "\nkey_top_scale_y={}" + "\nkey_top_translate_x={}" + "\nkey_top_translate_y={}" + "\ntype={}" + "\nkey_disable_flags={}" + "\nkey_top_as_floating={}" + "\nenable_backspace_button={}" + "\nenable_return_button={}" + "\ndisable_cancel_button={}", + appear_parameters.max_text_length, appear_parameters.min_text_length, + appear_parameters.key_top_scale_x, appear_parameters.key_top_scale_y, + appear_parameters.key_top_translate_x, appear_parameters.key_top_translate_y, + appear_parameters.type, appear_parameters.key_disable_flags.raw, + appear_parameters.key_top_as_floating, appear_parameters.enable_backspace_button, + appear_parameters.enable_return_button, appear_parameters.disable_cancel_button); + + std::thread([this] { SubmitInlineText(u"yuzu"); }).detach(); +} - out(parameters.initial_text); +void DefaultSoftwareKeyboardApplet::HideInlineKeyboard() const { + LOG_WARNING(Service_AM, + "(STUBBED) called, backend requested to hide the inline software keyboard."); } -void DefaultSoftwareKeyboardApplet::SendTextCheckDialog( - std::u16string error_message, std::function<void()> finished_check) const { +void DefaultSoftwareKeyboardApplet::InlineTextChanged(InlineTextParameters text_parameters) const { LOG_WARNING(Service_AM, - "(STUBBED) called - Default fallback software keyboard does not support text " - "check! (error_message={})", - Common::UTF16ToUTF8(error_message)); - finished_check(); + "(STUBBED) called, backend requested to change the inline keyboard text."); + + LOG_INFO(Service_AM, + "\nInlineTextParameters:" + "\ninput_text={}" + "\ncursor_position={}", + Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position); + + submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, + text_parameters.input_text, text_parameters.cursor_position); +} + +void DefaultSoftwareKeyboardApplet::ExitKeyboard() const { + LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to exit the software keyboard."); } + +void DefaultSoftwareKeyboardApplet::SubmitNormalText(std::u16string text) const { + submit_normal_callback(Service::AM::Applets::SwkbdResult::Ok, text); +} + +void DefaultSoftwareKeyboardApplet::SubmitInlineText(std::u16string_view text) const { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + for (std::size_t index = 0; index < text.size(); ++index) { + submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, + std::u16string(text.data(), text.data() + index + 1), + static_cast<s32>(index) + 1); + + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } + + submit_inline_callback(Service::AM::Applets::SwkbdReplyType::DecidedEnter, std::u16string(text), + static_cast<s32>(text.size())); +} + } // namespace Core::Frontend diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index f9b202664..506eb35bb 100644 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -1,54 +1,116 @@ -// Copyright 2018 yuzu emulator team +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include <functional> -#include <optional> -#include <string> -#include "common/bit_field.h" +#include <thread> + #include "common/common_types.h" +#include "core/hle/service/am/applets/software_keyboard_types.h" + namespace Core::Frontend { -struct SoftwareKeyboardParameters { - std::u16string submit_text; + +struct KeyboardInitializeParameters { + std::u16string ok_text; std::u16string header_text; std::u16string sub_text; std::u16string guide_text; std::u16string initial_text; - std::size_t max_length; - bool password; - bool cursor_at_beginning; - - union { - u8 value; - - BitField<1, 1, u8> disable_space; - BitField<2, 1, u8> disable_address; - BitField<3, 1, u8> disable_percent; - BitField<4, 1, u8> disable_slash; - BitField<6, 1, u8> disable_number; - BitField<7, 1, u8> disable_download_code; - }; + u32 max_text_length; + u32 min_text_length; + s32 initial_cursor_position; + Service::AM::Applets::SwkbdType type; + Service::AM::Applets::SwkbdPasswordMode password_mode; + Service::AM::Applets::SwkbdTextDrawType text_draw_type; + Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags; + bool use_blur_background; + bool enable_backspace_button; + bool enable_return_button; + bool disable_cancel_button; +}; + +struct InlineAppearParameters { + u32 max_text_length; + u32 min_text_length; + f32 key_top_scale_x; + f32 key_top_scale_y; + f32 key_top_translate_x; + f32 key_top_translate_y; + Service::AM::Applets::SwkbdType type; + Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags; + bool key_top_as_floating; + bool enable_backspace_button; + bool enable_return_button; + bool disable_cancel_button; +}; + +struct InlineTextParameters { + std::u16string input_text; + s32 cursor_position; }; class SoftwareKeyboardApplet { public: virtual ~SoftwareKeyboardApplet(); - virtual void RequestText(std::function<void(std::optional<std::u16string>)> out, - SoftwareKeyboardParameters parameters) const = 0; - virtual void SendTextCheckDialog(std::u16string error_message, - std::function<void()> finished_check) const = 0; + virtual void InitializeKeyboard( + bool is_inline, KeyboardInitializeParameters initialize_parameters, + std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)> + submit_normal_callback_, + std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> + submit_inline_callback_) = 0; + + virtual void ShowNormalKeyboard() const = 0; + + virtual void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const = 0; + + virtual void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const = 0; + + virtual void HideInlineKeyboard() const = 0; + + virtual void InlineTextChanged(InlineTextParameters text_parameters) const = 0; + + virtual void ExitKeyboard() const = 0; }; class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet { public: - void RequestText(std::function<void(std::optional<std::u16string>)> out, - SoftwareKeyboardParameters parameters) const override; - void SendTextCheckDialog(std::u16string error_message, - std::function<void()> finished_check) const override; + ~DefaultSoftwareKeyboardApplet() override; + + void InitializeKeyboard( + bool is_inline, KeyboardInitializeParameters initialize_parameters, + std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)> + submit_normal_callback_, + std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> + submit_inline_callback_) override; + + void ShowNormalKeyboard() const override; + + void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const override; + + void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const override; + + void HideInlineKeyboard() const override; + + void InlineTextChanged(InlineTextParameters text_parameters) const override; + + void ExitKeyboard() const override; + +private: + void SubmitNormalText(std::u16string text) const; + void SubmitInlineText(std::u16string_view text) const; + + KeyboardInitializeParameters parameters; + + mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)> + submit_normal_callback; + mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> + submit_inline_callback; }; } // namespace Core::Frontend diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index ee7a58b1c..474de9206 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -4,9 +4,9 @@ #include <cmath> #include <mutex> +#include "common/settings.h" #include "core/frontend/emu_window.h" #include "core/frontend/input.h" -#include "core/settings.h" namespace Core::Frontend { diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index b9a270a55..0832463d6 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -5,8 +5,8 @@ #include <cmath> #include "common/assert.h" +#include "common/settings.h" #include "core/frontend/framebuffer_layout.h" -#include "core/settings.h" namespace Layout { diff --git a/src/core/frontend/input_interpreter.cpp b/src/core/frontend/input_interpreter.cpp index ec5fe660e..9f6a90e8f 100644 --- a/src/core/frontend/input_interpreter.cpp +++ b/src/core/frontend/input_interpreter.cpp @@ -12,7 +12,9 @@ InputInterpreter::InputInterpreter(Core::System& system) : npad{system.ServiceManager() .GetService<Service::HID::Hid>("hid") ->GetAppletResource() - ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {} + ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} { + ResetButtonStates(); +} InputInterpreter::~InputInterpreter() = default; @@ -25,6 +27,17 @@ void InputInterpreter::PollInput() { button_states[current_index] = button_state; } +void InputInterpreter::ResetButtonStates() { + previous_index = 0; + current_index = 0; + + button_states[0] = 0xFFFFFFFF; + + for (std::size_t i = 1; i < button_states.size(); ++i) { + button_states[i] = 0; + } +} + bool InputInterpreter::IsButtonPressed(HIDButton button) const { return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; } diff --git a/src/core/frontend/input_interpreter.h b/src/core/frontend/input_interpreter.h index 73fc47ffb..9495e3daf 100644 --- a/src/core/frontend/input_interpreter.h +++ b/src/core/frontend/input_interpreter.h @@ -66,6 +66,9 @@ public: /// Gets a button state from HID and inserts it into the array of button states. void PollInput(); + /// Resets all the button states to their defaults. + void ResetButtonStates(); + /** * Checks whether the button is pressed. * diff --git a/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc b/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc new file mode 100644 index 000000000..857b512ba --- /dev/null +++ b/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc @@ -0,0 +1,20 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// All architectures must define NumArchitectureDeviceRegions. +constexpr inline const auto NumArchitectureDeviceRegions = 3; + +constexpr inline const auto KMemoryRegionType_Uart = + KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 0); +constexpr inline const auto KMemoryRegionType_InterruptCpuInterface = + KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 1) + .SetAttribute(KMemoryRegionAttr_NoUserMap); +constexpr inline const auto KMemoryRegionType_InterruptDistributor = + KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 2) + .SetAttribute(KMemoryRegionAttr_NoUserMap); +static_assert(KMemoryRegionType_Uart.GetValue() == (0x1D)); +static_assert(KMemoryRegionType_InterruptCpuInterface.GetValue() == + (0x2D | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_InterruptDistributor.GetValue() == + (0x4D | KMemoryRegionAttr_NoUserMap)); diff --git a/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc b/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc new file mode 100644 index 000000000..58d6c0b16 --- /dev/null +++ b/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc @@ -0,0 +1,52 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// All architectures must define NumBoardDeviceRegions. +constexpr inline const auto NumBoardDeviceRegions = 6; +// UNUSED: .Derive(NumBoardDeviceRegions, 0); +constexpr inline const auto KMemoryRegionType_MemoryController = + KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 1) + .SetAttribute(KMemoryRegionAttr_NoUserMap); +constexpr inline const auto KMemoryRegionType_MemoryController1 = + KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 2) + .SetAttribute(KMemoryRegionAttr_NoUserMap); +constexpr inline const auto KMemoryRegionType_MemoryController0 = + KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 3) + .SetAttribute(KMemoryRegionAttr_NoUserMap); +constexpr inline const auto KMemoryRegionType_PowerManagementController = + KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 4).DeriveTransition(); +constexpr inline const auto KMemoryRegionType_LegacyLpsDevices = + KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 5); +static_assert(KMemoryRegionType_MemoryController.GetValue() == + (0x55 | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_MemoryController1.GetValue() == + (0x65 | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_MemoryController0.GetValue() == + (0x95 | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_PowerManagementController.GetValue() == (0x1A5)); + +static_assert(KMemoryRegionType_LegacyLpsDevices.GetValue() == 0xC5); + +constexpr inline const auto NumLegacyLpsDevices = 7; +constexpr inline const auto KMemoryRegionType_LegacyLpsExceptionVectors = + KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 0); +constexpr inline const auto KMemoryRegionType_LegacyLpsIram = + KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 1); +constexpr inline const auto KMemoryRegionType_LegacyLpsFlowController = + KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 2); +constexpr inline const auto KMemoryRegionType_LegacyLpsPrimaryICtlr = + KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 3); +constexpr inline const auto KMemoryRegionType_LegacyLpsSemaphore = + KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 4); +constexpr inline const auto KMemoryRegionType_LegacyLpsAtomics = + KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 5); +constexpr inline const auto KMemoryRegionType_LegacyLpsClkRst = + KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 6); +static_assert(KMemoryRegionType_LegacyLpsExceptionVectors.GetValue() == 0x3C5); +static_assert(KMemoryRegionType_LegacyLpsIram.GetValue() == 0x5C5); +static_assert(KMemoryRegionType_LegacyLpsFlowController.GetValue() == 0x6C5); +static_assert(KMemoryRegionType_LegacyLpsPrimaryICtlr.GetValue() == 0x9C5); +static_assert(KMemoryRegionType_LegacyLpsSemaphore.GetValue() == 0xAC5); +static_assert(KMemoryRegionType_LegacyLpsAtomics.GetValue() == 0xCC5); +static_assert(KMemoryRegionType_LegacyLpsClkRst.GetValue() == 0x11C5); diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp new file mode 100644 index 000000000..86472b5ce --- /dev/null +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp @@ -0,0 +1,164 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <random> + +#include "common/common_sizes.h" +#include "core/hle/kernel/board/nintendo/nx/k_system_control.h" +#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h" +#include "core/hle/kernel/k_trace.h" + +namespace Kernel::Board::Nintendo::Nx { + +namespace impl { + +constexpr const std::size_t RequiredNonSecureSystemMemorySizeVi = 0x2238 * 4 * 1024; +constexpr const std::size_t RequiredNonSecureSystemMemorySizeNvservices = 0x710 * 4 * 1024; +constexpr const std::size_t RequiredNonSecureSystemMemorySizeMisc = 0x80 * 4 * 1024; + +} // namespace impl + +constexpr const std::size_t RequiredNonSecureSystemMemorySize = + impl::RequiredNonSecureSystemMemorySizeVi + impl::RequiredNonSecureSystemMemorySizeNvservices + + impl::RequiredNonSecureSystemMemorySizeMisc; + +namespace { + +u32 GetMemoryModeForInit() { + return 0x01; +} + +u32 GetMemorySizeForInit() { + return 0; +} + +Smc::MemoryArrangement GetMemoryArrangeForInit() { + switch (GetMemoryModeForInit() & 0x3F) { + case 0x01: + default: + return Smc::MemoryArrangement_4GB; + case 0x02: + return Smc::MemoryArrangement_4GBForAppletDev; + case 0x03: + return Smc::MemoryArrangement_4GBForSystemDev; + case 0x11: + return Smc::MemoryArrangement_6GB; + case 0x12: + return Smc::MemoryArrangement_6GBForAppletDev; + case 0x21: + return Smc::MemoryArrangement_8GB; + } +} +} // namespace + +// Initialization. +size_t KSystemControl::Init::GetIntendedMemorySize() { + switch (GetMemorySizeForInit()) { + case Smc::MemorySize_4GB: + default: // All invalid modes should go to 4GB. + return Common::Size_4_GB; + case Smc::MemorySize_6GB: + return Common::Size_6_GB; + case Smc::MemorySize_8GB: + return Common::Size_8_GB; + } +} + +PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) { + return base_address; +} + +bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() { + return true; +} + +std::size_t KSystemControl::Init::GetApplicationPoolSize() { + // Get the base pool size. + const size_t base_pool_size = []() -> size_t { + switch (GetMemoryArrangeForInit()) { + case Smc::MemoryArrangement_4GB: + default: + return Common::Size_3285_MB; + case Smc::MemoryArrangement_4GBForAppletDev: + return Common::Size_2048_MB; + case Smc::MemoryArrangement_4GBForSystemDev: + return Common::Size_3285_MB; + case Smc::MemoryArrangement_6GB: + return Common::Size_4916_MB; + case Smc::MemoryArrangement_6GBForAppletDev: + return Common::Size_3285_MB; + case Smc::MemoryArrangement_8GB: + return Common::Size_4916_MB; + } + }(); + + // Return (possibly) adjusted size. + return base_pool_size; +} + +size_t KSystemControl::Init::GetAppletPoolSize() { + // Get the base pool size. + const size_t base_pool_size = []() -> size_t { + switch (GetMemoryArrangeForInit()) { + case Smc::MemoryArrangement_4GB: + default: + return Common::Size_507_MB; + case Smc::MemoryArrangement_4GBForAppletDev: + return Common::Size_1554_MB; + case Smc::MemoryArrangement_4GBForSystemDev: + return Common::Size_448_MB; + case Smc::MemoryArrangement_6GB: + return Common::Size_562_MB; + case Smc::MemoryArrangement_6GBForAppletDev: + return Common::Size_2193_MB; + case Smc::MemoryArrangement_8GB: + return Common::Size_2193_MB; + } + }(); + + // Return (possibly) adjusted size. + constexpr size_t ExtraSystemMemoryForAtmosphere = Common::Size_33_MB; + return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize; +} + +size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() { + // Verify that our minimum is at least as large as Nintendo's. + constexpr size_t MinimumSize = RequiredNonSecureSystemMemorySize; + static_assert(MinimumSize >= 0x29C8000); + + return MinimumSize; +} + +namespace { +template <typename F> +u64 GenerateUniformRange(u64 min, u64 max, F f) { + // Handle the case where the difference is too large to represent. + if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) { + return f(); + } + + // Iterate until we get a value in range. + const u64 range_size = ((max + 1) - min); + const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size; + while (true) { + if (const u64 rnd = f(); rnd < effective_max) { + return min + (rnd % range_size); + } + } +} + +} // Anonymous namespace + +u64 KSystemControl::GenerateRandomU64() { + static std::random_device device; + static std::mt19937 gen(device()); + static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); + return distribution(gen); +} + +u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) { + return GenerateUniformRange(min, max, GenerateRandomU64); +} + +} // namespace Kernel::Board::Nintendo::Nx diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h new file mode 100644 index 000000000..52f230ced --- /dev/null +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h @@ -0,0 +1,28 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Kernel::Board::Nintendo::Nx { + +class KSystemControl { +public: + class Init { + public: + // Initialization. + static std::size_t GetIntendedMemorySize(); + static PAddr GetKernelPhysicalBaseAddress(u64 base_address); + static bool ShouldIncreaseThreadResourceLimit(); + static std::size_t GetApplicationPoolSize(); + static std::size_t GetAppletPoolSize(); + static std::size_t GetMinimumNonSecureSystemPoolSize(); + }; + + static u64 GenerateRandomRange(u64 min, u64 max); + static u64 GenerateRandomU64(); +}; + +} // namespace Kernel::Board::Nintendo::Nx diff --git a/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h b/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h new file mode 100644 index 000000000..0c366b252 --- /dev/null +++ b/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h @@ -0,0 +1,26 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Kernel::Board::Nintendo::Nx::Smc { + +enum MemorySize { + MemorySize_4GB = 0, + MemorySize_6GB = 1, + MemorySize_8GB = 2, +}; + +enum MemoryArrangement { + MemoryArrangement_4GB = 0, + MemoryArrangement_4GBForAppletDev = 1, + MemoryArrangement_4GBForSystemDev = 2, + MemoryArrangement_6GB = 3, + MemoryArrangement_6GBForAppletDev = 4, + MemoryArrangement_8GB = 5, +}; + +} // namespace Kernel::Board::Nintendo::Nx::Smc diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 161d9f782..2b363b1d9 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -75,10 +75,14 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_ if (incoming) { // Populate the object lists with the data in the IPC request. for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { - copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>())); + const u32 copy_handle{rp.Pop<Handle>()}; + copy_handles.push_back(copy_handle); + copy_objects.push_back(handle_table.GetGeneric(copy_handle)); } for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_move; ++handle) { - move_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>())); + const u32 move_handle{rp.Pop<Handle>()}; + move_handles.push_back(move_handle); + move_objects.push_back(handle_table.GetGeneric(move_handle)); } } else { // For responses we just ignore the handles, they're empty and will be populated when diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 9a769781b..6fba42615 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -210,6 +210,14 @@ public: /// Helper function to test whether the output buffer at buffer_index can be written bool CanWriteBuffer(std::size_t buffer_index = 0) const; + Handle GetCopyHandle(std::size_t index) const { + return copy_handles.at(index); + } + + Handle GetMoveHandle(std::size_t index) const { + return move_handles.at(index); + } + template <typename T> std::shared_ptr<T> GetCopyObject(std::size_t index) { return DynamicObjectCast<T>(copy_objects.at(index)); @@ -285,6 +293,8 @@ private: std::shared_ptr<Kernel::ServerSession> server_session; std::shared_ptr<KThread> thread; // TODO(yuriks): Check common usage of this and optimize size accordingly + boost::container::small_vector<Handle, 8> move_handles; + boost::container::small_vector<Handle, 8> copy_handles; boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects; boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects; boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects; diff --git a/src/core/hle/kernel/k_address_space_info.cpp b/src/core/hle/kernel/k_address_space_info.cpp index 24944d15b..c7549f7a2 100644 --- a/src/core/hle/kernel/k_address_space_info.cpp +++ b/src/core/hle/kernel/k_address_space_info.cpp @@ -5,45 +5,34 @@ #include <array> #include "common/assert.h" +#include "common/common_sizes.h" #include "core/hle/kernel/k_address_space_info.h" namespace Kernel { namespace { -enum : u64 { - Size_1_MB = 0x100000, - Size_2_MB = 2 * Size_1_MB, - Size_128_MB = 128 * Size_1_MB, - Size_1_GB = 0x40000000, - Size_2_GB = 2 * Size_1_GB, - Size_4_GB = 4 * Size_1_GB, - Size_6_GB = 6 * Size_1_GB, - Size_64_GB = 64 * Size_1_GB, - Size_512_GB = 512 * Size_1_GB, - Invalid = std::numeric_limits<u64>::max(), -}; - // clang-format off constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{ - { .bit_width = 32, .address = Size_2_MB , .size = Size_1_GB - Size_2_MB , .type = KAddressSpaceInfo::Type::MapSmall, }, - { .bit_width = 32, .address = Size_1_GB , .size = Size_4_GB - Size_1_GB , .type = KAddressSpaceInfo::Type::MapLarge, }, - { .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = KAddressSpaceInfo::Type::Heap, }, - { .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = KAddressSpaceInfo::Type::Alias, }, - { .bit_width = 36, .address = Size_128_MB, .size = Size_2_GB - Size_128_MB, .type = KAddressSpaceInfo::Type::MapSmall, }, - { .bit_width = 36, .address = Size_2_GB , .size = Size_64_GB - Size_2_GB , .type = KAddressSpaceInfo::Type::MapLarge, }, - { .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, }, - { .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = KAddressSpaceInfo::Type::Alias, }, - { .bit_width = 39, .address = Size_128_MB, .size = Size_512_GB - Size_128_MB, .type = KAddressSpaceInfo::Type::Map39Bit, }, - { .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = KAddressSpaceInfo::Type::MapSmall }, - { .bit_width = 39, .address = Invalid , .size = Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, }, - { .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = KAddressSpaceInfo::Type::Alias, }, - { .bit_width = 39, .address = Invalid , .size = Size_2_GB , .type = KAddressSpaceInfo::Type::Stack, }, + { .bit_width = 32, .address = Common::Size_2_MB , .size = Common::Size_1_GB - Common::Size_2_MB , .type = KAddressSpaceInfo::Type::MapSmall, }, + { .bit_width = 32, .address = Common::Size_1_GB , .size = Common::Size_4_GB - Common::Size_1_GB , .type = KAddressSpaceInfo::Type::MapLarge, }, + { .bit_width = 32, .address = Common::Size_Invalid, .size = Common::Size_1_GB , .type = KAddressSpaceInfo::Type::Alias, }, + { .bit_width = 32, .address = Common::Size_Invalid, .size = Common::Size_1_GB , .type = KAddressSpaceInfo::Type::Heap, }, + { .bit_width = 36, .address = Common::Size_128_MB , .size = Common::Size_2_GB - Common::Size_128_MB, .type = KAddressSpaceInfo::Type::MapSmall, }, + { .bit_width = 36, .address = Common::Size_2_GB , .size = Common::Size_64_GB - Common::Size_2_GB , .type = KAddressSpaceInfo::Type::MapLarge, }, + { .bit_width = 36, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, }, + { .bit_width = 36, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Alias, }, + { .bit_width = 39, .address = Common::Size_128_MB , .size = Common::Size_512_GB - Common::Size_128_MB, .type = KAddressSpaceInfo::Type::Map39Bit, }, + { .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_64_GB , .type = KAddressSpaceInfo::Type::MapSmall }, + { .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, }, + { .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_64_GB , .type = KAddressSpaceInfo::Type::Alias, }, + { .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_2_GB , .type = KAddressSpaceInfo::Type::Stack, }, }}; // clang-format on constexpr bool IsAllowedIndexForAddress(std::size_t index) { - return index < AddressSpaceInfos.size() && AddressSpaceInfos[index].address != Invalid; + return index < AddressSpaceInfos.size() && + AddressSpaceInfos[index].address != Common::Size_Invalid; } using IndexArray = diff --git a/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp b/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp new file mode 100644 index 000000000..a78551291 --- /dev/null +++ b/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp @@ -0,0 +1,199 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/alignment.h" +#include "core/hle/kernel/k_memory_layout.h" +#include "core/hle/kernel/k_memory_manager.h" +#include "core/hle/kernel/k_system_control.h" +#include "core/hle/kernel/k_trace.h" + +namespace Kernel { + +namespace { + +constexpr size_t CarveoutAlignment = 0x20000; +constexpr size_t CarveoutSizeMax = (512ULL * 1024 * 1024) - CarveoutAlignment; + +bool SetupPowerManagementControllerMemoryRegion(KMemoryLayout& memory_layout) { + // Above firmware 2.0.0, the PMC is not mappable. + return memory_layout.GetPhysicalMemoryRegionTree().Insert( + 0x7000E000, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap) && + memory_layout.GetPhysicalMemoryRegionTree().Insert( + 0x7000E400, 0xC00, + KMemoryRegionType_PowerManagementController | KMemoryRegionAttr_NoUserMap); +} + +void InsertPoolPartitionRegionIntoBothTrees(KMemoryLayout& memory_layout, size_t start, size_t size, + KMemoryRegionType phys_type, + KMemoryRegionType virt_type, u32& cur_attr) { + const u32 attr = cur_attr++; + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(start, size, + static_cast<u32>(phys_type), attr)); + const KMemoryRegion* phys = memory_layout.GetPhysicalMemoryRegionTree().FindByTypeAndAttribute( + static_cast<u32>(phys_type), attr); + ASSERT(phys != nullptr); + ASSERT(phys->GetEndAddress() != 0); + ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(phys->GetPairAddress(), size, + static_cast<u32>(virt_type), attr)); +} + +} // namespace + +namespace Init { + +void SetupDevicePhysicalMemoryRegions(KMemoryLayout& memory_layout) { + ASSERT(SetupPowerManagementControllerMemoryRegion(memory_layout)); + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + 0x70019000, 0x1000, KMemoryRegionType_MemoryController | KMemoryRegionAttr_NoUserMap)); + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + 0x7001C000, 0x1000, KMemoryRegionType_MemoryController0 | KMemoryRegionAttr_NoUserMap)); + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + 0x7001D000, 0x1000, KMemoryRegionType_MemoryController1 | KMemoryRegionAttr_NoUserMap)); + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + 0x50040000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + 0x50041000, 0x1000, + KMemoryRegionType_InterruptDistributor | KMemoryRegionAttr_ShouldKernelMap)); + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + 0x50042000, 0x1000, + KMemoryRegionType_InterruptCpuInterface | KMemoryRegionAttr_ShouldKernelMap)); + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + 0x50043000, 0x1D000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); + + // Map IRAM unconditionally, to support debug-logging-to-iram build config. + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + 0x40000000, 0x40000, KMemoryRegionType_LegacyLpsIram | KMemoryRegionAttr_ShouldKernelMap)); + + // Above firmware 2.0.0, prevent mapping the bpmp exception vectors or the ipatch region. + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + 0x6000F000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + 0x6001DC00, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); +} + +void SetupDramPhysicalMemoryRegions(KMemoryLayout& memory_layout) { + const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize(); + const PAddr physical_memory_base_address = + KSystemControl::Init::GetKernelPhysicalBaseAddress(DramPhysicalAddress); + + // Insert blocks into the tree. + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + physical_memory_base_address, intended_memory_size, KMemoryRegionType_Dram)); + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + physical_memory_base_address, ReservedEarlyDramSize, KMemoryRegionType_DramReservedEarly)); + + // Insert the KTrace block at the end of Dram, if KTrace is enabled. + static_assert(!IsKTraceEnabled || KTraceBufferSize > 0); + if constexpr (IsKTraceEnabled) { + const PAddr ktrace_buffer_phys_addr = + physical_memory_base_address + intended_memory_size - KTraceBufferSize; + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + ktrace_buffer_phys_addr, KTraceBufferSize, KMemoryRegionType_KernelTraceBuffer)); + } +} + +void SetupPoolPartitionMemoryRegions(KMemoryLayout& memory_layout) { + // Start by identifying the extents of the DRAM memory region. + const auto dram_extents = memory_layout.GetMainMemoryPhysicalExtents(); + ASSERT(dram_extents.GetEndAddress() != 0); + + // Determine the end of the pool region. + const u64 pool_end = dram_extents.GetEndAddress() - KTraceBufferSize; + + // Find the start of the kernel DRAM region. + const KMemoryRegion* kernel_dram_region = + memory_layout.GetPhysicalMemoryRegionTree().FindFirstDerived( + KMemoryRegionType_DramKernelBase); + ASSERT(kernel_dram_region != nullptr); + + const u64 kernel_dram_start = kernel_dram_region->GetAddress(); + ASSERT(Common::IsAligned(kernel_dram_start, CarveoutAlignment)); + + // Find the start of the pool partitions region. + const KMemoryRegion* pool_partitions_region = + memory_layout.GetPhysicalMemoryRegionTree().FindByTypeAndAttribute( + KMemoryRegionType_DramPoolPartition, 0); + ASSERT(pool_partitions_region != nullptr); + const u64 pool_partitions_start = pool_partitions_region->GetAddress(); + + // Setup the pool partition layouts. + // On 5.0.0+, setup modern 4-pool-partition layout. + + // Get Application and Applet pool sizes. + const size_t application_pool_size = KSystemControl::Init::GetApplicationPoolSize(); + const size_t applet_pool_size = KSystemControl::Init::GetAppletPoolSize(); + const size_t unsafe_system_pool_min_size = + KSystemControl::Init::GetMinimumNonSecureSystemPoolSize(); + + // Decide on starting addresses for our pools. + const u64 application_pool_start = pool_end - application_pool_size; + const u64 applet_pool_start = application_pool_start - applet_pool_size; + const u64 unsafe_system_pool_start = std::min( + kernel_dram_start + CarveoutSizeMax, + Common::AlignDown(applet_pool_start - unsafe_system_pool_min_size, CarveoutAlignment)); + const size_t unsafe_system_pool_size = applet_pool_start - unsafe_system_pool_start; + + // We want to arrange application pool depending on where the middle of dram is. + const u64 dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2; + u32 cur_pool_attr = 0; + size_t total_overhead_size = 0; + if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) { + InsertPoolPartitionRegionIntoBothTrees( + memory_layout, application_pool_start, application_pool_size, + KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, + cur_pool_attr); + total_overhead_size += + KMemoryManager::CalculateManagementOverheadSize(application_pool_size); + } else { + const size_t first_application_pool_size = dram_midpoint - application_pool_start; + const size_t second_application_pool_size = + application_pool_start + application_pool_size - dram_midpoint; + InsertPoolPartitionRegionIntoBothTrees( + memory_layout, application_pool_start, first_application_pool_size, + KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, + cur_pool_attr); + InsertPoolPartitionRegionIntoBothTrees( + memory_layout, dram_midpoint, second_application_pool_size, + KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, + cur_pool_attr); + total_overhead_size += + KMemoryManager::CalculateManagementOverheadSize(first_application_pool_size); + total_overhead_size += + KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size); + } + + // Insert the applet pool. + InsertPoolPartitionRegionIntoBothTrees(memory_layout, applet_pool_start, applet_pool_size, + KMemoryRegionType_DramAppletPool, + KMemoryRegionType_VirtualDramAppletPool, cur_pool_attr); + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(applet_pool_size); + + // Insert the nonsecure system pool. + InsertPoolPartitionRegionIntoBothTrees( + memory_layout, unsafe_system_pool_start, unsafe_system_pool_size, + KMemoryRegionType_DramSystemNonSecurePool, KMemoryRegionType_VirtualDramSystemNonSecurePool, + cur_pool_attr); + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(unsafe_system_pool_size); + + // Insert the pool management region. + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize( + (unsafe_system_pool_start - pool_partitions_start) - total_overhead_size); + const u64 pool_management_start = unsafe_system_pool_start - total_overhead_size; + const size_t pool_management_size = total_overhead_size; + u32 pool_management_attr = 0; + InsertPoolPartitionRegionIntoBothTrees( + memory_layout, pool_management_start, pool_management_size, + KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement, + pool_management_attr); + + // Insert the system pool. + const u64 system_pool_size = pool_management_start - pool_partitions_start; + InsertPoolPartitionRegionIntoBothTrees(memory_layout, pool_partitions_start, system_pool_size, + KMemoryRegionType_DramSystemPool, + KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr); +} + +} // namespace Init + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_layout.cpp b/src/core/hle/kernel/k_memory_layout.cpp new file mode 100644 index 000000000..fb1e2435f --- /dev/null +++ b/src/core/hle/kernel/k_memory_layout.cpp @@ -0,0 +1,166 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <array> + +#include "common/alignment.h" +#include "core/hle/kernel/k_memory_layout.h" +#include "core/hle/kernel/k_system_control.h" + +namespace Kernel { + +namespace { + +template <typename... Args> +KMemoryRegion* AllocateRegion(KMemoryRegionAllocator& memory_region_allocator, Args&&... args) { + return memory_region_allocator.Allocate(std::forward<Args>(args)...); +} + +} // namespace + +KMemoryRegionTree::KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_) + : memory_region_allocator{memory_region_allocator_} {} + +void KMemoryRegionTree::InsertDirectly(u64 address, u64 last_address, u32 attr, u32 type_id) { + this->insert(*AllocateRegion(memory_region_allocator, address, last_address, attr, type_id)); +} + +bool KMemoryRegionTree::Insert(u64 address, size_t size, u32 type_id, u32 new_attr, u32 old_attr) { + // Locate the memory region that contains the address. + KMemoryRegion* found = this->FindModifiable(address); + + // We require that the old attr is correct. + if (found->GetAttributes() != old_attr) { + return false; + } + + // We further require that the region can be split from the old region. + const u64 inserted_region_end = address + size; + const u64 inserted_region_last = inserted_region_end - 1; + if (found->GetLastAddress() < inserted_region_last) { + return false; + } + + // Further, we require that the type id is a valid transformation. + if (!found->CanDerive(type_id)) { + return false; + } + + // Cache information from the region before we remove it. + const u64 old_address = found->GetAddress(); + const u64 old_last = found->GetLastAddress(); + const u64 old_pair = found->GetPairAddress(); + const u32 old_type = found->GetType(); + + // Erase the existing region from the tree. + this->erase(this->iterator_to(*found)); + + // Insert the new region into the tree. + if (old_address == address) { + // Reuse the old object for the new region, if we can. + found->Reset(address, inserted_region_last, old_pair, new_attr, type_id); + this->insert(*found); + } else { + // If we can't re-use, adjust the old region. + found->Reset(old_address, address - 1, old_pair, old_attr, old_type); + this->insert(*found); + + // Insert a new region for the split. + const u64 new_pair = (old_pair != std::numeric_limits<u64>::max()) + ? old_pair + (address - old_address) + : old_pair; + this->insert(*AllocateRegion(memory_region_allocator, address, inserted_region_last, + new_pair, new_attr, type_id)); + } + + // If we need to insert a region after the region, do so. + if (old_last != inserted_region_last) { + const u64 after_pair = (old_pair != std::numeric_limits<u64>::max()) + ? old_pair + (inserted_region_end - old_address) + : old_pair; + this->insert(*AllocateRegion(memory_region_allocator, inserted_region_end, old_last, + after_pair, old_attr, old_type)); + } + + return true; +} + +VAddr KMemoryRegionTree::GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id) { + // We want to find the total extents of the type id. + const auto extents = this->GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id)); + + // Ensure that our alignment is correct. + ASSERT(Common::IsAligned(extents.GetAddress(), alignment)); + + const u64 first_address = extents.GetAddress(); + const u64 last_address = extents.GetLastAddress(); + + const u64 first_index = first_address / alignment; + const u64 last_index = last_address / alignment; + + while (true) { + const u64 candidate = + KSystemControl::GenerateRandomRange(first_index, last_index) * alignment; + + // Ensure that the candidate doesn't overflow with the size. + if (!(candidate < candidate + size)) { + continue; + } + + const u64 candidate_last = candidate + size - 1; + + // Ensure that the candidate fits within the region. + if (candidate_last > last_address) { + continue; + } + + // Locate the candidate region, and ensure it fits and has the correct type id. + if (const auto& candidate_region = *this->Find(candidate); + !(candidate_last <= candidate_region.GetLastAddress() && + candidate_region.GetType() == type_id)) { + continue; + } + + return candidate; + } +} + +KMemoryLayout::KMemoryLayout() + : virtual_tree{memory_region_allocator}, physical_tree{memory_region_allocator}, + virtual_linear_tree{memory_region_allocator}, physical_linear_tree{memory_region_allocator} {} + +void KMemoryLayout::InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start, + VAddr linear_virtual_start) { + // Set static differences. + linear_phys_to_virt_diff = linear_virtual_start - aligned_linear_phys_start; + linear_virt_to_phys_diff = aligned_linear_phys_start - linear_virtual_start; + + // Initialize linear trees. + for (auto& region : GetPhysicalMemoryRegionTree()) { + if (region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) { + GetPhysicalLinearMemoryRegionTree().InsertDirectly( + region.GetAddress(), region.GetLastAddress(), region.GetAttributes(), + region.GetType()); + } + } + + for (auto& region : GetVirtualMemoryRegionTree()) { + if (region.IsDerivedFrom(KMemoryRegionType_Dram)) { + GetVirtualLinearMemoryRegionTree().InsertDirectly( + region.GetAddress(), region.GetLastAddress(), region.GetAttributes(), + region.GetType()); + } + } +} + +size_t KMemoryLayout::GetResourceRegionSizeForInit() { + // Calculate resource region size based on whether we allow extra threads. + const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit(); + size_t resource_region_size = + KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0); + + return resource_region_size; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h index 0821d2d8c..288642d9a 100644 --- a/src/core/hle/kernel/k_memory_layout.h +++ b/src/core/hle/kernel/k_memory_layout.h @@ -1,23 +1,69 @@ -// Copyright 2020 yuzu Emulator Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once +#include <utility> + +#include "common/alignment.h" +#include "common/common_sizes.h" #include "common/common_types.h" #include "core/device_memory.h" +#include "core/hle/kernel/k_memory_region.h" +#include "core/hle/kernel/k_memory_region_type.h" +#include "core/hle/kernel/memory_types.h" namespace Kernel { -constexpr std::size_t KernelAslrAlignment = 2 * 1024 * 1024; +constexpr std::size_t L1BlockSize = Common::Size_1_GB; +constexpr std::size_t L2BlockSize = Common::Size_2_MB; + +constexpr std::size_t GetMaximumOverheadSize(std::size_t size) { + return (Common::DivideUp(size, L1BlockSize) + Common::DivideUp(size, L2BlockSize)) * PageSize; +} + +constexpr std::size_t MainMemorySize = Common::Size_4_GB; +constexpr std::size_t MainMemorySizeMax = Common::Size_8_GB; + +constexpr std::size_t ReservedEarlyDramSize = 0x60000; +constexpr std::size_t DramPhysicalAddress = 0x80000000; + +constexpr std::size_t KernelAslrAlignment = Common::Size_2_MB; constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39; constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48; + constexpr std::size_t KernelVirtualAddressSpaceBase = 0ULL - KernelVirtualAddressSpaceWidth; constexpr std::size_t KernelVirtualAddressSpaceEnd = KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment); -constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1; +constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1ULL; constexpr std::size_t KernelVirtualAddressSpaceSize = KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase; +constexpr std::size_t KernelVirtualAddressCodeBase = KernelVirtualAddressSpaceBase; +constexpr std::size_t KernelVirtualAddressCodeSize = 0x62000; +constexpr std::size_t KernelVirtualAddressCodeEnd = + KernelVirtualAddressCodeBase + KernelVirtualAddressCodeSize; + +constexpr std::size_t KernelPhysicalAddressSpaceBase = 0ULL; +constexpr std::size_t KernelPhysicalAddressSpaceEnd = + KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceWidth; +constexpr std::size_t KernelPhysicalAddressSpaceLast = KernelPhysicalAddressSpaceEnd - 1ULL; +constexpr std::size_t KernelPhysicalAddressSpaceSize = + KernelPhysicalAddressSpaceEnd - KernelPhysicalAddressSpaceBase; +constexpr std::size_t KernelPhysicalAddressCodeBase = DramPhysicalAddress + ReservedEarlyDramSize; + +constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemorySizeMax); +constexpr std::size_t KernelInitialPageHeapSize = Common::Size_128_KB; + +constexpr std::size_t KernelSlabHeapDataSize = Common::Size_5_MB; +constexpr std::size_t KernelSlabHeapGapsSize = Common::Size_2_MB - Common::Size_64_KB; +constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize; + +// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. +constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000ULL; + +constexpr std::size_t KernelResourceSize = + KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize; constexpr bool IsKernelAddressKey(VAddr key) { return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast; @@ -27,64 +73,327 @@ constexpr bool IsKernelAddress(VAddr address) { return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd; } -class KMemoryRegion final { - friend class KMemoryLayout; - +class KMemoryLayout final { public: - constexpr PAddr StartAddress() const { - return start_address; + KMemoryLayout(); + + KMemoryRegionTree& GetVirtualMemoryRegionTree() { + return virtual_tree; + } + const KMemoryRegionTree& GetVirtualMemoryRegionTree() const { + return virtual_tree; + } + KMemoryRegionTree& GetPhysicalMemoryRegionTree() { + return physical_tree; + } + const KMemoryRegionTree& GetPhysicalMemoryRegionTree() const { + return physical_tree; + } + KMemoryRegionTree& GetVirtualLinearMemoryRegionTree() { + return virtual_linear_tree; + } + const KMemoryRegionTree& GetVirtualLinearMemoryRegionTree() const { + return virtual_linear_tree; + } + KMemoryRegionTree& GetPhysicalLinearMemoryRegionTree() { + return physical_linear_tree; + } + const KMemoryRegionTree& GetPhysicalLinearMemoryRegionTree() const { + return physical_linear_tree; + } + + VAddr GetLinearVirtualAddress(PAddr address) const { + return address + linear_phys_to_virt_diff; + } + PAddr GetLinearPhysicalAddress(VAddr address) const { + return address + linear_virt_to_phys_diff; + } + + const KMemoryRegion* FindVirtual(VAddr address) const { + return Find(address, GetVirtualMemoryRegionTree()); + } + const KMemoryRegion* FindPhysical(PAddr address) const { + return Find(address, GetPhysicalMemoryRegionTree()); + } + + const KMemoryRegion* FindVirtualLinear(VAddr address) const { + return Find(address, GetVirtualLinearMemoryRegionTree()); + } + const KMemoryRegion* FindPhysicalLinear(PAddr address) const { + return Find(address, GetPhysicalLinearMemoryRegionTree()); + } + + VAddr GetMainStackTopAddress(s32 core_id) const { + return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscMainStack); + } + VAddr GetIdleStackTopAddress(s32 core_id) const { + return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscIdleStack); + } + VAddr GetExceptionStackTopAddress(s32 core_id) const { + return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack); + } + + VAddr GetSlabRegionAddress() const { + return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab)) + .GetAddress(); + } + + const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const { + return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type)); + } + PAddr GetDevicePhysicalAddress(KMemoryRegionType type) const { + return GetDeviceRegion(type).GetAddress(); + } + VAddr GetDeviceVirtualAddress(KMemoryRegionType type) const { + return GetDeviceRegion(type).GetPairAddress(); + } + + const KMemoryRegion& GetPoolManagementRegion() const { + return Dereference( + GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramPoolManagement)); + } + const KMemoryRegion& GetPageTableHeapRegion() const { + return Dereference( + GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramKernelPtHeap)); + } + const KMemoryRegion& GetKernelStackRegion() const { + return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelStack)); + } + const KMemoryRegion& GetTempRegion() const { + return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelTemp)); + } + + const KMemoryRegion& GetKernelTraceBufferRegion() const { + return Dereference(GetVirtualLinearMemoryRegionTree().FindByType( + KMemoryRegionType_VirtualDramKernelTraceBuffer)); + } + + const KMemoryRegion& GetVirtualLinearRegion(VAddr address) const { + return Dereference(FindVirtualLinear(address)); + } + + const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const { + return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer); + } + const KMemoryRegion* GetPhysicalOnMemoryBootImageRegion() const { + return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_OnMemoryBootImage); + } + const KMemoryRegion* GetPhysicalDTBRegion() const { + return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DTB); + } + + bool IsHeapPhysicalAddress(const KMemoryRegion*& region, PAddr address) const { + return IsTypedAddress(region, address, GetPhysicalLinearMemoryRegionTree(), + KMemoryRegionType_DramUserPool); + } + bool IsHeapVirtualAddress(const KMemoryRegion*& region, VAddr address) const { + return IsTypedAddress(region, address, GetVirtualLinearMemoryRegionTree(), + KMemoryRegionType_VirtualDramUserPool); + } + + bool IsHeapPhysicalAddress(const KMemoryRegion*& region, PAddr address, size_t size) const { + return IsTypedAddress(region, address, size, GetPhysicalLinearMemoryRegionTree(), + KMemoryRegionType_DramUserPool); + } + bool IsHeapVirtualAddress(const KMemoryRegion*& region, VAddr address, size_t size) const { + return IsTypedAddress(region, address, size, GetVirtualLinearMemoryRegionTree(), + KMemoryRegionType_VirtualDramUserPool); + } + + bool IsLinearMappedPhysicalAddress(const KMemoryRegion*& region, PAddr address) const { + return IsTypedAddress(region, address, GetPhysicalLinearMemoryRegionTree(), + static_cast<KMemoryRegionType>(KMemoryRegionAttr_LinearMapped)); + } + bool IsLinearMappedPhysicalAddress(const KMemoryRegion*& region, PAddr address, + size_t size) const { + return IsTypedAddress(region, address, size, GetPhysicalLinearMemoryRegionTree(), + static_cast<KMemoryRegionType>(KMemoryRegionAttr_LinearMapped)); + } + + std::pair<size_t, size_t> GetTotalAndKernelMemorySizes() const { + size_t total_size = 0, kernel_size = 0; + for (const auto& region : GetPhysicalMemoryRegionTree()) { + if (region.IsDerivedFrom(KMemoryRegionType_Dram)) { + total_size += region.GetSize(); + if (!region.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { + kernel_size += region.GetSize(); + } + } + } + return std::make_pair(total_size, kernel_size); + } + + void InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start, + VAddr linear_virtual_start); + static size_t GetResourceRegionSizeForInit(); + + auto GetKernelRegionExtents() const { + return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel); + } + auto GetKernelCodeRegionExtents() const { + return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelCode); + } + auto GetKernelStackRegionExtents() const { + return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelStack); + } + auto GetKernelMiscRegionExtents() const { + return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelMisc); + } + auto GetKernelSlabRegionExtents() const { + return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelSlab); + } + + auto GetLinearRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionAttr_LinearMapped); + } + + auto GetLinearRegionVirtualExtents() const { + const auto physical = GetLinearRegionPhysicalExtents(); + return KMemoryRegion(GetLinearVirtualAddress(physical.GetAddress()), + GetLinearVirtualAddress(physical.GetLastAddress()), 0, + KMemoryRegionType_None); + } + + auto GetMainMemoryPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Dram); + } + auto GetCarveoutRegionExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionAttr_CarveoutProtected); + } + + auto GetKernelRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_DramKernelBase); + } + auto GetKernelCodeRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_DramKernelCode); + } + auto GetKernelSlabRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_DramKernelSlab); + } + auto GetKernelPageTableHeapRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_DramKernelPtHeap); + } + auto GetKernelInitPageTableRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_DramKernelInitPt); + } + + auto GetKernelPoolManagementRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_DramPoolManagement); + } + auto GetKernelPoolPartitionRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_DramPoolPartition); + } + auto GetKernelSystemPoolRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_DramSystemPool); + } + auto GetKernelSystemNonSecurePoolRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_DramSystemNonSecurePool); + } + auto GetKernelAppletPoolRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_DramAppletPool); + } + auto GetKernelApplicationPoolRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_DramApplicationPool); } - constexpr PAddr EndAddress() const { - return end_address; + auto GetKernelTraceBufferRegionPhysicalExtents() const { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_KernelTraceBuffer); } private: - constexpr KMemoryRegion() = default; - constexpr KMemoryRegion(PAddr start_address, PAddr end_address) - : start_address{start_address}, end_address{end_address} {} + template <typename AddressType> + static bool IsTypedAddress(const KMemoryRegion*& region, AddressType address, + const KMemoryRegionTree& tree, KMemoryRegionType type) { + // Check if the cached region already contains the address. + if (region != nullptr && region->Contains(address)) { + return true; + } - const PAddr start_address{}; - const PAddr end_address{}; -}; + // Find the containing region, and update the cache. + if (const KMemoryRegion* found = tree.Find(address); + found != nullptr && found->IsDerivedFrom(type)) { + region = found; + return true; + } else { + return false; + } + } -class KMemoryLayout final { -public: - constexpr const KMemoryRegion& Application() const { - return application; + template <typename AddressType> + static bool IsTypedAddress(const KMemoryRegion*& region, AddressType address, size_t size, + const KMemoryRegionTree& tree, KMemoryRegionType type) { + // Get the end of the checked region. + const u64 last_address = address + size - 1; + + // Walk the tree to verify the region is correct. + const KMemoryRegion* cur = + (region != nullptr && region->Contains(address)) ? region : tree.Find(address); + while (cur != nullptr && cur->IsDerivedFrom(type)) { + if (last_address <= cur->GetLastAddress()) { + region = cur; + return true; + } + + cur = cur->GetNext(); + } + return false; } - constexpr const KMemoryRegion& Applet() const { - return applet; + template <typename AddressType> + static const KMemoryRegion* Find(AddressType address, const KMemoryRegionTree& tree) { + return tree.Find(address); } - constexpr const KMemoryRegion& System() const { - return system; + static KMemoryRegion& Dereference(KMemoryRegion* region) { + ASSERT(region != nullptr); + return *region; } - static constexpr KMemoryLayout GetDefaultLayout() { - constexpr std::size_t application_size{0xcd500000}; - constexpr std::size_t applet_size{0x1fb00000}; - constexpr PAddr application_start_address{Core::DramMemoryMap::End - application_size}; - constexpr PAddr application_end_address{Core::DramMemoryMap::End}; - constexpr PAddr applet_start_address{application_start_address - applet_size}; - constexpr PAddr applet_end_address{applet_start_address + applet_size}; - constexpr PAddr system_start_address{Core::DramMemoryMap::SlabHeapEnd}; - constexpr PAddr system_end_address{applet_start_address}; - return {application_start_address, application_end_address, applet_start_address, - applet_end_address, system_start_address, system_end_address}; + static const KMemoryRegion& Dereference(const KMemoryRegion* region) { + ASSERT(region != nullptr); + return *region; + } + + VAddr GetStackTopAddress(s32 core_id, KMemoryRegionType type) const { + const auto& region = Dereference( + GetVirtualMemoryRegionTree().FindByTypeAndAttribute(type, static_cast<u32>(core_id))); + ASSERT(region.GetEndAddress() != 0); + return region.GetEndAddress(); } private: - constexpr KMemoryLayout(PAddr application_start_address, std::size_t application_size, - PAddr applet_start_address, std::size_t applet_size, - PAddr system_start_address, std::size_t system_size) - : application{application_start_address, application_size}, - applet{applet_start_address, applet_size}, system{system_start_address, system_size} {} - - const KMemoryRegion application; - const KMemoryRegion applet; - const KMemoryRegion system; + u64 linear_phys_to_virt_diff{}; + u64 linear_virt_to_phys_diff{}; + KMemoryRegionAllocator memory_region_allocator; + KMemoryRegionTree virtual_tree; + KMemoryRegionTree physical_tree; + KMemoryRegionTree virtual_linear_tree; + KMemoryRegionTree physical_linear_tree; }; +namespace Init { + +// These should be generic, regardless of board. +void SetupPoolPartitionMemoryRegions(KMemoryLayout& memory_layout); + +// These may be implemented in a board-specific manner. +void SetupDevicePhysicalMemoryRegions(KMemoryLayout& memory_layout); +void SetupDramPhysicalMemoryRegions(KMemoryLayout& memory_layout); + +} // namespace Init + } // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index 9027602bf..aa71697b2 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp @@ -173,4 +173,16 @@ ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_page return RESULT_SUCCESS; } +std::size_t KMemoryManager::Impl::CalculateManagementOverheadSize(std::size_t region_size) { + const std::size_t ref_count_size = (region_size / PageSize) * sizeof(u16); + const std::size_t optimize_map_size = + (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) / + Common::BitSize<u64>()) * + sizeof(u64); + const std::size_t manager_meta_size = + Common::AlignUp(optimize_map_size + ref_count_size, PageSize); + const std::size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size); + return manager_meta_size + page_heap_size; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h index ae9f683b8..ac840b3d0 100644 --- a/src/core/hle/kernel/k_memory_manager.h +++ b/src/core/hle/kernel/k_memory_manager.h @@ -29,6 +29,10 @@ public: Shift = 4, Mask = (0xF << Shift), + + // Aliases. + Unsafe = Application, + Secure = System, }; enum class Direction : u32 { @@ -56,6 +60,10 @@ public: static constexpr std::size_t MaxManagerCount = 10; public: + static std::size_t CalculateManagementOverheadSize(std::size_t region_size) { + return Impl::CalculateManagementOverheadSize(region_size); + } + static constexpr u32 EncodeOption(Pool pool, Direction dir) { return (static_cast<u32>(pool) << static_cast<u32>(Pool::Shift)) | (static_cast<u32>(dir) << static_cast<u32>(Direction::Shift)); @@ -86,6 +94,16 @@ private: Pool pool{}; public: + static std::size_t CalculateManagementOverheadSize(std::size_t region_size); + + static constexpr std::size_t CalculateOptimizedProcessOverheadSize( + std::size_t region_size) { + return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) / + Common::BitSize<u64>()) * + sizeof(u64); + } + + public: Impl() = default; std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address); diff --git a/src/core/hle/kernel/k_memory_region.h b/src/core/hle/kernel/k_memory_region.h new file mode 100644 index 000000000..a861c04ab --- /dev/null +++ b/src/core/hle/kernel/k_memory_region.h @@ -0,0 +1,350 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/intrusive_red_black_tree.h" +#include "core/hle/kernel/k_memory_region_type.h" + +namespace Kernel { + +class KMemoryRegionAllocator; + +class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion>, + NonCopyable { + friend class KMemoryRegionTree; + +public: + constexpr KMemoryRegion() = default; + constexpr KMemoryRegion(u64 address_, u64 last_address_) + : address{address_}, last_address{last_address_} {} + constexpr KMemoryRegion(u64 address_, u64 last_address_, u64 pair_address_, u32 attributes_, + u32 type_id_) + : address(address_), last_address(last_address_), pair_address(pair_address_), + attributes(attributes_), type_id(type_id_) {} + constexpr KMemoryRegion(u64 address_, u64 last_address_, u32 attributes_, u32 type_id_) + : KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_, + type_id_) {} + + static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) { + if (lhs.GetAddress() < rhs.GetAddress()) { + return -1; + } else if (lhs.GetAddress() <= rhs.GetLastAddress()) { + return 0; + } else { + return 1; + } + } + +private: + constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) { + address = a; + pair_address = p; + last_address = la; + attributes = r; + type_id = t; + } + +public: + constexpr u64 GetAddress() const { + return address; + } + + constexpr u64 GetPairAddress() const { + return pair_address; + } + + constexpr u64 GetLastAddress() const { + return last_address; + } + + constexpr u64 GetEndAddress() const { + return this->GetLastAddress() + 1; + } + + constexpr size_t GetSize() const { + return this->GetEndAddress() - this->GetAddress(); + } + + constexpr u32 GetAttributes() const { + return attributes; + } + + constexpr u32 GetType() const { + return type_id; + } + + constexpr void SetType(u32 type) { + ASSERT(this->CanDerive(type)); + type_id = type; + } + + constexpr bool Contains(u64 address) const { + ASSERT(this->GetEndAddress() != 0); + return this->GetAddress() <= address && address <= this->GetLastAddress(); + } + + constexpr bool IsDerivedFrom(u32 type) const { + return (this->GetType() | type) == this->GetType(); + } + + constexpr bool HasTypeAttribute(u32 attr) const { + return (this->GetType() | attr) == this->GetType(); + } + + constexpr bool CanDerive(u32 type) const { + return (this->GetType() | type) == type; + } + + constexpr void SetPairAddress(u64 a) { + pair_address = a; + } + + constexpr void SetTypeAttribute(u32 attr) { + type_id |= attr; + } + +private: + u64 address{}; + u64 last_address{}; + u64 pair_address{}; + u32 attributes{}; + u32 type_id{}; +}; + +class KMemoryRegionTree final : NonCopyable { +public: + struct DerivedRegionExtents { + const KMemoryRegion* first_region{}; + const KMemoryRegion* last_region{}; + + constexpr DerivedRegionExtents() = default; + + constexpr u64 GetAddress() const { + return this->first_region->GetAddress(); + } + + constexpr u64 GetLastAddress() const { + return this->last_region->GetLastAddress(); + } + + constexpr u64 GetEndAddress() const { + return this->GetLastAddress() + 1; + } + + constexpr size_t GetSize() const { + return this->GetEndAddress() - this->GetAddress(); + } + }; + +private: + using TreeType = + Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>; + +public: + using value_type = TreeType::value_type; + using size_type = TreeType::size_type; + using difference_type = TreeType::difference_type; + using pointer = TreeType::pointer; + using const_pointer = TreeType::const_pointer; + using reference = TreeType::reference; + using const_reference = TreeType::const_reference; + using iterator = TreeType::iterator; + using const_iterator = TreeType::const_iterator; + +private: + TreeType m_tree{}; + KMemoryRegionAllocator& memory_region_allocator; + +public: + explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_); + +public: + KMemoryRegion* FindModifiable(u64 address) { + if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) { + return std::addressof(*it); + } else { + return nullptr; + } + } + + const KMemoryRegion* Find(u64 address) const { + if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->cend()) { + return std::addressof(*it); + } else { + return nullptr; + } + } + + const KMemoryRegion* FindByType(KMemoryRegionType type_id) const { + for (auto it = this->cbegin(); it != this->cend(); ++it) { + if (it->GetType() == static_cast<u32>(type_id)) { + return std::addressof(*it); + } + } + return nullptr; + } + + const KMemoryRegion* FindByTypeAndAttribute(u32 type_id, u32 attr) const { + for (auto it = this->cbegin(); it != this->cend(); ++it) { + if (it->GetType() == type_id && it->GetAttributes() == attr) { + return std::addressof(*it); + } + } + return nullptr; + } + + const KMemoryRegion* FindFirstDerived(KMemoryRegionType type_id) const { + for (auto it = this->cbegin(); it != this->cend(); it++) { + if (it->IsDerivedFrom(type_id)) { + return std::addressof(*it); + } + } + return nullptr; + } + + const KMemoryRegion* FindLastDerived(KMemoryRegionType type_id) const { + const KMemoryRegion* region = nullptr; + for (auto it = this->begin(); it != this->end(); it++) { + if (it->IsDerivedFrom(type_id)) { + region = std::addressof(*it); + } + } + return region; + } + + DerivedRegionExtents GetDerivedRegionExtents(KMemoryRegionType type_id) const { + DerivedRegionExtents extents; + + ASSERT(extents.first_region == nullptr); + ASSERT(extents.last_region == nullptr); + + for (auto it = this->cbegin(); it != this->cend(); it++) { + if (it->IsDerivedFrom(type_id)) { + if (extents.first_region == nullptr) { + extents.first_region = std::addressof(*it); + } + extents.last_region = std::addressof(*it); + } + } + + ASSERT(extents.first_region != nullptr); + ASSERT(extents.last_region != nullptr); + + return extents; + } + + DerivedRegionExtents GetDerivedRegionExtents(u32 type_id) const { + return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id)); + } + +public: + void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0); + bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0); + + VAddr GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id); + + VAddr GetRandomAlignedRegionWithGuard(size_t size, size_t alignment, u32 type_id, + size_t guard_size) { + return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size; + } + +public: + // Iterator accessors. + iterator begin() { + return m_tree.begin(); + } + + const_iterator begin() const { + return m_tree.begin(); + } + + iterator end() { + return m_tree.end(); + } + + const_iterator end() const { + return m_tree.end(); + } + + const_iterator cbegin() const { + return this->begin(); + } + + const_iterator cend() const { + return this->end(); + } + + iterator iterator_to(reference ref) { + return m_tree.iterator_to(ref); + } + + const_iterator iterator_to(const_reference ref) const { + return m_tree.iterator_to(ref); + } + + // Content management. + bool empty() const { + return m_tree.empty(); + } + + reference back() { + return m_tree.back(); + } + + const_reference back() const { + return m_tree.back(); + } + + reference front() { + return m_tree.front(); + } + + const_reference front() const { + return m_tree.front(); + } + + iterator insert(reference ref) { + return m_tree.insert(ref); + } + + iterator erase(iterator it) { + return m_tree.erase(it); + } + + iterator find(const_reference ref) const { + return m_tree.find(ref); + } + + iterator nfind(const_reference ref) const { + return m_tree.nfind(ref); + } +}; + +class KMemoryRegionAllocator final : NonCopyable { +public: + static constexpr size_t MaxMemoryRegions = 200; + + constexpr KMemoryRegionAllocator() = default; + + template <typename... Args> + KMemoryRegion* Allocate(Args&&... args) { + // Ensure we stay within the bounds of our heap. + ASSERT(this->num_regions < MaxMemoryRegions); + + // Create the new region. + KMemoryRegion* region = std::addressof(this->region_heap[this->num_regions++]); + new (region) KMemoryRegion(std::forward<Args>(args)...); + + return region; + } + +private: + std::array<KMemoryRegion, MaxMemoryRegions> region_heap{}; + size_t num_regions{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_region_type.h b/src/core/hle/kernel/k_memory_region_type.h new file mode 100644 index 000000000..a05e66677 --- /dev/null +++ b/src/core/hle/kernel/k_memory_region_type.h @@ -0,0 +1,338 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/bit_util.h" +#include "common/common_funcs.h" +#include "common/common_types.h" + +#define ARCH_ARM64 +#define BOARD_NINTENDO_NX + +namespace Kernel { + +enum KMemoryRegionType : u32 { + KMemoryRegionAttr_CarveoutProtected = 0x04000000, + KMemoryRegionAttr_DidKernelMap = 0x08000000, + KMemoryRegionAttr_ShouldKernelMap = 0x10000000, + KMemoryRegionAttr_UserReadOnly = 0x20000000, + KMemoryRegionAttr_NoUserMap = 0x40000000, + KMemoryRegionAttr_LinearMapped = 0x80000000, +}; +DECLARE_ENUM_FLAG_OPERATORS(KMemoryRegionType); + +namespace impl { + +constexpr size_t BitsForDeriveSparse(size_t n) { + return n + 1; +} + +constexpr size_t BitsForDeriveDense(size_t n) { + size_t low = 0, high = 1; + for (size_t i = 0; i < n - 1; ++i) { + if ((++low) == high) { + ++high; + low = 0; + } + } + return high + 1; +} + +class KMemoryRegionTypeValue { +public: + using ValueType = std::underlying_type_t<KMemoryRegionType>; + + constexpr KMemoryRegionTypeValue() = default; + + constexpr operator KMemoryRegionType() const { + return static_cast<KMemoryRegionType>(m_value); + } + + constexpr ValueType GetValue() const { + return m_value; + } + + constexpr const KMemoryRegionTypeValue& Finalize() { + m_finalized = true; + return *this; + } + + constexpr const KMemoryRegionTypeValue& SetSparseOnly() { + m_sparse_only = true; + return *this; + } + + constexpr const KMemoryRegionTypeValue& SetDenseOnly() { + m_dense_only = true; + return *this; + } + + constexpr KMemoryRegionTypeValue& SetAttribute(u32 attr) { + m_value |= attr; + return *this; + } + + constexpr KMemoryRegionTypeValue DeriveInitial( + size_t i, size_t next = Common::BitSize<ValueType>()) const { + KMemoryRegionTypeValue new_type = *this; + new_type.m_value = (ValueType{1} << i); + new_type.m_next_bit = next; + return new_type; + } + + constexpr KMemoryRegionTypeValue DeriveAttribute(u32 attr) const { + KMemoryRegionTypeValue new_type = *this; + new_type.m_value |= attr; + return new_type; + } + + constexpr KMemoryRegionTypeValue DeriveTransition(size_t ofs = 0, size_t adv = 1) const { + KMemoryRegionTypeValue new_type = *this; + new_type.m_value |= (ValueType{1} << (m_next_bit + ofs)); + new_type.m_next_bit += adv; + return new_type; + } + + constexpr KMemoryRegionTypeValue DeriveSparse(size_t ofs, size_t n, size_t i) const { + KMemoryRegionTypeValue new_type = *this; + new_type.m_value |= (ValueType{1} << (m_next_bit + ofs)); + new_type.m_value |= (ValueType{1} << (m_next_bit + ofs + 1 + i)); + new_type.m_next_bit += ofs + n + 1; + return new_type; + } + + constexpr KMemoryRegionTypeValue Derive(size_t n, size_t i) const { + size_t low = 0, high = 1; + for (size_t j = 0; j < i; ++j) { + if ((++low) == high) { + ++high; + low = 0; + } + } + + KMemoryRegionTypeValue new_type = *this; + new_type.m_value |= (ValueType{1} << (m_next_bit + low)); + new_type.m_value |= (ValueType{1} << (m_next_bit + high)); + new_type.m_next_bit += BitsForDeriveDense(n); + return new_type; + } + + constexpr KMemoryRegionTypeValue Advance(size_t n) const { + KMemoryRegionTypeValue new_type = *this; + new_type.m_next_bit += n; + return new_type; + } + + constexpr bool IsAncestorOf(ValueType v) const { + return (m_value | v) == v; + } + +private: + constexpr KMemoryRegionTypeValue(ValueType v) : m_value(v) {} + +private: + ValueType m_value{}; + size_t m_next_bit{}; + bool m_finalized{}; + bool m_sparse_only{}; + bool m_dense_only{}; +}; + +} // namespace impl + +constexpr auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue(); +constexpr auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2); +constexpr auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2); +static_assert(KMemoryRegionType_Kernel.GetValue() == 0x1); +static_assert(KMemoryRegionType_Dram.GetValue() == 0x2); + +constexpr auto KMemoryRegionType_DramKernelBase = + KMemoryRegionType_Dram.DeriveSparse(0, 3, 0) + .SetAttribute(KMemoryRegionAttr_NoUserMap) + .SetAttribute(KMemoryRegionAttr_CarveoutProtected); +constexpr auto KMemoryRegionType_DramReservedBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 1); +constexpr auto KMemoryRegionType_DramHeapBase = + KMemoryRegionType_Dram.DeriveSparse(0, 3, 2).SetAttribute(KMemoryRegionAttr_LinearMapped); +static_assert(KMemoryRegionType_DramKernelBase.GetValue() == + (0xE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_DramReservedBase.GetValue() == (0x16)); +static_assert(KMemoryRegionType_DramHeapBase.GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped)); + +constexpr auto KMemoryRegionType_DramKernelCode = + KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 0); +constexpr auto KMemoryRegionType_DramKernelSlab = + KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 1); +constexpr auto KMemoryRegionType_DramKernelPtHeap = + KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 2).SetAttribute( + KMemoryRegionAttr_LinearMapped); +constexpr auto KMemoryRegionType_DramKernelInitPt = + KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 3).SetAttribute( + KMemoryRegionAttr_LinearMapped); +static_assert(KMemoryRegionType_DramKernelCode.GetValue() == + (0xCE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_DramKernelSlab.GetValue() == + (0x14E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_DramKernelPtHeap.GetValue() == + (0x24E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap | + KMemoryRegionAttr_LinearMapped)); +static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() == + (0x44E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap | + KMemoryRegionAttr_LinearMapped)); + +constexpr auto KMemoryRegionType_DramReservedEarly = + KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap); +static_assert(KMemoryRegionType_DramReservedEarly.GetValue() == + (0x16 | KMemoryRegionAttr_NoUserMap)); + +constexpr auto KMemoryRegionType_KernelTraceBuffer = + KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 0) + .SetAttribute(KMemoryRegionAttr_LinearMapped) + .SetAttribute(KMemoryRegionAttr_UserReadOnly); +constexpr auto KMemoryRegionType_OnMemoryBootImage = + KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 1); +constexpr auto KMemoryRegionType_DTB = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2); +static_assert(KMemoryRegionType_KernelTraceBuffer.GetValue() == + (0xD6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_UserReadOnly)); +static_assert(KMemoryRegionType_OnMemoryBootImage.GetValue() == 0x156); +static_assert(KMemoryRegionType_DTB.GetValue() == 0x256); + +constexpr auto KMemoryRegionType_DramPoolPartition = + KMemoryRegionType_DramHeapBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap); +static_assert(KMemoryRegionType_DramPoolPartition.GetValue() == + (0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); + +constexpr auto KMemoryRegionType_DramPoolManagement = + KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute( + KMemoryRegionAttr_CarveoutProtected); +constexpr auto KMemoryRegionType_DramUserPool = + KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition(); +static_assert(KMemoryRegionType_DramPoolManagement.GetValue() == + (0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap | + KMemoryRegionAttr_CarveoutProtected)); +static_assert(KMemoryRegionType_DramUserPool.GetValue() == + (0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); + +constexpr auto KMemoryRegionType_DramApplicationPool = KMemoryRegionType_DramUserPool.Derive(4, 0); +constexpr auto KMemoryRegionType_DramAppletPool = KMemoryRegionType_DramUserPool.Derive(4, 1); +constexpr auto KMemoryRegionType_DramSystemNonSecurePool = + KMemoryRegionType_DramUserPool.Derive(4, 2); +constexpr auto KMemoryRegionType_DramSystemPool = + KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected); +static_assert(KMemoryRegionType_DramApplicationPool.GetValue() == + (0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_DramAppletPool.GetValue() == + (0xBA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() == + (0xDA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_DramSystemPool.GetValue() == + (0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap | + KMemoryRegionAttr_CarveoutProtected)); + +constexpr auto KMemoryRegionType_VirtualDramHeapBase = KMemoryRegionType_Dram.DeriveSparse(1, 3, 0); +constexpr auto KMemoryRegionType_VirtualDramKernelPtHeap = + KMemoryRegionType_Dram.DeriveSparse(1, 3, 1); +constexpr auto KMemoryRegionType_VirtualDramKernelTraceBuffer = + KMemoryRegionType_Dram.DeriveSparse(1, 3, 2); +static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A); +static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A); +static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); + +constexpr auto KMemoryRegionType_VirtualDramKernelInitPt = + KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); +constexpr auto KMemoryRegionType_VirtualDramPoolManagement = + KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1); +constexpr auto KMemoryRegionType_VirtualDramUserPool = + KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2); +static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A); +static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A); +static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A); + +// NOTE: For unknown reason, the pools are derived out-of-order here. It's worth eventually trying +// to understand why Nintendo made this choice. +// UNUSED: .Derive(6, 0); +// UNUSED: .Derive(6, 1); +constexpr auto KMemoryRegionType_VirtualDramAppletPool = + KMemoryRegionType_VirtualDramUserPool.Derive(6, 2); +constexpr auto KMemoryRegionType_VirtualDramApplicationPool = + KMemoryRegionType_VirtualDramUserPool.Derive(6, 3); +constexpr auto KMemoryRegionType_VirtualDramSystemNonSecurePool = + KMemoryRegionType_VirtualDramUserPool.Derive(6, 4); +constexpr auto KMemoryRegionType_VirtualDramSystemPool = + KMemoryRegionType_VirtualDramUserPool.Derive(6, 5); +static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A); +static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A); +static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A); +static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A); + +constexpr auto KMemoryRegionType_ArchDeviceBase = + KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly(); +constexpr auto KMemoryRegionType_BoardDeviceBase = + KMemoryRegionType_Kernel.DeriveTransition(0, 2).SetDenseOnly(); +static_assert(KMemoryRegionType_ArchDeviceBase.GetValue() == 0x5); +static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5); + +#if defined(ARCH_ARM64) +#include "core/hle/kernel/arch/arm64/k_memory_region_device_types.inc" +#elif defined(ARCH_ARM) +#error "Unimplemented" +#else +// Default to no architecture devices. +constexpr auto NumArchitectureDeviceRegions = 0; +#endif +static_assert(NumArchitectureDeviceRegions >= 0); + +#if defined(BOARD_NINTENDO_NX) +#include "core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc" +#else +// Default to no board devices. +constexpr auto NumBoardDeviceRegions = 0; +#endif +static_assert(NumBoardDeviceRegions >= 0); + +constexpr auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0); +constexpr auto KMemoryRegionType_KernelStack = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1); +constexpr auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2); +constexpr auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3); +static_assert(KMemoryRegionType_KernelCode.GetValue() == 0x19); +static_assert(KMemoryRegionType_KernelStack.GetValue() == 0x29); +static_assert(KMemoryRegionType_KernelMisc.GetValue() == 0x49); +static_assert(KMemoryRegionType_KernelSlab.GetValue() == 0x89); + +constexpr auto KMemoryRegionType_KernelMiscDerivedBase = + KMemoryRegionType_KernelMisc.DeriveTransition(); +static_assert(KMemoryRegionType_KernelMiscDerivedBase.GetValue() == 0x149); + +// UNUSED: .Derive(7, 0); +constexpr auto KMemoryRegionType_KernelMiscMainStack = + KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 1); +constexpr auto KMemoryRegionType_KernelMiscMappedDevice = + KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 2); +constexpr auto KMemoryRegionType_KernelMiscExceptionStack = + KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 3); +constexpr auto KMemoryRegionType_KernelMiscUnknownDebug = + KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 4); +// UNUSED: .Derive(7, 5); +constexpr auto KMemoryRegionType_KernelMiscIdleStack = + KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 6); +static_assert(KMemoryRegionType_KernelMiscMainStack.GetValue() == 0xB49); +static_assert(KMemoryRegionType_KernelMiscMappedDevice.GetValue() == 0xD49); +static_assert(KMemoryRegionType_KernelMiscExceptionStack.GetValue() == 0x1349); +static_assert(KMemoryRegionType_KernelMiscUnknownDebug.GetValue() == 0x1549); +static_assert(KMemoryRegionType_KernelMiscIdleStack.GetValue() == 0x2349); + +constexpr auto KMemoryRegionType_KernelTemp = KMemoryRegionType_Kernel.Advance(2).Derive(2, 0); +static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31); + +constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) { + if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) { + return KMemoryRegionType_VirtualDramKernelTraceBuffer; + } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) { + return KMemoryRegionType_VirtualDramKernelPtHeap; + } else { + return KMemoryRegionType_Dram; + } +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp index d7a4a38e6..d05b34ea3 100644 --- a/src/core/hle/kernel/k_resource_limit.cpp +++ b/src/core/hle/kernel/k_resource_limit.cpp @@ -2,21 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. - #include "common/assert.h" -#include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/svc_results.h" namespace Kernel { constexpr s64 DefaultTimeout = 10000000000; // 10 seconds -KResourceLimit::KResourceLimit(KernelCore& kernel, Core::System& system) - : Object{kernel}, lock{kernel}, cond_var{kernel}, kernel{kernel}, system(system) {} +KResourceLimit::KResourceLimit(KernelCore& kernel, const Core::Timing::CoreTiming& core_timing_) + : Object{kernel}, lock{kernel}, cond_var{kernel}, core_timing(core_timing_) {} KResourceLimit::~KResourceLimit() = default; s64 KResourceLimit::GetLimitValue(LimitableResource which) const { @@ -83,7 +78,7 @@ ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) { } bool KResourceLimit::Reserve(LimitableResource which, s64 value) { - return Reserve(which, value, system.CoreTiming().GetGlobalTimeNs().count() + DefaultTimeout); + return Reserve(which, value, core_timing.GetGlobalTimeNs().count() + DefaultTimeout); } bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) { @@ -114,7 +109,7 @@ bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) { } if (current_hints[index] + value <= limit_values[index] && - (timeout < 0 || system.CoreTiming().GetGlobalTimeNs().count() < timeout)) { + (timeout < 0 || core_timing.GetGlobalTimeNs().count() < timeout)) { waiter_count++; cond_var.Wait(&lock, timeout); waiter_count--; diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h index 58ae456f1..4542317d0 100644 --- a/src/core/hle/kernel/k_resource_limit.h +++ b/src/core/hle/kernel/k_resource_limit.h @@ -2,9 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. - #pragma once #include <array> @@ -15,8 +12,8 @@ union ResultCode; -namespace Core { -class System; +namespace Core::Timing { +class CoreTiming; } namespace Kernel { @@ -37,7 +34,7 @@ constexpr bool IsValidResourceType(LimitableResource type) { class KResourceLimit final : public Object { public: - explicit KResourceLimit(KernelCore& kernel, Core::System& system); + explicit KResourceLimit(KernelCore& kernel, const Core::Timing::CoreTiming& core_timing_); ~KResourceLimit(); s64 GetLimitValue(LimitableResource which) const; @@ -75,7 +72,6 @@ private: mutable KLightLock lock; s32 waiter_count{}; KLightConditionVariable cond_var; - KernelCore& kernel; - Core::System& system; + const Core::Timing::CoreTiming& core_timing; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index e7de48476..d1df97305 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -62,7 +62,7 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul } u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) { - std::scoped_lock lock{guard}; + KScopedSpinLock lk{guard}; if (KThread* prev_highest_thread = state.highest_priority_thread; prev_highest_thread != highest_thread) { if (prev_highest_thread != nullptr) { @@ -637,11 +637,11 @@ void KScheduler::RescheduleCurrentCore() { if (phys_core.IsInterrupted()) { phys_core.ClearInterrupt(); } - guard.lock(); + guard.Lock(); if (state.needs_scheduling.load()) { Schedule(); } else { - guard.unlock(); + guard.Unlock(); } } @@ -669,7 +669,7 @@ void KScheduler::Unload(KThread* thread) { } else { prev_thread = nullptr; } - thread->context_guard.unlock(); + thread->context_guard.Unlock(); } } @@ -713,7 +713,7 @@ void KScheduler::ScheduleImpl() { // If we're not actually switching thread, there's nothing to do. if (next_thread == current_thread.load()) { - guard.unlock(); + guard.Unlock(); return; } @@ -732,7 +732,7 @@ void KScheduler::ScheduleImpl() { } else { old_context = &idle_thread->GetHostContext(); } - guard.unlock(); + guard.Unlock(); Common::Fiber::YieldTo(*old_context, *switch_fiber); /// When a thread wakes up, the scheduler may have changed to other in another core. @@ -748,24 +748,24 @@ void KScheduler::OnSwitch(void* this_scheduler) { void KScheduler::SwitchToCurrent() { while (true) { { - std::scoped_lock lock{guard}; + KScopedSpinLock lk{guard}; current_thread.store(state.highest_priority_thread); state.needs_scheduling.store(false); } const auto is_switch_pending = [this] { - std::scoped_lock lock{guard}; + KScopedSpinLock lk{guard}; return state.needs_scheduling.load(); }; do { auto next_thread = current_thread.load(); if (next_thread != nullptr) { - next_thread->context_guard.lock(); + next_thread->context_guard.Lock(); if (next_thread->GetRawState() != ThreadState::Runnable) { - next_thread->context_guard.unlock(); + next_thread->context_guard.Unlock(); break; } if (next_thread->GetActiveCore() != core_id) { - next_thread->context_guard.unlock(); + next_thread->context_guard.Unlock(); break; } } diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index f595b9a5c..8e32865aa 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -2,19 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. - #pragma once #include <atomic> #include "common/common_types.h" -#include "common/spin_lock.h" #include "core/hle/kernel/global_scheduler_context.h" #include "core/hle/kernel/k_priority_queue.h" #include "core/hle/kernel/k_scheduler_lock.h" #include "core/hle/kernel/k_scoped_lock.h" +#include "core/hle/kernel/k_spin_lock.h" namespace Common { class Fiber; @@ -195,12 +192,12 @@ private: u64 last_context_switch_time{}; const s32 core_id; - Common::SpinLock guard{}; + KSpinLock guard{}; }; -class KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> { +class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> { public: - explicit KScopedSchedulerLock(KernelCore& kernel); + explicit KScopedSchedulerLock(KernelCore & kernel); ~KScopedSchedulerLock(); }; diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 169455d18..47e315555 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h @@ -2,14 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. - #pragma once #include "common/assert.h" -#include "common/spin_lock.h" #include "core/hardware_properties.h" +#include "core/hle/kernel/k_spin_lock.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -34,7 +31,7 @@ public: } else { // Otherwise, we want to disable scheduling and acquire the spinlock. SchedulerType::DisableScheduling(kernel); - spin_lock.lock(); + spin_lock.Lock(); // For debug, ensure that our state is valid. ASSERT(lock_count == 0); @@ -58,7 +55,7 @@ public: // Note that we no longer hold the lock, and unlock the spinlock. owner_thread = nullptr; - spin_lock.unlock(); + spin_lock.Unlock(); // Enable scheduling, and perform a rescheduling operation. SchedulerType::EnableScheduling(kernel, cores_needing_scheduling); @@ -67,7 +64,7 @@ public: private: KernelCore& kernel; - Common::SpinLock spin_lock{}; + KAlignedSpinLock spin_lock{}; s32 lock_count{}; KThread* owner_thread{}; }; diff --git a/src/core/hle/kernel/k_scoped_lock.h b/src/core/hle/kernel/k_scoped_lock.h index d7cc557b2..72c3b0252 100644 --- a/src/core/hle/kernel/k_scoped_lock.h +++ b/src/core/hle/kernel/k_scoped_lock.h @@ -20,19 +20,22 @@ concept KLockable = !std::is_reference_v<T> && requires(T & t) { }; template <typename T> -requires KLockable<T> class KScopedLock { +requires KLockable<T> class [[nodiscard]] KScopedLock { public: - explicit KScopedLock(T* l) : lock_ptr(l) { + explicit KScopedLock(T * l) : lock_ptr(l) { this->lock_ptr->Lock(); } - explicit KScopedLock(T& l) : KScopedLock(std::addressof(l)) { /* ... */ - } + explicit KScopedLock(T & l) : KScopedLock(std::addressof(l)) {} + ~KScopedLock() { this->lock_ptr->Unlock(); } KScopedLock(const KScopedLock&) = delete; - KScopedLock(KScopedLock&&) = delete; + KScopedLock& operator=(const KScopedLock&) = delete; + + KScopedLock(KScopedLock &&) = delete; + KScopedLock& operator=(KScopedLock&&) = delete; private: T* lock_ptr; diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h index f8189e107..ebecf0c77 100644 --- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h +++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h @@ -15,9 +15,9 @@ namespace Kernel { -class KScopedSchedulerLockAndSleep { +class [[nodiscard]] KScopedSchedulerLockAndSleep { public: - explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, KThread* t, s64 timeout) + explicit KScopedSchedulerLockAndSleep(KernelCore & kernel, KThread * t, s64 timeout) : kernel(kernel), thread(t), timeout_tick(timeout) { // Lock the scheduler. kernel.GlobalSchedulerContext().scheduler_lock.Lock(); diff --git a/src/core/hle/kernel/k_spin_lock.h b/src/core/hle/kernel/k_spin_lock.h index 12c4b2e88..4d87d006a 100644 --- a/src/core/hle/kernel/k_spin_lock.h +++ b/src/core/hle/kernel/k_spin_lock.h @@ -28,6 +28,12 @@ private: std::atomic_flag lck = ATOMIC_FLAG_INIT; }; +// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future. +using KAlignedSpinLock = KSpinLock; +using KNotAlignedSpinLock = KSpinLock; + using KScopedSpinLock = KScopedLock<KSpinLock>; +using KScopedAlignedSpinLock = KScopedLock<KAlignedSpinLock>; +using KScopedNotAlignedSpinLock = KScopedLock<KNotAlignedSpinLock>; } // namespace Kernel diff --git a/src/core/hle/kernel/k_system_control.cpp b/src/core/hle/kernel/k_system_control.cpp deleted file mode 100644 index aa1682f69..000000000 --- a/src/core/hle/kernel/k_system_control.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <random> - -#include "core/hle/kernel/k_system_control.h" - -namespace Kernel { - -namespace { -template <typename F> -u64 GenerateUniformRange(u64 min, u64 max, F f) { - // Handle the case where the difference is too large to represent. - if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) { - return f(); - } - - // Iterate until we get a value in range. - const u64 range_size = ((max + 1) - min); - const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size; - while (true) { - if (const u64 rnd = f(); rnd < effective_max) { - return min + (rnd % range_size); - } - } -} - -} // Anonymous namespace - -u64 KSystemControl::GenerateRandomU64() { - static std::random_device device; - static std::mt19937 gen(device()); - static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); - return distribution(gen); -} - -u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) { - return GenerateUniformRange(min, max, GenerateRandomU64); -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/k_system_control.h b/src/core/hle/kernel/k_system_control.h index 1d5b64ffa..d755082c2 100644 --- a/src/core/hle/kernel/k_system_control.h +++ b/src/core/hle/kernel/k_system_control.h @@ -6,14 +6,18 @@ #include "common/common_types.h" -namespace Kernel { +#define BOARD_NINTENDO_NX + +#ifdef BOARD_NINTENDO_NX -class KSystemControl { -public: - KSystemControl() = default; +#include "core/hle/kernel/board/nintendo/nx/k_system_control.h" - static u64 GenerateRandomRange(u64 min, u64 max); - static u64 GenerateRandomU64(); -}; +namespace Kernel { + +using Kernel::Board::Nintendo::Nx::KSystemControl; } // namespace Kernel + +#else +#error "Unknown board for KSystemControl" +#endif diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 1c19b23dc..b442dfe57 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -14,10 +14,10 @@ #include "common/common_types.h" #include "common/intrusive_red_black_tree.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" +#include "core/hle/kernel/k_spin_lock.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/svc_common.h" @@ -402,7 +402,7 @@ public: return wait_cancelled; } - [[nodiscard]] void ClearWaitCancelled() { + void ClearWaitCancelled() { wait_cancelled = false; } @@ -732,7 +732,7 @@ private: s8 priority_inheritance_count{}; bool resource_limit_release_hint{}; StackParameters stack_parameters{}; - Common::SpinLock context_guard{}; + KSpinLock context_guard{}; // For emulation std::shared_ptr<Common::Fiber> host_context{}; diff --git a/src/core/hle/kernel/k_trace.h b/src/core/hle/kernel/k_trace.h new file mode 100644 index 000000000..91ebf9ab2 --- /dev/null +++ b/src/core/hle/kernel/k_trace.h @@ -0,0 +1,12 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Kernel { + +constexpr bool IsKTraceEnabled = false; +constexpr std::size_t KTraceBufferSize = IsKTraceEnabled ? 16 * 1024 * 1024 : 0; + +} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 5b6c7792e..5c4f45ab4 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -12,6 +12,7 @@ #include <utility> #include "common/assert.h" +#include "common/common_sizes.h" #include "common/logging/log.h" #include "common/microprofile.h" #include "common/thread.h" @@ -66,8 +67,13 @@ struct KernelCore::Impl { is_phantom_mode_for_singlecore = false; InitializePhysicalCores(); - InitializeSystemResourceLimit(kernel, system); - InitializeMemoryLayout(); + + // Derive the initial memory layout from the emulated board + KMemoryLayout memory_layout; + DeriveInitialMemoryLayout(memory_layout); + InitializeMemoryLayout(memory_layout); + InitializeSystemResourceLimit(kernel, system.CoreTiming(), memory_layout); + InitializeSlabHeaps(); InitializeSchedulers(); InitializeSuspendThreads(); InitializePreemption(kernel); @@ -136,27 +142,33 @@ struct KernelCore::Impl { } // Creates the default system resource limit - void InitializeSystemResourceLimit(KernelCore& kernel, Core::System& system) { - system_resource_limit = std::make_shared<KResourceLimit>(kernel, system); + void InitializeSystemResourceLimit(KernelCore& kernel, + const Core::Timing::CoreTiming& core_timing, + const KMemoryLayout& memory_layout) { + system_resource_limit = std::make_shared<KResourceLimit>(kernel, core_timing); + const auto [total_size, kernel_size] = memory_layout.GetTotalAndKernelMemorySizes(); // If setting the default system values fails, then something seriously wrong has occurred. - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, 0x100000000) + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) .IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 700).IsSuccess()); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200) .IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 933).IsSuccess()); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); + system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size); - // Derived from recent software updates. The kernel reserves 27MB - constexpr u64 kernel_size{0x1b00000}; - if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size)) { - UNREACHABLE(); - } // Reserve secure applet memory, introduced in firmware 5.0.0 - constexpr u64 secure_applet_memory_size{0x400000}; + constexpr u64 secure_applet_memory_size{Common::Size_4_MB}; ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, secure_applet_memory_size)); + + // This memory seems to be reserved on hardware, but is not reserved/used by yuzu. + // Likely Horizon OS reserved memory + // TODO(ameerj): Derive the memory rather than hardcode it. + constexpr u64 unknown_reserved_memory{0x2f896000}; + ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, + unknown_reserved_memory)); } void InitializePreemption(KernelCore& kernel) { @@ -268,51 +280,319 @@ struct KernelCore::Impl { return schedulers[thread_id]->GetCurrentThread(); } - void InitializeMemoryLayout() { - // Initialize memory layout - constexpr KMemoryLayout layout{KMemoryLayout::GetDefaultLayout()}; + void DeriveInitialMemoryLayout(KMemoryLayout& memory_layout) { + // Insert the root region for the virtual memory tree, from which all other regions will + // derive. + memory_layout.GetVirtualMemoryRegionTree().InsertDirectly( + KernelVirtualAddressSpaceBase, + KernelVirtualAddressSpaceBase + KernelVirtualAddressSpaceSize - 1); + + // Insert the root region for the physical memory tree, from which all other regions will + // derive. + memory_layout.GetPhysicalMemoryRegionTree().InsertDirectly( + KernelPhysicalAddressSpaceBase, + KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceSize - 1); + + // Save start and end for ease of use. + const VAddr code_start_virt_addr = KernelVirtualAddressCodeBase; + const VAddr code_end_virt_addr = KernelVirtualAddressCodeEnd; + + // Setup the containing kernel region. + constexpr size_t KernelRegionSize = Common::Size_1_GB; + constexpr size_t KernelRegionAlign = Common::Size_1_GB; + constexpr VAddr kernel_region_start = + Common::AlignDown(code_start_virt_addr, KernelRegionAlign); + size_t kernel_region_size = KernelRegionSize; + if (!(kernel_region_start + KernelRegionSize - 1 <= KernelVirtualAddressSpaceLast)) { + kernel_region_size = KernelVirtualAddressSpaceEnd - kernel_region_start; + } + ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + kernel_region_start, kernel_region_size, KMemoryRegionType_Kernel)); + + // Setup the code region. + constexpr size_t CodeRegionAlign = PageSize; + constexpr VAddr code_region_start = + Common::AlignDown(code_start_virt_addr, CodeRegionAlign); + constexpr VAddr code_region_end = Common::AlignUp(code_end_virt_addr, CodeRegionAlign); + constexpr size_t code_region_size = code_region_end - code_region_start; + ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + code_region_start, code_region_size, KMemoryRegionType_KernelCode)); + + // Setup board-specific device physical regions. + Init::SetupDevicePhysicalMemoryRegions(memory_layout); + + // Determine the amount of space needed for the misc region. + size_t misc_region_needed_size; + { + // Each core has a one page stack for all three stack types (Main, Idle, Exception). + misc_region_needed_size = Core::Hardware::NUM_CPU_CORES * (3 * (PageSize + PageSize)); + + // Account for each auto-map device. + for (const auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + if (region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) { + // Check that the region is valid. + ASSERT(region.GetEndAddress() != 0); + + // Account for the region. + misc_region_needed_size += + PageSize + (Common::AlignUp(region.GetLastAddress(), PageSize) - + Common::AlignDown(region.GetAddress(), PageSize)); + } + } + + // Multiply the needed size by three, to account for the need for guard space. + misc_region_needed_size *= 3; + } + + // Decide on the actual size for the misc region. + constexpr size_t MiscRegionAlign = KernelAslrAlignment; + constexpr size_t MiscRegionMinimumSize = Common::Size_32_MB; + const size_t misc_region_size = Common::AlignUp( + std::max(misc_region_needed_size, MiscRegionMinimumSize), MiscRegionAlign); + ASSERT(misc_region_size > 0); + + // Setup the misc region. + const VAddr misc_region_start = + memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( + misc_region_size, MiscRegionAlign, KMemoryRegionType_Kernel); + ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc)); + + // Setup the stack region. + constexpr size_t StackRegionSize = Common::Size_14_MB; + constexpr size_t StackRegionAlign = KernelAslrAlignment; + const VAddr stack_region_start = + memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( + StackRegionSize, StackRegionAlign, KMemoryRegionType_Kernel); + ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack)); + + // Determine the size of the resource region. + const size_t resource_region_size = memory_layout.GetResourceRegionSizeForInit(); + + // Determine the size of the slab region. + const size_t slab_region_size = Common::AlignUp(KernelSlabHeapSize, PageSize); + ASSERT(slab_region_size <= resource_region_size); + + // Setup the slab region. + const PAddr code_start_phys_addr = KernelPhysicalAddressCodeBase; + const PAddr code_end_phys_addr = code_start_phys_addr + code_region_size; + const PAddr slab_start_phys_addr = code_end_phys_addr; + const PAddr slab_end_phys_addr = slab_start_phys_addr + slab_region_size; + constexpr size_t SlabRegionAlign = KernelAslrAlignment; + const size_t slab_region_needed_size = + Common::AlignUp(code_end_phys_addr + slab_region_size, SlabRegionAlign) - + Common::AlignDown(code_end_phys_addr, SlabRegionAlign); + const VAddr slab_region_start = + memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( + slab_region_needed_size, SlabRegionAlign, KMemoryRegionType_Kernel) + + (code_end_phys_addr % SlabRegionAlign); + ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab)); + + // Setup the temp region. + constexpr size_t TempRegionSize = Common::Size_128_MB; + constexpr size_t TempRegionAlign = KernelAslrAlignment; + const VAddr temp_region_start = + memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( + TempRegionSize, TempRegionAlign, KMemoryRegionType_Kernel); + ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize, + KMemoryRegionType_KernelTemp)); + + // Automatically map in devices that have auto-map attributes. + for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + // We only care about kernel regions. + if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) { + continue; + } + + // Check whether we should map the region. + if (!region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) { + continue; + } + + // If this region has already been mapped, no need to consider it. + if (region.HasTypeAttribute(KMemoryRegionAttr_DidKernelMap)) { + continue; + } + + // Check that the region is valid. + ASSERT(region.GetEndAddress() != 0); + + // Set the attribute to note we've mapped this region. + region.SetTypeAttribute(KMemoryRegionAttr_DidKernelMap); + + // Create a virtual pair region and insert it into the tree. + const PAddr map_phys_addr = Common::AlignDown(region.GetAddress(), PageSize); + const size_t map_size = + Common::AlignUp(region.GetEndAddress(), PageSize) - map_phys_addr; + const VAddr map_virt_addr = + memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( + map_size, PageSize, KMemoryRegionType_KernelMisc, PageSize); + ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + map_virt_addr, map_size, KMemoryRegionType_KernelMiscMappedDevice)); + region.SetPairAddress(map_virt_addr + region.GetAddress() - map_phys_addr); + } + + Init::SetupDramPhysicalMemoryRegions(memory_layout); + + // Insert a physical region for the kernel code region. + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + code_start_phys_addr, code_region_size, KMemoryRegionType_DramKernelCode)); + + // Insert a physical region for the kernel slab region. + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + slab_start_phys_addr, slab_region_size, KMemoryRegionType_DramKernelSlab)); + + // Determine size available for kernel page table heaps, requiring > 8 MB. + const PAddr resource_end_phys_addr = slab_start_phys_addr + resource_region_size; + const size_t page_table_heap_size = resource_end_phys_addr - slab_end_phys_addr; + ASSERT(page_table_heap_size / Common::Size_4_MB > 2); + + // Insert a physical region for the kernel page table heap region + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + slab_end_phys_addr, page_table_heap_size, KMemoryRegionType_DramKernelPtHeap)); + + // All DRAM regions that we haven't tagged by this point will be mapped under the linear + // mapping. Tag them. + for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + if (region.GetType() == KMemoryRegionType_Dram) { + // Check that the region is valid. + ASSERT(region.GetEndAddress() != 0); + + // Set the linear map attribute. + region.SetTypeAttribute(KMemoryRegionAttr_LinearMapped); + } + } + + // Get the linear region extents. + const auto linear_extents = + memory_layout.GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionAttr_LinearMapped); + ASSERT(linear_extents.GetEndAddress() != 0); + + // Setup the linear mapping region. + constexpr size_t LinearRegionAlign = Common::Size_1_GB; + const PAddr aligned_linear_phys_start = + Common::AlignDown(linear_extents.GetAddress(), LinearRegionAlign); + const size_t linear_region_size = + Common::AlignUp(linear_extents.GetEndAddress(), LinearRegionAlign) - + aligned_linear_phys_start; + const VAddr linear_region_start = + memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( + linear_region_size, LinearRegionAlign, KMemoryRegionType_None, LinearRegionAlign); + + const u64 linear_region_phys_to_virt_diff = linear_region_start - aligned_linear_phys_start; + + // Map and create regions for all the linearly-mapped data. + { + PAddr cur_phys_addr = 0; + u64 cur_size = 0; + for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) { + continue; + } + + ASSERT(region.GetEndAddress() != 0); + + if (cur_size == 0) { + cur_phys_addr = region.GetAddress(); + cur_size = region.GetSize(); + } else if (cur_phys_addr + cur_size == region.GetAddress()) { + cur_size += region.GetSize(); + } else { + cur_phys_addr = region.GetAddress(); + cur_size = region.GetSize(); + } + + const VAddr region_virt_addr = + region.GetAddress() + linear_region_phys_to_virt_diff; + ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + region_virt_addr, region.GetSize(), + GetTypeForVirtualLinearMapping(region.GetType()))); + region.SetPairAddress(region_virt_addr); + + KMemoryRegion* virt_region = + memory_layout.GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr); + ASSERT(virt_region != nullptr); + virt_region->SetPairAddress(region.GetAddress()); + } + } + + // Insert regions for the initial page table region. + ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + resource_end_phys_addr, KernelPageTableHeapSize, KMemoryRegionType_DramKernelInitPt)); + ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + resource_end_phys_addr + linear_region_phys_to_virt_diff, KernelPageTableHeapSize, + KMemoryRegionType_VirtualDramKernelInitPt)); + + // All linear-mapped DRAM regions that we haven't tagged by this point will be allocated to + // some pool partition. Tag them. + for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + if (region.GetType() == (KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped)) { + region.SetType(KMemoryRegionType_DramPoolPartition); + } + } + + // Setup all other memory regions needed to arrange the pool partitions. + Init::SetupPoolPartitionMemoryRegions(memory_layout); + + // Cache all linear regions in their own trees for faster access, later. + memory_layout.InitializeLinearMemoryRegionTrees(aligned_linear_phys_start, + linear_region_start); + } + + void InitializeMemoryLayout(const KMemoryLayout& memory_layout) { + const auto system_pool = memory_layout.GetKernelSystemPoolRegionPhysicalExtents(); + const auto applet_pool = memory_layout.GetKernelAppletPoolRegionPhysicalExtents(); + const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents(); + + // Initialize memory managers + memory_manager = std::make_unique<KMemoryManager>(); + memory_manager->InitializeManager(KMemoryManager::Pool::Application, + application_pool.GetAddress(), + application_pool.GetEndAddress()); + memory_manager->InitializeManager(KMemoryManager::Pool::Applet, applet_pool.GetAddress(), + applet_pool.GetEndAddress()); + memory_manager->InitializeManager(KMemoryManager::Pool::System, system_pool.GetAddress(), + system_pool.GetEndAddress()); + + // 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 PAddr hid_addr{layout.System().StartAddress()}; - constexpr PAddr font_pa{layout.System().StartAddress() + hid_size}; - constexpr PAddr irs_addr{layout.System().StartAddress() + hid_size + font_size}; - constexpr PAddr time_addr{layout.System().StartAddress() + hid_size + font_size + irs_size}; - // Initialize memory manager - memory_manager = std::make_unique<KMemoryManager>(); - memory_manager->InitializeManager(KMemoryManager::Pool::Application, - layout.Application().StartAddress(), - layout.Application().EndAddress()); - memory_manager->InitializeManager(KMemoryManager::Pool::Applet, - layout.Applet().StartAddress(), - layout.Applet().EndAddress()); - memory_manager->InitializeManager(KMemoryManager::Pool::System, - layout.System().StartAddress(), - layout.System().EndAddress()); + const PAddr hid_phys_addr{system_pool.GetAddress()}; + const PAddr font_phys_addr{system_pool.GetAddress() + hid_size}; + const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size}; + const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size}; hid_shared_mem = Kernel::KSharedMemory::Create( - system.Kernel(), system.DeviceMemory(), nullptr, {hid_addr, hid_size / PageSize}, - KMemoryPermission::None, KMemoryPermission::Read, hid_addr, hid_size, + system.Kernel(), system.DeviceMemory(), nullptr, {hid_phys_addr, hid_size / PageSize}, + KMemoryPermission::None, KMemoryPermission::Read, hid_phys_addr, hid_size, "HID:SharedMemory"); font_shared_mem = Kernel::KSharedMemory::Create( - system.Kernel(), system.DeviceMemory(), nullptr, {font_pa, font_size / PageSize}, - KMemoryPermission::None, KMemoryPermission::Read, font_pa, font_size, + system.Kernel(), system.DeviceMemory(), nullptr, {font_phys_addr, font_size / PageSize}, + KMemoryPermission::None, KMemoryPermission::Read, font_phys_addr, font_size, "Font:SharedMemory"); irs_shared_mem = Kernel::KSharedMemory::Create( - system.Kernel(), system.DeviceMemory(), nullptr, {irs_addr, irs_size / PageSize}, - KMemoryPermission::None, KMemoryPermission::Read, irs_addr, irs_size, + system.Kernel(), system.DeviceMemory(), nullptr, {irs_phys_addr, irs_size / PageSize}, + KMemoryPermission::None, KMemoryPermission::Read, irs_phys_addr, irs_size, "IRS:SharedMemory"); time_shared_mem = Kernel::KSharedMemory::Create( - system.Kernel(), system.DeviceMemory(), nullptr, {time_addr, time_size / PageSize}, - KMemoryPermission::None, KMemoryPermission::Read, time_addr, time_size, + system.Kernel(), system.DeviceMemory(), nullptr, {time_phys_addr, time_size / PageSize}, + KMemoryPermission::None, KMemoryPermission::Read, time_phys_addr, time_size, "Time:SharedMemory"); + } + void InitializeSlabHeaps() { // Allocate slab heaps user_slab_heap_pages = std::make_unique<KSlabHeap<Page>>(); - constexpr u64 user_slab_heap_size{0x1ef000}; + // TODO(ameerj): This should be derived, not hardcoded within the kernel + constexpr u64 user_slab_heap_size{0x3de000}; // Reserve slab heaps ASSERT( system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size)); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 56906f2da..a500e63bc 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project / PPSSPP Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 9d5956ead..e35deb8e2 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -10,6 +10,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" #include "core/device_memory.h" #include "core/file_sys/program_metadata.h" @@ -26,7 +27,6 @@ #include "core/hle/kernel/svc_results.h" #include "core/hle/lock.h" #include "core/memory.h" -#include "core/settings.h" namespace Kernel { namespace { @@ -120,9 +120,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name, std::shared_ptr<Process> process = std::make_shared<Process>(system); process->name = std::move(name); - // TODO: This is inaccurate - // The process should hold a reference to the kernel-wide resource limit. - process->resource_limit = std::make_shared<KResourceLimit>(kernel, system); + process->resource_limit = kernel.GetSystemResourceLimit(); process->status = ProcessStatus::Created; process->program_id = 0; process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() @@ -160,17 +158,13 @@ void Process::DecrementThreadCount() { } u64 Process::GetTotalPhysicalMemoryAvailable() const { - // TODO: This is expected to always return the application memory pool size after accurately - // reserving kernel resources. The current workaround uses a process-local resource limit of - // application memory pool size, which is inaccurate. const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size + main_thread_stack_size}; - + ASSERT(capacity == kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application)); if (capacity < memory_usage_capacity) { return capacity; } - return memory_usage_capacity; } @@ -272,10 +266,6 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, system_resource_size = metadata.GetSystemResourceSize(); image_size = code_size; - // Set initial resource limits - resource_limit->SetLimitValue( - LimitableResource::PhysicalMemory, - kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application)); KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, code_size + system_resource_size); if (!memory_reservation.Succeeded()) { @@ -324,16 +314,6 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, UNREACHABLE(); } - // Set initial resource limits - resource_limit->SetLimitValue( - LimitableResource::PhysicalMemory, - kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application)); - - resource_limit->SetLimitValue(LimitableResource::Threads, 608); - resource_limit->SetLimitValue(LimitableResource::Events, 700); - resource_limit->SetLimitValue(LimitableResource::TransferMemory, 128); - resource_limit->SetLimitValue(LimitableResource::Sessions, 894); - // Create TLS region tls_region_address = CreateTLSRegion(); memory_reservation.Commit(); diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index 3fc326eab..1006ee50c 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -281,11 +281,6 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) continue; } - if (svc_number >= svc_capabilities.size()) { - LOG_ERROR(Kernel, "Process svc capability is out of range! svc_number={}", svc_number); - return ResultOutOfRange; - } - svc_capabilities[svc_number] = true; } diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h index 73ad197fa..b7a9b2e45 100644 --- a/src/core/hle/kernel/process_capability.h +++ b/src/core/hle/kernel/process_capability.h @@ -68,7 +68,7 @@ enum class ProgramType { class ProcessCapabilities { public: using InterruptCapabilities = std::bitset<1024>; - using SyscallCapabilities = std::bitset<128>; + using SyscallCapabilities = std::bitset<192>; ProcessCapabilities() = default; ProcessCapabilities(const ProcessCapabilities&) = delete; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 326d3b9ec..bebb86154 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -2156,7 +2156,7 @@ static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) LOG_DEBUG(Kernel_SVC, "called"); auto& kernel = system.Kernel(); - auto resource_limit = std::make_shared<KResourceLimit>(kernel, system); + auto resource_limit = std::make_shared<KResourceLimit>(kernel, system.CoreTiming()); auto* const current_process = kernel.CurrentProcess(); ASSERT(current_process != nullptr); @@ -2455,6 +2455,74 @@ static const FunctionDef SVC_Table_32[] = { {0x79, nullptr, "Unknown"}, {0x7A, nullptr, "Unknown"}, {0x7B, nullptr, "TerminateProcess32"}, + {0x7C, nullptr, "GetProcessInfo32"}, + {0x7D, nullptr, "CreateResourceLimit32"}, + {0x7E, nullptr, "SetResourceLimitLimitValue32"}, + {0x7F, nullptr, "CallSecureMonitor32"}, + {0x80, nullptr, "Unknown"}, + {0x81, nullptr, "Unknown"}, + {0x82, nullptr, "Unknown"}, + {0x83, nullptr, "Unknown"}, + {0x84, nullptr, "Unknown"}, + {0x85, nullptr, "Unknown"}, + {0x86, nullptr, "Unknown"}, + {0x87, nullptr, "Unknown"}, + {0x88, nullptr, "Unknown"}, + {0x89, nullptr, "Unknown"}, + {0x8A, nullptr, "Unknown"}, + {0x8B, nullptr, "Unknown"}, + {0x8C, nullptr, "Unknown"}, + {0x8D, nullptr, "Unknown"}, + {0x8E, nullptr, "Unknown"}, + {0x8F, nullptr, "Unknown"}, + {0x90, nullptr, "Unknown"}, + {0x91, nullptr, "Unknown"}, + {0x92, nullptr, "Unknown"}, + {0x93, nullptr, "Unknown"}, + {0x94, nullptr, "Unknown"}, + {0x95, nullptr, "Unknown"}, + {0x96, nullptr, "Unknown"}, + {0x97, nullptr, "Unknown"}, + {0x98, nullptr, "Unknown"}, + {0x99, nullptr, "Unknown"}, + {0x9A, nullptr, "Unknown"}, + {0x9B, nullptr, "Unknown"}, + {0x9C, nullptr, "Unknown"}, + {0x9D, nullptr, "Unknown"}, + {0x9E, nullptr, "Unknown"}, + {0x9F, nullptr, "Unknown"}, + {0xA0, nullptr, "Unknown"}, + {0xA1, nullptr, "Unknown"}, + {0xA2, nullptr, "Unknown"}, + {0xA3, nullptr, "Unknown"}, + {0xA4, nullptr, "Unknown"}, + {0xA5, nullptr, "Unknown"}, + {0xA6, nullptr, "Unknown"}, + {0xA7, nullptr, "Unknown"}, + {0xA8, nullptr, "Unknown"}, + {0xA9, nullptr, "Unknown"}, + {0xAA, nullptr, "Unknown"}, + {0xAB, nullptr, "Unknown"}, + {0xAC, nullptr, "Unknown"}, + {0xAD, nullptr, "Unknown"}, + {0xAE, nullptr, "Unknown"}, + {0xAF, nullptr, "Unknown"}, + {0xB0, nullptr, "Unknown"}, + {0xB1, nullptr, "Unknown"}, + {0xB2, nullptr, "Unknown"}, + {0xB3, nullptr, "Unknown"}, + {0xB4, nullptr, "Unknown"}, + {0xB5, nullptr, "Unknown"}, + {0xB6, nullptr, "Unknown"}, + {0xB7, nullptr, "Unknown"}, + {0xB8, nullptr, "Unknown"}, + {0xB9, nullptr, "Unknown"}, + {0xBA, nullptr, "Unknown"}, + {0xBB, nullptr, "Unknown"}, + {0xBC, nullptr, "Unknown"}, + {0xBD, nullptr, "Unknown"}, + {0xBE, nullptr, "Unknown"}, + {0xBF, nullptr, "Unknown"}, }; static const FunctionDef SVC_Table_64[] = { @@ -2586,6 +2654,70 @@ static const FunctionDef SVC_Table_64[] = { {0x7D, SvcWrap64<CreateResourceLimit>, "CreateResourceLimit"}, {0x7E, SvcWrap64<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"}, {0x7F, nullptr, "CallSecureMonitor"}, + {0x80, nullptr, "Unknown"}, + {0x81, nullptr, "Unknown"}, + {0x82, nullptr, "Unknown"}, + {0x83, nullptr, "Unknown"}, + {0x84, nullptr, "Unknown"}, + {0x85, nullptr, "Unknown"}, + {0x86, nullptr, "Unknown"}, + {0x87, nullptr, "Unknown"}, + {0x88, nullptr, "Unknown"}, + {0x89, nullptr, "Unknown"}, + {0x8A, nullptr, "Unknown"}, + {0x8B, nullptr, "Unknown"}, + {0x8C, nullptr, "Unknown"}, + {0x8D, nullptr, "Unknown"}, + {0x8E, nullptr, "Unknown"}, + {0x8F, nullptr, "Unknown"}, + {0x90, nullptr, "Unknown"}, + {0x91, nullptr, "Unknown"}, + {0x92, nullptr, "Unknown"}, + {0x93, nullptr, "Unknown"}, + {0x94, nullptr, "Unknown"}, + {0x95, nullptr, "Unknown"}, + {0x96, nullptr, "Unknown"}, + {0x97, nullptr, "Unknown"}, + {0x98, nullptr, "Unknown"}, + {0x99, nullptr, "Unknown"}, + {0x9A, nullptr, "Unknown"}, + {0x9B, nullptr, "Unknown"}, + {0x9C, nullptr, "Unknown"}, + {0x9D, nullptr, "Unknown"}, + {0x9E, nullptr, "Unknown"}, + {0x9F, nullptr, "Unknown"}, + {0xA0, nullptr, "Unknown"}, + {0xA1, nullptr, "Unknown"}, + {0xA2, nullptr, "Unknown"}, + {0xA3, nullptr, "Unknown"}, + {0xA4, nullptr, "Unknown"}, + {0xA5, nullptr, "Unknown"}, + {0xA6, nullptr, "Unknown"}, + {0xA7, nullptr, "Unknown"}, + {0xA8, nullptr, "Unknown"}, + {0xA9, nullptr, "Unknown"}, + {0xAA, nullptr, "Unknown"}, + {0xAB, nullptr, "Unknown"}, + {0xAC, nullptr, "Unknown"}, + {0xAD, nullptr, "Unknown"}, + {0xAE, nullptr, "Unknown"}, + {0xAF, nullptr, "Unknown"}, + {0xB0, nullptr, "Unknown"}, + {0xB1, nullptr, "Unknown"}, + {0xB2, nullptr, "Unknown"}, + {0xB3, nullptr, "Unknown"}, + {0xB4, nullptr, "Unknown"}, + {0xB5, nullptr, "Unknown"}, + {0xB6, nullptr, "Unknown"}, + {0xB7, nullptr, "Unknown"}, + {0xB8, nullptr, "Unknown"}, + {0xB9, nullptr, "Unknown"}, + {0xBA, nullptr, "Unknown"}, + {0xBB, nullptr, "Unknown"}, + {0xBC, nullptr, "Unknown"}, + {0xBD, nullptr, "Unknown"}, + {0xBE, nullptr, "Unknown"}, + {0xBF, nullptr, "Unknown"}, }; static const FunctionDef* GetSVCInfo32(u32 func_num) { diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 615e20a54..52535ecc0 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -610,12 +610,17 @@ public: explicit DAUTH_O(Core::System& system_, Common::UUID) : ServiceFramework{system_, "dauth:o"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "EnsureAuthenticationTokenCacheAsync"}, // [5.0.0-5.1.0] GeneratePostData - {1, nullptr, "LoadAuthenticationTokenCache"}, // 6.0.0+ - {2, nullptr, "InvalidateAuthenticationTokenCache"}, // 6.0.0+ - {10, nullptr, "EnsureEdgeTokenCacheAsync"}, // 6.0.0+ - {11, nullptr, "LoadEdgeTokenCache"}, // 6.0.0+ - {12, nullptr, "InvalidateEdgeTokenCache"}, // 6.0.0+ + {0, nullptr, "EnsureAuthenticationTokenCacheAsync"}, + {1, nullptr, "LoadAuthenticationTokenCache"}, + {2, nullptr, "InvalidateAuthenticationTokenCache"}, + {10, nullptr, "EnsureEdgeTokenCacheAsync"}, + {11, nullptr, "LoadEdgeTokenCache"}, + {12, nullptr, "InvalidateEdgeTokenCache"}, + {20, nullptr, "EnsureApplicationAuthenticationCacheAsync"}, + {21, nullptr, "LoadApplicationAuthenticationTokenCache"}, + {22, nullptr, "LoadApplicationNetworkServiceClientConfigCache"}, + {23, nullptr, "IsApplicationAuthenticationCacheAvailable"}, + {24, nullptr, "InvalidateApplicationAuthenticationCache"}, }; // clang-format on diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 49b22583e..bb6118abf 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -17,28 +17,30 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {3, &ACC_SU::ListOpenUsers, "ListOpenUsers"}, {4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_SU::GetProfile, "GetProfile"}, - {6, nullptr, "GetProfileDigest"}, // 3.0.0+ + {6, nullptr, "GetProfileDigest"}, {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, - {60, &ACC_SU::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 - {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ + {60, &ACC_SU::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, + {99, nullptr, "DebugActivateOpenContextRetention"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, {102, nullptr, "GetBaasAccountManagerForSystemService"}, {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, {104, nullptr, "GetProfileUpdateNotifier"}, - {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ - {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+ + {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, + {106, nullptr, "GetProfileSyncNotifier"}, {110, &ACC_SU::StoreSaveDataThumbnailSystem, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, - {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+ - {120, nullptr, "ListOpenUsersInApplication"}, // 10.0.0+ - {130, nullptr, "ActivateOpenContextRetention"}, // 6.0.0+ - {140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ - {150, nullptr, "AuthenticateApplicationAsync"}, // 10.0.0+ - {190, nullptr, "GetUserLastOpenedApplication"}, // 1.0.0 - 9.2.0 - {191, nullptr, "ActivateOpenContextHolder"}, // 7.0.0+ + {113, nullptr, "GetSaveDataThumbnailExistence"}, + {120, nullptr, "ListOpenUsersInApplication"}, + {130, nullptr, "ActivateOpenContextRetention"}, + {140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"}, + {150, nullptr, "AuthenticateApplicationAsync"}, + {151, nullptr, "Unknown151"}, + {152, nullptr, "Unknown152"}, + {190, nullptr, "GetUserLastOpenedApplication"}, + {191, nullptr, "ActivateOpenContextHolder"}, {200, nullptr, "BeginUserRegistration"}, {201, nullptr, "CompleteUserRegistration"}, {202, nullptr, "CancelUserRegistration"}, @@ -46,15 +48,15 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {204, nullptr, "SetUserPosition"}, {205, &ACC_SU::GetProfileEditor, "GetProfileEditor"}, {206, nullptr, "CompleteUserRegistrationForcibly"}, - {210, nullptr, "CreateFloatingRegistrationRequest"}, // 3.0.0+ - {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, // 8.0.0+ - {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, // 8.0.0+ + {210, nullptr, "CreateFloatingRegistrationRequest"}, + {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, + {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, {230, nullptr, "AuthenticateServiceAsync"}, {250, nullptr, "GetBaasAccountAdministrator"}, {290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"}, - {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, // 3.0.0+ + {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, {299, nullptr, "SuspendBackgroundDaemon"}, - {997, nullptr, "DebugInvalidateTokenCacheForUser"}, // 3.0.0+ + {997, nullptr, "DebugInvalidateTokenCacheForUser"}, {998, nullptr, "DebugSetUserStateClose"}, {999, nullptr, "DebugSetUserStateOpen"}, }; diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index 951081cd0..71982ad5a 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -17,29 +17,31 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {3, &ACC_U1::ListOpenUsers, "ListOpenUsers"}, {4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_U1::GetProfile, "GetProfile"}, - {6, nullptr, "GetProfileDigest"}, // 3.0.0+ + {6, nullptr, "GetProfileDigest"}, {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, - {60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 - {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ + {60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, + {99, nullptr, "DebugActivateOpenContextRetention"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, {102, nullptr, "GetBaasAccountManagerForSystemService"}, {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, {104, nullptr, "GetProfileUpdateNotifier"}, - {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ - {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+ + {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, + {106, nullptr, "GetProfileSyncNotifier"}, {110, &ACC_U1::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, - {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+ - {120, nullptr, "ListOpenUsersInApplication"}, // 10.0.0+ - {130, nullptr, "ActivateOpenContextRetention"}, // 6.0.0+ - {140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ - {150, nullptr, "AuthenticateApplicationAsync"}, // 10.0.0+ - {190, nullptr, "GetUserLastOpenedApplication"}, // 1.0.0 - 9.2.0 - {191, nullptr, "ActivateOpenContextHolder"}, // 7.0.0+ - {997, nullptr, "DebugInvalidateTokenCacheForUser"}, // 3.0.0+ + {113, nullptr, "GetSaveDataThumbnailExistence"}, + {120, nullptr, "ListOpenUsersInApplication"}, + {130, nullptr, "ActivateOpenContextRetention"}, + {140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"}, + {150, nullptr, "AuthenticateApplicationAsync"}, + {151, nullptr, "Unknown151"}, + {152, nullptr, "Unknown152"}, + {190, nullptr, "GetUserLastOpenedApplication"}, + {191, nullptr, "ActivateOpenContextHolder"}, + {997, nullptr, "DebugInvalidateTokenCacheForUser"}, {998, nullptr, "DebugSetUserStateClose"}, {999, nullptr, "DebugSetUserStateOpen"}, }; diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 50b2c58e2..de83d82a4 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -8,8 +8,8 @@ #include <fmt/format.h> #include "common/file_util.h" +#include "common/settings.h" #include "core/hle/service/acc/profile_manager.h" -#include "core/settings.h" namespace Service::Account { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 8e1fe9438..c59054468 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -7,6 +7,7 @@ #include <cinttypes> #include <cstring> #include "audio_core/audio_renderer.h" +#include "common/settings.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" @@ -41,7 +42,6 @@ #include "core/hle/service/set/set.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/vi/vi.h" -#include "core/settings.h" namespace Service::AM { @@ -231,6 +231,7 @@ IDebugFunctions::IDebugFunctions(Core::System& system_) {10, nullptr, "PerformSystemButtonPressing"}, {20, nullptr, "InvalidateTransitionLayer"}, {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, + {31, nullptr, "RequestLaunchApplicationByApplicationLaunchInfoForDebug"}, {40, nullptr, "GetAppletResourceUsageInfo"}, {100, nullptr, "SetCpuBoostModeForApplet"}, {101, nullptr, "CancelCpuBoostModeForApplet"}, @@ -242,6 +243,7 @@ IDebugFunctions::IDebugFunctions(Core::System& system_) {130, nullptr, "FriendInvitationSetApplicationParameter"}, {131, nullptr, "FriendInvitationClearApplicationParameter"}, {132, nullptr, "FriendInvitationPushApplicationParameter"}, + {900, nullptr, "GetGrcProcessLaunchedSystemEvent"}, }; // clang-format on @@ -295,8 +297,9 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv {80, nullptr, "SetWirelessPriorityMode"}, {90, &ISelfController::GetAccumulatedSuspendedTickValue, "GetAccumulatedSuspendedTickValue"}, {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"}, - {100, nullptr, "SetAlbumImageTakenNotificationEnabled"}, + {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, {110, nullptr, "SetApplicationAlbumUserData"}, + {120, nullptr, "SaveCurrentScreenshot"}, {1000, nullptr, "GetDebugStorageChannel"}, }; // clang-format on @@ -560,6 +563,21 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest rb.PushCopyObjects(accumulated_suspended_tick_changed_event->GetReadableEvent()); } +void ISelfController::SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + // This service call sets an internal flag whether a notification is shown when an image is + // captured. Currently we do not support capturing images via the capture button, so this can be + // stubbed for now. + const bool album_image_taken_notification_enabled = rp.Pop<bool>(); + + LOG_WARNING(Service_AM, "(STUBBED) called. album_image_taken_notification_enabled={}", + album_image_taken_notification_enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) { on_new_message = Kernel::KEvent::Create(kernel, "AMMessageQueue:OnMessageReceived"); on_new_message->Initialize(); @@ -630,6 +648,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, {11, nullptr, "ReleaseSleepLock"}, {12, nullptr, "ReleaseSleepLockTransiently"}, {13, nullptr, "GetAcquiredSleepLockEvent"}, + {14, nullptr, "GetWakeupCount"}, {20, nullptr, "PushToGeneralChannel"}, {30, nullptr, "GetHomeButtonReaderLockAccessor"}, {31, nullptr, "GetReaderLockAccessorEx"}, @@ -641,6 +660,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, {53, &ICommonStateGetter::BeginVrModeEx, "BeginVrModeEx"}, {54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"}, {55, nullptr, "IsInControllerFirmwareUpdateSection"}, + {59, nullptr, "SetVrPositionForDebug"}, {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, {62, nullptr, "GetHdcpAuthenticationState"}, @@ -649,14 +669,21 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, {65, nullptr, "GetApplicationIdByContentActionName"}, {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, {67, nullptr, "CancelCpuBoostMode"}, + {68, nullptr, "GetBuiltInDisplayType"}, {80, nullptr, "PerformSystemButtonPressingIfInFocus"}, {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, {91, nullptr, "GetCurrentPerformanceConfiguration"}, {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"}, + {110, nullptr, "OpenMyGpuErrorHandler"}, {200, nullptr, "GetOperationModeSystemInfo"}, {300, nullptr, "GetSettingsPlatformRegion"}, {400, nullptr, "ActivateMigrationService"}, {401, nullptr, "DeactivateMigrationService"}, + {500, nullptr, "DisableSleepTillShutdown"}, + {501, nullptr, "SuppressDisablingSleepTemporarily"}, + {502, nullptr, "IsSleepEnabled"}, + {503, nullptr, "IsDisablingSleepSuppressed"}, + {900, nullptr, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, }; // clang-format on @@ -944,7 +971,7 @@ private: auto storage = applet->GetBroker().PopNormalDataToGame(); if (storage == nullptr) { - LOG_ERROR(Service_AM, + LOG_DEBUG(Service_AM, "storage is a nullptr. There is no data in the current normal channel"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_NO_DATA_IN_CHANNEL); @@ -975,7 +1002,7 @@ private: auto storage = applet->GetBroker().PopInteractiveDataToGame(); if (storage == nullptr) { - LOG_ERROR(Service_AM, + LOG_DEBUG(Service_AM, "storage is a nullptr. There is no data in the current interactive channel"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_NO_DATA_IN_CHANNEL); @@ -1098,7 +1125,7 @@ ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_) {2, nullptr, "AreAnyLibraryAppletsLeft"}, {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"}, - {12, nullptr, "CreateHandleStorage"}, + {12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"}, }; RegisterHandlers(functions); } @@ -1107,14 +1134,15 @@ ILibraryAppletCreator::~ILibraryAppletCreator() = default; void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; + const auto applet_id = rp.PopRaw<Applets::AppletId>(); - const auto applet_mode = rp.PopRaw<u32>(); + const auto applet_mode = rp.PopRaw<Applets::LibraryAppletMode>(); LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id, applet_mode); const auto& applet_manager{system.GetAppletManager()}; - const auto applet = applet_manager.GetApplet(applet_id); + const auto applet = applet_manager.GetApplet(applet_id, applet_mode); if (applet == nullptr) { LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); @@ -1132,9 +1160,18 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const u64 size{rp.Pop<u64>()}; + + const s64 size{rp.Pop<s64>()}; + LOG_DEBUG(Service_AM, "called, size={}", size); + if (size <= 0) { + LOG_ERROR(Service_AM, "size is less than or equal to 0"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_UNKNOWN); + return; + } + std::vector<u8> buffer(size); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; @@ -1143,18 +1180,65 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { } void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); + IPC::RequestParser rp{ctx}; + + struct Parameters { + u8 permissions; + s64 size; + }; + + const auto parameters{rp.PopRaw<Parameters>()}; + const auto handle{ctx.GetCopyHandle(0)}; + + LOG_DEBUG(Service_AM, "called, permissions={}, size={}, handle={:08X}", parameters.permissions, + parameters.size, handle); + + if (parameters.size <= 0) { + LOG_ERROR(Service_AM, "size is less than or equal to 0"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_UNKNOWN); + return; + } + + auto transfer_mem = + system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle); + + if (transfer_mem == nullptr) { + LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_UNKNOWN); + return; + } + + const u8* const mem_begin = transfer_mem->GetPointer(); + const u8* const mem_end = mem_begin + transfer_mem->GetSize(); + std::vector<u8> memory{mem_begin, mem_end}; + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IStorage>(system, std::move(memory)); +} +void ILibraryAppletCreator::CreateHandleStorage(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - rp.SetCurrentOffset(3); - const auto handle{rp.Pop<Kernel::Handle>()}; + const s64 size{rp.Pop<s64>()}; + const auto handle{ctx.GetCopyHandle(0)}; + + LOG_DEBUG(Service_AM, "called, size={}, handle={:08X}", size, handle); + + if (size <= 0) { + LOG_ERROR(Service_AM, "size is less than or equal to 0"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_UNKNOWN); + return; + } auto transfer_mem = system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle); if (transfer_mem == nullptr) { - LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle); + LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_UNKNOWN); return; @@ -1188,11 +1272,14 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, {27, nullptr, "CreateCacheStorage"}, + {28, nullptr, "GetSaveDataSizeMax"}, + {29, nullptr, "GetCacheStorageMax"}, {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, {34, nullptr, "SelectApplicationLicense"}, + {35, nullptr, "GetDeviceSaveDataSizeMax"}, {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, {60, nullptr, "SetMediaPlaybackStateForApplication"}, @@ -1216,6 +1303,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"}, {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, + {131, nullptr, "SetDelayTimeToAbortOnGpuError"}, {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"}, {150, nullptr, "GetNotificationStorageChannelEvent"}, @@ -1224,6 +1312,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {170, nullptr, "SetHdcpAuthenticationActivated"}, {180, nullptr, "GetLaunchRequiredVersion"}, {181, nullptr, "UpgradeLaunchRequiredVersion"}, + {190, nullptr, "SendServerMaintenanceOverlayNotification"}, + {200, nullptr, "GetLastApplicationExitReason"}, {500, nullptr, "StartContinuousRecordingFlushForDebug"}, {1000, nullptr, "CreateMovieMaker"}, {1001, nullptr, "PrepareForJit"}, @@ -1690,9 +1780,12 @@ IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_) {21, &IHomeMenuFunctions::GetPopFromGeneralChannelEvent, "GetPopFromGeneralChannelEvent"}, {30, nullptr, "GetHomeButtonWriterLockAccessor"}, {31, nullptr, "GetWriterLockAccessorEx"}, + {40, nullptr, "IsSleepEnabled"}, + {41, nullptr, "IsRebootEnabled"}, {100, nullptr, "PopRequestLaunchApplicationForDebug"}, {110, nullptr, "IsForceTerminateApplicationDisabledForDebug"}, {200, nullptr, "LaunchDevMenu"}, + {1000, nullptr, "SetLastApplicationExitReason"}, }; // clang-format on @@ -1736,6 +1829,7 @@ IGlobalStateController::IGlobalStateController(Core::System& system_) {13, nullptr, "UpdateDefaultDisplayResolution"}, {14, nullptr, "ShouldSleepOnBoot"}, {15, nullptr, "GetHdcpAuthenticationFailedEvent"}, + {30, nullptr, "OpenCradleFirmwareUpdater"}, }; // clang-format on diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 6911f0d6e..aefbdf0d5 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -146,6 +146,7 @@ private: void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx); void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); + void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx); enum class ScreenshotPermission : u32 { Inherit = 0, @@ -253,6 +254,7 @@ private: void CreateLibraryApplet(Kernel::HLERequestContext& ctx); void CreateStorage(Kernel::HLERequestContext& ctx); void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx); + void CreateHandleStorage(Kernel::HLERequestContext& ctx); }; class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index e2f3b7563..5ddad851a 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -241,31 +241,31 @@ void AppletManager::ClearAll() { frontend = {}; } -std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const { +std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode mode) const { switch (id) { case AppletId::Auth: - return std::make_shared<Auth>(system, *frontend.parental_controls); + return std::make_shared<Auth>(system, mode, *frontend.parental_controls); case AppletId::Controller: - return std::make_shared<Controller>(system, *frontend.controller); + return std::make_shared<Controller>(system, mode, *frontend.controller); case AppletId::Error: - return std::make_shared<Error>(system, *frontend.error); + return std::make_shared<Error>(system, mode, *frontend.error); case AppletId::ProfileSelect: - return std::make_shared<ProfileSelect>(system, *frontend.profile_select); + return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select); case AppletId::SoftwareKeyboard: - return std::make_shared<SoftwareKeyboard>(system, *frontend.software_keyboard); + return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard); case AppletId::Web: case AppletId::Shop: case AppletId::OfflineWeb: case AppletId::LoginShare: case AppletId::WebAuth: - return std::make_shared<WebBrowser>(system, *frontend.web_browser); + return std::make_shared<WebBrowser>(system, mode, *frontend.web_browser); case AppletId::PhotoViewer: - return std::make_shared<PhotoViewer>(system, *frontend.photo_viewer); + return std::make_shared<PhotoViewer>(system, mode, *frontend.photo_viewer); default: UNIMPLEMENTED_MSG( "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", static_cast<u8>(id)); - return std::make_shared<StubApplet>(system, id); + return std::make_shared<StubApplet>(system, id, mode); } } diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index b9a006317..26b482015 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -62,6 +62,14 @@ enum class AppletId : u32 { MyPage = 0x1A, }; +enum class LibraryAppletMode : u32 { + AllForeground = 0, + Background = 1, + NoUI = 2, + BackgroundIndirectDisplay = 3, + AllForegroundInitiallyHidden = 4, +}; + class AppletDataBroker final { public: explicit AppletDataBroker(Kernel::KernelCore& kernel_); @@ -200,7 +208,7 @@ public: void SetDefaultAppletsIfMissing(); void ClearAll(); - std::shared_ptr<Applet> GetApplet(AppletId id) const; + std::shared_ptr<Applet> GetApplet(AppletId id, LibraryAppletMode mode) const; private: AppletFrontendSet frontend; diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp index c2bfe698f..a33f05f97 100644 --- a/src/core/hle/service/am/applets/controller.cpp +++ b/src/core/hle/service/am/applets/controller.cpp @@ -45,8 +45,9 @@ static Core::Frontend::ControllerParameters ConvertToFrontendParameters( }; } -Controller::Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_) - : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} +Controller::Controller(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ControllerApplet& frontend_) + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {} Controller::~Controller() = default; diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h index d4c9da7b1..07cb92bf9 100644 --- a/src/core/hle/service/am/applets/controller.h +++ b/src/core/hle/service/am/applets/controller.h @@ -106,7 +106,8 @@ static_assert(sizeof(ControllerSupportResultInfo) == 0xC, class Controller final : public Applet { public: - explicit Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_); + explicit Controller(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ControllerApplet& frontend_); ~Controller() override; void Initialize() override; @@ -119,6 +120,7 @@ public: void ConfigurationComplete(); private: + LibraryAppletMode applet_mode; const Core::Frontend::ControllerApplet& frontend; Core::System& system; diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp index 0c8b632e8..a9f0a9c95 100644 --- a/src/core/hle/service/am/applets/error.cpp +++ b/src/core/hle/service/am/applets/error.cpp @@ -86,8 +86,9 @@ ResultCode Decode64BitError(u64 error) { } // Anonymous namespace -Error::Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_) - : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} +Error::Error(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ErrorApplet& frontend_) + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {} Error::~Error() = default; diff --git a/src/core/hle/service/am/applets/error.h b/src/core/hle/service/am/applets/error.h index a105cdb0c..a3e520cd4 100644 --- a/src/core/hle/service/am/applets/error.h +++ b/src/core/hle/service/am/applets/error.h @@ -25,7 +25,8 @@ enum class ErrorAppletMode : u8 { class Error final : public Applet { public: - explicit Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_); + explicit Error(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ErrorApplet& frontend_); ~Error() override; void Initialize() override; @@ -40,6 +41,7 @@ public: private: union ErrorArguments; + LibraryAppletMode applet_mode; const Core::Frontend::ErrorApplet& frontend; ResultCode error_code = RESULT_SUCCESS; ErrorAppletMode mode = ErrorAppletMode::ShowError; diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp index 4d1df5cbe..71016cce7 100644 --- a/src/core/hle/service/am/applets/general_backend.cpp +++ b/src/core/hle/service/am/applets/general_backend.cpp @@ -37,8 +37,9 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) } } -Auth::Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_) - : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} +Auth::Auth(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::ParentalControlsApplet& frontend_) + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {} Auth::~Auth() = default; @@ -152,8 +153,9 @@ void Auth::AuthFinished(bool is_successful) { broker.SignalStateChanged(); } -PhotoViewer::PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_) - : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} +PhotoViewer::PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::PhotoViewerApplet& frontend_) + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {} PhotoViewer::~PhotoViewer() = default; @@ -202,8 +204,8 @@ void PhotoViewer::ViewFinished() { broker.SignalStateChanged(); } -StubApplet::StubApplet(Core::System& system_, AppletId id_) - : Applet{system_.Kernel()}, id{id_}, system{system_} {} +StubApplet::StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_) + : Applet{system_.Kernel()}, id{id_}, applet_mode{applet_mode_}, system{system_} {} StubApplet::~StubApplet() = default; diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h index ba76ae3d3..d9e6d4384 100644 --- a/src/core/hle/service/am/applets/general_backend.h +++ b/src/core/hle/service/am/applets/general_backend.h @@ -20,7 +20,8 @@ enum class AuthAppletType : u32 { class Auth final : public Applet { public: - explicit Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_); + explicit Auth(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::ParentalControlsApplet& frontend_); ~Auth() override; void Initialize() override; @@ -32,6 +33,7 @@ public: void AuthFinished(bool is_successful = true); private: + LibraryAppletMode applet_mode; Core::Frontend::ParentalControlsApplet& frontend; Core::System& system; bool complete = false; @@ -50,7 +52,8 @@ enum class PhotoViewerAppletMode : u8 { class PhotoViewer final : public Applet { public: - explicit PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_); + explicit PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::PhotoViewerApplet& frontend_); ~PhotoViewer() override; void Initialize() override; @@ -62,6 +65,7 @@ public: void ViewFinished(); private: + LibraryAppletMode applet_mode; const Core::Frontend::PhotoViewerApplet& frontend; bool complete = false; PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp; @@ -70,7 +74,7 @@ private: class StubApplet final : public Applet { public: - explicit StubApplet(Core::System& system_, AppletId id_); + explicit StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_); ~StubApplet() override; void Initialize() override; @@ -82,6 +86,7 @@ public: private: AppletId id; + LibraryAppletMode applet_mode; Core::System& system; }; diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp index 77fba16c7..ab8b6fcc5 100644 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ b/src/core/hle/service/am/applets/profile_select.cpp @@ -15,9 +15,9 @@ namespace Service::AM::Applets { constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; -ProfileSelect::ProfileSelect(Core::System& system_, +ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, const Core::Frontend::ProfileSelectApplet& frontend_) - : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {} ProfileSelect::~ProfileSelect() = default; diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h index 648d33a24..90f054030 100644 --- a/src/core/hle/service/am/applets/profile_select.h +++ b/src/core/hle/service/am/applets/profile_select.h @@ -33,7 +33,7 @@ static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has inco class ProfileSelect final : public Applet { public: - explicit ProfileSelect(Core::System& system_, + explicit ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, const Core::Frontend::ProfileSelectApplet& frontend_); ~ProfileSelect() override; @@ -47,6 +47,7 @@ public: void SelectionComplete(std::optional<Common::UUID> uuid); private: + LibraryAppletMode applet_mode; const Core::Frontend::ProfileSelectApplet& frontend; UserSelectionConfig config; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 79b209c6b..c3a05de9c 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -1,93 +1,80 @@ -// Copyright 2018 yuzu emulator team +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <cstring> -#include "common/assert.h" #include "common/string_util.h" #include "core/core.h" #include "core/frontend/applets/software_keyboard.h" -#include "core/hle/result.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/software_keyboard.h" namespace Service::AM::Applets { namespace { -enum class Request : u32 { - Finalize = 0x4, - SetUserWordInfo = 0x6, - SetCustomizeDic = 0x7, - Calc = 0xa, - SetCustomizedDictionaries = 0xb, - UnsetCustomizedDictionaries = 0xc, - UnknownD = 0xd, - UnknownE = 0xe, -}; -constexpr std::size_t SWKBD_INLINE_INIT_SIZE = 0x8; -constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; -constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; -constexpr std::size_t DEFAULT_MAX_LENGTH = 500; -constexpr bool INTERACTIVE_STATUS_OK = false; + +// The maximum number of UTF-16 characters that can be input into the swkbd text field. +constexpr u32 DEFAULT_MAX_TEXT_LENGTH = 500; + +constexpr std::size_t REPLY_BASE_SIZE = sizeof(SwkbdState) + sizeof(SwkbdReplyType); +constexpr std::size_t REPLY_UTF8_SIZE = 0x7D4; +constexpr std::size_t REPLY_UTF16_SIZE = 0x3EC; + +constexpr const char* GetTextCheckResultName(SwkbdTextCheckResult text_check_result) { + switch (text_check_result) { + case SwkbdTextCheckResult::Success: + return "Success"; + case SwkbdTextCheckResult::Failure: + return "Failure"; + case SwkbdTextCheckResult::Confirm: + return "Confirm"; + case SwkbdTextCheckResult::Silent: + return "Silent"; + default: + UNIMPLEMENTED_MSG("Unknown TextCheckResult={}", text_check_result); + return "Unknown"; + } +} + +void SetReplyBase(std::vector<u8>& reply, SwkbdState state, SwkbdReplyType reply_type) { + std::memcpy(reply.data(), &state, sizeof(SwkbdState)); + std::memcpy(reply.data() + sizeof(SwkbdState), &reply_type, sizeof(SwkbdReplyType)); +} + } // Anonymous namespace -static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( - KeyboardConfig config, std::u16string initial_text) { - Core::Frontend::SoftwareKeyboardParameters params{}; - - params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - config.submit_text.data(), config.submit_text.size()); - params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - config.header_text.data(), config.header_text.size()); - params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(), - config.sub_text.size()); - params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(), - config.guide_text.size()); - params.initial_text = std::move(initial_text); - params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit; - params.password = static_cast<bool>(config.is_password); - params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position); - params.value = static_cast<u8>(config.keyset_disable_bitmask); - - return params; -} - -SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, - const Core::Frontend::SoftwareKeyboardApplet& frontend_) - : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} + +SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::SoftwareKeyboardApplet& frontend_) + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {} SoftwareKeyboard::~SoftwareKeyboard() = default; void SoftwareKeyboard::Initialize() { - complete = false; - is_inline = false; - initial_text.clear(); - final_data.clear(); - Applet::Initialize(); - const auto keyboard_config_storage = broker.PopNormalDataToApplet(); - ASSERT(keyboard_config_storage != nullptr); - const auto& keyboard_config = keyboard_config_storage->GetData(); - - if (keyboard_config.size() == SWKBD_INLINE_INIT_SIZE) { - is_inline = true; - return; - } - - ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); - std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); + LOG_INFO(Service_AM, "Initializing Software Keyboard Applet with LibraryAppletMode={}", + applet_mode); - const auto work_buffer_storage = broker.PopNormalDataToApplet(); - ASSERT_OR_EXECUTE(work_buffer_storage != nullptr, { return; }); - const auto& work_buffer = work_buffer_storage->GetData(); + LOG_DEBUG(Service_AM, + "Initializing Applet with common_args: arg_version={}, lib_version={}, " + "play_startup_sound={}, size={}, system_tick={}, theme_color={}", + common_args.arguments_version, common_args.library_version, + common_args.play_startup_sound, common_args.size, common_args.system_tick, + common_args.theme_color); - if (config.initial_string_size == 0) - return; + swkbd_applet_version = SwkbdAppletVersion{common_args.library_version}; - std::vector<char16_t> string(config.initial_string_size); - std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset, - string.size() * 2); - initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()); + switch (applet_mode) { + case LibraryAppletMode::AllForeground: + InitializeForeground(); + break; + case LibraryAppletMode::Background: + case LibraryAppletMode::BackgroundIndirectDisplay: + InitializeBackground(applet_mode); + break; + default: + UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode); + break; + } } bool SoftwareKeyboard::TransactionComplete() const { @@ -95,106 +82,996 @@ bool SoftwareKeyboard::TransactionComplete() const { } ResultCode SoftwareKeyboard::GetStatus() const { - return RESULT_SUCCESS; + return status; } void SoftwareKeyboard::ExecuteInteractive() { - if (complete) + if (complete) { return; + } - const auto storage = broker.PopInteractiveDataToApplet(); - ASSERT(storage != nullptr); - const auto data = storage->GetData(); - if (!is_inline) { - const auto status = static_cast<bool>(data[0]); - if (status == INTERACTIVE_STATUS_OK) { - complete = true; - } else { - std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; - std::memcpy(string.data(), data.data() + 4, string.size() * 2); - frontend.SendTextCheckDialog( - Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), - [this] { broker.SignalStateChanged(); }); - } + if (is_background) { + ProcessInlineKeyboardRequest(); } else { - Request request{}; - std::memcpy(&request, data.data(), sizeof(Request)); + ProcessTextCheck(); + } +} + +void SoftwareKeyboard::Execute() { + if (complete) { + return; + } + + if (is_background) { + return; + } + + ShowNormalKeyboard(); +} - switch (request) { - case Request::Finalize: - complete = true; - broker.SignalStateChanged(); +void SoftwareKeyboard::SubmitTextNormal(SwkbdResult result, std::u16string submitted_text) { + if (complete) { + return; + } + + if (swkbd_config_common.use_text_check && result == SwkbdResult::Ok) { + SubmitForTextCheck(submitted_text); + } else { + SubmitNormalOutputAndExit(result, submitted_text); + } +} + +void SoftwareKeyboard::SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, + s32 cursor_position) { + if (complete) { + return; + } + + current_text = std::move(submitted_text); + current_cursor_position = cursor_position; + + if (inline_use_utf8) { + switch (reply_type) { + case SwkbdReplyType::ChangedString: + reply_type = SwkbdReplyType::ChangedStringUtf8; break; - case Request::Calc: { - broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>{1})); - broker.SignalStateChanged(); + case SwkbdReplyType::MovedCursor: + reply_type = SwkbdReplyType::MovedCursorUtf8; + break; + case SwkbdReplyType::DecidedEnter: + reply_type = SwkbdReplyType::DecidedEnterUtf8; + break; + default: break; } + } + + if (use_changed_string_v2) { + switch (reply_type) { + case SwkbdReplyType::ChangedString: + reply_type = SwkbdReplyType::ChangedStringV2; + break; + case SwkbdReplyType::ChangedStringUtf8: + reply_type = SwkbdReplyType::ChangedStringUtf8V2; + break; default: - UNIMPLEMENTED_MSG("Request {:X} is not implemented", request); break; } } + + if (use_moved_cursor_v2) { + switch (reply_type) { + case SwkbdReplyType::MovedCursor: + reply_type = SwkbdReplyType::MovedCursorV2; + break; + case SwkbdReplyType::MovedCursorUtf8: + reply_type = SwkbdReplyType::MovedCursorUtf8V2; + break; + default: + break; + } + } + + SendReply(reply_type); } -void SoftwareKeyboard::Execute() { - if (complete) { - broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(final_data))); - broker.SignalStateChanged(); +void SoftwareKeyboard::InitializeForeground() { + LOG_INFO(Service_AM, "Initializing Normal Software Keyboard Applet."); + + is_background = false; + + const auto swkbd_config_storage = broker.PopNormalDataToApplet(); + ASSERT(swkbd_config_storage != nullptr); + + const auto& swkbd_config_data = swkbd_config_storage->GetData(); + ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon)); + + std::memcpy(&swkbd_config_common, swkbd_config_data.data(), sizeof(SwkbdConfigCommon)); + + switch (swkbd_applet_version) { + case SwkbdAppletVersion::Version5: + case SwkbdAppletVersion::Version65542: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld)); + std::memcpy(&swkbd_config_old, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigOld)); + break; + case SwkbdAppletVersion::Version196615: + case SwkbdAppletVersion::Version262152: + case SwkbdAppletVersion::Version327689: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld2)); + std::memcpy(&swkbd_config_old2, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigOld2)); + break; + case SwkbdAppletVersion::Version393227: + case SwkbdAppletVersion::Version524301: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); + std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigNew)); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdConfig revision={} with size={}", swkbd_applet_version, + swkbd_config_data.size()); + ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); + std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigNew)); + break; + } + + const auto work_buffer_storage = broker.PopNormalDataToApplet(); + ASSERT(work_buffer_storage != nullptr); + + if (swkbd_config_common.initial_string_length == 0) { + InitializeFrontendKeyboard(); return; } - const auto parameters = ConvertToFrontendParameters(config, initial_text); - if (!is_inline) { - frontend.RequestText( - [this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, parameters); + const auto& work_buffer = work_buffer_storage->GetData(); + + std::vector<char16_t> initial_string(swkbd_config_common.initial_string_length); + + std::memcpy(initial_string.data(), + work_buffer.data() + swkbd_config_common.initial_string_offset, + swkbd_config_common.initial_string_length * sizeof(char16_t)); + + initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(initial_string.data(), + initial_string.size()); + + LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); + + InitializeFrontendKeyboard(); +} + +void SoftwareKeyboard::InitializeBackground(LibraryAppletMode applet_mode) { + LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); + + is_background = true; + + const auto swkbd_inline_initialize_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(swkbd_inline_initialize_arg_storage != nullptr); + + const auto& swkbd_inline_initialize_arg = swkbd_inline_initialize_arg_storage->GetData(); + ASSERT(swkbd_inline_initialize_arg.size() == sizeof(SwkbdInitializeArg)); + + std::memcpy(&swkbd_initialize_arg, swkbd_inline_initialize_arg.data(), + swkbd_inline_initialize_arg.size()); + + if (swkbd_initialize_arg.library_applet_mode_flag) { + ASSERT(applet_mode == LibraryAppletMode::Background); + } else { + ASSERT(applet_mode == LibraryAppletMode::BackgroundIndirectDisplay); + } +} + +void SoftwareKeyboard::ProcessTextCheck() { + const auto text_check_storage = broker.PopInteractiveDataToApplet(); + ASSERT(text_check_storage != nullptr); + + const auto& text_check_data = text_check_storage->GetData(); + ASSERT(text_check_data.size() == sizeof(SwkbdTextCheck)); + + SwkbdTextCheck swkbd_text_check; + + std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck)); + + std::u16string text_check_message = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_text_check.text_check_message.data(), swkbd_text_check.text_check_message.size()); + + LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}", + GetTextCheckResultName(swkbd_text_check.text_check_result), + Common::UTF16ToUTF8(text_check_message)); + + switch (swkbd_text_check.text_check_result) { + case SwkbdTextCheckResult::Success: + SubmitNormalOutputAndExit(SwkbdResult::Ok, current_text); + break; + case SwkbdTextCheckResult::Failure: + ShowTextCheckDialog(SwkbdTextCheckResult::Failure, text_check_message); + break; + case SwkbdTextCheckResult::Confirm: + ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, text_check_message); + break; + case SwkbdTextCheckResult::Silent: + default: + break; } } -void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { - std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE); +void SoftwareKeyboard::ProcessInlineKeyboardRequest() { + const auto request_data_storage = broker.PopInteractiveDataToApplet(); + ASSERT(request_data_storage != nullptr); + + const auto& request_data = request_data_storage->GetData(); + ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand)); - if (text.has_value()) { - std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE); + SwkbdRequestCommand request_command; - if (config.utf_8) { - const u64 size = text->size() + sizeof(u64); - const auto new_text = Common::UTF16ToUTF8(*text); + std::memcpy(&request_command, request_data.data(), sizeof(SwkbdRequestCommand)); - std::memcpy(output_sub.data(), &size, sizeof(u64)); - std::memcpy(output_sub.data() + 8, new_text.data(), - std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8)); + switch (request_command) { + case SwkbdRequestCommand::Finalize: + RequestFinalize(request_data); + break; + case SwkbdRequestCommand::SetUserWordInfo: + RequestSetUserWordInfo(request_data); + break; + case SwkbdRequestCommand::SetCustomizeDic: + RequestSetCustomizeDic(request_data); + break; + case SwkbdRequestCommand::Calc: + RequestCalc(request_data); + break; + case SwkbdRequestCommand::SetCustomizedDictionaries: + RequestSetCustomizedDictionaries(request_data); + break; + case SwkbdRequestCommand::UnsetCustomizedDictionaries: + RequestUnsetCustomizedDictionaries(request_data); + break; + case SwkbdRequestCommand::SetChangedStringV2Flag: + RequestSetChangedStringV2Flag(request_data); + break; + case SwkbdRequestCommand::SetMovedCursorV2Flag: + RequestSetMovedCursorV2Flag(request_data); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdRequestCommand={}", request_command); + break; + } +} - output_main[0] = INTERACTIVE_STATUS_OK; - std::memcpy(output_main.data() + 4, new_text.data(), - std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4)); - } else { - const u64 size = text->size() * 2 + sizeof(u64); - std::memcpy(output_sub.data(), &size, sizeof(u64)); - std::memcpy(output_sub.data() + 8, text->data(), - std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); +void SoftwareKeyboard::SubmitNormalOutputAndExit(SwkbdResult result, + std::u16string submitted_text) { + std::vector<u8> out_data(sizeof(SwkbdResult) + STRING_BUFFER_SIZE); - output_main[0] = INTERACTIVE_STATUS_OK; - std::memcpy(output_main.data() + 4, text->data(), - std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); - } + if (swkbd_config_common.use_utf8) { + std::string utf8_submitted_text = Common::UTF16ToUTF8(submitted_text); - complete = !config.text_check; - final_data = output_main; + LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-8 Submitted Text: {}", result, + utf8_submitted_text); - if (complete) { - broker.PushNormalDataFromApplet( - std::make_shared<IStorage>(system, std::move(output_main))); - broker.SignalStateChanged(); - } else { - broker.PushInteractiveDataFromApplet( - std::make_shared<IStorage>(system, std::move(output_sub))); - } + std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); + std::memcpy(out_data.data() + sizeof(SwkbdResult), utf8_submitted_text.data(), + utf8_submitted_text.size()); } else { - output_main[0] = 1; - complete = true; - broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(output_main))); - broker.SignalStateChanged(); + LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-16 Submitted Text: {}", result, + Common::UTF16ToUTF8(submitted_text)); + + std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); + std::memcpy(out_data.data() + sizeof(SwkbdResult), submitted_text.data(), + submitted_text.size() * sizeof(char16_t)); + } + + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); + + ExitKeyboard(); +} + +void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) { + current_text = std::move(submitted_text); + + std::vector<u8> out_data(sizeof(u64) + STRING_BUFFER_SIZE); + + if (swkbd_config_common.use_utf8) { + std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text); + const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size(); + + LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size, + utf8_submitted_text); + + std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); + std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(), + utf8_submitted_text.size()); + } else { + const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t); + + LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size, + Common::UTF16ToUTF8(current_text)); + + std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); + std::memcpy(out_data.data() + sizeof(u64), current_text.data(), + current_text.size() * sizeof(char16_t)); + } + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); +} + +void SoftwareKeyboard::SendReply(SwkbdReplyType reply_type) { + switch (reply_type) { + case SwkbdReplyType::FinishedInitialize: + ReplyFinishedInitialize(); + break; + case SwkbdReplyType::Default: + ReplyDefault(); + break; + case SwkbdReplyType::ChangedString: + ReplyChangedString(); + break; + case SwkbdReplyType::MovedCursor: + ReplyMovedCursor(); + break; + case SwkbdReplyType::MovedTab: + ReplyMovedTab(); + break; + case SwkbdReplyType::DecidedEnter: + ReplyDecidedEnter(); + break; + case SwkbdReplyType::DecidedCancel: + ReplyDecidedCancel(); + break; + case SwkbdReplyType::ChangedStringUtf8: + ReplyChangedStringUtf8(); + break; + case SwkbdReplyType::MovedCursorUtf8: + ReplyMovedCursorUtf8(); + break; + case SwkbdReplyType::DecidedEnterUtf8: + ReplyDecidedEnterUtf8(); + break; + case SwkbdReplyType::UnsetCustomizeDic: + ReplyUnsetCustomizeDic(); + break; + case SwkbdReplyType::ReleasedUserWordInfo: + ReplyReleasedUserWordInfo(); + break; + case SwkbdReplyType::UnsetCustomizedDictionaries: + ReplyUnsetCustomizedDictionaries(); + break; + case SwkbdReplyType::ChangedStringV2: + ReplyChangedStringV2(); + break; + case SwkbdReplyType::MovedCursorV2: + ReplyMovedCursorV2(); + break; + case SwkbdReplyType::ChangedStringUtf8V2: + ReplyChangedStringUtf8V2(); + break; + case SwkbdReplyType::MovedCursorUtf8V2: + ReplyMovedCursorUtf8V2(); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdReplyType={}", reply_type); + ReplyDefault(); + break; + } +} + +void SoftwareKeyboard::ChangeState(SwkbdState state) { + swkbd_state = state; + + ReplyDefault(); +} + +void SoftwareKeyboard::InitializeFrontendKeyboard() { + if (is_background) { + const auto& appear_arg = swkbd_calc_arg.appear_arg; + + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + appear_arg.ok_text.data(), appear_arg.ok_text.size()); + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + const s32 initial_cursor_position = + current_cursor_position > 0 ? current_cursor_position : 0; + + const auto text_draw_type = + max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{ok_text}, + .header_text{}, + .sub_text{}, + .guide_text{}, + .initial_text{current_text}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{appear_arg.type}, + .password_mode{SwkbdPasswordMode::Disabled}, + .text_draw_type{text_draw_type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .use_blur_background{false}, + .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + frontend.InitializeKeyboard( + true, std::move(initialize_parameters), {}, + [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { + SubmitTextInline(reply_type, submitted_text, cursor_position); + }); + } else { + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); + + std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); + + std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); + + std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); + + const u32 max_text_length = + swkbd_config_common.max_text_length > 0 && + swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? swkbd_config_common.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length + ? swkbd_config_common.min_text_length + : 0; + + const s32 initial_cursor_position = [this] { + switch (swkbd_config_common.initial_cursor_position) { + case SwkbdInitialCursorPosition::Start: + default: + return 0; + case SwkbdInitialCursorPosition::End: + return static_cast<s32>(initial_text.size()); + } + }(); + + const auto text_draw_type = [this, max_text_length] { + switch (swkbd_config_common.text_draw_type) { + case SwkbdTextDrawType::Line: + default: + return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + case SwkbdTextDrawType::Box: + case SwkbdTextDrawType::DownloadCode: + return swkbd_config_common.text_draw_type; + } + }(); + + const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box + ? swkbd_config_common.enable_return_button + : false; + + const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 + ? swkbd_config_new.disable_cancel_button + : false; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{ok_text}, + .header_text{header_text}, + .sub_text{sub_text}, + .guide_text{guide_text}, + .initial_text{initial_text}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{swkbd_config_common.type}, + .password_mode{swkbd_config_common.password_mode}, + .text_draw_type{text_draw_type}, + .key_disable_flags{swkbd_config_common.key_disable_flags}, + .use_blur_background{swkbd_config_common.use_blur_background}, + .enable_backspace_button{true}, + .enable_return_button{enable_return_button}, + .disable_cancel_button{disable_cancel_button}, + }; + + frontend.InitializeKeyboard(false, std::move(initialize_parameters), + [this](SwkbdResult result, std::u16string submitted_text) { + SubmitTextNormal(result, submitted_text); + }, + {}); + } +} + +void SoftwareKeyboard::ShowNormalKeyboard() { + frontend.ShowNormalKeyboard(); +} + +void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) { + frontend.ShowTextCheckDialog(text_check_result, text_check_message); +} + +void SoftwareKeyboard::ShowInlineKeyboard() { + if (swkbd_state != SwkbdState::InitializedIsHidden) { + return; + } + + ChangeState(SwkbdState::InitializedIsAppearing); + + const auto& appear_arg = swkbd_calc_arg.appear_arg; + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + Core::Frontend::InlineAppearParameters appear_parameters{ + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .key_top_scale_x{swkbd_calc_arg.key_top_scale_x}, + .key_top_scale_y{swkbd_calc_arg.key_top_scale_y}, + .key_top_translate_x{swkbd_calc_arg.key_top_translate_x}, + .key_top_translate_y{swkbd_calc_arg.key_top_translate_y}, + .type{appear_arg.type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .key_top_as_floating{swkbd_calc_arg.key_top_as_floating}, + .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + frontend.ShowInlineKeyboard(std::move(appear_parameters)); + + ChangeState(SwkbdState::InitializedIsShown); +} + +void SoftwareKeyboard::HideInlineKeyboard() { + if (swkbd_state != SwkbdState::InitializedIsShown) { + return; + } + + ChangeState(SwkbdState::InitializedIsDisappearing); + + frontend.HideInlineKeyboard(); + + ChangeState(SwkbdState::InitializedIsHidden); +} + +void SoftwareKeyboard::InlineTextChanged() { + Core::Frontend::InlineTextParameters text_parameters{ + .input_text{current_text}, + .cursor_position{current_cursor_position}, + }; + + frontend.InlineTextChanged(std::move(text_parameters)); +} + +void SoftwareKeyboard::ExitKeyboard() { + complete = true; + status = RESULT_SUCCESS; + + frontend.ExitKeyboard(); + + broker.SignalStateChanged(); +} + +// Inline Software Keyboard Requests + +void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: Finalize"); + + ChangeState(SwkbdState::NotInitialized); + + ExitKeyboard(); +} + +void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) { + LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); +} + +void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) { + LOG_WARNING(Service_AM, "SetCustomizeDic is not implemented."); +} + +void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: Calc"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg)); + + std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand), + sizeof(SwkbdCalcArg)); + + if (swkbd_calc_arg.flags.set_input_text) { + current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size()); + } + + if (swkbd_calc_arg.flags.set_cursor_position) { + current_cursor_position = swkbd_calc_arg.cursor_position; + } + + if (swkbd_calc_arg.flags.set_utf8_mode) { + inline_use_utf8 = swkbd_calc_arg.utf8_mode; + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg.flags.unset_customize_dic) { + ReplyUnsetCustomizeDic(); } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg.flags.unset_user_word_info) { + ReplyReleasedUserWordInfo(); + } + + if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) { + InitializeFrontendKeyboard(); + + ChangeState(SwkbdState::InitializedIsHidden); + + ReplyFinishedInitialize(); + } + + if (!swkbd_calc_arg.flags.set_initialize_arg && + (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) { + InlineTextChanged(); + } + + if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) { + ShowInlineKeyboard(); + return; + } + + if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) { + HideInlineKeyboard(); + return; + } +} + +void SoftwareKeyboard::RequestSetCustomizedDictionaries(const std::vector<u8>& request_data) { + LOG_WARNING(Service_AM, "SetCustomizedDictionaries is not implemented."); +} + +void SoftwareKeyboard::RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data) { + LOG_WARNING(Service_AM, "(STUBBED) Processing Request: UnsetCustomizedDictionaries"); + + ReplyUnsetCustomizedDictionaries(); +} + +void SoftwareKeyboard::RequestSetChangedStringV2Flag(const std::vector<u8>& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: SetChangedStringV2Flag"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); + + std::memcpy(&use_changed_string_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); +} + +void SoftwareKeyboard::RequestSetMovedCursorV2Flag(const std::vector<u8>& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: SetMovedCursorV2Flag"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); + + std::memcpy(&use_moved_cursor_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); +} + +// Inline Software Keyboard Replies + +void SoftwareKeyboard::ReplyFinishedInitialize() { + LOG_DEBUG(Service_AM, "Sending Reply: FinishedInitialize"); + + std::vector<u8> reply(REPLY_BASE_SIZE + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::FinishedInitialize); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDefault() { + LOG_DEBUG(Service_AM, "Sending Reply: Default"); + + std::vector<u8> reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::Default); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedString() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedString"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedString); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursor() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursor"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursor); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedTab() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedTab"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedTabArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedTab); + + const SwkbdMovedTabArg moved_tab_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_tab_arg, + sizeof(SwkbdMovedTabArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDecidedEnter() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnter"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdDecidedEnterArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnter); + + const SwkbdDecidedEnterArg decided_enter_arg{ + .text_length{static_cast<u32>(current_text.size())}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &decided_enter_arg, + sizeof(SwkbdDecidedEnterArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyDecidedCancel() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedCancel"); + + std::vector<u8> reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedCancel); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyChangedStringUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); } + +void SoftwareKeyboard::ReplyMovedCursorUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDecidedEnterUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnterUtf8"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdDecidedEnterArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnterUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdDecidedEnterArg decided_enter_arg{ + .text_length{static_cast<u32>(current_text.size())}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &decided_enter_arg, + sizeof(SwkbdDecidedEnterArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyUnsetCustomizeDic() { + LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizeDic"); + + std::vector<u8> reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizeDic); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyReleasedUserWordInfo() { + LOG_DEBUG(Service_AM, "Sending Reply: ReleasedUserWordInfo"); + + std::vector<u8> reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ReleasedUserWordInfo); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyUnsetCustomizedDictionaries() { + LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizedDictionaries"); + + std::vector<u8> reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizedDictionaries); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedStringV2() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringV2"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringV2); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorV2() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorV2"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorV2); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedStringUtf8V2() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8V2"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8V2); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorUtf8V2() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8V2"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8V2); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index 1d260fef8..85aeb4eb1 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -1,20 +1,14 @@ -// Copyright 2018 yuzu emulator team +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once -#include <array> -#include <string> -#include <vector> - #include "common/common_funcs.h" #include "common/common_types.h" -#include "common/swap.h" -#include "core/hle/service/am/am.h" +#include "core/hle/result.h" #include "core/hle/service/am/applets/applets.h" - -union ResultCode; +#include "core/hle/service/am/applets/software_keyboard_types.h" namespace Core { class System; @@ -22,45 +16,10 @@ class System; namespace Service::AM::Applets { -enum class KeysetDisable : u32 { - Space = 0x02, - Address = 0x04, - Percent = 0x08, - Slashes = 0x10, - Numbers = 0x40, - DownloadCode = 0x80, -}; - -struct KeyboardConfig { - INSERT_PADDING_BYTES(4); - std::array<char16_t, 9> submit_text; - u16_le left_symbol_key; - u16_le right_symbol_key; - INSERT_PADDING_BYTES(1); - KeysetDisable keyset_disable_bitmask; - u32_le initial_cursor_position; - std::array<char16_t, 65> header_text; - std::array<char16_t, 129> sub_text; - std::array<char16_t, 257> guide_text; - u32_le length_limit; - INSERT_PADDING_BYTES(4); - u32_le is_password; - INSERT_PADDING_BYTES(5); - bool utf_8; - bool draw_background; - u32_le initial_string_offset; - u32_le initial_string_size; - u32_le user_dictionary_offset; - u32_le user_dictionary_size; - bool text_check; - u64_le text_check_callback; -}; -static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size."); - class SoftwareKeyboard final : public Applet { public: - explicit SoftwareKeyboard(Core::System& system_, - const Core::Frontend::SoftwareKeyboardApplet& frontend_); + explicit SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::SoftwareKeyboardApplet& frontend_); ~SoftwareKeyboard() override; void Initialize() override; @@ -70,17 +29,139 @@ public: void ExecuteInteractive() override; void Execute() override; - void WriteText(std::optional<std::u16string> text); + /** + * Submits the input text to the application. + * If text checking is enabled, the application will verify the input text. + * If use_utf8 is enabled, the input text will be converted to UTF-8 prior to being submitted. + * This should only be used by the normal software keyboard. + * + * @param result SwkbdResult enum + * @param submitted_text UTF-16 encoded string + */ + void SubmitTextNormal(SwkbdResult result, std::u16string submitted_text); + + /** + * Submits the input text to the application. + * If utf8_mode is enabled, the input text will be converted to UTF-8 prior to being submitted. + * This should only be used by the inline software keyboard. + * + * @param reply_type SwkbdReplyType enum + * @param submitted_text UTF-16 encoded string + * @param cursor_position The current position of the text cursor + */ + void SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, + s32 cursor_position); private: - const Core::Frontend::SoftwareKeyboardApplet& frontend; + /// Initializes the normal software keyboard. + void InitializeForeground(); - KeyboardConfig config; - std::u16string initial_text; - bool complete = false; - bool is_inline = false; - std::vector<u8> final_data; + /// Initializes the inline software keyboard. + void InitializeBackground(LibraryAppletMode applet_mode); + + /// Processes the text check sent by the application. + void ProcessTextCheck(); + + /// Processes the inline software keyboard request command sent by the application. + void ProcessInlineKeyboardRequest(); + + /// Submits the input text and exits the applet. + void SubmitNormalOutputAndExit(SwkbdResult result, std::u16string submitted_text); + + /// Submits the input text for text checking. + void SubmitForTextCheck(std::u16string submitted_text); + + /// Sends a reply to the application after processing a request command. + void SendReply(SwkbdReplyType reply_type); + + /// Changes the inline keyboard state. + void ChangeState(SwkbdState state); + + /** + * Signals the frontend to initialize the software keyboard with common parameters. + * This initializes either the normal software keyboard or the inline software keyboard + * depending on the state of is_background. + * Note that this does not cause the keyboard to appear. + * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear. + */ + void InitializeFrontendKeyboard(); + + /// Signals the frontend to show the normal software keyboard. + void ShowNormalKeyboard(); + + /// Signals the frontend to show the text check dialog. + void ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, + std::u16string text_check_message); + + /// Signals the frontend to show the inline software keyboard. + void ShowInlineKeyboard(); + + /// Signals the frontend to hide the inline software keyboard. + void HideInlineKeyboard(); + + /// Signals the frontend that the current inline keyboard text has changed. + void InlineTextChanged(); + + /// Signals both the frontend and application that the software keyboard is exiting. + void ExitKeyboard(); + + // Inline Software Keyboard Requests + + void RequestFinalize(const std::vector<u8>& request_data); + void RequestSetUserWordInfo(const std::vector<u8>& request_data); + void RequestSetCustomizeDic(const std::vector<u8>& request_data); + void RequestCalc(const std::vector<u8>& request_data); + void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data); + void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data); + void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data); + void RequestSetMovedCursorV2Flag(const std::vector<u8>& request_data); + + // Inline Software Keyboard Replies + + void ReplyFinishedInitialize(); + void ReplyDefault(); + void ReplyChangedString(); + void ReplyMovedCursor(); + void ReplyMovedTab(); + void ReplyDecidedEnter(); + void ReplyDecidedCancel(); + void ReplyChangedStringUtf8(); + void ReplyMovedCursorUtf8(); + void ReplyDecidedEnterUtf8(); + void ReplyUnsetCustomizeDic(); + void ReplyReleasedUserWordInfo(); + void ReplyUnsetCustomizedDictionaries(); + void ReplyChangedStringV2(); + void ReplyMovedCursorV2(); + void ReplyChangedStringUtf8V2(); + void ReplyMovedCursorUtf8V2(); + + LibraryAppletMode applet_mode; + Core::Frontend::SoftwareKeyboardApplet& frontend; Core::System& system; + + SwkbdAppletVersion swkbd_applet_version; + + SwkbdConfigCommon swkbd_config_common; + SwkbdConfigOld swkbd_config_old; + SwkbdConfigOld2 swkbd_config_old2; + SwkbdConfigNew swkbd_config_new; + std::u16string initial_text; + + SwkbdState swkbd_state{SwkbdState::NotInitialized}; + SwkbdInitializeArg swkbd_initialize_arg; + SwkbdCalcArg swkbd_calc_arg; + bool use_changed_string_v2{false}; + bool use_moved_cursor_v2{false}; + bool inline_use_utf8{false}; + s32 current_cursor_position{}; + + std::u16string current_text; + + bool is_background{false}; + + bool complete{false}; + ResultCode status{RESULT_SUCCESS}; }; } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard_types.h b/src/core/hle/service/am/applets/software_keyboard_types.h new file mode 100644 index 000000000..21aa8e800 --- /dev/null +++ b/src/core/hle/service/am/applets/software_keyboard_types.h @@ -0,0 +1,295 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" + +namespace Service::AM::Applets { + +constexpr std::size_t MAX_OK_TEXT_LENGTH = 8; +constexpr std::size_t MAX_HEADER_TEXT_LENGTH = 64; +constexpr std::size_t MAX_SUB_TEXT_LENGTH = 128; +constexpr std::size_t MAX_GUIDE_TEXT_LENGTH = 256; +constexpr std::size_t STRING_BUFFER_SIZE = 0x7D4; + +enum class SwkbdAppletVersion : u32_le { + Version5 = 0x5, // 1.0.0 + Version65542 = 0x10006, // 2.0.0 - 2.3.0 + Version196615 = 0x30007, // 3.0.0 - 3.0.2 + Version262152 = 0x40008, // 4.0.0 - 4.1.0 + Version327689 = 0x50009, // 5.0.0 - 5.1.0 + Version393227 = 0x6000B, // 6.0.0 - 7.0.1 + Version524301 = 0x8000D, // 8.0.0+ +}; + +enum class SwkbdType : u32 { + Normal, + NumberPad, + Qwerty, + Unknown3, + Latin, + SimplifiedChinese, + TraditionalChinese, + Korean, +}; + +enum class SwkbdInitialCursorPosition : u32 { + Start, + End, +}; + +enum class SwkbdPasswordMode : u32 { + Disabled, + Enabled, +}; + +enum class SwkbdTextDrawType : u32 { + Line, + Box, + DownloadCode, +}; + +enum class SwkbdResult : u32 { + Ok, + Cancel, +}; + +enum class SwkbdTextCheckResult : u32 { + Success, + Failure, + Confirm, + Silent, +}; + +enum class SwkbdState : u32 { + NotInitialized = 0x0, + InitializedIsHidden = 0x1, + InitializedIsAppearing = 0x2, + InitializedIsShown = 0x3, + InitializedIsDisappearing = 0x4, +}; + +enum class SwkbdRequestCommand : u32 { + Finalize = 0x4, + SetUserWordInfo = 0x6, + SetCustomizeDic = 0x7, + Calc = 0xA, + SetCustomizedDictionaries = 0xB, + UnsetCustomizedDictionaries = 0xC, + SetChangedStringV2Flag = 0xD, + SetMovedCursorV2Flag = 0xE, +}; + +enum class SwkbdReplyType : u32 { + FinishedInitialize = 0x0, + Default = 0x1, + ChangedString = 0x2, + MovedCursor = 0x3, + MovedTab = 0x4, + DecidedEnter = 0x5, + DecidedCancel = 0x6, + ChangedStringUtf8 = 0x7, + MovedCursorUtf8 = 0x8, + DecidedEnterUtf8 = 0x9, + UnsetCustomizeDic = 0xA, + ReleasedUserWordInfo = 0xB, + UnsetCustomizedDictionaries = 0xC, + ChangedStringV2 = 0xD, + MovedCursorV2 = 0xE, + ChangedStringUtf8V2 = 0xF, + MovedCursorUtf8V2 = 0x10, +}; + +struct SwkbdKeyDisableFlags { + union { + u32 raw{}; + + BitField<1, 1, u32> space; + BitField<2, 1, u32> at; + BitField<3, 1, u32> percent; + BitField<4, 1, u32> slash; + BitField<5, 1, u32> backslash; + BitField<6, 1, u32> numbers; + BitField<7, 1, u32> download_code; + BitField<8, 1, u32> username; + }; +}; +static_assert(sizeof(SwkbdKeyDisableFlags) == 0x4, "SwkbdKeyDisableFlags has incorrect size."); + +struct SwkbdConfigCommon { + SwkbdType type{}; + std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{}; + char16_t left_optional_symbol_key{}; + char16_t right_optional_symbol_key{}; + bool use_prediction{}; + INSERT_PADDING_BYTES(1); + SwkbdKeyDisableFlags key_disable_flags{}; + SwkbdInitialCursorPosition initial_cursor_position{}; + std::array<char16_t, MAX_HEADER_TEXT_LENGTH + 1> header_text{}; + std::array<char16_t, MAX_SUB_TEXT_LENGTH + 1> sub_text{}; + std::array<char16_t, MAX_GUIDE_TEXT_LENGTH + 1> guide_text{}; + u32 max_text_length{}; + u32 min_text_length{}; + SwkbdPasswordMode password_mode{}; + SwkbdTextDrawType text_draw_type{}; + bool enable_return_button{}; + bool use_utf8{}; + bool use_blur_background{}; + INSERT_PADDING_BYTES(1); + u32 initial_string_offset{}; + u32 initial_string_length{}; + u32 user_dictionary_offset{}; + u32 user_dictionary_entries{}; + bool use_text_check{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(SwkbdConfigCommon) == 0x3D4, "SwkbdConfigCommon has incorrect size."); + +#pragma pack(push, 4) +// SwkbdAppletVersion 0x5, 0x10006 +struct SwkbdConfigOld { + INSERT_PADDING_WORDS(1); + VAddr text_check_callback{}; +}; +static_assert(sizeof(SwkbdConfigOld) == 0x3E0 - sizeof(SwkbdConfigCommon), + "SwkbdConfigOld has incorrect size."); + +// SwkbdAppletVersion 0x30007, 0x40008, 0x50009 +struct SwkbdConfigOld2 { + INSERT_PADDING_WORDS(1); + VAddr text_check_callback{}; + std::array<u32, 8> text_grouping{}; +}; +static_assert(sizeof(SwkbdConfigOld2) == 0x400 - sizeof(SwkbdConfigCommon), + "SwkbdConfigOld2 has incorrect size."); + +// SwkbdAppletVersion 0x6000B, 0x8000D +struct SwkbdConfigNew { + std::array<u32, 8> text_grouping{}; + std::array<u64, 24> customized_dictionary_set_entries{}; + u8 total_customized_dictionary_set_entries{}; + bool disable_cancel_button{}; + INSERT_PADDING_BYTES(18); +}; +static_assert(sizeof(SwkbdConfigNew) == 0x4C8 - sizeof(SwkbdConfigCommon), + "SwkbdConfigNew has incorrect size."); +#pragma pack(pop) + +struct SwkbdTextCheck { + SwkbdTextCheckResult text_check_result{}; + std::array<char16_t, STRING_BUFFER_SIZE / 2> text_check_message{}; +}; +static_assert(sizeof(SwkbdTextCheck) == 0x7D8, "SwkbdTextCheck has incorrect size."); + +struct SwkbdCalcArgFlags { + union { + u64 raw{}; + + BitField<0, 1, u64> set_initialize_arg; + BitField<1, 1, u64> set_volume; + BitField<2, 1, u64> appear; + BitField<3, 1, u64> set_input_text; + BitField<4, 1, u64> set_cursor_position; + BitField<5, 1, u64> set_utf8_mode; + BitField<6, 1, u64> unset_customize_dic; + BitField<7, 1, u64> disappear; + BitField<8, 1, u64> unknown; + BitField<9, 1, u64> set_key_top_translate_scale; + BitField<10, 1, u64> unset_user_word_info; + BitField<11, 1, u64> set_disable_hardware_keyboard; + }; +}; +static_assert(sizeof(SwkbdCalcArgFlags) == 0x8, "SwkbdCalcArgFlags has incorrect size."); + +struct SwkbdInitializeArg { + u32 unknown{}; + bool library_applet_mode_flag{}; + bool is_above_hos_500{}; + INSERT_PADDING_BYTES(2); +}; +static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size."); + +struct SwkbdAppearArg { + SwkbdType type{}; + std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{}; + char16_t left_optional_symbol_key{}; + char16_t right_optional_symbol_key{}; + bool use_prediction{}; + bool disable_cancel_button{}; + SwkbdKeyDisableFlags key_disable_flags{}; + u32 max_text_length{}; + u32 min_text_length{}; + bool enable_return_button{}; + INSERT_PADDING_BYTES(3); + u32 flags{}; + INSERT_PADDING_WORDS(6); +}; +static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size."); + +struct SwkbdCalcArg { + u32 unknown{}; + u16 calc_arg_size{}; + INSERT_PADDING_BYTES(2); + SwkbdCalcArgFlags flags{}; + SwkbdInitializeArg initialize_arg{}; + f32 volume{}; + s32 cursor_position{}; + SwkbdAppearArg appear_arg{}; + std::array<char16_t, 0x1FA> input_text{}; + bool utf8_mode{}; + INSERT_PADDING_BYTES(1); + bool enable_backspace_button{}; + INSERT_PADDING_BYTES(3); + bool key_top_as_floating{}; + bool footer_scalable{}; + bool alpha_enabled_in_input_mode{}; + u8 input_mode_fade_type{}; + bool disable_touch{}; + bool disable_hardware_keyboard{}; + INSERT_PADDING_BYTES(8); + f32 key_top_scale_x{}; + f32 key_top_scale_y{}; + f32 key_top_translate_x{}; + f32 key_top_translate_y{}; + f32 key_top_bg_alpha{}; + f32 footer_bg_alpha{}; + f32 balloon_scale{}; + INSERT_PADDING_WORDS(4); + u8 se_group{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size."); + +struct SwkbdChangedStringArg { + u32 text_length{}; + s32 dictionary_start_cursor_position{}; + s32 dictionary_end_cursor_position{}; + s32 cursor_position{}; +}; +static_assert(sizeof(SwkbdChangedStringArg) == 0x10, "SwkbdChangedStringArg has incorrect size."); + +struct SwkbdMovedCursorArg { + u32 text_length{}; + s32 cursor_position{}; +}; +static_assert(sizeof(SwkbdMovedCursorArg) == 0x8, "SwkbdMovedCursorArg has incorrect size."); + +struct SwkbdMovedTabArg { + u32 text_length{}; + s32 cursor_position{}; +}; +static_assert(sizeof(SwkbdMovedTabArg) == 0x8, "SwkbdMovedTabArg has incorrect size."); + +struct SwkbdDecidedEnterArg { + u32 text_length{}; +}; +static_assert(sizeof(SwkbdDecidedEnterArg) == 0x4, "SwkbdDecidedEnterArg has incorrect size."); + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 2ab420789..b28b849bc 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp @@ -208,8 +208,9 @@ void ExtractSharedFonts(Core::System& system) { } // namespace -WebBrowser::WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_) - : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {} +WebBrowser::WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::WebBrowserApplet& frontend_) + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend(frontend_), system{system_} {} WebBrowser::~WebBrowser() = default; diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h index 04c274754..5eafbae7b 100644 --- a/src/core/hle/service/am/applets/web_browser.h +++ b/src/core/hle/service/am/applets/web_browser.h @@ -25,7 +25,8 @@ namespace Service::AM::Applets { class WebBrowser final : public Applet { public: - WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_); + WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::WebBrowserApplet& frontend_); ~WebBrowser() override; @@ -63,6 +64,7 @@ private: void ExecuteWifi(); void ExecuteLobby(); + LibraryAppletMode applet_mode; const Core::Frontend::WebBrowserApplet& frontend; bool complete{false}; diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 8d657c0bf..75867e349 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -7,6 +7,7 @@ #include <vector> #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" #include "core/file_sys/common_funcs.h" #include "core/file_sys/content_archive.h" @@ -21,7 +22,6 @@ #include "core/hle/kernel/process.h" #include "core/hle/service/aoc/aoc_u.h" #include "core/loader/loader.h" -#include "core/settings.h" namespace Service::AOC { @@ -118,8 +118,10 @@ AOC_U::AOC_U(Core::System& system_) {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, {9, nullptr, "GetAddOnContentLostErrorCode"}, + {10, nullptr, "GetAddOnContentListChangedEventWithProcessId"}, {100, &AOC_U::CreateEcPurchasedEventManager, "CreateEcPurchasedEventManager"}, {101, &AOC_U::CreatePermanentEcPurchasedEventManager, "CreatePermanentEcPurchasedEventManager"}, + {110, nullptr, "CreateContentsServiceManager"}, }; // clang-format on diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/controller.cpp index 03636642b..00c174bb0 100644 --- a/src/core/hle/service/apm/controller.cpp +++ b/src/core/hle/service/apm/controller.cpp @@ -7,9 +7,9 @@ #include <utility> #include "common/logging/log.h" +#include "common/settings.h" #include "core/core_timing.h" #include "core/hle/service/apm/controller.h" -#include "core/settings.h" namespace Service::APM { diff --git a/src/core/hle/service/audio/audin_a.cpp b/src/core/hle/service/audio/audin_a.cpp index 79c3aa920..10acaad19 100644 --- a/src/core/hle/service/audio/audin_a.cpp +++ b/src/core/hle/service/audio/audin_a.cpp @@ -9,10 +9,10 @@ namespace Service::Audio { AudInA::AudInA(Core::System& system_) : ServiceFramework{system_, "audin:a"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "RequestSuspendAudioIns"}, - {1, nullptr, "RequestResumeAudioIns"}, - {2, nullptr, "GetAudioInsProcessMasterVolume"}, - {3, nullptr, "SetAudioInsProcessMasterVolume"}, + {0, nullptr, "RequestSuspend"}, + {1, nullptr, "RequestResume"}, + {2, nullptr, "GetProcessMasterVolume"}, + {3, nullptr, "SetProcessMasterVolume"}, }; // clang-format on diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 26a6deddf..ecd05e4a6 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -15,19 +15,19 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetAudioInState"}, - {1, nullptr, "StartAudioIn"}, - {2, nullptr, "StopAudioIn"}, + {1, nullptr, "Start"}, + {2, nullptr, "Stop"}, {3, nullptr, "AppendAudioInBuffer"}, {4, nullptr, "RegisterBufferEvent"}, {5, nullptr, "GetReleasedAudioInBuffer"}, {6, nullptr, "ContainsAudioInBuffer"}, - {7, nullptr, "AppendAudioInBufferWithUserEvent"}, + {7, nullptr, "AppendUacInBuffer"}, {8, nullptr, "AppendAudioInBufferAuto"}, - {9, nullptr, "GetReleasedAudioInBufferAuto"}, - {10, nullptr, "AppendAudioInBufferWithUserEventAuto"}, + {9, nullptr, "GetReleasedAudioInBuffersAuto"}, + {10, nullptr, "AppendUacInBufferAuto"}, {11, nullptr, "GetAudioInBufferCount"}, - {12, nullptr, "SetAudioInDeviceGain"}, - {13, nullptr, "GetAudioInDeviceGain"}, + {12, nullptr, "SetDeviceGain"}, + {13, nullptr, "GetDeviceGain"}, {14, nullptr, "FlushAudioInBuffers"}, }; // clang-format on diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp index 19825fd5d..3ee522b50 100644 --- a/src/core/hle/service/audio/audout_a.cpp +++ b/src/core/hle/service/audio/audout_a.cpp @@ -9,12 +9,12 @@ namespace Service::Audio { AudOutA::AudOutA(Core::System& system_) : ServiceFramework{system_, "audout:a"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "RequestSuspendAudioOuts"}, - {1, nullptr, "RequestResumeAudioOuts"}, - {2, nullptr, "GetAudioOutsProcessMasterVolume"}, - {3, nullptr, "SetAudioOutsProcessMasterVolume"}, - {4, nullptr, "GetAudioOutsProcessRecordVolume"}, - {5, nullptr, "SetAudioOutsProcessRecordVolume"}, + {0, nullptr, "RequestSuspend"}, + {1, nullptr, "RequestResume"}, + {2, nullptr, "GetProcessMasterVolume"}, + {3, nullptr, "SetProcessMasterVolume"}, + {4, nullptr, "GetProcessRecordVolume"}, + {5, nullptr, "SetProcessRecordVolume"}, }; // clang-format on diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 5ed9cb20e..5f51fca9a 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -49,11 +49,11 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, - {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, - {2, &IAudioOut::StopAudioOut, "StopAudioOut"}, + {1, &IAudioOut::StartAudioOut, "Start"}, + {2, &IAudioOut::StopAudioOut, "Stop"}, {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"}, {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, - {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"}, + {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffers"}, {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"}, {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, diff --git a/src/core/hle/service/audio/audrec_a.cpp b/src/core/hle/service/audio/audrec_a.cpp index c5ab7cad4..70fc17ae2 100644 --- a/src/core/hle/service/audio/audrec_a.cpp +++ b/src/core/hle/service/audio/audrec_a.cpp @@ -9,8 +9,8 @@ namespace Service::Audio { AudRecA::AudRecA(Core::System& system_) : ServiceFramework{system_, "audrec:a"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "RequestSuspendFinalOutputRecorders"}, - {1, nullptr, "RequestResumeFinalOutputRecorders"}, + {0, nullptr, "RequestSuspend"}, + {1, nullptr, "RequestResume"}, }; // clang-format on diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp index eb5c63c62..74a65ccff 100644 --- a/src/core/hle/service/audio/audrec_u.cpp +++ b/src/core/hle/service/audio/audrec_u.cpp @@ -13,16 +13,17 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetFinalOutputRecorderState"}, - {1, nullptr, "StartFinalOutputRecorder"}, - {2, nullptr, "StopFinalOutputRecorder"}, + {1, nullptr, "Start"}, + {2, nullptr, "Stop"}, {3, nullptr, "AppendFinalOutputRecorderBuffer"}, {4, nullptr, "RegisterBufferEvent"}, - {5, nullptr, "GetReleasedFinalOutputRecorderBuffer"}, + {5, nullptr, "GetReleasedFinalOutputRecorderBuffers"}, {6, nullptr, "ContainsFinalOutputRecorderBuffer"}, {7, nullptr, "GetFinalOutputRecorderBufferEndTime"}, {8, nullptr, "AppendFinalOutputRecorderBufferAuto"}, {9, nullptr, "GetReleasedFinalOutputRecorderBufferAuto"}, {10, nullptr, "FlushFinalOutputRecorderBuffers"}, + {11, nullptr, "AttachWorkBuffer"}, }; // clang-format on diff --git a/src/core/hle/service/audio/audren_a.cpp b/src/core/hle/service/audio/audren_a.cpp index 5e9f866f0..cf8c34a15 100644 --- a/src/core/hle/service/audio/audren_a.cpp +++ b/src/core/hle/service/audio/audren_a.cpp @@ -9,14 +9,14 @@ namespace Service::Audio { AudRenA::AudRenA(Core::System& system_) : ServiceFramework{system_, "audren:a"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "RequestSuspendAudioRenderers"}, - {1, nullptr, "RequestResumeAudioRenderers"}, - {2, nullptr, "GetAudioRenderersProcessMasterVolume"}, - {3, nullptr, "SetAudioRenderersProcessMasterVolume"}, + {0, nullptr, "RequestSuspend"}, + {1, nullptr, "RequestResume"}, + {2, nullptr, "GetProcessMasterVolume"}, + {3, nullptr, "SetProcessMasterVolume"}, {4, nullptr, "RegisterAppletResourceUserId"}, {5, nullptr, "UnregisterAppletResourceUserId"}, - {6, nullptr, "GetAudioRenderersProcessRecordVolume"}, - {7, nullptr, "SetAudioRenderersProcessRecordVolume"}, + {6, nullptr, "GetProcessRecordVolume"}, + {7, nullptr, "SetProcessRecordVolume"}, }; // clang-format on diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index b2b2ffc5a..572be8e00 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -332,9 +332,9 @@ AudRenU::AudRenU(Core::System& system_) : ServiceFramework{system_, "audren:u"} // clang-format off static const FunctionInfo functions[] = { {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, - {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"}, + {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetWorkBufferSize"}, {2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"}, - {3, &AudRenU::OpenAudioRendererAuto, "OpenAudioRendererAuto"}, + {3, &AudRenU::OpenAudioRendererForManualExecution, "OpenAudioRendererForManualExecution"}, {4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"}, }; // clang-format on @@ -665,7 +665,7 @@ void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { rb.PushIpcInterface<IAudioDevice>(system, Common::MakeMagic('R', 'E', 'V', '1')); } -void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) { +void AudRenU::OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); OpenAudioRendererImpl(ctx); diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index d693dc406..37e8b4716 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -25,7 +25,7 @@ private: void OpenAudioRenderer(Kernel::HLERequestContext& ctx); void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); void GetAudioDeviceService(Kernel::HLERequestContext& ctx); - void OpenAudioRendererAuto(Kernel::HLERequestContext& ctx); + void OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx); void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx); void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/audio/codecctl.cpp b/src/core/hle/service/audio/codecctl.cpp index 94afec1b6..42961d908 100644 --- a/src/core/hle/service/audio/codecctl.cpp +++ b/src/core/hle/service/audio/codecctl.cpp @@ -8,19 +8,19 @@ namespace Service::Audio { CodecCtl::CodecCtl(Core::System& system_) : ServiceFramework{system_, "codecctl"} { static const FunctionInfo functions[] = { - {0, nullptr, "InitializeCodecController"}, - {1, nullptr, "FinalizeCodecController"}, - {2, nullptr, "SleepCodecController"}, - {3, nullptr, "WakeCodecController"}, - {4, nullptr, "SetCodecVolume"}, - {5, nullptr, "GetCodecVolumeMax"}, - {6, nullptr, "GetCodecVolumeMin"}, - {7, nullptr, "SetCodecActiveTarget"}, - {8, nullptr, "GetCodecActiveTarget"}, - {9, nullptr, "BindCodecHeadphoneMicJackInterrupt"}, - {10, nullptr, "IsCodecHeadphoneMicJackInserted"}, - {11, nullptr, "ClearCodecHeadphoneMicJackInterrupt"}, - {12, nullptr, "IsCodecDeviceRequested"}, + {0, nullptr, "Initialize"}, + {1, nullptr, "Finalize"}, + {2, nullptr, "Sleep"}, + {3, nullptr, "Wake"}, + {4, nullptr, "SetVolume"}, + {5, nullptr, "GetVolumeMax"}, + {6, nullptr, "GetVolumeMin"}, + {7, nullptr, "SetActiveTarget"}, + {8, nullptr, "GetActiveTarget"}, + {9, nullptr, "BindHeadphoneMicJackInterrupt"}, + {10, nullptr, "IsHeadphoneMicJackInserted"}, + {11, nullptr, "ClearHeadphoneMicJackInterrupt"}, + {12, nullptr, "IsRequested"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index ea3414fd2..19c578b3a 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -297,6 +297,10 @@ HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, {2, nullptr, "OpenOpusDecoderForMultiStream"}, {3, nullptr, "GetWorkBufferSizeForMultiStream"}, + {4, nullptr, "OpenHardwareOpusDecoderEx"}, + {5, nullptr, "GetWorkBufferSizeEx"}, + {6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"}, + {7, nullptr, "GetWorkBufferSizeForMultiStreamEx"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index e43f3f47f..78c047bd2 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp @@ -9,6 +9,7 @@ #include "common/hex_util.h" #include "common/logging/backend.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_libzip.h" @@ -16,7 +17,6 @@ #include "core/frontend/applets/error.h" #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/bcat/backend/boxcat.h" -#include "core/settings.h" namespace Service::BCAT { namespace { diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 503109fdd..c7dd04a6e 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -7,6 +7,7 @@ #include "backend/boxcat.h" #include "common/hex_util.h" #include "common/logging/log.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/core.h" #include "core/file_sys/vfs.h" @@ -18,7 +19,6 @@ #include "core/hle/service/bcat/bcat.h" #include "core/hle/service/bcat/module.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/settings.h" namespace Service::BCAT { @@ -155,10 +155,12 @@ public: {30210, nullptr, "SetDeliveryTaskTimer"}, {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"}, {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, + {90101, nullptr, "Unknown90101"}, {90200, nullptr, "GetDeliveryList"}, {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"}, {90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"}, {90300, nullptr, "GetPushNotificationLog"}, + {90301, nullptr, "Unknown90301"}, }; // clang-format on RegisterHandlers(functions); diff --git a/src/core/hle/service/bpc/bpc.cpp b/src/core/hle/service/bpc/bpc.cpp index e4630320e..78e01d8d8 100644 --- a/src/core/hle/service/bpc/bpc.cpp +++ b/src/core/hle/service/bpc/bpc.cpp @@ -29,8 +29,8 @@ public: {11, nullptr, "CreateWakeupTimerEx"}, {12, nullptr, "GetLastEnabledWakeupTimerType"}, {13, nullptr, "CleanAllWakeupTimers"}, - {14, nullptr, "Unknown"}, - {15, nullptr, "Unknown2"}, + {14, nullptr, "GetPowerButton"}, + {15, nullptr, "SetEnableWakeupTimer"}, }; // clang-format on diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 17a2ac899..af3a5842d 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -156,6 +156,25 @@ public: {97, nullptr, "RegisterBleHidEvent"}, {98, nullptr, "SetBleScanParameter"}, {99, nullptr, "MoveToSecondaryPiconet"}, + {100, nullptr, "IsBluetoothEnabled"}, + {128, nullptr, "AcquireAudioEvent"}, + {129, nullptr, "GetAudioEventInfo"}, + {130, nullptr, "OpenAudioConnection"}, + {131, nullptr, "CloseAudioConnection"}, + {132, nullptr, "OpenAudioOut"}, + {133, nullptr, "CloseAudioOut"}, + {134, nullptr, "AcquireAudioOutStateChangedEvent"}, + {135, nullptr, "StartAudioOut"}, + {136, nullptr, "StopAudioOut"}, + {137, nullptr, "GetAudioOutState"}, + {138, nullptr, "GetAudioOutFeedingCodec"}, + {139, nullptr, "GetAudioOutFeedingParameter"}, + {140, nullptr, "AcquireAudioOutBufferAvailableEvent"}, + {141, nullptr, "SendAudioData"}, + {142, nullptr, "AcquireAudioControlInputStateChangedEvent"}, + {143, nullptr, "GetAudioControlInputState"}, + {144, nullptr, "AcquireAudioConnectionStateChangedEvent"}, + {145, nullptr, "GetConnectedAudioDevice"}, {256, nullptr, "IsManufacturingMode"}, {257, nullptr, "EmulateBluetoothCrash"}, {258, nullptr, "GetBleChannelMap"}, diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 9cf2ee92a..d1ebc2388 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -223,6 +223,7 @@ public: {10, nullptr, "GetGattClientDisconnectionReason"}, {11, nullptr, "GetBleConnectionParameter"}, {12, nullptr, "GetBleConnectionParameterRequest"}, + {13, nullptr, "Unknown13"}, }; // clang-format on diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 1fe4f0e14..6220e9f77 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp @@ -49,6 +49,7 @@ CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} { {16, nullptr, "GetAlbumMountResult"}, {17, nullptr, "GetAlbumUsage16"}, {18, nullptr, "Unknown18"}, + {19, nullptr, "Unknown19"}, {100, nullptr, "GetAlbumFileCountEx0"}, {101, nullptr, "GetAlbumFileListEx0"}, {202, nullptr, "SaveEditedScreenShot"}, diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index 842316a2e..10b8d54b1 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp @@ -43,6 +43,7 @@ CAPS_U::CAPS_U(Core::System& system_) : ServiceFramework{system_, "caps:u"} { {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, {142, &CAPS_U::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"}, {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, + {144, nullptr, "GetAllAlbumFileList3AaeAruid"}, {60002, nullptr, "OpenAccessorSessionForApplication"}, }; // clang-format on diff --git a/src/core/hle/service/erpt/erpt.cpp b/src/core/hle/service/erpt/erpt.cpp index 4924c61c3..c767926a4 100644 --- a/src/core/hle/service/erpt/erpt.cpp +++ b/src/core/hle/service/erpt/erpt.cpp @@ -16,7 +16,7 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "SubmitContext"}, - {1, nullptr, "CreateReport"}, + {1, nullptr, "CreateReportV0"}, {2, nullptr, "SetInitialLaunchSettingsCompletionTime"}, {3, nullptr, "ClearInitialLaunchSettingsCompletionTime"}, {4, nullptr, "UpdatePowerOnTime"}, @@ -26,6 +26,11 @@ public: {8, nullptr, "ClearApplicationLaunchTime"}, {9, nullptr, "SubmitAttachment"}, {10, nullptr, "CreateReportWithAttachments"}, + {11, nullptr, "CreateReport"}, + {20, nullptr, "RegisterRunningApplet"}, + {21, nullptr, "UnregisterRunningApplet"}, + {22, nullptr, "UpdateAppletSuspendedDuration"}, + {30, nullptr, "InvalidateForcedShutdownDetection"}, }; // clang-format on diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index b15c737e1..72ad273b2 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -6,6 +6,7 @@ #include "common/assert.h" #include "common/file_util.h" +#include "common/settings.h" #include "core/core.h" #include "core/file_sys/bis_factory.h" #include "core/file_sys/card_image.h" @@ -26,7 +27,6 @@ #include "core/hle/service/filesystem/fsp_pr.h" #include "core/hle/service/filesystem/fsp_srv.h" #include "core/loader/loader.h" -#include "core/settings.h" namespace Service::FileSystem { diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 9cc260515..a0215c4d7 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -118,9 +118,13 @@ public: explicit IFile(Core::System& system_, FileSys::VirtualFile backend_) : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { static const FunctionInfo functions[] = { - {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, - {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"}, - {4, &IFile::GetSize, "GetSize"}, {5, nullptr, "OperateRange"}, + {0, &IFile::Read, "Read"}, + {1, &IFile::Write, "Write"}, + {2, &IFile::Flush, "Flush"}, + {3, &IFile::SetSize, "SetSize"}, + {4, &IFile::GetSize, "GetSize"}, + {5, nullptr, "OperateRange"}, + {6, nullptr, "OperateRangeWithBuffer"}, }; RegisterHandlers(functions); } @@ -708,7 +712,10 @@ FSP_SRV::FSP_SRV(Core::System& system_) {84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"}, {85, nullptr, "OpenSaveDataTransferManagerForSaveDataRepair"}, {86, nullptr, "OpenSaveDataMover"}, + {87, nullptr, "OpenSaveDataTransferManagerForRepair"}, {100, nullptr, "OpenImageDirectoryFileSystem"}, + {101, nullptr, "OpenBaseFileSystem"}, + {102, nullptr, "FormatBaseFileSystem"}, {110, nullptr, "OpenContentStorageFileSystem"}, {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"}, {130, nullptr, "OpenCustomStorageFileSystem"}, @@ -764,10 +771,12 @@ FSP_SRV::FSP_SRV(Core::System& system_) {1008, nullptr, "OpenRegisteredUpdatePartition"}, {1009, nullptr, "GetAndClearMemoryReportInfo"}, {1010, nullptr, "SetDataStorageRedirectTarget"}, - {1011, &FSP_SRV::GetAccessLogVersionInfo, "GetAccessLogVersionInfo"}, + {1011, &FSP_SRV::GetProgramIndexForAccessLog, "GetProgramIndexForAccessLog"}, {1012, nullptr, "GetFsStackUsage"}, {1013, nullptr, "UnsetSaveDataRootPath"}, {1014, nullptr, "OutputMultiProgramTagAccessLog"}, + {1016, nullptr, "FlushAccessLogOnSdCard"}, + {1017, nullptr, "OutputApplicationInfoAccessLog"}, {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, {1200, &FSP_SRV::OpenMultiCommitManager, "OpenMultiCommitManager"}, @@ -1051,7 +1060,7 @@ void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } -void FSP_SRV::GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx) { +void FSP_SRV::GetProgramIndexForAccessLog(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called"); IPC::ResponseBuilder rb{ctx, 4}; diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 8ed933279..b01b924eb 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -53,7 +53,7 @@ private: void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); - void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); + void GetProgramIndexForAccessLog(Kernel::HLERequestContext& ctx); void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); FileSystemController& fsc; diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 0a6621ef2..a35979053 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -38,7 +38,7 @@ public: {10600, nullptr, "DeclareOpenOnlinePlaySession"}, {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"}, {10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"}, - {10700, nullptr, "GetPlayHistoryRegistrationKey"}, + {10700, &IFriendService::GetPlayHistoryRegistrationKey, "GetPlayHistoryRegistrationKey"}, {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"}, {10702, nullptr, "AddPlayHistory"}, {11000, nullptr, "GetProfileImageUrl"}, @@ -153,6 +153,18 @@ private: rb.Push(RESULT_SUCCESS); } + void GetPlayHistoryRegistrationKey(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto local_play = rp.Pop<bool>(); + const auto uuid = rp.PopRaw<Common::UUID>(); + + LOG_WARNING(Service_Friend, "(STUBBED) called local_play={} uuid={}", local_play, + uuid.Format()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + void GetFriendList(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto friend_offset = rp.Pop<u32>(); diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp index fc77e7286..322125135 100644 --- a/src/core/hle/service/glue/arp.cpp +++ b/src/core/hle/service/glue/arp.cpp @@ -41,6 +41,12 @@ ARP_R::ARP_R(Core::System& system_, const ARPManager& manager_) {1, &ARP_R::GetApplicationLaunchPropertyWithApplicationId, "GetApplicationLaunchPropertyWithApplicationId"}, {2, &ARP_R::GetApplicationControlProperty, "GetApplicationControlProperty"}, {3, &ARP_R::GetApplicationControlPropertyWithApplicationId, "GetApplicationControlPropertyWithApplicationId"}, + {4, nullptr, "GetApplicationInstanceUnregistrationNotifier"}, + {5, nullptr, "ListApplicationInstanceId"}, + {6, nullptr, "GetMicroApplicationInstanceId"}, + {7, nullptr, "GetApplicationCertificate"}, + {9998, nullptr, "GetPreomiaApplicationLaunchProperty"}, + {9999, nullptr, "GetPreomiaApplicationControlProperty"}, }; // clang-format on @@ -243,7 +249,8 @@ ARP_W::ARP_W(Core::System& system_, ARPManager& manager_) // clang-format off static const FunctionInfo functions[] = { {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"}, - {1, &ARP_W::DeleteProperties, "DeleteProperties"}, + {1, &ARP_W::UnregisterApplicationInstance , "UnregisterApplicationInstance "}, + {2, nullptr, "AcquireUpdater"}, }; // clang-format on @@ -270,7 +277,7 @@ void ARP_W::AcquireRegistrar(Kernel::HLERequestContext& ctx) { rb.PushIpcInterface(registrar); } -void ARP_W::DeleteProperties(Kernel::HLERequestContext& ctx) { +void ARP_W::UnregisterApplicationInstance(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto process_id = rp.PopRaw<u64>(); diff --git a/src/core/hle/service/glue/arp.h b/src/core/hle/service/glue/arp.h index 34b412e26..0df3c5e1f 100644 --- a/src/core/hle/service/glue/arp.h +++ b/src/core/hle/service/glue/arp.h @@ -32,7 +32,7 @@ public: private: void AcquireRegistrar(Kernel::HLERequestContext& ctx); - void DeleteProperties(Kernel::HLERequestContext& ctx); + void UnregisterApplicationInstance(Kernel::HLERequestContext& ctx); ARPManager& manager; std::shared_ptr<IRegistrar> registrar; diff --git a/src/core/hle/service/glue/bgtc.cpp b/src/core/hle/service/glue/bgtc.cpp index a478b68e1..daecfff15 100644 --- a/src/core/hle/service/glue/bgtc.cpp +++ b/src/core/hle/service/glue/bgtc.cpp @@ -2,6 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/service/glue/bgtc.h" namespace Service::Glue { @@ -9,6 +12,26 @@ namespace Service::Glue { BGTC_T::BGTC_T(Core::System& system_) : ServiceFramework{system_, "bgtc:t"} { // clang-format off static const FunctionInfo functions[] = { + {100, &BGTC_T::OpenTaskService, "OpenTaskService"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +BGTC_T::~BGTC_T() = default; + +void BGTC_T::OpenTaskService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BGTC, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ITaskService>(system); +} + +ITaskService::ITaskService(Core::System& system_) : ServiceFramework{system_, "ITaskService"} { + // clang-format off + static const FunctionInfo functions[] = { {1, nullptr, "NotifyTaskStarting"}, {2, nullptr, "NotifyTaskFinished"}, {3, nullptr, "GetTriggerEvent"}, @@ -20,16 +43,18 @@ BGTC_T::BGTC_T(Core::System& system_) : ServiceFramework{system_, "bgtc:t"} { {13, nullptr, "UnscheduleTask"}, {14, nullptr, "GetScheduleEvent"}, {15, nullptr, "SchedulePeriodicTask"}, + {16, nullptr, "Unknown16"}, {101, nullptr, "GetOperationMode"}, {102, nullptr, "WillDisconnectNetworkWhenEnteringSleep"}, {103, nullptr, "WillStayHalfAwakeInsteadSleep"}, + {200, nullptr, "Unknown200"}, }; // clang-format on RegisterHandlers(functions); } -BGTC_T::~BGTC_T() = default; +ITaskService::~ITaskService() = default; BGTC_SC::BGTC_SC(Core::System& system_) : ServiceFramework{system_, "bgtc:sc"} { // clang-format off diff --git a/src/core/hle/service/glue/bgtc.h b/src/core/hle/service/glue/bgtc.h index 906116ba6..4c0142fd5 100644 --- a/src/core/hle/service/glue/bgtc.h +++ b/src/core/hle/service/glue/bgtc.h @@ -16,6 +16,14 @@ class BGTC_T final : public ServiceFramework<BGTC_T> { public: explicit BGTC_T(Core::System& system_); ~BGTC_T() override; + + void OpenTaskService(Kernel::HLERequestContext& ctx); +}; + +class ITaskService final : public ServiceFramework<ITaskService> { +public: + explicit ITaskService(Core::System& system_); + ~ITaskService() override; }; class BGTC_SC final : public ServiceFramework<BGTC_SC> { diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index ad251ed4a..a460f2f79 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -4,9 +4,9 @@ #include <cstring> #include "common/common_types.h" +#include "common/settings.h" #include "core/core_timing.h" #include "core/hle/service/hid/controllers/debug_pad.h" -#include "core/settings.h" namespace Service::HID { diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index 555b29d76..0593d7d39 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h @@ -8,10 +8,10 @@ #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/settings.h" #include "common/swap.h" #include "core/frontend/input.h" #include "core/hle/service/hid/controllers/controller_base.h" -#include "core/settings.h" namespace Service::HID { class Controller_DebugPad final : public ControllerBase { diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 93c43a203..155808f6a 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -5,10 +5,10 @@ #include <cstring> #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" #include "core/hle/service/hid/controllers/gesture.h" -#include "core/settings.h" namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index c4a59147d..18b76038f 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -4,9 +4,9 @@ #include <cstring> #include "common/common_types.h" +#include "common/settings.h" #include "core/core_timing.h" #include "core/hle/service/hid/controllers/keyboard.h" -#include "core/settings.h" namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index b5b281752..e72948591 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h @@ -8,10 +8,10 @@ #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/settings.h" #include "common/swap.h" #include "core/frontend/input.h" #include "core/hle/service/hid/controllers/controller_base.h" -#include "core/settings.h" namespace Service::HID { class Controller_Keyboard final : public ControllerBase { diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 3b432a36e..0ec0c2b94 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h @@ -7,10 +7,10 @@ #include <array> #include "common/bit_field.h" #include "common/common_types.h" +#include "common/settings.h" #include "common/swap.h" #include "core/frontend/input.h" #include "core/hle/service/hid/controllers/controller_base.h" -#include "core/settings.h" namespace Service::HID { class Controller_Mouse final : public ControllerBase { diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 70b9f3824..783386fcf 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -9,6 +9,7 @@ #include "common/bit_field.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/input.h" @@ -17,7 +18,6 @@ #include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/hid/controllers/npad.h" -#include "core/settings.h" namespace Service::HID { constexpr s32 HID_JOYSTICK_MAX = 0x7fff; @@ -413,12 +413,16 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); } - if (controller_type == NPadControllerType::JoyLeft || - controller_type == NPadControllerType::JoyRight) { + if (controller_type == NPadControllerType::JoyLeft) { pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); } + if (controller_type == NPadControllerType::JoyRight) { + pad_state.right_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.right_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); + } + if (controller_type == NPadControllerType::GameCube) { trigger_entry.l_analog = static_cast<s32>( button_state[ZL - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0); @@ -1134,6 +1138,10 @@ void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_prot unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled; } +void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { + analog_stick_use_center_clamp = use_center_clamp; +} + void Controller_NPad::ClearAllConnectedControllers() { for (auto& controller : connected_controllers) { if (controller.is_connected && controller.type != NPadControllerType::None) { diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index bc2e6779d..14d0ac067 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -8,10 +8,10 @@ #include <atomic> #include "common/bit_field.h" #include "common/common_types.h" +#include "common/settings.h" #include "core/frontend/input.h" #include "core/hle/kernel/object.h" #include "core/hle/service/hid/controllers/controller_base.h" -#include "core/settings.h" namespace Kernel { class KEvent; @@ -219,6 +219,7 @@ public: LedPattern GetLedPattern(u32 npad_id); bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const; void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id); + void SetAnalogStickUseCenterClamp(bool use_center_clamp); void ClearAllConnectedControllers(); void DisconnectAllConnectedControllers(); void ConnectAllDisconnectedControllers(); @@ -577,6 +578,7 @@ private: std::array<std::array<bool, 2>, 10> vibration_devices_mounted{}; std::array<ControllerHolder, 10> connected_controllers{}; std::array<bool, 10> unintended_home_button_input_protection{}; + bool analog_stick_use_center_clamp{}; GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; bool sixaxis_sensors_enabled{true}; f32 sixaxis_fusion_parameter1{}; diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index be60492a4..b5f8077be 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -6,11 +6,11 @@ #include <cstring> #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" #include "core/frontend/input.h" #include "core/hle/service/hid/controllers/touchscreen.h" -#include "core/settings.h" namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index ba27bbb05..4c1c0ac68 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -5,6 +5,7 @@ #include <array> #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" @@ -23,7 +24,6 @@ #include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/xcd.h" #include "core/hle/service/service.h" -#include "core/settings.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/controllers/debug_pad.h" @@ -263,7 +263,7 @@ Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} { {131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"}, {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, - {134, nullptr, "SetNpadAnalogStickUseCenterClamp"}, + {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"}, {135, nullptr, "SetNpadCaptureButtonAssignment"}, {136, nullptr, "ClearNpadCaptureButtonAssignment"}, {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, @@ -278,6 +278,7 @@ Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} { {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, + {212, nullptr, "SendVibrationValueInBool"}, {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, @@ -1087,6 +1088,27 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c rb.Push(RESULT_SUCCESS); } +void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool analog_stick_use_center_clamp; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp); + + LOG_WARNING(Service_HID, + "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}", + parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; @@ -1553,6 +1575,7 @@ public: {11, nullptr, "SetTouchScreenAutoPilotState"}, {12, nullptr, "UnsetTouchScreenAutoPilotState"}, {13, nullptr, "GetTouchScreenConfiguration"}, + {14, nullptr, "ProcessTouchScreenAutoTune"}, {20, nullptr, "DeactivateMouse"}, {21, nullptr, "SetMouseAutoPilotState"}, {22, nullptr, "UnsetMouseAutoPilotState"}, @@ -1562,6 +1585,7 @@ public: {50, nullptr, "DeactivateXpad"}, {51, nullptr, "SetXpadAutoPilotState"}, {52, nullptr, "UnsetXpadAutoPilotState"}, + {53, nullptr, "DeactivateJoyXpad"}, {60, nullptr, "ClearNpadSystemCommonPolicy"}, {61, nullptr, "DeactivateNpad"}, {62, nullptr, "ForceDisconnectNpad"}, @@ -1632,6 +1656,11 @@ public: {244, nullptr, "RequestKuinaFirmwareVersion"}, {245, nullptr, "GetKuinaFirmwareVersion"}, {246, nullptr, "GetVidPid"}, + {247, nullptr, "GetAnalogStickCalibrationValue"}, + {248, nullptr, "GetUniquePadIdsFull"}, + {249, nullptr, "ConnectUniquePad"}, + {250, nullptr, "IsVirtual"}, + {251, nullptr, "GetAnalogStickModuleParam"}, {301, nullptr, "GetAbstractedPadHandles"}, {302, nullptr, "GetAbstractedPadState"}, {303, nullptr, "GetAbstractedPadsState"}, @@ -1652,12 +1681,16 @@ public: {401, nullptr, "DisableRailDeviceFiltering"}, {402, nullptr, "EnableWiredPairing"}, {403, nullptr, "EnableShipmentModeAutoClear"}, + {404, nullptr, "SetRailEnabled"}, {500, nullptr, "SetFactoryInt"}, {501, nullptr, "IsFactoryBootEnabled"}, {550, nullptr, "SetAnalogStickModelDataTemporarily"}, {551, nullptr, "GetAnalogStickModelData"}, {552, nullptr, "ResetAnalogStickModelData"}, {600, nullptr, "ConvertPadState"}, + {650, nullptr, "AddButtonPlayData"}, + {651, nullptr, "StartButtonPlayData"}, + {652, nullptr, "StopButtonPlayData"}, {2000, nullptr, "DeactivateDigitizer"}, {2001, nullptr, "SetDigitizerAutoPilotState"}, {2002, nullptr, "UnsetDigitizerAutoPilotState"}, @@ -1689,6 +1722,8 @@ public: {215, nullptr, "IsNfcActivated"}, {230, nullptr, "AcquireIrSensorEventHandle"}, {231, nullptr, "ActivateIrSensor"}, + {232, nullptr, "GetIrSensorState"}, + {233, nullptr, "GetXcdHandleForNpadWithIrSensor"}, {301, nullptr, "ActivateNpadSystem"}, {303, nullptr, "ApplyNpadSystemCommonPolicy"}, {304, nullptr, "EnableAssigningSingleOnSlSrPress"}, @@ -1703,9 +1738,16 @@ public: {313, nullptr, "GetNpadCaptureButtonAssignment"}, {314, nullptr, "GetAppletFooterUiType"}, {315, nullptr, "GetAppletDetailedUiType"}, + {316, nullptr, "GetNpadInterfaceType"}, + {317, nullptr, "GetNpadLeftRightInterfaceType"}, + {318, nullptr, "HasBattery"}, + {319, nullptr, "HasLeftRightBattery"}, {321, nullptr, "GetUniquePadsFromNpad"}, {322, nullptr, "GetIrSensorState"}, {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, + {324, nullptr, "GetUniquePadButtonSet"}, + {325, nullptr, "GetUniquePadColor"}, + {326, nullptr, "GetUniquePadAppletDetailedUiType"}, {500, nullptr, "SetAppletResourceUserId"}, {501, nullptr, "RegisterAppletResourceUserId"}, {502, nullptr, "UnregisterAppletResourceUserId"}, @@ -1716,10 +1758,13 @@ public: {511, nullptr, "GetVibrationMasterVolume"}, {512, nullptr, "BeginPermitVibrationSession"}, {513, nullptr, "EndPermitVibrationSession"}, + {514, nullptr, "Unknown514"}, {520, nullptr, "EnableHandheldHids"}, {521, nullptr, "DisableHandheldHids"}, {522, nullptr, "SetJoyConRailEnabled"}, {523, nullptr, "IsJoyConRailEnabled"}, + {524, nullptr, "IsHandheldHidsEnabled"}, + {525, nullptr, "IsJoyConAttachedOnAllRail"}, {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, {541, nullptr, "GetPlayReportControllerUsages"}, {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, @@ -1795,6 +1840,65 @@ public: {1154, nullptr, "IsFirmwareAvailableForNotification"}, {1155, nullptr, "SetForceHandheldStyleVibration"}, {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, + {1157, nullptr, "CancelConnectionTrigger"}, + {1200, nullptr, "IsButtonConfigSupported"}, + {1201, nullptr, "IsButtonConfigEmbeddedSupported"}, + {1202, nullptr, "DeleteButtonConfig"}, + {1203, nullptr, "DeleteButtonConfigEmbedded"}, + {1204, nullptr, "SetButtonConfigEnabled"}, + {1205, nullptr, "SetButtonConfigEmbeddedEnabled"}, + {1206, nullptr, "IsButtonConfigEnabled"}, + {1207, nullptr, "IsButtonConfigEmbeddedEnabled"}, + {1208, nullptr, "SetButtonConfigEmbedded"}, + {1209, nullptr, "SetButtonConfigFull"}, + {1210, nullptr, "SetButtonConfigLeft"}, + {1211, nullptr, "SetButtonConfigRight"}, + {1212, nullptr, "GetButtonConfigEmbedded"}, + {1213, nullptr, "GetButtonConfigFull"}, + {1214, nullptr, "GetButtonConfigLeft"}, + {1215, nullptr, "GetButtonConfigRight"}, + {1250, nullptr, "IsCustomButtonConfigSupported"}, + {1251, nullptr, "IsDefaultButtonConfigEmbedded"}, + {1252, nullptr, "IsDefaultButtonConfigFull"}, + {1253, nullptr, "IsDefaultButtonConfigLeft"}, + {1254, nullptr, "IsDefaultButtonConfigRight"}, + {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"}, + {1256, nullptr, "IsButtonConfigStorageFullEmpty"}, + {1257, nullptr, "IsButtonConfigStorageLeftEmpty"}, + {1258, nullptr, "IsButtonConfigStorageRightEmpty"}, + {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"}, + {1260, nullptr, "GetButtonConfigStorageFullDeprecated"}, + {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"}, + {1262, nullptr, "GetButtonConfigStorageRightDeprecated"}, + {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"}, + {1264, nullptr, "SetButtonConfigStorageFullDeprecated"}, + {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"}, + {1266, nullptr, "SetButtonConfigStorageRightDeprecated"}, + {1267, nullptr, "DeleteButtonConfigStorageEmbedded"}, + {1268, nullptr, "DeleteButtonConfigStorageFull"}, + {1269, nullptr, "DeleteButtonConfigStorageLeft"}, + {1270, nullptr, "DeleteButtonConfigStorageRight"}, + {1271, nullptr, "IsUsingCustomButtonConfig"}, + {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, + {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, + {1274, nullptr, "SetDefaultButtonConfig"}, + {1275, nullptr, "SetAllDefaultButtonConfig"}, + {1276, nullptr, "SetHidButtonConfigEmbedded"}, + {1277, nullptr, "SetHidButtonConfigFull"}, + {1278, nullptr, "SetHidButtonConfigLeft"}, + {1279, nullptr, "SetHidButtonConfigRight"}, + {1280, nullptr, "GetHidButtonConfigEmbedded"}, + {1281, nullptr, "GetHidButtonConfigFull"}, + {1282, nullptr, "GetHidButtonConfigLeft"}, + {1283, nullptr, "GetHidButtonConfigRight"}, + {1284, nullptr, "GetButtonConfigStorageEmbedded"}, + {1285, nullptr, "GetButtonConfigStorageFull"}, + {1286, nullptr, "GetButtonConfigStorageLeft"}, + {1287, nullptr, "GetButtonConfigStorageRight"}, + {1288, nullptr, "SetButtonConfigStorageEmbedded"}, + {1289, nullptr, "SetButtonConfigStorageFull"}, + {1290, nullptr, "DeleteButtonConfigStorageRight"}, + {1291, nullptr, "DeleteButtonConfigStorageRight"}, }; // clang-format on diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 36ed228c8..c2bdd39a3 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -129,6 +129,7 @@ private: void SwapNpadAssignment(Kernel::HLERequestContext& ctx); void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx); void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx); + void SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx); void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx); void SendVibrationValue(Kernel::HLERequestContext& ctx); void GetActualVibrationValue(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/hid/xcd.cpp b/src/core/hle/service/hid/xcd.cpp index 43a8840d0..b1efa3d05 100644 --- a/src/core/hle/service/hid/xcd.cpp +++ b/src/core/hle/service/hid/xcd.cpp @@ -28,6 +28,8 @@ XCD_SYS::XCD_SYS(Core::System& system_) : ServiceFramework{system_, "xcd:sys"} { {20, nullptr, "StartMifareWrite"}, {101, nullptr, "GetAwakeTriggerReasonForLeftRail"}, {102, nullptr, "GetAwakeTriggerReasonForRightRail"}, + {103, nullptr, "GetAwakeTriggerBatteryLevelTransitionForLeftRail"}, + {104, nullptr, "GetAwakeTriggerBatteryLevelTransitionForRightRail"}, }; // clang-format on diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index d111c1357..c8bc60ad1 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -118,9 +118,9 @@ public: explicit DebugMonitor(Core::System& system_) : ServiceFramework{system_, "ldr:dmnt"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "AddProcessToDebugLaunchQueue"}, - {1, nullptr, "ClearDebugLaunchQueue"}, - {2, nullptr, "GetNsoInfos"}, + {0, nullptr, "SetProgramArgument"}, + {1, nullptr, "FlushArguments"}, + {2, nullptr, "GetProcessModuleInfo"}, }; // clang-format on @@ -135,8 +135,8 @@ public: static const FunctionInfo functions[] = { {0, nullptr, "CreateProcess"}, {1, nullptr, "GetProgramInfo"}, - {2, nullptr, "RegisterTitle"}, - {3, nullptr, "UnregisterTitle"}, + {2, nullptr, "PinProgram"}, + {3, nullptr, "UnpinProgram"}, {4, nullptr, "SetEnabledProgramVerification"}, }; // clang-format on @@ -150,8 +150,8 @@ public: explicit Shell(Core::System& system_) : ServiceFramework{system_, "ldr:shel"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "AddProcessToLaunchQueue"}, - {1, nullptr, "ClearLaunchQueue"}, + {0, nullptr, "SetProgramArgument"}, + {1, nullptr, "FlushArguments"}, }; // clang-format on @@ -164,19 +164,19 @@ public: explicit RelocatableObject(Core::System& system_) : ServiceFramework{system_, "ldr:ro"} { // clang-format off static const FunctionInfo functions[] = { - {0, &RelocatableObject::LoadNro, "LoadNro"}, - {1, &RelocatableObject::UnloadNro, "UnloadNro"}, - {2, &RelocatableObject::LoadNrr, "LoadNrr"}, - {3, &RelocatableObject::UnloadNrr, "UnloadNrr"}, + {0, &RelocatableObject::LoadModule, "LoadModule"}, + {1, &RelocatableObject::UnloadModule, "UnloadModule"}, + {2, &RelocatableObject::RegisterModuleInfo, "RegisterModuleInfo"}, + {3, &RelocatableObject::UnregisterModuleInfo, "UnregisterModuleInfo"}, {4, &RelocatableObject::Initialize, "Initialize"}, - {10, nullptr, "LoadNrrEx"}, + {10, nullptr, "RegisterModuleInfo2"}, }; // clang-format on RegisterHandlers(functions); } - void LoadNrr(Kernel::HLERequestContext& ctx) { + void RegisterModuleInfo(Kernel::HLERequestContext& ctx) { struct Parameters { u64_le process_id; u64_le nrr_address; @@ -273,7 +273,7 @@ public: rb.Push(RESULT_SUCCESS); } - void UnloadNrr(Kernel::HLERequestContext& ctx) { + void UnregisterModuleInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto pid = rp.Pop<u64>(); const auto nrr_address = rp.Pop<VAddr>(); @@ -408,7 +408,7 @@ public: data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite); } - void LoadNro(Kernel::HLERequestContext& ctx) { + void LoadModule(Kernel::HLERequestContext& ctx) { struct Parameters { u64_le process_id; u64_le image_address; @@ -546,7 +546,7 @@ public: return RESULT_SUCCESS; } - void UnloadNro(Kernel::HLERequestContext& ctx) { + void UnloadModule(Kernel::HLERequestContext& ctx) { if (!initialized) { LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 6ab35de47..44a5d5789 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -5,12 +5,12 @@ #include <memory> #include "common/logging/log.h" +#include "common/settings.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" -#include "core/settings.h" namespace Service::NFC { diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index afb3342d6..9f110df8e 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/settings.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" @@ -10,7 +11,6 @@ #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/service.h" #include "core/network/network.h" -#include "core/settings.h" namespace Service::NIFM { diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index f3be0b878..fee360ab9 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -125,51 +125,51 @@ public: {39, nullptr, "PrepareShutdown"}, {40, nullptr, "ListApplyDeltaTask"}, {41, nullptr, "ClearNotEnoughSpaceStateOfApplyDeltaTask"}, - {42, nullptr, "Unknown42"}, - {43, nullptr, "Unknown43"}, - {44, nullptr, "Unknown44"}, - {45, nullptr, "Unknown45"}, - {46, nullptr, "Unknown46"}, - {47, nullptr, "Unknown47"}, - {48, nullptr, "Unknown48"}, - {49, nullptr, "Unknown49"}, - {50, nullptr, "Unknown50"}, - {51, nullptr, "Unknown51"}, - {52, nullptr, "Unknown52"}, - {53, nullptr, "Unknown53"}, - {54, nullptr, "Unknown54"}, - {55, nullptr, "Unknown55"}, - {56, nullptr, "Unknown56"}, - {57, nullptr, "Unknown57"}, - {58, nullptr, "Unknown58"}, - {59, nullptr, "Unknown59"}, - {60, nullptr, "Unknown60"}, - {61, nullptr, "Unknown61"}, - {62, nullptr, "Unknown62"}, - {63, nullptr, "Unknown63"}, - {64, nullptr, "Unknown64"}, - {65, nullptr, "Unknown65"}, - {66, nullptr, "Unknown66"}, - {67, nullptr, "Unknown67"}, - {68, nullptr, "Unknown68"}, - {69, nullptr, "Unknown69"}, - {70, nullptr, "Unknown70"}, - {71, nullptr, "Unknown71"}, - {72, nullptr, "Unknown72"}, - {73, nullptr, "Unknown73"}, - {74, nullptr, "Unknown74"}, - {75, nullptr, "Unknown75"}, - {76, nullptr, "Unknown76"}, - {77, nullptr, "Unknown77"}, - {78, nullptr, "Unknown78"}, - {79, nullptr, "Unknown79"}, - {80, nullptr, "Unknown80"}, - {81, nullptr, "Unknown81"}, - {82, nullptr, "Unknown82"}, - {83, nullptr, "Unknown83"}, + {42, nullptr, "CreateApplyDeltaTaskFromDownloadTask"}, + {43, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"}, + {44, nullptr, "GetApplyDeltaTaskRequiredStorage"}, + {45, nullptr, "CalculateNetworkInstallTaskContentsSize"}, + {46, nullptr, "PrepareShutdownForSystemUpdate"}, + {47, nullptr, "FindMaxRequiredApplicationVersionOfTask"}, + {48, nullptr, "CommitNetworkInstallTaskPartially"}, + {49, nullptr, "ListNetworkInstallTaskCommittedContentMeta"}, + {50, nullptr, "ListNetworkInstallTaskNotCommittedContentMeta"}, + {51, nullptr, "FindMaxRequiredSystemVersionOfTask"}, + {52, nullptr, "GetNetworkInstallTaskErrorContext"}, + {53, nullptr, "CreateLocalCommunicationReceiveApplicationTask"}, + {54, nullptr, "DestroyLocalCommunicationReceiveApplicationTask"}, + {55, nullptr, "ListLocalCommunicationReceiveApplicationTask"}, + {56, nullptr, "RequestLocalCommunicationReceiveApplicationTaskRun"}, + {57, nullptr, "GetLocalCommunicationReceiveApplicationTaskInfo"}, + {58, nullptr, "CommitLocalCommunicationReceiveApplicationTask"}, + {59, nullptr, "ListLocalCommunicationReceiveApplicationTaskContentMeta"}, + {60, nullptr, "CreateLocalCommunicationSendApplicationTask"}, + {61, nullptr, "RequestLocalCommunicationSendApplicationTaskRun"}, + {62, nullptr, "GetLocalCommunicationReceiveApplicationTaskErrorContext"}, + {63, nullptr, "GetLocalCommunicationSendApplicationTaskInfo"}, + {64, nullptr, "DestroyLocalCommunicationSendApplicationTask"}, + {65, nullptr, "GetLocalCommunicationSendApplicationTaskErrorContext"}, + {66, nullptr, "CalculateLocalCommunicationReceiveApplicationTaskRequiredSize"}, + {67, nullptr, "ListApplicationLocalCommunicationReceiveApplicationTask"}, + {68, nullptr, "ListApplicationLocalCommunicationSendApplicationTask"}, + {69, nullptr, "CreateLocalCommunicationReceiveSystemUpdateTask"}, + {70, nullptr, "DestroyLocalCommunicationReceiveSystemUpdateTask"}, + {71, nullptr, "ListLocalCommunicationReceiveSystemUpdateTask"}, + {72, nullptr, "RequestLocalCommunicationReceiveSystemUpdateTaskRun"}, + {73, nullptr, "GetLocalCommunicationReceiveSystemUpdateTaskInfo"}, + {74, nullptr, "CommitLocalCommunicationReceiveSystemUpdateTask"}, + {75, nullptr, "GetLocalCommunicationReceiveSystemUpdateTaskErrorContext"}, + {76, nullptr, "CreateLocalCommunicationSendSystemUpdateTask"}, + {77, nullptr, "RequestLocalCommunicationSendSystemUpdateTaskRun"}, + {78, nullptr, "GetLocalCommunicationSendSystemUpdateTaskInfo"}, + {79, nullptr, "DestroyLocalCommunicationSendSystemUpdateTask"}, + {80, nullptr, "GetLocalCommunicationSendSystemUpdateTaskErrorContext"}, + {81, nullptr, "ListLocalCommunicationSendSystemUpdateTask"}, + {82, nullptr, "GetReceivedSystemDataPath"}, + {83, nullptr, "CalculateApplyDeltaTaskOccupiedSize"}, {84, nullptr, "Unknown84"}, - {85, nullptr, "Unknown85"}, - {86, nullptr, "Unknown86"}, + {85, nullptr, "ListNetworkInstallTaskContentMetaFromInstallMeta"}, + {86, nullptr, "ListNetworkInstallTaskOccupiedSize"}, {87, nullptr, "Unknown87"}, {88, nullptr, "Unknown88"}, {89, nullptr, "Unknown89"}, @@ -202,6 +202,17 @@ public: {116, nullptr, "Unknown116"}, {117, nullptr, "Unknown117"}, {118, nullptr, "Unknown118"}, + {119, nullptr, "Unknown119"}, + {120, nullptr, "Unknown120"}, + {121, nullptr, "Unknown121"}, + {122, nullptr, "Unknown122"}, + {123, nullptr, "Unknown123"}, + {124, nullptr, "Unknown124"}, + {125, nullptr, "Unknown125"}, + {126, nullptr, "Unknown126"}, + {127, nullptr, "Unknown127"}, + {128, nullptr, "Unknown128"}, + {129, nullptr, "Unknown129"}, }; // clang-format on diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp index f7a58f659..e4c703da4 100644 --- a/src/core/hle/service/npns/npns.cpp +++ b/src/core/hle/service/npns/npns.cpp @@ -49,6 +49,8 @@ public: {151, nullptr, "GetStateWithHandover"}, {152, nullptr, "GetStateChangeEventWithHandover"}, {153, nullptr, "GetDropEventWithHandover"}, + {154, nullptr, "CreateTokenAsync"}, + {155, nullptr, "CreateTokenAsyncWithApplicationId"}, {161, nullptr, "GetRequestChangeStateCancelEvent"}, {162, nullptr, "RequestChangeStateForceTimedWithCancelEvent"}, {201, nullptr, "RequestChangeStateForceTimed"}, @@ -84,6 +86,7 @@ public: {151, nullptr, "GetStateWithHandover"}, {152, nullptr, "GetStateChangeEventWithHandover"}, {153, nullptr, "GetDropEventWithHandover"}, + {154, nullptr, "CreateTokenAsync"}, }; // clang-format on diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 6ccf8995c..e373609a1 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" @@ -14,7 +15,6 @@ #include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/pl_u.h" #include "core/hle/service/set/set.h" -#include "core/settings.h" namespace Service::NS { @@ -55,6 +55,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_ {26, nullptr, "BeginInstallApplication"}, {27, nullptr, "DeleteApplicationRecord"}, {30, nullptr, "RequestApplicationUpdateInfo"}, + {31, nullptr, "Unknown31"}, {32, nullptr, "CancelApplicationDownload"}, {33, nullptr, "ResumeApplicationDownload"}, {35, nullptr, "UpdateVersionList"}, @@ -182,6 +183,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_ {913, nullptr, "ListAllApplicationRecord"}, {914, nullptr, "HideApplicationRecord"}, {915, nullptr, "ShowApplicationRecord"}, + {916, nullptr, "IsApplicationAutoDeleteDisabled"}, {1000, nullptr, "RequestVerifyApplicationDeprecated"}, {1001, nullptr, "CorruptApplicationForDebug"}, {1002, nullptr, "RequestVerifyAddOnContentsRights"}, @@ -201,6 +203,8 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_ {1310, nullptr, "RequestMoveApplicationEntity"}, {1311, nullptr, "EstimateSizeToMove"}, {1312, nullptr, "HasMovableEntity"}, + {1313, nullptr, "CleanupOrphanContents"}, + {1314, nullptr, "CheckPreconditionSatisfiedToMove"}, {1400, nullptr, "PrepareShutdown"}, {1500, nullptr, "FormatSdCard"}, {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, @@ -215,6 +219,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_ {1702, nullptr, "GetApplicationDownloadTaskStatus"}, {1703, nullptr, "GetApplicationViewDownloadErrorContext"}, {1704, nullptr, "GetApplicationViewWithPromotionInfo"}, + {1705, nullptr, "IsPatchAutoDeletableApplication"}, {1800, nullptr, "IsNotificationSetupCompleted"}, {1801, nullptr, "GetLastNotificationInfoCount"}, {1802, nullptr, "ListLastNotificationInfo"}, @@ -269,6 +274,9 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_ {2351, nullptr, "RequestNoDownloadRightsErrorResolution"}, {2352, nullptr, "RequestResolveNoDownloadRightsError"}, {2353, nullptr, "GetApplicationDownloadTaskInfo"}, + {2354, nullptr, "PrioritizeApplicationBackgroundTask"}, + {2355, nullptr, "Unknown2355"}, + {2356, nullptr, "Unknown2356"}, {2400, nullptr, "GetPromotionInfo"}, {2401, nullptr, "CountPromotionInfo"}, {2402, nullptr, "ListPromotionInfo"}, @@ -282,6 +290,21 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_ {2515, nullptr, "CleanupAllPlaceHolderAndFragmentsIfNoTask"}, {2516, nullptr, "EnsureApplicationCertificate"}, {2800, nullptr, "GetApplicationIdOfPreomia"}, + {3000, nullptr, "RegisterDeviceLockKey"}, + {3001, nullptr, "UnregisterDeviceLockKey"}, + {3002, nullptr, "VerifyDeviceLockKey"}, + {3003, nullptr, "HideApplicationIcon"}, + {3004, nullptr, "ShowApplicationIcon"}, + {3005, nullptr, "HideApplicationTitle"}, + {3006, nullptr, "ShowApplicationTitle"}, + {3007, nullptr, "EnableGameCard"}, + {3008, nullptr, "DisableGameCard"}, + {3009, nullptr, "EnableLocalContentShare"}, + {3010, nullptr, "DisableLocalContentShare"}, + {3011, nullptr, "IsApplicationIconHidden"}, + {3012, nullptr, "IsApplicationTitleHidden"}, + {3013, nullptr, "IsGameCardEnabled"}, + {3014, nullptr, "IsLocalContentShareEnabled"}, {9999, nullptr, "GetApplicationCertificate"}, }; // clang-format on @@ -441,7 +464,11 @@ IApplicationVersionInterface::IApplicationVersionInterface(Core::System& system_ {800, nullptr, "RequestVersionList"}, {801, nullptr, "ListVersionList"}, {802, nullptr, "RequestVersionListData"}, + {900, nullptr, "ImportAutoUpdatePolicyJsonForDebug"}, + {901, nullptr, "ListDefaultAutoUpdatePolicy"}, + {902, nullptr, "ListAutoUpdatePolicyForSpecificApplication"}, {1000, nullptr, "PerformAutoUpdate"}, + {1001, nullptr, "ListAutoUpdateSchedule"}, }; // clang-format on @@ -547,6 +574,9 @@ IFactoryResetInterface::~IFactoryResetInterface() = default; NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} { // clang-format off static const FunctionInfo functions[] = { + {7988, nullptr, "GetDynamicRightsInterface"}, + {7989, nullptr, "GetReadOnlyApplicationControlDataInterface"}, + {7991, nullptr, "GetReadOnlyApplicationRecordInterface"}, {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"}, @@ -575,18 +605,22 @@ public: {0, nullptr, "LaunchProgram"}, {1, nullptr, "TerminateProcess"}, {2, nullptr, "TerminateProgram"}, - {4, nullptr, "GetShellEventHandle"}, + {4, nullptr, "GetShellEvent"}, {5, nullptr, "GetShellEventInfo"}, {6, nullptr, "TerminateApplication"}, {7, nullptr, "PrepareLaunchProgramFromHost"}, - {8, nullptr, "LaunchApplication"}, + {8, nullptr, "LaunchApplicationFromHost"}, {9, nullptr, "LaunchApplicationWithStorageIdForDevelop"}, {10, nullptr, "IsSystemMemoryResourceLimitBoosted"}, {11, nullptr, "GetRunningApplicationProcessIdForDevelop"}, - {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"}, + {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActiveForDevelop"}, {13, nullptr, "CreateApplicationResourceForDevelop"}, {14, nullptr, "IsPreomiaForDevelop"}, {15, nullptr, "GetApplicationProgramIdFromHost"}, + {16, nullptr, "RefreshCachedDebugValues"}, + {17, nullptr, "PrepareLaunchApplicationFromHost"}, + {18, nullptr, "GetLaunchEvent"}, + {19, nullptr, "GetLaunchResult"}, }; // clang-format on @@ -699,6 +733,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system std::make_shared<NS>("ns:rid", system)->InstallAsService(service_manager); std::make_shared<NS>("ns:rt", system)->InstallAsService(service_manager); std::make_shared<NS>("ns:web", system)->InstallAsService(service_manager); + std::make_shared<NS>("ns:ro", system)->InstallAsService(service_manager); std::make_shared<NS_DEV>(system)->InstallAsService(service_manager); std::make_shared<NS_SU>(system)->InstallAsService(service_manager); diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index fcd15d81f..da139fdc4 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp @@ -154,6 +154,10 @@ PL_U::PL_U(Core::System& system_) {100, nullptr, "RequestApplicationFunctionAuthorization"}, {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"}, {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"}, + {103, nullptr, "RefreshApplicationFunctionBlackListDebugRecord"}, + {104, nullptr, "RequestApplicationFunctionAuthorizationByProgramId"}, + {105, nullptr, "GetFunctionBlackListSystemVersionToAuthorize"}, + {106, nullptr, "GetFunctionBlackListVersion"}, {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"}, {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"}, }; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 933d42f3f..2edd803f3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -248,7 +248,13 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector< IoctlZbcSetTable params{}; std::memcpy(¶ms, input.data(), input.size()); // TODO(ogniK): What does this even actually do? - std::memcpy(output.data(), ¶ms, output.size()); + + // Prevent null pointer being passed as arg 1 + if (output.empty()) { + LOG_WARNING(Service_NVDRV, "Avoiding passing null pointer to memcpy"); + } else { + std::memcpy(output.data(), ¶ms, output.size()); + } return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 4898dc27a..c2f152190 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -23,17 +23,22 @@ namespace { template <typename T> std::size_t SpliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count, std::size_t offset) { - std::memcpy(dst.data(), input.data() + offset, count * sizeof(T)); - offset += count * sizeof(T); - return offset; + if (!dst.empty()) { + std::memcpy(dst.data(), input.data() + offset, count * sizeof(T)); + } + return 0; } // Write vectors will write data to the output buffer template <typename T> std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::size_t offset) { - std::memcpy(dst.data() + offset, src.data(), src.size() * sizeof(T)); - offset += src.size() * sizeof(T); - return offset; + if (src.empty()) { + return 0; + } else { + std::memcpy(dst.data() + offset, src.data(), src.size() * sizeof(T)); + offset += src.size() * sizeof(T); + return offset; + } } } // Anonymous namespace diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index ac2906e5b..539b02bc4 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -9,6 +9,7 @@ #include "common/logging/log.h" #include "common/microprofile.h" #include "common/scope_exit.h" +#include "common/settings.h" #include "common/thread.h" #include "core/core.h" #include "core/core_timing.h" @@ -23,7 +24,6 @@ #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" #include "core/perf_stats.h" -#include "core/settings.h" #include "video_core/renderer_base.h" namespace Service::NVFlinger { diff --git a/src/core/hle/service/olsc/olsc.cpp b/src/core/hle/service/olsc/olsc.cpp index e2ac71fa1..b066c3417 100644 --- a/src/core/hle/service/olsc/olsc.cpp +++ b/src/core/hle/service/olsc/olsc.cpp @@ -26,6 +26,7 @@ public: {22, nullptr, "DeleteSaveDataBackupAsync"}, {25, nullptr, "ListDownloadableSaveDataBackupInfoAsync"}, {26, nullptr, "DownloadSaveDataBackupAsync"}, + {27, nullptr, "UploadSaveDataBackupAsync"}, {9010, nullptr, "VerifySaveDataBackupLicenseAsyncForDebug"}, {9013, nullptr, "GetSaveDataBackupSettingForDebug"}, {9014, nullptr, "SetSaveDataBackupSettingEnabledForDebug"}, diff --git a/src/core/hle/service/pcie/pcie.cpp b/src/core/hle/service/pcie/pcie.cpp index f6686fc4d..9bc851591 100644 --- a/src/core/hle/service/pcie/pcie.cpp +++ b/src/core/hle/service/pcie/pcie.cpp @@ -37,7 +37,7 @@ public: {19, nullptr, "SetIrqEnable"}, {20, nullptr, "SetAspmEnable"}, {21, nullptr, "SetResetUponResumeEnable"}, - {22, nullptr, "Unknown22"}, + {22, nullptr, "ResetFunction"}, {23, nullptr, "Unknown23"}, }; // clang-format on diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp index f9089bf2f..dc59702f1 100644 --- a/src/core/hle/service/pctl/module.cpp +++ b/src/core/hle/service/pctl/module.cpp @@ -3,16 +3,30 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "core/core.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/pctl/module.h" #include "core/hle/service/pctl/pctl.h" namespace Service::PCTL { +namespace Error { + +constexpr ResultCode ResultNoFreeCommunication{ErrorModule::PCTL, 101}; +constexpr ResultCode ResultStereoVisionRestricted{ErrorModule::PCTL, 104}; +constexpr ResultCode ResultNoCapability{ErrorModule::PCTL, 131}; +constexpr ResultCode ResultNoRestrictionEnabled{ErrorModule::PCTL, 181}; + +} // namespace Error + class IParentalControlService final : public ServiceFramework<IParentalControlService> { public: - explicit IParentalControlService(Core::System& system_) - : ServiceFramework{system_, "IParentalControlService"} { + explicit IParentalControlService(Core::System& system_, Capability capability) + : ServiceFramework{system_, "IParentalControlService"}, system(system_), + capability(capability) { // clang-format off static const FunctionInfo functions[] = { {1, &IParentalControlService::Initialize, "Initialize"}, @@ -28,13 +42,13 @@ public: {1010, nullptr, "IsRestrictedSystemSettingsEntered"}, {1011, nullptr, "RevertRestrictedSystemSettingsEntered"}, {1012, nullptr, "GetRestrictedFeatures"}, - {1013, nullptr, "ConfirmStereoVisionPermission"}, + {1013, &IParentalControlService::ConfirmStereoVisionPermission, "ConfirmStereoVisionPermission"}, {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, {1015, nullptr, "ConfirmPlayableApplicationVideo"}, {1016, nullptr, "ConfirmShowNewsPermission"}, {1017, nullptr, "EndFreeCommunication"}, - {1018, nullptr, "IsFreeCommunicationAvailable"}, - {1031, nullptr, "IsRestrictionEnabled"}, + {1018, &IParentalControlService::IsFreeCommunicationAvailable, "IsFreeCommunicationAvailable"}, + {1031, &IParentalControlService::IsRestrictionEnabled, "IsRestrictionEnabled"}, {1032, nullptr, "GetSafetyLevel"}, {1033, nullptr, "SetSafetyLevel"}, {1034, nullptr, "GetSafetyLevelSettings"}, @@ -50,6 +64,7 @@ public: {1046, nullptr, "DisableFeaturesForReset"}, {1047, nullptr, "NotifyApplicationDownloadStarted"}, {1048, nullptr, "NotifyNetworkProfileCreated"}, + {1049, nullptr, "ResetFreeCommunicationApplicationList"}, {1061, &IParentalControlService::ConfirmStereoVisionRestrictionConfigurable, "ConfirmStereoVisionRestrictionConfigurable"}, {1062, &IParentalControlService::GetStereoVisionRestriction, "GetStereoVisionRestriction"}, {1063, &IParentalControlService::SetStereoVisionRestriction, "SetStereoVisionRestriction"}, @@ -69,6 +84,8 @@ public: {1421, nullptr, "GetAccountNickname"}, {1424, nullptr, "GetAccountState"}, {1425, nullptr, "RequestPostEvents"}, + {1426, nullptr, "GetPostEventInterval"}, + {1427, nullptr, "SetPostEventInterval"}, {1432, nullptr, "GetSynchronizationEvent"}, {1451, nullptr, "StartPlayTimer"}, {1452, nullptr, "StopPlayTimer"}, @@ -119,62 +136,235 @@ public: } private: + bool CheckFreeCommunicationPermissionImpl() const { + if (states.temporary_unlocked) { + return true; + } + if ((states.application_info.parental_control_flag & 1) == 0) { + return true; + } + if (pin_code[0] == '\0') { + return true; + } + if (!settings.is_free_communication_default_on) { + return true; + } + // TODO(ogniK): Check for blacklisted/exempted applications. Return false can happen here + // but as we don't have multiproceses support yet, we can just assume our application is + // valid for the time being + return true; + } + + bool ConfirmStereoVisionPermissionImpl() const { + if (states.temporary_unlocked) { + return true; + } + if (pin_code[0] == '\0') { + return true; + } + if (!settings.is_stero_vision_restricted) { + return false; + } + return true; + } + + void SetStereoVisionRestrictionImpl(bool is_restricted) { + if (settings.disabled) { + return; + } + + if (pin_code[0] == '\0') { + return; + } + settings.is_stero_vision_restricted = is_restricted; + } + void Initialize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_PCTL, "(STUBBED) called"); + LOG_DEBUG(Service_PCTL, "called"); + IPC::ResponseBuilder rb{ctx, 2}; + + if (False(capability & (Capability::Application | Capability::System))) { + LOG_ERROR(Service_PCTL, "Invalid capability! capability={:X}", capability); + return; + } + + // TODO(ogniK): Recovery flag initialization for pctl:r + + const auto tid = system.CurrentProcess()->GetTitleID(); + if (tid != 0) { + const FileSys::PatchManager pm{tid, system.GetFileSystemController(), + system.GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + if (control.first) { + states.tid_from_event = 0; + states.launch_time_valid = false; + states.is_suspended = false; + states.free_communication = false; + states.stereo_vision = false; + states.application_info = ApplicationInfo{ + .tid = tid, + .age_rating = control.first->GetRatingAge(), + .parental_control_flag = control.first->GetParentalControlFlag(), + .capability = capability, + }; + + if (False(capability & (Capability::System | Capability::Recovery))) { + // TODO(ogniK): Signal application launch event + } + } + } - IPC::ResponseBuilder rb{ctx, 2, 0, 0}; rb.Push(RESULT_SUCCESS); } void CheckFreeCommunicationPermission(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_PCTL, "(STUBBED) called"); + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + if (!CheckFreeCommunicationPermissionImpl()) { + rb.Push(Error::ResultNoFreeCommunication); + } else { + rb.Push(RESULT_SUCCESS); + } + + states.free_communication = true; + } + + void ConfirmStereoVisionPermission(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + states.stereo_vision = true; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } - void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) { + void IsFreeCommunicationAvailable(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_PCTL, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; + if (!CheckFreeCommunicationPermissionImpl()) { + rb.Push(Error::ResultNoFreeCommunication); + } else { + rb.Push(RESULT_SUCCESS); + } + } + + void IsRestrictionEnabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + if (False(capability & (Capability::Status | Capability::Recovery))) { + LOG_ERROR(Service_PCTL, "Application does not have Status or Recovery capabilities!"); + rb.Push(Error::ResultNoCapability); + rb.Push(false); + return; + } + + rb.Push(pin_code[0] != '\0'); + } + + void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + + if (False(capability & Capability::StereoVision)) { + LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!"); + rb.Push(Error::ResultNoCapability); + return; + } + + if (pin_code[0] == '\0') { + rb.Push(Error::ResultNoRestrictionEnabled); + return; + } + rb.Push(RESULT_SUCCESS); } void IsStereoVisionPermitted(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_PCTL, "(STUBBED) called"); + LOG_DEBUG(Service_PCTL, "called"); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push(true); + if (!ConfirmStereoVisionPermissionImpl()) { + rb.Push(Error::ResultStereoVisionRestricted); + rb.Push(false); + } else { + rb.Push(RESULT_SUCCESS); + rb.Push(true); + } } void SetStereoVisionRestriction(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto can_use = rp.Pop<bool>(); - LOG_WARNING(Service_PCTL, "(STUBBED) called, can_use={}", can_use); - - can_use_stereo_vision = can_use; + LOG_DEBUG(Service_PCTL, "called, can_use={}", can_use); IPC::ResponseBuilder rb{ctx, 2}; + if (False(capability & Capability::StereoVision)) { + LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!"); + rb.Push(Error::ResultNoCapability); + return; + } + + SetStereoVisionRestrictionImpl(can_use); rb.Push(RESULT_SUCCESS); } void GetStereoVisionRestriction(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_PCTL, "(STUBBED) called"); + LOG_DEBUG(Service_PCTL, "called"); IPC::ResponseBuilder rb{ctx, 3}; + if (False(capability & Capability::StereoVision)) { + LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!"); + rb.Push(Error::ResultNoCapability); + rb.Push(false); + return; + } + rb.Push(RESULT_SUCCESS); - rb.Push(can_use_stereo_vision); + rb.Push(settings.is_stero_vision_restricted); } void ResetConfirmedStereoVisionPermission(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_PCTL, "(STUBBED) called"); + LOG_DEBUG(Service_PCTL, "called"); + + states.stereo_vision = false; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + struct ApplicationInfo { + u64 tid{}; + std::array<u8, 32> age_rating{}; + u32 parental_control_flag{}; + Capability capability{}; + }; + + struct States { + u64 current_tid{}; + ApplicationInfo application_info{}; + u64 tid_from_event{}; + bool launch_time_valid{}; + bool is_suspended{}; + bool temporary_unlocked{}; + bool free_communication{}; + bool stereo_vision{}; + }; + + struct ParentalControlSettings { + bool is_stero_vision_restricted{}; + bool is_free_communication_default_on{}; + bool disabled{}; + }; + + States states{}; + ParentalControlSettings settings{}; + std::array<char, 8> pin_code{}; bool can_use_stereo_vision = true; + Core::System& system; + Capability capability{}; }; void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) { @@ -182,7 +372,9 @@ void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IParentalControlService>(system); + // TODO(ogniK): Get TID from process + + rb.PushIpcInterface<IParentalControlService>(system, capability); } void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) { @@ -190,21 +382,28 @@ void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IParentalControlService>(system); + rb.PushIpcInterface<IParentalControlService>(system, capability); } Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, - const char* name) - : ServiceFramework{system_, name}, module{std::move(module_)} {} + const char* name, Capability capability) + : ServiceFramework{system_, name}, module{std::move(module_)}, capability(capability) {} Module::Interface::~Interface() = default; void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { auto module = std::make_shared<Module>(); - std::make_shared<PCTL>(system, module, "pctl")->InstallAsService(service_manager); - std::make_shared<PCTL>(system, module, "pctl:a")->InstallAsService(service_manager); - std::make_shared<PCTL>(system, module, "pctl:r")->InstallAsService(service_manager); - std::make_shared<PCTL>(system, module, "pctl:s")->InstallAsService(service_manager); + std::make_shared<PCTL>(system, module, "pctl", + Capability::Application | Capability::SnsPost | Capability::Status | + Capability::StereoVision) + ->InstallAsService(service_manager); + // TODO(ogniK): Implement remaining capabilities + std::make_shared<PCTL>(system, module, "pctl:a", Capability::None) + ->InstallAsService(service_manager); + std::make_shared<PCTL>(system, module, "pctl:r", Capability::None) + ->InstallAsService(service_manager); + std::make_shared<PCTL>(system, module, "pctl:s", Capability::None) + ->InstallAsService(service_manager); } } // namespace Service::PCTL diff --git a/src/core/hle/service/pctl/module.h b/src/core/hle/service/pctl/module.h index 4c7e09a3b..032481b00 100644 --- a/src/core/hle/service/pctl/module.h +++ b/src/core/hle/service/pctl/module.h @@ -4,6 +4,7 @@ #pragma once +#include "common/common_funcs.h" #include "core/hle/service/service.h" namespace Core { @@ -12,12 +13,23 @@ class System; namespace Service::PCTL { +enum class Capability : u32 { + None = 0, + Application = 1 << 0, + SnsPost = 1 << 1, + Recovery = 1 << 6, + Status = 1 << 8, + StereoVision = 1 << 9, + System = 1 << 15, +}; +DECLARE_ENUM_FLAG_OPERATORS(Capability); + class Module final { public: class Interface : public ServiceFramework<Interface> { public: - explicit Interface(Core::System& system_, std::shared_ptr<Module> module_, - const char* name); + explicit Interface(Core::System& system_, std::shared_ptr<Module> module_, const char* name, + Capability capability); ~Interface() override; void CreateService(Kernel::HLERequestContext& ctx); @@ -25,6 +37,9 @@ public: protected: std::shared_ptr<Module> module; + + private: + Capability capability{}; }; }; diff --git a/src/core/hle/service/pctl/pctl.cpp b/src/core/hle/service/pctl/pctl.cpp index 16dd34f90..e4d155c86 100644 --- a/src/core/hle/service/pctl/pctl.cpp +++ b/src/core/hle/service/pctl/pctl.cpp @@ -6,8 +6,9 @@ namespace Service::PCTL { -PCTL::PCTL(Core::System& system_, std::shared_ptr<Module> module_, const char* name) - : Interface{system_, std::move(module_), name} { +PCTL::PCTL(Core::System& system_, std::shared_ptr<Module> module_, const char* name, + Capability capability) + : Interface{system_, std::move(module_), name, capability} { static const FunctionInfo functions[] = { {0, &PCTL::CreateService, "CreateService"}, {1, &PCTL::CreateServiceWithoutInitialize, "CreateServiceWithoutInitialize"}, diff --git a/src/core/hle/service/pctl/pctl.h b/src/core/hle/service/pctl/pctl.h index 275d23007..fd0a1e486 100644 --- a/src/core/hle/service/pctl/pctl.h +++ b/src/core/hle/service/pctl/pctl.h @@ -14,7 +14,8 @@ namespace Service::PCTL { class PCTL final : public Module::Interface { public: - explicit PCTL(Core::System& system_, std::shared_ptr<Module> module_, const char* name); + explicit PCTL(Core::System& system_, std::shared_ptr<Module> module_, const char* name, + Capability capability); ~PCTL() override; }; diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 1da56bc27..41a502d8d 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -6,6 +6,7 @@ #include <fmt/format.h> #include "common/assert.h" #include "common/logging/log.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/core.h" #include "core/hle/ipc.h" @@ -146,6 +147,11 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext system.GetReporter().SaveUnimplementedFunctionReport(ctx, ctx.GetCommand(), function_name, service_name); UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf)); + if (Settings::values.use_auto_stub) { + LOG_WARNING(Service, "Using auto stub fallback!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } } void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index d953b4303..bc7dc776f 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -6,9 +6,9 @@ #include <array> #include <chrono> #include "common/logging/log.h" +#include "common/settings.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/set/set.h" -#include "core/settings.h" namespace Service::Set { namespace { diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index b58b2c8c5..5909fdd85 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -261,6 +261,10 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {155, nullptr, "SetAccountOnlineStorageSettings"}, {156, nullptr, "GetPctlReadyFlag"}, {157, nullptr, "SetPctlReadyFlag"}, + {158, nullptr, "GetAnalogStickUserCalibrationL"}, + {159, nullptr, "SetAnalogStickUserCalibrationL"}, + {160, nullptr, "GetAnalogStickUserCalibrationR"}, + {161, nullptr, "SetAnalogStickUserCalibrationR"}, {162, nullptr, "GetPtmBatteryVersion"}, {163, nullptr, "SetPtmBatteryVersion"}, {164, nullptr, "GetUsb30HostEnableFlag"}, @@ -302,6 +306,8 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {200, nullptr, "SetButtonConfigRegisteredSettings"}, {201, nullptr, "GetFieldTestingFlag"}, {202, nullptr, "SetFieldTestingFlag"}, + {203, nullptr, "GetPanelCrcMode"}, + {204, nullptr, "SetPanelCrcMode"}, }; // clang-format on diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 2b91a89d1..94608d529 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -190,10 +190,11 @@ SM::SM(std::shared_ptr<ServiceManager> service_manager_, Core::System& system_) : ServiceFramework{system_, "sm:", 4}, service_manager{std::move(service_manager_)}, kernel{system_.Kernel()} { static const FunctionInfo functions[] = { - {0x00000000, &SM::Initialize, "Initialize"}, - {0x00000001, &SM::GetService, "GetService"}, - {0x00000002, &SM::RegisterService, "RegisterService"}, - {0x00000003, &SM::UnregisterService, "UnregisterService"}, + {0, &SM::Initialize, "Initialize"}, + {1, &SM::GetService, "GetService"}, + {2, &SM::RegisterService, "RegisterService"}, + {3, &SM::UnregisterService, "UnregisterService"}, + {4, nullptr, "DetachClient"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/sockets/ethc.cpp b/src/core/hle/service/sockets/ethc.cpp index 05681ca2d..899a64c2f 100644 --- a/src/core/hle/service/sockets/ethc.cpp +++ b/src/core/hle/service/sockets/ethc.cpp @@ -15,6 +15,7 @@ ETHC_C::ETHC_C(Core::System& system_) : ServiceFramework{system_, "ethc:c"} { {3, nullptr, "GetMediaList"}, {4, nullptr, "SetMediaType"}, {5, nullptr, "GetMediaType"}, + {6, nullptr, "Unknown6"}, }; // clang-format on diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp index 51c3739bb..1159debc5 100644 --- a/src/core/hle/service/sockets/nsd.cpp +++ b/src/core/hle/service/sockets/nsd.cpp @@ -9,6 +9,7 @@ namespace Service::Sockets { NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { // clang-format off static const FunctionInfo functions[] = { + {5, nullptr, "GetSettingUrl"}, {10, nullptr, "GetSettingName"}, {11, nullptr, "GetEnvironmentIdentifier"}, {12, nullptr, "GetDeviceId"}, diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index 3a6329f56..5c71f423c 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -9,8 +9,8 @@ namespace Service::Sockets { SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"} { static const FunctionInfo functions[] = { - {0, nullptr, "SetDnsAddressesPrivate"}, - {1, nullptr, "GetDnsAddressPrivate"}, + {0, nullptr, "SetDnsAddressesPrivateRequest"}, + {1, nullptr, "GetDnsAddressPrivateRequest"}, {2, nullptr, "GetHostByNameRequest"}, {3, nullptr, "GetHostByAddrRequest"}, {4, nullptr, "GetHostStringErrorRequest"}, diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp index 6903dd534..b1552c3f0 100644 --- a/src/core/hle/service/spl/module.cpp +++ b/src/core/hle/service/spl/module.cpp @@ -9,11 +9,11 @@ #include <functional> #include <vector> #include "common/logging/log.h" +#include "common/settings.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/spl/csrng.h" #include "core/hle/service/spl/module.h" #include "core/hle/service/spl/spl.h" -#include "core/settings.h" namespace Service::SPL { diff --git a/src/core/hle/service/spl/spl.cpp b/src/core/hle/service/spl/spl.cpp index 4e212610f..fff3f3c42 100644 --- a/src/core/hle/service/spl/spl.cpp +++ b/src/core/hle/service/spl/spl.cpp @@ -60,6 +60,8 @@ SPL_FS::SPL_FS(Core::System& system_, std::shared_ptr<Module> module_) {4, nullptr, "GenerateAesKey"}, {5, nullptr, "SetConfig"}, {7, &SPL::GetRandomBytes, "GenerateRandomBytes"}, + {9, nullptr, "ImportLotusKey"}, + {10, nullptr, "DecryptLotusMessage"}, {11, nullptr, "IsDevelopment"}, {12, nullptr, "GenerateSpecificAesKey"}, {14, nullptr, "DecryptAesKey"}, @@ -123,6 +125,7 @@ SPL_ES::SPL_ES(Core::System& system_, std::shared_ptr<Module> module_) {14, nullptr, "DecryptAesKey"}, {15, nullptr, "CryptAesCtr"}, {16, nullptr, "ComputeCmac"}, + {17, nullptr, "ImportEsKey"}, {18, nullptr, "UnwrapTitleKey"}, {20, nullptr, "PrepareEsCommonKey"}, {21, nullptr, "AllocateAesKeyslot"}, diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h index b78892223..a9cfe3eb0 100644 --- a/src/core/hle/service/time/clock_types.h +++ b/src/core/hle/service/time/clock_types.h @@ -12,6 +12,12 @@ namespace Service::Time::Clock { +enum class TimeType : u8 { + UserSystemClock, + NetworkSystemClock, + LocalSystemClock, +}; + /// https://switchbrew.org/wiki/Glue_services#SteadyClockTimePoint struct SteadyClockTimePoint { s64 time_point; @@ -84,7 +90,7 @@ struct ClockSnapshot { SteadyClockTimePoint steady_clock_time_point; TimeZone::LocationName location_name; u8 is_automatic_correction_enabled; - u8 type; + TimeType type; INSERT_PADDING_BYTES_NOINIT(0x2); static ResultCode GetCurrentTime(s64& current_time, diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 78543688f..63e0247de 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -122,14 +122,16 @@ private: ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( Kernel::KThread* thread, Clock::SystemClockContext user_context, - Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) { + Clock::SystemClockContext network_context, Clock::TimeType type, + Clock::ClockSnapshot& clock_snapshot) { auto& time_manager{system.GetTimeManager()}; + clock_snapshot.steady_clock_time_point = + time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system); clock_snapshot.is_automatic_correction_enabled = time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled(); - clock_snapshot.user_context = user_context; - clock_snapshot.network_context = network_context; + clock_snapshot.type = type; if (const ResultCode result{ time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName( @@ -138,12 +140,11 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( return result; } - const auto current_time_point{ - time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)}; - clock_snapshot.steady_clock_time_point = current_time_point; + clock_snapshot.user_context = user_context; if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime( - clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)}; + clock_snapshot.user_time, clock_snapshot.steady_clock_time_point, + clock_snapshot.user_context)}; result != RESULT_SUCCESS) { return result; } @@ -157,9 +158,12 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( } clock_snapshot.user_calendar_time = userCalendarInfo.time; - clock_snapshot.user_calendar_additional_time = userCalendarInfo.additiona_info; + clock_snapshot.user_calendar_additional_time = userCalendarInfo.additional_info; + + clock_snapshot.network_context = network_context; - if (Clock::ClockSnapshot::GetCurrentTime(clock_snapshot.network_time, current_time_point, + if (Clock::ClockSnapshot::GetCurrentTime(clock_snapshot.network_time, + clock_snapshot.steady_clock_time_point, clock_snapshot.network_context) != RESULT_SUCCESS) { clock_snapshot.network_time = 0; } @@ -173,8 +177,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( } clock_snapshot.network_calendar_time = networkCalendarInfo.time; - clock_snapshot.network_calendar_additional_time = networkCalendarInfo.additiona_info; - clock_snapshot.type = type; + clock_snapshot.network_calendar_additional_time = networkCalendarInfo.additional_info; return RESULT_SUCCESS; } @@ -257,9 +260,10 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERe } void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); IPC::RequestParser rp{ctx}; - const auto type{rp.PopRaw<u8>()}; + const auto type{rp.PopEnum<Clock::TimeType>()}; + + LOG_DEBUG(Service_Time, "called, type={}", type); Clock::SystemClockContext user_context{}; if (const ResultCode result{ @@ -270,6 +274,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { rb.Push(result); return; } + Clock::SystemClockContext network_context{}; if (const ResultCode result{ system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext( @@ -295,14 +300,16 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { } void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); IPC::RequestParser rp{ctx}; - const auto type{rp.PopRaw<u8>()}; + const auto type{rp.PopEnum<Clock::TimeType>()}; + rp.AlignWithPadding(); const Clock::SystemClockContext user_context{rp.PopRaw<Clock::SystemClockContext>()}; const Clock::SystemClockContext network_context{rp.PopRaw<Clock::SystemClockContext>()}; + LOG_DEBUG(Service_Time, "called, type={}", type); + Clock::ClockSnapshot clock_snapshot{}; if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal( &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; @@ -321,9 +328,14 @@ void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser( Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called"); - IPC::RequestParser rp{ctx}; - const auto snapshot_a = rp.PopRaw<Clock::ClockSnapshot>(); - const auto snapshot_b = rp.PopRaw<Clock::ClockSnapshot>(); + Clock::ClockSnapshot snapshot_a; + Clock::ClockSnapshot snapshot_b; + + const auto snapshot_a_data = ctx.ReadBuffer(0); + const auto snapshot_b_data = ctx.ReadBuffer(1); + + std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot)); + std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot)); auto time_span_type{Clock::TimeSpanType::FromSeconds(snapshot_b.user_context.offset - snapshot_a.user_context.offset)}; diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index 4154c7ee9..ce9c479c6 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -40,7 +40,7 @@ public: private: ResultCode GetClockSnapshotFromSystemClockContextInternal( Kernel::KThread* thread, Clock::SystemClockContext user_context, - Clock::SystemClockContext network_context, u8 type, + Clock::SystemClockContext network_context, Clock::TimeType type, Clock::ClockSnapshot& cloc_snapshot); protected: diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp index 1f7309f6b..f89c5aaad 100644 --- a/src/core/hle/service/time/time_manager.cpp +++ b/src/core/hle/service/time/time_manager.cpp @@ -5,12 +5,12 @@ #include <chrono> #include <ctime> +#include "common/settings.h" #include "common/time_zone.h" #include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h" #include "core/hle/service/time/local_system_clock_context_writer.h" #include "core/hle/service/time/network_system_clock_context_writer.h" #include "core/hle/service/time/time_manager.h" -#include "core/settings.h" namespace Service::Time { @@ -44,7 +44,11 @@ struct TimeManager::Impl final { const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())}; SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {}); SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds()); - SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy); + + Clock::SystemClockContext clock_context{}; + standard_local_system_clock_core.GetClockContext(system, clock_context); + + SetupStandardNetworkSystemClock(clock_context, standard_network_clock_accuracy); SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom()); SetupEphemeralNetworkSystemClock(); } diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp index 4177d0a41..3c8e71a3c 100644 --- a/src/core/hle/service/time/time_zone_content_manager.cpp +++ b/src/core/hle/service/time/time_zone_content_manager.cpp @@ -5,6 +5,7 @@ #include <sstream> #include "common/logging/log.h" +#include "common/settings.h" #include "common/time_zone.h" #include "core/core.h" #include "core/file_sys/content_archive.h" @@ -15,7 +16,6 @@ #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/time/time_manager.h" #include "core/hle/service/time/time_zone_content_manager.h" -#include "core/settings.h" namespace Service::Time::TimeZone { diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp index bdf0439f2..3032ca193 100644 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ b/src/core/hle/service/time/time_zone_manager.cpp @@ -818,7 +818,7 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) { CalendarTimeInternal calendar_time{}; const ResultCode result{ - ToCalendarTimeInternal(rules, time, calendar_time, calendar.additiona_info)}; + ToCalendarTimeInternal(rules, time, calendar_time, calendar.additional_info)}; calendar.time.year = static_cast<s16>(calendar_time.year); // Internal impl. uses 0-indexed month diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp index 25cecbc83..3117627cf 100644 --- a/src/core/hle/service/time/time_zone_service.cpp +++ b/src/core/hle/service/time/time_zone_service.cpp @@ -20,6 +20,7 @@ ITimeZoneService ::ITimeZoneService(Core::System& system_, {3, nullptr, "LoadLocationNameList"}, {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"}, {5, nullptr, "GetTimeZoneRuleVersion"}, + {6, nullptr, "GetDeviceLocationNameAndUpdatedTime"}, {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"}, diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h index 4a57e036d..d39103253 100644 --- a/src/core/hle/service/time/time_zone_types.h +++ b/src/core/hle/service/time/time_zone_types.h @@ -66,8 +66,8 @@ struct CalendarTime { static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size"); struct CalendarInfo { - CalendarTime time{}; - CalendarAdditionalInfo additiona_info{}; + CalendarTime time; + CalendarAdditionalInfo additional_info; }; static_assert(sizeof(CalendarInfo) == 0x20, "CalendarInfo is incorrect size"); diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp index 579de83e4..b3b230a8c 100644 --- a/src/core/hle/service/usb/usb.cpp +++ b/src/core/hle/service/usb/usb.cpp @@ -69,15 +69,15 @@ public: : ServiceFramework{system_, "IClientEpSession"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Open"}, + {0, nullptr, "ReOpen"}, {1, nullptr, "Close"}, - {2, nullptr, "Unknown2"}, - {3, nullptr, "Populate"}, + {2, nullptr, "GetCompletionEvent"}, + {3, nullptr, "PopulateRing"}, {4, nullptr, "PostBufferAsync"}, {5, nullptr, "GetXferReport"}, {6, nullptr, "PostBufferMultiAsync"}, - {7, nullptr, "Unknown7"}, - {8, nullptr, "Unknown8"}, + {7, nullptr, "CreateSmmuSpace"}, + {8, nullptr, "ShareReportRing"}, }; // clang-format on @@ -91,7 +91,7 @@ public: : ServiceFramework{system_, "IClientIfSession"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Unknown0"}, + {0, nullptr, "GetStateChangeEvent"}, {1, nullptr, "SetInterface"}, {2, nullptr, "GetInterface"}, {3, nullptr, "GetAlternateInterface"}, @@ -176,15 +176,15 @@ public: : ServiceFramework{system_, "IPdCradleSession"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "VdmUserWrite"}, - {1, nullptr, "VdmUserRead"}, - {2, nullptr, "Vdm20Init"}, - {3, nullptr, "GetFwType"}, - {4, nullptr, "GetFwRevision"}, - {5, nullptr, "GetManufacturerId"}, - {6, nullptr, "GetDeviceId"}, - {7, nullptr, "Unknown7"}, - {8, nullptr, "Unknown8"}, + {0, nullptr, "SetCradleVdo"}, + {1, nullptr, "GetCradleVdo"}, + {2, nullptr, "ResetCradleUsbHub"}, + {3, nullptr, "GetHostPdcFirmwareType"}, + {4, nullptr, "GetHostPdcFirmwareRevision"}, + {5, nullptr, "GetHostPdcManufactureId"}, + {6, nullptr, "GetHostPdcDeviceId"}, + {7, nullptr, "AwakeCradle"}, + {8, nullptr, "SleepCradle"}, }; // clang-format on @@ -219,12 +219,12 @@ public: explicit USB_PM(Core::System& system_) : ServiceFramework{system_, "usb:pm"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Unknown0"}, - {1, nullptr, "Unknown1"}, - {2, nullptr, "Unknown2"}, - {3, nullptr, "Unknown3"}, - {4, nullptr, "Unknown4"}, - {5, nullptr, "Unknown5"}, + {0, nullptr, "GetPowerEvent"}, + {1, nullptr, "GetPowerState"}, + {2, nullptr, "GetDataEvent"}, + {3, nullptr, "GetDataRole"}, + {4, nullptr, "SetDiagData"}, + {5, nullptr, "GetDiagData"}, }; // clang-format on diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 7423287ea..348360b51 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -15,6 +15,7 @@ #include "common/common_funcs.h" #include "common/logging/log.h" #include "common/math_util.h" +#include "common/settings.h" #include "common/swap.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" @@ -30,7 +31,6 @@ #include "core/hle/service/vi/vi_m.h" #include "core/hle/service/vi/vi_s.h" #include "core/hle/service/vi/vi_u.h" -#include "core/settings.h" namespace Service::VI { @@ -695,6 +695,7 @@ public: {2205, &ISystemDisplayService::SetLayerZ, "SetLayerZ"}, {2207, &ISystemDisplayService::SetLayerVisibility, "SetLayerVisibility"}, {2209, nullptr, "SetLayerAlpha"}, + {2210, nullptr, "SetLayerPositionAndSize"}, {2312, nullptr, "CreateStrayLayer"}, {2400, nullptr, "OpenIndirectLayer"}, {2401, nullptr, "CloseIndirectLayer"}, @@ -718,6 +719,7 @@ public: {3215, nullptr, "SetDisplayGamma"}, {3216, nullptr, "GetDisplayCmuLuma"}, {3217, nullptr, "SetDisplayCmuLuma"}, + {3218, nullptr, "SetDisplayCrcMode"}, {6013, nullptr, "GetLayerPresentationSubmissionTimestamps"}, {8225, nullptr, "GetSharedBufferMemoryHandleId"}, {8250, nullptr, "OpenSharedLayer"}, @@ -729,6 +731,7 @@ public: {8256, nullptr, "GetSharedFrameBufferAcquirableEvent"}, {8257, nullptr, "FillSharedFrameBufferColor"}, {8258, nullptr, "CancelSharedFrameBuffer"}, + {9000, nullptr, "GetDp2hdmiController"}, }; RegisterHandlers(functions); } @@ -808,10 +811,15 @@ public: {2402, nullptr, "GetDisplayHotplugState"}, {2501, nullptr, "GetCompositorErrorInfo"}, {2601, nullptr, "GetDisplayErrorEvent"}, + {2701, nullptr, "GetDisplayFatalErrorEvent"}, {4201, nullptr, "SetDisplayAlpha"}, {4203, nullptr, "SetDisplayLayerStack"}, {4205, nullptr, "SetDisplayPowerState"}, {4206, nullptr, "SetDefaultDisplay"}, + {4207, nullptr, "ResetDisplayPanel"}, + {4208, nullptr, "SetDisplayFatalErrorEnabled"}, + {4209, nullptr, "IsDisplayPanelOn"}, + {4300, nullptr, "GetInternalPanelId"}, {6000, &IManagerDisplayService::AddToLayerStack, "AddToLayerStack"}, {6001, nullptr, "RemoveFromLayerStack"}, {6002, &IManagerDisplayService::SetLayerVisibility, "SetLayerVisibility"}, diff --git a/src/core/hle/service/wlan/wlan.cpp b/src/core/hle/service/wlan/wlan.cpp index ddbf04069..44957e01d 100644 --- a/src/core/hle/service/wlan/wlan.cpp +++ b/src/core/hle/service/wlan/wlan.cpp @@ -46,6 +46,13 @@ public: {28, nullptr, "Unknown28"}, {29, nullptr, "Unknown29"}, {30, nullptr, "Unknown30"}, + {31, nullptr, "Unknown31"}, + {32, nullptr, "Unknown32"}, + {33, nullptr, "Unknown33"}, + {34, nullptr, "Unknown34"}, + {35, nullptr, "Unknown35"}, + {36, nullptr, "Unknown36"}, + {37, nullptr, "Unknown37"}, }; // clang-format on diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 14618cb40..0115ed0c4 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -9,6 +9,7 @@ #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "common/settings.h" #include "common/swap.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" @@ -22,7 +23,6 @@ #include "core/loader/nro.h" #include "core/loader/nso.h" #include "core/memory.h" -#include "core/settings.h" namespace Loader { diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index cbd048695..0c83dd666 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -11,6 +11,7 @@ #include "common/hex_util.h" #include "common/logging/log.h" #include "common/lz4_compression.h" +#include "common/settings.h" #include "common/swap.h" #include "core/core.h" #include "core/file_sys/patch_manager.h" @@ -20,7 +21,6 @@ #include "core/hle/kernel/process.h" #include "core/loader/nso.h" #include "core/memory.h" -#include "core/settings.h" namespace Loader { namespace { diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index b93396a80..c92337079 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -13,8 +13,8 @@ #include <fmt/format.h> #include "common/file_util.h" #include "common/math_util.h" +#include "common/settings.h" #include "core/perf_stats.h" -#include "core/settings.h" using namespace std::chrono_literals; using DoubleSecs = std::chrono::duration<double, std::chrono::seconds::period>; diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 74fb32814..311d4dda8 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -14,6 +14,7 @@ #include "common/file_util.h" #include "common/hex_util.h" #include "common/scm_rev.h" +#include "common/settings.h" #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/hle/kernel/hle_ipc.h" @@ -22,7 +23,6 @@ #include "core/hle/result.h" #include "core/memory.h" #include "core/reporter.h" -#include "core/settings.h" namespace { diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index d11b15f38..6dcff5400 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -12,10 +12,10 @@ #include "common/file_util.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/loader/loader.h" -#include "core/settings.h" #include "core/telemetry_session.h" #ifdef ENABLE_WEB_SERVICE diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 38ab31898..c3cfe7efc 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -9,8 +9,6 @@ add_library(input_common STATIC motion_from_button.h motion_input.cpp motion_input.h - settings.cpp - settings.h touch_from_button.cpp touch_from_button.h gcadapter/gc_adapter.cpp diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp index 770893687..f8ec179d0 100755 --- a/src/input_common/analog_from_button.cpp +++ b/src/input_common/analog_from_button.cpp @@ -7,7 +7,7 @@ #include <cmath> #include <thread> #include "common/math_util.h" -#include "core/settings.h" +#include "common/settings.h" #include "input_common/analog_from_button.h" namespace InputCommon { diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp index d80195c82..ec3167bea 100644 --- a/src/input_common/gcadapter/gc_adapter.cpp +++ b/src/input_common/gcadapter/gc_adapter.cpp @@ -16,8 +16,8 @@ #include "common/logging/log.h" #include "common/param_package.h" +#include "common/settings_input.h" #include "input_common/gcadapter/gc_adapter.h" -#include "input_common/settings.h" namespace GCAdapter { diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp index 329e416c7..fff1c6b45 100644 --- a/src/input_common/mouse/mouse_input.cpp +++ b/src/input_common/mouse/mouse_input.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "core/settings.h" +#include "common/settings.h" #include "input_common/mouse/mouse_input.h" namespace MouseInput { diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp index 0e1db54fb..d96104a4e 100644 --- a/src/input_common/mouse/mouse_poller.cpp +++ b/src/input_common/mouse/mouse_poller.cpp @@ -5,8 +5,8 @@ #include <mutex> #include <utility> +#include "common/settings.h" #include "common/threadsafe_queue.h" -#include "core/settings.h" #include "input_common/mouse/mouse_input.h" #include "input_common/mouse/mouse_poller.h" diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index f67de37e3..9418e78fa 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -20,11 +20,11 @@ #include <SDL.h> #include "common/logging/log.h" #include "common/param_package.h" +#include "common/settings_input.h" #include "common/threadsafe_queue.h" #include "core/frontend/input.h" #include "input_common/motion_input.h" #include "input_common/sdl/sdl_impl.h" -#include "input_common/settings.h" namespace InputCommon::SDL { @@ -761,7 +761,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() { for (const auto& joystick : value) { if (auto* const controller = joystick->GetSDLGameController()) { std::string name = - fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort()); + fmt::format("{} {}", GetControllerName(controller), joystick->GetPort()); devices.emplace_back(Common::ParamPackage{ {"class", "sdl"}, {"display", std::move(name)}, @@ -782,6 +782,17 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() { return devices; } +std::string SDLState::GetControllerName(SDL_GameController* controller) const { + switch (SDL_GameControllerGetType(controller)) { + case SDL_CONTROLLER_TYPE_XBOX360: + return "XBox 360 Controller"; + case SDL_CONTROLLER_TYPE_XBOXONE: + return "XBox One Controller"; + default: + return SDL_GameControllerName(controller); + } +} + namespace { Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis, float value = 0.1f) { @@ -930,16 +941,19 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa return {}; } + const bool invert = + SDL_GameControllerGetType(controller) != SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; + // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. // We will add those afterwards // This list also excludes Screenshot since theres not really a mapping for that using ButtonBindings = std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>; - static constexpr ButtonBindings switch_to_sdl_button{{ - {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B}, - {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A}, - {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y}, - {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X}, + const ButtonBindings switch_to_sdl_button{{ + {Settings::NativeButton::A, invert ? SDL_CONTROLLER_BUTTON_B : SDL_CONTROLLER_BUTTON_A}, + {Settings::NativeButton::B, invert ? SDL_CONTROLLER_BUTTON_A : SDL_CONTROLLER_BUTTON_B}, + {Settings::NativeButton::X, invert ? SDL_CONTROLLER_BUTTON_Y : SDL_CONTROLLER_BUTTON_X}, + {Settings::NativeButton::Y, invert ? SDL_CONTROLLER_BUTTON_X : SDL_CONTROLLER_BUTTON_Y}, {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index 08044b00d..8b7363f56 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -14,6 +14,7 @@ #include "input_common/sdl/sdl.h" union SDL_Event; +using SDL_GameController = struct _SDL_GameController; using SDL_Joystick = struct _SDL_Joystick; using SDL_JoystickID = s32; @@ -64,6 +65,9 @@ private: /// Needs to be called before SDL_QuitSubSystem. void CloseJoysticks(); + /// Returns a custom name for specific controllers because the default name is not correct + std::string GetControllerName(SDL_GameController* controller) const; + // Set to true if SDL supports game controller subsystem bool has_gamecontroller = false; diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp index ffbe4f2ed..e94ba197b 100644 --- a/src/input_common/touch_from_button.cpp +++ b/src/input_common/touch_from_button.cpp @@ -2,8 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/settings.h" #include "core/frontend/framebuffer_layout.h" -#include "core/settings.h" #include "input_common/touch_from_button.h" namespace InputCommon { diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index df73f9ff7..8a38a380d 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -9,7 +9,7 @@ #include <thread> #include <boost/asio.hpp> #include "common/logging/log.h" -#include "core/settings.h" +#include "common/settings.h" #include "input_common/udp/client.h" #include "input_common/udp/protocol.h" @@ -27,11 +27,9 @@ class Socket { public: using clock = std::chrono::system_clock; - explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, - SocketCallback callback_) + explicit Socket(const std::string& host, u16 port, SocketCallback callback_) : callback(std::move(callback_)), timer(io_service), - socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()), - pad_index(pad_index_) { + socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()) { boost::system::error_code ec{}; auto ipv4 = boost::asio::ip::make_address_v4(host, ec); if (ec.value() != boost::system::errc::success) { @@ -99,15 +97,15 @@ private: void HandleSend(const boost::system::error_code&) { boost::system::error_code _ignored{}; // Send a request for getting port info for the pad - const Request::PortInfo port_info{1, {static_cast<u8>(pad_index), 0, 0, 0}}; + const Request::PortInfo port_info{4, {0, 1, 2, 3}}; const auto port_message = Request::Create(port_info, client_id); std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE); socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored); // Send a request for getting pad data for the pad const Request::PadData pad_data{ - Request::PadData::Flags::Id, - static_cast<u8>(pad_index), + Request::PadData::Flags::AllPorts, + 0, EMPTY_MAC_ADDRESS, }; const auto pad_message = Request::Create(pad_data, client_id); @@ -122,7 +120,6 @@ private: udp::socket socket; const u32 client_id; - std::size_t pad_index{}; static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>); static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>); @@ -150,34 +147,32 @@ Client::~Client() { Reset(); } -Client::ClientData::ClientData() = default; +Client::ClientConnection::ClientConnection() = default; -Client::ClientData::~ClientData() = default; +Client::ClientConnection::~ClientConnection() = default; std::vector<Common::ParamPackage> Client::GetInputDevices() const { std::vector<Common::ParamPackage> devices; - for (std::size_t client = 0; client < clients.size(); client++) { - if (!DeviceConnected(client)) { + for (std::size_t pad = 0; pad < pads.size(); pad++) { + if (!DeviceConnected(pad)) { continue; } - std::string name = fmt::format("UDP Controller {}", client); + std::string name = fmt::format("UDP Controller {}", pad); devices.emplace_back(Common::ParamPackage{ {"class", "cemuhookudp"}, {"display", std::move(name)}, - {"port", std::to_string(client)}, + {"port", std::to_string(pad)}, }); } return devices; } -bool Client::DeviceConnected(std::size_t client) const { +bool Client::DeviceConnected(std::size_t pad) const { // Use last timestamp to detect if the socket has stopped sending data const auto now = std::chrono::steady_clock::now(); - const auto time_difference = - static_cast<u64>(std::chrono::duration_cast<std::chrono::milliseconds>( - now - clients[client].last_motion_update) - .count()); - return time_difference < 1000 && clients[client].active == 1; + const auto time_difference = static_cast<u64>( + std::chrono::duration_cast<std::chrono::milliseconds>(now - pads[pad].last_update).count()); + return time_difference < 1000 && pads[pad].connected; } void Client::ReloadSockets() { @@ -202,25 +197,21 @@ void Client::ReloadSockets() { continue; } - for (std::size_t pad = 0; pad < 4; ++pad) { - const std::size_t client_number = - GetClientNumber(udp_input_address, udp_input_port, pad); - if (client_number != MAX_UDP_CLIENTS) { - LOG_ERROR(Input, "Duplicated UDP servers found"); - continue; - } - StartCommunication(client++, udp_input_address, udp_input_port, pad); + const std::size_t client_number = GetClientNumber(udp_input_address, udp_input_port); + if (client_number != MAX_UDP_CLIENTS) { + LOG_ERROR(Input, "Duplicated UDP servers found"); + continue; } + StartCommunication(client++, udp_input_address, udp_input_port); } } -std::size_t Client::GetClientNumber(std::string_view host, u16 port, std::size_t pad) const { +std::size_t Client::GetClientNumber(std::string_view host, u16 port) const { for (std::size_t client = 0; client < clients.size(); client++) { if (clients[client].active == -1) { continue; } - if (clients[client].host == host && clients[client].port == port && - clients[client].pad_index == pad) { + if (clients[client].host == host && clients[client].port == port) { return client; } } @@ -236,69 +227,75 @@ void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) { } void Client::OnPadData(Response::PadData data, std::size_t client) { - // Accept packets only for the correct pad - if (static_cast<u8>(clients[client].pad_index) != data.info.id) { + const std::size_t pad_index = (client * PADS_PER_CLIENT) + data.info.id; + + if (pad_index >= pads.size()) { + LOG_ERROR(Input, "Invalid pad id {}", data.info.id); return; } LOG_TRACE(Input, "PadData packet received"); - if (data.packet_counter == clients[client].packet_sequence) { + if (data.packet_counter == pads[pad_index].packet_sequence) { LOG_WARNING( Input, "PadData packet dropped because its stale info. Current count: {} Packet count: {}", - clients[client].packet_sequence, data.packet_counter); + pads[pad_index].packet_sequence, data.packet_counter); + pads[pad_index].connected = false; return; } - clients[client].active = static_cast<s8>(data.info.is_pad_active); - clients[client].packet_sequence = data.packet_counter; + + clients[client].active = 1; + pads[pad_index].connected = true; + pads[pad_index].packet_sequence = data.packet_counter; + const auto now = std::chrono::steady_clock::now(); - const auto time_difference = - static_cast<u64>(std::chrono::duration_cast<std::chrono::microseconds>( - now - clients[client].last_motion_update) - .count()); - clients[client].last_motion_update = now; + const auto time_difference = static_cast<u64>( + std::chrono::duration_cast<std::chrono::microseconds>(now - pads[pad_index].last_update) + .count()); + pads[pad_index].last_update = now; + const Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw}; - clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y}); + pads[pad_index].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y}); // Gyroscope values are not it the correct scale from better joy. // Dividing by 312 allows us to make one full turn = 1 turn // This must be a configurable valued called sensitivity - clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f); - clients[client].motion.UpdateRotation(time_difference); - clients[client].motion.UpdateOrientation(time_difference); + pads[pad_index].motion.SetGyroscope(raw_gyroscope / 312.0f); + pads[pad_index].motion.UpdateRotation(time_difference); + pads[pad_index].motion.UpdateOrientation(time_difference); { - std::lock_guard guard(clients[client].status.update_mutex); - clients[client].status.motion_status = clients[client].motion.GetMotion(); + std::lock_guard guard(pads[pad_index].status.update_mutex); + pads[pad_index].status.motion_status = pads[pad_index].motion.GetMotion(); for (std::size_t id = 0; id < data.touch.size(); ++id) { UpdateTouchInput(data.touch[id], client, id); } if (configuring) { - const Common::Vec3f gyroscope = clients[client].motion.GetGyroscope(); - const Common::Vec3f accelerometer = clients[client].motion.GetAcceleration(); - UpdateYuzuSettings(client, accelerometer, gyroscope); + const Common::Vec3f gyroscope = pads[pad_index].motion.GetGyroscope(); + const Common::Vec3f accelerometer = pads[pad_index].motion.GetAcceleration(); + UpdateYuzuSettings(client, data.info.id, accelerometer, gyroscope); } } } -void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, - std::size_t pad_index) { +void Client::StartCommunication(std::size_t client, const std::string& host, u16 port) { SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, [this](Response::PortInfo info) { OnPortInfo(info); }, [this, client](Response::PadData data) { OnPadData(data, client); }}; - LOG_INFO(Input, "Starting communication with UDP input server on {}:{}:{}", host, port, - pad_index); + LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); clients[client].host = host; clients[client].port = port; - clients[client].pad_index = pad_index; clients[client].active = 0; - clients[client].socket = std::make_unique<Socket>(host, port, pad_index, callback); + clients[client].socket = std::make_unique<Socket>(host, port, callback); clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()}; + // Set motion parameters // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode // Real HW values are unknown, 0.0001 is an approximate to Standard - clients[client].motion.SetGyroThreshold(0.0001f); + for (std::size_t pad = 0; pad < PADS_PER_CLIENT; pad++) { + pads[client * PADS_PER_CLIENT + pad].motion.SetGyroThreshold(0.0001f); + } } void Client::Reset() { @@ -311,8 +308,8 @@ void Client::Reset() { } } -void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, - const Common::Vec3<float>& gyro) { +void Client::UpdateYuzuSettings(std::size_t client, std::size_t pad_index, + const Common::Vec3<float>& acc, const Common::Vec3<float>& gyro) { if (gyro.Length() > 0.2f) { LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {})", client, gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2]); @@ -320,7 +317,7 @@ void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& a UDPPadStatus pad{ .host = clients[client].host, .port = clients[client].port, - .pad_index = clients[client].pad_index, + .pad_index = pad_index, }; for (std::size_t i = 0; i < 3; ++i) { if (gyro[i] > 5.0f || gyro[i] < -5.0f) { @@ -391,19 +388,19 @@ void Client::EndConfiguration() { } DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) { - const std::size_t client_number = GetClientNumber(host, port, pad); - if (client_number == MAX_UDP_CLIENTS) { - return clients[0].status; + const std::size_t client_number = GetClientNumber(host, port); + if (client_number == MAX_UDP_CLIENTS || pad >= PADS_PER_CLIENT) { + return pads[0].status; } - return clients[client_number].status; + return pads[(client_number * PADS_PER_CLIENT) + pad].status; } const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const { - const std::size_t client_number = GetClientNumber(host, port, pad); - if (client_number == MAX_UDP_CLIENTS) { - return clients[0].status; + const std::size_t client_number = GetClientNumber(host, port); + if (client_number == MAX_UDP_CLIENTS || pad >= PADS_PER_CLIENT) { + return pads[0].status; } - return clients[client_number].status; + return pads[(client_number * PADS_PER_CLIENT) + pad].status; } Input::TouchStatus& Client::GetTouchState() { @@ -422,7 +419,7 @@ const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const { return pad_queue; } -void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, +void TestCommunication(const std::string& host, u16 port, const std::function<void()>& success_callback, const std::function<void()>& failure_callback) { std::thread([=] { @@ -432,9 +429,10 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, .port_info = [](Response::PortInfo) {}, .pad_data = [&](Response::PadData) { success_event.Set(); }, }; - Socket socket{host, port, pad_index, std::move(callback)}; + Socket socket{host, port, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; - const bool result = success_event.WaitFor(std::chrono::seconds(5)); + const bool result = + success_event.WaitUntil(std::chrono::steady_clock::now() + std::chrono::seconds(10)); socket.Stop(); worker_thread.join(); if (result) { @@ -446,8 +444,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, } CalibrationConfigurationJob::CalibrationConfigurationJob( - const std::string& host, u16 port, std::size_t pad_index, - std::function<void(Status)> status_callback, + const std::string& host, u16 port, std::function<void(Status)> status_callback, std::function<void(u16, u16, u16, u16)> data_callback) { std::thread([=, this] { @@ -491,7 +488,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( complete_event.Set(); } }}; - Socket socket{host, port, pad_index, std::move(callback)}; + Socket socket{host, port, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; complete_event.Wait(); socket.Stop(); diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h index e9e438e88..a11ea3068 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/udp/client.h @@ -84,7 +84,7 @@ public: std::vector<Common::ParamPackage> GetInputDevices() const; - bool DeviceConnected(std::size_t client) const; + bool DeviceConnected(std::size_t pad) const; void ReloadSockets(); Common::SPSCQueue<UDPPadStatus>& GetPadQueue(); @@ -97,38 +97,40 @@ public: const Input::TouchStatus& GetTouchState() const; private: - struct ClientData { - ClientData(); - ~ClientData(); - - std::string host{"127.0.0.1"}; - u16 port{26760}; + struct PadData { std::size_t pad_index{}; - std::unique_ptr<Socket> socket; + bool connected{}; DeviceStatus status; - std::thread thread; u64 packet_sequence{}; - s8 active{-1}; // Realtime values // motion is initalized with PID values for drift correction on joycons InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f}; - std::chrono::time_point<std::chrono::steady_clock> last_motion_update; + std::chrono::time_point<std::chrono::steady_clock> last_update; + }; + + struct ClientConnection { + ClientConnection(); + ~ClientConnection(); + std::string host{"127.0.0.1"}; + u16 port{26760}; + s8 active{-1}; + std::unique_ptr<Socket> socket; + std::thread thread; }; // For shutting down, clear all data, join all threads, release usb void Reset(); // Translates configuration to client number - std::size_t GetClientNumber(std::string_view host, u16 port, std::size_t pad) const; + std::size_t GetClientNumber(std::string_view host, u16 port) const; void OnVersion(Response::Version); void OnPortInfo(Response::PortInfo); void OnPadData(Response::PadData, std::size_t client); - void StartCommunication(std::size_t client, const std::string& host, u16 port, - std::size_t pad_index); - void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, - const Common::Vec3<float>& gyro); + void StartCommunication(std::size_t client, const std::string& host, u16 port); + void UpdateYuzuSettings(std::size_t client, std::size_t pad_index, + const Common::Vec3<float>& acc, const Common::Vec3<float>& gyro); // Returns an unused finger id, if there is no fingers available std::nullopt will be // returned @@ -140,10 +142,12 @@ private: bool configuring = false; // Allocate clients for 8 udp servers - static constexpr std::size_t MAX_UDP_CLIENTS = 4 * 8; + static constexpr std::size_t MAX_UDP_CLIENTS = 8; + static constexpr std::size_t PADS_PER_CLIENT = 4; // Each client can have up 2 touch inputs static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2; - std::array<ClientData, MAX_UDP_CLIENTS> clients{}; + std::array<PadData, MAX_UDP_CLIENTS * PADS_PER_CLIENT> pads{}; + std::array<ClientConnection, MAX_UDP_CLIENTS> clients{}; Common::SPSCQueue<UDPPadStatus> pad_queue{}; Input::TouchStatus touch_status{}; std::array<std::size_t, MAX_TOUCH_FINGERS> finger_id{}; @@ -164,7 +168,7 @@ public: * @param status_callback Callback for job status updates * @param data_callback Called when calibration data is ready */ - explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index, + explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::function<void(Status)> status_callback, std::function<void(u16, u16, u16, u16)> data_callback); ~CalibrationConfigurationJob(); @@ -174,7 +178,7 @@ private: Common::Event complete_event; }; -void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, +void TestCommunication(const std::string& host, u16 port, const std::function<void()>& success_callback, const std::function<void()>& failure_callback); diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 9b931976a..47190c464 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -236,7 +236,6 @@ add_library(video_core STATIC texture_cache/types.h texture_cache/util.cpp texture_cache/util.h - textures/astc.cpp textures/astc.h textures/decoders.cpp textures/decoders.h diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 4de1e37e5..32dcbd693 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -20,8 +20,8 @@ #include "common/div_ceil.h" #include "common/microprofile.h" #include "common/scope_exit.h" +#include "common/settings.h" #include "core/memory.h" -#include "core/settings.h" #include "video_core/buffer_cache/buffer_base.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/dirty_flags.h" diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp index 59e586695..29bb31418 100644 --- a/src/video_core/command_classes/codecs/vp9.cpp +++ b/src/video_core/command_classes/codecs/vp9.cpp @@ -2,8 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <cstring> // for std::memcpy +#include <algorithm> // for std::copy #include <numeric> +#include "common/assert.h" #include "video_core/command_classes/codecs/vp9.h" #include "video_core/gpu.h" #include "video_core/memory_manager.h" @@ -362,7 +363,8 @@ Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) // surface_luma_offset[0:3] contains the address of the reference frame offsets in the following // order: last, golden, altref, current. It may be worthwhile to track the updates done here // to avoid buffering frame data needed for reference frame updating in the header composition. - std::memcpy(vp9_info.frame_offsets.data(), state.surface_luma_offset.data(), 4 * sizeof(u64)); + std::copy(state.surface_luma_offset.begin(), state.surface_luma_offset.begin() + 4, + vp9_info.frame_offsets.begin()); return vp9_info; } @@ -821,11 +823,11 @@ const std::vector<u8>& VP9::ComposeFrameHeader(const NvdecCommon::NvdecRegisters // Write headers and frame to buffer frame.resize(uncompressed_header.size() + compressed_header.size() + bitstream.size()); - std::memcpy(frame.data(), uncompressed_header.data(), uncompressed_header.size()); - std::memcpy(frame.data() + uncompressed_header.size(), compressed_header.data(), - compressed_header.size()); - std::memcpy(frame.data() + uncompressed_header.size() + compressed_header.size(), - bitstream.data(), bitstream.size()); + std::copy(uncompressed_header.begin(), uncompressed_header.end(), frame.begin()); + std::copy(compressed_header.begin(), compressed_header.end(), + frame.begin() + uncompressed_header.size()); + std::copy(bitstream.begin(), bitstream.end(), + frame.begin() + uncompressed_header.size() + compressed_header.size()); // keep track of frame number current_frame_number++; diff --git a/src/video_core/engines/engine_interface.h b/src/video_core/engines/engine_interface.h index 18a9db7e6..c7ffd68c5 100644 --- a/src/video_core/engines/engine_interface.h +++ b/src/video_core/engines/engine_interface.h @@ -4,13 +4,14 @@ #pragma once -#include <type_traits> #include "common/common_types.h" namespace Tegra::Engines { class EngineInterface { public: + virtual ~EngineInterface() = default; + /// Write the value to the register identified by method. virtual void CallMethod(u32 method, u32 method_argument, bool is_last_call) = 0; diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index c808a577d..a4170ffff 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h @@ -35,7 +35,7 @@ namespace Tegra::Engines { class Fermi2D final : public EngineInterface { public: explicit Fermi2D(); - ~Fermi2D(); + ~Fermi2D() override; /// Binds a rasterizer to this engine. void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index 19808a5c6..0d8ea09a9 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h @@ -36,7 +36,7 @@ namespace Tegra::Engines { class KeplerMemory final : public EngineInterface { public: explicit KeplerMemory(Core::System& system_, MemoryManager& memory_manager); - ~KeplerMemory(); + ~KeplerMemory() override; /// Write the value to the register identified by method. void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index a2f19559f..2ee980bab 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -4,8 +4,8 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_dma.h" #include "video_core/memory_manager.h" diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 3c59eeb13..c77f02a22 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -188,7 +188,7 @@ public: static_assert(sizeof(RemapConst) == 12); explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_); - ~MaxwellDMA(); + ~MaxwellDMA() override; /// Write the value to the register identified by method. void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 36c31fec2..7c42f1177 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -6,13 +6,13 @@ #include "common/assert.h" #include "common/microprofile.h" +#include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/frontend/emu_window.h" #include "core/hardware_interrupt_manager.h" #include "core/memory.h" -#include "core/settings.h" #include "video_core/engines/fermi_2d.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/kepler_memory.h" @@ -519,8 +519,8 @@ void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const { interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); } -void GPU::WaitIdle() const { - gpu_thread.WaitIdle(); +void GPU::ShutDown() { + gpu_thread.ShutDown(); } void GPU::OnCommandListEnd() { diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index d40982a54..b1960ea86 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -219,8 +219,8 @@ public: return *shader_notify; } - // Waits for the GPU to finish working - void WaitIdle() const; + // Stops the GPU execution and waits for the GPU to finish working + void ShutDown(); /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. void WaitFence(u32 syncpoint_id, u32 value); diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 99353f15f..cd1fbb9bf 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -5,10 +5,10 @@ #include "common/assert.h" #include "common/microprofile.h" #include "common/scope_exit.h" +#include "common/settings.h" #include "common/thread.h" #include "core/core.h" #include "core/frontend/emu_window.h" -#include "core/settings.h" #include "video_core/dma_pusher.h" #include "video_core/gpu.h" #include "video_core/gpu_thread.h" @@ -29,8 +29,7 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, system.RegisterHostThread(); // Wait for first GPU command before acquiring the window context - while (state.queue.Empty()) - ; + state.queue.Wait(); // If emulation was stopped during disk shader loading, abort before trying to acquire context if (!state.is_running) { @@ -57,11 +56,17 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) { rasterizer->OnCPUWrite(invalidate->addr, invalidate->size); } else if (std::holds_alternative<EndProcessingCommand>(next.data)) { - return; + ASSERT(state.is_running == false); } else { UNREACHABLE(); } state.signaled_fence.store(next.fence); + if (next.block) { + // We have to lock the write_lock to ensure that the condition_variable wait not get a + // race between the check and the lock itself. + std::lock_guard lk(state.write_lock); + state.cv.notify_all(); + } } } @@ -69,13 +74,7 @@ ThreadManager::ThreadManager(Core::System& system_, bool is_async_) : system{system_}, is_async{is_async_} {} ThreadManager::~ThreadManager() { - if (!thread.joinable()) { - return; - } - - // Notify GPU thread that a shutdown is pending - PushCommand(EndProcessingCommand()); - thread.join(); + ShutDown(); } void ThreadManager::StartThread(VideoCore::RendererBase& renderer, @@ -112,9 +111,8 @@ void ThreadManager::FlushRegion(VAddr addr, u64 size) { case Settings::GPUAccuracy::Extreme: { auto& gpu = system.GPU(); u64 fence = gpu.RequestFlush(addr, size); - PushCommand(GPUTickCommand()); - while (fence > gpu.CurrentFlushRequestFence()) { - } + PushCommand(GPUTickCommand(), true); + ASSERT(fence <= gpu.CurrentFlushRequestFence()); break; } default: @@ -131,23 +129,45 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { rasterizer->OnCPUWrite(addr, size); } -void ThreadManager::WaitIdle() const { - while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed) && - system.IsPoweredOn()) { +void ThreadManager::ShutDown() { + if (!state.is_running) { + return; } + + { + std::lock_guard lk(state.write_lock); + state.is_running = false; + state.cv.notify_all(); + } + + if (!thread.joinable()) { + return; + } + + // Notify GPU thread that a shutdown is pending + PushCommand(EndProcessingCommand()); + thread.join(); } void ThreadManager::OnCommandListEnd() { PushCommand(OnCommandListEndCommand()); } -u64 ThreadManager::PushCommand(CommandData&& command_data) { - const u64 fence{++state.last_fence}; - state.queue.Push(CommandDataContainer(std::move(command_data), fence)); - +u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { if (!is_async) { // In synchronous GPU mode, block the caller until the command has executed - WaitIdle(); + block = true; + } + + std::unique_lock lk(state.write_lock); + const u64 fence{++state.last_fence}; + state.queue.Push(CommandDataContainer(std::move(command_data), fence, block)); + + if (block) { + state.cv.wait(lk, [this, fence] { + return fence <= state.signaled_fence.load(std::memory_order_relaxed) || + !state.is_running; + }); } return fence; diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 18269e51c..11a648f38 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -90,21 +90,24 @@ using CommandData = struct CommandDataContainer { CommandDataContainer() = default; - explicit CommandDataContainer(CommandData&& data_, u64 next_fence_) - : data{std::move(data_)}, fence{next_fence_} {} + explicit CommandDataContainer(CommandData&& data_, u64 next_fence_, bool block_) + : data{std::move(data_)}, fence{next_fence_}, block(block_) {} CommandData data; u64 fence{}; + bool block{}; }; /// Struct used to synchronize the GPU thread struct SynchState final { std::atomic_bool is_running{true}; - using CommandQueue = Common::MPSCQueue<CommandDataContainer>; + using CommandQueue = Common::SPSCQueue<CommandDataContainer>; + std::mutex write_lock; CommandQueue queue; u64 last_fence{}; std::atomic<u64> signaled_fence{}; + std::condition_variable cv; }; /// Class used to manage the GPU thread @@ -132,14 +135,14 @@ public: /// Notify rasterizer that any caches of the specified region should be flushed and invalidated void FlushAndInvalidateRegion(VAddr addr, u64 size); - // Wait until the gpu thread is idle. - void WaitIdle() const; + // Stops the GPU execution and waits for the GPU to finish working + void ShutDown(); void OnCommandListEnd(); private: /// Pushes a command to be executed by the GPU thread - u64 PushCommand(CommandData&& command_data); + u64 PushCommand(CommandData&& command_data, bool block = false); Core::System& system; const bool is_async; diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 3494318ca..2208e1922 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -1,4 +1,5 @@ set(SHADER_FILES + astc_decoder.comp block_linear_unswizzle_2d.comp block_linear_unswizzle_3d.comp convert_depth_to_float.frag diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake index c0fc49768..1b4bc6103 100644 --- a/src/video_core/host_shaders/StringShaderHeader.cmake +++ b/src/video_core/host_shaders/StringShaderHeader.cmake @@ -6,7 +6,27 @@ get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME) string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME}) string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME) -file(READ ${SOURCE_FILE} CONTENTS) +FILE(READ ${SOURCE_FILE} line_contents) + +# Replace double quotes with single quotes, +# as double quotes will be used to wrap the lines +STRING(REGEX REPLACE "\"" "'" line_contents "${line_contents}") + +# CMake separates list elements with semicolons, but semicolons +# are used extensively in the shader code. +# Replace with a temporary marker, to be reverted later. +STRING(REGEX REPLACE ";" "{{SEMICOLON}}" line_contents "${line_contents}") + +# Make every line an individual element in the CMake list. +STRING(REGEX REPLACE "\n" ";" line_contents "${line_contents}") + +# Build the shader string, wrapping each line in double quotes. +foreach(line IN LISTS line_contents) + string(CONCAT CONTENTS "${CONTENTS}" \"${line}\\n\"\n) +endforeach() + +# Revert the original semicolons in the source. +STRING(REGEX REPLACE "{{SEMICOLON}}" ";" CONTENTS "${CONTENTS}") get_filename_component(OUTPUT_DIR ${HEADER_FILE} DIRECTORY) make_directory(${OUTPUT_DIR}) diff --git a/src/video_core/host_shaders/astc_decoder.comp b/src/video_core/host_shaders/astc_decoder.comp new file mode 100644 index 000000000..703e34587 --- /dev/null +++ b/src/video_core/host_shaders/astc_decoder.comp @@ -0,0 +1,1339 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 450 + +#ifdef VULKAN + +#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { +#define END_PUSH_CONSTANTS }; +#define UNIFORM(n) +#define BINDING_INPUT_BUFFER 0 +#define BINDING_ENC_BUFFER 1 +#define BINDING_6_TO_8_BUFFER 2 +#define BINDING_7_TO_8_BUFFER 3 +#define BINDING_8_TO_8_BUFFER 4 +#define BINDING_BYTE_TO_16_BUFFER 5 +#define BINDING_SWIZZLE_BUFFER 6 +#define BINDING_OUTPUT_IMAGE 7 + +#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv + +#define BEGIN_PUSH_CONSTANTS +#define END_PUSH_CONSTANTS +#define UNIFORM(n) layout(location = n) uniform +#define BINDING_SWIZZLE_BUFFER 0 +#define BINDING_INPUT_BUFFER 1 +#define BINDING_ENC_BUFFER 2 +#define BINDING_6_TO_8_BUFFER 3 +#define BINDING_7_TO_8_BUFFER 4 +#define BINDING_8_TO_8_BUFFER 5 +#define BINDING_BYTE_TO_16_BUFFER 6 +#define BINDING_OUTPUT_IMAGE 0 + +#endif + +layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in; + +BEGIN_PUSH_CONSTANTS +UNIFORM(1) uvec2 block_dims; + +UNIFORM(2) uint bytes_per_block_log2; +UNIFORM(3) uint layer_stride; +UNIFORM(4) uint block_size; +UNIFORM(5) uint x_shift; +UNIFORM(6) uint block_height; +UNIFORM(7) uint block_height_mask; +END_PUSH_CONSTANTS + +struct EncodingData { + uint encoding; + uint num_bits; + uint bit_value; + uint quint_trit_value; +}; + +struct TexelWeightParams { + uvec2 size; + uint max_weight; + bool dual_plane; + bool error_state; + bool void_extent_ldr; + bool void_extent_hdr; +}; + +// Swizzle data +layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable { + uint swizzle_table[]; +}; + +layout(binding = BINDING_INPUT_BUFFER, std430) readonly buffer InputBufferU32 { + uint astc_data[]; +}; + +// ASTC Encodings data +layout(binding = BINDING_ENC_BUFFER, std430) readonly buffer EncodingsValues { + EncodingData encoding_values[]; +}; +// ASTC Precompiled tables +layout(binding = BINDING_6_TO_8_BUFFER, std430) readonly buffer REPLICATE_6_BIT_TO_8 { + uint REPLICATE_6_BIT_TO_8_TABLE[]; +}; +layout(binding = BINDING_7_TO_8_BUFFER, std430) readonly buffer REPLICATE_7_BIT_TO_8 { + uint REPLICATE_7_BIT_TO_8_TABLE[]; +}; +layout(binding = BINDING_8_TO_8_BUFFER, std430) readonly buffer REPLICATE_8_BIT_TO_8 { + uint REPLICATE_8_BIT_TO_8_TABLE[]; +}; +layout(binding = BINDING_BYTE_TO_16_BUFFER, std430) readonly buffer REPLICATE_BYTE_TO_16 { + uint REPLICATE_BYTE_TO_16_TABLE[]; +}; + +layout(binding = BINDING_OUTPUT_IMAGE, rgba8) uniform writeonly image2DArray dest_image; + +const uint GOB_SIZE_X = 64; +const uint GOB_SIZE_Y = 8; +const uint GOB_SIZE_Z = 1; +const uint GOB_SIZE = GOB_SIZE_X * GOB_SIZE_Y * GOB_SIZE_Z; + +const uint GOB_SIZE_X_SHIFT = 6; +const uint GOB_SIZE_Y_SHIFT = 3; +const uint GOB_SIZE_Z_SHIFT = 0; +const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT; + +const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1); + +const int BLOCK_SIZE_IN_BYTES = 16; + +const int BLOCK_INFO_ERROR = 0; +const int BLOCK_INFO_VOID_EXTENT_HDR = 1; +const int BLOCK_INFO_VOID_EXTENT_LDR = 2; +const int BLOCK_INFO_NORMAL = 3; + +const int JUST_BITS = 0; +const int QUINT = 1; +const int TRIT = 2; + +// The following constants are expanded variants of the Replicate() +// function calls corresponding to the following arguments: +// value: index into the generated table +// num_bits: the after "REPLICATE" in the table name. i.e. 4 is num_bits in REPLICATE_4. +// to_bit: the integer after "TO_" +const uint REPLICATE_BIT_TO_7_TABLE[2] = uint[](0, 127); +const uint REPLICATE_1_BIT_TO_9_TABLE[2] = uint[](0, 511); + +const uint REPLICATE_1_BIT_TO_8_TABLE[2] = uint[](0, 255); +const uint REPLICATE_2_BIT_TO_8_TABLE[4] = uint[](0, 85, 170, 255); +const uint REPLICATE_3_BIT_TO_8_TABLE[8] = uint[](0, 36, 73, 109, 146, 182, 219, 255); +const uint REPLICATE_4_BIT_TO_8_TABLE[16] = + uint[](0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255); +const uint REPLICATE_5_BIT_TO_8_TABLE[32] = + uint[](0, 8, 16, 24, 33, 41, 49, 57, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140, 148, 156, 165, + 173, 181, 189, 198, 206, 214, 222, 231, 239, 247, 255); +const uint REPLICATE_1_BIT_TO_6_TABLE[2] = uint[](0, 63); +const uint REPLICATE_2_BIT_TO_6_TABLE[4] = uint[](0, 21, 42, 63); +const uint REPLICATE_3_BIT_TO_6_TABLE[8] = uint[](0, 9, 18, 27, 36, 45, 54, 63); +const uint REPLICATE_4_BIT_TO_6_TABLE[16] = + uint[](0, 4, 8, 12, 17, 21, 25, 29, 34, 38, 42, 46, 51, 55, 59, 63); +const uint REPLICATE_5_BIT_TO_6_TABLE[32] = + uint[](0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 33, 35, 37, 39, 41, 43, 45, + 47, 49, 51, 53, 55, 57, 59, 61, 63); + +// Input ASTC texture globals +uint current_index = 0; +int bitsread = 0; +uint total_bitsread = 0; +uint local_buff[16]; + +// Color data globals +uint color_endpoint_data[16]; +int color_bitsread = 0; +uint total_color_bitsread = 0; +int color_index = 0; + +// Four values, two endpoints, four maximum paritions +uint color_values[32]; +int colvals_index = 0; + +// Weight data globals +uint texel_weight_data[16]; +int texel_bitsread = 0; +uint total_texel_bitsread = 0; +int texel_index = 0; + +bool texel_flag = false; + +// Global "vectors" to be pushed into when decoding +EncodingData result_vector[100]; +int result_index = 0; + +EncodingData texel_vector[100]; +int texel_vector_index = 0; + +uint unquantized_texel_weights[2][144]; + +uint SwizzleOffset(uvec2 pos) { + pos = pos & SWIZZLE_MASK; + return swizzle_table[pos.y * 64 + pos.x]; +} + +uint ReadTexel(uint offset) { + // extract the 8-bit value from the 32-bit packed data. + return bitfieldExtract(astc_data[offset / 4], int((offset * 8) & 24), 8); +} + +// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)] +// is the same as [(num_bits - 1):0] and repeats all the way down. +uint Replicate(uint val, uint num_bits, uint to_bit) { + if (num_bits == 0 || to_bit == 0) { + return 0; + } + const uint v = val & uint((1 << num_bits) - 1); + uint res = v; + uint reslen = num_bits; + while (reslen < to_bit) { + uint comp = 0; + if (num_bits > to_bit - reslen) { + uint newshift = to_bit - reslen; + comp = num_bits - newshift; + num_bits = newshift; + } + res = uint(res << num_bits); + res = uint(res | (v >> comp)); + reslen += num_bits; + } + return res; +} + +uvec4 ReplicateByteTo16(uvec4 value) { + return uvec4(REPLICATE_BYTE_TO_16_TABLE[value.x], REPLICATE_BYTE_TO_16_TABLE[value.y], + REPLICATE_BYTE_TO_16_TABLE[value.z], REPLICATE_BYTE_TO_16_TABLE[value.w]); +} + +uint ReplicateBitTo7(uint value) { + return REPLICATE_BIT_TO_7_TABLE[value]; +} + +uint ReplicateBitTo9(uint value) { + return REPLICATE_1_BIT_TO_9_TABLE[value]; +} + +uint FastReplicateTo8(uint value, uint num_bits) { + switch (num_bits) { + case 1: + return REPLICATE_1_BIT_TO_8_TABLE[value]; + case 2: + return REPLICATE_2_BIT_TO_8_TABLE[value]; + case 3: + return REPLICATE_3_BIT_TO_8_TABLE[value]; + case 4: + return REPLICATE_4_BIT_TO_8_TABLE[value]; + case 5: + return REPLICATE_5_BIT_TO_8_TABLE[value]; + case 6: + return REPLICATE_6_BIT_TO_8_TABLE[value]; + case 7: + return REPLICATE_7_BIT_TO_8_TABLE[value]; + case 8: + return REPLICATE_8_BIT_TO_8_TABLE[value]; + } + return Replicate(value, num_bits, 8); +} + +uint FastReplicateTo6(uint value, uint num_bits) { + switch (num_bits) { + case 1: + return REPLICATE_1_BIT_TO_6_TABLE[value]; + case 2: + return REPLICATE_2_BIT_TO_6_TABLE[value]; + case 3: + return REPLICATE_3_BIT_TO_6_TABLE[value]; + case 4: + return REPLICATE_4_BIT_TO_6_TABLE[value]; + case 5: + return REPLICATE_5_BIT_TO_6_TABLE[value]; + } + return Replicate(value, num_bits, 6); +} + +uint Div3Floor(uint v) { + return (v * 0x5556) >> 16; +} + +uint Div3Ceil(uint v) { + return Div3Floor(v + 2); +} + +uint Div5Floor(uint v) { + return (v * 0x3334) >> 16; +} + +uint Div5Ceil(uint v) { + return Div5Floor(v + 4); +} + +uint Hash52(uint p) { + p ^= p >> 15; + p -= p << 17; + p += p << 7; + p += p << 4; + p ^= p >> 5; + p += p << 16; + p ^= p >> 7; + p ^= p >> 3; + p ^= p << 6; + p ^= p >> 17; + return p; +} + +uint SelectPartition(uint seed, uint x, uint y, uint z, uint partition_count, bool small_block) { + if (partition_count == 1) { + return 0; + } + if (small_block) { + x <<= 1; + y <<= 1; + z <<= 1; + } + + seed += (partition_count - 1) * 1024; + + uint rnum = Hash52(uint(seed)); + uint seed1 = uint(rnum & 0xF); + uint seed2 = uint((rnum >> 4) & 0xF); + uint seed3 = uint((rnum >> 8) & 0xF); + uint seed4 = uint((rnum >> 12) & 0xF); + uint seed5 = uint((rnum >> 16) & 0xF); + uint seed6 = uint((rnum >> 20) & 0xF); + uint seed7 = uint((rnum >> 24) & 0xF); + uint seed8 = uint((rnum >> 28) & 0xF); + uint seed9 = uint((rnum >> 18) & 0xF); + uint seed10 = uint((rnum >> 22) & 0xF); + uint seed11 = uint((rnum >> 26) & 0xF); + uint seed12 = uint(((rnum >> 30) | (rnum << 2)) & 0xF); + + seed1 = (seed1 * seed1); + seed2 = (seed2 * seed2); + seed3 = (seed3 * seed3); + seed4 = (seed4 * seed4); + seed5 = (seed5 * seed5); + seed6 = (seed6 * seed6); + seed7 = (seed7 * seed7); + seed8 = (seed8 * seed8); + seed9 = (seed9 * seed9); + seed10 = (seed10 * seed10); + seed11 = (seed11 * seed11); + seed12 = (seed12 * seed12); + + int sh1, sh2, sh3; + if ((seed & 1) > 0) { + sh1 = (seed & 2) > 0 ? 4 : 5; + sh2 = (partition_count == 3) ? 6 : 5; + } else { + sh1 = (partition_count == 3) ? 6 : 5; + sh2 = (seed & 2) > 0 ? 4 : 5; + } + sh3 = (seed & 0x10) > 0 ? sh1 : sh2; + + seed1 = (seed1 >> sh1); + seed2 = (seed2 >> sh2); + seed3 = (seed3 >> sh1); + seed4 = (seed4 >> sh2); + seed5 = (seed5 >> sh1); + seed6 = (seed6 >> sh2); + seed7 = (seed7 >> sh1); + seed8 = (seed8 >> sh2); + seed9 = (seed9 >> sh3); + seed10 = (seed10 >> sh3); + seed11 = (seed11 >> sh3); + seed12 = (seed12 >> sh3); + + uint a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14); + uint b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10); + uint c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 6); + uint d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 2); + + a &= 0x3F; + b &= 0x3F; + c &= 0x3F; + d &= 0x3F; + + if (partition_count < 4) { + d = 0; + } + if (partition_count < 3) { + c = 0; + } + + if (a >= b && a >= c && a >= d) { + return 0; + } else if (b >= c && b >= d) { + return 1; + } else if (c >= d) { + return 2; + } else { + return 3; + } +} + +uint Select2DPartition(uint seed, uint x, uint y, uint partition_count, bool small_block) { + return SelectPartition(seed, x, y, 0, partition_count, small_block); +} + +uint ReadBit() { + if (current_index >= local_buff.length()) { + return 0; + } + uint bit = bitfieldExtract(local_buff[current_index], bitsread, 1); + ++bitsread; + ++total_bitsread; + if (bitsread == 8) { + ++current_index; + bitsread = 0; + } + return bit; +} + +uint StreamBits(uint num_bits) { + uint ret = 0; + for (uint i = 0; i < num_bits; i++) { + ret |= ((ReadBit() & 1) << i); + } + return ret; +} + +uint ReadColorBit() { + uint bit = 0; + if (texel_flag) { + bit = bitfieldExtract(texel_weight_data[texel_index], texel_bitsread, 1); + ++texel_bitsread; + ++total_texel_bitsread; + if (texel_bitsread == 8) { + ++texel_index; + texel_bitsread = 0; + } + } else { + bit = bitfieldExtract(color_endpoint_data[color_index], color_bitsread, 1); + ++color_bitsread; + ++total_color_bitsread; + if (color_bitsread == 8) { + ++color_index; + color_bitsread = 0; + } + } + return bit; +} + +uint StreamColorBits(uint num_bits) { + uint ret = 0; + for (uint i = 0; i < num_bits; i++) { + ret |= ((ReadColorBit() & 1) << i); + } + return ret; +} + +void ResultEmplaceBack(EncodingData val) { + if (texel_flag) { + texel_vector[texel_vector_index] = val; + ++texel_vector_index; + } else { + result_vector[result_index] = val; + ++result_index; + } +} + +// Returns the number of bits required to encode n_vals values. +uint GetBitLength(uint n_vals, uint encoding_index) { + uint total_bits = encoding_values[encoding_index].num_bits * n_vals; + if (encoding_values[encoding_index].encoding == TRIT) { + total_bits += Div5Ceil(n_vals * 8); + } else if (encoding_values[encoding_index].encoding == QUINT) { + total_bits += Div3Ceil(n_vals * 7); + } + return total_bits; +} + +uint GetNumWeightValues(uvec2 size, bool dual_plane) { + uint n_vals = size.x * size.y; + if (dual_plane) { + n_vals *= 2; + } + return n_vals; +} + +uint GetPackedBitSize(uvec2 size, bool dual_plane, uint max_weight) { + uint n_vals = GetNumWeightValues(size, dual_plane); + return GetBitLength(n_vals, max_weight); +} + +uint BitsBracket(uint bits, uint pos) { + return ((bits >> pos) & 1); +} + +uint BitsOp(uint bits, uint start, uint end) { + if (start == end) { + return BitsBracket(bits, start); + } else if (start > end) { + uint t = start; + start = end; + end = t; + } + + uint mask = (1 << (end - start + 1)) - 1; + return ((bits >> start) & mask); +} + +void DecodeQuintBlock(uint num_bits) { + uint m[3]; + uint q[3]; + uint Q; + m[0] = StreamColorBits(num_bits); + Q = StreamColorBits(3); + m[1] = StreamColorBits(num_bits); + Q |= StreamColorBits(2) << 3; + m[2] = StreamColorBits(num_bits); + Q |= StreamColorBits(2) << 5; + if (BitsOp(Q, 1, 2) == 3 && BitsOp(Q, 5, 6) == 0) { + q[0] = 4; + q[1] = 4; + q[2] = (BitsBracket(Q, 0) << 2) | ((BitsBracket(Q, 4) & ~BitsBracket(Q, 0)) << 1) | + (BitsBracket(Q, 3) & ~BitsBracket(Q, 0)); + } else { + uint C = 0; + if (BitsOp(Q, 1, 2) == 3) { + q[2] = 4; + C = (BitsOp(Q, 3, 4) << 3) | ((~BitsOp(Q, 5, 6) & 3) << 1) | BitsBracket(Q, 0); + } else { + q[2] = BitsOp(Q, 5, 6); + C = BitsOp(Q, 0, 4); + } + if (BitsOp(C, 0, 2) == 5) { + q[1] = 4; + q[0] = BitsOp(C, 3, 4); + } else { + q[1] = BitsOp(C, 3, 4); + q[0] = BitsOp(C, 0, 2); + } + } + for (uint i = 0; i < 3; i++) { + EncodingData val; + val.encoding = QUINT; + val.num_bits = num_bits; + val.bit_value = m[i]; + val.quint_trit_value = q[i]; + ResultEmplaceBack(val); + } +} + +void DecodeTritBlock(uint num_bits) { + uint m[5]; + uint t[5]; + uint T; + m[0] = StreamColorBits(num_bits); + T = StreamColorBits(2); + m[1] = StreamColorBits(num_bits); + T |= StreamColorBits(2) << 2; + m[2] = StreamColorBits(num_bits); + T |= StreamColorBits(1) << 4; + m[3] = StreamColorBits(num_bits); + T |= StreamColorBits(2) << 5; + m[4] = StreamColorBits(num_bits); + T |= StreamColorBits(1) << 7; + uint C = 0; + if (BitsOp(T, 2, 4) == 7) { + C = (BitsOp(T, 5, 7) << 2) | BitsOp(T, 0, 1); + t[4] = 2; + t[3] = 2; + } else { + C = BitsOp(T, 0, 4); + if (BitsOp(T, 5, 6) == 3) { + t[4] = 2; + t[3] = BitsBracket(T, 7); + } else { + t[4] = BitsBracket(T, 7); + t[3] = BitsOp(T, 5, 6); + } + } + if (BitsOp(C, 0, 1) == 3) { + t[2] = 2; + t[1] = BitsBracket(C, 4); + t[0] = (BitsBracket(C, 3) << 1) | (BitsBracket(C, 2) & ~BitsBracket(C, 3)); + } else if (BitsOp(C, 2, 3) == 3) { + t[2] = 2; + t[1] = 2; + t[0] = BitsOp(C, 0, 1); + } else { + t[2] = BitsBracket(C, 4); + t[1] = BitsOp(C, 2, 3); + t[0] = (BitsBracket(C, 1) << 1) | (BitsBracket(C, 0) & ~BitsBracket(C, 1)); + } + for (uint i = 0; i < 5; i++) { + EncodingData val; + val.encoding = TRIT; + val.num_bits = num_bits; + val.bit_value = m[i]; + val.quint_trit_value = t[i]; + ResultEmplaceBack(val); + } +} + +void DecodeIntegerSequence(uint max_range, uint num_values) { + EncodingData val = encoding_values[max_range]; + uint vals_decoded = 0; + while (vals_decoded < num_values) { + switch (val.encoding) { + case QUINT: + DecodeQuintBlock(val.num_bits); + vals_decoded += 3; + break; + case TRIT: + DecodeTritBlock(val.num_bits); + vals_decoded += 5; + break; + case JUST_BITS: + val.bit_value = StreamColorBits(val.num_bits); + ResultEmplaceBack(val); + vals_decoded++; + break; + } + } +} + +void DecodeColorValues(uvec4 modes, uint num_partitions, uint color_data_bits) { + uint num_values = 0; + for (uint i = 0; i < num_partitions; i++) { + num_values += ((modes[i] >> 2) + 1) << 1; + } + int range = 256; + while (--range > 0) { + EncodingData val = encoding_values[range]; + uint bit_length = GetBitLength(num_values, range); + if (bit_length <= color_data_bits) { + while (--range > 0) { + EncodingData newval = encoding_values[range]; + if (newval.encoding != val.encoding && newval.num_bits != val.num_bits) { + break; + } + } + ++range; + break; + } + } + DecodeIntegerSequence(range, num_values); + uint out_index = 0; + for (int itr = 0; itr < result_index; ++itr) { + if (out_index >= num_values) { + break; + } + EncodingData val = result_vector[itr]; + uint bitlen = val.num_bits; + uint bitval = val.bit_value; + uint A = 0, B = 0, C = 0, D = 0; + A = ReplicateBitTo9((bitval & 1)); + switch (val.encoding) { + case JUST_BITS: + color_values[out_index++] = FastReplicateTo8(bitval, bitlen); + break; + case TRIT: { + D = val.quint_trit_value; + switch (bitlen) { + case 1: + C = 204; + break; + case 2: { + C = 93; + uint b = (bitval >> 1) & 1; + B = (b << 8) | (b << 4) | (b << 2) | (b << 1); + break; + } + case 3: { + C = 44; + uint cb = (bitval >> 1) & 3; + B = (cb << 7) | (cb << 2) | cb; + break; + } + case 4: { + C = 22; + uint dcb = (bitval >> 1) & 7; + B = (dcb << 6) | dcb; + break; + } + case 5: { + C = 11; + uint edcb = (bitval >> 1) & 0xF; + B = (edcb << 5) | (edcb >> 2); + break; + } + case 6: { + C = 5; + uint fedcb = (bitval >> 1) & 0x1F; + B = (fedcb << 4) | (fedcb >> 4); + break; + } + } + break; + } + case QUINT: { + D = val.quint_trit_value; + switch (bitlen) { + case 1: + C = 113; + break; + case 2: { + C = 54; + uint b = (bitval >> 1) & 1; + B = (b << 8) | (b << 3) | (b << 2); + break; + } + case 3: { + C = 26; + uint cb = (bitval >> 1) & 3; + B = (cb << 7) | (cb << 1) | (cb >> 1); + break; + } + case 4: { + C = 13; + uint dcb = (bitval >> 1) & 7; + B = (dcb << 6) | (dcb >> 1); + break; + } + case 5: { + C = 6; + uint edcb = (bitval >> 1) & 0xF; + B = (edcb << 5) | (edcb >> 3); + break; + } + } + break; + } + } + if (val.encoding != JUST_BITS) { + uint T = (D * C) + B; + T ^= A; + T = (A & 0x80) | (T >> 2); + color_values[out_index++] = T; + } + } +} + +ivec2 BitTransferSigned(int a, int b) { + ivec2 transferred; + transferred.y = b >> 1; + transferred.y |= a & 0x80; + transferred.x = a >> 1; + transferred.x &= 0x3F; + if ((transferred.x & 0x20) > 0) { + transferred.x -= 0x40; + } + return transferred; +} + +uvec4 ClampByte(ivec4 color) { + for (uint i = 0; i < 4; ++i) { + color[i] = (color[i] < 0) ? 0 : ((color[i] > 255) ? 255 : color[i]); + } + return uvec4(color); +} + +ivec4 BlueContract(int a, int r, int g, int b) { + return ivec4(a, (r + b) >> 1, (g + b) >> 1, b); +} + +void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode) { +#define READ_UINT_VALUES(N) \ + uint v[N]; \ + for (uint i = 0; i < N; i++) { \ + v[i] = color_values[colvals_index++]; \ + } + +#define READ_INT_VALUES(N) \ + int v[N]; \ + for (uint i = 0; i < N; i++) { \ + v[i] = int(color_values[colvals_index++]); \ + } + + switch (color_endpoint_mode) { + case 0: { + READ_UINT_VALUES(2) + ep1 = uvec4(0xFF, v[0], v[0], v[0]); + ep2 = uvec4(0xFF, v[1], v[1], v[1]); + break; + } + case 1: { + READ_UINT_VALUES(2) + uint L0 = (v[0] >> 2) | (v[1] & 0xC0); + uint L1 = max(L0 + (v[1] & 0x3F), 0xFFU); + ep1 = uvec4(0xFF, L0, L0, L0); + ep2 = uvec4(0xFF, L1, L1, L1); + break; + } + case 4: { + READ_UINT_VALUES(4) + ep1 = uvec4(v[2], v[0], v[0], v[0]); + ep2 = uvec4(v[3], v[1], v[1], v[1]); + break; + } + case 5: { + READ_INT_VALUES(4) + ivec2 transferred = BitTransferSigned(v[1], v[0]); + v[1] = transferred.x; + v[0] = transferred.y; + transferred = BitTransferSigned(v[3], v[2]); + v[3] = transferred.x; + v[2] = transferred.y; + ep1 = ClampByte(ivec4(v[2], v[0], v[0], v[0])); + ep2 = ClampByte(ivec4(v[2] + v[3], v[0] + v[1], v[0] + v[1], v[0] + v[1])); + break; + } + case 6: { + READ_UINT_VALUES(4) + ep1 = uvec4(0xFF, (v[0] * v[3]) >> 8, (v[1] * v[3]) >> 8, (v[2] * v[3]) >> 8); + ep2 = uvec4(0xFF, v[0], v[1], v[2]); + break; + } + case 8: { + READ_UINT_VALUES(6) + if ((v[1] + v[3] + v[5]) >= (v[0] + v[2] + v[4])) { + ep1 = uvec4(0xFF, v[0], v[2], v[4]); + ep2 = uvec4(0xFF, v[1], v[3], v[5]); + } else { + ep1 = uvec4(BlueContract(0xFF, int(v[1]), int(v[3]), int(v[5]))); + ep2 = uvec4(BlueContract(0xFF, int(v[0]), int(v[2]), int(v[4]))); + } + break; + } + case 9: { + READ_INT_VALUES(6) + ivec2 transferred = BitTransferSigned(v[1], v[0]); + v[1] = transferred.x; + v[0] = transferred.y; + transferred = BitTransferSigned(v[3], v[2]); + v[3] = transferred.x; + v[2] = transferred.y; + transferred = BitTransferSigned(v[5], v[4]); + v[5] = transferred.x; + v[4] = transferred.y; + if ((v[1] + v[3] + v[5]) >= 0) { + ep1 = ClampByte(ivec4(0xFF, v[0], v[2], v[4])); + ep2 = ClampByte(ivec4(0xFF, v[0] + v[1], v[2] + v[3], v[4] + v[5])); + } else { + ep1 = ClampByte(BlueContract(0xFF, v[0] + v[1], v[2] + v[3], v[4] + v[5])); + ep2 = ClampByte(BlueContract(0xFF, v[0], v[2], v[4])); + } + break; + } + case 10: { + READ_UINT_VALUES(6) + ep1 = uvec4(v[4], (v[0] * v[3]) >> 8, (v[1] * v[3]) >> 8, (v[2] * v[3]) >> 8); + ep2 = uvec4(v[5], v[0], v[1], v[2]); + break; + } + case 12: { + READ_UINT_VALUES(8) + if ((v[1] + v[3] + v[5]) >= (v[0] + v[2] + v[4])) { + ep1 = uvec4(v[6], v[0], v[2], v[4]); + ep2 = uvec4(v[7], v[1], v[3], v[5]); + } else { + ep1 = uvec4(BlueContract(int(v[7]), int(v[1]), int(v[3]), int(v[5]))); + ep2 = uvec4(BlueContract(int(v[6]), int(v[0]), int(v[2]), int(v[4]))); + } + break; + } + case 13: { + READ_INT_VALUES(8) + ivec2 transferred = BitTransferSigned(v[1], v[0]); + v[1] = transferred.x; + v[0] = transferred.y; + transferred = BitTransferSigned(v[3], v[2]); + v[3] = transferred.x; + v[2] = transferred.y; + + transferred = BitTransferSigned(v[5], v[4]); + v[5] = transferred.x; + v[4] = transferred.y; + + transferred = BitTransferSigned(v[7], v[6]); + v[7] = transferred.x; + v[6] = transferred.y; + + if ((v[1] + v[3] + v[5]) >= 0) { + ep1 = ClampByte(ivec4(v[6], v[0], v[2], v[4])); + ep2 = ClampByte(ivec4(v[7] + v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5])); + } else { + ep1 = ClampByte(BlueContract(v[6] + v[7], v[0] + v[1], v[2] + v[3], v[4] + v[5])); + ep2 = ClampByte(BlueContract(v[6], v[0], v[2], v[4])); + } + break; + } + default: { + // HDR mode, or more likely a bug computing the color_endpoint_mode + ep1 = uvec4(0xFF, 0xFF, 0, 0); + ep2 = uvec4(0xFF, 0xFF, 0, 0); + break; + } + } +#undef READ_UINT_VALUES +#undef READ_INT_VALUES +} + +uint UnquantizeTexelWeight(EncodingData val) { + uint bitval = val.bit_value; + uint bitlen = val.num_bits; + uint A = ReplicateBitTo7((bitval & 1)); + uint B = 0, C = 0, D = 0; + uint result = 0; + switch (val.encoding) { + case JUST_BITS: + result = FastReplicateTo6(bitval, bitlen); + break; + case TRIT: { + D = val.quint_trit_value; + switch (bitlen) { + case 0: { + uint results[3] = {0, 32, 63}; + result = results[D]; + break; + } + case 1: { + C = 50; + break; + } + case 2: { + C = 23; + uint b = (bitval >> 1) & 1; + B = (b << 6) | (b << 2) | b; + break; + } + case 3: { + C = 11; + uint cb = (bitval >> 1) & 3; + B = (cb << 5) | cb; + break; + } + default: + break; + } + break; + } + case QUINT: { + D = val.quint_trit_value; + switch (bitlen) { + case 0: { + uint results[5] = {0, 16, 32, 47, 63}; + result = results[D]; + break; + } + case 1: { + C = 28; + break; + } + case 2: { + C = 13; + uint b = (bitval >> 1) & 1; + B = (b << 6) | (b << 1); + break; + } + } + break; + } + } + if (val.encoding != JUST_BITS && bitlen > 0) { + result = D * C + B; + result ^= A; + result = (A & 0x20) | (result >> 2); + } + if (result > 32) { + result += 1; + } + return result; +} + +void UnquantizeTexelWeights(bool dual_plane, uvec2 size) { + uint weight_idx = 0; + uint unquantized[2][144]; + uint area = size.x * size.y; + for (uint itr = 0; itr < texel_vector_index; itr++) { + unquantized[0][weight_idx] = UnquantizeTexelWeight(texel_vector[itr]); + if (dual_plane) { + ++itr; + unquantized[1][weight_idx] = UnquantizeTexelWeight(texel_vector[itr]); + if (itr == texel_vector_index) { + break; + } + } + if (++weight_idx >= (area)) + break; + } + + const uint Ds = uint((block_dims.x * 0.5f + 1024) / (block_dims.x - 1)); + const uint Dt = uint((block_dims.y * 0.5f + 1024) / (block_dims.y - 1)); + const uint k_plane_scale = dual_plane ? 2 : 1; + for (uint plane = 0; plane < k_plane_scale; plane++) { + for (uint t = 0; t < block_dims.y; t++) { + for (uint s = 0; s < block_dims.x; s++) { + uint cs = Ds * s; + uint ct = Dt * t; + uint gs = (cs * (size.x - 1) + 32) >> 6; + uint gt = (ct * (size.y - 1) + 32) >> 6; + uint js = gs >> 4; + uint fs = gs & 0xF; + uint jt = gt >> 4; + uint ft = gt & 0x0F; + uint w11 = (fs * ft + 8) >> 4; + uint w10 = ft - w11; + uint w01 = fs - w11; + uint w00 = 16 - fs - ft + w11; + uvec4 w = uvec4(w00, w01, w10, w11); + uint v0 = jt * size.x + js; + + uvec4 p = uvec4(0); + if (v0 < area) { + p.x = unquantized[plane][v0]; + } + if ((v0 + 1) < (area)) { + p.y = unquantized[plane][v0 + 1]; + } + if ((v0 + size.x) < (area)) { + p.z = unquantized[plane][(v0 + size.x)]; + } + if ((v0 + size.x + 1) < (area)) { + p.w = unquantized[plane][(v0 + size.x + 1)]; + } + unquantized_texel_weights[plane][t * block_dims.x + s] = (uint(dot(p, w)) + 8) >> 4; + } + } + } +} + +int FindLayout(uint mode) { + if ((mode & 3) != 0) { + if ((mode & 8) != 0) { + if ((mode & 4) != 0) { + if ((mode & 0x100) != 0) { + return 4; + } + return 3; + } + return 2; + } + if ((mode & 4) != 0) { + return 1; + } + return 0; + } + if ((mode & 0x100) != 0) { + if ((mode & 0x80) != 0) { + if ((mode & 0x20) != 0) { + return 8; + } + return 7; + } + return 9; + } + if ((mode & 0x80) != 0) { + return 6; + } + return 5; +} + +TexelWeightParams DecodeBlockInfo(uint block_index) { + TexelWeightParams params = TexelWeightParams(uvec2(0), 0, false, false, false, false); + uint mode = StreamBits(11); + if ((mode & 0x1ff) == 0x1fc) { + if ((mode & 0x200) != 0) { + params.void_extent_hdr = true; + } else { + params.void_extent_ldr = true; + } + if ((mode & 0x400) == 0 || StreamBits(1) == 0) { + params.error_state = true; + } + return params; + } + if ((mode & 0xf) == 0) { + params.error_state = true; + return params; + } + if ((mode & 3) == 0 && (mode & 0x1c0) == 0x1c0) { + params.error_state = true; + return params; + } + uint A, B; + uint mode_layout = FindLayout(mode); + switch (mode_layout) { + case 0: + A = (mode >> 5) & 0x3; + B = (mode >> 7) & 0x3; + params.size = uvec2(B + 4, A + 2); + break; + case 1: + A = (mode >> 5) & 0x3; + B = (mode >> 7) & 0x3; + params.size = uvec2(B + 8, A + 2); + break; + case 2: + A = (mode >> 5) & 0x3; + B = (mode >> 7) & 0x3; + params.size = uvec2(A + 2, B + 8); + break; + case 3: + A = (mode >> 5) & 0x3; + B = (mode >> 7) & 0x1; + params.size = uvec2(A + 2, B + 6); + break; + case 4: + A = (mode >> 5) & 0x3; + B = (mode >> 7) & 0x1; + params.size = uvec2(B + 2, A + 2); + break; + case 5: + A = (mode >> 5) & 0x3; + params.size = uvec2(12, A + 2); + break; + case 6: + A = (mode >> 5) & 0x3; + params.size = uvec2(A + 2, 12); + break; + case 7: + params.size = uvec2(6, 10); + break; + case 8: + params.size = uvec2(10, 6); + break; + case 9: + A = (mode >> 5) & 0x3; + B = (mode >> 9) & 0x3; + params.size = uvec2(A + 6, B + 6); + break; + default: + params.error_state = true; + break; + } + params.dual_plane = (mode_layout != 9) && ((mode & 0x400) != 0); + uint weight_index = (mode & 0x10) != 0 ? 1 : 0; + if (mode_layout < 5) { + weight_index |= (mode & 0x3) << 1; + } else { + weight_index |= (mode & 0xc) >> 1; + } + weight_index -= 2; + if ((mode_layout != 9) && ((mode & 0x200) != 0)) { + const int max_weights[6] = int[6](9, 11, 15, 19, 23, 31); + params.max_weight = max_weights[weight_index]; + } else { + const int max_weights[6] = int[6](1, 2, 3, 4, 5, 7); + params.max_weight = max_weights[weight_index]; + } + return params; +} + +void FillError(ivec3 coord) { + for (uint j = 0; j < block_dims.y; j++) { + for (uint i = 0; i < block_dims.x; i++) { + imageStore(dest_image, coord + ivec3(i, j, 0), vec4(1.0, 1.0, 0.0, 1.0)); + } + } +} + +void FillVoidExtentLDR(ivec3 coord) { + StreamBits(52); + uint r_u = StreamBits(16); + uint g_u = StreamBits(16); + uint b_u = StreamBits(16); + uint a_u = StreamBits(16); + float a = float(a_u) / 65535.0f; + float r = float(r_u) / 65535.0f; + float g = float(g_u) / 65535.0f; + float b = float(b_u) / 65535.0f; + for (uint j = 0; j < block_dims.y; j++) { + for (uint i = 0; i < block_dims.x; i++) { + imageStore(dest_image, coord + ivec3(i, j, 0), vec4(r, g, b, a)); + } + } +} + +void DecompressBlock(ivec3 coord, uint block_index) { + TexelWeightParams params = DecodeBlockInfo(block_index); + if (params.error_state) { + FillError(coord); + return; + } + if (params.void_extent_hdr) { + FillError(coord); + return; + } + if (params.void_extent_ldr) { + FillVoidExtentLDR(coord); + return; + } + if ((params.size.x > block_dims.x) || (params.size.y > block_dims.y)) { + FillError(coord); + return; + } + uint num_partitions = StreamBits(2) + 1; + if (num_partitions > 4 || (num_partitions == 4 && params.dual_plane)) { + FillError(coord); + return; + } + int plane_index = -1; + uint partition_index = 1; + uvec4 color_endpoint_mode = uvec4(0); + uint ced_pointer = 0; + uint base_cem = 0; + if (num_partitions == 1) { + color_endpoint_mode.x = StreamBits(4); + partition_index = 0; + } else { + partition_index = StreamBits(10); + base_cem = StreamBits(6); + } + uint base_mode = base_cem & 3; + uint weight_bits = GetPackedBitSize(params.size, params.dual_plane, params.max_weight); + uint remaining_bits = 128 - weight_bits - total_bitsread; + uint extra_cem_bits = 0; + if (base_mode > 0) { + switch (num_partitions) { + case 2: + extra_cem_bits += 2; + break; + case 3: + extra_cem_bits += 5; + break; + case 4: + extra_cem_bits += 8; + break; + default: + return; + } + } + remaining_bits -= extra_cem_bits; + uint plane_selector_bits = 0; + if (params.dual_plane) { + plane_selector_bits = 2; + } + remaining_bits -= plane_selector_bits; + if (remaining_bits > 128) { + // Bad data, more remaining bits than 4 bytes + // return early + return; + } + // Read color data... + uint color_data_bits = remaining_bits; + while (remaining_bits > 0) { + int nb = int(min(remaining_bits, 8U)); + uint b = StreamBits(nb); + color_endpoint_data[ced_pointer] = uint(bitfieldExtract(b, 0, nb)); + ++ced_pointer; + remaining_bits -= nb; + } + plane_index = int(StreamBits(plane_selector_bits)); + if (base_mode > 0) { + uint extra_cem = StreamBits(extra_cem_bits); + uint cem = (extra_cem << 6) | base_cem; + cem >>= 2; + uvec4 C = uvec4(0); + for (uint i = 0; i < num_partitions; i++) { + C[i] = (cem & 1); + cem >>= 1; + } + uvec4 M = uvec4(0); + for (uint i = 0; i < num_partitions; i++) { + M[i] = cem & 3; + cem >>= 2; + } + for (uint i = 0; i < num_partitions; i++) { + color_endpoint_mode[i] = base_mode; + if (C[i] == 0) { + --color_endpoint_mode[i]; + } + color_endpoint_mode[i] <<= 2; + color_endpoint_mode[i] |= M[i]; + } + } else if (num_partitions > 1) { + uint cem = base_cem >> 2; + for (uint i = 0; i < num_partitions; i++) { + color_endpoint_mode[i] = cem; + } + } + DecodeColorValues(color_endpoint_mode, num_partitions, color_data_bits); + + uvec4 endpoints[4][2]; + for (uint i = 0; i < num_partitions; i++) { + ComputeEndpoints(endpoints[i][0], endpoints[i][1], color_endpoint_mode[i]); + } + + for (uint i = 0; i < 16; i++) { + texel_weight_data[i] = local_buff[i]; + } + for (uint i = 0; i < 8; i++) { +#define REVERSE_BYTE(b) ((b * 0x0802U & 0x22110U) | (b * 0x8020U & 0x88440U)) * 0x10101U >> 16 + uint a = REVERSE_BYTE(texel_weight_data[i]); + uint b = REVERSE_BYTE(texel_weight_data[15 - i]); +#undef REVERSE_BYTE + texel_weight_data[i] = uint(bitfieldExtract(b, 0, 8)); + texel_weight_data[15 - i] = uint(bitfieldExtract(a, 0, 8)); + } + uint clear_byte_start = + (GetPackedBitSize(params.size, params.dual_plane, params.max_weight) >> 3) + 1; + texel_weight_data[clear_byte_start - 1] = + texel_weight_data[clear_byte_start - 1] & + uint( + ((1 << (GetPackedBitSize(params.size, params.dual_plane, params.max_weight) % 8)) - 1)); + for (uint i = 0; i < 16 - clear_byte_start; i++) { + texel_weight_data[clear_byte_start + i] = 0U; + } + texel_flag = true; // use texel "vector" and bit stream in integer decoding + DecodeIntegerSequence(params.max_weight, GetNumWeightValues(params.size, params.dual_plane)); + + UnquantizeTexelWeights(params.dual_plane, params.size); + + for (uint j = 0; j < block_dims.y; j++) { + for (uint i = 0; i < block_dims.x; i++) { + uint local_partition = Select2DPartition(partition_index, i, j, num_partitions, + (block_dims.y * block_dims.x) < 32); + vec4 p; + uvec4 C0 = ReplicateByteTo16(endpoints[local_partition][0]); + uvec4 C1 = ReplicateByteTo16(endpoints[local_partition][1]); + uvec4 plane_vec = uvec4(0); + uvec4 weight_vec = uvec4(0); + for (uint c = 0; c < 4; c++) { + if (params.dual_plane && (((plane_index + 1) & 3) == c)) { + plane_vec[c] = 1; + } + weight_vec[c] = unquantized_texel_weights[plane_vec[c]][j * block_dims.x + i]; + } + vec4 Cf = vec4((C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + uvec4(32)) / 64); + p = (Cf / 65535.0); + imageStore(dest_image, coord + ivec3(i, j, 0), p.gbar); + } + } +} + +void main() { + uvec3 pos = gl_GlobalInvocationID; + pos.x <<= bytes_per_block_log2; + + // Read as soon as possible due to its latency + const uint swizzle = SwizzleOffset(pos.xy); + + const uint block_y = pos.y >> GOB_SIZE_Y_SHIFT; + + uint offset = 0; + offset += pos.z * layer_stride; + offset += (block_y >> block_height) * block_size; + offset += (block_y & block_height_mask) << GOB_SIZE_SHIFT; + offset += (pos.x >> GOB_SIZE_X_SHIFT) << x_shift; + offset += swizzle; + + const ivec3 coord = ivec3(gl_GlobalInvocationID * uvec3(block_dims, 1)); + uint block_index = + pos.z * gl_WorkGroupSize.x * gl_WorkGroupSize.y + pos.y * gl_WorkGroupSize.x + pos.x; + + current_index = 0; + bitsread = 0; + for (int i = 0; i < 16; i++) { + local_buff[i] = ReadTexel(offset + i); + } + DecompressBlock(coord, block_index); +} diff --git a/src/video_core/host_shaders/source_shader.h.in b/src/video_core/host_shaders/source_shader.h.in index ccdb0d2a9..929dec39b 100644 --- a/src/video_core/host_shaders/source_shader.h.in +++ b/src/video_core/host_shaders/source_shader.h.in @@ -4,6 +4,8 @@ namespace HostShaders { -constexpr std::string_view @CONTENTS_NAME@ = R"(@CONTENTS@)"; +constexpr std::string_view @CONTENTS_NAME@ = { +@CONTENTS@ +}; } // namespace HostShaders diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index cd21a2112..d7fabe605 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp @@ -6,7 +6,7 @@ #include <boost/container_hash/hash.hpp> #include "common/assert.h" #include "common/logging/log.h" -#include "core/settings.h" +#include "common/settings.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/macro/macro.h" #include "video_core/macro/macro_hle.h" diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index 203f2af05..aac851253 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h @@ -16,8 +16,8 @@ #include <vector> #include "common/assert.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/gpu.h" #include "video_core/memory_manager.h" @@ -208,9 +208,9 @@ public: private: /// Flushes a memory range to guest memory and removes it from the cache. void FlushAndRemoveRegion(VAddr addr, std::size_t size) { - const u64 addr_begin = static_cast<u64>(addr); - const u64 addr_end = addr_begin + static_cast<u64>(size); - const auto in_range = [addr_begin, addr_end](CachedQuery& query) { + const u64 addr_begin = addr; + const u64 addr_end = addr_begin + size; + const auto in_range = [addr_begin, addr_end](const CachedQuery& query) { const u64 cache_begin = query.GetCpuAddr(); const u64 cache_end = cache_begin + query.SizeInBytes(); return cache_begin < addr_end && addr_begin < cache_end; @@ -230,8 +230,7 @@ private: rasterizer.UpdatePagesCachedCount(query.GetCpuAddr(), query.SizeInBytes(), -1); query.Flush(); } - contents.erase(std::remove_if(std::begin(contents), std::end(contents), in_range), - std::end(contents)); + std::erase_if(contents, in_range); } } diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index a93a1732c..c9a360aaf 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -3,8 +3,8 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "common/settings.h" #include "core/frontend/emu_window.h" -#include "core/settings.h" #include "video_core/renderer_base.h" namespace VideoCore { diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 5776fccdc..b113f54db 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -16,7 +16,7 @@ #include "common/logging/log.h" #include "common/scope_exit.h" -#include "core/settings.h" +#include "common/settings.h" #include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_resource_manager.h" diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 4610fd160..0863904e9 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -17,10 +17,10 @@ #include "common/math_util.h" #include "common/microprofile.h" #include "common/scope_exit.h" +#include "common/settings.h" #include "core/core.h" #include "core/hle/kernel/process.h" #include "core/memory.h" -#include "core/settings.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/engines/shader_type.h" diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index 955b2abc4..97fb11ac6 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -12,10 +12,10 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/scm_rev.h" +#include "common/settings.h" #include "common/zstd_compression.h" #include "core/core.h" #include "core/hle/kernel/process.h" -#include "core/settings.h" #include "video_core/engines/shader_type.h" #include "video_core/renderer_opengl/gl_shader_cache.h" #include "video_core/renderer_opengl/gl_shader_disk_cache.h" diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index e028677e9..623b43d8a 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -307,7 +307,8 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4 [[nodiscard]] bool CanBeAccelerated(const TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info) { - // Disable accelerated uploads for now as they don't implement swizzled uploads + return !runtime.HasNativeASTC() && IsPixelFormatASTC(info.format); + // Disable other accelerated uploads for now as they don't implement swizzled uploads return false; switch (info.type) { case ImageType::e2D: @@ -569,7 +570,11 @@ void TextureCacheRuntime::AccelerateImageUpload(Image& image, const ImageBufferM std::span<const SwizzleParameters> swizzles) { switch (image.info.type) { case ImageType::e2D: - return util_shaders.BlockLinearUpload2D(image, map, swizzles); + if (IsPixelFormatASTC(image.info.format)) { + return util_shaders.ASTCDecode(image, map, swizzles); + } else { + return util_shaders.BlockLinearUpload2D(image, map, swizzles); + } case ImageType::e3D: return util_shaders.BlockLinearUpload3D(image, map, swizzles); case ImageType::Linear: @@ -599,6 +604,10 @@ FormatProperties TextureCacheRuntime::FormatInfo(ImageType type, GLenum internal } } +bool TextureCacheRuntime::HasNativeASTC() const noexcept { + return device.HasASTC(); +} + TextureCacheRuntime::StagingBuffers::StagingBuffers(GLenum storage_flags_, GLenum map_flags_) : storage_flags{storage_flags_}, map_flags{map_flags_} {} diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 3fbaa102f..3c871541b 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -95,6 +95,8 @@ public: return has_broken_texture_view_formats; } + bool HasNativeASTC() const noexcept; + private: struct StagingBuffers { explicit StagingBuffers(GLenum storage_flags_, GLenum map_flags_); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 9d2acd4d9..cc2e499f9 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -13,13 +13,13 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/microprofile.h" +#include "common/settings.h" #include "common/telemetry.h" #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" #include "core/memory.h" #include "core/perf_stats.h" -#include "core/settings.h" #include "core/telemetry_session.h" #include "video_core/host_shaders/opengl_present_frag.h" #include "video_core/host_shaders/opengl_present_vert.h" diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp index 2fe4799bc..47fddcb6e 100644 --- a/src/video_core/renderer_opengl/util_shaders.cpp +++ b/src/video_core/renderer_opengl/util_shaders.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <bit> #include <span> #include <string_view> @@ -11,6 +10,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/div_ceil.h" +#include "video_core/host_shaders/astc_decoder_comp.h" #include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h" #include "video_core/host_shaders/opengl_copy_bc4_comp.h" @@ -20,16 +20,18 @@ #include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_texture_cache.h" #include "video_core/renderer_opengl/util_shaders.h" -#include "video_core/surface.h" #include "video_core/texture_cache/accelerated_swizzle.h" #include "video_core/texture_cache/types.h" #include "video_core/texture_cache/util.h" +#include "video_core/textures/astc.h" #include "video_core/textures/decoders.h" namespace OpenGL { using namespace HostShaders; +using namespace Tegra::Texture::ASTC; +using VideoCommon::Extent2D; using VideoCommon::Extent3D; using VideoCommon::ImageCopy; using VideoCommon::ImageType; @@ -57,7 +59,7 @@ size_t NumPixelsInCopy(const VideoCommon::ImageCopy& copy) { } // Anonymous namespace UtilShaders::UtilShaders(ProgramManager& program_manager_) - : program_manager{program_manager_}, + : program_manager{program_manager_}, astc_decoder_program(MakeProgram(ASTC_DECODER_COMP)), block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)), block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)), pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)), @@ -65,11 +67,79 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_) copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) { const auto swizzle_table = Tegra::Texture::MakeSwizzleTable(); swizzle_table_buffer.Create(); + astc_buffer.Create(); glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0); + glNamedBufferStorage(astc_buffer.handle, sizeof(ASTC_BUFFER_DATA), &ASTC_BUFFER_DATA, 0); } UtilShaders::~UtilShaders() = default; +void UtilShaders::ASTCDecode(Image& image, const ImageBufferMap& map, + std::span<const VideoCommon::SwizzleParameters> swizzles) { + static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0; + static constexpr GLuint BINDING_INPUT_BUFFER = 1; + static constexpr GLuint BINDING_ENC_BUFFER = 2; + + static constexpr GLuint BINDING_6_TO_8_BUFFER = 3; + static constexpr GLuint BINDING_7_TO_8_BUFFER = 4; + static constexpr GLuint BINDING_8_TO_8_BUFFER = 5; + static constexpr GLuint BINDING_BYTE_TO_16_BUFFER = 6; + + static constexpr GLuint BINDING_OUTPUT_IMAGE = 0; + + const Extent2D tile_size{ + .width = VideoCore::Surface::DefaultBlockWidth(image.info.format), + .height = VideoCore::Surface::DefaultBlockHeight(image.info.format), + }; + program_manager.BindHostCompute(astc_decoder_program.handle); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_ENC_BUFFER, astc_buffer.handle, + offsetof(AstcBufferData, encoding_values), + sizeof(AstcBufferData::encoding_values)); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_6_TO_8_BUFFER, astc_buffer.handle, + offsetof(AstcBufferData, replicate_6_to_8), + sizeof(AstcBufferData::replicate_6_to_8)); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_7_TO_8_BUFFER, astc_buffer.handle, + offsetof(AstcBufferData, replicate_7_to_8), + sizeof(AstcBufferData::replicate_7_to_8)); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_8_TO_8_BUFFER, astc_buffer.handle, + offsetof(AstcBufferData, replicate_8_to_8), + sizeof(AstcBufferData::replicate_8_to_8)); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_BYTE_TO_16_BUFFER, astc_buffer.handle, + offsetof(AstcBufferData, replicate_byte_to_16), + sizeof(AstcBufferData::replicate_byte_to_16)); + + glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes); + glUniform2ui(1, tile_size.width, tile_size.height); + // Ensure buffer data is valid before dispatching + glFlush(); + for (const SwizzleParameters& swizzle : swizzles) { + const size_t input_offset = swizzle.buffer_offset + map.offset; + const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U); + const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U); + + const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info); + ASSERT(params.origin == (std::array<u32, 3>{0, 0, 0})); + ASSERT(params.destination == (std::array<s32, 3>{0, 0, 0})); + + glUniform1ui(2, params.bytes_per_block_log2); + glUniform1ui(3, params.layer_stride); + glUniform1ui(4, params.block_size); + glUniform1ui(5, params.x_shift); + glUniform1ui(6, params.block_height); + glUniform1ui(7, params.block_height_mask); + + glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0, + GL_WRITE_ONLY, GL_RGBA8); + // ASTC texture data + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset, + image.guest_size_bytes - swizzle.buffer_offset); + + glDispatchCompute(num_dispatches_x, num_dispatches_y, image.info.resources.layers); + } + program_manager.RestoreGuestCompute(); +} + void UtilShaders::BlockLinearUpload2D(Image& image, const ImageBufferMap& map, std::span<const SwizzleParameters> swizzles) { static constexpr Extent3D WORKGROUP_SIZE{32, 32, 1}; diff --git a/src/video_core/renderer_opengl/util_shaders.h b/src/video_core/renderer_opengl/util_shaders.h index 93b009743..53d65f368 100644 --- a/src/video_core/renderer_opengl/util_shaders.h +++ b/src/video_core/renderer_opengl/util_shaders.h @@ -40,6 +40,9 @@ public: explicit UtilShaders(ProgramManager& program_manager); ~UtilShaders(); + void ASTCDecode(Image& image, const ImageBufferMap& map, + std::span<const VideoCommon::SwizzleParameters> swizzles); + void BlockLinearUpload2D(Image& image, const ImageBufferMap& map, std::span<const VideoCommon::SwizzleParameters> swizzles); @@ -59,7 +62,9 @@ private: ProgramManager& program_manager; OGLBuffer swizzle_table_buffer; + OGLBuffer astc_buffer; + OGLProgram astc_decoder_program; OGLProgram block_linear_unswizzle_2d_program; OGLProgram block_linear_unswizzle_3d_program; OGLProgram pitch_unswizzle_program; diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 19aaf034f..f088447e9 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -166,7 +166,7 @@ struct FormatTuple { {VK_FORMAT_R16G16_SINT, Attachable | Storage}, // R16G16_SINT {VK_FORMAT_R16G16_SNORM, Attachable | Storage}, // R16G16_SNORM {VK_FORMAT_UNDEFINED}, // R32G32B32_FLOAT - {VK_FORMAT_R8G8B8A8_SRGB, Attachable}, // A8B8G8R8_SRGB + {VK_FORMAT_A8B8G8R8_SRGB_PACK32, Attachable}, // A8B8G8R8_SRGB {VK_FORMAT_R8G8_UNORM, Attachable | Storage}, // R8G8_UNORM {VK_FORMAT_R8G8_SNORM, Attachable | Storage}, // R8G8_SNORM {VK_FORMAT_R8G8_SINT, Attachable | Storage}, // R8G8_SINT diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 1cc720ddd..2e0cf4232 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -13,11 +13,11 @@ #include <fmt/format.h> #include "common/logging/log.h" +#include "common/settings.h" #include "common/telemetry.h" #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" -#include "core/settings.h" #include "core/telemetry_session.h" #include "video_core/gpu.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" @@ -143,7 +143,10 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { scheduler.WaitWorker(); - swapchain.AcquireNextImage(); + while (!swapchain.AcquireNextImage()) { + swapchain.Create(layout.width, layout.height, is_srgb); + blit_screen.Recreate(); + } const VkSemaphore render_semaphore = blit_screen.Draw(*framebuffer, use_accelerated); scheduler.Flush(render_semaphore); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 668633e7b..8cb65e588 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -176,7 +176,7 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset u32 stride) { if (device.IsExtExtendedDynamicStateSupported()) { scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { - const VkDeviceSize vk_offset = offset; + const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE; const VkDeviceSize vk_stride = stride; cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride); diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 2f9a7b028..e11406e58 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -11,18 +11,39 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/div_ceil.h" +#include "video_core/host_shaders/astc_decoder_comp_spv.h" #include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h" #include "video_core/host_shaders/vulkan_uint8_comp_spv.h" #include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" +#include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h" +#include "video_core/texture_cache/accelerated_swizzle.h" +#include "video_core/texture_cache/types.h" +#include "video_core/textures/astc.h" +#include "video_core/textures/decoders.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { + +using Tegra::Texture::SWIZZLE_TABLE; +using Tegra::Texture::ASTC::EncodingsValues; +using namespace Tegra::Texture::ASTC; + namespace { + +constexpr u32 ASTC_BINDING_INPUT_BUFFER = 0; +constexpr u32 ASTC_BINDING_ENC_BUFFER = 1; +constexpr u32 ASTC_BINDING_6_TO_8_BUFFER = 2; +constexpr u32 ASTC_BINDING_7_TO_8_BUFFER = 3; +constexpr u32 ASTC_BINDING_8_TO_8_BUFFER = 4; +constexpr u32 ASTC_BINDING_BYTE_TO_16_BUFFER = 5; +constexpr u32 ASTC_BINDING_SWIZZLE_BUFFER = 6; +constexpr u32 ASTC_BINDING_OUTPUT_IMAGE = 7; + VkPushConstantRange BuildComputePushConstantRange(std::size_t size) { return { .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, @@ -50,6 +71,67 @@ std::array<VkDescriptorSetLayoutBinding, 2> BuildInputOutputDescriptorSetBinding }}; } +std::array<VkDescriptorSetLayoutBinding, 8> BuildASTCDescriptorSetBindings() { + return {{ + { + .binding = ASTC_BINDING_INPUT_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = ASTC_BINDING_ENC_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = ASTC_BINDING_6_TO_8_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = ASTC_BINDING_7_TO_8_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = ASTC_BINDING_8_TO_8_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = ASTC_BINDING_BYTE_TO_16_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = ASTC_BINDING_SWIZZLE_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = ASTC_BINDING_OUTPUT_IMAGE, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + }}; +} + VkDescriptorUpdateTemplateEntryKHR BuildInputOutputDescriptorUpdateTemplate() { return { .dstBinding = 0, @@ -61,6 +143,94 @@ VkDescriptorUpdateTemplateEntryKHR BuildInputOutputDescriptorUpdateTemplate() { }; } +std::array<VkDescriptorUpdateTemplateEntryKHR, 8> BuildASTCPassDescriptorUpdateTemplateEntry() { + return {{ + { + .dstBinding = ASTC_BINDING_INPUT_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = ASTC_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = ASTC_BINDING_ENC_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = ASTC_BINDING_ENC_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = ASTC_BINDING_6_TO_8_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = ASTC_BINDING_6_TO_8_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = ASTC_BINDING_7_TO_8_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = ASTC_BINDING_7_TO_8_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = ASTC_BINDING_8_TO_8_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = ASTC_BINDING_8_TO_8_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = ASTC_BINDING_BYTE_TO_16_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = ASTC_BINDING_BYTE_TO_16_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = ASTC_BINDING_SWIZZLE_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = ASTC_BINDING_SWIZZLE_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = ASTC_BINDING_OUTPUT_IMAGE, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .offset = ASTC_BINDING_OUTPUT_IMAGE * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + }}; +} + +struct AstcPushConstants { + std::array<u32, 2> blocks_dims; + u32 bytes_per_block_log2; + u32 layer_stride; + u32 block_size; + u32 x_shift; + u32 block_height; + u32 block_height_mask; +}; + +struct AstcBufferData { + decltype(SWIZZLE_TABLE) swizzle_table_buffer = SWIZZLE_TABLE; + decltype(EncodingsValues) encoding_values = EncodingsValues; + decltype(REPLICATE_6_BIT_TO_8_TABLE) replicate_6_to_8 = REPLICATE_6_BIT_TO_8_TABLE; + decltype(REPLICATE_7_BIT_TO_8_TABLE) replicate_7_to_8 = REPLICATE_7_BIT_TO_8_TABLE; + decltype(REPLICATE_8_BIT_TO_8_TABLE) replicate_8_to_8 = REPLICATE_8_BIT_TO_8_TABLE; + decltype(REPLICATE_BYTE_TO_16_TABLE) replicate_byte_to_16 = REPLICATE_BYTE_TO_16_TABLE; +} constexpr ASTC_BUFFER_DATA; + } // Anonymous namespace VKComputePass::VKComputePass(const Device& device, VKDescriptorPool& descriptor_pool, @@ -238,4 +408,167 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble( return {staging.buffer, staging.offset}; } +ASTCDecoderPass::ASTCDecoderPass(const Device& device_, VKScheduler& scheduler_, + VKDescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + VKUpdateDescriptorQueue& update_descriptor_queue_, + MemoryAllocator& memory_allocator_) + : VKComputePass(device_, descriptor_pool_, BuildASTCDescriptorSetBindings(), + BuildASTCPassDescriptorUpdateTemplateEntry(), + BuildComputePushConstantRange(sizeof(AstcPushConstants)), + ASTC_DECODER_COMP_SPV), + device{device_}, scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, + update_descriptor_queue{update_descriptor_queue_}, memory_allocator{memory_allocator_} {} + +ASTCDecoderPass::~ASTCDecoderPass() = default; + +void ASTCDecoderPass::MakeDataBuffer() { + constexpr size_t TOTAL_BUFFER_SIZE = sizeof(ASTC_BUFFER_DATA) + sizeof(SWIZZLE_TABLE); + data_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = TOTAL_BUFFER_SIZE, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }); + data_buffer_commit = memory_allocator.Commit(data_buffer, MemoryUsage::Upload); + + const auto staging_ref = staging_buffer_pool.Request(TOTAL_BUFFER_SIZE, MemoryUsage::Upload); + std::memcpy(staging_ref.mapped_span.data(), &ASTC_BUFFER_DATA, sizeof(ASTC_BUFFER_DATA)); + // Tack on the swizzle table at the end of the buffer + std::memcpy(staging_ref.mapped_span.data() + sizeof(ASTC_BUFFER_DATA), &SWIZZLE_TABLE, + sizeof(SWIZZLE_TABLE)); + + scheduler.Record([src = staging_ref.buffer, offset = staging_ref.offset, dst = *data_buffer, + TOTAL_BUFFER_SIZE](vk::CommandBuffer cmdbuf) { + cmdbuf.CopyBuffer(src, dst, + VkBufferCopy{ + .srcOffset = offset, + .dstOffset = 0, + .size = TOTAL_BUFFER_SIZE, + }); + cmdbuf.PipelineBarrier( + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, + VkMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + }); + }); +} + +void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map, + std::span<const VideoCommon::SwizzleParameters> swizzles) { + using namespace VideoCommon::Accelerated; + const std::array<u32, 2> block_dims{ + VideoCore::Surface::DefaultBlockWidth(image.info.format), + VideoCore::Surface::DefaultBlockHeight(image.info.format), + }; + scheduler.RequestOutsideRenderPassOperationContext(); + if (!data_buffer) { + MakeDataBuffer(); + } + const VkPipeline vk_pipeline = *pipeline; + const VkImageAspectFlags aspect_mask = image.AspectMask(); + const VkImage vk_image = image.Handle(); + const bool is_initialized = image.ExchangeInitialization(); + scheduler.Record( + [vk_pipeline, vk_image, aspect_mask, is_initialized](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier image_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = vk_image, + .subresourceRange{ + .aspectMask = aspect_mask, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(is_initialized ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT : 0, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline); + }); + for (const VideoCommon::SwizzleParameters& swizzle : swizzles) { + const size_t input_offset = swizzle.buffer_offset + map.offset; + const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U); + const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U); + const u32 num_dispatches_z = image.info.resources.layers; + + update_descriptor_queue.Acquire(); + update_descriptor_queue.AddBuffer(map.buffer, input_offset, + image.guest_size_bytes - swizzle.buffer_offset); + update_descriptor_queue.AddBuffer(*data_buffer, offsetof(AstcBufferData, encoding_values), + sizeof(AstcBufferData::encoding_values)); + update_descriptor_queue.AddBuffer(*data_buffer, offsetof(AstcBufferData, replicate_6_to_8), + sizeof(AstcBufferData::replicate_6_to_8)); + update_descriptor_queue.AddBuffer(*data_buffer, offsetof(AstcBufferData, replicate_7_to_8), + sizeof(AstcBufferData::replicate_7_to_8)); + update_descriptor_queue.AddBuffer(*data_buffer, offsetof(AstcBufferData, replicate_8_to_8), + sizeof(AstcBufferData::replicate_8_to_8)); + update_descriptor_queue.AddBuffer(*data_buffer, + offsetof(AstcBufferData, replicate_byte_to_16), + sizeof(AstcBufferData::replicate_byte_to_16)); + update_descriptor_queue.AddBuffer(*data_buffer, sizeof(AstcBufferData), + sizeof(SWIZZLE_TABLE)); + update_descriptor_queue.AddImage(image.StorageImageView(swizzle.level)); + + const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); + const VkPipelineLayout vk_layout = *layout; + + // To unswizzle the ASTC data + const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info); + ASSERT(params.origin == (std::array<u32, 3>{0, 0, 0})); + ASSERT(params.destination == (std::array<s32, 3>{0, 0, 0})); + scheduler.Record([vk_layout, num_dispatches_x, num_dispatches_y, num_dispatches_z, + block_dims, params, set](vk::CommandBuffer cmdbuf) { + const AstcPushConstants uniforms{ + .blocks_dims = block_dims, + .bytes_per_block_log2 = params.bytes_per_block_log2, + .layer_stride = params.layer_stride, + .block_size = params.block_size, + .x_shift = params.x_shift, + .block_height = params.block_height, + .block_height_mask = params.block_height_mask, + }; + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, vk_layout, 0, set, {}); + cmdbuf.PushConstants(vk_layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms); + cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z); + }); + } + scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier image_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = vk_image, + .subresourceRange{ + .aspectMask = aspect_mask, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, image_barrier); + }); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index 17d781d99..5ea187c30 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -11,14 +11,21 @@ #include "common/common_types.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" +namespace VideoCommon { +struct SwizzleParameters; +} + namespace Vulkan { class Device; class StagingBufferPool; class VKScheduler; class VKUpdateDescriptorQueue; +class Image; +struct StagingBufferRef; class VKComputePass { public: @@ -77,4 +84,29 @@ private: VKUpdateDescriptorQueue& update_descriptor_queue; }; +class ASTCDecoderPass final : public VKComputePass { +public: + explicit ASTCDecoderPass(const Device& device_, VKScheduler& scheduler_, + VKDescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + VKUpdateDescriptorQueue& update_descriptor_queue_, + MemoryAllocator& memory_allocator_); + ~ASTCDecoderPass(); + + void Assemble(Image& image, const StagingBufferRef& map, + std::span<const VideoCommon::SwizzleParameters> swizzles); + +private: + void MakeDataBuffer(); + + const Device& device; + VKScheduler& scheduler; + StagingBufferPool& staging_buffer_pool; + VKUpdateDescriptorQueue& update_descriptor_queue; + MemoryAllocator& memory_allocator; + + vk::Buffer data_buffer; + MemoryCommit data_buffer_commit; +}; + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index 56ec5e380..db78ce3d9 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp @@ -5,7 +5,7 @@ #include <atomic> #include <chrono> -#include "core/settings.h" +#include "common/settings.h" #include "video_core/renderer_vulkan/vk_master_semaphore.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_wrapper.h" diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h index 2c7ed654d..4b6d64daa 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.h +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h @@ -35,8 +35,8 @@ public: } /// Returns true when a tick has been hit by the GPU. - [[nodiscard]] bool IsFree(u64 tick) { - return gpu_tick.load(std::memory_order_relaxed) >= tick; + [[nodiscard]] bool IsFree(u64 tick) const noexcept { + return KnownGpuTick() >= tick; } /// Advance to the logical tick. diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index dfd38f575..e9a0e7811 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -13,8 +13,8 @@ #include "common/logging/log.h" #include "common/microprofile.h" #include "common/scope_exit.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_vulkan/blit_image.h" @@ -241,7 +241,10 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), update_descriptor_queue(device, scheduler), blit_image(device, scheduler, state_tracker, descriptor_pool), - texture_cache_runtime{device, scheduler, memory_allocator, staging_pool, blit_image}, + astc_decoder_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue, + memory_allocator), + texture_cache_runtime{device, scheduler, memory_allocator, + staging_pool, blit_image, astc_decoder_pass}, texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory), buffer_cache_runtime(device, memory_allocator, scheduler, staging_pool, update_descriptor_queue, descriptor_pool), diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index acea1ba2d..235afc6f3 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -173,6 +173,7 @@ private: VKDescriptorPool descriptor_pool; VKUpdateDescriptorQueue update_descriptor_queue; BlitImageHelper blit_image; + ASTCDecoderPass astc_decoder_pass; GraphicsPipelineCacheKey graphics_key; diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 0b63bd6c8..dfd5c65ba 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -82,11 +82,13 @@ void VKSwapchain::Create(u32 width, u32 height, bool srgb) { resource_ticks.resize(image_count); } -void VKSwapchain::AcquireNextImage() { - device.GetLogical().AcquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(), - *present_semaphores[frame_index], {}, &image_index); +bool VKSwapchain::AcquireNextImage() { + const VkResult result = + device.GetLogical().AcquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(), + *present_semaphores[frame_index], {}, &image_index); scheduler.Wait(resource_ticks[image_index]); + return result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR; } bool VKSwapchain::Present(VkSemaphore render_semaphore) { diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index a728511e0..adc8d27cf 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -28,7 +28,7 @@ public: void Create(u32 width, u32 height, bool srgb); /// Acquires the next image in the swapchain, waits as needed. - void AcquireNextImage(); + bool AcquireNextImage(); /// Presents the rendered image to the swapchain. Returns true when the swapchains had to be /// recreated. Takes responsability for the ownership of fence. diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 22a1014a9..bc2a53841 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -7,9 +7,12 @@ #include <span> #include <vector> +#include "common/bit_cast.h" + #include "video_core/engines/fermi_2d.h" #include "video_core/renderer_vulkan/blit_image.h" #include "video_core/renderer_vulkan/maxwell_to_vk.h" +#include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" @@ -807,7 +810,7 @@ Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_ commit = runtime.memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal); } if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) { - flags |= VideoCommon::ImageFlagBits::Converted; + flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; } if (runtime.device.HasDebuggingToolAttached()) { if (image) { @@ -816,6 +819,38 @@ Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_ buffer.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); } } + static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, + .pNext = nullptr, + .usage = VK_IMAGE_USAGE_STORAGE_BIT, + }; + if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) { + const auto& device = runtime.device.GetLogical(); + storage_image_views.reserve(info.resources.levels); + for (s32 level = 0; level < info.resources.levels; ++level) { + storage_image_views.push_back(device.CreateImageView(VkImageViewCreateInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = &storage_image_view_usage_create_info, + .flags = 0, + .image = *image, + .viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY, + .format = VK_FORMAT_A8B8G8R8_UNORM_PACK32, + .components{ + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange{ + .aspectMask = aspect_mask, + .baseMipLevel = static_cast<u32>(level), + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + })); + } + } } void Image::UploadMemory(const StagingBufferRef& map, std::span<const BufferImageCopy> copies) { @@ -918,7 +953,6 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI } } const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format); - const VkFormat vk_format = format_info.format; const VkImageViewUsageCreateInfo image_view_usage{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, .pNext = nullptr, @@ -930,7 +964,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI .flags = 0, .image = image.Handle(), .viewType = VkImageViewType{}, - .format = vk_format, + .format = format_info.format, .components{ .r = ComponentSwizzle(swizzle[0]), .g = ComponentSwizzle(swizzle[1]), @@ -982,7 +1016,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI .pNext = nullptr, .flags = 0, .buffer = image.Buffer(), - .format = vk_format, + .format = format_info.format, .offset = 0, // TODO: Redesign buffer cache to support this .range = image.guest_size_bytes, }); @@ -1030,14 +1064,13 @@ vk::ImageView ImageView::MakeDepthStencilView(VkImageAspectFlags aspect_mask) { Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) { const auto& device = runtime.device; const bool arbitrary_borders = runtime.device.IsExtCustomBorderColorSupported(); - const std::array<float, 4> color = tsc.BorderColor(); - // C++20 bit_cast - VkClearColorValue border_color; - std::memcpy(&border_color, &color, sizeof(color)); + const auto color = tsc.BorderColor(); + const VkSamplerCustomBorderColorCreateInfoEXT border_ci{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, .pNext = nullptr, - .customBorderColor = border_color, + // TODO: Make use of std::bit_cast once libc++ supports it. + .customBorderColor = Common::BitCast<VkClearColorValue>(color), .format = VK_FORMAT_UNDEFINED, }; const void* pnext = nullptr; @@ -1167,4 +1200,13 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM } } +void TextureCacheRuntime::AccelerateImageUpload( + Image& image, const StagingBufferRef& map, + std::span<const VideoCommon::SwizzleParameters> swizzles) { + if (IsPixelFormatASTC(image.info.format)) { + return astc_decoder_pass.Assemble(image, map, swizzles); + } + UNREACHABLE(); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 3aee27ce0..628785d5e 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -20,6 +20,7 @@ using VideoCommon::Offset2D; using VideoCommon::RenderTargets; using VideoCore::Surface::PixelFormat; +class ASTCDecoderPass; class BlitImageHelper; class Device; class Image; @@ -60,6 +61,7 @@ struct TextureCacheRuntime { MemoryAllocator& memory_allocator; StagingBufferPool& staging_buffer_pool; BlitImageHelper& blit_image_helper; + ASTCDecoderPass& astc_decoder_pass; std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache{}; void Finish(); @@ -83,9 +85,7 @@ struct TextureCacheRuntime { } void AccelerateImageUpload(Image&, const StagingBufferRef&, - std::span<const VideoCommon::SwizzleParameters>) { - UNREACHABLE(); - } + std::span<const VideoCommon::SwizzleParameters>); void InsertUploadMemoryBarrier() {} @@ -121,15 +121,26 @@ public: return *buffer; } - [[nodiscard]] VkImageCreateFlags AspectMask() const noexcept { + [[nodiscard]] VkImageAspectFlags AspectMask() const noexcept { return aspect_mask; } + [[nodiscard]] VkImageView StorageImageView(s32 level) const noexcept { + return *storage_image_views[level]; + } + + /// Returns true when the image is already initialized and mark it as initialized + [[nodiscard]] bool ExchangeInitialization() noexcept { + return std::exchange(initialized, true); + } + private: VKScheduler* scheduler; vk::Image image; vk::Buffer buffer; MemoryCommit commit; + vk::ImageView image_view; + std::vector<vk::ImageView> storage_image_views; VkImageAspectFlags aspect_mask = 0; bool initialized = false; }; diff --git a/src/video_core/texture_cache/accelerated_swizzle.h b/src/video_core/texture_cache/accelerated_swizzle.h index 6ec5c78c4..a11c924e1 100644 --- a/src/video_core/texture_cache/accelerated_swizzle.h +++ b/src/video_core/texture_cache/accelerated_swizzle.h @@ -13,8 +13,8 @@ namespace VideoCommon::Accelerated { struct BlockLinearSwizzle2DParams { - std::array<u32, 3> origin; - std::array<s32, 3> destination; + alignas(16) std::array<u32, 3> origin; + alignas(16) std::array<s32, 3> destination; u32 bytes_per_block_log2; u32 layer_stride; u32 block_size; diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp index f89a40b4c..e8d632f9e 100644 --- a/src/video_core/texture_cache/image_view_base.cpp +++ b/src/video_core/texture_cache/image_view_base.cpp @@ -5,7 +5,7 @@ #include <algorithm> #include "common/assert.h" -#include "core/settings.h" +#include "common/settings.h" #include "video_core/compatible_formats.h" #include "video_core/surface.h" #include "video_core/texture_cache/formatter.h" diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 2c42d1449..0ab297413 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -47,7 +47,6 @@ #include "video_core/texture_cache/formatter.h" #include "video_core/texture_cache/samples_helper.h" #include "video_core/texture_cache/util.h" -#include "video_core/textures/astc.h" #include "video_core/textures/decoders.h" namespace VideoCommon { @@ -269,16 +268,19 @@ template <u32 GOB_EXTENT> return num_tiles << shift; } -[[nodiscard]] constexpr std::array<u32, MAX_MIP_LEVELS> CalculateLevelSizes(const LevelInfo& info, - u32 num_levels) { +[[nodiscard]] constexpr LevelArray CalculateLevelSizes(const LevelInfo& info, u32 num_levels) { ASSERT(num_levels <= MAX_MIP_LEVELS); - std::array<u32, MAX_MIP_LEVELS> sizes{}; + LevelArray sizes{}; for (u32 level = 0; level < num_levels; ++level) { sizes[level] = CalculateLevelSize(info, level); } return sizes; } +[[nodiscard]] u32 CalculateLevelBytes(const LevelArray& sizes, u32 num_levels) { + return std::reduce(sizes.begin(), sizes.begin() + num_levels, 0U); +} + [[nodiscard]] constexpr LevelInfo MakeLevelInfo(PixelFormat format, Extent3D size, Extent3D block, u32 num_samples, u32 tile_width_spacing) { const auto [samples_x, samples_y] = Samples(num_samples); @@ -567,10 +569,10 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr const u32 num_levels = info.resources.levels; const std::array sizes = CalculateLevelSizes(level_info, num_levels); - size_t guest_offset = std::reduce(sizes.begin(), sizes.begin() + level, 0); + size_t guest_offset = CalculateLevelBytes(sizes, level); const size_t layer_stride = - AlignLayerSize(std::reduce(sizes.begin(), sizes.begin() + num_levels, 0), size, - level_info.block, tile_size.height, info.tile_width_spacing); + AlignLayerSize(CalculateLevelBytes(sizes, num_levels), size, level_info.block, + tile_size.height, info.tile_width_spacing); const size_t subresource_size = sizes[level]; const auto dst_data = std::make_unique<u8[]>(subresource_size); @@ -644,10 +646,10 @@ u32 CalculateLayerSize(const ImageInfo& info) noexcept { info.tile_width_spacing, info.resources.levels); } -std::array<u32, MAX_MIP_LEVELS> CalculateMipLevelOffsets(const ImageInfo& info) noexcept { +LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept { ASSERT(info.resources.levels <= static_cast<s32>(MAX_MIP_LEVELS)); const LevelInfo level_info = MakeLevelInfo(info); - std::array<u32, MAX_MIP_LEVELS> offsets{}; + LevelArray offsets{}; u32 offset = 0; for (s32 level = 0; level < info.resources.levels; ++level) { offsets[level] = offset; @@ -813,7 +815,7 @@ std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GP const Extent2D tile_size = DefaultBlockSize(info.format); const std::array level_sizes = CalculateLevelSizes(level_info, num_levels); const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing); - const u32 layer_size = std::reduce(level_sizes.begin(), level_sizes.begin() + num_levels, 0); + const u32 layer_size = CalculateLevelBytes(level_sizes, num_levels); const u32 layer_stride = AlignLayerSize(layer_size, size, level_info.block, tile_size.height, info.tile_width_spacing); size_t guest_offset = 0; @@ -879,17 +881,8 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8 ASSERT(copy.image_extent == mip_size); ASSERT(copy.buffer_row_length == Common::AlignUp(mip_size.width, tile_size.width)); ASSERT(copy.buffer_image_height == Common::AlignUp(mip_size.height, tile_size.height)); - - if (IsPixelFormatASTC(info.format)) { - ASSERT(copy.image_extent.depth == 1); - Tegra::Texture::ASTC::Decompress(input.subspan(copy.buffer_offset), - copy.image_extent.width, copy.image_extent.height, - copy.image_subresource.num_layers, tile_size.width, - tile_size.height, output.subspan(output_offset)); - } else { - DecompressBC4(input.subspan(copy.buffer_offset), copy.image_extent, - output.subspan(output_offset)); - } + DecompressBC4(input.subspan(copy.buffer_offset), copy.image_extent, + output.subspan(output_offset)); copy.buffer_offset = output_offset; copy.buffer_row_length = mip_size.width; copy.buffer_image_height = mip_size.height; diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h index 4d0072867..cdc5cbc75 100644 --- a/src/video_core/texture_cache/util.h +++ b/src/video_core/texture_cache/util.h @@ -20,6 +20,8 @@ namespace VideoCommon { using Tegra::Texture::TICEntry; +using LevelArray = std::array<u32, MAX_MIP_LEVELS>; + struct OverlapResult { GPUVAddr gpu_addr; VAddr cpu_addr; @@ -36,8 +38,7 @@ struct OverlapResult { [[nodiscard]] u32 CalculateLayerSize(const ImageInfo& info) noexcept; -[[nodiscard]] std::array<u32, MAX_MIP_LEVELS> CalculateMipLevelOffsets( - const ImageInfo& info) noexcept; +[[nodiscard]] LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept; [[nodiscard]] std::vector<u32> CalculateSliceOffsets(const ImageInfo& info); diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp deleted file mode 100644 index 3625b666c..000000000 --- a/src/video_core/textures/astc.cpp +++ /dev/null @@ -1,1710 +0,0 @@ -// Copyright 2016 The University of North Carolina at Chapel Hill -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Please send all BUG REPORTS to <pavel@cs.unc.edu>. -// <http://gamma.cs.unc.edu/FasTC/> - -#include <algorithm> -#include <cassert> -#include <cstring> -#include <span> -#include <vector> - -#include <boost/container/static_vector.hpp> - -#include "common/common_types.h" - -#include "video_core/textures/astc.h" - -namespace { - -/// Count the number of bits set in a number. -constexpr u32 Popcnt(u32 n) { - u32 c = 0; - for (; n; c++) { - n &= n - 1; - } - return c; -} - -} // Anonymous namespace - -class InputBitStream { -public: - constexpr explicit InputBitStream(std::span<const u8> data, size_t start_offset = 0) - : cur_byte{data.data()}, total_bits{data.size()}, next_bit{start_offset % 8} {} - - constexpr size_t GetBitsRead() const { - return bits_read; - } - - constexpr bool ReadBit() { - if (bits_read >= total_bits * 8) { - return 0; - } - const bool bit = ((*cur_byte >> next_bit) & 1) != 0; - ++next_bit; - while (next_bit >= 8) { - next_bit -= 8; - ++cur_byte; - } - ++bits_read; - return bit; - } - - constexpr u32 ReadBits(std::size_t nBits) { - u32 ret = 0; - for (std::size_t i = 0; i < nBits; ++i) { - ret |= (ReadBit() & 1) << i; - } - return ret; - } - - template <std::size_t nBits> - constexpr u32 ReadBits() { - u32 ret = 0; - for (std::size_t i = 0; i < nBits; ++i) { - ret |= (ReadBit() & 1) << i; - } - return ret; - } - -private: - const u8* cur_byte; - size_t total_bits = 0; - size_t next_bit = 0; - size_t bits_read = 0; -}; - -class OutputBitStream { -public: - constexpr explicit OutputBitStream(u8* ptr, std::size_t bits = 0, std::size_t start_offset = 0) - : cur_byte{ptr}, num_bits{bits}, next_bit{start_offset % 8} {} - - constexpr std::size_t GetBitsWritten() const { - return bits_written; - } - - constexpr void WriteBitsR(u32 val, u32 nBits) { - for (u32 i = 0; i < nBits; i++) { - WriteBit((val >> (nBits - i - 1)) & 1); - } - } - - constexpr void WriteBits(u32 val, u32 nBits) { - for (u32 i = 0; i < nBits; i++) { - WriteBit((val >> i) & 1); - } - } - -private: - constexpr void WriteBit(bool b) { - if (bits_written >= num_bits) { - return; - } - - const u32 mask = 1 << next_bit++; - - // clear the bit - *cur_byte &= static_cast<u8>(~mask); - - // Write the bit, if necessary - if (b) - *cur_byte |= static_cast<u8>(mask); - - // Next byte? - if (next_bit >= 8) { - cur_byte += 1; - next_bit = 0; - } - } - - u8* cur_byte; - std::size_t num_bits; - std::size_t bits_written = 0; - std::size_t next_bit = 0; -}; - -template <typename IntType> -class Bits { -public: - explicit Bits(const IntType& v) : m_Bits(v) {} - - Bits(const Bits&) = delete; - Bits& operator=(const Bits&) = delete; - - u8 operator[](u32 bitPos) const { - return static_cast<u8>((m_Bits >> bitPos) & 1); - } - - IntType operator()(u32 start, u32 end) const { - if (start == end) { - return (*this)[start]; - } else if (start > end) { - u32 t = start; - start = end; - end = t; - } - - u64 mask = (1 << (end - start + 1)) - 1; - return (m_Bits >> start) & static_cast<IntType>(mask); - } - -private: - const IntType& m_Bits; -}; - -enum class IntegerEncoding { JustBits, Qus32, Trit }; - -struct IntegerEncodedValue { - constexpr IntegerEncodedValue() = default; - - constexpr IntegerEncodedValue(IntegerEncoding encoding_, u32 num_bits_) - : encoding{encoding_}, num_bits{num_bits_} {} - - constexpr bool MatchesEncoding(const IntegerEncodedValue& other) const { - return encoding == other.encoding && num_bits == other.num_bits; - } - - // Returns the number of bits required to encode nVals values. - u32 GetBitLength(u32 nVals) const { - u32 totalBits = num_bits * nVals; - if (encoding == IntegerEncoding::Trit) { - totalBits += (nVals * 8 + 4) / 5; - } else if (encoding == IntegerEncoding::Qus32) { - totalBits += (nVals * 7 + 2) / 3; - } - return totalBits; - } - - IntegerEncoding encoding{}; - u32 num_bits = 0; - u32 bit_value = 0; - union { - u32 qus32_value = 0; - u32 trit_value; - }; -}; -using IntegerEncodedVector = boost::container::static_vector< - IntegerEncodedValue, 256, - boost::container::static_vector_options< - boost::container::inplace_alignment<alignof(IntegerEncodedValue)>, - boost::container::throw_on_overflow<false>>::type>; - -static void DecodeTritBlock(InputBitStream& bits, IntegerEncodedVector& result, u32 nBitsPerValue) { - // Implement the algorithm in section C.2.12 - std::array<u32, 5> m; - std::array<u32, 5> t; - u32 T; - - // Read the trit encoded block according to - // table C.2.14 - m[0] = bits.ReadBits(nBitsPerValue); - T = bits.ReadBits<2>(); - m[1] = bits.ReadBits(nBitsPerValue); - T |= bits.ReadBits<2>() << 2; - m[2] = bits.ReadBits(nBitsPerValue); - T |= bits.ReadBit() << 4; - m[3] = bits.ReadBits(nBitsPerValue); - T |= bits.ReadBits<2>() << 5; - m[4] = bits.ReadBits(nBitsPerValue); - T |= bits.ReadBit() << 7; - - u32 C = 0; - - Bits<u32> Tb(T); - if (Tb(2, 4) == 7) { - C = (Tb(5, 7) << 2) | Tb(0, 1); - t[4] = t[3] = 2; - } else { - C = Tb(0, 4); - if (Tb(5, 6) == 3) { - t[4] = 2; - t[3] = Tb[7]; - } else { - t[4] = Tb[7]; - t[3] = Tb(5, 6); - } - } - - Bits<u32> Cb(C); - if (Cb(0, 1) == 3) { - t[2] = 2; - t[1] = Cb[4]; - t[0] = (Cb[3] << 1) | (Cb[2] & ~Cb[3]); - } else if (Cb(2, 3) == 3) { - t[2] = 2; - t[1] = 2; - t[0] = Cb(0, 1); - } else { - t[2] = Cb[4]; - t[1] = Cb(2, 3); - t[0] = (Cb[1] << 1) | (Cb[0] & ~Cb[1]); - } - - for (std::size_t i = 0; i < 5; ++i) { - IntegerEncodedValue& val = result.emplace_back(IntegerEncoding::Trit, nBitsPerValue); - val.bit_value = m[i]; - val.trit_value = t[i]; - } -} - -static void DecodeQus32Block(InputBitStream& bits, IntegerEncodedVector& result, - u32 nBitsPerValue) { - // Implement the algorithm in section C.2.12 - u32 m[3]; - u32 q[3]; - u32 Q; - - // Read the trit encoded block according to - // table C.2.15 - m[0] = bits.ReadBits(nBitsPerValue); - Q = bits.ReadBits<3>(); - m[1] = bits.ReadBits(nBitsPerValue); - Q |= bits.ReadBits<2>() << 3; - m[2] = bits.ReadBits(nBitsPerValue); - Q |= bits.ReadBits<2>() << 5; - - Bits<u32> Qb(Q); - if (Qb(1, 2) == 3 && Qb(5, 6) == 0) { - q[0] = q[1] = 4; - q[2] = (Qb[0] << 2) | ((Qb[4] & ~Qb[0]) << 1) | (Qb[3] & ~Qb[0]); - } else { - u32 C = 0; - if (Qb(1, 2) == 3) { - q[2] = 4; - C = (Qb(3, 4) << 3) | ((~Qb(5, 6) & 3) << 1) | Qb[0]; - } else { - q[2] = Qb(5, 6); - C = Qb(0, 4); - } - - Bits<u32> Cb(C); - if (Cb(0, 2) == 5) { - q[1] = 4; - q[0] = Cb(3, 4); - } else { - q[1] = Cb(3, 4); - q[0] = Cb(0, 2); - } - } - - for (std::size_t i = 0; i < 3; ++i) { - IntegerEncodedValue& val = result.emplace_back(IntegerEncoding::Qus32, nBitsPerValue); - val.bit_value = m[i]; - val.qus32_value = q[i]; - } -} - -// Returns a new instance of this struct that corresponds to the -// can take no more than maxval values -static constexpr IntegerEncodedValue CreateEncoding(u32 maxVal) { - while (maxVal > 0) { - u32 check = maxVal + 1; - - // Is maxVal a power of two? - if (!(check & (check - 1))) { - return IntegerEncodedValue(IntegerEncoding::JustBits, Popcnt(maxVal)); - } - - // Is maxVal of the type 3*2^n - 1? - if ((check % 3 == 0) && !((check / 3) & ((check / 3) - 1))) { - return IntegerEncodedValue(IntegerEncoding::Trit, Popcnt(check / 3 - 1)); - } - - // Is maxVal of the type 5*2^n - 1? - if ((check % 5 == 0) && !((check / 5) & ((check / 5) - 1))) { - return IntegerEncodedValue(IntegerEncoding::Qus32, Popcnt(check / 5 - 1)); - } - - // Apparently it can't be represented with a bounded integer sequence... - // just iterate. - maxVal--; - } - return IntegerEncodedValue(IntegerEncoding::JustBits, 0); -} - -static constexpr std::array<IntegerEncodedValue, 256> MakeEncodedValues() { - std::array<IntegerEncodedValue, 256> encodings{}; - for (std::size_t i = 0; i < encodings.size(); ++i) { - encodings[i] = CreateEncoding(static_cast<u32>(i)); - } - return encodings; -} - -static constexpr std::array EncodingsValues = MakeEncodedValues(); - -// Fills result with the values that are encoded in the given -// bitstream. We must know beforehand what the maximum possible -// value is, and how many values we're decoding. -static void DecodeIntegerSequence(IntegerEncodedVector& result, InputBitStream& bits, u32 maxRange, - u32 nValues) { - // Determine encoding parameters - IntegerEncodedValue val = EncodingsValues[maxRange]; - - // Start decoding - u32 nValsDecoded = 0; - while (nValsDecoded < nValues) { - switch (val.encoding) { - case IntegerEncoding::Qus32: - DecodeQus32Block(bits, result, val.num_bits); - nValsDecoded += 3; - break; - - case IntegerEncoding::Trit: - DecodeTritBlock(bits, result, val.num_bits); - nValsDecoded += 5; - break; - - case IntegerEncoding::JustBits: - val.bit_value = bits.ReadBits(val.num_bits); - result.push_back(val); - nValsDecoded++; - break; - } - } -} - -namespace ASTCC { - -struct TexelWeightParams { - u32 m_Width = 0; - u32 m_Height = 0; - bool m_bDualPlane = false; - u32 m_MaxWeight = 0; - bool m_bError = false; - bool m_bVoidExtentLDR = false; - bool m_bVoidExtentHDR = false; - - u32 GetPackedBitSize() const { - // How many indices do we have? - u32 nIdxs = m_Height * m_Width; - if (m_bDualPlane) { - nIdxs *= 2; - } - - return EncodingsValues[m_MaxWeight].GetBitLength(nIdxs); - } - - u32 GetNumWeightValues() const { - u32 ret = m_Width * m_Height; - if (m_bDualPlane) { - ret *= 2; - } - return ret; - } -}; - -static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) { - TexelWeightParams params; - - // Read the entire block mode all at once - u16 modeBits = static_cast<u16>(strm.ReadBits<11>()); - - // Does this match the void extent block mode? - if ((modeBits & 0x01FF) == 0x1FC) { - if (modeBits & 0x200) { - params.m_bVoidExtentHDR = true; - } else { - params.m_bVoidExtentLDR = true; - } - - // Next two bits must be one. - if (!(modeBits & 0x400) || !strm.ReadBit()) { - params.m_bError = true; - } - - return params; - } - - // First check if the last four bits are zero - if ((modeBits & 0xF) == 0) { - params.m_bError = true; - return params; - } - - // If the last two bits are zero, then if bits - // [6-8] are all ones, this is also reserved. - if ((modeBits & 0x3) == 0 && (modeBits & 0x1C0) == 0x1C0) { - params.m_bError = true; - return params; - } - - // Otherwise, there is no error... Figure out the layout - // of the block mode. Layout is determined by a number - // between 0 and 9 corresponding to table C.2.8 of the - // ASTC spec. - u32 layout = 0; - - if ((modeBits & 0x1) || (modeBits & 0x2)) { - // layout is in [0-4] - if (modeBits & 0x8) { - // layout is in [2-4] - if (modeBits & 0x4) { - // layout is in [3-4] - if (modeBits & 0x100) { - layout = 4; - } else { - layout = 3; - } - } else { - layout = 2; - } - } else { - // layout is in [0-1] - if (modeBits & 0x4) { - layout = 1; - } else { - layout = 0; - } - } - } else { - // layout is in [5-9] - if (modeBits & 0x100) { - // layout is in [7-9] - if (modeBits & 0x80) { - // layout is in [7-8] - assert((modeBits & 0x40) == 0U); - if (modeBits & 0x20) { - layout = 8; - } else { - layout = 7; - } - } else { - layout = 9; - } - } else { - // layout is in [5-6] - if (modeBits & 0x80) { - layout = 6; - } else { - layout = 5; - } - } - } - - assert(layout < 10); - - // Determine R - u32 R = !!(modeBits & 0x10); - if (layout < 5) { - R |= (modeBits & 0x3) << 1; - } else { - R |= (modeBits & 0xC) >> 1; - } - assert(2 <= R && R <= 7); - - // Determine width & height - switch (layout) { - case 0: { - u32 A = (modeBits >> 5) & 0x3; - u32 B = (modeBits >> 7) & 0x3; - params.m_Width = B + 4; - params.m_Height = A + 2; - break; - } - - case 1: { - u32 A = (modeBits >> 5) & 0x3; - u32 B = (modeBits >> 7) & 0x3; - params.m_Width = B + 8; - params.m_Height = A + 2; - break; - } - - case 2: { - u32 A = (modeBits >> 5) & 0x3; - u32 B = (modeBits >> 7) & 0x3; - params.m_Width = A + 2; - params.m_Height = B + 8; - break; - } - - case 3: { - u32 A = (modeBits >> 5) & 0x3; - u32 B = (modeBits >> 7) & 0x1; - params.m_Width = A + 2; - params.m_Height = B + 6; - break; - } - - case 4: { - u32 A = (modeBits >> 5) & 0x3; - u32 B = (modeBits >> 7) & 0x1; - params.m_Width = B + 2; - params.m_Height = A + 2; - break; - } - - case 5: { - u32 A = (modeBits >> 5) & 0x3; - params.m_Width = 12; - params.m_Height = A + 2; - break; - } - - case 6: { - u32 A = (modeBits >> 5) & 0x3; - params.m_Width = A + 2; - params.m_Height = 12; - break; - } - - case 7: { - params.m_Width = 6; - params.m_Height = 10; - break; - } - - case 8: { - params.m_Width = 10; - params.m_Height = 6; - break; - } - - case 9: { - u32 A = (modeBits >> 5) & 0x3; - u32 B = (modeBits >> 9) & 0x3; - params.m_Width = A + 6; - params.m_Height = B + 6; - break; - } - - default: - assert(false && "Don't know this layout..."); - params.m_bError = true; - break; - } - - // Determine whether or not we're using dual planes - // and/or high precision layouts. - bool D = (layout != 9) && (modeBits & 0x400); - bool H = (layout != 9) && (modeBits & 0x200); - - if (H) { - const u32 maxWeights[6] = {9, 11, 15, 19, 23, 31}; - params.m_MaxWeight = maxWeights[R - 2]; - } else { - const u32 maxWeights[6] = {1, 2, 3, 4, 5, 7}; - params.m_MaxWeight = maxWeights[R - 2]; - } - - params.m_bDualPlane = D; - - return params; -} - -static void FillVoidExtentLDR(InputBitStream& strm, std::span<u32> outBuf, u32 blockWidth, - u32 blockHeight) { - // Don't actually care about the void extent, just read the bits... - for (s32 i = 0; i < 4; ++i) { - strm.ReadBits<13>(); - } - - // Decode the RGBA components and renormalize them to the range [0, 255] - u16 r = static_cast<u16>(strm.ReadBits<16>()); - u16 g = static_cast<u16>(strm.ReadBits<16>()); - u16 b = static_cast<u16>(strm.ReadBits<16>()); - u16 a = static_cast<u16>(strm.ReadBits<16>()); - - u32 rgba = (r >> 8) | (g & 0xFF00) | (static_cast<u32>(b) & 0xFF00) << 8 | - (static_cast<u32>(a) & 0xFF00) << 16; - - for (u32 j = 0; j < blockHeight; j++) { - for (u32 i = 0; i < blockWidth; i++) { - outBuf[j * blockWidth + i] = rgba; - } - } -} - -static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) { - for (u32 j = 0; j < blockHeight; j++) { - for (u32 i = 0; i < blockWidth; i++) { - outBuf[j * blockWidth + i] = 0xFFFF00FF; - } - } -} - -// Replicates low numBits such that [(toBit - 1):(toBit - 1 - fromBit)] -// is the same as [(numBits - 1):0] and repeats all the way down. -template <typename IntType> -static constexpr IntType Replicate(IntType val, u32 numBits, u32 toBit) { - if (numBits == 0) { - return 0; - } - if (toBit == 0) { - return 0; - } - const IntType v = val & static_cast<IntType>((1 << numBits) - 1); - IntType res = v; - u32 reslen = numBits; - while (reslen < toBit) { - u32 comp = 0; - if (numBits > toBit - reslen) { - u32 newshift = toBit - reslen; - comp = numBits - newshift; - numBits = newshift; - } - res = static_cast<IntType>(res << numBits); - res = static_cast<IntType>(res | (v >> comp)); - reslen += numBits; - } - return res; -} - -static constexpr std::size_t NumReplicateEntries(u32 num_bits) { - return std::size_t(1) << num_bits; -} - -template <typename IntType, u32 num_bits, u32 to_bit> -static constexpr auto MakeReplicateTable() { - std::array<IntType, NumReplicateEntries(num_bits)> table{}; - for (IntType value = 0; value < static_cast<IntType>(std::size(table)); ++value) { - table[value] = Replicate(value, num_bits, to_bit); - } - return table; -} - -static constexpr auto REPLICATE_BYTE_TO_16_TABLE = MakeReplicateTable<u32, 8, 16>(); -static constexpr u32 ReplicateByteTo16(std::size_t value) { - return REPLICATE_BYTE_TO_16_TABLE[value]; -} - -static constexpr auto REPLICATE_BIT_TO_7_TABLE = MakeReplicateTable<u32, 1, 7>(); -static constexpr u32 ReplicateBitTo7(std::size_t value) { - return REPLICATE_BIT_TO_7_TABLE[value]; -} - -static constexpr auto REPLICATE_BIT_TO_9_TABLE = MakeReplicateTable<u32, 1, 9>(); -static constexpr u32 ReplicateBitTo9(std::size_t value) { - return REPLICATE_BIT_TO_9_TABLE[value]; -} - -static constexpr auto REPLICATE_1_BIT_TO_8_TABLE = MakeReplicateTable<u32, 1, 8>(); -static constexpr auto REPLICATE_2_BIT_TO_8_TABLE = MakeReplicateTable<u32, 2, 8>(); -static constexpr auto REPLICATE_3_BIT_TO_8_TABLE = MakeReplicateTable<u32, 3, 8>(); -static constexpr auto REPLICATE_4_BIT_TO_8_TABLE = MakeReplicateTable<u32, 4, 8>(); -static constexpr auto REPLICATE_5_BIT_TO_8_TABLE = MakeReplicateTable<u32, 5, 8>(); -static constexpr auto REPLICATE_6_BIT_TO_8_TABLE = MakeReplicateTable<u32, 6, 8>(); -static constexpr auto REPLICATE_7_BIT_TO_8_TABLE = MakeReplicateTable<u32, 7, 8>(); -static constexpr auto REPLICATE_8_BIT_TO_8_TABLE = MakeReplicateTable<u32, 8, 8>(); -/// Use a precompiled table with the most common usages, if it's not in the expected range, fallback -/// to the runtime implementation -static constexpr u32 FastReplicateTo8(u32 value, u32 num_bits) { - switch (num_bits) { - case 1: - return REPLICATE_1_BIT_TO_8_TABLE[value]; - case 2: - return REPLICATE_2_BIT_TO_8_TABLE[value]; - case 3: - return REPLICATE_3_BIT_TO_8_TABLE[value]; - case 4: - return REPLICATE_4_BIT_TO_8_TABLE[value]; - case 5: - return REPLICATE_5_BIT_TO_8_TABLE[value]; - case 6: - return REPLICATE_6_BIT_TO_8_TABLE[value]; - case 7: - return REPLICATE_7_BIT_TO_8_TABLE[value]; - case 8: - return REPLICATE_8_BIT_TO_8_TABLE[value]; - default: - return Replicate(value, num_bits, 8); - } -} - -static constexpr auto REPLICATE_1_BIT_TO_6_TABLE = MakeReplicateTable<u32, 1, 6>(); -static constexpr auto REPLICATE_2_BIT_TO_6_TABLE = MakeReplicateTable<u32, 2, 6>(); -static constexpr auto REPLICATE_3_BIT_TO_6_TABLE = MakeReplicateTable<u32, 3, 6>(); -static constexpr auto REPLICATE_4_BIT_TO_6_TABLE = MakeReplicateTable<u32, 4, 6>(); -static constexpr auto REPLICATE_5_BIT_TO_6_TABLE = MakeReplicateTable<u32, 5, 6>(); -static constexpr u32 FastReplicateTo6(u32 value, u32 num_bits) { - switch (num_bits) { - case 1: - return REPLICATE_1_BIT_TO_6_TABLE[value]; - case 2: - return REPLICATE_2_BIT_TO_6_TABLE[value]; - case 3: - return REPLICATE_3_BIT_TO_6_TABLE[value]; - case 4: - return REPLICATE_4_BIT_TO_6_TABLE[value]; - case 5: - return REPLICATE_5_BIT_TO_6_TABLE[value]; - default: - return Replicate(value, num_bits, 6); - } -} - -class Pixel { -protected: - using ChannelType = s16; - u8 m_BitDepth[4] = {8, 8, 8, 8}; - s16 color[4] = {}; - -public: - Pixel() = default; - Pixel(u32 a, u32 r, u32 g, u32 b, u32 bitDepth = 8) - : m_BitDepth{u8(bitDepth), u8(bitDepth), u8(bitDepth), u8(bitDepth)}, - color{static_cast<ChannelType>(a), static_cast<ChannelType>(r), - static_cast<ChannelType>(g), static_cast<ChannelType>(b)} {} - - // Changes the depth of each pixel. This scales the values to - // the appropriate bit depth by either truncating the least - // significant bits when going from larger to smaller bit depth - // or by repeating the most significant bits when going from - // smaller to larger bit depths. - void ChangeBitDepth() { - for (u32 i = 0; i < 4; i++) { - Component(i) = ChangeBitDepth(Component(i), m_BitDepth[i]); - m_BitDepth[i] = 8; - } - } - - template <typename IntType> - static float ConvertChannelToFloat(IntType channel, u8 bitDepth) { - float denominator = static_cast<float>((1 << bitDepth) - 1); - return static_cast<float>(channel) / denominator; - } - - // Changes the bit depth of a single component. See the comment - // above for how we do this. - static ChannelType ChangeBitDepth(Pixel::ChannelType val, u8 oldDepth) { - assert(oldDepth <= 8); - - if (oldDepth == 8) { - // Do nothing - return val; - } else if (oldDepth == 0) { - return static_cast<ChannelType>((1 << 8) - 1); - } else if (8 > oldDepth) { - return static_cast<ChannelType>(FastReplicateTo8(static_cast<u32>(val), oldDepth)); - } else { - // oldDepth > newDepth - const u8 bitsWasted = static_cast<u8>(oldDepth - 8); - u16 v = static_cast<u16>(val); - v = static_cast<u16>((v + (1 << (bitsWasted - 1))) >> bitsWasted); - v = ::std::min<u16>(::std::max<u16>(0, v), static_cast<u16>((1 << 8) - 1)); - return static_cast<u8>(v); - } - - assert(false && "We shouldn't get here."); - return 0; - } - - const ChannelType& A() const { - return color[0]; - } - ChannelType& A() { - return color[0]; - } - const ChannelType& R() const { - return color[1]; - } - ChannelType& R() { - return color[1]; - } - const ChannelType& G() const { - return color[2]; - } - ChannelType& G() { - return color[2]; - } - const ChannelType& B() const { - return color[3]; - } - ChannelType& B() { - return color[3]; - } - const ChannelType& Component(u32 idx) const { - return color[idx]; - } - ChannelType& Component(u32 idx) { - return color[idx]; - } - - void GetBitDepth(u8 (&outDepth)[4]) const { - for (s32 i = 0; i < 4; i++) { - outDepth[i] = m_BitDepth[i]; - } - } - - // Take all of the components, transform them to their 8-bit variants, - // and then pack each channel into an R8G8B8A8 32-bit integer. We assume - // that the architecture is little-endian, so the alpha channel will end - // up in the most-significant byte. - u32 Pack() const { - Pixel eightBit(*this); - eightBit.ChangeBitDepth(); - - u32 r = 0; - r |= eightBit.A(); - r <<= 8; - r |= eightBit.B(); - r <<= 8; - r |= eightBit.G(); - r <<= 8; - r |= eightBit.R(); - return r; - } - - // Clamps the pixel to the range [0,255] - void ClampByte() { - for (u32 i = 0; i < 4; i++) { - color[i] = (color[i] < 0) ? 0 : ((color[i] > 255) ? 255 : color[i]); - } - } - - void MakeOpaque() { - A() = 255; - } -}; - -static void DecodeColorValues(u32* out, std::span<u8> data, const u32* modes, const u32 nPartitions, - const u32 nBitsForColorData) { - // First figure out how many color values we have - u32 nValues = 0; - for (u32 i = 0; i < nPartitions; i++) { - nValues += ((modes[i] >> 2) + 1) << 1; - } - - // Then based on the number of values and the remaining number of bits, - // figure out the max value for each of them... - u32 range = 256; - while (--range > 0) { - IntegerEncodedValue val = EncodingsValues[range]; - u32 bitLength = val.GetBitLength(nValues); - if (bitLength <= nBitsForColorData) { - // Find the smallest possible range that matches the given encoding - while (--range > 0) { - IntegerEncodedValue newval = EncodingsValues[range]; - if (!newval.MatchesEncoding(val)) { - break; - } - } - - // Return to last matching range. - range++; - break; - } - } - - // We now have enough to decode our integer sequence. - IntegerEncodedVector decodedColorValues; - - InputBitStream colorStream(data, 0); - DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues); - - // Once we have the decoded values, we need to dequantize them to the 0-255 range - // This procedure is outlined in ASTC spec C.2.13 - u32 outIdx = 0; - for (auto itr = decodedColorValues.begin(); itr != decodedColorValues.end(); ++itr) { - // Have we already decoded all that we need? - if (outIdx >= nValues) { - break; - } - - const IntegerEncodedValue& val = *itr; - u32 bitlen = val.num_bits; - u32 bitval = val.bit_value; - - assert(bitlen >= 1); - - u32 A = 0, B = 0, C = 0, D = 0; - // A is just the lsb replicated 9 times. - A = ReplicateBitTo9(bitval & 1); - - switch (val.encoding) { - // Replicate bits - case IntegerEncoding::JustBits: - out[outIdx++] = FastReplicateTo8(bitval, bitlen); - break; - - // Use algorithm in C.2.13 - case IntegerEncoding::Trit: { - - D = val.trit_value; - - switch (bitlen) { - case 1: { - C = 204; - } break; - - case 2: { - C = 93; - // B = b000b0bb0 - u32 b = (bitval >> 1) & 1; - B = (b << 8) | (b << 4) | (b << 2) | (b << 1); - } break; - - case 3: { - C = 44; - // B = cb000cbcb - u32 cb = (bitval >> 1) & 3; - B = (cb << 7) | (cb << 2) | cb; - } break; - - case 4: { - C = 22; - // B = dcb000dcb - u32 dcb = (bitval >> 1) & 7; - B = (dcb << 6) | dcb; - } break; - - case 5: { - C = 11; - // B = edcb000ed - u32 edcb = (bitval >> 1) & 0xF; - B = (edcb << 5) | (edcb >> 2); - } break; - - case 6: { - C = 5; - // B = fedcb000f - u32 fedcb = (bitval >> 1) & 0x1F; - B = (fedcb << 4) | (fedcb >> 4); - } break; - - default: - assert(false && "Unsupported trit encoding for color values!"); - break; - } // switch(bitlen) - } // case IntegerEncoding::Trit - break; - - case IntegerEncoding::Qus32: { - - D = val.qus32_value; - - switch (bitlen) { - case 1: { - C = 113; - } break; - - case 2: { - C = 54; - // B = b0000bb00 - u32 b = (bitval >> 1) & 1; - B = (b << 8) | (b << 3) | (b << 2); - } break; - - case 3: { - C = 26; - // B = cb0000cbc - u32 cb = (bitval >> 1) & 3; - B = (cb << 7) | (cb << 1) | (cb >> 1); - } break; - - case 4: { - C = 13; - // B = dcb0000dc - u32 dcb = (bitval >> 1) & 7; - B = (dcb << 6) | (dcb >> 1); - } break; - - case 5: { - C = 6; - // B = edcb0000e - u32 edcb = (bitval >> 1) & 0xF; - B = (edcb << 5) | (edcb >> 3); - } break; - - default: - assert(false && "Unsupported quint encoding for color values!"); - break; - } // switch(bitlen) - } // case IntegerEncoding::Qus32 - break; - } // switch(val.encoding) - - if (val.encoding != IntegerEncoding::JustBits) { - u32 T = D * C + B; - T ^= A; - T = (A & 0x80) | (T >> 2); - out[outIdx++] = T; - } - } - - // Make sure that each of our values is in the proper range... - for (u32 i = 0; i < nValues; i++) { - assert(out[i] <= 255); - } -} - -static u32 UnquantizeTexelWeight(const IntegerEncodedValue& val) { - u32 bitval = val.bit_value; - u32 bitlen = val.num_bits; - - u32 A = ReplicateBitTo7(bitval & 1); - u32 B = 0, C = 0, D = 0; - - u32 result = 0; - switch (val.encoding) { - case IntegerEncoding::JustBits: - result = FastReplicateTo6(bitval, bitlen); - break; - - case IntegerEncoding::Trit: { - D = val.trit_value; - assert(D < 3); - - switch (bitlen) { - case 0: { - u32 results[3] = {0, 32, 63}; - result = results[D]; - } break; - - case 1: { - C = 50; - } break; - - case 2: { - C = 23; - u32 b = (bitval >> 1) & 1; - B = (b << 6) | (b << 2) | b; - } break; - - case 3: { - C = 11; - u32 cb = (bitval >> 1) & 3; - B = (cb << 5) | cb; - } break; - - default: - assert(false && "Invalid trit encoding for texel weight"); - break; - } - } break; - - case IntegerEncoding::Qus32: { - D = val.qus32_value; - assert(D < 5); - - switch (bitlen) { - case 0: { - u32 results[5] = {0, 16, 32, 47, 63}; - result = results[D]; - } break; - - case 1: { - C = 28; - } break; - - case 2: { - C = 13; - u32 b = (bitval >> 1) & 1; - B = (b << 6) | (b << 1); - } break; - - default: - assert(false && "Invalid quint encoding for texel weight"); - break; - } - } break; - } - - if (val.encoding != IntegerEncoding::JustBits && bitlen > 0) { - // Decode the value... - result = D * C + B; - result ^= A; - result = (A & 0x20) | (result >> 2); - } - - assert(result < 64); - - // Change from [0,63] to [0,64] - if (result > 32) { - result += 1; - } - - return result; -} - -static void UnquantizeTexelWeights(u32 out[2][144], const IntegerEncodedVector& weights, - const TexelWeightParams& params, const u32 blockWidth, - const u32 blockHeight) { - u32 weightIdx = 0; - u32 unquantized[2][144]; - - for (auto itr = weights.begin(); itr != weights.end(); ++itr) { - unquantized[0][weightIdx] = UnquantizeTexelWeight(*itr); - - if (params.m_bDualPlane) { - ++itr; - unquantized[1][weightIdx] = UnquantizeTexelWeight(*itr); - if (itr == weights.end()) { - break; - } - } - - if (++weightIdx >= (params.m_Width * params.m_Height)) - break; - } - - // Do infill if necessary (Section C.2.18) ... - u32 Ds = (1024 + (blockWidth / 2)) / (blockWidth - 1); - u32 Dt = (1024 + (blockHeight / 2)) / (blockHeight - 1); - - const u32 kPlaneScale = params.m_bDualPlane ? 2U : 1U; - for (u32 plane = 0; plane < kPlaneScale; plane++) - for (u32 t = 0; t < blockHeight; t++) - for (u32 s = 0; s < blockWidth; s++) { - u32 cs = Ds * s; - u32 ct = Dt * t; - - u32 gs = (cs * (params.m_Width - 1) + 32) >> 6; - u32 gt = (ct * (params.m_Height - 1) + 32) >> 6; - - u32 js = gs >> 4; - u32 fs = gs & 0xF; - - u32 jt = gt >> 4; - u32 ft = gt & 0x0F; - - u32 w11 = (fs * ft + 8) >> 4; - u32 w10 = ft - w11; - u32 w01 = fs - w11; - u32 w00 = 16 - fs - ft + w11; - - u32 v0 = js + jt * params.m_Width; - -#define FIND_TEXEL(tidx, bidx) \ - u32 p##bidx = 0; \ - do { \ - if ((tidx) < (params.m_Width * params.m_Height)) { \ - p##bidx = unquantized[plane][(tidx)]; \ - } \ - } while (0) - - FIND_TEXEL(v0, 00); - FIND_TEXEL(v0 + 1, 01); - FIND_TEXEL(v0 + params.m_Width, 10); - FIND_TEXEL(v0 + params.m_Width + 1, 11); - -#undef FIND_TEXEL - - out[plane][t * blockWidth + s] = - (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4; - } -} - -// Transfers a bit as described in C.2.14 -static inline void BitTransferSigned(s32& a, s32& b) { - b >>= 1; - b |= a & 0x80; - a >>= 1; - a &= 0x3F; - if (a & 0x20) - a -= 0x40; -} - -// Adds more precision to the blue channel as described -// in C.2.14 -static inline Pixel BlueContract(s32 a, s32 r, s32 g, s32 b) { - return Pixel(static_cast<s16>(a), static_cast<s16>((r + b) >> 1), - static_cast<s16>((g + b) >> 1), static_cast<s16>(b)); -} - -// Partition selection functions as specified in -// C.2.21 -static inline u32 hash52(u32 p) { - p ^= p >> 15; - p -= p << 17; - p += p << 7; - p += p << 4; - p ^= p >> 5; - p += p << 16; - p ^= p >> 7; - p ^= p >> 3; - p ^= p << 6; - p ^= p >> 17; - return p; -} - -static u32 SelectPartition(s32 seed, s32 x, s32 y, s32 z, s32 partitionCount, s32 smallBlock) { - if (1 == partitionCount) - return 0; - - if (smallBlock) { - x <<= 1; - y <<= 1; - z <<= 1; - } - - seed += (partitionCount - 1) * 1024; - - u32 rnum = hash52(static_cast<u32>(seed)); - u8 seed1 = static_cast<u8>(rnum & 0xF); - u8 seed2 = static_cast<u8>((rnum >> 4) & 0xF); - u8 seed3 = static_cast<u8>((rnum >> 8) & 0xF); - u8 seed4 = static_cast<u8>((rnum >> 12) & 0xF); - u8 seed5 = static_cast<u8>((rnum >> 16) & 0xF); - u8 seed6 = static_cast<u8>((rnum >> 20) & 0xF); - u8 seed7 = static_cast<u8>((rnum >> 24) & 0xF); - u8 seed8 = static_cast<u8>((rnum >> 28) & 0xF); - u8 seed9 = static_cast<u8>((rnum >> 18) & 0xF); - u8 seed10 = static_cast<u8>((rnum >> 22) & 0xF); - u8 seed11 = static_cast<u8>((rnum >> 26) & 0xF); - u8 seed12 = static_cast<u8>(((rnum >> 30) | (rnum << 2)) & 0xF); - - seed1 = static_cast<u8>(seed1 * seed1); - seed2 = static_cast<u8>(seed2 * seed2); - seed3 = static_cast<u8>(seed3 * seed3); - seed4 = static_cast<u8>(seed4 * seed4); - seed5 = static_cast<u8>(seed5 * seed5); - seed6 = static_cast<u8>(seed6 * seed6); - seed7 = static_cast<u8>(seed7 * seed7); - seed8 = static_cast<u8>(seed8 * seed8); - seed9 = static_cast<u8>(seed9 * seed9); - seed10 = static_cast<u8>(seed10 * seed10); - seed11 = static_cast<u8>(seed11 * seed11); - seed12 = static_cast<u8>(seed12 * seed12); - - s32 sh1, sh2, sh3; - if (seed & 1) { - sh1 = (seed & 2) ? 4 : 5; - sh2 = (partitionCount == 3) ? 6 : 5; - } else { - sh1 = (partitionCount == 3) ? 6 : 5; - sh2 = (seed & 2) ? 4 : 5; - } - sh3 = (seed & 0x10) ? sh1 : sh2; - - seed1 = static_cast<u8>(seed1 >> sh1); - seed2 = static_cast<u8>(seed2 >> sh2); - seed3 = static_cast<u8>(seed3 >> sh1); - seed4 = static_cast<u8>(seed4 >> sh2); - seed5 = static_cast<u8>(seed5 >> sh1); - seed6 = static_cast<u8>(seed6 >> sh2); - seed7 = static_cast<u8>(seed7 >> sh1); - seed8 = static_cast<u8>(seed8 >> sh2); - seed9 = static_cast<u8>(seed9 >> sh3); - seed10 = static_cast<u8>(seed10 >> sh3); - seed11 = static_cast<u8>(seed11 >> sh3); - seed12 = static_cast<u8>(seed12 >> sh3); - - s32 a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14); - s32 b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10); - s32 c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 6); - s32 d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 2); - - a &= 0x3F; - b &= 0x3F; - c &= 0x3F; - d &= 0x3F; - - if (partitionCount < 4) - d = 0; - if (partitionCount < 3) - c = 0; - - if (a >= b && a >= c && a >= d) - return 0; - else if (b >= c && b >= d) - return 1; - else if (c >= d) - return 2; - return 3; -} - -static inline u32 Select2DPartition(s32 seed, s32 x, s32 y, s32 partitionCount, s32 smallBlock) { - return SelectPartition(seed, x, y, 0, partitionCount, smallBlock); -} - -// Section C.2.14 -static void ComputeEndpos32s(Pixel& ep1, Pixel& ep2, const u32*& colorValues, - u32 colorEndpos32Mode) { -#define READ_UINT_VALUES(N) \ - u32 v[N]; \ - for (u32 i = 0; i < N; i++) { \ - v[i] = *(colorValues++); \ - } - -#define READ_INT_VALUES(N) \ - s32 v[N]; \ - for (u32 i = 0; i < N; i++) { \ - v[i] = static_cast<s32>(*(colorValues++)); \ - } - - switch (colorEndpos32Mode) { - case 0: { - READ_UINT_VALUES(2) - ep1 = Pixel(0xFF, v[0], v[0], v[0]); - ep2 = Pixel(0xFF, v[1], v[1], v[1]); - } break; - - case 1: { - READ_UINT_VALUES(2) - u32 L0 = (v[0] >> 2) | (v[1] & 0xC0); - u32 L1 = std::max(L0 + (v[1] & 0x3F), 0xFFU); - ep1 = Pixel(0xFF, L0, L0, L0); - ep2 = Pixel(0xFF, L1, L1, L1); - } break; - - case 4: { - READ_UINT_VALUES(4) - ep1 = Pixel(v[2], v[0], v[0], v[0]); - ep2 = Pixel(v[3], v[1], v[1], v[1]); - } break; - - case 5: { - READ_INT_VALUES(4) - BitTransferSigned(v[1], v[0]); - BitTransferSigned(v[3], v[2]); - ep1 = Pixel(v[2], v[0], v[0], v[0]); - ep2 = Pixel(v[2] + v[3], v[0] + v[1], v[0] + v[1], v[0] + v[1]); - ep1.ClampByte(); - ep2.ClampByte(); - } break; - - case 6: { - READ_UINT_VALUES(4) - ep1 = Pixel(0xFF, v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8); - ep2 = Pixel(0xFF, v[0], v[1], v[2]); - } break; - - case 8: { - READ_UINT_VALUES(6) - if (v[1] + v[3] + v[5] >= v[0] + v[2] + v[4]) { - ep1 = Pixel(0xFF, v[0], v[2], v[4]); - ep2 = Pixel(0xFF, v[1], v[3], v[5]); - } else { - ep1 = BlueContract(0xFF, v[1], v[3], v[5]); - ep2 = BlueContract(0xFF, v[0], v[2], v[4]); - } - } break; - - case 9: { - READ_INT_VALUES(6) - BitTransferSigned(v[1], v[0]); - BitTransferSigned(v[3], v[2]); - BitTransferSigned(v[5], v[4]); - if (v[1] + v[3] + v[5] >= 0) { - ep1 = Pixel(0xFF, v[0], v[2], v[4]); - ep2 = Pixel(0xFF, v[0] + v[1], v[2] + v[3], v[4] + v[5]); - } else { - ep1 = BlueContract(0xFF, v[0] + v[1], v[2] + v[3], v[4] + v[5]); - ep2 = BlueContract(0xFF, v[0], v[2], v[4]); - } - ep1.ClampByte(); - ep2.ClampByte(); - } break; - - case 10: { - READ_UINT_VALUES(6) - ep1 = Pixel(v[4], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8); - ep2 = Pixel(v[5], v[0], v[1], v[2]); - } break; - - case 12: { - READ_UINT_VALUES(8) - if (v[1] + v[3] + v[5] >= v[0] + v[2] + v[4]) { - ep1 = Pixel(v[6], v[0], v[2], v[4]); - ep2 = Pixel(v[7], v[1], v[3], v[5]); - } else { - ep1 = BlueContract(v[7], v[1], v[3], v[5]); - ep2 = BlueContract(v[6], v[0], v[2], v[4]); - } - } break; - - case 13: { - READ_INT_VALUES(8) - BitTransferSigned(v[1], v[0]); - BitTransferSigned(v[3], v[2]); - BitTransferSigned(v[5], v[4]); - BitTransferSigned(v[7], v[6]); - if (v[1] + v[3] + v[5] >= 0) { - ep1 = Pixel(v[6], v[0], v[2], v[4]); - ep2 = Pixel(v[7] + v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5]); - } else { - ep1 = BlueContract(v[6] + v[7], v[0] + v[1], v[2] + v[3], v[4] + v[5]); - ep2 = BlueContract(v[6], v[0], v[2], v[4]); - } - ep1.ClampByte(); - ep2.ClampByte(); - } break; - - default: - assert(false && "Unsupported color endpoint mode (is it HDR?)"); - break; - } - -#undef READ_UINT_VALUES -#undef READ_INT_VALUES -} - -static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth, - const u32 blockHeight, std::span<u32, 12 * 12> outBuf) { - InputBitStream strm(inBuf); - TexelWeightParams weightParams = DecodeBlockInfo(strm); - - // Was there an error? - if (weightParams.m_bError) { - assert(false && "Invalid block mode"); - FillError(outBuf, blockWidth, blockHeight); - return; - } - - if (weightParams.m_bVoidExtentLDR) { - FillVoidExtentLDR(strm, outBuf, blockWidth, blockHeight); - return; - } - - if (weightParams.m_bVoidExtentHDR) { - assert(false && "HDR void extent blocks are unsupported!"); - FillError(outBuf, blockWidth, blockHeight); - return; - } - - if (weightParams.m_Width > blockWidth) { - assert(false && "Texel weight grid width should be smaller than block width"); - FillError(outBuf, blockWidth, blockHeight); - return; - } - - if (weightParams.m_Height > blockHeight) { - assert(false && "Texel weight grid height should be smaller than block height"); - FillError(outBuf, blockWidth, blockHeight); - return; - } - - // Read num partitions - u32 nPartitions = strm.ReadBits<2>() + 1; - assert(nPartitions <= 4); - - if (nPartitions == 4 && weightParams.m_bDualPlane) { - assert(false && "Dual plane mode is incompatible with four partition blocks"); - FillError(outBuf, blockWidth, blockHeight); - return; - } - - // Based on the number of partitions, read the color endpos32 mode for - // each partition. - - // Determine partitions, partition index, and color endpos32 modes - s32 planeIdx = -1; - u32 partitionIndex; - u32 colorEndpos32Mode[4] = {0, 0, 0, 0}; - - // Define color data. - u8 colorEndpos32Data[16]; - memset(colorEndpos32Data, 0, sizeof(colorEndpos32Data)); - OutputBitStream colorEndpos32Stream(colorEndpos32Data, 16 * 8, 0); - - // Read extra config data... - u32 baseCEM = 0; - if (nPartitions == 1) { - colorEndpos32Mode[0] = strm.ReadBits<4>(); - partitionIndex = 0; - } else { - partitionIndex = strm.ReadBits<10>(); - baseCEM = strm.ReadBits<6>(); - } - u32 baseMode = (baseCEM & 3); - - // Remaining bits are color endpos32 data... - u32 nWeightBits = weightParams.GetPackedBitSize(); - s32 remainingBits = 128 - nWeightBits - static_cast<s32>(strm.GetBitsRead()); - - // Consider extra bits prior to texel data... - u32 extraCEMbits = 0; - if (baseMode) { - switch (nPartitions) { - case 2: - extraCEMbits += 2; - break; - case 3: - extraCEMbits += 5; - break; - case 4: - extraCEMbits += 8; - break; - default: - assert(false); - break; - } - } - remainingBits -= extraCEMbits; - - // Do we have a dual plane situation? - u32 planeSelectorBits = 0; - if (weightParams.m_bDualPlane) { - planeSelectorBits = 2; - } - remainingBits -= planeSelectorBits; - - // Read color data... - u32 colorDataBits = remainingBits; - while (remainingBits > 0) { - u32 nb = std::min(remainingBits, 8); - u32 b = strm.ReadBits(nb); - colorEndpos32Stream.WriteBits(b, nb); - remainingBits -= 8; - } - - // Read the plane selection bits - planeIdx = strm.ReadBits(planeSelectorBits); - - // Read the rest of the CEM - if (baseMode) { - u32 extraCEM = strm.ReadBits(extraCEMbits); - u32 CEM = (extraCEM << 6) | baseCEM; - CEM >>= 2; - - bool C[4] = {0}; - for (u32 i = 0; i < nPartitions; i++) { - C[i] = CEM & 1; - CEM >>= 1; - } - - u8 M[4] = {0}; - for (u32 i = 0; i < nPartitions; i++) { - M[i] = CEM & 3; - CEM >>= 2; - assert(M[i] <= 3); - } - - for (u32 i = 0; i < nPartitions; i++) { - colorEndpos32Mode[i] = baseMode; - if (!(C[i])) - colorEndpos32Mode[i] -= 1; - colorEndpos32Mode[i] <<= 2; - colorEndpos32Mode[i] |= M[i]; - } - } else if (nPartitions > 1) { - u32 CEM = baseCEM >> 2; - for (u32 i = 0; i < nPartitions; i++) { - colorEndpos32Mode[i] = CEM; - } - } - - // Make sure everything up till here is sane. - for (u32 i = 0; i < nPartitions; i++) { - assert(colorEndpos32Mode[i] < 16); - } - assert(strm.GetBitsRead() + weightParams.GetPackedBitSize() == 128); - - // Decode both color data and texel weight data - u32 colorValues[32]; // Four values, two endpos32s, four maximum paritions - DecodeColorValues(colorValues, colorEndpos32Data, colorEndpos32Mode, nPartitions, - colorDataBits); - - Pixel endpos32s[4][2]; - const u32* colorValuesPtr = colorValues; - for (u32 i = 0; i < nPartitions; i++) { - ComputeEndpos32s(endpos32s[i][0], endpos32s[i][1], colorValuesPtr, colorEndpos32Mode[i]); - } - - // Read the texel weight data.. - std::array<u8, 16> texelWeightData; - std::ranges::copy(inBuf, texelWeightData.begin()); - - // Reverse everything - for (u32 i = 0; i < 8; i++) { -// Taken from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits -#define REVERSE_BYTE(b) (((b)*0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32 - u8 a = static_cast<u8>(REVERSE_BYTE(texelWeightData[i])); - u8 b = static_cast<u8>(REVERSE_BYTE(texelWeightData[15 - i])); -#undef REVERSE_BYTE - - texelWeightData[i] = b; - texelWeightData[15 - i] = a; - } - - // Make sure that higher non-texel bits are set to zero - const u32 clearByteStart = (weightParams.GetPackedBitSize() >> 3) + 1; - if (clearByteStart > 0 && clearByteStart <= texelWeightData.size()) { - texelWeightData[clearByteStart - 1] &= - static_cast<u8>((1 << (weightParams.GetPackedBitSize() % 8)) - 1); - std::memset(texelWeightData.data() + clearByteStart, 0, - std::min(16U - clearByteStart, 16U)); - } - - IntegerEncodedVector texelWeightValues; - - InputBitStream weightStream(texelWeightData); - - DecodeIntegerSequence(texelWeightValues, weightStream, weightParams.m_MaxWeight, - weightParams.GetNumWeightValues()); - - // Blocks can be at most 12x12, so we can have as many as 144 weights - u32 weights[2][144]; - UnquantizeTexelWeights(weights, texelWeightValues, weightParams, blockWidth, blockHeight); - - // Now that we have endpos32s and weights, we can s32erpolate and generate - // the proper decoding... - for (u32 j = 0; j < blockHeight; j++) - for (u32 i = 0; i < blockWidth; i++) { - u32 partition = Select2DPartition(partitionIndex, i, j, nPartitions, - (blockHeight * blockWidth) < 32); - assert(partition < nPartitions); - - Pixel p; - for (u32 c = 0; c < 4; c++) { - u32 C0 = endpos32s[partition][0].Component(c); - C0 = ReplicateByteTo16(C0); - u32 C1 = endpos32s[partition][1].Component(c); - C1 = ReplicateByteTo16(C1); - - u32 plane = 0; - if (weightParams.m_bDualPlane && (((planeIdx + 1) & 3) == c)) { - plane = 1; - } - - u32 weight = weights[plane][j * blockWidth + i]; - u32 C = (C0 * (64 - weight) + C1 * weight + 32) / 64; - if (C == 65535) { - p.Component(c) = 255; - } else { - double Cf = static_cast<double>(C); - p.Component(c) = static_cast<u16>(255.0 * (Cf / 65536.0) + 0.5); - } - } - - outBuf[j * blockWidth + i] = p.Pack(); - } -} - -} // namespace ASTCC - -namespace Tegra::Texture::ASTC { - -void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth, - uint32_t block_width, uint32_t block_height, std::span<uint8_t> output) { - u32 block_index = 0; - std::size_t depth_offset = 0; - for (u32 z = 0; z < depth; z++) { - for (u32 y = 0; y < height; y += block_height) { - for (u32 x = 0; x < width; x += block_width) { - const std::span<const u8, 16> blockPtr{data.subspan(block_index * 16, 16)}; - - // Blocks can be at most 12x12 - std::array<u32, 12 * 12> uncompData; - ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData); - - u32 decompWidth = std::min(block_width, width - x); - u32 decompHeight = std::min(block_height, height - y); - - const std::span<u8> outRow = output.subspan(depth_offset + (y * width + x) * 4); - for (u32 jj = 0; jj < decompHeight; jj++) { - std::memcpy(outRow.data() + jj * width * 4, - uncompData.data() + jj * block_width, decompWidth * 4); - } - ++block_index; - } - } - depth_offset += height * width * 4; - } -} - -} // namespace Tegra::Texture::ASTC diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h index 9105119bc..c1c73fda5 100644 --- a/src/video_core/textures/astc.h +++ b/src/video_core/textures/astc.h @@ -4,11 +4,129 @@ #pragma once -#include <cstdint> +#include <bit> +#include "common/common_types.h" namespace Tegra::Texture::ASTC { -void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth, - uint32_t block_width, uint32_t block_height, std::span<uint8_t> output); +enum class IntegerEncoding { JustBits, Quint, Trit }; + +struct IntegerEncodedValue { + constexpr IntegerEncodedValue() = default; + + constexpr IntegerEncodedValue(IntegerEncoding encoding_, u32 num_bits_) + : encoding{encoding_}, num_bits{num_bits_} {} + + constexpr bool MatchesEncoding(const IntegerEncodedValue& other) const { + return encoding == other.encoding && num_bits == other.num_bits; + } + + // Returns the number of bits required to encode num_vals values. + u32 GetBitLength(u32 num_vals) const { + u32 total_bits = num_bits * num_vals; + if (encoding == IntegerEncoding::Trit) { + total_bits += (num_vals * 8 + 4) / 5; + } else if (encoding == IntegerEncoding::Quint) { + total_bits += (num_vals * 7 + 2) / 3; + } + return total_bits; + } + + IntegerEncoding encoding{}; + u32 num_bits = 0; + u32 bit_value = 0; + union { + u32 quint_value = 0; + u32 trit_value; + }; +}; + +// Returns a new instance of this struct that corresponds to the +// can take no more than mav_value values +constexpr IntegerEncodedValue CreateEncoding(u32 mav_value) { + while (mav_value > 0) { + u32 check = mav_value + 1; + + // Is mav_value a power of two? + if (!(check & (check - 1))) { + return IntegerEncodedValue(IntegerEncoding::JustBits, std::popcount(mav_value)); + } + + // Is mav_value of the type 3*2^n - 1? + if ((check % 3 == 0) && !((check / 3) & ((check / 3) - 1))) { + return IntegerEncodedValue(IntegerEncoding::Trit, std::popcount(check / 3 - 1)); + } + + // Is mav_value of the type 5*2^n - 1? + if ((check % 5 == 0) && !((check / 5) & ((check / 5) - 1))) { + return IntegerEncodedValue(IntegerEncoding::Quint, std::popcount(check / 5 - 1)); + } + + // Apparently it can't be represented with a bounded integer sequence... + // just iterate. + mav_value--; + } + return IntegerEncodedValue(IntegerEncoding::JustBits, 0); +} + +constexpr std::array<IntegerEncodedValue, 256> MakeEncodedValues() { + std::array<IntegerEncodedValue, 256> encodings{}; + for (std::size_t i = 0; i < encodings.size(); ++i) { + encodings[i] = CreateEncoding(static_cast<u32>(i)); + } + return encodings; +} + +constexpr std::array<IntegerEncodedValue, 256> EncodingsValues = MakeEncodedValues(); + +// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)] +// is the same as [(num_bits - 1):0] and repeats all the way down. +template <typename IntType> +constexpr IntType Replicate(IntType val, u32 num_bits, u32 to_bit) { + if (num_bits == 0 || to_bit == 0) { + return 0; + } + const IntType v = val & static_cast<IntType>((1 << num_bits) - 1); + IntType res = v; + u32 reslen = num_bits; + while (reslen < to_bit) { + u32 comp = 0; + if (num_bits > to_bit - reslen) { + u32 newshift = to_bit - reslen; + comp = num_bits - newshift; + num_bits = newshift; + } + res = static_cast<IntType>(res << num_bits); + res = static_cast<IntType>(res | (v >> comp)); + reslen += num_bits; + } + return res; +} + +constexpr std::size_t NumReplicateEntries(u32 num_bits) { + return std::size_t(1) << num_bits; +} + +template <typename IntType, u32 num_bits, u32 to_bit> +constexpr auto MakeReplicateTable() { + std::array<IntType, NumReplicateEntries(num_bits)> table{}; + for (IntType value = 0; value < static_cast<IntType>(std::size(table)); ++value) { + table[value] = Replicate(value, num_bits, to_bit); + } + return table; +} + +constexpr auto REPLICATE_BYTE_TO_16_TABLE = MakeReplicateTable<u32, 8, 16>(); +constexpr auto REPLICATE_6_BIT_TO_8_TABLE = MakeReplicateTable<u32, 6, 8>(); +constexpr auto REPLICATE_7_BIT_TO_8_TABLE = MakeReplicateTable<u32, 7, 8>(); +constexpr auto REPLICATE_8_BIT_TO_8_TABLE = MakeReplicateTable<u32, 8, 8>(); + +struct AstcBufferData { + decltype(EncodingsValues) encoding_values = EncodingsValues; + decltype(REPLICATE_6_BIT_TO_8_TABLE) replicate_6_to_8 = REPLICATE_6_BIT_TO_8_TABLE; + decltype(REPLICATE_7_BIT_TO_8_TABLE) replicate_7_to_8 = REPLICATE_7_BIT_TO_8_TABLE; + decltype(REPLICATE_8_BIT_TO_8_TABLE) replicate_8_to_8 = REPLICATE_8_BIT_TO_8_TABLE; + decltype(REPLICATE_BYTE_TO_16_TABLE) replicate_byte_to_16 = REPLICATE_BYTE_TO_16_TABLE; +} constexpr ASTC_BUFFER_DATA; } // namespace Tegra::Texture::ASTC diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 62685a183..3a463d5db 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -17,26 +17,7 @@ #include "video_core/textures/texture.h" namespace Tegra::Texture { - namespace { -/** - * This table represents the internal swizzle of a gob, in format 16 bytes x 2 sector packing. - * Calculates the offset of an (x, y) position within a swizzled texture. - * Taken from the Tegra X1 Technical Reference Manual. pages 1187-1188 - */ -constexpr SwizzleTable MakeSwizzleTableConst() { - SwizzleTable table{}; - for (u32 y = 0; y < table.size(); ++y) { - for (u32 x = 0; x < table[0].size(); ++x) { - table[y][x] = ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 + - (y % 2) * 16 + (x % 16); - } - } - return table; -} - -constexpr SwizzleTable SWIZZLE_TABLE = MakeSwizzleTableConst(); - template <bool TO_LINEAR> void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { @@ -91,10 +72,6 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe } } // Anonymous namespace -SwizzleTable MakeSwizzleTable() { - return SWIZZLE_TABLE; -} - void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index d7cdc81e8..4c14cefbf 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h @@ -23,8 +23,22 @@ constexpr u32 GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_ using SwizzleTable = std::array<std::array<u32, GOB_SIZE_X>, GOB_SIZE_Y>; -/// Returns a z-order swizzle table -SwizzleTable MakeSwizzleTable(); +/** + * This table represents the internal swizzle of a gob, in format 16 bytes x 2 sector packing. + * Calculates the offset of an (x, y) position within a swizzled texture. + * Taken from the Tegra X1 Technical Reference Manual. pages 1187-1188 + */ +constexpr SwizzleTable MakeSwizzleTable() { + SwizzleTable table{}; + for (u32 y = 0; y < table.size(); ++y) { + for (u32 x = 0; x < table[0].size(); ++x) { + table[y][x] = ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 + + (y % 2) * 16 + (x % 16); + } + } + return table; +} +constexpr SwizzleTable SWIZZLE_TABLE = MakeSwizzleTable(); /// Unswizzles a block linear texture into linear memory. void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp index ae5621a7d..a552543ed 100644 --- a/src/video_core/textures/texture.cpp +++ b/src/video_core/textures/texture.cpp @@ -6,7 +6,7 @@ #include <array> #include "common/cityhash.h" -#include "core/settings.h" +#include "common/settings.h" #include "video_core/textures/texture.h" using Tegra::Texture::TICEntry; diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index e1b38c6ac..3b575db4d 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -5,8 +5,8 @@ #include <memory> #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "video_core/renderer_base.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 34d396434..64206b3d2 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -12,7 +12,7 @@ #include <vector> #include "common/assert.h" -#include "core/settings.h" +#include "common/settings.h" #include "video_core/vulkan_common/nsight_aftermath_tracker.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -51,7 +51,7 @@ constexpr std::array REQUIRED_EXTENSIONS{ #ifdef _WIN32 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, #endif -#ifdef __linux__ +#ifdef __unix__ VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, #endif }; @@ -294,6 +294,15 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR }; SetNext(next, bit8_storage); + VkPhysicalDeviceRobustness2FeaturesEXT robustness2{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT, + .pNext = nullptr, + .robustBufferAccess2 = true, + .robustImageAccess2 = true, + .nullDescriptor = true, + }; + SetNext(next, robustness2); + VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT, .pNext = nullptr, diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 2a8b7a907..fa37aa79a 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -62,7 +62,7 @@ public: : memory{std::move(memory_)}, allocation_size{allocation_size_}, property_flags{properties}, shifted_memory_type{1U << type} {} -#if defined(_WIN32) || defined(__linux__) +#if defined(_WIN32) || defined(__unix__) ~MemoryAllocation() { if (owning_opengl_handle != 0) { glDeleteMemoryObjectsEXT(1, &owning_opengl_handle); @@ -114,7 +114,7 @@ public: } return owning_opengl_handle; } -#elif __linux__ +#elif __unix__ [[nodiscard]] u32 ExportOpenGLHandle() { if (!owning_opengl_handle) { glCreateMemoryObjectsEXT(1, &owning_opengl_handle); @@ -165,7 +165,7 @@ private: const u32 shifted_memory_type; ///< Shifted Vulkan memory type. std::vector<Range> commits; ///< All commit ranges done from this allocation. std::span<u8> memory_mapped_span; ///< Memory mapped span. Empty if not queried before. -#if defined(_WIN32) || defined(__linux__) +#if defined(_WIN32) || defined(__unix__) u32 owning_opengl_handle{}; ///< Owning OpenGL memory object handle. #endif }; @@ -249,7 +249,7 @@ void MemoryAllocator::AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u6 .pNext = nullptr, #ifdef _WIN32 .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, -#elif __linux__ +#elif __unix__ .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, #else .handleTypes = 0, diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index b025ced1c..cc0790e07 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(yuzu applets/profile_select.h applets/software_keyboard.cpp applets/software_keyboard.h + applets/software_keyboard.ui applets/web_browser.cpp applets/web_browser.h bootmanager.cpp @@ -143,6 +144,9 @@ add_executable(yuzu uisettings.h util/limitable_input_dialog.cpp util/limitable_input_dialog.h + util/overlay_dialog.cpp + util/overlay_dialog.h + util/overlay_dialog.ui util/sequence_dialog/sequence_dialog.cpp util/sequence_dialog/sequence_dialog.h util/url_request_interceptor.cpp diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index b92cd6886..836d90fda 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -16,6 +16,7 @@ #include "yuzu/applets/controller.h" #include "yuzu/configuration/configure_input.h" #include "yuzu/configuration/configure_input_profile_dialog.h" +#include "yuzu/configuration/configure_motion_touch.h" #include "yuzu/configuration/configure_vibration.h" #include "yuzu/configuration/input_profiles.h" #include "yuzu/main.h" @@ -206,6 +207,9 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( connect(ui->vibrationButton, &QPushButton::clicked, this, &QtControllerSelectorDialog::CallConfigureVibrationDialog); + connect(ui->motionButton, &QPushButton::clicked, this, + &QtControllerSelectorDialog::CallConfigureMotionTouchDialog); + connect(ui->inputConfigButton, &QPushButton::clicked, this, &QtControllerSelectorDialog::CallConfigureInputProfileDialog); @@ -276,6 +280,18 @@ void QtControllerSelectorDialog::CallConfigureVibrationDialog() { } } +void QtControllerSelectorDialog::CallConfigureMotionTouchDialog() { + ConfigureMotionTouch dialog(this, input_subsystem); + + dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | + Qt::WindowSystemMenuHint); + dialog.setWindowModality(Qt::WindowModal); + + if (dialog.exec() == QDialog::Accepted) { + dialog.ApplyConfiguration(); + } +} + void QtControllerSelectorDialog::CallConfigureInputProfileDialog() { ConfigureInputProfileDialog dialog(this, input_subsystem, input_profiles.get()); diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h index 3518eed56..9b57aea1a 100644 --- a/src/yuzu/applets/controller.h +++ b/src/yuzu/applets/controller.h @@ -51,6 +51,9 @@ private: // Initializes the "Configure Vibration" Dialog. void CallConfigureVibrationDialog(); + // Initializes the "Configure Motion / Touch" Dialog. + void CallConfigureMotionTouchDialog(); + // Initializes the "Create Input Profile" Dialog. void CallConfigureInputProfileDialog(); diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/error.cpp index 8ee03ddb3..085688cd4 100644 --- a/src/yuzu/applets/error.cpp +++ b/src/yuzu/applets/error.cpp @@ -19,11 +19,11 @@ QtErrorDisplay::~QtErrorDisplay() = default; void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const { callback = std::move(finished); emit MainWindowDisplayError( - tr("An error has occurred.\nPlease try again or contact the developer of the " - "software.\n\nError Code: %1-%2 (0x%3)") + tr("Error Code: %1-%2 (0x%3)") .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) .arg(error.description, 4, 10, QChar::fromLatin1('0')) - .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); + .arg(error.raw, 8, 16, QChar::fromLatin1('0')), + tr("An error has occurred.\nPlease try again or contact the developer of the software.")); } void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, @@ -32,13 +32,14 @@ void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::secon const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); emit MainWindowDisplayError( - tr("An error occurred on %1 at %2.\nPlease try again or contact the " - "developer of the software.\n\nError Code: %3-%4 (0x%5)") - .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy"))) - .arg(date_time.toString(QStringLiteral("h:mm:ss A"))) + tr("Error Code: %1-%2 (0x%3)") .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) .arg(error.description, 4, 10, QChar::fromLatin1('0')) - .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); + .arg(error.raw, 8, 16, QChar::fromLatin1('0')), + tr("An error occurred on %1 at %2.\nPlease try again or contact the developer of the " + "software.") + .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy"))) + .arg(date_time.toString(QStringLiteral("h:mm:ss A")))); } void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_text, @@ -46,10 +47,11 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te std::function<void()> finished) const { callback = std::move(finished); emit MainWindowDisplayError( - tr("An error has occurred.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5") + tr("Error Code: %1-%2 (0x%3)") .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) .arg(error.description, 4, 10, QChar::fromLatin1('0')) - .arg(error.raw, 8, 16, QChar::fromLatin1('0')) + .arg(error.raw, 8, 16, QChar::fromLatin1('0')), + tr("An error has occurred.\n\n%1\n\n%2") .arg(QString::fromStdString(dialog_text)) .arg(QString::fromStdString(fullscreen_text))); } diff --git a/src/yuzu/applets/error.h b/src/yuzu/applets/error.h index b0932d895..8bd895a32 100644 --- a/src/yuzu/applets/error.h +++ b/src/yuzu/applets/error.h @@ -24,7 +24,7 @@ public: std::function<void()> finished) const override; signals: - void MainWindowDisplayError(QString error) const; + void MainWindowDisplayError(QString error_code, QString error_text) const; private: void MainWindowFinishedError(); diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index ab8cfd8ee..fd3368479 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -1,153 +1,1641 @@ -// Copyright 2018 yuzu Emulator Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <algorithm> -#include <mutex> -#include <QDialogButtonBox> -#include <QFont> -#include <QLabel> -#include <QLineEdit> -#include <QVBoxLayout> -#include "core/hle/lock.h" +#include <QCursor> +#include <QKeyEvent> +#include <QScreen> + +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/input_interpreter.h" +#include "ui_software_keyboard.h" #include "yuzu/applets/software_keyboard.h" #include "yuzu/main.h" +#include "yuzu/util/overlay_dialog.h" + +namespace { + +using namespace Service::AM::Applets; + +constexpr float BASE_HEADER_FONT_SIZE = 23.0f; +constexpr float BASE_SUB_FONT_SIZE = 17.0f; +constexpr float BASE_EDITOR_FONT_SIZE = 26.0f; +constexpr float BASE_CHAR_BUTTON_FONT_SIZE = 28.0f; +constexpr float BASE_LABEL_BUTTON_FONT_SIZE = 18.0f; +constexpr float BASE_ICON_BUTTON_SIZE = 36.0f; +[[maybe_unused]] constexpr float BASE_WIDTH = 1280.0f; +constexpr float BASE_HEIGHT = 720.0f; + +} // Anonymous namespace + +QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( + QWidget* parent, Core::System& system_, bool is_inline_, + Core::Frontend::KeyboardInitializeParameters initialize_parameters_) + : QDialog(parent), ui{std::make_unique<Ui::QtSoftwareKeyboardDialog>()}, system{system_}, + is_inline{is_inline_}, initialize_parameters{std::move(initialize_parameters_)} { + ui->setupUi(this); + + setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowTitleHint | + Qt::WindowSystemMenuHint | Qt::CustomizeWindowHint); + setWindowModality(Qt::WindowModal); + setAttribute(Qt::WA_DeleteOnClose); + setAttribute(Qt::WA_TranslucentBackground); + + keyboard_buttons = {{ + {{ + { + ui->button_1, + ui->button_2, + ui->button_3, + ui->button_4, + ui->button_5, + ui->button_6, + ui->button_7, + ui->button_8, + ui->button_9, + ui->button_0, + ui->button_minus, + ui->button_backspace, + }, + { + ui->button_q, + ui->button_w, + ui->button_e, + ui->button_r, + ui->button_t, + ui->button_y, + ui->button_u, + ui->button_i, + ui->button_o, + ui->button_p, + ui->button_slash, + ui->button_return, + }, + { + ui->button_a, + ui->button_s, + ui->button_d, + ui->button_f, + ui->button_g, + ui->button_h, + ui->button_j, + ui->button_k, + ui->button_l, + ui->button_colon, + ui->button_apostrophe, + ui->button_return, + }, + { + ui->button_z, + ui->button_x, + ui->button_c, + ui->button_v, + ui->button_b, + ui->button_n, + ui->button_m, + ui->button_comma, + ui->button_dot, + ui->button_question, + ui->button_exclamation, + ui->button_ok, + }, + { + ui->button_shift, + ui->button_shift, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_ok, + }, + }}, + {{ + { + ui->button_hash, + ui->button_left_bracket, + ui->button_right_bracket, + ui->button_dollar, + ui->button_percent, + ui->button_circumflex, + ui->button_ampersand, + ui->button_asterisk, + ui->button_left_parenthesis, + ui->button_right_parenthesis, + ui->button_underscore, + ui->button_backspace_shift, + }, + { + ui->button_q_shift, + ui->button_w_shift, + ui->button_e_shift, + ui->button_r_shift, + ui->button_t_shift, + ui->button_y_shift, + ui->button_u_shift, + ui->button_i_shift, + ui->button_o_shift, + ui->button_p_shift, + ui->button_at, + ui->button_return_shift, + }, + { + ui->button_a_shift, + ui->button_s_shift, + ui->button_d_shift, + ui->button_f_shift, + ui->button_g_shift, + ui->button_h_shift, + ui->button_j_shift, + ui->button_k_shift, + ui->button_l_shift, + ui->button_semicolon, + ui->button_quotation, + ui->button_return_shift, + }, + { + ui->button_z_shift, + ui->button_x_shift, + ui->button_c_shift, + ui->button_v_shift, + ui->button_b_shift, + ui->button_n_shift, + ui->button_m_shift, + ui->button_less_than, + ui->button_greater_than, + ui->button_plus, + ui->button_equal, + ui->button_ok_shift, + }, + { + ui->button_shift_shift, + ui->button_shift_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_ok_shift, + }, + }}, + }}; -QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator( - Core::Frontend::SoftwareKeyboardParameters parameters) - : parameters(std::move(parameters)) {} + numberpad_buttons = {{ + { + ui->button_1_num, + ui->button_2_num, + ui->button_3_num, + ui->button_backspace_num, + }, + { + ui->button_4_num, + ui->button_5_num, + ui->button_6_num, + ui->button_ok_num, + }, + { + ui->button_7_num, + ui->button_8_num, + ui->button_9_num, + ui->button_ok_num, + }, + { + nullptr, + ui->button_0_num, + nullptr, + ui->button_ok_num, + }, + }}; -QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const { - if (input.size() > static_cast<s64>(parameters.max_length)) { - return Invalid; + all_buttons = { + ui->button_1, + ui->button_2, + ui->button_3, + ui->button_4, + ui->button_5, + ui->button_6, + ui->button_7, + ui->button_8, + ui->button_9, + ui->button_0, + ui->button_minus, + ui->button_backspace, + ui->button_q, + ui->button_w, + ui->button_e, + ui->button_r, + ui->button_t, + ui->button_y, + ui->button_u, + ui->button_i, + ui->button_o, + ui->button_p, + ui->button_slash, + ui->button_return, + ui->button_a, + ui->button_s, + ui->button_d, + ui->button_f, + ui->button_g, + ui->button_h, + ui->button_j, + ui->button_k, + ui->button_l, + ui->button_colon, + ui->button_apostrophe, + ui->button_z, + ui->button_x, + ui->button_c, + ui->button_v, + ui->button_b, + ui->button_n, + ui->button_m, + ui->button_comma, + ui->button_dot, + ui->button_question, + ui->button_exclamation, + ui->button_ok, + ui->button_shift, + ui->button_space, + ui->button_hash, + ui->button_left_bracket, + ui->button_right_bracket, + ui->button_dollar, + ui->button_percent, + ui->button_circumflex, + ui->button_ampersand, + ui->button_asterisk, + ui->button_left_parenthesis, + ui->button_right_parenthesis, + ui->button_underscore, + ui->button_backspace_shift, + ui->button_q_shift, + ui->button_w_shift, + ui->button_e_shift, + ui->button_r_shift, + ui->button_t_shift, + ui->button_y_shift, + ui->button_u_shift, + ui->button_i_shift, + ui->button_o_shift, + ui->button_p_shift, + ui->button_at, + ui->button_return_shift, + ui->button_a_shift, + ui->button_s_shift, + ui->button_d_shift, + ui->button_f_shift, + ui->button_g_shift, + ui->button_h_shift, + ui->button_j_shift, + ui->button_k_shift, + ui->button_l_shift, + ui->button_semicolon, + ui->button_quotation, + ui->button_z_shift, + ui->button_x_shift, + ui->button_c_shift, + ui->button_v_shift, + ui->button_b_shift, + ui->button_n_shift, + ui->button_m_shift, + ui->button_less_than, + ui->button_greater_than, + ui->button_plus, + ui->button_equal, + ui->button_ok_shift, + ui->button_shift_shift, + ui->button_space_shift, + ui->button_1_num, + ui->button_2_num, + ui->button_3_num, + ui->button_backspace_num, + ui->button_4_num, + ui->button_5_num, + ui->button_6_num, + ui->button_ok_num, + ui->button_7_num, + ui->button_8_num, + ui->button_9_num, + ui->button_0_num, + }; + + SetupMouseHover(); + + if (!initialize_parameters.ok_text.empty()) { + ui->button_ok->setText(QString::fromStdU16String(initialize_parameters.ok_text)); } - if (parameters.disable_space && input.contains(QLatin1Char{' '})) { - return Invalid; + + ui->label_header->setText(QString::fromStdU16String(initialize_parameters.header_text)); + ui->label_sub->setText(QString::fromStdU16String(initialize_parameters.sub_text)); + + current_text = initialize_parameters.initial_text; + cursor_position = initialize_parameters.initial_cursor_position; + + SetTextDrawType(); + + for (auto* button : all_buttons) { + connect(button, &QPushButton::clicked, this, [this, button](bool) { + if (is_inline) { + InlineKeyboardButtonClicked(button); + } else { + NormalKeyboardButtonClicked(button); + } + }); } - if (parameters.disable_address && input.contains(QLatin1Char{'@'})) { - return Invalid; + + // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend + if (system.IsPoweredOn()) { + input_interpreter = std::make_unique<InputInterpreter>(system); } - if (parameters.disable_percent && input.contains(QLatin1Char{'%'})) { - return Invalid; +} + +QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() { + StopInputThread(); +} + +void QtSoftwareKeyboardDialog::ShowNormalKeyboard(QPoint pos, QSize size) { + if (isVisible()) { + return; } - if (parameters.disable_slash && - (input.contains(QLatin1Char{'/'}) || input.contains(QLatin1Char{'\\'}))) { - return Invalid; + + MoveAndResizeWindow(pos, size); + + SetKeyboardType(); + SetPasswordMode(); + SetControllerImage(); + DisableKeyboardButtons(); + SetBackspaceOkEnabled(); + + open(); +} + +void QtSoftwareKeyboardDialog::ShowTextCheckDialog( + Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) { + switch (text_check_result) { + case SwkbdTextCheckResult::Success: + case SwkbdTextCheckResult::Silent: + default: + break; + case SwkbdTextCheckResult::Failure: { + StopInputThread(); + + OverlayDialog dialog(this, system, QString{}, QString::fromStdU16String(text_check_message), + QString{}, tr("OK"), Qt::AlignCenter); + dialog.exec(); + + StartInputThread(); + break; } - if (parameters.disable_number && - std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) { - return Invalid; + case SwkbdTextCheckResult::Confirm: { + StopInputThread(); + + OverlayDialog dialog(this, system, QString{}, QString::fromStdU16String(text_check_message), + tr("Cancel"), tr("OK"), Qt::AlignCenter); + if (dialog.exec() == QDialog::Accepted) { + emit SubmitNormalText(SwkbdResult::Ok, current_text); + break; + } + + StartInputThread(); + break; } + } +} + +void QtSoftwareKeyboardDialog::ShowInlineKeyboard( + Core::Frontend::InlineAppearParameters appear_parameters, QPoint pos, QSize size) { + MoveAndResizeWindow(pos, size); + + ui->topOSK->setStyleSheet(QStringLiteral("background: rgba(0, 0, 0, 0);")); + + ui->headerOSK->hide(); + ui->subOSK->hide(); + ui->inputOSK->hide(); + ui->charactersOSK->hide(); + ui->inputBoxOSK->hide(); + ui->charactersBoxOSK->hide(); + + initialize_parameters.max_text_length = appear_parameters.max_text_length; + initialize_parameters.min_text_length = appear_parameters.min_text_length; + initialize_parameters.type = appear_parameters.type; + initialize_parameters.key_disable_flags = appear_parameters.key_disable_flags; + initialize_parameters.enable_backspace_button = appear_parameters.enable_backspace_button; + initialize_parameters.enable_return_button = appear_parameters.enable_return_button; + initialize_parameters.disable_cancel_button = initialize_parameters.disable_cancel_button; - if (parameters.disable_download_code && std::any_of(input.begin(), input.end(), [](QChar c) { - return c == QLatin1Char{'O'} || c == QLatin1Char{'I'}; - })) { - return Invalid; + SetKeyboardType(); + SetControllerImage(); + DisableKeyboardButtons(); + SetBackspaceOkEnabled(); + + open(); +} + +void QtSoftwareKeyboardDialog::HideInlineKeyboard() { + StopInputThread(); + QDialog::hide(); +} + +void QtSoftwareKeyboardDialog::InlineTextChanged( + Core::Frontend::InlineTextParameters text_parameters) { + current_text = text_parameters.input_text; + cursor_position = text_parameters.cursor_position; + + SetBackspaceOkEnabled(); +} + +void QtSoftwareKeyboardDialog::ExitKeyboard() { + StopInputThread(); + QDialog::done(QDialog::Accepted); +} + +void QtSoftwareKeyboardDialog::open() { + QDialog::open(); + + row = 0; + column = 0; + + const auto* const curr_button = + keyboard_buttons[static_cast<int>(bottom_osk_index)][row][column]; + + // This is a workaround for setFocus() randomly not showing focus in the UI + QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); + + StartInputThread(); +} + +void QtSoftwareKeyboardDialog::reject() { + // Pressing the ESC key in a dialog calls QDialog::reject(). + // We will override this behavior to the "Cancel" action on the software keyboard. + if (is_inline) { + emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); + } else { + emit SubmitNormalText(SwkbdResult::Cancel, current_text); } +} + +void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) { + if (!is_inline) { + QDialog::keyPressEvent(event); + return; + } + + const auto entered_key = event->key(); - return Acceptable; + switch (entered_key) { + case Qt::Key_Escape: + QDialog::keyPressEvent(event); + return; + case Qt::Key_Backspace: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_backspace->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_backspace_shift->click(); + break; + case BottomOSKIndex::NumberPad: + ui->button_backspace_num->click(); + break; + default: + break; + } + return; + case Qt::Key_Return: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_ok->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_ok_shift->click(); + break; + case BottomOSKIndex::NumberPad: + ui->button_ok_num->click(); + break; + default: + break; + } + return; + case Qt::Key_Left: + MoveTextCursorDirection(Direction::Left); + return; + case Qt::Key_Right: + MoveTextCursorDirection(Direction::Right); + return; + default: + break; + } + + const auto entered_text = event->text(); + + if (entered_text.isEmpty()) { + return; + } + + InlineTextInsertString(entered_text.toStdU16String()); } -QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( - QWidget* parent, Core::Frontend::SoftwareKeyboardParameters parameters_) - : QDialog(parent), parameters(std::move(parameters_)) { - layout = new QVBoxLayout; - - header_label = new QLabel(QString::fromStdU16String(parameters.header_text)); - header_label->setFont({header_label->font().family(), 11, QFont::Bold}); - if (header_label->text().isEmpty()) - header_label->setText(tr("Enter text:")); - - sub_label = new QLabel(QString::fromStdU16String(parameters.sub_text)); - sub_label->setFont({sub_label->font().family(), sub_label->font().pointSize(), - sub_label->font().weight(), true}); - sub_label->setHidden(parameters.sub_text.empty()); - - guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text)); - guide_label->setHidden(parameters.guide_text.empty()); - - length_label = new QLabel(QStringLiteral("0/%1").arg(parameters.max_length)); - length_label->setAlignment(Qt::AlignRight); - length_label->setFont({length_label->font().family(), 8}); - - line_edit = new QLineEdit; - line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters)); - line_edit->setMaxLength(static_cast<int>(parameters.max_length)); - line_edit->setText(QString::fromStdU16String(parameters.initial_text)); - line_edit->setCursorPosition( - parameters.cursor_at_beginning ? 0 : static_cast<int>(parameters.initial_text.size())); - line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal); - - connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) { - length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length)); - }); - - buttons = new QDialogButtonBox(QDialogButtonBox::Cancel); - if (parameters.submit_text.empty()) { - buttons->addButton(QDialogButtonBox::Ok); +void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) { + QDialog::move(pos); + QDialog::resize(size); + + // High DPI + const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f; + + RescaleKeyboardElements(size.width(), size.height(), dpi_scale); +} + +void QtSoftwareKeyboardDialog::RescaleKeyboardElements(float width, float height, float dpi_scale) { + const auto header_font_size = BASE_HEADER_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; + const auto sub_font_size = BASE_SUB_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; + const auto editor_font_size = BASE_EDITOR_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; + const auto char_button_font_size = + BASE_CHAR_BUTTON_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; + const auto label_button_font_size = + BASE_LABEL_BUTTON_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; + + QFont header_font(QStringLiteral("MS Shell Dlg 2"), header_font_size, QFont::Normal); + QFont sub_font(QStringLiteral("MS Shell Dlg 2"), sub_font_size, QFont::Normal); + QFont editor_font(QStringLiteral("MS Shell Dlg 2"), editor_font_size, QFont::Normal); + QFont char_button_font(QStringLiteral("MS Shell Dlg 2"), char_button_font_size, QFont::Normal); + QFont label_button_font(QStringLiteral("MS Shell Dlg 2"), label_button_font_size, + QFont::Normal); + + ui->label_header->setFont(header_font); + ui->label_sub->setFont(sub_font); + ui->line_edit_osk->setFont(editor_font); + ui->text_edit_osk->setFont(editor_font); + ui->label_characters->setFont(sub_font); + ui->label_characters_box->setFont(sub_font); + + ui->label_shift->setFont(label_button_font); + ui->label_shift_shift->setFont(label_button_font); + ui->label_cancel->setFont(label_button_font); + ui->label_cancel_shift->setFont(label_button_font); + ui->label_cancel_num->setFont(label_button_font); + ui->label_enter->setFont(label_button_font); + ui->label_enter_shift->setFont(label_button_font); + ui->label_enter_num->setFont(label_button_font); + + for (auto* button : all_buttons) { + if (button == ui->button_return || button == ui->button_return_shift) { + button->setFont(label_button_font); + continue; + } + + if (button == ui->button_space || button == ui->button_space_shift) { + button->setFont(label_button_font); + continue; + } + + if (button == ui->button_shift || button == ui->button_shift_shift) { + button->setFont(label_button_font); + button->setIconSize(QSize(BASE_ICON_BUTTON_SIZE, BASE_ICON_BUTTON_SIZE) * + (height / BASE_HEIGHT)); + continue; + } + + if (button == ui->button_backspace || button == ui->button_backspace_shift || + button == ui->button_backspace_num) { + button->setFont(label_button_font); + button->setIconSize(QSize(BASE_ICON_BUTTON_SIZE, BASE_ICON_BUTTON_SIZE) * + (height / BASE_HEIGHT)); + continue; + } + + if (button == ui->button_ok || button == ui->button_ok_shift || + button == ui->button_ok_num) { + button->setFont(label_button_font); + continue; + } + + button->setFont(char_button_font); + } +} + +void QtSoftwareKeyboardDialog::SetKeyboardType() { + switch (initialize_parameters.type) { + case SwkbdType::Normal: + case SwkbdType::Qwerty: + case SwkbdType::Unknown3: + case SwkbdType::Latin: + case SwkbdType::SimplifiedChinese: + case SwkbdType::TraditionalChinese: + case SwkbdType::Korean: + default: { + bottom_osk_index = BottomOSKIndex::LowerCase; + ui->bottomOSK->setCurrentIndex(static_cast<int>(bottom_osk_index)); + + ui->verticalLayout_2->setStretch(0, 320); + ui->verticalLayout_2->setStretch(1, 400); + + ui->gridLineOSK->setRowStretch(5, 94); + ui->gridBoxOSK->setRowStretch(2, 81); + break; + } + case SwkbdType::NumberPad: { + bottom_osk_index = BottomOSKIndex::NumberPad; + ui->bottomOSK->setCurrentIndex(static_cast<int>(bottom_osk_index)); + + ui->verticalLayout_2->setStretch(0, 370); + ui->verticalLayout_2->setStretch(1, 350); + + ui->gridLineOSK->setRowStretch(5, 144); + ui->gridBoxOSK->setRowStretch(2, 131); + break; + } + } +} + +void QtSoftwareKeyboardDialog::SetPasswordMode() { + switch (initialize_parameters.password_mode) { + case SwkbdPasswordMode::Disabled: + default: + ui->line_edit_osk->setEchoMode(QLineEdit::Normal); + break; + case SwkbdPasswordMode::Enabled: + ui->line_edit_osk->setEchoMode(QLineEdit::Password); + break; + } +} + +void QtSoftwareKeyboardDialog::SetTextDrawType() { + switch (initialize_parameters.text_draw_type) { + case SwkbdTextDrawType::Line: + case SwkbdTextDrawType::DownloadCode: { + ui->topOSK->setCurrentIndex(0); + + if (initialize_parameters.max_text_length <= 10) { + ui->gridLineOSK->setColumnStretch(0, 390); + ui->gridLineOSK->setColumnStretch(1, 500); + ui->gridLineOSK->setColumnStretch(2, 390); + } else { + ui->gridLineOSK->setColumnStretch(0, 130); + ui->gridLineOSK->setColumnStretch(1, 1020); + ui->gridLineOSK->setColumnStretch(2, 130); + } + + if (is_inline) { + return; + } + + connect(ui->line_edit_osk, &QLineEdit::textChanged, [this](const QString& changed_string) { + const auto is_valid = ValidateInputText(changed_string); + + const auto text_length = static_cast<u32>(changed_string.length()); + + ui->label_characters->setText(QStringLiteral("%1/%2") + .arg(text_length) + .arg(initialize_parameters.max_text_length)); + + ui->button_ok->setEnabled(is_valid); + ui->button_ok_shift->setEnabled(is_valid); + ui->button_ok_num->setEnabled(is_valid); + + ui->line_edit_osk->setFocus(); + }); + + connect(ui->line_edit_osk, &QLineEdit::cursorPositionChanged, + [this](int old_cursor_position, int new_cursor_position) { + ui->button_backspace->setEnabled( + initialize_parameters.enable_backspace_button && new_cursor_position > 0); + ui->button_backspace_shift->setEnabled( + initialize_parameters.enable_backspace_button && new_cursor_position > 0); + ui->button_backspace_num->setEnabled( + initialize_parameters.enable_backspace_button && new_cursor_position > 0); + + ui->line_edit_osk->setFocus(); + }); + + connect(ui->line_edit_osk, &QLineEdit::returnPressed, [this] { + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_ok->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_ok_shift->click(); + break; + case BottomOSKIndex::NumberPad: + ui->button_ok_num->click(); + break; + default: + break; + } + }); + + ui->line_edit_osk->setPlaceholderText( + QString::fromStdU16String(initialize_parameters.guide_text)); + ui->line_edit_osk->setText(QString::fromStdU16String(initialize_parameters.initial_text)); + ui->line_edit_osk->setMaxLength(initialize_parameters.max_text_length); + ui->line_edit_osk->setCursorPosition(initialize_parameters.initial_cursor_position); + + ui->label_characters->setText(QStringLiteral("%1/%2") + .arg(initialize_parameters.initial_text.size()) + .arg(initialize_parameters.max_text_length)); + break; + } + case SwkbdTextDrawType::Box: + default: { + ui->topOSK->setCurrentIndex(1); + + if (is_inline) { + return; + } + + connect(ui->text_edit_osk, &QTextEdit::textChanged, [this] { + if (static_cast<u32>(ui->text_edit_osk->toPlainText().length()) > + initialize_parameters.max_text_length) { + auto text_cursor = ui->text_edit_osk->textCursor(); + ui->text_edit_osk->setTextCursor(text_cursor); + text_cursor.deletePreviousChar(); + } + + const auto is_valid = ValidateInputText(ui->text_edit_osk->toPlainText()); + + const auto text_length = static_cast<u32>(ui->text_edit_osk->toPlainText().length()); + + ui->label_characters_box->setText(QStringLiteral("%1/%2") + .arg(text_length) + .arg(initialize_parameters.max_text_length)); + + ui->button_ok->setEnabled(is_valid); + ui->button_ok_shift->setEnabled(is_valid); + ui->button_ok_num->setEnabled(is_valid); + + ui->text_edit_osk->setFocus(); + }); + + connect(ui->text_edit_osk, &QTextEdit::cursorPositionChanged, [this] { + const auto new_cursor_position = ui->text_edit_osk->textCursor().position(); + + ui->button_backspace->setEnabled(initialize_parameters.enable_backspace_button && + new_cursor_position > 0); + ui->button_backspace_shift->setEnabled(initialize_parameters.enable_backspace_button && + new_cursor_position > 0); + ui->button_backspace_num->setEnabled(initialize_parameters.enable_backspace_button && + new_cursor_position > 0); + + ui->text_edit_osk->setFocus(); + }); + + ui->text_edit_osk->setPlaceholderText( + QString::fromStdU16String(initialize_parameters.guide_text)); + ui->text_edit_osk->setText(QString::fromStdU16String(initialize_parameters.initial_text)); + ui->text_edit_osk->moveCursor(initialize_parameters.initial_cursor_position == 0 + ? QTextCursor::Start + : QTextCursor::End); + + ui->label_characters_box->setText(QStringLiteral("%1/%2") + .arg(initialize_parameters.initial_text.size()) + .arg(initialize_parameters.max_text_length)); + break; + } + } +} + +void QtSoftwareKeyboardDialog::SetControllerImage() { + const auto controller_type = Settings::values.players.GetValue()[8].connected + ? Settings::values.players.GetValue()[8].controller_type + : Settings::values.players.GetValue()[0].controller_type; + + const QString theme = [] { + if (QIcon::themeName().contains(QStringLiteral("dark")) || + QIcon::themeName().contains(QStringLiteral("midnight"))) { + return QStringLiteral("_dark"); + } else { + return QString{}; + } + }(); + + switch (controller_type) { + case Settings::ControllerType::ProController: + case Settings::ControllerType::GameCube: + ui->icon_controller->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); + ui->icon_controller_shift->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); + ui->icon_controller_num->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); + break; + case Settings::ControllerType::DualJoyconDetached: + ui->icon_controller->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); + ui->icon_controller_shift->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); + ui->icon_controller_num->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); + break; + case Settings::ControllerType::LeftJoycon: + ui->icon_controller->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") + .arg(theme)); + ui->icon_controller_shift->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") + .arg(theme)); + ui->icon_controller_num->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") + .arg(theme)); + break; + case Settings::ControllerType::RightJoycon: + ui->icon_controller->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") + .arg(theme)); + ui->icon_controller_shift->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") + .arg(theme)); + ui->icon_controller_num->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") + .arg(theme)); + break; + case Settings::ControllerType::Handheld: + ui->icon_controller->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme)); + ui->icon_controller_shift->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme)); + ui->icon_controller_num->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme)); + break; + default: + break; + } +} + +void QtSoftwareKeyboardDialog::DisableKeyboardButtons() { + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + case BottomOSKIndex::UpperCase: + default: { + for (const auto& keys : keyboard_buttons) { + for (const auto& rows : keys) { + for (auto* button : rows) { + if (!button) { + continue; + } + + button->setEnabled(true); + } + } + } + + const auto& key_disable_flags = initialize_parameters.key_disable_flags; + + ui->button_space->setDisabled(key_disable_flags.space); + ui->button_space_shift->setDisabled(key_disable_flags.space); + + ui->button_at->setDisabled(key_disable_flags.at || key_disable_flags.username); + + ui->button_percent->setDisabled(key_disable_flags.percent || key_disable_flags.username); + + ui->button_slash->setDisabled(key_disable_flags.slash); + + ui->button_1->setDisabled(key_disable_flags.numbers); + ui->button_2->setDisabled(key_disable_flags.numbers); + ui->button_3->setDisabled(key_disable_flags.numbers); + ui->button_4->setDisabled(key_disable_flags.numbers); + ui->button_5->setDisabled(key_disable_flags.numbers); + ui->button_6->setDisabled(key_disable_flags.numbers); + ui->button_7->setDisabled(key_disable_flags.numbers); + ui->button_8->setDisabled(key_disable_flags.numbers); + ui->button_9->setDisabled(key_disable_flags.numbers); + ui->button_0->setDisabled(key_disable_flags.numbers); + + ui->button_return->setEnabled(initialize_parameters.enable_return_button); + ui->button_return_shift->setEnabled(initialize_parameters.enable_return_button); + break; + } + case BottomOSKIndex::NumberPad: { + for (const auto& rows : numberpad_buttons) { + for (auto* button : rows) { + if (!button) { + continue; + } + + button->setEnabled(true); + } + } + break; + } + } +} + +void QtSoftwareKeyboardDialog::SetBackspaceOkEnabled() { + if (is_inline) { + ui->button_ok->setEnabled(current_text.size() >= initialize_parameters.min_text_length); + ui->button_ok_shift->setEnabled(current_text.size() >= + initialize_parameters.min_text_length); + ui->button_ok_num->setEnabled(current_text.size() >= initialize_parameters.min_text_length); + + ui->button_backspace->setEnabled(initialize_parameters.enable_backspace_button && + cursor_position > 0); + ui->button_backspace_shift->setEnabled(initialize_parameters.enable_backspace_button && + cursor_position > 0); + ui->button_backspace_num->setEnabled(initialize_parameters.enable_backspace_button && + cursor_position > 0); } else { - buttons->addButton(QString::fromStdU16String(parameters.submit_text), - QDialogButtonBox::AcceptRole); + const auto text_length = [this] { + if (ui->topOSK->currentIndex() == 1) { + return static_cast<u32>(ui->text_edit_osk->toPlainText().length()); + } else { + return static_cast<u32>(ui->line_edit_osk->text().length()); + } + }(); + + const auto normal_cursor_position = [this] { + if (ui->topOSK->currentIndex() == 1) { + return ui->text_edit_osk->textCursor().position(); + } else { + return ui->line_edit_osk->cursorPosition(); + } + }(); + + ui->button_ok->setEnabled(text_length >= initialize_parameters.min_text_length); + ui->button_ok_shift->setEnabled(text_length >= initialize_parameters.min_text_length); + ui->button_ok_num->setEnabled(text_length >= initialize_parameters.min_text_length); + + ui->button_backspace->setEnabled(initialize_parameters.enable_backspace_button && + normal_cursor_position > 0); + ui->button_backspace_shift->setEnabled(initialize_parameters.enable_backspace_button && + normal_cursor_position > 0); + ui->button_backspace_num->setEnabled(initialize_parameters.enable_backspace_button && + normal_cursor_position > 0); } - connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject); - layout->addWidget(header_label); - layout->addWidget(sub_label); - layout->addWidget(guide_label); - layout->addWidget(length_label); - layout->addWidget(line_edit); - layout->addWidget(buttons); - setLayout(layout); - setWindowTitle(tr("Software Keyboard")); } -QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default; +bool QtSoftwareKeyboardDialog::ValidateInputText(const QString& input_text) { + const auto& key_disable_flags = initialize_parameters.key_disable_flags; + + const auto input_text_length = static_cast<u32>(input_text.length()); + + if (input_text_length < initialize_parameters.min_text_length || + input_text_length > initialize_parameters.max_text_length) { + return false; + } + + if (key_disable_flags.space && input_text.contains(QLatin1Char{' '})) { + return false; + } + + if ((key_disable_flags.at || key_disable_flags.username) && + input_text.contains(QLatin1Char{'@'})) { + return false; + } + + if ((key_disable_flags.percent || key_disable_flags.username) && + input_text.contains(QLatin1Char{'%'})) { + return false; + } + + if (key_disable_flags.slash && input_text.contains(QLatin1Char{'/'})) { + return false; + } + + if ((key_disable_flags.backslash || key_disable_flags.username) && + input_text.contains(QLatin1Char('\\'))) { + return false; + } -void QtSoftwareKeyboardDialog::accept() { - text = line_edit->text().toStdU16String(); - QDialog::accept(); + if (key_disable_flags.numbers && + std::any_of(input_text.begin(), input_text.end(), [](QChar c) { return c.isDigit(); })) { + return false; + } + + if (bottom_osk_index == BottomOSKIndex::NumberPad && + std::any_of(input_text.begin(), input_text.end(), [](QChar c) { return !c.isDigit(); })) { + return false; + } + + return true; } -void QtSoftwareKeyboardDialog::reject() { - text.clear(); - QDialog::reject(); +void QtSoftwareKeyboardDialog::ChangeBottomOSKIndex() { + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + bottom_osk_index = BottomOSKIndex::UpperCase; + ui->bottomOSK->setCurrentIndex(static_cast<int>(bottom_osk_index)); + + ui->button_shift_shift->setStyleSheet( + QStringLiteral("background-image: url(:/overlay/osk_button_shift_lock_off.png);" + "\nbackground-position: left top;" + "\nbackground-repeat: no-repeat;" + "\nbackground-origin: content;")); + + ui->button_shift_shift->setIconSize(ui->button_shift->iconSize()); + ui->button_backspace_shift->setIconSize(ui->button_backspace->iconSize()); + break; + case BottomOSKIndex::UpperCase: + if (caps_lock_enabled) { + caps_lock_enabled = false; + + ui->button_shift_shift->setStyleSheet( + QStringLiteral("background-image: url(:/overlay/osk_button_shift_lock_off.png);" + "\nbackground-position: left top;" + "\nbackground-repeat: no-repeat;" + "\nbackground-origin: content;")); + + ui->button_shift_shift->setIconSize(ui->button_shift->iconSize()); + ui->button_backspace_shift->setIconSize(ui->button_backspace->iconSize()); + + ui->label_shift_shift->setText(QStringLiteral("Caps Lock")); + + bottom_osk_index = BottomOSKIndex::LowerCase; + ui->bottomOSK->setCurrentIndex(static_cast<int>(bottom_osk_index)); + } else { + caps_lock_enabled = true; + + ui->button_shift_shift->setStyleSheet( + QStringLiteral("background-image: url(:/overlay/osk_button_shift_lock_on.png);" + "\nbackground-position: left top;" + "\nbackground-repeat: no-repeat;" + "\nbackground-origin: content;")); + + ui->button_shift_shift->setIconSize(ui->button_shift->iconSize()); + ui->button_backspace_shift->setIconSize(ui->button_backspace->iconSize()); + + ui->label_shift_shift->setText(QStringLiteral("Caps Lock Off")); + } + break; + case BottomOSKIndex::NumberPad: + default: + break; + } } -std::u16string QtSoftwareKeyboardDialog::GetText() const { - return text; +void QtSoftwareKeyboardDialog::NormalKeyboardButtonClicked(QPushButton* button) { + if (button == ui->button_ampersand) { + if (ui->topOSK->currentIndex() == 1) { + ui->text_edit_osk->insertPlainText(QStringLiteral("&")); + } else { + ui->line_edit_osk->insert(QStringLiteral("&")); + } + return; + } + + if (button == ui->button_return || button == ui->button_return_shift) { + if (ui->topOSK->currentIndex() == 1) { + ui->text_edit_osk->insertPlainText(QStringLiteral("\n")); + } else { + ui->line_edit_osk->insert(QStringLiteral("\n")); + } + return; + } + + if (button == ui->button_space || button == ui->button_space_shift) { + if (ui->topOSK->currentIndex() == 1) { + ui->text_edit_osk->insertPlainText(QStringLiteral(" ")); + } else { + ui->line_edit_osk->insert(QStringLiteral(" ")); + } + return; + } + + if (button == ui->button_shift || button == ui->button_shift_shift) { + ChangeBottomOSKIndex(); + return; + } + + if (button == ui->button_backspace || button == ui->button_backspace_shift || + button == ui->button_backspace_num) { + if (ui->topOSK->currentIndex() == 1) { + auto text_cursor = ui->text_edit_osk->textCursor(); + ui->text_edit_osk->setTextCursor(text_cursor); + text_cursor.deletePreviousChar(); + } else { + ui->line_edit_osk->backspace(); + } + return; + } + + if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) { + if (ui->topOSK->currentIndex() == 1) { + emit SubmitNormalText(SwkbdResult::Ok, + ui->text_edit_osk->toPlainText().toStdU16String()); + } else { + emit SubmitNormalText(SwkbdResult::Ok, ui->line_edit_osk->text().toStdU16String()); + } + return; + } + + if (ui->topOSK->currentIndex() == 1) { + ui->text_edit_osk->insertPlainText(button->text()); + } else { + ui->line_edit_osk->insert(button->text()); + } + + // Revert the keyboard to lowercase if the shift key is active. + if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) { + // This is set to true since ChangeBottomOSKIndex will change bottom_osk_index to LowerCase + // if bottom_osk_index is UpperCase and caps_lock_enabled is true. + caps_lock_enabled = true; + ChangeBottomOSKIndex(); + } +} + +void QtSoftwareKeyboardDialog::InlineKeyboardButtonClicked(QPushButton* button) { + if (!button->isEnabled()) { + return; + } + + if (button == ui->button_ampersand) { + InlineTextInsertString(u"&"); + return; + } + + if (button == ui->button_return || button == ui->button_return_shift) { + InlineTextInsertString(u"\n"); + return; + } + + if (button == ui->button_space || button == ui->button_space_shift) { + InlineTextInsertString(u" "); + return; + } + + if (button == ui->button_shift || button == ui->button_shift_shift) { + ChangeBottomOSKIndex(); + return; + } + + if (button == ui->button_backspace || button == ui->button_backspace_shift || + button == ui->button_backspace_num) { + if (cursor_position <= 0 || current_text.empty()) { + cursor_position = 0; + return; + } + + --cursor_position; + + current_text.erase(cursor_position, 1); + + SetBackspaceOkEnabled(); + + emit SubmitInlineText(SwkbdReplyType::ChangedString, current_text, cursor_position); + return; + } + + if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) { + emit SubmitInlineText(SwkbdReplyType::DecidedEnter, current_text, cursor_position); + return; + } + + InlineTextInsertString(button->text().toStdU16String()); + + // Revert the keyboard to lowercase if the shift key is active. + if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) { + // This is set to true since ChangeBottomOSKIndex will change bottom_osk_index to LowerCase + // if bottom_osk_index is UpperCase and caps_lock_enabled is true. + caps_lock_enabled = true; + ChangeBottomOSKIndex(); + } +} + +void QtSoftwareKeyboardDialog::InlineTextInsertString(std::u16string_view string) { + if ((current_text.size() + string.size()) > initialize_parameters.max_text_length) { + return; + } + + current_text.insert(cursor_position, string); + + cursor_position += static_cast<s32>(string.size()); + + SetBackspaceOkEnabled(); + + emit SubmitInlineText(SwkbdReplyType::ChangedString, current_text, cursor_position); +} + +void QtSoftwareKeyboardDialog::SetupMouseHover() { + // setFocus() has a bug where continuously changing focus will cause the focus UI to + // mysteriously disappear. A workaround we have found is using the mouse to hover over + // the buttons to act in place of the button focus. As a result, we will have to set + // a blank cursor when hovering over all the buttons and set a no focus policy so the + // buttons do not stay in focus in addition to the mouse hover. + for (auto* button : all_buttons) { + button->setCursor(QCursor(Qt::BlankCursor)); + button->setFocusPolicy(Qt::NoFocus); + } +} + +template <HIDButton... T> +void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() { + const auto f = [this](HIDButton button) { + if (input_interpreter->IsButtonPressedOnce(button)) { + TranslateButtonPress(button); + } + }; + + (f(T), ...); +} + +template <HIDButton... T> +void QtSoftwareKeyboardDialog::HandleButtonHold() { + const auto f = [this](HIDButton button) { + if (input_interpreter->IsButtonHeld(button)) { + TranslateButtonPress(button); + } + }; + + (f(T), ...); +} + +void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { + switch (button) { + case HIDButton::A: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + case BottomOSKIndex::UpperCase: + keyboard_buttons[static_cast<std::size_t>(bottom_osk_index)][row][column]->click(); + break; + case BottomOSKIndex::NumberPad: + numberpad_buttons[row][column]->click(); + break; + default: + break; + } + break; + case HIDButton::B: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_backspace->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_backspace_shift->click(); + break; + case BottomOSKIndex::NumberPad: + ui->button_backspace_num->click(); + break; + default: + break; + } + break; + case HIDButton::X: + if (is_inline) { + emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); + } else { + if (ui->topOSK->currentIndex() == 1) { + emit SubmitNormalText(SwkbdResult::Cancel, + ui->text_edit_osk->toPlainText().toStdU16String()); + } else { + emit SubmitNormalText(SwkbdResult::Cancel, + ui->line_edit_osk->text().toStdU16String()); + } + } + break; + case HIDButton::Y: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_space->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_space_shift->click(); + break; + case BottomOSKIndex::NumberPad: + default: + break; + } + break; + case HIDButton::LStick: + case HIDButton::RStick: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_shift->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_shift_shift->click(); + break; + case BottomOSKIndex::NumberPad: + default: + break; + } + break; + case HIDButton::L: + MoveTextCursorDirection(Direction::Left); + break; + case HIDButton::R: + MoveTextCursorDirection(Direction::Right); + break; + case HIDButton::Plus: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_ok->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_ok_shift->click(); + break; + case BottomOSKIndex::NumberPad: + ui->button_ok_num->click(); + break; + default: + break; + } + break; + case HIDButton::DLeft: + case HIDButton::LStickLeft: + case HIDButton::RStickLeft: + MoveButtonDirection(Direction::Left); + break; + case HIDButton::DUp: + case HIDButton::LStickUp: + case HIDButton::RStickUp: + MoveButtonDirection(Direction::Up); + break; + case HIDButton::DRight: + case HIDButton::LStickRight: + case HIDButton::RStickRight: + MoveButtonDirection(Direction::Right); + break; + case HIDButton::DDown: + case HIDButton::LStickDown: + case HIDButton::RStickDown: + MoveButtonDirection(Direction::Down); + break; + default: + break; + } +} + +void QtSoftwareKeyboardDialog::MoveButtonDirection(Direction direction) { + // Changes the row or column index depending on the direction. + auto move_direction = [this, direction](std::size_t max_rows, std::size_t max_columns) { + switch (direction) { + case Direction::Left: + column = (column + max_columns - 1) % max_columns; + break; + case Direction::Up: + row = (row + max_rows - 1) % max_rows; + break; + case Direction::Right: + column = (column + 1) % max_columns; + break; + case Direction::Down: + row = (row + 1) % max_rows; + break; + default: + break; + } + }; + + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + case BottomOSKIndex::UpperCase: { + const auto index = static_cast<std::size_t>(bottom_osk_index); + + const auto* const prev_button = keyboard_buttons[index][row][column]; + move_direction(NUM_ROWS_NORMAL, NUM_COLUMNS_NORMAL); + auto* curr_button = keyboard_buttons[index][row][column]; + + while (!curr_button || !curr_button->isEnabled() || curr_button == prev_button) { + move_direction(NUM_ROWS_NORMAL, NUM_COLUMNS_NORMAL); + curr_button = keyboard_buttons[index][row][column]; + } + + // This is a workaround for setFocus() randomly not showing focus in the UI + QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); + break; + } + case BottomOSKIndex::NumberPad: { + const auto* const prev_button = numberpad_buttons[row][column]; + move_direction(NUM_ROWS_NUMPAD, NUM_COLUMNS_NUMPAD); + auto* curr_button = numberpad_buttons[row][column]; + + while (!curr_button || !curr_button->isEnabled() || curr_button == prev_button) { + move_direction(NUM_ROWS_NUMPAD, NUM_COLUMNS_NUMPAD); + curr_button = numberpad_buttons[row][column]; + } + + // This is a workaround for setFocus() randomly not showing focus in the UI + QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); + break; + } + default: + break; + } +} + +void QtSoftwareKeyboardDialog::MoveTextCursorDirection(Direction direction) { + switch (direction) { + case Direction::Left: + if (is_inline) { + if (cursor_position <= 0) { + cursor_position = 0; + } else { + --cursor_position; + emit SubmitInlineText(SwkbdReplyType::MovedCursor, current_text, cursor_position); + } + } else { + if (ui->topOSK->currentIndex() == 1) { + ui->text_edit_osk->moveCursor(QTextCursor::Left); + } else { + ui->line_edit_osk->setCursorPosition(ui->line_edit_osk->cursorPosition() - 1); + } + } + break; + case Direction::Right: + if (is_inline) { + if (cursor_position >= static_cast<s32>(current_text.size())) { + cursor_position = static_cast<s32>(current_text.size()); + } else { + ++cursor_position; + emit SubmitInlineText(SwkbdReplyType::MovedCursor, current_text, cursor_position); + } + } else { + if (ui->topOSK->currentIndex() == 1) { + ui->text_edit_osk->moveCursor(QTextCursor::Right); + } else { + ui->line_edit_osk->setCursorPosition(ui->line_edit_osk->cursorPosition() + 1); + } + } + break; + default: + break; + } +} + +void QtSoftwareKeyboardDialog::StartInputThread() { + if (input_thread_running) { + return; + } + + input_thread_running = true; + + input_thread = std::thread(&QtSoftwareKeyboardDialog::InputThread, this); +} + +void QtSoftwareKeyboardDialog::StopInputThread() { + input_thread_running = false; + + if (input_thread.joinable()) { + input_thread.join(); + } + + if (input_interpreter) { + input_interpreter->ResetButtonStates(); + } +} + +void QtSoftwareKeyboardDialog::InputThread() { + while (input_thread_running) { + input_interpreter->PollInput(); + + HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::X, HIDButton::Y, + HIDButton::LStick, HIDButton::RStick, HIDButton::L, HIDButton::R, + HIDButton::Plus, HIDButton::DLeft, HIDButton::DUp, + HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft, + HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown, + HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight, + HIDButton::RStickDown>(); + + HandleButtonHold<HIDButton::B, HIDButton::L, HIDButton::R, HIDButton::DLeft, HIDButton::DUp, + HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft, + HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown, + HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight, + HIDButton::RStickDown>(); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } } QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) { - connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window, - &GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection); - connect(this, &QtSoftwareKeyboard::MainWindowTextCheckDialog, &main_window, - &GMainWindow::SoftwareKeyboardInvokeCheckDialog, Qt::BlockingQueuedConnection); - connect(&main_window, &GMainWindow::SoftwareKeyboardFinishedText, this, - &QtSoftwareKeyboard::MainWindowFinishedText, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowInitializeKeyboard, &main_window, + &GMainWindow::SoftwareKeyboardInitialize, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowShowNormalKeyboard, &main_window, + &GMainWindow::SoftwareKeyboardShowNormal, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowShowTextCheckDialog, &main_window, + &GMainWindow::SoftwareKeyboardShowTextCheck, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowShowInlineKeyboard, &main_window, + &GMainWindow::SoftwareKeyboardShowInline, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowHideInlineKeyboard, &main_window, + &GMainWindow::SoftwareKeyboardHideInline, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowInlineTextChanged, &main_window, + &GMainWindow::SoftwareKeyboardInlineTextChanged, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowExitKeyboard, &main_window, + &GMainWindow::SoftwareKeyboardExit, Qt::QueuedConnection); + connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitNormalText, this, + &QtSoftwareKeyboard::SubmitNormalText, Qt::QueuedConnection); + connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitInlineText, this, + &QtSoftwareKeyboard::SubmitInlineText, Qt::QueuedConnection); } QtSoftwareKeyboard::~QtSoftwareKeyboard() = default; -void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out, - Core::Frontend::SoftwareKeyboardParameters parameters) const { - text_output = std::move(out); - emit MainWindowGetText(parameters); +void QtSoftwareKeyboard::InitializeKeyboard( + bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters, + std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)> submit_normal_callback_, + std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> + submit_inline_callback_) { + if (is_inline) { + submit_inline_callback = std::move(submit_inline_callback_); + } else { + submit_normal_callback = std::move(submit_normal_callback_); + } + + LOG_INFO(Service_AM, + "\nKeyboardInitializeParameters:" + "\nok_text={}" + "\nheader_text={}" + "\nsub_text={}" + "\nguide_text={}" + "\ninitial_text={}" + "\nmax_text_length={}" + "\nmin_text_length={}" + "\ninitial_cursor_position={}" + "\ntype={}" + "\npassword_mode={}" + "\ntext_draw_type={}" + "\nkey_disable_flags={}" + "\nuse_blur_background={}" + "\nenable_backspace_button={}" + "\nenable_return_button={}" + "\ndisable_cancel_button={}", + Common::UTF16ToUTF8(initialize_parameters.ok_text), + Common::UTF16ToUTF8(initialize_parameters.header_text), + Common::UTF16ToUTF8(initialize_parameters.sub_text), + Common::UTF16ToUTF8(initialize_parameters.guide_text), + Common::UTF16ToUTF8(initialize_parameters.initial_text), + initialize_parameters.max_text_length, initialize_parameters.min_text_length, + initialize_parameters.initial_cursor_position, initialize_parameters.type, + initialize_parameters.password_mode, initialize_parameters.text_draw_type, + initialize_parameters.key_disable_flags.raw, initialize_parameters.use_blur_background, + initialize_parameters.enable_backspace_button, + initialize_parameters.enable_return_button, + initialize_parameters.disable_cancel_button); + + emit MainWindowInitializeKeyboard(is_inline, std::move(initialize_parameters)); +} + +void QtSoftwareKeyboard::ShowNormalKeyboard() const { + emit MainWindowShowNormalKeyboard(); +} + +void QtSoftwareKeyboard::ShowTextCheckDialog( + Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const { + emit MainWindowShowTextCheckDialog(text_check_result, text_check_message); +} + +void QtSoftwareKeyboard::ShowInlineKeyboard( + Core::Frontend::InlineAppearParameters appear_parameters) const { + LOG_INFO(Service_AM, + "\nInlineAppearParameters:" + "\nmax_text_length={}" + "\nmin_text_length={}" + "\nkey_top_scale_x={}" + "\nkey_top_scale_y={}" + "\nkey_top_translate_x={}" + "\nkey_top_translate_y={}" + "\ntype={}" + "\nkey_disable_flags={}" + "\nkey_top_as_floating={}" + "\nenable_backspace_button={}" + "\nenable_return_button={}" + "\ndisable_cancel_button={}", + appear_parameters.max_text_length, appear_parameters.min_text_length, + appear_parameters.key_top_scale_x, appear_parameters.key_top_scale_y, + appear_parameters.key_top_translate_x, appear_parameters.key_top_translate_y, + appear_parameters.type, appear_parameters.key_disable_flags.raw, + appear_parameters.key_top_as_floating, appear_parameters.enable_backspace_button, + appear_parameters.enable_return_button, appear_parameters.disable_cancel_button); + + emit MainWindowShowInlineKeyboard(std::move(appear_parameters)); +} + +void QtSoftwareKeyboard::HideInlineKeyboard() const { + emit MainWindowHideInlineKeyboard(); +} + +void QtSoftwareKeyboard::InlineTextChanged( + Core::Frontend::InlineTextParameters text_parameters) const { + LOG_INFO(Service_AM, + "\nInlineTextParameters:" + "\ninput_text={}" + "\ncursor_position={}", + Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position); + + emit MainWindowInlineTextChanged(std::move(text_parameters)); } -void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, - std::function<void()> finished_check_) const { - finished_check = std::move(finished_check_); - emit MainWindowTextCheckDialog(error_message); +void QtSoftwareKeyboard::ExitKeyboard() const { + emit MainWindowExitKeyboard(); } -void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) { - // Acquire the HLE mutex - std::lock_guard lock{HLE::g_hle_lock}; - text_output(std::move(text)); +void QtSoftwareKeyboard::SubmitNormalText(Service::AM::Applets::SwkbdResult result, + std::u16string submitted_text) const { + submit_normal_callback(result, submitted_text); } -void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() { - // Acquire the HLE mutex - std::lock_guard lock{HLE::g_hle_lock}; - finished_check(); +void QtSoftwareKeyboard::SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, + std::u16string submitted_text, + s32 cursor_position) const { + submit_inline_callback(reply_type, submitted_text, cursor_position); } diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h index 9e1094cce..1a03c098c 100644 --- a/src/yuzu/applets/software_keyboard.h +++ b/src/yuzu/applets/software_keyboard.h @@ -1,54 +1,228 @@ -// Copyright 2018 yuzu Emulator Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once +#include <array> +#include <atomic> +#include <memory> +#include <thread> + #include <QDialog> #include <QValidator> + #include "core/frontend/applets/software_keyboard.h" -class GMainWindow; -class QDialogButtonBox; -class QLabel; -class QLineEdit; -class QVBoxLayout; -class QtSoftwareKeyboard; +enum class HIDButton : u8; -class QtSoftwareKeyboardValidator final : public QValidator { -public: - explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters); - State validate(QString& input, int& pos) const override; +class InputInterpreter; -private: - Core::Frontend::SoftwareKeyboardParameters parameters; -}; +namespace Core { +class System; +} + +namespace Ui { +class QtSoftwareKeyboardDialog; +} + +class GMainWindow; class QtSoftwareKeyboardDialog final : public QDialog { Q_OBJECT public: - QtSoftwareKeyboardDialog(QWidget* parent, - Core::Frontend::SoftwareKeyboardParameters parameters); + QtSoftwareKeyboardDialog(QWidget* parent, Core::System& system_, bool is_inline_, + Core::Frontend::KeyboardInitializeParameters initialize_parameters_); ~QtSoftwareKeyboardDialog() override; - void accept() override; + void ShowNormalKeyboard(QPoint pos, QSize size); + + void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message); + + void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters, QPoint pos, + QSize size); + + void HideInlineKeyboard(); + + void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters); + + void ExitKeyboard(); + +signals: + void SubmitNormalText(Service::AM::Applets::SwkbdResult result, + std::u16string submitted_text) const; + + void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, + std::u16string submitted_text, s32 cursor_position) const; + +public slots: + void open() override; void reject() override; - std::u16string GetText() const; +protected: + /// We override the keyPressEvent for inputting text into the inline software keyboard. + void keyPressEvent(QKeyEvent* event) override; private: - std::u16string text; + enum class Direction { + Left, + Up, + Right, + Down, + }; + + enum class BottomOSKIndex { + LowerCase, + UpperCase, + NumberPad, + }; + + /** + * Moves and resizes the window to a specified position and size. + * + * @param pos Top-left window position + * @param size Window size + */ + void MoveAndResizeWindow(QPoint pos, QSize size); + + /** + * Rescales all keyboard elements to account for High DPI displays. + * + * @param width Window width + * @param height Window height + * @param dpi_scale Display scaling factor + */ + void RescaleKeyboardElements(float width, float height, float dpi_scale); + + /// Sets the keyboard type based on initialize_parameters. + void SetKeyboardType(); + + /// Sets the password mode based on initialize_parameters. + void SetPasswordMode(); + + /// Sets the text draw type based on initialize_parameters. + void SetTextDrawType(); + + /// Sets the controller image at the bottom left of the software keyboard. + void SetControllerImage(); + + /// Disables buttons based on initialize_parameters. + void DisableKeyboardButtons(); + + /// Changes whether the backspace or/and ok buttons should be enabled or disabled. + void SetBackspaceOkEnabled(); + + /** + * Validates the input text sent in based on the parameters in initialize_parameters. + * + * @param input_text Input text + * + * @returns True if the input text is valid, false otherwise. + */ + bool ValidateInputText(const QString& input_text); + + /// Switches between LowerCase and UpperCase (Shift and Caps Lock) + void ChangeBottomOSKIndex(); + + /// Processes a keyboard button click from the UI as normal keyboard input. + void NormalKeyboardButtonClicked(QPushButton* button); + + /// Processes a keyboard button click from the UI as inline keyboard input. + void InlineKeyboardButtonClicked(QPushButton* button); + + /** + * Inserts a string of arbitrary length into the current_text at the current cursor position. + * This is only used for the inline software keyboard. + */ + void InlineTextInsertString(std::u16string_view string); - QDialogButtonBox* buttons; - QLabel* header_label; - QLabel* sub_label; - QLabel* guide_label; - QLabel* length_label; - QLineEdit* line_edit; - QVBoxLayout* layout; + /// Setup the mouse hover workaround for "focusing" buttons. This should only be called once. + void SetupMouseHover(); - Core::Frontend::SoftwareKeyboardParameters parameters; + /** + * Handles button presses and converts them into keyboard input. + * + * @tparam HIDButton The list of buttons that can be converted into keyboard input. + */ + template <HIDButton... T> + void HandleButtonPressedOnce(); + + /** + * Handles button holds and converts them into keyboard input. + * + * @tparam HIDButton The list of buttons that can be converted into keyboard input. + */ + template <HIDButton... T> + void HandleButtonHold(); + + /** + * Translates a button press to focus or click a keyboard button. + * + * @param button The button press to process. + */ + void TranslateButtonPress(HIDButton button); + + /** + * Moves the focus of a button in a certain direction. + * + * @param direction The direction to move. + */ + void MoveButtonDirection(Direction direction); + + /** + * Moves the text cursor in a certain direction. + * + * @param direction The direction to move. + */ + void MoveTextCursorDirection(Direction direction); + + void StartInputThread(); + void StopInputThread(); + + /// The thread where input is being polled and processed. + void InputThread(); + + std::unique_ptr<Ui::QtSoftwareKeyboardDialog> ui; + + Core::System& system; + + // True if it is the inline software keyboard. + bool is_inline; + + // Common software keyboard initialize parameters. + Core::Frontend::KeyboardInitializeParameters initialize_parameters; + + // Used only by the inline software keyboard since the QLineEdit or QTextEdit is hidden. + std::u16string current_text; + s32 cursor_position{0}; + + static constexpr std::size_t NUM_ROWS_NORMAL = 5; + static constexpr std::size_t NUM_COLUMNS_NORMAL = 12; + static constexpr std::size_t NUM_ROWS_NUMPAD = 4; + static constexpr std::size_t NUM_COLUMNS_NUMPAD = 4; + + // Stores the normal keyboard layout. + std::array<std::array<std::array<QPushButton*, NUM_COLUMNS_NORMAL>, NUM_ROWS_NORMAL>, 2> + keyboard_buttons; + // Stores the numberpad keyboard layout. + std::array<std::array<QPushButton*, NUM_COLUMNS_NUMPAD>, NUM_ROWS_NUMPAD> numberpad_buttons; + + // Contains a set of all buttons used in keyboard_buttons and numberpad_buttons. + std::array<QPushButton*, 110> all_buttons; + + std::size_t row{0}; + std::size_t column{0}; + + BottomOSKIndex bottom_osk_index{BottomOSKIndex::LowerCase}; + std::atomic<bool> caps_lock_enabled{false}; + + std::unique_ptr<InputInterpreter> input_interpreter; + + std::thread input_thread; + + std::atomic<bool> input_thread_running{}; }; class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet { @@ -58,19 +232,54 @@ public: explicit QtSoftwareKeyboard(GMainWindow& parent); ~QtSoftwareKeyboard() override; - void RequestText(std::function<void(std::optional<std::u16string>)> out, - Core::Frontend::SoftwareKeyboardParameters parameters) const override; - void SendTextCheckDialog(std::u16string error_message, - std::function<void()> finished_check_) const override; + void InitializeKeyboard( + bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters, + std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)> + submit_normal_callback_, + std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> + submit_inline_callback_) override; + + void ShowNormalKeyboard() const override; + + void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const override; + + void ShowInlineKeyboard( + Core::Frontend::InlineAppearParameters appear_parameters) const override; + + void HideInlineKeyboard() const override; + + void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const override; + + void ExitKeyboard() const override; signals: - void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const; - void MainWindowTextCheckDialog(std::u16string error_message) const; + void MainWindowInitializeKeyboard( + bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters) const; + + void MainWindowShowNormalKeyboard() const; + + void MainWindowShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const; + + void MainWindowShowInlineKeyboard( + Core::Frontend::InlineAppearParameters appear_parameters) const; + + void MainWindowHideInlineKeyboard() const; + + void MainWindowInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const; + + void MainWindowExitKeyboard() const; private: - void MainWindowFinishedText(std::optional<std::u16string> text); - void MainWindowFinishedCheckDialog(); + void SubmitNormalText(Service::AM::Applets::SwkbdResult result, + std::u16string submitted_text) const; + + void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, + std::u16string submitted_text, s32 cursor_position) const; - mutable std::function<void(std::optional<std::u16string>)> text_output; - mutable std::function<void()> finished_check; + mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)> + submit_normal_callback; + mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> + submit_inline_callback; }; diff --git a/src/yuzu/applets/software_keyboard.ui b/src/yuzu/applets/software_keyboard.ui new file mode 100644 index 000000000..b0a1fcde9 --- /dev/null +++ b/src/yuzu/applets/software_keyboard.ui @@ -0,0 +1,3503 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtSoftwareKeyboardDialog</class> + <widget class="QDialog" name="QtSoftwareKeyboardDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1280</width> + <height>720</height> + </rect> + </property> + <property name="windowTitle"> + <string>Software Keyboard</string> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QWidget" name="mainOSK" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="320,400"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QStackedWidget" name="topOSK"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="lineOSK"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>100</height> + </size> + </property> + <layout class="QVBoxLayout" name="lineOSKVerticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QGridLayout" name="gridLineOSK" rowstretch="40,50,23,48,65,94" columnstretch="130,1020,130"> + <property name="topMargin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <item row="4" column="2"> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="4" column="0"> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="5" column="1"> + <widget class="QWidget" name="charactersOSK" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item alignment="Qt::AlignRight|Qt::AlignTop"> + <widget class="QLabel" name="label_characters"> + <property name="font"> + <font> + <pointsize>17</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">0/32</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="3" column="1"> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="5" column="0"> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="4" column="1"> + <widget class="QWidget" name="inputOSK" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QLineEdit" name="line_edit_osk"> + <property name="font"> + <font> + <pointsize>26</pointsize> + <weight>50</weight> + <bold>false</bold> + </font> + </property> + <property name="focusPolicy"> + <enum>Qt::StrongFocus</enum> + </property> + <property name="text"> + <string/> + </property> + <property name="maxLength"> + <number>32</number> + </property> + <property name="placeholderText"> + <string>Enter Text</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="0" column="1"> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0" colspan="3"> + <widget class="QWidget" name="headerOSK" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_4" stretch="130,1020,130"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <spacer name="horizontalSpacer_18"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>127</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_header"> + <property name="font"> + <font> + <pointsize>23</pointsize> + </font> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_19"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>127</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item row="2" column="0" colspan="3"> + <widget class="QWidget" name="subOSK" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="130,1020,130"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <spacer name="horizontalSpacer_16"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>127</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_sub"> + <property name="font"> + <font> + <pointsize>17</pointsize> + </font> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_17"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>127</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="boxOSK"> + <layout class="QVBoxLayout" name="boxOSKVerticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QGridLayout" name="gridBoxOSK" rowstretch="61,178,81" columnstretch="120,1040,120"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <item row="0" column="1"> + <spacer name="verticalSpacer_5"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0"> + <spacer name="horizontalSpacer_20"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="2"> + <spacer name="horizontalSpacer_21"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1" alignment="Qt::AlignRight|Qt::AlignTop"> + <widget class="QWidget" name="charactersBoxOSK" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_characters_box"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="font"> + <font> + <pointsize>17</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">0/500</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="1"> + <widget class="QWidget" name="inputBoxOSK" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>14</number> + </property> + <property name="topMargin"> + <number>9</number> + </property> + <property name="rightMargin"> + <number>14</number> + </property> + <property name="bottomMargin"> + <number>9</number> + </property> + <item> + <widget class="QTextEdit" name="text_edit_osk"> + <property name="font"> + <font> + <pointsize>26</pointsize> + </font> + </property> + <property name="focusPolicy"> + <enum>Qt::StrongFocus</enum> + </property> + <property name="html"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:26pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <widget class="QStackedWidget" name="bottomOSK"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="normalOSK"> + <layout class="QVBoxLayout" name="normalPageVerticalLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QGridLayout" name="kbOSKnormal" rowstretch="15,63,63,63,63,63,70" columnstretch="54,96,96,96,96,96,96,96,96,96,96,96,116,54"> + <property name="spacing"> + <number>0</number> + </property> + <item row="6" column="1" colspan="12"> + <widget class="QWidget" name="legendOSK" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="70,525,25,12,22,41,25,12,22,41,25,12,47,37,29,12,69,37,29,12,56,8"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>2</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QWidget" name="icon_controller" native="true"> + <property name="styleSheet"> + <string notr="true"/> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_L" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_14"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="arrow_left" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_13"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_R" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_12"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="arrow_right" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_11"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_press_stick" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_10"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_shift"> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Shift</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_15"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_X" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_9"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_cancel"> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Cancel</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_8"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_A" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_7"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_enter"> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Enter</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_6"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item row="1" column="11"> + <widget class="QPushButton" name="button_minus"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">-</string> + </property> + </widget> + </item> + <item row="3" column="11"> + <widget class="QPushButton" name="button_apostrophe"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">'</string> + </property> + </widget> + </item> + <item row="2" column="11"> + <widget class="QPushButton" name="button_slash"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">/</string> + </property> + </widget> + </item> + <item row="4" column="11"> + <widget class="QPushButton" name="button_exclamation"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">!</string> + </property> + </widget> + </item> + <item row="1" column="7"> + <widget class="QPushButton" name="button_7"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">7</string> + </property> + </widget> + </item> + <item row="1" column="8"> + <widget class="QPushButton" name="button_8"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">8</string> + </property> + </widget> + </item> + <item row="1" column="10"> + <widget class="QPushButton" name="button_0"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">0</string> + </property> + </widget> + </item> + <item row="1" column="9"> + <widget class="QPushButton" name="button_9"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">9</string> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QPushButton" name="button_w"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">w</string> + </property> + </widget> + </item> + <item row="2" column="4"> + <widget class="QPushButton" name="button_r"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">r</string> + </property> + </widget> + </item> + <item row="2" column="3"> + <widget class="QPushButton" name="button_e"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">e</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QPushButton" name="button_q"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">q</string> + </property> + </widget> + </item> + <item row="2" column="7"> + <widget class="QPushButton" name="button_u"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">u</string> + </property> + </widget> + </item> + <item row="2" column="6"> + <widget class="QPushButton" name="button_y"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">y</string> + </property> + </widget> + </item> + <item row="2" column="5"> + <widget class="QPushButton" name="button_t"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">t</string> + </property> + </widget> + </item> + <item row="2" column="9"> + <widget class="QPushButton" name="button_o"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">o</string> + </property> + </widget> + </item> + <item row="2" column="10"> + <widget class="QPushButton" name="button_p"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">p</string> + </property> + </widget> + </item> + <item row="2" column="8"> + <widget class="QPushButton" name="button_i"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">i</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QPushButton" name="button_a"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">a</string> + </property> + </widget> + </item> + <item row="3" column="2"> + <widget class="QPushButton" name="button_s"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">s</string> + </property> + </widget> + </item> + <item row="3" column="3"> + <widget class="QPushButton" name="button_d"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">d</string> + </property> + </widget> + </item> + <item row="3" column="4"> + <widget class="QPushButton" name="button_f"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">f</string> + </property> + </widget> + </item> + <item row="3" column="6"> + <widget class="QPushButton" name="button_h"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">h</string> + </property> + </widget> + </item> + <item row="3" column="7"> + <widget class="QPushButton" name="button_j"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">j</string> + </property> + </widget> + </item> + <item row="3" column="5"> + <widget class="QPushButton" name="button_g"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">g</string> + </property> + </widget> + </item> + <item row="3" column="8"> + <widget class="QPushButton" name="button_k"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">k</string> + </property> + </widget> + </item> + <item row="3" column="9"> + <widget class="QPushButton" name="button_l"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">l</string> + </property> + </widget> + </item> + <item row="3" column="10"> + <widget class="QPushButton" name="button_colon"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">:</string> + </property> + </widget> + </item> + <item row="2" column="12" rowspan="2"> + <widget class="QPushButton" name="button_return"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Return</string> + </property> + </widget> + </item> + <item row="4" column="12" rowspan="2"> + <widget class="QPushButton" name="button_ok"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">OK</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QPushButton" name="button_z"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">z</string> + </property> + </widget> + </item> + <item row="4" column="3"> + <widget class="QPushButton" name="button_c"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">c</string> + </property> + </widget> + </item> + <item row="4" column="2"> + <widget class="QPushButton" name="button_x"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">x</string> + </property> + </widget> + </item> + <item row="4" column="4"> + <widget class="QPushButton" name="button_v"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">v</string> + </property> + </widget> + </item> + <item row="4" column="7"> + <widget class="QPushButton" name="button_m"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">m</string> + </property> + </widget> + </item> + <item row="4" column="8"> + <widget class="QPushButton" name="button_comma"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">,</string> + </property> + </widget> + </item> + <item row="4" column="6"> + <widget class="QPushButton" name="button_n"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">n</string> + </property> + </widget> + </item> + <item row="4" column="5"> + <widget class="QPushButton" name="button_b"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">b</string> + </property> + </widget> + </item> + <item row="5" column="1" colspan="2"> + <widget class="QPushButton" name="button_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true"/> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="4" column="10"> + <widget class="QPushButton" name="button_question"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">?</string> + </property> + </widget> + </item> + <item row="4" column="9"> + <widget class="QPushButton" name="button_dot"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">.</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QPushButton" name="button_1"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">1</string> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QPushButton" name="button_3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">3</string> + </property> + </widget> + </item> + <item row="1" column="4"> + <widget class="QPushButton" name="button_4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">4</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="button_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">2</string> + </property> + </widget> + </item> + <item row="1" column="6"> + <widget class="QPushButton" name="button_6"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">6</string> + </property> + </widget> + </item> + <item row="1" column="5"> + <widget class="QPushButton" name="button_5"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">5</string> + </property> + </widget> + </item> + <item row="5" column="3" colspan="9"> + <widget class="QPushButton" name="button_space"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Space</string> + </property> + </widget> + </item> + <item row="1" column="12"> + <widget class="QPushButton" name="button_backspace"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true"/> + </property> + </widget> + </item> + <item row="1" column="0"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="1"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="13"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="shiftOSK"> + <layout class="QVBoxLayout" name="shiftPageVerticalLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QGridLayout" name="kbOSKshift" rowstretch="15,63,63,63,63,63,70" columnstretch="54,96,96,96,96,96,96,96,96,96,96,96,116,54"> + <property name="spacing"> + <number>0</number> + </property> + <item row="6" column="1" colspan="12"> + <widget class="QWidget" name="legendOSKshift" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_5" stretch="70,464,25,12,22,41,25,12,22,41,25,12,95,37,29,12,69,37,29,12,56,8"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>2</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QWidget" name="icon_controller_shift" native="true"> + <property name="styleSheet"> + <string notr="true"/> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_22"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_L_shift" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_23"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="arrow_left_shift" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_24"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_R_shift" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_25"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="arrow_right_shift" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_26"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_press_stick_shift" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_27"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_shift_shift"> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Caps Lock</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_28"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_X_shift" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_29"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_cancel_shift"> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Cancel</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_30"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_A_shift" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_31"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_enter_shift"> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Enter</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_32"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item row="1" column="11"> + <widget class="QPushButton" name="button_underscore"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">_</string> + </property> + </widget> + </item> + <item row="3" column="11"> + <widget class="QPushButton" name="button_quotation"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">"</string> + </property> + </widget> + </item> + <item row="2" column="11"> + <widget class="QPushButton" name="button_at"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">@</string> + </property> + </widget> + </item> + <item row="4" column="11"> + <widget class="QPushButton" name="button_equal"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">=</string> + </property> + </widget> + </item> + <item row="1" column="7"> + <widget class="QPushButton" name="button_ampersand"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">&&</string> + </property> + </widget> + </item> + <item row="1" column="8"> + <widget class="QPushButton" name="button_asterisk"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">*</string> + </property> + </widget> + </item> + <item row="1" column="10"> + <widget class="QPushButton" name="button_right_parenthesis"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">)</string> + </property> + </widget> + </item> + <item row="1" column="9"> + <widget class="QPushButton" name="button_left_parenthesis"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">(</string> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QPushButton" name="button_w_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">W</string> + </property> + </widget> + </item> + <item row="2" column="4"> + <widget class="QPushButton" name="button_r_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">R</string> + </property> + </widget> + </item> + <item row="2" column="3"> + <widget class="QPushButton" name="button_e_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">E</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QPushButton" name="button_q_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Q</string> + </property> + </widget> + </item> + <item row="2" column="7"> + <widget class="QPushButton" name="button_u_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">U</string> + </property> + </widget> + </item> + <item row="2" column="6"> + <widget class="QPushButton" name="button_y_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Y</string> + </property> + </widget> + </item> + <item row="2" column="5"> + <widget class="QPushButton" name="button_t_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">T</string> + </property> + </widget> + </item> + <item row="2" column="9"> + <widget class="QPushButton" name="button_o_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">O</string> + </property> + </widget> + </item> + <item row="2" column="10"> + <widget class="QPushButton" name="button_p_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">P</string> + </property> + </widget> + </item> + <item row="2" column="8"> + <widget class="QPushButton" name="button_i_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">I</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QPushButton" name="button_a_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">A</string> + </property> + </widget> + </item> + <item row="3" column="2"> + <widget class="QPushButton" name="button_s_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">S</string> + </property> + </widget> + </item> + <item row="3" column="3"> + <widget class="QPushButton" name="button_d_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">D</string> + </property> + </widget> + </item> + <item row="3" column="4"> + <widget class="QPushButton" name="button_f_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">F</string> + </property> + </widget> + </item> + <item row="3" column="6"> + <widget class="QPushButton" name="button_h_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">H</string> + </property> + </widget> + </item> + <item row="3" column="7"> + <widget class="QPushButton" name="button_j_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">J</string> + </property> + </widget> + </item> + <item row="3" column="5"> + <widget class="QPushButton" name="button_g_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">G</string> + </property> + </widget> + </item> + <item row="3" column="8"> + <widget class="QPushButton" name="button_k_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">K</string> + </property> + </widget> + </item> + <item row="3" column="9"> + <widget class="QPushButton" name="button_l_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">L</string> + </property> + </widget> + </item> + <item row="3" column="10"> + <widget class="QPushButton" name="button_semicolon"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">;</string> + </property> + </widget> + </item> + <item row="2" column="12" rowspan="2"> + <widget class="QPushButton" name="button_return_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Return</string> + </property> + </widget> + </item> + <item row="4" column="12" rowspan="2"> + <widget class="QPushButton" name="button_ok_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">OK</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QPushButton" name="button_z_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Z</string> + </property> + </widget> + </item> + <item row="4" column="3"> + <widget class="QPushButton" name="button_c_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">C</string> + </property> + </widget> + </item> + <item row="4" column="2"> + <widget class="QPushButton" name="button_x_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">X</string> + </property> + </widget> + </item> + <item row="4" column="4"> + <widget class="QPushButton" name="button_v_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">V</string> + </property> + </widget> + </item> + <item row="4" column="7"> + <widget class="QPushButton" name="button_m_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">M</string> + </property> + </widget> + </item> + <item row="4" column="8"> + <widget class="QPushButton" name="button_less_than"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true"><</string> + </property> + </widget> + </item> + <item row="4" column="6"> + <widget class="QPushButton" name="button_n_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">N</string> + </property> + </widget> + </item> + <item row="4" column="5"> + <widget class="QPushButton" name="button_b_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">B</string> + </property> + </widget> + </item> + <item row="5" column="1" colspan="2"> + <widget class="QPushButton" name="button_shift_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true"/> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="4" column="10"> + <widget class="QPushButton" name="button_plus"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">+</string> + </property> + </widget> + </item> + <item row="4" column="9"> + <widget class="QPushButton" name="button_greater_than"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">></string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QPushButton" name="button_hash"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">#</string> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QPushButton" name="button_right_bracket"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">]</string> + </property> + </widget> + </item> + <item row="1" column="4"> + <widget class="QPushButton" name="button_dollar"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">$</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="button_left_bracket"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">[</string> + </property> + </widget> + </item> + <item row="1" column="6"> + <widget class="QPushButton" name="button_circumflex"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">^</string> + </property> + </widget> + </item> + <item row="1" column="5"> + <widget class="QPushButton" name="button_percent"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">%</string> + </property> + </widget> + </item> + <item row="5" column="3" colspan="9"> + <widget class="QPushButton" name="button_space_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Space</string> + </property> + </widget> + </item> + <item row="1" column="12"> + <widget class="QPushButton" name="button_backspace_shift"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true"/> + </property> + </widget> + </item> + <item row="1" column="0"> + <spacer name="horizontalSpacer_33"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="1"> + <spacer name="verticalSpacer_6"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="13"> + <spacer name="horizontalSpacer_34"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="numOSK"> + <layout class="QVBoxLayout" name="graphicsTabVerticalLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QGridLayout" name="kbOSKnum" rowstretch="18,63,63,63,63,10,70" columnstretch="54,307,186,186,186,120,187,54"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <item row="1" column="5"> + <widget class="QPushButton" name="button_backspace_num"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true"/> + </property> + </widget> + </item> + <item row="1" column="6"> + <spacer name="horizontalSpacer_35"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1"> + <spacer name="horizontalSpacer_36"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="2"> + <spacer name="verticalSpacer_7"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item row="6" column="1" colspan="6"> + <widget class="QWidget" name="legendOSKnum" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_6" stretch="25,70,601,25,12,22,41,25,12,22,41,29,12,69,37,29,12,56,30"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <spacer name="horizontalSpacer_48"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="icon_controller_num" native="true"> + <property name="styleSheet"> + <string notr="true"/> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_38"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_L_num" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_39"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="arrow_left_num" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_40"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_R_num" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_41"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="arrow_right_num" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_42"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_X_num" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_43"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_cancel_num"> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Cancel</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_44"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QWidget" name="button_A_num" native="true"/> + </item> + <item> + <spacer name="horizontalSpacer_45"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_enter_num"> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">Enter</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_46"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item row="2" column="4"> + <widget class="QPushButton" name="button_6_num"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">6</string> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QPushButton" name="button_4_num"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">4</string> + </property> + </widget> + </item> + <item row="3" column="4"> + <widget class="QPushButton" name="button_9_num"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">9</string> + </property> + </widget> + </item> + <item row="2" column="3"> + <widget class="QPushButton" name="button_5_num"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">5</string> + </property> + </widget> + </item> + <item row="2" column="5" rowspan="3"> + <widget class="QPushButton" name="button_ok_num"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">OK</string> + </property> + </widget> + </item> + <item row="3" column="2"> + <widget class="QPushButton" name="button_7_num"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">7</string> + </property> + </widget> + </item> + <item row="3" column="3"> + <widget class="QPushButton" name="button_8_num"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">8</string> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QPushButton" name="button_2_num"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">2</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="button_1_num"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">1</string> + </property> + </widget> + </item> + <item row="4" column="3"> + <widget class="QPushButton" name="button_0_num"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">0</string> + </property> + </widget> + </item> + <item row="1" column="4"> + <widget class="QPushButton" name="button_3_num"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>28</pointsize> + </font> + </property> + <property name="text"> + <string notr="true">3</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <spacer name="horizontalSpacer_37"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="7"> + <spacer name="horizontalSpacer_47"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="5" column="3"> + <spacer name="verticalSpacer_8"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>button_1</tabstop> + <tabstop>button_2</tabstop> + <tabstop>button_3</tabstop> + <tabstop>button_4</tabstop> + <tabstop>button_5</tabstop> + <tabstop>button_6</tabstop> + <tabstop>button_7</tabstop> + <tabstop>button_8</tabstop> + <tabstop>button_9</tabstop> + <tabstop>button_0</tabstop> + <tabstop>button_minus</tabstop> + <tabstop>button_backspace</tabstop> + <tabstop>button_q</tabstop> + <tabstop>button_w</tabstop> + <tabstop>button_e</tabstop> + <tabstop>button_r</tabstop> + <tabstop>button_t</tabstop> + <tabstop>button_y</tabstop> + <tabstop>button_u</tabstop> + <tabstop>button_i</tabstop> + <tabstop>button_o</tabstop> + <tabstop>button_p</tabstop> + <tabstop>button_slash</tabstop> + <tabstop>button_return</tabstop> + <tabstop>button_a</tabstop> + <tabstop>button_s</tabstop> + <tabstop>button_d</tabstop> + <tabstop>button_f</tabstop> + <tabstop>button_g</tabstop> + <tabstop>button_h</tabstop> + <tabstop>button_j</tabstop> + <tabstop>button_k</tabstop> + <tabstop>button_l</tabstop> + <tabstop>button_colon</tabstop> + <tabstop>button_apostrophe</tabstop> + <tabstop>button_z</tabstop> + <tabstop>button_x</tabstop> + <tabstop>button_c</tabstop> + <tabstop>button_v</tabstop> + <tabstop>button_b</tabstop> + <tabstop>button_n</tabstop> + <tabstop>button_m</tabstop> + <tabstop>button_comma</tabstop> + <tabstop>button_dot</tabstop> + <tabstop>button_question</tabstop> + <tabstop>button_exclamation</tabstop> + <tabstop>button_ok</tabstop> + <tabstop>button_shift</tabstop> + <tabstop>button_space</tabstop> + <tabstop>button_hash</tabstop> + <tabstop>button_left_bracket</tabstop> + <tabstop>button_right_bracket</tabstop> + <tabstop>button_dollar</tabstop> + <tabstop>button_percent</tabstop> + <tabstop>button_circumflex</tabstop> + <tabstop>button_ampersand</tabstop> + <tabstop>button_asterisk</tabstop> + <tabstop>button_left_parenthesis</tabstop> + <tabstop>button_right_parenthesis</tabstop> + <tabstop>button_underscore</tabstop> + <tabstop>button_backspace_shift</tabstop> + <tabstop>button_q_shift</tabstop> + <tabstop>button_w_shift</tabstop> + <tabstop>button_e_shift</tabstop> + <tabstop>button_r_shift</tabstop> + <tabstop>button_t_shift</tabstop> + <tabstop>button_y_shift</tabstop> + <tabstop>button_u_shift</tabstop> + <tabstop>button_i_shift</tabstop> + <tabstop>button_o_shift</tabstop> + <tabstop>button_p_shift</tabstop> + <tabstop>button_at</tabstop> + <tabstop>button_return_shift</tabstop> + <tabstop>button_a_shift</tabstop> + <tabstop>button_s_shift</tabstop> + <tabstop>button_d_shift</tabstop> + <tabstop>button_f_shift</tabstop> + <tabstop>button_g_shift</tabstop> + <tabstop>button_h_shift</tabstop> + <tabstop>button_j_shift</tabstop> + <tabstop>button_k_shift</tabstop> + <tabstop>button_l_shift</tabstop> + <tabstop>button_semicolon</tabstop> + <tabstop>button_quotation</tabstop> + <tabstop>button_z_shift</tabstop> + <tabstop>button_x_shift</tabstop> + <tabstop>button_c_shift</tabstop> + <tabstop>button_v_shift</tabstop> + <tabstop>button_b_shift</tabstop> + <tabstop>button_n_shift</tabstop> + <tabstop>button_m_shift</tabstop> + <tabstop>button_less_than</tabstop> + <tabstop>button_greater_than</tabstop> + <tabstop>button_plus</tabstop> + <tabstop>button_equal</tabstop> + <tabstop>button_ok_shift</tabstop> + <tabstop>button_shift_shift</tabstop> + <tabstop>button_space_shift</tabstop> + <tabstop>button_1_num</tabstop> + <tabstop>button_2_num</tabstop> + <tabstop>button_3_num</tabstop> + <tabstop>button_backspace_num</tabstop> + <tabstop>button_4_num</tabstop> + <tabstop>button_5_num</tabstop> + <tabstop>button_6_num</tabstop> + <tabstop>button_ok_num</tabstop> + <tabstop>button_7_num</tabstop> + <tabstop>button_8_num</tabstop> + <tabstop>button_9_num</tabstop> + <tabstop>button_0_num</tabstop> + </tabstops> + <resources> + <include location="../../../dist/icons/overlay/overlay.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 15c09e0ad..9c7daeac7 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -29,10 +29,10 @@ #include "common/microprofile.h" #include "common/scm_rev.h" #include "common/scope_exit.h" +#include "common/settings.h" #include "core/core.h" #include "core/frontend/framebuffer_layout.h" #include "core/hle/kernel/process.h" -#include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" #include "input_common/mouse/mouse_input.h" diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 1bac57bb2..851246233 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -641,6 +641,9 @@ void Config::ReadDebuggingValues() { ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool(); Settings::values.extended_logging = ReadSetting(QStringLiteral("extended_logging"), false).toBool(); + Settings::values.use_debug_asserts = + ReadSetting(QStringLiteral("use_debug_asserts"), false).toBool(); + Settings::values.use_auto_stub = ReadSetting(QStringLiteral("use_auto_stub"), false).toBool(); qt_config->endGroup(); } @@ -770,6 +773,13 @@ void Config::ReadRendererValues() { ReadSettingGlobal(Settings::values.renderer_backend, QStringLiteral("backend"), 0); ReadSettingGlobal(Settings::values.renderer_debug, QStringLiteral("debug"), false); ReadSettingGlobal(Settings::values.vulkan_device, QStringLiteral("vulkan_device"), 0); +#ifdef _WIN32 + ReadSettingGlobal(Settings::values.fullscreen_mode, QStringLiteral("fullscreen_mode"), 0); +#else + // *nix platforms may have issues with the borderless windowed fullscreen mode. + // Default to exclusive fullscreen on these platforms for now. + ReadSettingGlobal(Settings::values.fullscreen_mode, QStringLiteral("fullscreen_mode"), 1); +#endif ReadSettingGlobal(Settings::values.aspect_ratio, QStringLiteral("aspect_ratio"), 0); ReadSettingGlobal(Settings::values.max_anisotropy, QStringLiteral("max_anisotropy"), 0); ReadSettingGlobal(Settings::values.use_frame_limit, QStringLiteral("use_frame_limit"), true); @@ -1230,6 +1240,7 @@ void Config::SaveDebuggingValues() { WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false); WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); + WriteSetting(QStringLiteral("use_debug_asserts"), Settings::values.use_debug_asserts, false); WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false); qt_config->endGroup(); @@ -1333,6 +1344,13 @@ void Config::SaveRendererValues() { Settings::values.renderer_backend.UsingGlobal(), 0); WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false); WriteSettingGlobal(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); +#ifdef _WIN32 + WriteSettingGlobal(QStringLiteral("fullscreen_mode"), Settings::values.fullscreen_mode, 0); +#else + // *nix platforms may have issues with the borderless windowed fullscreen mode. + // Default to exclusive fullscreen on these platforms for now. + WriteSettingGlobal(QStringLiteral("fullscreen_mode"), Settings::values.fullscreen_mode, 1); +#endif WriteSettingGlobal(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0); WriteSettingGlobal(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0); WriteSettingGlobal(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); @@ -1584,7 +1602,7 @@ void Config::Reload() { ReadValues(); // To apply default value changes SaveValues(); - Settings::Apply(Core::System::GetInstance()); + Core::System::GetInstance().ApplySettings(); } void Config::Save() { diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 949c4eb13..5a2c026b3 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -9,7 +9,7 @@ #include <string> #include <QMetaType> #include <QVariant> -#include "core/settings.h" +#include "common/settings.h" #include "yuzu/uisettings.h" class QSettings; @@ -131,6 +131,6 @@ private: bool global; }; -// These metatype declarations cannot be in core/settings.h because core is devoid of QT +// These metatype declarations cannot be in common/settings.h because core is devoid of QT Q_DECLARE_METATYPE(Settings::RendererBackend); Q_DECLARE_METATYPE(Settings::GPUAccuracy); diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp index 18482795c..89be4a62d 100644 --- a/src/yuzu/configuration/configuration_shared.cpp +++ b/src/yuzu/configuration/configuration_shared.cpp @@ -6,7 +6,7 @@ #include <QComboBox> #include <QObject> #include <QString> -#include "core/settings.h" +#include "common/settings.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_per_game.h" diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h index 312b9e549..5b344cdbd 100644 --- a/src/yuzu/configuration/configuration_shared.h +++ b/src/yuzu/configuration/configuration_shared.h @@ -7,7 +7,7 @@ #include <QCheckBox> #include <QComboBox> #include <QString> -#include "core/settings.h" +#include "common/settings.h" namespace ConfigurationShared { diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index db9518798..f9507e228 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -8,8 +8,8 @@ #include "audio_core/sink.h" #include "audio_core/sink_details.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_audio.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_audio.h" diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp index d055cbd60..4f99bc80f 100644 --- a/src/yuzu/configuration/configure_cpu.cpp +++ b/src/yuzu/configuration/configure_cpu.cpp @@ -7,8 +7,8 @@ #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_cpu.h" #include "yuzu/configuration/configure_cpu.h" diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h index 3c5683d81..ef77b2e7e 100644 --- a/src/yuzu/configuration/configure_cpu.h +++ b/src/yuzu/configuration/configure_cpu.h @@ -6,7 +6,7 @@ #include <memory> #include <QWidget> -#include "core/settings.h" +#include "common/settings.h" namespace Ui { class ConfigureCpu; diff --git a/src/yuzu/configuration/configure_cpu_debug.cpp b/src/yuzu/configuration/configure_cpu_debug.cpp index 3385b2cf6..c925c023c 100644 --- a/src/yuzu/configuration/configure_cpu_debug.cpp +++ b/src/yuzu/configuration/configure_cpu_debug.cpp @@ -6,8 +6,8 @@ #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_cpu_debug.h" #include "yuzu/configuration/configure_cpu_debug.h" diff --git a/src/yuzu/configuration/configure_cpu_debug.h b/src/yuzu/configuration/configure_cpu_debug.h index c9941ef3b..10de55099 100644 --- a/src/yuzu/configuration/configure_cpu_debug.h +++ b/src/yuzu/configuration/configure_cpu_debug.h @@ -6,7 +6,7 @@ #include <memory> #include <QWidget> -#include "core/settings.h" +#include "common/settings.h" namespace Ui { class ConfigureCpuDebug; diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 121873f95..6730eb356 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -7,8 +7,8 @@ #include "common/file_util.h" #include "common/logging/backend.h" #include "common/logging/filter.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_debug.h" #include "yuzu/configuration/configure_debug.h" #include "yuzu/debugger/console.h" @@ -34,6 +34,8 @@ void ConfigureDebug::SetConfiguration() { ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); ui->reporting_services->setChecked(Settings::values.reporting_services); ui->quest_flag->setChecked(Settings::values.quest_flag); + ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts); + ui->use_auto_stub->setChecked(Settings::values.use_auto_stub); ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); @@ -47,13 +49,15 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); Settings::values.reporting_services = ui->reporting_services->isChecked(); Settings::values.quest_flag = ui->quest_flag->isChecked(); + Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); + Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); Settings::values.extended_logging = ui->extended_logging->isChecked(); Debugger::ToggleConsole(); - Log::Filter filter; + Common::Log::Filter filter; filter.ParseFilterString(Settings::values.log_filter); - Log::SetGlobalFilter(filter); + Common::Log::SetGlobalFilter(filter); } void ConfigureDebug::changeEvent(QEvent* event) { diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 9186aa732..d812858b6 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -185,6 +185,35 @@ </property> </widget> </item> + <item> + <widget class="QCheckBox" name="use_debug_asserts"> + <property name="text"> + <string>Enable Debug Asserts</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="use_auto_stub"> + <property name="text"> + <string>Enable Auto-Stub</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_3"> + <property name="font"> + <font> + <italic>true</italic> + </font> + </property> + <property name="text"> + <string>This will be reset automatically when yuzu closes.</string> + </property> + <property name="indent"> + <number>20</number> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index d6b17a28d..3ad40d2b3 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -5,8 +5,8 @@ #include <QHash> #include <QListWidgetItem> #include <QSignalBlocker> +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_dialog.h" @@ -55,7 +55,7 @@ void ConfigureDialog::ApplyConfiguration() { ui->debugTab->ApplyConfiguration(); ui->webTab->ApplyConfiguration(); ui->serviceTab->ApplyConfiguration(); - Settings::Apply(Core::System::GetInstance()); + Core::System::GetInstance().ApplySettings(); Settings::LogSettings(); } diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp index 58f644af4..006eda4b0 100644 --- a/src/yuzu/configuration/configure_filesystem.cpp +++ b/src/yuzu/configuration/configure_filesystem.cpp @@ -6,7 +6,7 @@ #include <QMessageBox> #include "common/common_paths.h" #include "common/file_util.h" -#include "core/settings.h" +#include "common/settings.h" #include "ui_configure_filesystem.h" #include "yuzu/configuration/configure_filesystem.h" #include "yuzu/uisettings.h" diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index d4d29d422..2fa88dcec 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -4,8 +4,8 @@ #include <QCheckBox> #include <QSpinBox> +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_general.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_general.h" diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 9ff32aec4..0a7536617 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -11,8 +11,8 @@ #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_graphics.h" #include "video_core/vulkan_common/vulkan_instance.h" #include "video_core/vulkan_common/vulkan_library.h" @@ -77,18 +77,25 @@ void ConfigureGraphics::SetConfiguration() { if (Settings::IsConfiguringGlobal()) { ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); + ui->fullscreen_mode_combobox->setCurrentIndex(Settings::values.fullscreen_mode.GetValue()); ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); } else { ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); ConfigurationShared::SetHighlight(ui->api_layout, !Settings::values.renderer_backend.UsingGlobal()); + + ConfigurationShared::SetPerGameSetting(ui->fullscreen_mode_combobox, + &Settings::values.fullscreen_mode); + ConfigurationShared::SetHighlight(ui->fullscreen_mode_label, + !Settings::values.fullscreen_mode.UsingGlobal()); + ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox, &Settings::values.aspect_ratio); + ConfigurationShared::SetHighlight(ui->ar_label, + !Settings::values.aspect_ratio.UsingGlobal()); ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1); ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal()); - ConfigurationShared::SetHighlight(ui->ar_label, - !Settings::values.aspect_ratio.UsingGlobal()); ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal()); } @@ -107,6 +114,9 @@ void ConfigureGraphics::ApplyConfiguration() { if (Settings::values.vulkan_device.UsingGlobal()) { Settings::values.vulkan_device.SetValue(vulkan_device); } + if (Settings::values.fullscreen_mode.UsingGlobal()) { + Settings::values.fullscreen_mode.SetValue(ui->fullscreen_mode_combobox->currentIndex()); + } if (Settings::values.aspect_ratio.UsingGlobal()) { Settings::values.aspect_ratio.SetValue(ui->aspect_ratio_combobox->currentIndex()); } @@ -140,6 +150,8 @@ void ConfigureGraphics::ApplyConfiguration() { } } + ConfigurationShared::ApplyPerGameSetting(&Settings::values.fullscreen_mode, + ui->fullscreen_mode_combobox); ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio, ui->aspect_ratio_combobox); @@ -227,7 +239,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() try { vulkan_devices.clear(); vulkan_devices.reserve(physical_devices.size()); for (const VkPhysicalDevice device : physical_devices) { - const char* const name = vk::PhysicalDevice(device, dld).GetProperties().deviceName; + const std::string name = vk::PhysicalDevice(device, dld).GetProperties().deviceName; vulkan_devices.push_back(QString::fromStdString(name)); } @@ -253,6 +265,7 @@ void ConfigureGraphics::SetupPerGameUI() { if (Settings::IsConfiguringGlobal()) { ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal()); ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal()); + ui->fullscreen_mode_combobox->setEnabled(Settings::values.fullscreen_mode.UsingGlobal()); ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); ui->use_asynchronous_gpu_emulation->setEnabled( Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); @@ -278,6 +291,8 @@ void ConfigureGraphics::SetupPerGameUI() { ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label, Settings::values.aspect_ratio.GetValue(true)); + ConfigurationShared::SetColoredComboBox(ui->fullscreen_mode_combobox, ui->fullscreen_mode_label, + Settings::values.fullscreen_mode.GetValue(true)); ConfigurationShared::InsertGlobalItem( ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); } diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index 1fefc88eb..c162048a2 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -8,7 +8,7 @@ #include <vector> #include <QString> #include <QWidget> -#include "core/settings.h" +#include "common/settings.h" namespace ConfigurationShared { enum class CheckState; diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 58486eb1e..ab0bd4d77 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -105,8 +105,47 @@ </widget> </item> <item> + <widget class="QWidget" name="fullscreen_mode_layout" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_1"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="fullscreen_mode_label"> + <property name="text"> + <string>Fullscreen Mode:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="fullscreen_mode_combobox"> + <item> + <property name="text"> + <string>Borderless Windowed</string> + </property> + </item> + <item> + <property name="text"> + <string>Exclusive Fullscreen</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + </item> + <item> <widget class="QWidget" name="aspect_ratio_layout" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_6"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> <property name="leftMargin"> <number>0</number> </property> diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 383c7bac8..c67609b0e 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -2,8 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_graphics_advanced.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_graphics_advanced.h" diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index cbee51a5e..ed76fe18e 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp @@ -5,7 +5,7 @@ #include <QMenu> #include <QMessageBox> #include <QStandardItemModel> -#include "core/settings.h" +#include "common/settings.h" #include "ui_configure_hotkeys.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_hotkeys.h" diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index a1a0eb676..d8d3b83dc 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -3,8 +3,8 @@ // Refer to the license.txt file included. #include <QColorDialog> +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_input_advanced.h" #include "yuzu/configuration/configure_input_advanced.h" diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index efe953fbc..c7d101682 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -14,7 +14,7 @@ #include <QWidget> #include "common/param_package.h" -#include "core/settings.h" +#include "common/settings.h" #include "ui_configure_input.h" class QCheckBox; diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h index 91c3343f1..51bb84eb6 100644 --- a/src/yuzu/configuration/configure_input_player_widget.h +++ b/src/yuzu/configuration/configure_input_player_widget.h @@ -7,8 +7,8 @@ #include <array> #include <QFrame> #include <QPointer> +#include "common/settings.h" #include "core/frontend/input.h" -#include "core/settings.h" class QLabel; diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index 52fdf7265..6a5d625df 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -14,7 +14,7 @@ #include <QVBoxLayout> #include "common/logging/log.h" -#include "core/settings.h" +#include "common/settings.h" #include "input_common/main.h" #include "input_common/udp/client.h" #include "input_common/udp/udp.h" @@ -23,8 +23,7 @@ #include "yuzu/configuration/configure_touch_from_button.h" CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent, - const std::string& host, u16 port, - u8 pad_index) + const std::string& host, u16 port) : QDialog(parent) { layout = new QVBoxLayout; status_label = new QLabel(tr("Communicating with the server...")); @@ -41,7 +40,7 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent, using namespace InputCommon::CemuhookUDP; job = std::make_unique<CalibrationConfigurationJob>( - host, port, pad_index, + host, port, [this](CalibrationConfigurationJob::Status status) { QString text; switch (status) { @@ -217,7 +216,7 @@ void ConfigureMotionTouch::OnCemuhookUDPTest() { ui->udp_test->setText(tr("Testing")); udp_test_in_progress = true; InputCommon::CemuhookUDP::TestCommunication( - ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toInt()), 0, + ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toInt()), [this] { LOG_INFO(Frontend, "UDP input test success"); QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true)); @@ -232,7 +231,7 @@ void ConfigureMotionTouch::OnConfigureTouchCalibration() { ui->touch_calibration_config->setEnabled(false); ui->touch_calibration_config->setText(tr("Configuring")); CalibrationConfigurationDialog dialog(this, ui->udp_server->text().toStdString(), - static_cast<u16>(ui->udp_port->text().toUInt()), 0); + static_cast<u16>(ui->udp_port->text().toUInt())); dialog.exec(); if (dialog.completed) { min_x = dialog.min_x; diff --git a/src/yuzu/configuration/configure_motion_touch.h b/src/yuzu/configuration/configure_motion_touch.h index d76bc8154..8b707d2ff 100644 --- a/src/yuzu/configuration/configure_motion_touch.h +++ b/src/yuzu/configuration/configure_motion_touch.h @@ -29,8 +29,7 @@ class ConfigureMotionTouch; class CalibrationConfigurationDialog : public QDialog { Q_OBJECT public: - explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port, - u8 pad_index); + explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port); ~CalibrationConfigurationDialog() override; private: diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index f598513df..bd91ebc42 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -57,7 +57,7 @@ void ConfigurePerGame::ApplyConfiguration() { ui->graphicsAdvancedTab->ApplyConfiguration(); ui->audioTab->ApplyConfiguration(); - Settings::Apply(Core::System::GetInstance()); + Core::System::GetInstance().ApplySettings(); Settings::LogSettings(); game_config->Save(); diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index 51647a028..d61b5e29b 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -13,10 +13,10 @@ #include <QVBoxLayout> #include "common/assert.h" #include "common/file_util.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/core.h" #include "core/hle/service/acc/profile_manager.h" -#include "core/settings.h" #include "ui_configure_profile_manager.h" #include "yuzu/configuration/configure_profile_manager.h" #include "yuzu/util/limitable_input_dialog.h" @@ -180,7 +180,7 @@ void ConfigureProfileManager::ApplyConfiguration() { return; } - Settings::Apply(Core::System::GetInstance()); + Core::System::GetInstance().ApplySettings(); } void ConfigureProfileManager::SelectUser(const QModelIndex& index) { diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_service.cpp index b580cfff2..6d954a67f 100644 --- a/src/yuzu/configuration/configure_service.cpp +++ b/src/yuzu/configuration/configure_service.cpp @@ -4,8 +4,8 @@ #include <QGraphicsItem> #include <QtConcurrent/QtConcurrent> +#include "common/settings.h" #include "core/hle/service/bcat/backend/boxcat.h" -#include "core/settings.h" #include "ui_configure_service.h" #include "yuzu/configuration/configure_service.h" diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 6cf2032da..268ed44c3 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -11,9 +11,9 @@ #include <QMessageBox> #include "common/assert.h" #include "common/file_util.h" +#include "common/settings.h" #include "core/core.h" #include "core/hle/service/time/time.h" -#include "core/settings.h" #include "ui_configure_system.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_system.h" @@ -199,7 +199,7 @@ void ConfigureSystem::ApplyConfiguration() { } } - Settings::Apply(system); + system.ApplySettings(); } void ConfigureSystem::RefreshConsoleID() { diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp index 15557e4b8..40129f228 100644 --- a/src/yuzu/configuration/configure_touch_from_button.cpp +++ b/src/yuzu/configuration/configure_touch_from_button.cpp @@ -10,8 +10,8 @@ #include <QStandardItemModel> #include <QTimer> #include "common/param_package.h" +#include "common/settings.h" #include "core/frontend/framebuffer_layout.h" -#include "core/settings.h" #include "input_common/main.h" #include "ui_configure_touch_from_button.h" #include "yuzu/configuration/configure_touch_from_button.h" diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index aed876008..f35c89e04 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -9,8 +9,8 @@ #include <QDirIterator> #include "common/common_types.h" #include "common/file_util.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_ui.h" #include "yuzu/configuration/configure_ui.h" #include "yuzu/uisettings.h" @@ -85,7 +85,7 @@ void ConfigureUi::ApplyConfiguration() { UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked(); Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir, ui->screenshot_path_edit->text().toStdString()); - Settings::Apply(Core::System::GetInstance()); + Core::System::GetInstance().ApplySettings(); } void ConfigureUi::RequestGameListUpdate() { diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp index 7dcb2c5b9..9d92c4949 100644 --- a/src/yuzu/configuration/configure_vibration.cpp +++ b/src/yuzu/configuration/configure_vibration.cpp @@ -8,7 +8,7 @@ #include <fmt/format.h> #include "common/param_package.h" -#include "core/settings.h" +#include "common/settings.h" #include "ui_configure_vibration.h" #include "yuzu/configuration/configure_vibration.h" diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp index 8637f5b3c..f3f3b54d6 100644 --- a/src/yuzu/configuration/configure_web.cpp +++ b/src/yuzu/configuration/configure_web.cpp @@ -5,7 +5,7 @@ #include <QIcon> #include <QMessageBox> #include <QtConcurrent/QtConcurrentRun> -#include "core/settings.h" +#include "common/settings.h" #include "core/telemetry_session.h" #include "ui_configure_web.h" #include "yuzu/configuration/configure_web.h" diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp index 207ff4d58..c11a326ac 100644 --- a/src/yuzu/debugger/console.cpp +++ b/src/yuzu/debugger/console.cpp @@ -29,13 +29,13 @@ void ToggleConsole() { freopen_s(&temp, "CONIN$", "r", stdin); freopen_s(&temp, "CONOUT$", "w", stdout); freopen_s(&temp, "CONOUT$", "w", stderr); - Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); + Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>()); } } else { if (FreeConsole()) { // In order to close the console, we have to also detach the streams on it. // Just redirect them to NUL if there is no console window - Log::RemoveBackend(Log::ColorConsoleBackend::Name()); + Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); freopen_s(&temp, "NUL", "r", stdin); freopen_s(&temp, "NUL", "w", stdout); freopen_s(&temp, "NUL", "w", stderr); @@ -43,9 +43,9 @@ void ToggleConsole() { } #else if (UISettings::values.show_console) { - Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); + Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>()); } else { - Log::RemoveBackend(Log::ColorConsoleBackend::Name()); + Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); } #endif } diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp index 2731d948d..7186eac76 100644 --- a/src/yuzu/debugger/controller.cpp +++ b/src/yuzu/debugger/controller.cpp @@ -5,7 +5,7 @@ #include <QAction> #include <QLayout> #include <QString> -#include "core/settings.h" +#include "common/settings.h" #include "yuzu/configuration/configure_input_player_widget.h" #include "yuzu/debugger/controller.h" diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 24bfa4d34..5f6cdc0c6 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -79,6 +79,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #ifdef ARCHITECTURE_x86_64 #include "common/x64/cpu_detect.h" #endif +#include "common/settings.h" #include "common/telemetry.h" #include "core/core.h" #include "core/crypto/key_manager.h" @@ -98,9 +99,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" #include "core/perf_stats.h" -#include "core/settings.h" #include "core/telemetry_session.h" #include "input_common/main.h" +#include "util/overlay_dialog.h" #include "video_core/gpu.h" #include "video_core/shader_notify.h" #include "yuzu/about_dialog.h" @@ -164,19 +165,21 @@ void GMainWindow::ShowTelemetryCallout() { "<br/><br/>Would you like to share your usage data with us?"); if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) != QMessageBox::Yes) { Settings::values.enable_telemetry = false; - Settings::Apply(Core::System::GetInstance()); + Core::System::GetInstance().ApplySettings(); } } const int GMainWindow::max_recent_files_item; static void InitializeLogging() { + using namespace Common; + Log::Filter log_filter; log_filter.ParseFilterString(Settings::values.log_filter); Log::SetGlobalFilter(log_filter); - const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir); - Common::FS::CreateFullPath(log_dir); + const std::string& log_dir = FS::GetUserPath(FS::UserPath::LogDir); + FS::CreateFullPath(log_dir); Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); #ifdef _WIN32 Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); @@ -225,6 +228,8 @@ GMainWindow::GMainWindow() SetDiscordEnabled(UISettings::values.enable_discord_presence); discord_rpc->Update(); + RegisterMetaTypes(); + InitializeWidgets(); InitializeDebugWidgets(); InitializeRecentFileMenuActions(); @@ -320,6 +325,34 @@ GMainWindow::GMainWindow() continue; } + // Launch game with a specific user + if (args[i] == QStringLiteral("-u")) { + if (i >= args.size() - 1) { + continue; + } + + if (args[i + 1].startsWith(QChar::fromLatin1('-'))) { + continue; + } + + bool argument_ok; + const std::size_t selected_user = args[++i].toUInt(&argument_ok); + + if (!argument_ok) { + LOG_ERROR(Frontend, "Invalid user argument"); + continue; + } + + const Service::Account::ProfileManager manager; + if (!manager.UserExistsIndex(selected_user)) { + LOG_ERROR(Frontend, "Selected user doesn't exist"); + continue; + } + + Settings::values.current_user = selected_user; + continue; + } + // Launch game at path if (args[i] == QStringLiteral("-g")) { if (i >= args.size() - 1) { @@ -345,6 +378,55 @@ GMainWindow::~GMainWindow() { delete render_window; } +void GMainWindow::RegisterMetaTypes() { + // Register integral and floating point types + qRegisterMetaType<u8>("u8"); + qRegisterMetaType<u16>("u16"); + qRegisterMetaType<u32>("u32"); + qRegisterMetaType<u64>("u64"); + qRegisterMetaType<u128>("u128"); + qRegisterMetaType<s8>("s8"); + qRegisterMetaType<s16>("s16"); + qRegisterMetaType<s32>("s32"); + qRegisterMetaType<s64>("s64"); + qRegisterMetaType<f32>("f32"); + qRegisterMetaType<f64>("f64"); + + // Register string types + qRegisterMetaType<std::string>("std::string"); + qRegisterMetaType<std::wstring>("std::wstring"); + qRegisterMetaType<std::u8string>("std::u8string"); + qRegisterMetaType<std::u16string>("std::u16string"); + qRegisterMetaType<std::u32string>("std::u32string"); + qRegisterMetaType<std::string_view>("std::string_view"); + qRegisterMetaType<std::wstring_view>("std::wstring_view"); + qRegisterMetaType<std::u8string_view>("std::u8string_view"); + qRegisterMetaType<std::u16string_view>("std::u16string_view"); + qRegisterMetaType<std::u32string_view>("std::u32string_view"); + + // Register applet types + + // Controller Applet + qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters"); + + // Software Keyboard Applet + qRegisterMetaType<Core::Frontend::KeyboardInitializeParameters>( + "Core::Frontend::KeyboardInitializeParameters"); + qRegisterMetaType<Core::Frontend::InlineAppearParameters>( + "Core::Frontend::InlineAppearParameters"); + qRegisterMetaType<Core::Frontend::InlineTextParameters>("Core::Frontend::InlineTextParameters"); + qRegisterMetaType<Service::AM::Applets::SwkbdResult>("Service::AM::Applets::SwkbdResult"); + qRegisterMetaType<Service::AM::Applets::SwkbdTextCheckResult>( + "Service::AM::Applets::SwkbdTextCheckResult"); + qRegisterMetaType<Service::AM::Applets::SwkbdReplyType>("Service::AM::Applets::SwkbdReplyType"); + + // Web Browser Applet + qRegisterMetaType<Service::AM::Applets::WebExitReason>("Service::AM::Applets::WebExitReason"); + + // Register loader types + qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus"); +} + void GMainWindow::ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters) { QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get()); @@ -357,7 +439,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers( emit ControllerSelectorReconfigureFinished(); // Don't forget to apply settings. - Settings::Apply(Core::System::GetInstance()); + Core::System::GetInstance().ApplySettings(); config->Save(); UpdateStatusButtons(); @@ -384,25 +466,112 @@ void GMainWindow::ProfileSelectorSelectProfile() { emit ProfileSelectorFinishedSelection(uuid); } -void GMainWindow::SoftwareKeyboardGetText( - const Core::Frontend::SoftwareKeyboardParameters& parameters) { - QtSoftwareKeyboardDialog dialog(this, parameters); - dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | - Qt::WindowTitleHint | Qt::WindowSystemMenuHint | - Qt::WindowCloseButtonHint); - dialog.setWindowModality(Qt::WindowModal); +void GMainWindow::SoftwareKeyboardInitialize( + bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters) { + if (software_keyboard) { + LOG_ERROR(Frontend, "The software keyboard is already initialized!"); + return; + } - if (dialog.exec() == QDialog::Rejected) { - emit SoftwareKeyboardFinishedText(std::nullopt); + software_keyboard = new QtSoftwareKeyboardDialog(render_window, Core::System::GetInstance(), + is_inline, std::move(initialize_parameters)); + + if (is_inline) { + connect( + software_keyboard, &QtSoftwareKeyboardDialog::SubmitInlineText, this, + [this](Service::AM::Applets::SwkbdReplyType reply_type, std::u16string submitted_text, + s32 cursor_position) { + emit SoftwareKeyboardSubmitInlineText(reply_type, submitted_text, cursor_position); + }, + Qt::QueuedConnection); + } else { + connect( + software_keyboard, &QtSoftwareKeyboardDialog::SubmitNormalText, this, + [this](Service::AM::Applets::SwkbdResult result, std::u16string submitted_text) { + emit SoftwareKeyboardSubmitNormalText(result, submitted_text); + }, + Qt::QueuedConnection); + } +} + +void GMainWindow::SoftwareKeyboardShowNormal() { + if (!software_keyboard) { + LOG_ERROR(Frontend, "The software keyboard is not initialized!"); + return; + } + + const auto& layout = render_window->GetFramebufferLayout(); + + const auto x = layout.screen.left; + const auto y = layout.screen.top; + const auto w = layout.screen.GetWidth(); + const auto h = layout.screen.GetHeight(); + + software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y)), QSize(w, h)); +} + +void GMainWindow::SoftwareKeyboardShowTextCheck( + Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) { + if (!software_keyboard) { + LOG_ERROR(Frontend, "The software keyboard is not initialized!"); return; } - emit SoftwareKeyboardFinishedText(dialog.GetText()); + software_keyboard->ShowTextCheckDialog(text_check_result, text_check_message); } -void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) { - QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message)); - emit SoftwareKeyboardFinishedCheckDialog(); +void GMainWindow::SoftwareKeyboardShowInline( + Core::Frontend::InlineAppearParameters appear_parameters) { + if (!software_keyboard) { + LOG_ERROR(Frontend, "The software keyboard is not initialized!"); + return; + } + + const auto& layout = render_window->GetFramebufferLayout(); + + const auto x = + static_cast<int>(layout.screen.left + (0.5f * layout.screen.GetWidth() * + ((2.0f * appear_parameters.key_top_translate_x) + + (1.0f - appear_parameters.key_top_scale_x)))); + const auto y = + static_cast<int>(layout.screen.top + (layout.screen.GetHeight() * + ((2.0f * appear_parameters.key_top_translate_y) + + (1.0f - appear_parameters.key_top_scale_y)))); + const auto w = static_cast<int>(layout.screen.GetWidth() * appear_parameters.key_top_scale_x); + const auto h = static_cast<int>(layout.screen.GetHeight() * appear_parameters.key_top_scale_y); + + software_keyboard->ShowInlineKeyboard(std::move(appear_parameters), + render_window->mapToGlobal(QPoint(x, y)), QSize(w, h)); +} + +void GMainWindow::SoftwareKeyboardHideInline() { + if (!software_keyboard) { + LOG_ERROR(Frontend, "The software keyboard is not initialized!"); + return; + } + + software_keyboard->HideInlineKeyboard(); +} + +void GMainWindow::SoftwareKeyboardInlineTextChanged( + Core::Frontend::InlineTextParameters text_parameters) { + if (!software_keyboard) { + LOG_ERROR(Frontend, "The software keyboard is not initialized!"); + return; + } + + software_keyboard->InlineTextChanged(std::move(text_parameters)); +} + +void GMainWindow::SoftwareKeyboardExit() { + if (!software_keyboard) { + return; + } + + software_keyboard->ExitKeyboard(); + + software_keyboard = nullptr; } void GMainWindow::WebBrowserOpenWebPage(std::string_view main_url, std::string_view additional_args, @@ -622,7 +791,7 @@ void GMainWindow::InitializeWidgets() { Settings::values.use_asynchronous_gpu_emulation.SetValue( !Settings::values.use_asynchronous_gpu_emulation.GetValue()); async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); - Settings::Apply(Core::System::GetInstance()); + Core::System::GetInstance().ApplySettings(); }); async_status_button->setText(tr("ASYNC")); async_status_button->setCheckable(true); @@ -638,7 +807,7 @@ void GMainWindow::InitializeWidgets() { } Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue()); multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); - Settings::Apply(Core::System::GetInstance()); + Core::System::GetInstance().ApplySettings(); }); multicore_status_button->setText(tr("MULTICORE")); multicore_status_button->setCheckable(true); @@ -669,7 +838,7 @@ void GMainWindow::InitializeWidgets() { Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL); } - Settings::Apply(Core::System::GetInstance()); + Core::System::GetInstance().ApplySettings(); }); statusBar()->insertPermanentWidget(0, renderer_status_button); @@ -948,6 +1117,10 @@ void GMainWindow::ConnectWidgetEvents() { connect(this, &GMainWindow::EmulationStopping, render_window, &GRenderWindow::OnEmulationStopping); + // Software Keyboard Applet + connect(this, &GMainWindow::EmulationStarting, this, &GMainWindow::SoftwareKeyboardExit); + connect(this, &GMainWindow::EmulationStopping, this, &GMainWindow::SoftwareKeyboardExit); + connect(&status_bar_update_timer, &QTimer::timeout, this, &GMainWindow::UpdateStatusBar); } @@ -2157,15 +2330,6 @@ void GMainWindow::OnStartGame() { emu_thread->SetRunning(true); - qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters"); - qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>( - "Core::Frontend::SoftwareKeyboardParameters"); - qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus"); - qRegisterMetaType<std::string>("std::string"); - qRegisterMetaType<std::optional<std::u16string>>("std::optional<std::u16string>"); - qRegisterMetaType<std::string_view>("std::string_view"); - qRegisterMetaType<Service::AM::Applets::WebExitReason>("Service::AM::Applets::WebExitReason"); - connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); ui.action_Start->setEnabled(false); @@ -2214,8 +2378,11 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) { BootGame(last_filename_booted, program_index); } -void GMainWindow::ErrorDisplayDisplayError(QString body) { - QMessageBox::critical(this, tr("Error Display"), body); +void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { + OverlayDialog dialog(render_window, Core::System::GetInstance(), error_code, error_text, + QString{}, tr("OK"), Qt::AlignLeft | Qt::AlignVCenter); + dialog.exec(); + emit ErrorDisplayFinished(); } @@ -2267,24 +2434,66 @@ void GMainWindow::ToggleFullscreen() { void GMainWindow::ShowFullscreen() { if (ui.action_Single_Window_Mode->isChecked()) { UISettings::values.geometry = saveGeometry(); + ui.menubar->hide(); statusBar()->hide(); - showFullScreen(); + + if (Settings::values.fullscreen_mode.GetValue() == 1) { + showFullScreen(); + return; + } + + hide(); + setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + const auto screen_geometry = QApplication::desktop()->screenGeometry(this); + setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(), + screen_geometry.height() + 1); + raise(); + showNormal(); } else { UISettings::values.renderwindow_geometry = render_window->saveGeometry(); - render_window->showFullScreen(); + + if (Settings::values.fullscreen_mode.GetValue() == 1) { + render_window->showFullScreen(); + return; + } + + render_window->hide(); + render_window->setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + const auto screen_geometry = QApplication::desktop()->screenGeometry(this); + render_window->setGeometry(screen_geometry.x(), screen_geometry.y(), + screen_geometry.width(), screen_geometry.height() + 1); + render_window->raise(); + render_window->showNormal(); } } void GMainWindow::HideFullscreen() { if (ui.action_Single_Window_Mode->isChecked()) { + if (Settings::values.fullscreen_mode.GetValue() == 1) { + showNormal(); + restoreGeometry(UISettings::values.geometry); + } else { + hide(); + setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint); + restoreGeometry(UISettings::values.geometry); + raise(); + show(); + } + statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked()); ui.menubar->show(); - showNormal(); - restoreGeometry(UISettings::values.geometry); } else { - render_window->showNormal(); - render_window->restoreGeometry(UISettings::values.renderwindow_geometry); + if (Settings::values.fullscreen_mode.GetValue() == 1) { + render_window->showNormal(); + render_window->restoreGeometry(UISettings::values.renderwindow_geometry); + } else { + render_window->hide(); + render_window->setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint); + render_window->restoreGeometry(UISettings::values.renderwindow_geometry); + render_window->raise(); + render_window->show(); + } } } diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 04d37d4ae..7f1e50a5b 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -37,9 +37,13 @@ enum class GameListRemoveTarget; enum class InstalledEntryType; class GameListPlaceholder; +class QtSoftwareKeyboardDialog; + namespace Core::Frontend { struct ControllerParameters; -struct SoftwareKeyboardParameters; +struct InlineAppearParameters; +struct InlineTextParameters; +struct KeyboardInitializeParameters; } // namespace Core::Frontend namespace DiscordRPC { @@ -57,8 +61,11 @@ class InputSubsystem; } namespace Service::AM::Applets { +enum class SwkbdResult : u32; +enum class SwkbdTextCheckResult : u32; +enum class SwkbdReplyType : u32; enum class WebExitReason : u32; -} +} // namespace Service::AM::Applets enum class EmulatedDirectoryTarget { NAND, @@ -128,8 +135,10 @@ signals: void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid); - void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); - void SoftwareKeyboardFinishedCheckDialog(); + void SoftwareKeyboardSubmitNormalText(Service::AM::Applets::SwkbdResult result, + std::u16string submitted_text); + void SoftwareKeyboardSubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, + std::u16string submitted_text, s32 cursor_position); void WebBrowserExtractOfflineRomFS(); void WebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url); @@ -139,15 +148,24 @@ public slots: void OnExecuteProgram(std::size_t program_index); void ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters); - void ErrorDisplayDisplayError(QString body); + void SoftwareKeyboardInitialize( + bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters); + void SoftwareKeyboardShowNormal(); + void SoftwareKeyboardShowTextCheck(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message); + void SoftwareKeyboardShowInline(Core::Frontend::InlineAppearParameters appear_parameters); + void SoftwareKeyboardHideInline(); + void SoftwareKeyboardInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters); + void SoftwareKeyboardExit(); + void ErrorDisplayDisplayError(QString error_code, QString error_text); void ProfileSelectorSelectProfile(); - void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); - void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); void WebBrowserOpenWebPage(std::string_view main_url, std::string_view additional_args, bool is_local); void OnAppFocusStateChanged(Qt::ApplicationState state); private: + void RegisterMetaTypes(); + void InitializeWidgets(); void InitializeDebugWidgets(); void InitializeRecentFileMenuActions(); @@ -334,6 +352,9 @@ private: // Disables the web applet for the rest of the emulated session bool disable_web_applet{}; + // Applets + QtSoftwareKeyboardDialog* software_keyboard = nullptr; + protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp new file mode 100644 index 000000000..95b148545 --- /dev/null +++ b/src/yuzu/util/overlay_dialog.cpp @@ -0,0 +1,249 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QKeyEvent> +#include <QScreen> + +#include "core/core.h" +#include "core/frontend/input_interpreter.h" +#include "ui_overlay_dialog.h" +#include "yuzu/util/overlay_dialog.h" + +namespace { + +constexpr float BASE_TITLE_FONT_SIZE = 14.0f; +constexpr float BASE_FONT_SIZE = 18.0f; +constexpr float BASE_WIDTH = 1280.0f; +constexpr float BASE_HEIGHT = 720.0f; + +} // Anonymous namespace + +OverlayDialog::OverlayDialog(QWidget* parent, Core::System& system, const QString& title_text, + const QString& body_text, const QString& left_button_text, + const QString& right_button_text, Qt::Alignment alignment, + bool use_rich_text_) + : QDialog(parent), ui{std::make_unique<Ui::OverlayDialog>()}, use_rich_text{use_rich_text_} { + ui->setupUi(this); + + setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowTitleHint | + Qt::WindowSystemMenuHint | Qt::CustomizeWindowHint); + setWindowModality(Qt::WindowModal); + setAttribute(Qt::WA_TranslucentBackground); + + if (use_rich_text) { + InitializeRichTextDialog(title_text, body_text, left_button_text, right_button_text, + alignment); + } else { + InitializeRegularTextDialog(title_text, body_text, left_button_text, right_button_text, + alignment); + } + + MoveAndResizeWindow(); + + // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend + if (system.IsPoweredOn()) { + input_interpreter = std::make_unique<InputInterpreter>(system); + + StartInputThread(); + } +} + +OverlayDialog::~OverlayDialog() { + StopInputThread(); +} + +void OverlayDialog::InitializeRegularTextDialog(const QString& title_text, const QString& body_text, + const QString& left_button_text, + const QString& right_button_text, + Qt::Alignment alignment) { + ui->stackedDialog->setCurrentIndex(0); + + ui->label_title->setText(title_text); + ui->label_dialog->setText(body_text); + ui->button_cancel->setText(left_button_text); + ui->button_ok_label->setText(right_button_text); + + ui->label_dialog->setAlignment(alignment); + + if (title_text.isEmpty()) { + ui->label_title->hide(); + ui->verticalLayout_2->setStretch(0, 0); + ui->verticalLayout_2->setStretch(1, 219); + ui->verticalLayout_2->setStretch(2, 82); + } + + if (left_button_text.isEmpty()) { + ui->button_cancel->hide(); + ui->button_cancel->setEnabled(false); + } + + if (right_button_text.isEmpty()) { + ui->button_ok_label->hide(); + ui->button_ok_label->setEnabled(false); + } + + connect( + ui->button_cancel, &QPushButton::clicked, this, + [this](bool) { + StopInputThread(); + QDialog::reject(); + }, + Qt::QueuedConnection); + connect( + ui->button_ok_label, &QPushButton::clicked, this, + [this](bool) { + StopInputThread(); + QDialog::accept(); + }, + Qt::QueuedConnection); +} + +void OverlayDialog::InitializeRichTextDialog(const QString& title_text, const QString& body_text, + const QString& left_button_text, + const QString& right_button_text, + Qt::Alignment alignment) { + ui->stackedDialog->setCurrentIndex(1); + + ui->label_title_rich->setText(title_text); + ui->text_browser_dialog->setText(body_text); + ui->button_cancel_rich->setText(left_button_text); + ui->button_ok_rich->setText(right_button_text); + + // TODO (Morph/Rei): Replace this with something that works better + ui->text_browser_dialog->setAlignment(alignment); + + if (title_text.isEmpty()) { + ui->label_title_rich->hide(); + ui->verticalLayout_3->setStretch(0, 0); + ui->verticalLayout_3->setStretch(1, 438); + ui->verticalLayout_3->setStretch(2, 82); + } + + if (left_button_text.isEmpty()) { + ui->button_cancel_rich->hide(); + ui->button_cancel_rich->setEnabled(false); + } + + if (right_button_text.isEmpty()) { + ui->button_ok_rich->hide(); + ui->button_ok_rich->setEnabled(false); + } + + connect( + ui->button_cancel_rich, &QPushButton::clicked, this, + [this](bool) { + StopInputThread(); + QDialog::reject(); + }, + Qt::QueuedConnection); + connect( + ui->button_ok_rich, &QPushButton::clicked, this, + [this](bool) { + StopInputThread(); + QDialog::accept(); + }, + Qt::QueuedConnection); +} + +void OverlayDialog::MoveAndResizeWindow() { + const auto pos = parentWidget()->mapToGlobal(parentWidget()->rect().topLeft()); + const auto width = static_cast<float>(parentWidget()->width()); + const auto height = static_cast<float>(parentWidget()->height()); + + // High DPI + const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f; + + const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; + const auto body_text_font_size = + BASE_FONT_SIZE * (((width / BASE_WIDTH) + (height / BASE_HEIGHT)) / 2.0f) / dpi_scale; + const auto button_text_font_size = BASE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; + + QFont title_text_font(QStringLiteral("MS Shell Dlg 2"), title_text_font_size, QFont::Normal); + QFont body_text_font(QStringLiteral("MS Shell Dlg 2"), body_text_font_size, QFont::Normal); + QFont button_text_font(QStringLiteral("MS Shell Dlg 2"), button_text_font_size, QFont::Normal); + + if (use_rich_text) { + ui->label_title_rich->setFont(title_text_font); + ui->text_browser_dialog->setFont(body_text_font); + ui->button_cancel_rich->setFont(button_text_font); + ui->button_ok_rich->setFont(button_text_font); + } else { + ui->label_title->setFont(title_text_font); + ui->label_dialog->setFont(body_text_font); + ui->button_cancel->setFont(button_text_font); + ui->button_ok_label->setFont(button_text_font); + } + + QDialog::move(pos); + QDialog::resize(width, height); +} + +template <HIDButton... T> +void OverlayDialog::HandleButtonPressedOnce() { + const auto f = [this](HIDButton button) { + if (input_interpreter->IsButtonPressedOnce(button)) { + TranslateButtonPress(button); + } + }; + + (f(T), ...); +} + +void OverlayDialog::TranslateButtonPress(HIDButton button) { + QPushButton* left_button = use_rich_text ? ui->button_cancel_rich : ui->button_cancel; + QPushButton* right_button = use_rich_text ? ui->button_ok_rich : ui->button_ok_label; + + // TODO (Morph): Handle QTextBrowser text scrolling + // TODO (Morph): focusPrevious/NextChild() doesn't work well with the rich text dialog, fix it + + switch (button) { + case HIDButton::A: + case HIDButton::B: + if (left_button->hasFocus()) { + left_button->click(); + } else if (right_button->hasFocus()) { + right_button->click(); + } + break; + case HIDButton::DLeft: + case HIDButton::LStickLeft: + focusPreviousChild(); + break; + case HIDButton::DRight: + case HIDButton::LStickRight: + focusNextChild(); + break; + default: + break; + } +} + +void OverlayDialog::StartInputThread() { + if (input_thread_running) { + return; + } + + input_thread_running = true; + + input_thread = std::thread(&OverlayDialog::InputThread, this); +} + +void OverlayDialog::StopInputThread() { + input_thread_running = false; + + if (input_thread.joinable()) { + input_thread.join(); + } +} + +void OverlayDialog::InputThread() { + while (input_thread_running) { + input_interpreter->PollInput(); + + HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::DLeft, HIDButton::DRight, + HIDButton::LStickLeft, HIDButton::LStickRight>(); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } +} diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h new file mode 100644 index 000000000..e8c388bd0 --- /dev/null +++ b/src/yuzu/util/overlay_dialog.h @@ -0,0 +1,107 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <atomic> +#include <memory> +#include <thread> + +#include <QDialog> + +#include "common/common_types.h" + +enum class HIDButton : u8; + +class InputInterpreter; + +namespace Core { +class System; +} + +namespace Ui { +class OverlayDialog; +} + +/** + * An OverlayDialog is an interactive dialog that accepts controller input (while a game is running) + * This dialog attempts to replicate the look and feel of the Nintendo Switch's overlay dialogs and + * provide some extra features such as embedding HTML/Rich Text content in a QTextBrowser. + * The OverlayDialog provides 2 modes: one to embed regular text into a QLabel and another to embed + * HTML/Rich Text content into a QTextBrowser. + */ +class OverlayDialog final : public QDialog { + Q_OBJECT + +public: + explicit OverlayDialog(QWidget* parent, Core::System& system, const QString& title_text, + const QString& body_text, const QString& left_button_text, + const QString& right_button_text, + Qt::Alignment alignment = Qt::AlignCenter, bool use_rich_text_ = false); + ~OverlayDialog() override; + +private: + /** + * Initializes a text dialog with a QLabel storing text. + * Only use this for short text as the dialog buttons would be squashed with longer text. + * + * @param title_text Title text to be displayed + * @param body_text Main text to be displayed + * @param left_button_text Left button text. If empty, the button is hidden and disabled + * @param right_button_text Right button text. If empty, the button is hidden and disabled + * @param alignment Main text alignment + */ + void InitializeRegularTextDialog(const QString& title_text, const QString& body_text, + const QString& left_button_text, + const QString& right_button_text, Qt::Alignment alignment); + + /** + * Initializes a text dialog with a QTextBrowser storing text. + * This is ideal for longer text or rich text content. A scrollbar is shown for longer text. + * + * @param title_text Title text to be displayed + * @param body_text Main text to be displayed + * @param left_button_text Left button text. If empty, the button is hidden and disabled + * @param right_button_text Right button text. If empty, the button is hidden and disabled + * @param alignment Main text alignment + */ + void InitializeRichTextDialog(const QString& title_text, const QString& body_text, + const QString& left_button_text, const QString& right_button_text, + Qt::Alignment alignment); + + /// Moves and resizes the dialog to be fully overlayed on top of the parent window. + void MoveAndResizeWindow(); + + /** + * Handles button presses and converts them into keyboard input. + * + * @tparam HIDButton The list of buttons that can be converted into keyboard input. + */ + template <HIDButton... T> + void HandleButtonPressedOnce(); + + /** + * Translates a button press to focus or click either the left or right buttons. + * + * @param button The button press to process. + */ + void TranslateButtonPress(HIDButton button); + + void StartInputThread(); + void StopInputThread(); + + /// The thread where input is being polled and processed. + void InputThread(); + + std::unique_ptr<Ui::OverlayDialog> ui; + + bool use_rich_text; + + std::unique_ptr<InputInterpreter> input_interpreter; + + std::thread input_thread; + + std::atomic<bool> input_thread_running{}; +}; diff --git a/src/yuzu/util/overlay_dialog.ui b/src/yuzu/util/overlay_dialog.ui new file mode 100644 index 000000000..278e2f219 --- /dev/null +++ b/src/yuzu/util/overlay_dialog.ui @@ -0,0 +1,404 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>OverlayDialog</class> + <widget class="QDialog" name="OverlayDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1280</width> + <height>720</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QStackedWidget" name="stackedDialog"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="lineDialog"> + <layout class="QGridLayout" name="lineDialogGridLayout" rowstretch="210,300,210" columnstretch="250,780,250"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <item row="1" column="1"> + <widget class="QWidget" name="contentDialog" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="70,149,82"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_title"> + <property name="font"> + <font> + <pointsize>14</pointsize> + </font> + </property> + <property name="alignment"> + <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_dialog"> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QWidget" name="buttonsDialog" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="button_cancel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="button_ok_label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string>OK</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item row="0" column="1"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1"> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="2"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="richDialog"> + <layout class="QGridLayout" name="richDialogGridLayout" rowstretch="100,520,100" columnstretch="165,950,165"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <item row="1" column="0"> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1"> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="1"> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1"> + <widget class="QWidget" name="contentRichDialog" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_3" stretch="70,368,82"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_title_rich"> + <property name="font"> + <font> + <pointsize>14</pointsize> + </font> + </property> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> + </property> + </widget> + </item> + <item> + <widget class="QTextBrowser" name="text_browser_dialog"> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="html"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:18pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></string> + </property> + </widget> + </item> + <item> + <widget class="QWidget" name="buttonsRichDialog" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="button_cancel_rich"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="button_ok_rich"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + </font> + </property> + <property name="text"> + <string>OK</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="2"> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <resources> + <include location="../../../dist/icons/overlay/overlay.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 43877fc98..2f984d1b8 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -9,8 +9,8 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/param_package.h" +#include "common/settings.h" #include "core/hle/service/acc/profile_manager.h" -#include "core/settings.h" #include "input_common/main.h" #include "input_common/udp/client.h" #include "yuzu_cmd/config.h" @@ -428,6 +428,10 @@ void Config::ReadValues() { Settings::values.reporting_services = sdl2_config->GetBoolean("Debugging", "reporting_services", false); Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false); + Settings::values.use_debug_asserts = + sdl2_config->GetBoolean("Debugging", "use_debug_asserts", false); + Settings::values.use_auto_stub = sdl2_config->GetBoolean("Debugging", "use_auto_stub", false); + Settings::values.disable_macro_jit = sdl2_config->GetBoolean("Debugging", "disable_macro_jit", false); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 3ee0e037d..4ce8e08e4 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -325,6 +325,12 @@ dump_nso=false # Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode # false: Retail/Normal Mode (default), true: Kiosk Mode quest_flag = +# Determines whether debug asserts should be enabled, which will throw an exception on asserts. +# false: Disabled (default), true: Enabled +use_debug_asserts = +# Determines whether unimplemented HLE service calls should be automatically stubbed. +# false: Disabled (default), true: Enabled +use_auto_stub = # Enables/Disables the macro JIT compiler disable_macro_jit=false diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index a02485c14..a765fa7b3 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp @@ -12,9 +12,9 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/scm_rev.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/core.h" -#include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" #include "video_core/renderer_base.h" diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index 6f9b00461..dfd53e285 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp @@ -11,7 +11,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/scm_rev.h" -#include "core/settings.h" +#include "common/settings.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 982c41785..4871ac3bb 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -20,6 +20,7 @@ #include "common/nvidia_flags.h" #include "common/scm_rev.h" #include "common/scope_exit.h" +#include "common/settings.h" #include "common/string_util.h" #include "common/telemetry.h" #include "core/core.h" @@ -29,7 +30,6 @@ #include "core/hle/kernel/process.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" -#include "core/settings.h" #include "core/telemetry_session.h" #include "input_common/main.h" #include "video_core/renderer_base.h" @@ -74,14 +74,16 @@ static void PrintVersion() { } static void InitializeLogging() { + using namespace Common; + Log::Filter log_filter(Log::Level::Debug); log_filter.ParseFilterString(Settings::values.log_filter); Log::SetGlobalFilter(log_filter); Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); - const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir); - Common::FS::CreateFullPath(log_dir); + const std::string& log_dir = FS::GetUserPath(FS::UserPath::LogDir); + FS::CreateFullPath(log_dir); Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); #ifdef _WIN32 Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); @@ -164,7 +166,7 @@ int main(int argc, char** argv) { InputCommon::InputSubsystem input_subsystem; // Apply the command line arguments - Settings::Apply(system); + system.ApplySettings(); std::unique_ptr<EmuWindow_SDL2> emu_window; switch (Settings::values.renderer_backend.GetValue()) { |