diff options
Diffstat (limited to 'src')
182 files changed, 5757 insertions, 3151 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 207c7a0a6..d20e6c3b5 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -102,6 +102,7 @@ add_library(common STATIC atomic_ops.h detached_tasks.cpp detached_tasks.h + bit_cast.h bit_field.h bit_util.h cityhash.cpp diff --git a/src/common/bit_cast.h b/src/common/bit_cast.h new file mode 100644 index 000000000..a32a063d1 --- /dev/null +++ b/src/common/bit_cast.h @@ -0,0 +1,22 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <cstring> +#include <type_traits> + +namespace Common { + +template <typename To, typename From> +[[nodiscard]] std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From> && + std::is_trivially_copyable_v<To>, + To> +BitCast(const From& src) noexcept { + To dst; + std::memcpy(&dst, &src, sizeof(To)); + return dst; +} + +} // namespace Common diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 90dfa22ca..631f64d05 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -23,6 +23,7 @@ #include "common/logging/text_formatter.h" #include "common/string_util.h" #include "common/threadsafe_queue.h" +#include "core/settings.h" namespace Log { @@ -152,10 +153,19 @@ FileBackend::FileBackend(const std::string& filename) void FileBackend::Write(const Entry& entry) { // prevent logs from going over the maximum size (in case its spamming and the user doesn't // know) - constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; - if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { + constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024; + constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024; + + if (!file.IsOpen()) { + return; + } + + if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) { + return; + } else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) { return; } + bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n')); if (entry.log_level >= Level::Error) { file.Flush(); @@ -222,6 +232,7 @@ void DebuggerBackend::Write(const Entry& entry) { SUB(Service, NPNS) \ SUB(Service, NS) \ SUB(Service, NVDRV) \ + SUB(Service, OLSC) \ SUB(Service, PCIE) \ SUB(Service, PCTL) \ SUB(Service, PCV) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 13a4f1e30..835894918 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -95,6 +95,7 @@ enum class Class : ClassType { Service_NPNS, ///< The NPNS service Service_NS, ///< The NS services Service_NVDRV, ///< The NVDRV (Nvidia driver) service + Service_OLSC, ///< The OLSC service Service_PCIE, ///< The PCIe service Service_PCTL, ///< The PCTL (Parental control) service Service_PCV, ///< The PCV service diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp index e5d3090d5..bccea0894 100644 --- a/src/common/page_table.cpp +++ b/src/common/page_table.cpp @@ -8,7 +8,7 @@ namespace Common { PageTable::PageTable() = default; -PageTable::~PageTable() = default; +PageTable::~PageTable() noexcept = default; void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, bool has_attribute) { diff --git a/src/common/page_table.h b/src/common/page_table.h index cf5eed780..9754fabf9 100644 --- a/src/common/page_table.h +++ b/src/common/page_table.h @@ -4,9 +4,7 @@ #pragma once -#include <vector> - -#include <boost/icl/interval_map.hpp> +#include <tuple> #include "common/common_types.h" #include "common/memory_hook.h" @@ -51,13 +49,21 @@ struct SpecialRegion { */ struct PageTable { PageTable(); - ~PageTable(); + ~PageTable() noexcept; + + PageTable(const PageTable&) = delete; + PageTable& operator=(const PageTable&) = delete; + + PageTable(PageTable&&) noexcept = default; + PageTable& operator=(PageTable&&) noexcept = default; /** * Resizes the page table to be able to accomodate enough pages within * a given address space. * * @param address_space_width_in_bits The address size width in bits. + * @param page_size_in_bits The page size in bits. + * @param has_attribute Whether or not this page has any backing attributes. */ void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, bool has_attribute); diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp index b009cb500..e3ca29258 100644 --- a/src/common/virtual_buffer.cpp +++ b/src/common/virtual_buffer.cpp @@ -13,7 +13,7 @@ namespace Common { -void* AllocateMemoryPages(std::size_t size) { +void* AllocateMemoryPages(std::size_t size) noexcept { #ifdef _WIN32 void* base{VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE)}; #else @@ -29,7 +29,7 @@ void* AllocateMemoryPages(std::size_t size) { return base; } -void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) { +void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept { if (!base) { return; } diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h index 125cb42f0..91d430036 100644 --- a/src/common/virtual_buffer.h +++ b/src/common/virtual_buffer.h @@ -4,29 +4,53 @@ #pragma once -#include "common/common_funcs.h" +#include <type_traits> +#include <utility> namespace Common { -void* AllocateMemoryPages(std::size_t size); -void FreeMemoryPages(void* base, std::size_t size); +void* AllocateMemoryPages(std::size_t size) noexcept; +void FreeMemoryPages(void* base, std::size_t size) noexcept; template <typename T> -class VirtualBuffer final : NonCopyable { +class VirtualBuffer final { public: + static_assert( + std::is_trivially_constructible_v<T>, + "T must be trivially constructible, as non-trivial constructors will not be executed " + "with the current allocator"); + constexpr VirtualBuffer() = default; explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} { base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size)); } - ~VirtualBuffer() { + ~VirtualBuffer() noexcept { FreeMemoryPages(base_ptr, alloc_size); } + VirtualBuffer(const VirtualBuffer&) = delete; + VirtualBuffer& operator=(const VirtualBuffer&) = delete; + + VirtualBuffer(VirtualBuffer&& other) noexcept + : alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr), + nullptr} {} + + VirtualBuffer& operator=(VirtualBuffer&& other) noexcept { + alloc_size = std::exchange(other.alloc_size, 0); + base_ptr = std::exchange(other.base_ptr, nullptr); + return *this; + } + void resize(std::size_t count) { + const auto new_size = count * sizeof(T); + if (new_size == alloc_size) { + return; + } + FreeMemoryPages(base_ptr, alloc_size); - alloc_size = count * sizeof(T); + alloc_size = new_size; base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size)); } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a1d8dcfa5..e370fd225 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -458,6 +458,8 @@ add_library(core STATIC hle/service/nvflinger/buffer_queue.h hle/service/nvflinger/nvflinger.cpp hle/service/nvflinger/nvflinger.h + hle/service/olsc/olsc.cpp + hle/service/olsc/olsc.h hle/service/pcie/pcie.cpp hle/service/pcie/pcie.h hle/service/pctl/module.cpp diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h index 71e582f79..c20c280f1 100644 --- a/src/core/arm/cpu_interrupt_handler.h +++ b/src/core/arm/cpu_interrupt_handler.h @@ -21,8 +21,8 @@ public: CPUInterruptHandler(const CPUInterruptHandler&) = delete; CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete; - CPUInterruptHandler(CPUInterruptHandler&&) = default; - CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default; + CPUInterruptHandler(CPUInterruptHandler&&) = delete; + CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete; bool IsInterrupted() const { return is_interrupted; diff --git a/src/core/core.cpp b/src/core/core.cpp index 242796008..7ca3652af 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -145,7 +145,7 @@ struct System::Impl { } ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { - LOG_DEBUG(HW_Memory, "initialized OK"); + LOG_DEBUG(Core, "initialized OK"); device_memory = std::make_unique<Core::DeviceMemory>(); @@ -187,7 +187,7 @@ struct System::Impl { service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); - Service::Init(service_manager, system); + services = std::make_unique<Service::Services>(service_manager, system); GDBStub::DeferStart(); interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); @@ -208,9 +208,11 @@ struct System::Impl { return ResultStatus::Success; } - ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, - const std::string& filepath) { - app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); + ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, + std::size_t program_index) { + app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath), + program_index); + if (!app_loader) { LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); return ResultStatus::ErrorGetLoader; @@ -224,7 +226,7 @@ struct System::Impl { return init_result; } - telemetry_session->AddInitialInfo(*app_loader); + telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); auto main_process = Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland); const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); @@ -296,7 +298,7 @@ struct System::Impl { // Shutdown emulation session GDBStub::Shutdown(); - Service::Shutdown(); + services.reset(); service_manager.reset(); cheat_engine.reset(); telemetry_session.reset(); @@ -306,8 +308,8 @@ struct System::Impl { cpu_manager.Shutdown(); // Shutdown kernel and core timing - kernel.Shutdown(); core_timing.Shutdown(); + kernel.Shutdown(); // Close app loader app_loader.reset(); @@ -338,7 +340,7 @@ struct System::Impl { Service::Glue::ApplicationLaunchProperty launch{}; launch.title_id = process.GetTitleID(); - FileSys::PatchManager pm{launch.title_id}; + FileSys::PatchManager pm{launch.title_id, fs_controller, *content_provider}; launch.version = pm.GetGameVersion().value_or(0); // TODO(DarkLordZach): When FSController/Game Card Support is added, if @@ -398,6 +400,9 @@ struct System::Impl { /// Service manager std::shared_ptr<Service::SM::ServiceManager> service_manager; + /// Services + std::unique_ptr<Service::Services> services; + /// Telemetry session for this emulation session std::unique_ptr<Core::TelemetrySession> telemetry_session; @@ -413,6 +418,8 @@ struct System::Impl { bool is_multicore{}; bool is_async_gpu{}; + ExecuteProgramCallback execute_program_callback; + std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{}; }; @@ -444,8 +451,13 @@ void System::InvalidateCpuInstructionCaches() { impl->kernel.InvalidateAllInstructionCaches(); } -System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { - return impl->Load(*this, emu_window, filepath); +void System::Shutdown() { + impl->Shutdown(); +} + +System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath, + std::size_t program_index) { + return impl->Load(*this, emu_window, filepath, program_index); } bool System::IsPoweredOn() const { @@ -632,7 +644,11 @@ const std::string& System::GetStatusDetails() const { return impl->status_details; } -Loader::AppLoader& System::GetAppLoader() const { +Loader::AppLoader& System::GetAppLoader() { + return *impl->app_loader; +} + +const Loader::AppLoader& System::GetAppLoader() const { return *impl->app_loader; } @@ -748,14 +764,6 @@ const System::CurrentBuildProcessID& System::GetCurrentProcessBuildID() const { return impl->build_id; } -System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { - return impl->Init(*this, emu_window); -} - -void System::Shutdown() { - impl->Shutdown(); -} - Service::SM::ServiceManager& System::ServiceManager() { return *impl->service_manager; } @@ -786,4 +794,16 @@ bool System::IsMulticore() const { return impl->is_multicore; } +void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) { + impl->execute_program_callback = std::move(callback); +} + +void System::ExecuteProgram(std::size_t program_index) { + if (impl->execute_program_callback) { + impl->execute_program_callback(program_index); + } else { + LOG_CRITICAL(Core, "execute_program_callback must be initialized by the frontend"); + } +} + } // namespace Core diff --git a/src/core/core.h b/src/core/core.h index 6db896bae..f642befc0 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -5,6 +5,7 @@ #pragma once #include <cstddef> +#include <functional> #include <memory> #include <string> #include <vector> @@ -144,19 +145,19 @@ public: * Run the OS and Application * This function will start emulation and run the relevant devices */ - ResultStatus Run(); + [[nodiscard]] ResultStatus Run(); /** * Pause the OS and Application * This function will pause emulation and stop the relevant devices */ - ResultStatus Pause(); + [[nodiscard]] ResultStatus Pause(); /** * Step the CPU one instruction * @return Result status, indicating whether or not the operation succeeded. */ - ResultStatus SingleStep(); + [[nodiscard]] ResultStatus SingleStep(); /** * Invalidate the CPU instruction caches @@ -173,22 +174,24 @@ public: * @param emu_window Reference to the host-system window used for video output and keyboard * input. * @param filepath String path to the executable application to load on the host file system. + * @param program_index Specifies the index within the container of the program to launch. * @returns ResultStatus code, indicating if the operation succeeded. */ - ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath); + [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath, + std::size_t program_index = 0); /** * Indicates if the emulated system is powered on (all subsystems initialized and able to run an * application). * @returns True if the emulated system is powered on, otherwise false. */ - bool IsPoweredOn() const; + [[nodiscard]] bool IsPoweredOn() const; /// Gets a reference to the telemetry session for this emulation session. - Core::TelemetrySession& TelemetrySession(); + [[nodiscard]] Core::TelemetrySession& TelemetrySession(); /// Gets a reference to the telemetry session for this emulation session. - const Core::TelemetrySession& TelemetrySession() const; + [[nodiscard]] const Core::TelemetrySession& TelemetrySession() const; /// Prepare the core emulation for a reschedule void PrepareReschedule(); @@ -197,185 +200,178 @@ public: void PrepareReschedule(u32 core_index); /// Gets and resets core performance statistics - PerfStatsResults GetAndResetPerfStats(); + [[nodiscard]] PerfStatsResults GetAndResetPerfStats(); /// Gets an ARM interface to the CPU core that is currently running - ARM_Interface& CurrentArmInterface(); + [[nodiscard]] ARM_Interface& CurrentArmInterface(); /// Gets an ARM interface to the CPU core that is currently running - const ARM_Interface& CurrentArmInterface() const; + [[nodiscard]] const ARM_Interface& CurrentArmInterface() const; /// Gets the index of the currently running CPU core - std::size_t CurrentCoreIndex() const; + [[nodiscard]] std::size_t CurrentCoreIndex() const; /// Gets the scheduler for the CPU core that is currently running - Kernel::Scheduler& CurrentScheduler(); + [[nodiscard]] Kernel::Scheduler& CurrentScheduler(); /// Gets the scheduler for the CPU core that is currently running - const Kernel::Scheduler& CurrentScheduler() const; + [[nodiscard]] const Kernel::Scheduler& CurrentScheduler() const; /// Gets the physical core for the CPU core that is currently running - Kernel::PhysicalCore& CurrentPhysicalCore(); + [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore(); /// Gets the physical core for the CPU core that is currently running - const Kernel::PhysicalCore& CurrentPhysicalCore() const; + [[nodiscard]] const Kernel::PhysicalCore& CurrentPhysicalCore() const; /// Gets a reference to an ARM interface for the CPU core with the specified index - ARM_Interface& ArmInterface(std::size_t core_index); + [[nodiscard]] ARM_Interface& ArmInterface(std::size_t core_index); /// Gets a const reference to an ARM interface from the CPU core with the specified index - const ARM_Interface& ArmInterface(std::size_t core_index) const; + [[nodiscard]] const ARM_Interface& ArmInterface(std::size_t core_index) const; - CpuManager& GetCpuManager(); + /// Gets a reference to the underlying CPU manager. + [[nodiscard]] CpuManager& GetCpuManager(); - const CpuManager& GetCpuManager() const; + /// Gets a const reference to the underlying CPU manager + [[nodiscard]] const CpuManager& GetCpuManager() const; /// Gets a reference to the exclusive monitor - ExclusiveMonitor& Monitor(); + [[nodiscard]] ExclusiveMonitor& Monitor(); /// Gets a constant reference to the exclusive monitor - const ExclusiveMonitor& Monitor() const; + [[nodiscard]] const ExclusiveMonitor& Monitor() const; /// Gets a mutable reference to the system memory instance. - Core::Memory::Memory& Memory(); + [[nodiscard]] Core::Memory::Memory& Memory(); /// Gets a constant reference to the system memory instance. - const Core::Memory::Memory& Memory() const; + [[nodiscard]] const Core::Memory::Memory& Memory() const; /// Gets a mutable reference to the GPU interface - Tegra::GPU& GPU(); + [[nodiscard]] Tegra::GPU& GPU(); /// Gets an immutable reference to the GPU interface. - const Tegra::GPU& GPU() const; + [[nodiscard]] const Tegra::GPU& GPU() const; /// Gets a mutable reference to the renderer. - VideoCore::RendererBase& Renderer(); + [[nodiscard]] VideoCore::RendererBase& Renderer(); /// Gets an immutable reference to the renderer. - const VideoCore::RendererBase& Renderer() const; + [[nodiscard]] const VideoCore::RendererBase& Renderer() const; /// Gets the scheduler for the CPU core with the specified index - Kernel::Scheduler& Scheduler(std::size_t core_index); + [[nodiscard]] Kernel::Scheduler& Scheduler(std::size_t core_index); /// Gets the scheduler for the CPU core with the specified index - const Kernel::Scheduler& Scheduler(std::size_t core_index) const; + [[nodiscard]] const Kernel::Scheduler& Scheduler(std::size_t core_index) const; /// Gets the global scheduler - Kernel::GlobalScheduler& GlobalScheduler(); + [[nodiscard]] Kernel::GlobalScheduler& GlobalScheduler(); /// Gets the global scheduler - const Kernel::GlobalScheduler& GlobalScheduler() const; + [[nodiscard]] const Kernel::GlobalScheduler& GlobalScheduler() const; /// Gets the manager for the guest device memory - Core::DeviceMemory& DeviceMemory(); + [[nodiscard]] Core::DeviceMemory& DeviceMemory(); /// Gets the manager for the guest device memory - const Core::DeviceMemory& DeviceMemory() const; + [[nodiscard]] const Core::DeviceMemory& DeviceMemory() const; /// Provides a pointer to the current process - Kernel::Process* CurrentProcess(); + [[nodiscard]] Kernel::Process* CurrentProcess(); /// Provides a constant pointer to the current process. - const Kernel::Process* CurrentProcess() const; + [[nodiscard]] const Kernel::Process* CurrentProcess() const; /// Provides a reference to the core timing instance. - Timing::CoreTiming& CoreTiming(); + [[nodiscard]] Timing::CoreTiming& CoreTiming(); /// Provides a constant reference to the core timing instance. - const Timing::CoreTiming& CoreTiming() const; + [[nodiscard]] const Timing::CoreTiming& CoreTiming() const; /// Provides a reference to the interrupt manager instance. - Core::Hardware::InterruptManager& InterruptManager(); + [[nodiscard]] Core::Hardware::InterruptManager& InterruptManager(); /// Provides a constant reference to the interrupt manager instance. - const Core::Hardware::InterruptManager& InterruptManager() const; + [[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const; /// Provides a reference to the kernel instance. - Kernel::KernelCore& Kernel(); + [[nodiscard]] Kernel::KernelCore& Kernel(); /// Provides a constant reference to the kernel instance. - const Kernel::KernelCore& Kernel() const; + [[nodiscard]] const Kernel::KernelCore& Kernel() const; /// Provides a reference to the internal PerfStats instance. - Core::PerfStats& GetPerfStats(); + [[nodiscard]] Core::PerfStats& GetPerfStats(); /// Provides a constant reference to the internal PerfStats instance. - const Core::PerfStats& GetPerfStats() const; + [[nodiscard]] const Core::PerfStats& GetPerfStats() const; /// Provides a reference to the frame limiter; - Core::FrameLimiter& FrameLimiter(); + [[nodiscard]] Core::FrameLimiter& FrameLimiter(); /// Provides a constant referent to the frame limiter - const Core::FrameLimiter& FrameLimiter() const; + [[nodiscard]] const Core::FrameLimiter& FrameLimiter() const; /// Gets the name of the current game - Loader::ResultStatus GetGameName(std::string& out) const; + [[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const; void SetStatus(ResultStatus new_status, const char* details); - const std::string& GetStatusDetails() const; + [[nodiscard]] const std::string& GetStatusDetails() const; - Loader::AppLoader& GetAppLoader() const; + [[nodiscard]] Loader::AppLoader& GetAppLoader(); + [[nodiscard]] const Loader::AppLoader& GetAppLoader() const; - Service::SM::ServiceManager& ServiceManager(); - const Service::SM::ServiceManager& ServiceManager() const; + [[nodiscard]] Service::SM::ServiceManager& ServiceManager(); + [[nodiscard]] const Service::SM::ServiceManager& ServiceManager() const; void SetFilesystem(FileSys::VirtualFilesystem vfs); - FileSys::VirtualFilesystem GetFilesystem() const; + [[nodiscard]] FileSys::VirtualFilesystem GetFilesystem() const; void RegisterCheatList(const std::vector<Memory::CheatEntry>& list, const std::array<u8, 0x20>& build_id, VAddr main_region_begin, u64 main_region_size); void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set); - void SetDefaultAppletFrontendSet(); - Service::AM::Applets::AppletManager& GetAppletManager(); - - const Service::AM::Applets::AppletManager& GetAppletManager() const; + [[nodiscard]] Service::AM::Applets::AppletManager& GetAppletManager(); + [[nodiscard]] const Service::AM::Applets::AppletManager& GetAppletManager() const; void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider); - FileSys::ContentProvider& GetContentProvider(); - - const FileSys::ContentProvider& GetContentProvider() const; + [[nodiscard]] FileSys::ContentProvider& GetContentProvider(); + [[nodiscard]] const FileSys::ContentProvider& GetContentProvider() const; - Service::FileSystem::FileSystemController& GetFileSystemController(); - - const Service::FileSystem::FileSystemController& GetFileSystemController() const; + [[nodiscard]] Service::FileSystem::FileSystemController& GetFileSystemController(); + [[nodiscard]] const Service::FileSystem::FileSystemController& GetFileSystemController() const; void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, FileSys::ContentProvider* provider); void ClearContentProvider(FileSys::ContentProviderUnionSlot slot); - const Reporter& GetReporter() const; - - Service::Glue::ARPManager& GetARPManager(); + [[nodiscard]] const Reporter& GetReporter() const; - const Service::Glue::ARPManager& GetARPManager() const; + [[nodiscard]] Service::Glue::ARPManager& GetARPManager(); + [[nodiscard]] const Service::Glue::ARPManager& GetARPManager() const; - Service::APM::Controller& GetAPMController(); + [[nodiscard]] Service::APM::Controller& GetAPMController(); + [[nodiscard]] const Service::APM::Controller& GetAPMController() const; - const Service::APM::Controller& GetAPMController() const; + [[nodiscard]] Service::LM::Manager& GetLogManager(); + [[nodiscard]] const Service::LM::Manager& GetLogManager() const; - Service::LM::Manager& GetLogManager(); - - const Service::LM::Manager& GetLogManager() const; - - Service::Time::TimeManager& GetTimeManager(); - - const Service::Time::TimeManager& GetTimeManager() const; + [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); + [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; void SetExitLock(bool locked); - - bool GetExitLock() const; + [[nodiscard]] bool GetExitLock() const; void SetCurrentProcessBuildID(const CurrentBuildProcessID& id); - - const CurrentBuildProcessID& GetCurrentProcessBuildID() const; + [[nodiscard]] const CurrentBuildProcessID& GetCurrentProcessBuildID() const; /// Register a host thread as an emulated CPU Core. void RegisterCoreThread(std::size_t id); @@ -390,18 +386,27 @@ public: void ExitDynarmicProfile(); /// Tells if system is running on multicore. - bool IsMulticore() const; + [[nodiscard]] bool IsMulticore() const; -private: - System(); + /// Type used for the frontend to designate a callback for System to re-launch the application + /// using a specified program index. + using ExecuteProgramCallback = std::function<void(std::size_t)>; /** - * Initialize the emulated system. - * @param emu_window Reference to the host-system window used for video output and keyboard - * input. - * @return ResultStatus code, indicating if the operation succeeded. + * Registers a callback from the frontend for System to re-launch the application using a + * specified program index. + * @param callback Callback from the frontend to relaunch the application. + */ + void RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback); + + /** + * Instructs the frontend to re-launch the application using the specified program_index. + * @param program_index Specifies the index within the application of the program to launch. */ - ResultStatus Init(Frontend::EmuWindow& emu_window); + void ExecuteProgram(std::size_t program_index); + +private: + System(); struct Impl; std::unique_ptr<Impl> impl; diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 956da68f7..8dee5590b 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -29,7 +29,7 @@ constexpr std::array partition_names{ "logo", }; -XCI::XCI(VirtualFile file_) +XCI::XCI(VirtualFile file_, std::size_t program_index) : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, partitions(partition_names.size()), partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} { @@ -62,7 +62,8 @@ XCI::XCI(VirtualFile file_) } secure_partition = std::make_shared<NSP>( - main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)])); + main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]), + program_index); ncas = secure_partition->GetNCAsCollapsed(); program = diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index 2d0a0f285..4960e90fe 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -78,7 +78,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo }; class XCI : public ReadOnlyVfsDirectory { public: - explicit XCI(VirtualFile file); + explicit XCI(VirtualFile file, std::size_t program_index = 0); ~XCI() override; Loader::ResultStatus GetStatus() const; diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 807b05821..e9d1607d0 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -112,7 +112,10 @@ bool IsDirValidAndNonEmpty(const VirtualDir& dir) { } } // Anonymous namespace -PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} +PatchManager::PatchManager(u64 title_id_, + const Service::FileSystem::FileSystemController& fs_controller_, + const ContentProvider& content_provider_) + : title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {} PatchManager::~PatchManager() = default; @@ -128,34 +131,30 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { if (Settings::values.dump_exefs) { LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); - const auto dump_dir = - Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id); + const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id); if (dump_dir != nullptr) { const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); VfsRawCopyD(exefs, exefs_dir); } } - const auto& installed = Core::System::GetInstance().GetContentProvider(); - const auto& disabled = Settings::values.disabled_addons[title_id]; const auto update_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); // Game Updates const auto update_tid = GetUpdateTitleID(title_id); - const auto update = installed.GetEntry(update_tid, ContentRecordType::Program); + const auto update = content_provider.GetEntry(update_tid, ContentRecordType::Program); if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr && update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", - FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); + FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0))); exefs = update->GetExeFS(); } // LayeredExeFS - const auto load_dir = - Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); + const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); if (load_dir != nullptr && load_dir->GetSize() > 0) { auto patch_dirs = load_dir->GetSubdirectories(); std::sort( @@ -241,8 +240,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st if (Settings::values.dump_nso) { LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id, title_id); - const auto dump_dir = - Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id); + const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id); if (dump_dir != nullptr) { const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id)); @@ -254,8 +252,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); - const auto load_dir = - Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); + const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); if (load_dir == nullptr) { LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); return nso; @@ -298,8 +295,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_) const { LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); - const auto load_dir = - Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); + const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); if (load_dir == nullptr) { LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); return false; @@ -313,8 +309,8 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_) const { } std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( - const Core::System& system, const BuildID& build_id_) const { - const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); + const BuildID& build_id_) const { + const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); if (load_dir == nullptr) { LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); return {}; @@ -347,9 +343,9 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( return out; } -static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { - const auto load_dir = - Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); +static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type, + const Service::FileSystem::FileSystemController& fs_controller) { + const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || load_dir == nullptr || load_dir->GetSize() <= 0) { return; @@ -411,19 +407,19 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}", title_id, static_cast<u8>(type)); - if (type == ContentRecordType::Program || type == ContentRecordType::Data) + if (type == ContentRecordType::Program || type == ContentRecordType::Data) { LOG_INFO(Loader, "{}", log_string); - else + } else { LOG_DEBUG(Loader, "{}", log_string); + } - if (romfs == nullptr) + if (romfs == nullptr) { return romfs; - - const auto& installed = Core::System::GetInstance().GetContentProvider(); + } // Game Updates const auto update_tid = GetUpdateTitleID(title_id); - const auto update = installed.GetEntryRaw(update_tid, type); + const auto update = content_provider.GetEntryRaw(update_tid, type); const auto& disabled = Settings::values.disabled_addons[title_id]; const auto update_disabled = @@ -434,7 +430,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content if (new_nca->GetStatus() == Loader::ResultStatus::Success && new_nca->GetRomFS() != nullptr) { LOG_INFO(Loader, " RomFS: Update ({}) applied successfully", - FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); + FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0))); romfs = new_nca->GetRomFS(); } } else if (!update_disabled && update_raw != nullptr) { @@ -447,7 +443,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content } // LayeredFS - ApplyLayeredFS(romfs, title_id, type); + ApplyLayeredFS(romfs, title_id, type, fs_controller); return romfs; } @@ -458,12 +454,11 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u } std::map<std::string, std::string, std::less<>> out; - const auto& installed = Core::System::GetInstance().GetContentProvider(); const auto& disabled = Settings::values.disabled_addons[title_id]; // Game Updates const auto update_tid = GetUpdateTitleID(title_id); - PatchManager update{update_tid}; + PatchManager update{update_tid, fs_controller, content_provider}; const auto metadata = update.GetControlMetadata(); const auto& nacp = metadata.first; @@ -474,8 +469,8 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u if (nacp != nullptr) { out.insert_or_assign(update_label, nacp->GetVersionString()); } else { - if (installed.HasEntry(update_tid, ContentRecordType::Program)) { - const auto meta_ver = installed.GetEntryVersion(update_tid); + if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) { + const auto meta_ver = content_provider.GetEntryVersion(update_tid); if (meta_ver.value_or(0) == 0) { out.insert_or_assign(update_label, ""); } else { @@ -487,8 +482,7 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u } // General Mods (LayeredFS and IPS) - const auto mod_dir = - Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); + const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id); if (mod_dir != nullptr && mod_dir->GetSize() > 0) { for (const auto& mod : mod_dir->GetSubdirectories()) { std::string types; @@ -532,13 +526,15 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u } // DLC - const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); + const auto dlc_entries = + content_provider.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); std::vector<ContentProviderEntry> dlc_match; dlc_match.reserve(dlc_entries.size()); std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), - [this, &installed](const ContentProviderEntry& entry) { + [this](const ContentProviderEntry& entry) { return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id && - installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; + content_provider.GetEntry(entry)->GetStatus() == + Loader::ResultStatus::Success; }); if (!dlc_match.empty()) { // Ensure sorted so DLC IDs show in order. @@ -559,19 +555,16 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u } std::optional<u32> PatchManager::GetGameVersion() const { - const auto& installed = Core::System::GetInstance().GetContentProvider(); const auto update_tid = GetUpdateTitleID(title_id); - if (installed.HasEntry(update_tid, ContentRecordType::Program)) { - return installed.GetEntryVersion(update_tid); + if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) { + return content_provider.GetEntryVersion(update_tid); } - return installed.GetEntryVersion(title_id); + return content_provider.GetEntryVersion(title_id); } PatchManager::Metadata PatchManager::GetControlMetadata() const { - const auto& installed = Core::System::GetInstance().GetContentProvider(); - - const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); + const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control); if (base_control_nca == nullptr) { return {}; } diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 1f28c6241..fb1853035 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -17,8 +17,13 @@ namespace Core { class System; } +namespace Service::FileSystem { +class FileSystemController; +} + namespace FileSys { +class ContentProvider; class NCA; class NACP; @@ -29,7 +34,9 @@ public: using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>; using PatchVersionNames = std::map<std::string, std::string, std::less<>>; - explicit PatchManager(u64 title_id); + explicit PatchManager(u64 title_id_, + const Service::FileSystem::FileSystemController& fs_controller_, + const ContentProvider& content_provider_); ~PatchManager(); [[nodiscard]] u64 GetTitleID() const; @@ -50,7 +57,7 @@ public: // Creates a CheatList object with all [[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList( - const Core::System& system, const BuildID& build_id) const; + const BuildID& build_id) const; // Currently tracked RomFS patches: // - Game Updates @@ -80,6 +87,8 @@ private: const std::string& build_id) const; u64 title_id; + const Service::FileSystem::FileSystemController& fs_controller; + const ContentProvider& content_provider; }; } // namespace FileSys diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index e967a254e..987199747 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -37,10 +37,12 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) { } ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const { - if (!updatable) + if (!updatable) { return MakeResult<VirtualFile>(file); + } - const PatchManager patch_manager(current_process_title_id); + const PatchManager patch_manager{current_process_title_id, filesystem_controller, + content_provider}; return MakeResult<VirtualFile>( patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); } diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index 90641d23b..c05735ddd 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -20,8 +20,8 @@ namespace FileSys { -NSP::NSP(VirtualFile file_) - : file(std::move(file_)), status{Loader::ResultStatus::Success}, +NSP::NSP(VirtualFile file_, std::size_t program_index) + : file(std::move(file_)), program_index(program_index), status{Loader::ResultStatus::Success}, pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} { if (pfs->GetStatus() != Loader::ResultStatus::Success) { status = pfs->GetStatus(); @@ -146,7 +146,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType if (extracted) LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); - const auto title_id_iter = ncas.find(title_id); + const auto title_id_iter = ncas.find(title_id + program_index); if (title_id_iter == ncas.end()) return nullptr; diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index c70a11b5b..54581a6f3 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -27,7 +27,7 @@ enum class ContentRecordType : u8; class NSP : public ReadOnlyVfsDirectory { public: - explicit NSP(VirtualFile file); + explicit NSP(VirtualFile file, std::size_t program_index = 0); ~NSP() override; Loader::ResultStatus GetStatus() const; @@ -69,6 +69,8 @@ private: VirtualFile file; + const std::size_t program_index; + bool extracted = false; Loader::ResultStatus status; std::map<u64, Loader::ResultStatus> program_status; diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp index 5582091f4..03bbedf8b 100644 --- a/src/core/frontend/applets/controller.cpp +++ b/src/core/frontend/applets/controller.cpp @@ -27,19 +27,19 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb ->GetAppletResource() ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad); - auto& players = Settings::values.players; + auto& players = Settings::values.players.GetValue(); const std::size_t min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players; // Disconnect Handheld first. - npad.DisconnectNPadAtIndex(8); + npad.DisconnectNpadAtIndex(8); // Deduce the best configuration based on the input parameters. for (std::size_t index = 0; index < players.size() - 2; ++index) { // First, disconnect all controllers regardless of the value of keep_controllers_connected. // This makes it easy to connect the desired controllers. - npad.DisconnectNPadAtIndex(index); + npad.DisconnectNpadAtIndex(index); // Only connect the minimum number of required players. if (index >= min_supported_players) { @@ -66,7 +66,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index); } } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && - !Settings::values.use_docked_mode) { + !Settings::values.use_docked_mode.GetValue()) { // We should *never* reach here under any normal circumstances. npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld), index); diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index 1acc82497..b9a270a55 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -47,7 +47,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) { FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) { u32 width, height; - if (Settings::values.use_docked_mode) { + if (Settings::values.use_docked_mode.GetValue()) { width = ScreenDocked::Width * res_scale; height = ScreenDocked::Height * res_scale; } else { diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 277b70e53..11c2e96ca 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -30,10 +30,12 @@ public: virtual StatusType GetStatus() const { return {}; } - virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { + virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const { return {}; } - virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const { + virtual bool SetRumblePlay([[maybe_unused]] f32 amp_low, [[maybe_unused]] f32 freq_low, + [[maybe_unused]] f32 amp_high, + [[maybe_unused]] f32 freq_high) const { return {}; } }; @@ -122,6 +124,13 @@ using ButtonDevice = InputDevice<bool>; using AnalogDevice = InputDevice<std::tuple<float, float>>; /** + * A vibration device is an input device that returns an unsigned byte as status. + * It represents whether the vibration device supports vibration or not. + * If the status returns 1, it supports vibration. Otherwise, it does not support vibration. + */ +using VibrationDevice = InputDevice<u8>; + +/** * A motion status is an object that returns a tuple of accelerometer state vector, * gyroscope state vector, rotation state vector and orientation state matrix. * diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 1c354037d..d57776ce9 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -12,7 +12,6 @@ #include <utility> #include "common/assert.h" #include "common/common_types.h" -#include "core/core.h" #include "core/hle/ipc.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" @@ -73,14 +72,12 @@ public: AlwaysMoveHandles = 1, }; - explicit ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {} - explicit ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size, u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0, Flags flags = Flags::None) - : RequestHelperBase(context), normal_params_size(normal_params_size), - num_handles_to_copy(num_handles_to_copy), num_objects_to_move(num_objects_to_move) { + num_handles_to_copy(num_handles_to_copy), + num_objects_to_move(num_objects_to_move), kernel{context.kernel} { memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH); @@ -140,7 +137,6 @@ public: if (context->Session()->IsDomain()) { context->AddDomainObject(std::move(iface)); } else { - auto& kernel = Core::System::GetInstance().Kernel(); auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName()); context->AddMoveObject(std::move(client)); iface->ClientConnected(std::move(server)); @@ -214,6 +210,7 @@ private: u32 num_handles_to_copy{}; u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent std::ptrdiff_t datapayload_index{}; + Kernel::KernelCore& kernel; }; /// Push /// diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index f3277b766..c31a65476 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -24,6 +24,10 @@ namespace Core::Memory { class Memory; } +namespace IPC { +class ResponseBuilder; +} + namespace Service { class ServiceFrameworkBase; } @@ -287,6 +291,8 @@ public: } private: + friend class IPC::ResponseBuilder; + void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming); std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index bafd1ced7..e3b770d66 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -681,7 +681,7 @@ static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { } /// Used to output a message on a debug hardware unit - does nothing on a retail unit -static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { +static void OutputDebugString(Core::System& system, VAddr address, u64 len) { if (len == 0) { return; } diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 2850dd805..c2c11dbcb 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -11,6 +11,7 @@ #include "common/string_util.h" #include "common/swap.h" #include "core/constants.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" @@ -741,8 +742,10 @@ void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx bool is_locked = false; if (res != Loader::ResultStatus::Success) { - FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; - auto nacp_unique = pm.GetControlMetadata().first; + const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(), + system.GetFileSystemController(), + system.GetContentProvider()}; + const auto nacp_unique = pm.GetControlMetadata().first; if (nacp_unique != nullptr) { is_locked = nacp_unique->GetUserAccountSwitchLock(); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 2ce742e35..703a9b234 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -246,9 +246,8 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { IDebugFunctions::~IDebugFunctions() = default; -ISelfController::ISelfController(Core::System& system, - std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) { +ISelfController::ISelfController(Core::System& system, NVFlinger::NVFlinger& nvflinger) + : ServiceFramework("ISelfController"), system(system), nvflinger(nvflinger) { // clang-format off static const FunctionInfo functions[] = { {0, &ISelfController::Exit, "Exit"}, @@ -458,8 +457,8 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) // TODO(Subv): Find out how AM determines the display to use, for now just // create the layer in the Default display. - const auto display_id = nvflinger->OpenDisplay("Default"); - const auto layer_id = nvflinger->CreateLayer(*display_id); + const auto display_id = nvflinger.OpenDisplay("Default"); + const auto layer_id = nvflinger.CreateLayer(*display_id); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); @@ -476,8 +475,8 @@ void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestConte // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse // side effects. // TODO: Support multiple layers - const auto display_id = nvflinger->OpenDisplay("Default"); - const auto layer_id = nvflinger->CreateLayer(*display_id); + const auto display_id = nvflinger.OpenDisplay("Default"); + const auto layer_id = nvflinger.CreateLayer(*display_id); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); @@ -751,7 +750,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - if (Settings::values.use_docked_mode) { + if (Settings::values.use_docked_mode.GetValue()) { rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * static_cast<u32>(Settings::values.resolution_factor.GetValue())); rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * @@ -824,7 +823,7 @@ void IStorage::Open(Kernel::HLERequestContext& ctx) { } void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { - const bool use_docked_mode{Settings::values.use_docked_mode}; + const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()}; LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); IPC::ResponseBuilder rb{ctx, 3}; @@ -1189,9 +1188,9 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"}, {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"}, {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"}, - {120, nullptr, "ExecuteProgram"}, - {121, nullptr, "ClearUserChannel"}, - {122, nullptr, "UnpopToUserChannel"}, + {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"}, + {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"}, + {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"}, {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"}, {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, @@ -1381,13 +1380,16 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) { const auto res = [this] { const auto title_id = system.CurrentProcess()->GetTitleID(); - FileSys::PatchManager pm{title_id}; + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + system.GetContentProvider()}; auto res = pm.GetControlMetadata(); if (res.first != nullptr) { return res; } - FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)}; + const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id), + system.GetFileSystemController(), + system.GetContentProvider()}; return pm_update.GetControlMetadata(); }(); @@ -1415,13 +1417,16 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { const auto res = [this] { const auto title_id = system.CurrentProcess()->GetTitleID(); - FileSys::PatchManager pm{title_id}; + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + system.GetContentProvider()}; auto res = pm.GetControlMetadata(); if (res.first != nullptr) { return res; } - FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)}; + const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id), + system.GetFileSystemController(), + system.GetContentProvider()}; return pm_update.GetControlMetadata(); }(); @@ -1556,6 +1561,34 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque rb.Push<u32>(0); } +void IApplicationFunctions::ExecuteProgram(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::RequestParser rp{ctx}; + [[maybe_unused]] const auto unk_1 = rp.Pop<u32>(); + [[maybe_unused]] const auto unk_2 = rp.Pop<u32>(); + const auto program_index = rp.Pop<u64>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + system.ExecuteProgram(program_index); +} + +void IApplicationFunctions::ClearUserChannel(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void IApplicationFunctions::UnpopToUserChannel(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); @@ -1580,8 +1613,8 @@ void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERe rb.PushCopyObjects(friend_invitation_storage_channel_event.readable); } -void InstallInterfaces(SM::ServiceManager& service_manager, - std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) { +void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, + Core::System& system) { auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel()); // Needed on game boot message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index bcc06affe..af97c303a 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -121,8 +121,7 @@ public: class ISelfController final : public ServiceFramework<ISelfController> { public: - explicit ISelfController(Core::System& system_, - std::shared_ptr<NVFlinger::NVFlinger> nvflinger_); + explicit ISelfController(Core::System& system_, NVFlinger::NVFlinger& nvflinger_); ~ISelfController() override; private: @@ -156,7 +155,7 @@ private: }; Core::System& system; - std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + NVFlinger::NVFlinger& nvflinger; Kernel::EventPair launchable_event; Kernel::EventPair accumulated_suspended_tick_changed_event; @@ -288,6 +287,9 @@ private: void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx); void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx); void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx); + void ExecuteProgram(Kernel::HLERequestContext& ctx); + void ClearUserChannel(Kernel::HLERequestContext& ctx); + void UnpopToUserChannel(Kernel::HLERequestContext& ctx); void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx); void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); @@ -332,7 +334,7 @@ public: }; /// Registers all AM services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager, - std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system); +void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, + Core::System& system); } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 9df286d17..7de506b70 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -3,8 +3,8 @@ // 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/kernel/process.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -13,10 +13,10 @@ namespace Service::AM { class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { public: - explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + explicit ILibraryAppletProxy(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) - : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), + : ServiceFramework("ILibraryAppletProxy"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)), system(system) { // clang-format off static const FunctionInfo functions[] = { @@ -109,16 +109,16 @@ private: rb.PushIpcInterface<IApplicationFunctions>(system); } - std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + NVFlinger::NVFlinger& nvflinger; std::shared_ptr<AppletMessageQueue> msg_queue; Core::System& system; }; class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { public: - explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + explicit ISystemAppletProxy(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) - : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), + : ServiceFramework("ISystemAppletProxy"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)), system(system) { // clang-format off static const FunctionInfo functions[] = { @@ -220,7 +220,8 @@ private: rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IApplicationCreator>(); } - std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + + NVFlinger::NVFlinger& nvflinger; std::shared_ptr<AppletMessageQueue> msg_queue; Core::System& system; }; @@ -249,10 +250,10 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system); } -AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, - std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) - : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)), - msg_queue(std::move(msg_queue)), system(system) { +AppletAE::AppletAE(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue, + Core::System& system) + : ServiceFramework("appletAE"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)), + system(system) { // clang-format off static const FunctionInfo functions[] = { {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h index 2e3e45915..761844a1f 100644 --- a/src/core/hle/service/am/applet_ae.h +++ b/src/core/hle/service/am/applet_ae.h @@ -23,7 +23,7 @@ class AppletMessageQueue; class AppletAE final : public ServiceFramework<AppletAE> { public: - explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + explicit AppletAE(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system); ~AppletAE() override; @@ -34,7 +34,7 @@ private: void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx); void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx); - std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + NVFlinger::NVFlinger& nvflinger; std::shared_ptr<AppletMessageQueue> msg_queue; Core::System& system; }; diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index a2ffaa440..7bed86ec4 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -12,9 +12,9 @@ namespace Service::AM { class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { public: - explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + explicit IApplicationProxy(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) - : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)), + : ServiceFramework("IApplicationProxy"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)), system(system) { // clang-format off static const FunctionInfo functions[] = { @@ -98,7 +98,7 @@ private: rb.PushIpcInterface<IApplicationFunctions>(system); } - std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + NVFlinger::NVFlinger& nvflinger; std::shared_ptr<AppletMessageQueue> msg_queue; Core::System& system; }; @@ -111,10 +111,10 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue, system); } -AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, - std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) - : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)), - msg_queue(std::move(msg_queue)), system(system) { +AppletOE::AppletOE(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue, + Core::System& system) + : ServiceFramework("appletOE"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)), + system(system) { static const FunctionInfo functions[] = { {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, }; diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index 758da792d..88906d354 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h @@ -23,7 +23,7 @@ class AppletMessageQueue; class AppletOE final : public ServiceFramework<AppletOE> { public: - explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + explicit AppletOE(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system); ~AppletOE() override; @@ -32,7 +32,7 @@ public: private: void OpenApplicationProxy(Kernel::HLERequestContext& ctx); - std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + NVFlinger::NVFlinger& nvflinger; std::shared_ptr<AppletMessageQueue> msg_queue; Core::System& system; }; diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp index 2151da783..3ca63f020 100644 --- a/src/core/hle/service/am/applets/controller.cpp +++ b/src/core/hle/service/am/applets/controller.cpp @@ -25,7 +25,7 @@ namespace Service::AM::Applets { static Core::Frontend::ControllerParameters ConvertToFrontendParameters( ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) { - HID::Controller_NPad::NPadType npad_style_set; + HID::Controller_NPad::NpadStyleSet npad_style_set; npad_style_set.raw = private_arg.style_set; return { @@ -62,7 +62,7 @@ void Controller::Initialize() { common_args.play_startup_sound, common_args.size, common_args.system_tick, common_args.theme_color); - library_applet_version = LibraryAppletVersion{common_args.library_version}; + controller_applet_version = ControllerAppletVersion{common_args.library_version}; const auto private_arg_storage = broker.PopNormalDataToApplet(); ASSERT(private_arg_storage != nullptr); @@ -70,39 +70,78 @@ void Controller::Initialize() { const auto& private_arg = private_arg_storage->GetData(); ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate)); - std::memcpy(&controller_private_arg, private_arg.data(), sizeof(ControllerSupportArgPrivate)); + std::memcpy(&controller_private_arg, private_arg.data(), private_arg.size()); ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate), "Unknown ControllerSupportArgPrivate revision={} with size={}", - library_applet_version, controller_private_arg.arg_private_size); + controller_applet_version, controller_private_arg.arg_private_size); + + // Some games such as Cave Story+ set invalid values for the ControllerSupportMode. + // Defer to arg_size to set the ControllerSupportMode. + if (controller_private_arg.mode >= ControllerSupportMode::MaxControllerSupportMode) { + switch (controller_private_arg.arg_size) { + case sizeof(ControllerSupportArgOld): + case sizeof(ControllerSupportArgNew): + controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport; + break; + case sizeof(ControllerUpdateFirmwareArg): + controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate; + break; + default: + UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}", + controller_private_arg.mode, controller_private_arg.arg_size); + controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport; + break; + } + } + + // Some games such as Cave Story+ set invalid values for the ControllerSupportCaller. + // This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem. + if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) { + if (controller_private_arg.flag_1 && + controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate) { + controller_private_arg.caller = ControllerSupportCaller::System; + } else { + controller_private_arg.caller = ControllerSupportCaller::Application; + } + } switch (controller_private_arg.mode) { - case ControllerSupportMode::ShowControllerSupport: { + case ControllerSupportMode::ShowControllerSupport: + case ControllerSupportMode::ShowControllerStrapGuide: { const auto user_arg_storage = broker.PopNormalDataToApplet(); ASSERT(user_arg_storage != nullptr); const auto& user_arg = user_arg_storage->GetData(); - switch (library_applet_version) { - case LibraryAppletVersion::Version3: - case LibraryAppletVersion::Version4: - case LibraryAppletVersion::Version5: + switch (controller_applet_version) { + case ControllerAppletVersion::Version3: + case ControllerAppletVersion::Version4: + case ControllerAppletVersion::Version5: ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld)); - std::memcpy(&controller_user_arg_old, user_arg.data(), sizeof(ControllerSupportArgOld)); + std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size()); break; - case LibraryAppletVersion::Version7: + case ControllerAppletVersion::Version7: ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew)); - std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew)); + std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size()); break; default: UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}", - library_applet_version, controller_private_arg.arg_size); + controller_applet_version, controller_private_arg.arg_size); ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew)); std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew)); break; } break; } - case ControllerSupportMode::ShowControllerStrapGuide: - case ControllerSupportMode::ShowControllerFirmwareUpdate: + case ControllerSupportMode::ShowControllerFirmwareUpdate: { + const auto update_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(update_arg_storage != nullptr); + + const auto& update_arg = update_arg_storage->GetData(); + ASSERT(update_arg.size() == sizeof(ControllerUpdateFirmwareArg)); + + std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size()); + break; + } default: { UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode); break; @@ -126,10 +165,10 @@ void Controller::Execute() { switch (controller_private_arg.mode) { case ControllerSupportMode::ShowControllerSupport: { const auto parameters = [this] { - switch (library_applet_version) { - case LibraryAppletVersion::Version3: - case LibraryAppletVersion::Version4: - case LibraryAppletVersion::Version5: + switch (controller_applet_version) { + case ControllerAppletVersion::Version3: + case ControllerAppletVersion::Version4: + case ControllerAppletVersion::Version5: return ConvertToFrontendParameters( controller_private_arg, controller_user_arg_old.header, controller_user_arg_old.enable_explain_text, @@ -138,7 +177,7 @@ void Controller::Execute() { controller_user_arg_old.identification_colors.end()), std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(), controller_user_arg_old.explain_text.end())); - case LibraryAppletVersion::Version7: + case ControllerAppletVersion::Version7: default: return ConvertToFrontendParameters( controller_private_arg, controller_user_arg_new.header, @@ -170,6 +209,9 @@ void Controller::Execute() { } case ControllerSupportMode::ShowControllerStrapGuide: case ControllerSupportMode::ShowControllerFirmwareUpdate: + UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented", + controller_private_arg.mode); + [[fallthrough]]; default: { ConfigurationComplete(); break; @@ -180,7 +222,7 @@ void Controller::Execute() { void Controller::ConfigurationComplete() { ControllerSupportResultInfo result_info{}; - const auto& players = Settings::values.players; + const auto& players = Settings::values.players.GetValue(); // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. // Otherwise, only count connected players from P1-P8. diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h index f7bb3fba9..a7a1f2b65 100644 --- a/src/core/hle/service/am/applets/controller.h +++ b/src/core/hle/service/am/applets/controller.h @@ -21,7 +21,7 @@ namespace Service::AM::Applets { using IdentificationColor = std::array<u8, 4>; using ExplainText = std::array<char, 0x81>; -enum class LibraryAppletVersion : u32_le { +enum class ControllerAppletVersion : u32_le { Version3 = 0x3, // 1.0.0 - 2.3.0 Version4 = 0x4, // 3.0.0 - 5.1.0 Version5 = 0x5, // 6.0.0 - 7.0.1 @@ -29,14 +29,18 @@ enum class LibraryAppletVersion : u32_le { }; enum class ControllerSupportMode : u8 { - ShowControllerSupport = 0, - ShowControllerStrapGuide = 1, - ShowControllerFirmwareUpdate = 2, + ShowControllerSupport, + ShowControllerStrapGuide, + ShowControllerFirmwareUpdate, + + MaxControllerSupportMode, }; enum class ControllerSupportCaller : u8 { - Application = 0, - System = 1, + Application, + System, + + MaxControllerSupportCaller, }; struct ControllerSupportArgPrivate { @@ -84,6 +88,13 @@ struct ControllerSupportArgNew { static_assert(sizeof(ControllerSupportArgNew) == 0x430, "ControllerSupportArgNew has incorrect size."); +struct ControllerUpdateFirmwareArg { + bool enable_force_update{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4, + "ControllerUpdateFirmwareArg has incorrect size."); + struct ControllerSupportResultInfo { s8 player_count{}; INSERT_PADDING_BYTES(3); @@ -110,10 +121,11 @@ public: private: const Core::Frontend::ControllerApplet& frontend; - LibraryAppletVersion library_applet_version; + ControllerAppletVersion controller_applet_version; ControllerSupportArgPrivate controller_private_arg; ControllerSupportArgOld controller_user_arg_old; ControllerSupportArgNew controller_user_arg_new; + ControllerUpdateFirmwareArg controller_update_arg; bool complete{false}; ResultCode status{RESULT_SUCCESS}; bool is_single_mode{false}; diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 8e79f707b..173b36da4 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -6,6 +6,7 @@ #include <numeric> #include <vector> #include "common/logging/log.h" +#include "core/core.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/nca_metadata.h" @@ -163,7 +164,8 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); const auto title_id = system.CurrentProcess()->GetTitleID(); - FileSys::PatchManager pm{title_id}; + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + system.GetContentProvider()}; const auto res = pm.GetControlMetadata(); if (res.first == nullptr) { diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index 85bbf5988..e2d8f0027 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/apm/apm.h" #include "core/hle/service/apm/interface.h" diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/controller.cpp index 25a886238..ce993bad3 100644 --- a/src/core/hle/service/apm/controller.cpp +++ b/src/core/hle/service/apm/controller.cpp @@ -69,7 +69,8 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) { } PerformanceMode Controller::GetCurrentPerformanceMode() const { - return Settings::values.use_docked_mode ? PerformanceMode::Docked : PerformanceMode::Handheld; + return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked + : PerformanceMode::Handheld; } PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index db0e06ca1..68deb0600 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -8,6 +8,7 @@ #include "common/hex_util.h" #include "common/logging/log.h" #include "common/string_util.h" +#include "core/core.h" #include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/process.h" diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index f311afa2f..d4f0dd1ab 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -3,6 +3,7 @@ // 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/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 0d251c6d0..c8f8ddbd5 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -5,6 +5,7 @@ #include <memory> #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index 8e2b83629..f9479bdb3 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp @@ -41,7 +41,7 @@ CAPS_U::CAPS_U() : ServiceFramework("caps:u") { {130, nullptr, "PrecheckToCreateContentsForApplication"}, {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, - {142, nullptr, "GetAlbumFileList3AaeAruid"}, + {142, &CAPS_U::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"}, {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, {60002, nullptr, "OpenAccessorSessionForApplication"}, }; @@ -77,17 +77,24 @@ void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& c // TODO: Update this when we implement the album. // Currently we do not have a method of accessing album entries, set this to 0 for now. - constexpr s32 total_entries{0}; + constexpr u32 total_entries_1{}; + constexpr u32 total_entries_2{}; - LOG_WARNING(Service_Capture, - "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, " - "end_posix_time={}, applet_resource_user_id={}, total_entries={}", - pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id, - total_entries); + LOG_WARNING( + Service_Capture, + "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, " + "end_posix_time={}, applet_resource_user_id={}, total_entries_1={}, total_entries_2={}", + pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id, + total_entries_1, total_entries_2); - IPC::ResponseBuilder rb{ctx, 3}; + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push(total_entries); + rb.Push(total_entries_1); + rb.Push(total_entries_2); +} + +void CAPS_U::GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx) { + GetAlbumContentsFileListForApplication(ctx); } } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h index e04e56bbc..4b80f3156 100644 --- a/src/core/hle/service/caps/caps_u.h +++ b/src/core/hle/service/caps/caps_u.h @@ -20,6 +20,7 @@ public: private: void SetShimLibraryVersion(Kernel::HLERequestContext& ctx); void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx); + void GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx); }; } // namespace Service::Capture diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 3cdef4888..2e53cae5b 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -455,7 +455,9 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy const auto res = system.GetAppLoader().ReadControlData(nacp); if (res != Loader::ResultStatus::Success) { - FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; + const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(), + system.GetFileSystemController(), + system.GetContentProvider()}; const auto metadata = pm.GetControlMetadata(); const auto& nacp_unique = metadata.first; @@ -728,7 +730,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove void InstallInterfaces(Core::System& system) { std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager()); std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager()); - std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter()) + std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetContentProvider(), + system.GetReporter()) ->InstallAsService(system.ServiceManager()); } diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 649128be4..031c6dbf6 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -650,8 +650,10 @@ private: u64 next_entry_index = 0; }; -FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter) - : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) { +FSP_SRV::FSP_SRV(FileSystemController& fsc_, const FileSys::ContentProvider& content_provider_, + const Core::Reporter& reporter_) + : ServiceFramework("fsp-srv"), fsc(fsc_), content_provider{content_provider_}, + reporter(reporter_) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "OpenFileSystem"}, @@ -968,7 +970,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { return; } - FileSys::PatchManager pm{title_id}; + const FileSys::PatchManager pm{title_id, fsc, content_provider}; auto storage = std::make_shared<IStorage>( pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data)); diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 4964e874e..6c7239e6a 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -12,8 +12,9 @@ class Reporter; } namespace FileSys { +class ContentProvider; class FileSystemBackend; -} +} // namespace FileSys namespace Service::FileSystem { @@ -32,7 +33,8 @@ enum class LogMode : u32 { class FSP_SRV final : public ServiceFramework<FSP_SRV> { public: - explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter); + explicit FSP_SRV(FileSystemController& fsc_, const FileSys::ContentProvider& content_provider_, + const Core::Reporter& reporter_); ~FSP_SRV() override; private: @@ -55,6 +57,7 @@ private: void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); FileSystemController& fsc; + const FileSys::ContentProvider& content_provider; FileSys::VirtualFile romfs; u64 current_process_id = 0; diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index b7adaffc7..ebb323da2 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -5,6 +5,7 @@ #include <queue> #include "common/logging/log.h" #include "common/uuid.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/writable_event.h" diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp index b591ce31b..c6252ff89 100644 --- a/src/core/hle/service/glue/arp.cpp +++ b/src/core/hle/service/glue/arp.cpp @@ -5,6 +5,7 @@ #include <memory> #include "common/logging/log.h" +#include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index e311bc18c..e2539ded8 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -117,7 +117,10 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) { } Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} -Controller_NPad::~Controller_NPad() = default; + +Controller_NPad::~Controller_NPad() { + OnRelease(); +} void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { const auto controller_type = connected_controllers[controller_idx].type; @@ -139,7 +142,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { controller.properties.is_vertical.Assign(1); controller.properties.use_plus.Assign(1); controller.properties.use_minus.Assign(1); - controller.pad_assignment = NPadAssignments::Single; + controller.pad_assignment = NpadAssignments::Single; break; case NPadControllerType::Handheld: controller.joy_styles.handheld.Assign(1); @@ -147,7 +150,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { controller.properties.is_vertical.Assign(1); controller.properties.use_plus.Assign(1); controller.properties.use_minus.Assign(1); - controller.pad_assignment = NPadAssignments::Dual; + controller.pad_assignment = NpadAssignments::Dual; break; case NPadControllerType::JoyDual: controller.joy_styles.joycon_dual.Assign(1); @@ -156,26 +159,26 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { controller.properties.is_vertical.Assign(1); controller.properties.use_plus.Assign(1); controller.properties.use_minus.Assign(1); - controller.pad_assignment = NPadAssignments::Dual; + controller.pad_assignment = NpadAssignments::Dual; break; case NPadControllerType::JoyLeft: controller.joy_styles.joycon_left.Assign(1); controller.device_type.joycon_left.Assign(1); controller.properties.is_horizontal.Assign(1); controller.properties.use_minus.Assign(1); - controller.pad_assignment = NPadAssignments::Single; + controller.pad_assignment = NpadAssignments::Single; break; case NPadControllerType::JoyRight: controller.joy_styles.joycon_right.Assign(1); controller.device_type.joycon_right.Assign(1); controller.properties.is_horizontal.Assign(1); controller.properties.use_plus.Assign(1); - controller.pad_assignment = NPadAssignments::Single; + controller.pad_assignment = NpadAssignments::Single; break; case NPadControllerType::Pokeball: controller.joy_styles.pokeball.Assign(1); controller.device_type.pokeball.Assign(1); - controller.pad_assignment = NPadAssignments::Single; + controller.pad_assignment = NpadAssignments::Single; break; } @@ -184,11 +187,14 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { controller.single_color.button_color = 0; controller.dual_color_error = ColorReadError::ReadOk; - controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left; - controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left; - controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right; + controller.left_color.body_color = + Settings::values.players.GetValue()[controller_idx].body_color_left; + controller.left_color.button_color = + Settings::values.players.GetValue()[controller_idx].button_color_left; + controller.right_color.body_color = + Settings::values.players.GetValue()[controller_idx].body_color_right; controller.right_color.button_color = - Settings::values.players[controller_idx].button_color_right; + Settings::values.players.GetValue()[controller_idx].button_color_right; controller.battery_level[0] = BATTERY_FULL; controller.battery_level[1] = BATTERY_FULL; @@ -199,7 +205,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { void Controller_NPad::OnInit() { auto& kernel = system.Kernel(); - for (std::size_t i = 0; i < styleset_changed_events.size(); i++) { + for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair( kernel, fmt::format("npad:NpadStyleSetChanged_{}", i)); } @@ -208,6 +214,8 @@ void Controller_NPad::OnInit() { return; } + OnLoadInputDevices(); + if (style.raw == 0) { // We want to support all controllers style.handheld.Assign(1); @@ -218,12 +226,27 @@ void Controller_NPad::OnInit() { style.pokeball.Assign(1); } - std::transform(Settings::values.players.begin(), Settings::values.players.end(), - connected_controllers.begin(), [](const Settings::PlayerInput& player) { + std::transform(Settings::values.players.GetValue().begin(), + Settings::values.players.GetValue().end(), connected_controllers.begin(), + [](const Settings::PlayerInput& player) { return ControllerHolder{MapSettingsTypeToNPad(player.controller_type), player.connected}; }); + // Connect the Player 1 or Handheld controller if none are connected. + if (std::none_of(connected_controllers.begin(), connected_controllers.end(), + [](const ControllerHolder& controller) { return controller.is_connected; })) { + const auto controller = + MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type); + if (controller == NPadControllerType::Handheld) { + Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true; + connected_controllers[HANDHELD_INDEX] = {controller, true}; + } else { + Settings::values.players.GetValue()[0].connected = true; + connected_controllers[0] = {controller, true}; + } + } + // Account for handheld if (connected_controllers[HANDHELD_INDEX].is_connected) { connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld; @@ -242,7 +265,7 @@ void Controller_NPad::OnInit() { } void Controller_NPad::OnLoadInputDevices() { - const auto& players = Settings::values.players; + const auto& players = Settings::values.players.GetValue(); for (std::size_t i = 0; i < players.size(); ++i) { std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END, @@ -250,13 +273,26 @@ void Controller_NPad::OnLoadInputDevices() { std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); + std::transform(players[i].vibrations.begin() + + Settings::NativeVibration::VIBRATION_HID_BEGIN, + players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END, + vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>); std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END, motions[i].begin(), Input::CreateDevice<Input::MotionDevice>); + for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) { + InitializeVibrationDeviceAtIndex(i, device_idx); + } } } -void Controller_NPad::OnRelease() {} +void Controller_NPad::OnRelease() { + for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) { + for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) { + VibrateControllerAtIndex(npad_idx, device_idx, {}); + } + } +} void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { const auto controller_idx = NPadIdToIndex(npad_id); @@ -339,7 +375,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* if (!IsControllerActivated()) { return; } - for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { + for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) { auto& npad = shared_memory_entries[i]; const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states, &npad.handheld_states, @@ -481,7 +517,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing if (!IsControllerActivated()) { return; } - for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { + for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) { auto& npad = shared_memory_entries[i]; const auto& controller_type = connected_controllers[i].type; @@ -515,7 +551,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing // Try to read sixaxis sensor states std::array<MotionDevice, 2> motion_devices; - if (sixaxis_sensors_enabled && Settings::values.motion_enabled) { + if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) { sixaxis_at_rest = true; for (std::size_t e = 0; e < motion_devices.size(); ++e) { const auto& device = motions[i][e]; @@ -601,15 +637,15 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing shared_memory_entries.size() * sizeof(NPadEntry)); } -void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { +void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) { style.raw = style_set.raw; } -Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const { +Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const { return style; } -void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { +void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) { ASSERT(length > 0 && (length % sizeof(u32)) == 0); supported_npad_id_types.clear(); supported_npad_id_types.resize(length / sizeof(u32)); @@ -621,7 +657,7 @@ void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size()); } -std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const { +std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { return supported_npad_id_types.size(); } @@ -641,7 +677,7 @@ Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActi return handheld_activation_mode; } -void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { +void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) { const std::size_t npad_index = NPadIdToIndex(npad_id); ASSERT(npad_index < shared_memory_entries.size()); if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) { @@ -649,35 +685,140 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) } } -void Controller_NPad::VibrateController(const std::vector<u32>& controllers, - const std::vector<Vibration>& vibrations) { - LOG_TRACE(Service_HID, "called"); - - if (!Settings::values.vibration_enabled || !can_controllers_vibrate) { - return; +bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, + const VibrationValue& vibration_value) { + if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) { + return false; } - bool success = true; - for (std::size_t i = 0; i < controllers.size(); ++i) { - if (!connected_controllers[i].is_connected) { - continue; + + const auto& player = Settings::values.players.GetValue()[npad_index]; + + if (!player.vibration_enabled) { + if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f || + latest_vibration_values[npad_index][device_index].amp_high != 0.0f) { + // Send an empty vibration to stop any vibrations. + vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f); + // Then reset the vibration value to its default value. + latest_vibration_values[npad_index][device_index] = {}; } - using namespace Settings::NativeButton; - const auto& button_state = buttons[i]; - if (button_state[A - BUTTON_HID_BEGIN]) { - if (button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( - vibrations[0].amp_high, vibrations[0].amp_low, vibrations[0].freq_high, - vibrations[0].freq_low)) { - success = false; - } + + return false; + } + + if (!Settings::values.enable_accurate_vibrations.GetValue()) { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::steady_clock; + + const auto now = steady_clock::now(); + + // Filter out non-zero vibrations that are within 10ms of each other. + if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) && + duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) < + milliseconds(10)) { + return false; } + + last_vibration_timepoints[npad_index][device_index] = now; + } + + auto& vibration = vibrations[npad_index][device_index]; + const auto player_vibration_strength = static_cast<f32>(player.vibration_strength); + const auto amp_low = + std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f); + const auto amp_high = + std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f); + return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high, + vibration_value.freq_high); +} + +void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle, + const VibrationValue& vibration_value) { + if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { + return; + } + + const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); + const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); + + if (!vibration_devices_mounted[npad_index][device_index] || + !connected_controllers[npad_index].is_connected) { + return; } - if (success) { - last_processed_vibration = vibrations.back(); + + if (vibration_device_handle.device_index == DeviceIndex::None) { + UNREACHABLE_MSG("DeviceIndex should never be None!"); + return; + } + + // Some games try to send mismatched parameters in the device handle, block these. + if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && + (vibration_device_handle.npad_type == NpadType::JoyconRight || + vibration_device_handle.device_index == DeviceIndex::Right)) || + (connected_controllers[npad_index].type == NPadControllerType::JoyRight && + (vibration_device_handle.npad_type == NpadType::JoyconLeft || + vibration_device_handle.device_index == DeviceIndex::Left))) { + return; + } + + // Filter out vibrations with equivalent values to reduce unnecessary state changes. + if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low && + vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) { + return; + } + + if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) { + latest_vibration_values[npad_index][device_index] = vibration_value; } } -Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { - return last_processed_vibration; +void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles, + const std::vector<VibrationValue>& vibration_values) { + if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { + return; + } + + ASSERT_OR_EXECUTE_MSG( + vibration_device_handles.size() == vibration_values.size(), { return; }, + "The amount of device handles does not match with the amount of vibration values," + "this is undefined behavior!"); + + for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) { + VibrateController(vibration_device_handles[i], vibration_values[i]); + } +} + +Controller_NPad::VibrationValue Controller_NPad::GetLastVibration( + const DeviceHandle& vibration_device_handle) const { + const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); + const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); + return latest_vibration_values[npad_index][device_index]; +} + +void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) { + const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); + const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); + InitializeVibrationDeviceAtIndex(npad_index, device_index); +} + +void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index, + std::size_t device_index) { + if (vibrations[npad_index][device_index]) { + vibration_devices_mounted[npad_index][device_index] = + vibrations[npad_index][device_index]->GetStatus() == 1; + } else { + vibration_devices_mounted[npad_index][device_index] = false; + } +} + +void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { + permit_vibration_session_enabled = permit_vibration_session; +} + +bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const { + const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); + const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); + return vibration_devices_mounted[npad_index][device_index]; } std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { @@ -696,31 +837,38 @@ void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::siz void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected) { if (!connected) { - DisconnectNPadAtIndex(npad_index); + DisconnectNpadAtIndex(npad_index); return; } if (controller == NPadControllerType::Handheld) { - Settings::values.players[HANDHELD_INDEX].controller_type = + Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type = MapNPadToSettingsType(controller); - Settings::values.players[HANDHELD_INDEX].connected = true; + Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true; connected_controllers[HANDHELD_INDEX] = {controller, true}; InitNewlyAddedController(HANDHELD_INDEX); return; } - Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller); - Settings::values.players[npad_index].connected = true; + Settings::values.players.GetValue()[npad_index].controller_type = + MapNPadToSettingsType(controller); + Settings::values.players.GetValue()[npad_index].connected = true; connected_controllers[npad_index] = {controller, true}; InitNewlyAddedController(npad_index); } -void Controller_NPad::DisconnectNPad(u32 npad_id) { - DisconnectNPadAtIndex(NPadIdToIndex(npad_id)); +void Controller_NPad::DisconnectNpad(u32 npad_id) { + DisconnectNpadAtIndex(NPadIdToIndex(npad_id)); } -void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) { - Settings::values.players[npad_index].connected = false; +void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) { + for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) { + // Send an empty vibration to stop any vibrations. + VibrateControllerAtIndex(npad_index, device_idx, {}); + vibration_devices_mounted[npad_index][device_idx] = false; + } + + Settings::values.players.GetValue()[npad_index].connected = false; connected_controllers[npad_index].is_connected = false; auto& controller = shared_memory_entries[npad_index]; @@ -758,7 +906,7 @@ void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) { (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft && connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) { // Disconnect the joycon at the second id and connect the dual joycon at the first index. - DisconnectNPad(npad_id_2); + DisconnectNpad(npad_id_2); AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1); } } @@ -830,14 +978,6 @@ void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_prot unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled; } -void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { - can_controllers_vibrate = can_vibrate; -} - -bool Controller_NPad::IsVibrationEnabled() const { - return can_controllers_vibrate; -} - void Controller_NPad::ClearAllConnectedControllers() { for (auto& controller : connected_controllers) { if (controller.is_connected && controller.type != NPadControllerType::None) { @@ -882,7 +1022,7 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const return false; } // Handheld should not be supported in docked mode - if (Settings::values.use_docked_mode) { + if (Settings::values.use_docked_mode.GetValue()) { return false; } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index fd5c5a6eb..160dcbbe3 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -39,28 +39,30 @@ public: // Called when input devices should be loaded void OnLoadInputDevices() override; - struct NPadType { - union { - u32_le raw{}; - - BitField<0, 1, u32> pro_controller; - BitField<1, 1, u32> handheld; - BitField<2, 1, u32> joycon_dual; - BitField<3, 1, u32> joycon_left; - BitField<4, 1, u32> joycon_right; + enum class NPadControllerType { + None, + ProController, + Handheld, + JoyDual, + JoyLeft, + JoyRight, + Pokeball, + }; - BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible - }; + enum class NpadType : u8 { + ProController = 3, + Handheld = 4, + JoyconDual = 5, + JoyconLeft = 6, + JoyconRight = 7, + Pokeball = 9, }; - static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size"); - struct Vibration { - f32 amp_low; - f32 freq_low; - f32 amp_high; - f32 freq_high; + enum class DeviceIndex : u8 { + Left = 0, + Right = 1, + None = 2, }; - static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size"); enum class GyroscopeZeroDriftMode : u32 { Loose = 0, @@ -73,7 +75,7 @@ public: Horizontal = 1, }; - enum class NPadAssignments : u32_le { + enum class NpadAssignments : u32 { Dual = 0, Single = 1, }; @@ -84,15 +86,36 @@ public: None = 2, }; - enum class NPadControllerType { - None, - ProController, - Handheld, - JoyDual, - JoyLeft, - JoyRight, - Pokeball, + struct DeviceHandle { + NpadType npad_type{}; + u8 npad_id{}; + DeviceIndex device_index{}; + INSERT_PADDING_BYTES(1); + }; + static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size"); + + struct NpadStyleSet { + union { + u32_le raw{}; + + BitField<0, 1, u32> pro_controller; + BitField<1, 1, u32> handheld; + BitField<2, 1, u32> joycon_dual; + BitField<3, 1, u32> joycon_left; + BitField<4, 1, u32> joycon_right; + + BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible + }; + }; + static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size"); + + struct VibrationValue { + f32 amp_low{0.0f}; + f32 freq_low{160.0f}; + f32 amp_high{0.0f}; + f32 freq_high{320.0f}; }; + static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size"); struct LedPattern { explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { @@ -110,12 +133,12 @@ public: }; }; - void SetSupportedStyleSet(NPadType style_set); - NPadType GetSupportedStyleSet() const; + void SetSupportedStyleSet(NpadStyleSet style_set); + NpadStyleSet GetSupportedStyleSet() const; - void SetSupportedNPadIdTypes(u8* data, std::size_t length); + void SetSupportedNpadIdTypes(u8* data, std::size_t length); void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); - std::size_t GetSupportedNPadIdTypesSize() const; + std::size_t GetSupportedNpadIdTypesSize() const; void SetHoldType(NpadHoldType joy_hold_type); NpadHoldType GetHoldType() const; @@ -123,12 +146,26 @@ public: void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); NpadHandheldActivationMode GetNpadHandheldActivationMode() const; - void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); + void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); + + bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, + const VibrationValue& vibration_value); + + void VibrateController(const DeviceHandle& vibration_device_handle, + const VibrationValue& vibration_value); + + void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles, + const std::vector<VibrationValue>& vibration_values); + + VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const; + + void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle); + + void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index); - void VibrateController(const std::vector<u32>& controllers, - const std::vector<Vibration>& vibrations); + void SetPermitVibrationSession(bool permit_vibration_session); - Vibration GetLastVibration() const; + bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const; std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; void SignalStyleSetChangedEvent(u32 npad_id) const; @@ -138,8 +175,8 @@ public: // Adds a new controller at an index with connection status. void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected); - void DisconnectNPad(u32 npad_id); - void DisconnectNPadAtIndex(std::size_t index); + void DisconnectNpad(u32 npad_id); + void DisconnectNpadAtIndex(std::size_t index); void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; @@ -148,8 +185,6 @@ public: LedPattern GetLedPattern(u32 npad_id); bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const; void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id); - void SetVibrationEnabled(bool can_vibrate); - bool IsVibrationEnabled() const; void ClearAllConnectedControllers(); void DisconnectAllConnectedControllers(); void ConnectAllDisconnectedControllers(); @@ -324,8 +359,8 @@ private: }; struct NPadEntry { - NPadType joy_styles; - NPadAssignments pad_assignment; + NpadStyleSet joy_styles; + NpadAssignments pad_assignment; ColorReadError single_color_error; ControllerColor single_color; @@ -368,7 +403,7 @@ private: u32 press_state{}; - NPadType style{}; + NpadStyleSet style{}; std::array<NPadEntry, 10> shared_memory_entries{}; using ButtonArray = std::array< std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, @@ -376,22 +411,28 @@ private: using StickArray = std::array< std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, 10>; + using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>, + Settings::NativeVibration::NUM_VIBRATIONS_HID>, + 10>; using MotionArray = std::array< - std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>, + std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>, 10>; ButtonArray buttons; StickArray sticks; + VibrationArray vibrations; MotionArray motions; std::vector<u32> supported_npad_id_types{}; NpadHoldType hold_type{NpadHoldType::Vertical}; NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; // Each controller should have their own styleset changed event std::array<Kernel::EventPair, 10> styleset_changed_events; - Vibration last_processed_vibration{}; + std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10> last_vibration_timepoints; + std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{}; + bool permit_vibration_session_enabled{false}; + 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{}; GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; - bool can_controllers_vibrate{true}; bool sixaxis_sensors_enabled{true}; bool sixaxis_at_rest{true}; std::array<ControllerPad, 10> npad_pad_states{}; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 50f709b25..902516b29 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -139,20 +139,34 @@ void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanose class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { public: - IActiveVibrationDeviceList() : ServiceFramework("IActiveVibrationDeviceList") { + explicit IActiveVibrationDeviceList(std::shared_ptr<IAppletResource> applet_resource_) + : ServiceFramework("IActiveVibrationDeviceList"), applet_resource(applet_resource_) { + // clang-format off static const FunctionInfo functions[] = { - {0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"}, + {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"}, }; + // clang-format on + RegisterHandlers(functions); } private: - void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; + + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .InitializeVibrationDevice(vibration_device_handle); + + LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}", + vibration_device_handle.npad_type, vibration_device_handle.npad_id, + vibration_device_handle.device_index); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + + std::shared_ptr<IAppletResource> applet_resource; }; std::shared_ptr<IAppletResource> Hid::GetAppletResource() { @@ -241,7 +255,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { {208, nullptr, "GetActualVibrationGcErmCommand"}, {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, - {211, nullptr, "IsVibrationDeviceMounted"}, + {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, @@ -320,142 +334,152 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) { rb.PushIpcInterface<IAppletResource>(applet_resource); } -void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) { +void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto basic_xpad_id{rp.Pop<u32>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", basic_xpad_id, - applet_resource_user_id); + applet_resource->ActivateController(HidController::DebugPad); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - applet_resource->ActivateController(HidController::XPad); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) { +void Hid::ActivateTouchScreen(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push(0); -} + applet_resource->ActivateController(HidController::Touchscreen); -void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); - LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { +void Hid::ActivateMouse(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop<u32>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); - LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + applet_resource->ActivateController(HidController::Mouse); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { +void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; + applet_resource->ActivateController(HidController::Keyboard); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - applet_resource->ActivateController(HidController::DebugPad); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::ActivateTouchScreen(Kernel::HLERequestContext& ctx) { +void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + const auto flags{rp.Pop<u32>()}; - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); - applet_resource->ActivateController(HidController::Touchscreen); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::ActivateMouse(Kernel::HLERequestContext& ctx) { +void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + u32 basic_xpad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + const auto parameters{rp.PopRaw<Parameters>()}; + + applet_resource->ActivateController(HidController::XPad); + + LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", + parameters.basic_xpad_id, parameters.applet_resource_user_id); - applet_resource->ActivateController(HidController::Mouse); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) { +void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id); - applet_resource->ActivateController(HidController::Keyboard); - IPC::ResponseBuilder rb{ctx, 2}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); + rb.Push(0); } -void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { +void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto flags{rp.Pop<u32>()}; - LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} + const auto parameters{rp.PopRaw<Parameters>()}; -void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto unknown{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); - LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, - applet_resource_user_id); + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - applet_resource->ActivateController(HidController::Gesture); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { - // Should have no effect with how our npad sets up the data +void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto unknown{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, - applet_resource_user_id); + const auto parameters{rp.PopRaw<Parameters>()}; + + applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - applet_resource->ActivateController(HidController::NPad); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + const auto parameters{rp.PopRaw<Parameters>()}; + + applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -463,11 +487,20 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + const auto parameters{rp.PopRaw<Parameters>()}; + + applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -475,12 +508,21 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - [[maybe_unused]] const auto enable{rp.Pop<bool>()}; - const auto handle{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + bool enable_sixaxis_sensor_fusion{}; + INSERT_PADDING_BYTES(3); + Controller_NPad::DeviceHandle sixaxis_handle{}; + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " + "device_index={}, applet_resource_user_id={}", + parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, + parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -488,14 +530,17 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop<u32>()}; - const auto drift_mode{rp.Pop<u32>()}; + const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; + const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode{drift_mode}); + .SetGyroscopeZeroDriftMode(drift_mode); - LOG_DEBUG(Service_HID, "called, handle={}, drift_mode={}, applet_resource_user_id={}", handle, + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " + "applet_resource_user_id={}", + sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index, drift_mode, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -504,29 +549,42 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw<Parameters>()}; - LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>( - static_cast<u32>(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetGyroscopeZeroDriftMode())); + rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) + .GetGyroscopeZeroDriftMode()); } void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw<Parameters>()}; applet_resource->GetController<Controller_NPad>(HidController::NPad) .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard); - LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -534,11 +592,18 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw<Parameters>()}; - LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -546,15 +611,34 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { .IsSixAxisSensorAtRest()); } +void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + u32 unknown{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw<Parameters>()}; + + applet_resource->ActivateController(HidController::Gesture); + + LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto supported_styleset{rp.Pop<u32>()}; - LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset); - applet_resource->GetController<Controller_NPad>(HidController::NPad) .SetSupportedStyleSet({supported_styleset}); + LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -565,21 +649,22 @@ void Hid::GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); - IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(controller.GetSupportedStyleSet().raw); + rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) + .GetSupportedStyleSet() + .raw); } void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetSupportedNpadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -588,48 +673,62 @@ void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; + applet_resource->ActivateController(HidController::NPad); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - applet_resource->ActivateController(HidController::NPad); } void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; + applet_resource->DeactivateController(HidController::NPad); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - applet_resource->DeactivateController(HidController::NPad); } void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_id{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - const auto unknown{rp.Pop<u64>()}; + struct Parameters { + u32 npad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + u64 unknown{}; + }; - LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", npad_id, - applet_resource_user_id, unknown); + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", + parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetStyleSetChangedEvent(npad_id)); + .GetStyleSetChangedEvent(parameters.npad_id)); } void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_id{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + u32 npad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, - applet_resource_user_id); + const auto parameters{rp.PopRaw<Parameters>()}; + + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .DisconnectNpad(parameters.npad_id); + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, + parameters.applet_resource_user_id); - applet_resource->GetController<Controller_NPad>(HidController::NPad).DisconnectNPad(npad_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -642,22 +741,41 @@ void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetLedPattern(npad_id) - .raw); + rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) + .GetLedPattern(npad_id) + .raw); +} + +void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { + // Should have no effect with how our npad sets up the data + IPC::RequestParser rp{ctx}; + struct Parameters { + u32 unknown{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw<Parameters>()}; + + applet_resource->ActivateController(HidController::NPad); + + LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); } void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - const auto hold_type{rp.Pop<u64>()}; + const auto hold_type{rp.PopEnum<Controller_NPad::NpadHoldType>()}; + + applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type); LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", applet_resource_user_id, hold_type); - auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); - controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type}); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -668,22 +786,26 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - const auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push<u64>(static_cast<u64>(controller.GetHoldType())); + rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad).GetHoldType()); } void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_id{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + u32 npad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id, - applet_resource_user_id); + const auto parameters{rp.PopRaw<Parameters>()}; + + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single); - auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); - controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single); + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -692,16 +814,22 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault IPC::RequestParser rp{ctx}; - const auto npad_id{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - const auto npad_joy_device_type{rp.Pop<u64>()}; + struct Parameters { + u32 npad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + u64 npad_joy_device_type{}; + }; + + const auto parameters{rp.PopRaw<Parameters>()}; + + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single); LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", - npad_id, applet_resource_user_id, npad_joy_device_type); - - auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); - controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single); + parameters.npad_id, parameters.applet_resource_user_id, + parameters.npad_joy_device_type); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -709,14 +837,19 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_id{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + u32 npad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw<Parameters>()}; - LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, - applet_resource_user_id); + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Dual); - auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); - controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual); + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -728,12 +861,12 @@ void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { const auto npad_id_2{rp.Pop<u32>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); + LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", npad_id_1, npad_id_2, applet_resource_user_id); - auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); - controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -742,9 +875,9 @@ void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; + applet_resource->GetController<Controller_NPad>(HidController::NPad).StartLRAssignmentMode(); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); - controller.StartLRAssignmentMode(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -754,9 +887,9 @@ void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; + applet_resource->GetController<Controller_NPad>(HidController::NPad).StopLRAssignmentMode(); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); - controller.StopLRAssignmentMode(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -765,13 +898,13 @@ void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) { void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - const auto mode{rp.Pop<u64>()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, mode={}", applet_resource_user_id, - mode); + const auto activation_mode{rp.PopEnum<Controller_NPad::NpadHandheldActivationMode>()}; applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetNpadHandheldActivationMode(Controller_NPad::NpadHandheldActivationMode{mode}); + .SetNpadHandheldActivationMode(activation_mode); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", + applet_resource_user_id, activation_mode); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -785,23 +918,24 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push<u64>( - static_cast<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetNpadHandheldActivationMode())); + rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) + .GetNpadHandheldActivationMode()); } void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_1{rp.Pop<u32>()}; - const auto npad_2{rp.Pop<u32>()}; + const auto npad_id_1{rp.Pop<u32>()}; + const auto npad_id_2{rp.Pop<u32>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}", - applet_resource_user_id, npad_1, npad_2); + const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SwapNpadAssignment(npad_id_1, npad_id_2); + + LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", + npad_id_1, npad_id_2, applet_resource_user_id); - auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); IPC::ResponseBuilder rb{ctx, 2}; - if (controller.SwapNpadAssignment(npad_1, npad_2)) { + if (res) { rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_HID, "Npads are not connected!"); @@ -811,144 +945,219 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_id{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + u32 npad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id, - applet_resource_user_id); + const auto parameters{rp.PopRaw<Parameters>()}; - auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<bool>(controller.IsUnintendedHomeButtonInputProtectionEnabled(npad_id)); + rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) + .IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id)); } void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto unintended_home_button_input_protection{rp.Pop<bool>()}; - const auto npad_id{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + bool unintended_home_button_input_protection{}; + INSERT_PADDING_BYTES(3); + u32 npad_id{}; + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw<Parameters>()}; + + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetUnintendedHomeButtonInputProtectionEnabled( + parameters.unintended_home_button_input_protection, parameters.npad_id); LOG_WARNING(Service_HID, "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," "applet_resource_user_id={}", - npad_id, unintended_home_button_input_protection, applet_resource_user_id); - - auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); - controller.SetUnintendedHomeButtonInputProtectionEnabled( - unintended_home_button_input_protection, npad_id); + parameters.unintended_home_button_input_protection, parameters.npad_id, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { +void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; + + VibrationDeviceInfo vibration_device_info; + + vibration_device_info.type = VibrationDeviceType::LinearResonantActuator; + + switch (vibration_device_handle.device_index) { + case Controller_NPad::DeviceIndex::Left: + vibration_device_info.position = VibrationDevicePosition::Left; + break; + case Controller_NPad::DeviceIndex::Right: + vibration_device_info.position = VibrationDevicePosition::Right; + break; + case Controller_NPad::DeviceIndex::None: + default: + UNREACHABLE_MSG("DeviceIndex should never be None!"); + vibration_device_info.position = VibrationDevicePosition::None; + break; + } - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", + vibration_device_info.type, vibration_device_info.position); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(vibration_device_info); +} + +void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Controller_NPad::DeviceHandle vibration_device_handle{}; + Controller_NPad::VibrationValue vibration_value{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw<Parameters>()}; + + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .VibrateController(parameters.vibration_device_handle, parameters.vibration_value); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); - applet_resource->GetController<Controller_NPad>(HidController::NPad).SetVibrationEnabled(true); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { +void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Controller_NPad::DeviceHandle vibration_device_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(applet_resource->GetController<Controller_NPad>(HidController::NPad) + .GetLastVibration(parameters.vibration_device_handle)); +} + +void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called"); - applet_resource->GetController<Controller_NPad>(HidController::NPad).SetVibrationEnabled(false); - IPC::ResponseBuilder rb{ctx, 2}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IActiveVibrationDeviceList>(applet_resource); } -void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { +void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto controller{rp.Pop<u32>()}; - const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + const auto can_vibrate{rp.Pop<bool>()}; - LOG_DEBUG(Service_HID, "called, controller={}, applet_resource_user_id={}", controller, - applet_resource_user_id); + Settings::values.vibration_enabled.SetValue(can_vibrate); + + LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); +} - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .VibrateController({controller}, {vibration_values}); +void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(Settings::values.vibration_enabled.GetValue()); } void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - const auto controllers = ctx.ReadBuffer(0); + const auto handles = ctx.ReadBuffer(0); const auto vibrations = ctx.ReadBuffer(1); - std::vector<u32> controller_list(controllers.size() / sizeof(u32)); - std::vector<Controller_NPad::Vibration> vibration_list(vibrations.size() / - sizeof(Controller_NPad::Vibration)); + std::vector<Controller_NPad::DeviceHandle> vibration_device_handles( + handles.size() / sizeof(Controller_NPad::DeviceHandle)); + std::vector<Controller_NPad::VibrationValue> vibration_values( + vibrations.size() / sizeof(Controller_NPad::VibrationValue)); - std::memcpy(controller_list.data(), controllers.data(), controllers.size()); - std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); + std::memcpy(vibration_device_handles.data(), handles.data(), handles.size()); + std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size()); applet_resource->GetController<Controller_NPad>(HidController::NPad) - .VibrateController(controller_list, vibration_list); + .VibrateControllers(vibration_device_handles, vibration_values); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { +void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto controller_id{rp.Pop<u32>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id, - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(RESULT_SUCCESS); - rb.PushRaw<Controller_NPad::Vibration>( - applet_resource->GetController<Controller_NPad>(HidController::NPad).GetLastVibration()); -} + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetPermitVibrationSession(true); -void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - IPC::ResponseBuilder rb{ctx, 4}; + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(1); - rb.Push<u32>(0); } -void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { +void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetPermitVibrationSession(false); + LOG_DEBUG(Service_HID, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IActiveVibrationDeviceList>(); } -void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { +void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto can_vibrate{rp.Pop<bool>()}; - Settings::values.vibration_enabled = can_vibrate; + struct Parameters { + Controller_NPad::DeviceHandle vibration_device_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); + const auto parameters{rp.PopRaw<Parameters>()}; - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} - -void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(Settings::values.vibration_enabled); + rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) + .IsVibrationDeviceMounted(parameters.vibration_device_handle)); } void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { @@ -964,11 +1173,19 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -976,11 +1193,19 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop<u32>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index fd0372b18..c8e4a4b55 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -86,17 +86,15 @@ public: private: void CreateAppletResource(Kernel::HLERequestContext& ctx); - void ActivateXpad(Kernel::HLERequestContext& ctx); - void GetXpadIDs(Kernel::HLERequestContext& ctx); - void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx); - void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx); void ActivateDebugPad(Kernel::HLERequestContext& ctx); void ActivateTouchScreen(Kernel::HLERequestContext& ctx); void ActivateMouse(Kernel::HLERequestContext& ctx); void ActivateKeyboard(Kernel::HLERequestContext& ctx); void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx); - void ActivateGesture(Kernel::HLERequestContext& ctx); - void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); + void ActivateXpad(Kernel::HLERequestContext& ctx); + void GetXpadIDs(Kernel::HLERequestContext& ctx); + void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx); + void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx); void StartSixAxisSensor(Kernel::HLERequestContext& ctx); void StopSixAxisSensor(Kernel::HLERequestContext& ctx); void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx); @@ -104,6 +102,7 @@ private: void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); + void ActivateGesture(Kernel::HLERequestContext& ctx); void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx); @@ -112,6 +111,7 @@ private: void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx); void DisconnectNpad(Kernel::HLERequestContext& ctx); void GetPlayerLedPattern(Kernel::HLERequestContext& ctx); + void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx); void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx); void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx); @@ -125,15 +125,16 @@ private: void SwapNpadAssignment(Kernel::HLERequestContext& ctx); void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx); void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx); - void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx); - void EndPermitVibrationSession(Kernel::HLERequestContext& ctx); + void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx); void SendVibrationValue(Kernel::HLERequestContext& ctx); - void SendVibrationValues(Kernel::HLERequestContext& ctx); void GetActualVibrationValue(Kernel::HLERequestContext& ctx); - void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx); void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx); void PermitVibration(Kernel::HLERequestContext& ctx); void IsVibrationPermitted(Kernel::HLERequestContext& ctx); + void SendVibrationValues(Kernel::HLERequestContext& ctx); + void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx); + void EndPermitVibrationSession(Kernel::HLERequestContext& ctx); + void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx); void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); @@ -146,6 +147,22 @@ private: void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); + enum class VibrationDeviceType : u32 { + LinearResonantActuator = 1, + }; + + enum class VibrationDevicePosition : u32 { + None = 0, + Left = 1, + Right = 2, + }; + + struct VibrationDeviceInfo { + VibrationDeviceType type{}; + VibrationDevicePosition position{}; + }; + static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size."); + std::shared_ptr<IAppletResource> applet_resource; Core::System& system; }; diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index eeaca44b6..65c209725 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -9,6 +9,7 @@ #include "common/alignment.h" #include "common/hex_util.h" #include "common/scope_exit.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/memory/page_table.h" diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index dec96b771..49a42a9c9 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -7,6 +7,7 @@ #include "common/logging/log.h" #include "common/scope_exit.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/lm/lm.h" #include "core/hle/service/lm/manager.h" diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 58ee1f712..2594e6839 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 "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/vfs.h" @@ -29,8 +30,8 @@ IAccountProxyInterface::IAccountProxyInterface() : ServiceFramework{"IAccountPro IAccountProxyInterface::~IAccountProxyInterface() = default; -IApplicationManagerInterface::IApplicationManagerInterface() - : ServiceFramework{"IApplicationManagerInterface"} { +IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_) + : ServiceFramework{"IApplicationManagerInterface"}, system{system_} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "ListApplicationRecord"}, @@ -298,7 +299,8 @@ void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestC const auto size = ctx.GetWriteBufferSize(); - const FileSys::PatchManager pm{title_id}; + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + system.GetContentProvider()}; const auto control = pm.GetControlMetadata(); std::vector<u8> out; @@ -538,14 +540,14 @@ IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface() IFactoryResetInterface::~IFactoryResetInterface() = default; -NS::NS(const char* name) : ServiceFramework{name} { +NS::NS(const char* name, Core::System& system_) : ServiceFramework{name}, system{system_} { // clang-format off static const FunctionInfo functions[] = { {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"}, {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"}, - {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"}, + {7996, &NS::PushIApplicationManagerInterface, "GetApplicationManagerInterface"}, {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"}, {7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"}, {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"}, @@ -558,7 +560,7 @@ NS::NS(const char* name) : ServiceFramework{name} { NS::~NS() = default; std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const { - return GetInterface<IApplicationManagerInterface>(); + return GetInterface<IApplicationManagerInterface>(system); } class NS_DEV final : public ServiceFramework<NS_DEV> { @@ -678,11 +680,11 @@ public: void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { - std::make_shared<NS>("ns:am2")->InstallAsService(service_manager); - std::make_shared<NS>("ns:ec")->InstallAsService(service_manager); - std::make_shared<NS>("ns:rid")->InstallAsService(service_manager); - std::make_shared<NS>("ns:rt")->InstallAsService(service_manager); - std::make_shared<NS>("ns:web")->InstallAsService(service_manager); + std::make_shared<NS>("ns:am2", system)->InstallAsService(service_manager); + std::make_shared<NS>("ns:ec", system)->InstallAsService(service_manager); + 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_DEV>()->InstallAsService(service_manager); std::make_shared<NS_SU>()->InstallAsService(service_manager); diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index c2554b878..c90ccd755 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -6,6 +6,10 @@ #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Service { namespace FileSystem { @@ -22,7 +26,7 @@ public: class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> { public: - explicit IApplicationManagerInterface(); + explicit IApplicationManagerInterface(Core::System& system_); ~IApplicationManagerInterface() override; ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages); @@ -32,6 +36,8 @@ private: void GetApplicationControlData(Kernel::HLERequestContext& ctx); void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx); void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx); + + Core::System& system; }; class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { @@ -72,13 +78,13 @@ public: class NS final : public ServiceFramework<NS> { public: - explicit NS(const char* name); + explicit NS(const char* name, Core::System& system_); ~NS() override; std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const; private: - template <typename T> + template <typename T, typename... Args> void PushInterface(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NS, "called"); @@ -87,13 +93,23 @@ private: rb.PushIpcInterface<T>(); } - template <typename T> - std::shared_ptr<T> GetInterface() const { + void PushIApplicationManagerInterface(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NS, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IApplicationManagerInterface>(system); + } + + template <typename T, typename... Args> + std::shared_ptr<T> GetInterface(Args&&... args) const { static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>, "Not a base of ServiceFrameworkBase"); - return std::make_shared<T>(); + return std::make_shared<T>(std::forward<Args>(args)...); } + + Core::System& system; }; /// Registers all NS services with the specified service manager. diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 0240d6643..5681599ba 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -24,25 +24,37 @@ public: explicit nvdevice(Core::System& system) : system{system} {} virtual ~nvdevice() = default; - union Ioctl { - u32_le raw; - BitField<0, 8, u32> cmd; - BitField<8, 8, u32> group; - BitField<16, 14, u32> length; - BitField<30, 1, u32> is_in; - BitField<31, 1, u32> is_out; - }; + /** + * Handles an ioctl1 request. + * @param command The ioctl command id. + * @param input A buffer containing the input data for the ioctl. + * @param output A buffer where the output data will be written to. + * @returns The result code of the ioctl. + */ + virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, + std::vector<u8>& output) = 0; + + /** + * Handles an ioctl2 request. + * @param command The ioctl command id. + * @param input A buffer containing the input data for the ioctl. + * @param inline_input A buffer containing the input data for the ioctl which has been inlined. + * @param output A buffer where the output data will be written to. + * @returns The result code of the ioctl. + */ + virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) = 0; /** - * Handles an ioctl request. + * Handles an ioctl3 request. * @param command The ioctl command id. * @param input A buffer containing the input data for the ioctl. * @param output A buffer where the output data will be written to. + * @param inline_output A buffer where the inlined output data will be written to. * @returns The result code of the ioctl. */ - virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) = 0; + virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) = 0; protected: Core::System& system; diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 3f7b8e670..ce615c758 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -18,11 +18,22 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} nvdisp_disp0 ::~nvdisp_disp0() = default; -u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) { - UNIMPLEMENTED_MSG("Unimplemented ioctl"); - return 0; +NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input, + std::vector<u8>& output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; } void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 6fcdeee84..55a33b7e4 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -20,9 +20,11 @@ public: explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvdisp_disp0() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) override; + NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) override; + NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) override; /// Performs a screen flip, drawing the buffer pointed to by the handle. void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index f2529a12e..6b062e10e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -17,59 +17,77 @@ namespace Service::Nvidia::Devices { -namespace NvErrCodes { -constexpr u32 Success{}; -constexpr u32 OutOfMemory{static_cast<u32>(-12)}; -constexpr u32 InvalidInput{static_cast<u32>(-22)}; -} // namespace NvErrCodes - nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} nvhost_as_gpu::~nvhost_as_gpu() = default; -u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) { - LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", - command.raw, input.size(), output.size()); - - switch (static_cast<IoctlCommand>(command.raw)) { - case IoctlCommand::IocInitalizeExCommand: - return InitalizeEx(input, output); - case IoctlCommand::IocAllocateSpaceCommand: - return AllocateSpace(input, output); - case IoctlCommand::IocMapBufferExCommand: - return MapBufferEx(input, output); - case IoctlCommand::IocBindChannelCommand: - return BindChannel(input, output); - case IoctlCommand::IocGetVaRegionsCommand: - return GetVARegions(input, output); - case IoctlCommand::IocUnmapBufferCommand: - return UnmapBuffer(input, output); - case IoctlCommand::IocFreeSpaceCommand: - return FreeSpace(input, output); +NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, + std::vector<u8>& output) { + switch (command.group) { + case 'A': + switch (command.cmd) { + case 0x1: + return BindChannel(input, output); + case 0x2: + return AllocateSpace(input, output); + case 0x3: + return FreeSpace(input, output); + case 0x5: + return UnmapBuffer(input, output); + case 0x6: + return MapBufferEx(input, output); + case 0x8: + return GetVARegions(input, output); + case 0x9: + return InitalizeEx(input, output); + case 0x14: + return Remap(input, output); + default: + break; + } + break; default: break; } - if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) { - return Remap(input, output); - } + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} - UNIMPLEMENTED_MSG("Unimplemented ioctl command"); - return 0; +NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) { + switch (command.group) { + case 'A': + switch (command.cmd) { + case 0x8: + return GetVARegions(input, output, inline_output); + default: + break; + } + break; + default: + break; + } + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; } -u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { IoctlInitalizeEx params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); - return 0; + return NvResult::Success; } -u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { IoctlAllocSpace params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -83,17 +101,17 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& params.offset = system.GPU().MemoryManager().Allocate(size, params.align); } - auto result{NvErrCodes::Success}; + auto result = NvResult::Success; if (!params.offset) { LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size); - result = NvErrCodes::OutOfMemory; + result = NvResult::InsufficientMemory; } std::memcpy(output.data(), ¶ms, output.size()); return result; } -u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { IoctlFreeSpace params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -104,15 +122,15 @@ u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& outp static_cast<std::size_t>(params.pages) * params.page_size); std::memcpy(output.data(), ¶ms, output.size()); - return NvErrCodes::Success; + return NvResult::Success; } -u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { const auto num_entries = input.size() / sizeof(IoctlRemapEntry); LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); - auto result{NvErrCodes::Success}; + auto result = NvResult::Success; std::vector<IoctlRemapEntry> entries(num_entries); std::memcpy(entries.data(), input.data(), input.size()); @@ -123,7 +141,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) const auto object{nvmap_dev->GetObject(entry.nvmap_handle)}; if (!object) { LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); - result = NvErrCodes::InvalidInput; + result = NvResult::InvalidState; break; } @@ -134,7 +152,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) if (!addr) { LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); - result = NvErrCodes::InvalidInput; + result = NvResult::InvalidState; break; } } @@ -143,7 +161,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) return result; } -u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { IoctlMapBufferEx params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -157,7 +175,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou if (!object) { LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle); std::memcpy(output.data(), ¶ms, output.size()); - return NvErrCodes::InvalidInput; + return NvResult::InvalidState; } // The real nvservices doesn't make a distinction between handles and ids, and @@ -184,16 +202,16 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou params.mapping_size, params.offset); std::memcpy(output.data(), ¶ms, output.size()); - return NvErrCodes::InvalidInput; + return NvResult::InvalidState; } std::memcpy(output.data(), ¶ms, output.size()); - return NvErrCodes::Success; + return NvResult::Success; } else { LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); std::memcpy(output.data(), ¶ms, output.size()); - return NvErrCodes::InvalidInput; + return NvResult::InvalidState; } } @@ -213,10 +231,10 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size); } - auto result{NvErrCodes::Success}; + auto result = NvResult::Success; if (!params.offset) { LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size); - result = NvErrCodes::InvalidInput; + result = NvResult::InvalidState; } else { AddBufferMap(params.offset, size, physical_address, is_alloc); } @@ -225,7 +243,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou return result; } -u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { IoctlUnmapBuffer params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -238,20 +256,42 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou } std::memcpy(output.data(), ¶ms, output.size()); - return NvErrCodes::Success; + return NvResult::Success; } -u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { IoctlBindChannel params{}; std::memcpy(¶ms, input.data(), input.size()); - - LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd); channel = params.fd; - return 0; + return NvResult::Success; +} + +NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlGetVaRegions params{}; + std::memcpy(¶ms, input.data(), input.size()); + + LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, + params.buf_size); + + params.buf_size = 0x30; + params.regions[0].offset = 0x04000000; + params.regions[0].page_size = 0x1000; + params.regions[0].pages = 0x3fbfff; + + params.regions[1].offset = 0x04000000; + params.regions[1].page_size = 0x10000; + params.regions[1].pages = 0x1bffff; + + // TODO(ogniK): This probably can stay stubbed but should add support way way later + + std::memcpy(output.data(), ¶ms, output.size()); + return NvResult::Success; } -u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) { IoctlGetVaRegions params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -270,7 +310,8 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o // TODO(ogniK): This probably can stay stubbed but should add support way way later std::memcpy(output.data(), ¶ms, output.size()); - return 0; + std::memcpy(inline_output.data(), ¶ms.regions, inline_output.size()); + return NvResult::Success; } std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index fcdb40d93..08035fa0e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -30,9 +30,11 @@ public: explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvhost_as_gpu() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) override; + NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) override; + NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) override; private: class BufferMap final { @@ -74,32 +76,21 @@ private: bool is_allocated{}; }; - enum class IoctlCommand : u32_le { - IocInitalizeExCommand = 0x40284109, - IocAllocateSpaceCommand = 0xC0184102, - IocRemapCommand = 0x00000014, - IocMapBufferExCommand = 0xC0284106, - IocBindChannelCommand = 0x40044101, - IocGetVaRegionsCommand = 0xC0404108, - IocUnmapBufferCommand = 0xC0084105, - IocFreeSpaceCommand = 0xC0104103, - }; - struct IoctlInitalizeEx { - u32_le big_page_size; // depends on GPU's available_big_page_sizes; 0=default - s32_le as_fd; // ignored; passes 0 - u32_le flags; // passes 0 - u32_le reserved; // ignored; passes 0 - u64_le unk0; - u64_le unk1; - u64_le unk2; + u32_le big_page_size{}; // depends on GPU's available_big_page_sizes; 0=default + s32_le as_fd{}; // ignored; passes 0 + u32_le flags{}; // passes 0 + u32_le reserved{}; // ignored; passes 0 + u64_le unk0{}; + u64_le unk1{}; + u64_le unk2{}; }; static_assert(sizeof(IoctlInitalizeEx) == 40, "IoctlInitalizeEx is incorrect size"); struct IoctlAllocSpace { - u32_le pages; - u32_le page_size; - AddressSpaceFlags flags; + u32_le pages{}; + u32_le page_size{}; + AddressSpaceFlags flags{}; INSERT_PADDING_WORDS(1); union { u64_le offset; @@ -109,70 +100,73 @@ private: static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size"); struct IoctlFreeSpace { - u64_le offset; - u32_le pages; - u32_le page_size; + u64_le offset{}; + u32_le pages{}; + u32_le page_size{}; }; static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size"); struct IoctlRemapEntry { - u16_le flags; - u16_le kind; - u32_le nvmap_handle; - u32_le map_offset; - u32_le offset; - u32_le pages; + u16_le flags{}; + u16_le kind{}; + u32_le nvmap_handle{}; + u32_le map_offset{}; + u32_le offset{}; + u32_le pages{}; }; static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); struct IoctlMapBufferEx { - AddressSpaceFlags flags; // bit0: fixed_offset, bit2: cacheable - u32_le kind; // -1 is default - u32_le nvmap_handle; - u32_le page_size; // 0 means don't care - s64_le buffer_offset; - u64_le mapping_size; - s64_le offset; + AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable + u32_le kind{}; // -1 is default + u32_le nvmap_handle{}; + u32_le page_size{}; // 0 means don't care + s64_le buffer_offset{}; + u64_le mapping_size{}; + s64_le offset{}; }; static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size"); struct IoctlUnmapBuffer { - s64_le offset; + s64_le offset{}; }; static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size"); struct IoctlBindChannel { - u32_le fd; + s32_le fd{}; }; static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size"); struct IoctlVaRegion { - u64_le offset; - u32_le page_size; + u64_le offset{}; + u32_le page_size{}; INSERT_PADDING_WORDS(1); - u64_le pages; + u64_le pages{}; }; static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size"); struct IoctlGetVaRegions { - u64_le buf_addr; // (contained output user ptr on linux, ignored) - u32_le buf_size; // forced to 2*sizeof(struct va_region) - u32_le reserved; - IoctlVaRegion regions[2]; + u64_le buf_addr{}; // (contained output user ptr on linux, ignored) + u32_le buf_size{}; // forced to 2*sizeof(struct va_region) + u32_le reserved{}; + IoctlVaRegion regions[2]{}; }; static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, "IoctlGetVaRegions is incorrect size"); - u32 channel{}; + s32 channel{}; + + NvResult InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output); + NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); + NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output); + NvResult MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); + NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); + NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output); + NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output); - u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output); - u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); - u32 Remap(const std::vector<u8>& input, std::vector<u8>& output); - u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); - u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); - u32 FreeSpace(const std::vector<u8>& input, std::vector<u8>& output); - u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output); - u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); + NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); + NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output); std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 8356a8139..d90cf90a8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -20,41 +20,54 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface, : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {} nvhost_ctrl::~nvhost_ctrl() = default; -u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) { - LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", - command.raw, input.size(), output.size()); - - switch (static_cast<IoctlCommand>(command.raw)) { - case IoctlCommand::IocGetConfigCommand: - return NvOsGetConfigU32(input, output); - case IoctlCommand::IocCtrlEventWaitCommand: - return IocCtrlEventWait(input, output, false, ctrl); - case IoctlCommand::IocCtrlEventWaitAsyncCommand: - return IocCtrlEventWait(input, output, true, ctrl); - case IoctlCommand::IocCtrlEventRegisterCommand: - return IocCtrlEventRegister(input, output); - case IoctlCommand::IocCtrlEventUnregisterCommand: - return IocCtrlEventUnregister(input, output); - case IoctlCommand::IocCtrlClearEventWaitCommand: - return IocCtrlClearEventWait(input, output); +NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + switch (command.group) { + case 0x0: + switch (command.cmd) { + case 0x1b: + return NvOsGetConfigU32(input, output); + case 0x1c: + return IocCtrlClearEventWait(input, output); + case 0x1d: + return IocCtrlEventWait(input, output, false); + case 0x1e: + return IocCtrlEventWait(input, output, true); + case 0x1f: + return IocCtrlEventRegister(input, output); + case 0x20: + return IocCtrlEventUnregister(input, output); + } + break; default: - UNIMPLEMENTED_MSG("Unimplemented ioctl"); - return 0; + break; } + + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; } -u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { IocGetConfigParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), params.param_str.data()); - return 0x30006; // Returns error on production mode + return NvResult::ConfigVarNotFound; // Returns error on production mode } -u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, - bool is_async, IoctlCtrl& ctrl) { +NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, + bool is_async) { IocCtrlEventWaitParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", @@ -126,10 +139,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& params.value |= event_id; event.event.writable->Clear(); gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); - if (!is_async && ctrl.fresh_call) { - ctrl.must_delay = true; - ctrl.timeout = params.timeout; - ctrl.event_id = event_id; + if (!is_async) { return NvResult::Timeout; } std::memcpy(output.data(), ¶ms, sizeof(params)); @@ -139,7 +149,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& return NvResult::BadParameter; } -u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { IocCtrlEventRegisterParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); const u32 event_id = params.user_event_id & 0x00FF; @@ -154,7 +164,8 @@ u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector< return NvResult::Success; } -u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, + std::vector<u8>& output) { IocCtrlEventUnregisterParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); const u32 event_id = params.user_event_id & 0x00FF; @@ -169,7 +180,7 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto return NvResult::Success; } -u32 nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { IocCtrlEventSignalParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 24ad96cb9..c5aa1362a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -18,132 +18,113 @@ public: SyncpointManager& syncpoint_manager); ~nvhost_ctrl() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) override; + NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) override; + NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) override; private: - enum class IoctlCommand : u32_le { - IocSyncptReadCommand = 0xC0080014, - IocSyncptIncrCommand = 0x40040015, - IocSyncptWaitCommand = 0xC00C0016, - IocModuleMutexCommand = 0x40080017, - IocModuleRegRDWRCommand = 0xC0180018, - IocSyncptWaitexCommand = 0xC0100019, - IocSyncptReadMaxCommand = 0xC008001A, - IocGetConfigCommand = 0xC183001B, - IocCtrlClearEventWaitCommand = 0xC004001C, - IocCtrlEventWaitCommand = 0xC010001D, - IocCtrlEventWaitAsyncCommand = 0xC010001E, - IocCtrlEventRegisterCommand = 0xC004001F, - IocCtrlEventUnregisterCommand = 0xC0040020, - IocCtrlEventKillCommand = 0x40080021, - }; struct IocSyncptReadParams { - u32_le id; - u32_le value; + u32_le id{}; + u32_le value{}; }; static_assert(sizeof(IocSyncptReadParams) == 8, "IocSyncptReadParams is incorrect size"); struct IocSyncptIncrParams { - u32_le id; + u32_le id{}; }; static_assert(sizeof(IocSyncptIncrParams) == 4, "IocSyncptIncrParams is incorrect size"); struct IocSyncptWaitParams { - u32_le id; - u32_le thresh; - s32_le timeout; + u32_le id{}; + u32_le thresh{}; + s32_le timeout{}; }; static_assert(sizeof(IocSyncptWaitParams) == 12, "IocSyncptWaitParams is incorrect size"); struct IocModuleMutexParams { - u32_le id; - u32_le lock; // (0 = unlock and 1 = lock) + u32_le id{}; + u32_le lock{}; // (0 = unlock and 1 = lock) }; static_assert(sizeof(IocModuleMutexParams) == 8, "IocModuleMutexParams is incorrect size"); struct IocModuleRegRDWRParams { - u32_le id; - u32_le num_offsets; - u32_le block_size; - u32_le offsets; - u32_le values; - u32_le write; + u32_le id{}; + u32_le num_offsets{}; + u32_le block_size{}; + u32_le offsets{}; + u32_le values{}; + u32_le write{}; }; static_assert(sizeof(IocModuleRegRDWRParams) == 24, "IocModuleRegRDWRParams is incorrect size"); struct IocSyncptWaitexParams { - u32_le id; - u32_le thresh; - s32_le timeout; - u32_le value; + u32_le id{}; + u32_le thresh{}; + s32_le timeout{}; + u32_le value{}; }; static_assert(sizeof(IocSyncptWaitexParams) == 16, "IocSyncptWaitexParams is incorrect size"); struct IocSyncptReadMaxParams { - u32_le id; - u32_le value; + u32_le id{}; + u32_le value{}; }; static_assert(sizeof(IocSyncptReadMaxParams) == 8, "IocSyncptReadMaxParams is incorrect size"); struct IocGetConfigParams { - std::array<char, 0x41> domain_str; - std::array<char, 0x41> param_str; - std::array<char, 0x101> config_str; + std::array<char, 0x41> domain_str{}; + std::array<char, 0x41> param_str{}; + std::array<char, 0x101> config_str{}; }; static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); struct IocCtrlEventSignalParams { - u32_le event_id; + u32_le event_id{}; }; static_assert(sizeof(IocCtrlEventSignalParams) == 4, "IocCtrlEventSignalParams is incorrect size"); struct IocCtrlEventWaitParams { - u32_le syncpt_id; - u32_le threshold; - s32_le timeout; - u32_le value; + u32_le syncpt_id{}; + u32_le threshold{}; + s32_le timeout{}; + u32_le value{}; }; static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size"); struct IocCtrlEventWaitAsyncParams { - u32_le syncpt_id; - u32_le threshold; - u32_le timeout; - u32_le value; + u32_le syncpt_id{}; + u32_le threshold{}; + u32_le timeout{}; + u32_le value{}; }; static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16, "IocCtrlEventWaitAsyncParams is incorrect size"); struct IocCtrlEventRegisterParams { - u32_le user_event_id; + u32_le user_event_id{}; }; static_assert(sizeof(IocCtrlEventRegisterParams) == 4, "IocCtrlEventRegisterParams is incorrect size"); struct IocCtrlEventUnregisterParams { - u32_le user_event_id; + u32_le user_event_id{}; }; static_assert(sizeof(IocCtrlEventUnregisterParams) == 4, "IocCtrlEventUnregisterParams is incorrect size"); struct IocCtrlEventKill { - u64_le user_events; + u64_le user_events{}; }; static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); - u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); - - u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async, - IoctlCtrl& ctrl); - - u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); - - u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); - - u32 IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); + NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); + NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async); + NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); + NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); + NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); EventInterface& events_interface; SyncpointManager& syncpoint_manager; 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 fba89e7a6..2d7ea433c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -15,39 +15,66 @@ namespace Service::Nvidia::Devices { nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {} nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; -u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, - const std::vector<u8>& input2, std::vector<u8>& output, - std::vector<u8>& output2, IoctlCtrl& ctrl, IoctlVersion version) { - LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", - command.raw, input.size(), output.size()); - - switch (static_cast<IoctlCommand>(command.raw)) { - case IoctlCommand::IocGetCharacteristicsCommand: - return GetCharacteristics(input, output, output2, version); - case IoctlCommand::IocGetTPCMasksCommand: - return GetTPCMasks(input, output, output2, version); - case IoctlCommand::IocGetActiveSlotMaskCommand: - return GetActiveSlotMask(input, output); - case IoctlCommand::IocZcullGetCtxSizeCommand: - return ZCullGetCtxSize(input, output); - case IoctlCommand::IocZcullGetInfo: - return ZCullGetInfo(input, output); - case IoctlCommand::IocZbcSetTable: - return ZBCSetTable(input, output); - case IoctlCommand::IocZbcQueryTable: - return ZBCQueryTable(input, output); - case IoctlCommand::IocFlushL2: - return FlushL2(input, output); - case IoctlCommand::IocGetGpuTime: - return GetGpuTime(input, output); +NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, + std::vector<u8>& output) { + switch (command.group) { + case 'G': + switch (command.cmd) { + case 0x1: + return ZCullGetCtxSize(input, output); + case 0x2: + return ZCullGetInfo(input, output); + case 0x3: + return ZBCSetTable(input, output); + case 0x4: + return ZBCQueryTable(input, output); + case 0x5: + return GetCharacteristics(input, output); + case 0x6: + return GetTPCMasks(input, output); + case 0x7: + return FlushL2(input, output); + case 0x14: + return GetActiveSlotMask(input, output); + case 0x1c: + return GetGpuTime(input, output); + default: + break; + } + break; + } + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, + std::vector<u8>& output, std::vector<u8>& inline_output) { + switch (command.group) { + case 'G': + switch (command.cmd) { + case 0x5: + return GetCharacteristics(input, output, inline_output); + case 0x6: + return GetTPCMasks(input, output, inline_output); + default: + break; + } + break; default: - UNIMPLEMENTED_MSG("Unimplemented ioctl"); - return 0; + break; } + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; } -u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, - std::vector<u8>& output2, IoctlVersion version) { +NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, + std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlCharacteristics params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -88,36 +115,83 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto params.gc.gr_compbit_store_base_hw = 0x0; params.gpu_characteristics_buf_size = 0xA0; params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) + std::memcpy(output.data(), ¶ms, output.size()); + return NvResult::Success; +} - if (version == IoctlVersion::Version3) { - std::memcpy(output.data(), input.data(), output.size()); - std::memcpy(output2.data(), ¶ms.gc, output2.size()); - } else { - std::memcpy(output.data(), ¶ms, output.size()); - } - return 0; +NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlCharacteristics params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.gc.arch = 0x120; + params.gc.impl = 0xb; + params.gc.rev = 0xa1; + params.gc.num_gpc = 0x1; + params.gc.l2_cache_size = 0x40000; + params.gc.on_board_video_memory_size = 0x0; + params.gc.num_tpc_per_gpc = 0x2; + params.gc.bus_type = 0x20; + params.gc.big_page_size = 0x20000; + params.gc.compression_page_size = 0x20000; + params.gc.pde_coverage_bit_count = 0x1B; + params.gc.available_big_page_sizes = 0x30000; + params.gc.gpc_mask = 0x1; + params.gc.sm_arch_sm_version = 0x503; + params.gc.sm_arch_spa_version = 0x503; + params.gc.sm_arch_warp_count = 0x80; + params.gc.gpu_va_bit_count = 0x28; + params.gc.reserved = 0x0; + params.gc.flags = 0x55; + params.gc.twod_class = 0x902D; + params.gc.threed_class = 0xB197; + params.gc.compute_class = 0xB1C0; + params.gc.gpfifo_class = 0xB06F; + params.gc.inline_to_memory_class = 0xA140; + params.gc.dma_copy_class = 0xB0B5; + params.gc.max_fbps_count = 0x1; + params.gc.fbp_en_mask = 0x0; + params.gc.max_ltc_per_fbp = 0x2; + params.gc.max_lts_per_ltc = 0x1; + params.gc.max_tex_per_tpc = 0x0; + params.gc.max_gpc_count = 0x1; + params.gc.rop_l2_en_mask_0 = 0x21D70; + params.gc.rop_l2_en_mask_1 = 0x0; + params.gc.chipname = 0x6230326D67; + params.gc.gr_compbit_store_base_hw = 0x0; + params.gpu_characteristics_buf_size = 0xA0; + params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) + + std::memcpy(output.data(), input.data(), output.size()); + std::memcpy(inline_output.data(), ¶ms.gc, inline_output.size()); + return NvResult::Success; } -u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, - std::vector<u8>& output2, IoctlVersion version) { +NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) { IoctlGpuGetTpcMasksArgs params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); if (params.mask_buffer_size != 0) { params.tcp_mask = 3; } + std::memcpy(output.data(), ¶ms, output.size()); + return NvResult::Success; +} - if (version == IoctlVersion::Version3) { - std::memcpy(output.data(), input.data(), output.size()); - std::memcpy(output2.data(), ¶ms.tcp_mask, output2.size()); - } else { - std::memcpy(output.data(), ¶ms, output.size()); +NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) { + IoctlGpuGetTpcMasksArgs params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); + if (params.mask_buffer_size != 0) { + params.tcp_mask = 3; } - - return 0; + std::memcpy(output.data(), ¶ms, output.size()); + std::memcpy(inline_output.data(), ¶ms.tcp_mask, inline_output.size()); + return NvResult::Success; } -u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlActiveSlotMask params{}; @@ -127,10 +201,10 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector params.slot = 0x07; params.mask = 0x01; std::memcpy(output.data(), ¶ms, output.size()); - return 0; + return NvResult::Success; } -u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlZcullGetCtxSize params{}; @@ -139,10 +213,10 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u } params.size = 0x1; std::memcpy(output.data(), ¶ms, output.size()); - return 0; + return NvResult::Success; } -u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlNvgpuGpuZcullGetInfoArgs params{}; @@ -162,47 +236,47 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& params.subregion_height_align_pixels = 0x40; params.subregion_count = 0x10; std::memcpy(output.data(), ¶ms, output.size()); - return 0; + return NvResult::Success; } -u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); IoctlZbcSetTable params{}; std::memcpy(¶ms, input.data(), input.size()); // TODO(ogniK): What does this even actually do? std::memcpy(output.data(), ¶ms, output.size()); - return 0; + return NvResult::Success; } -u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); IoctlZbcQueryTable params{}; std::memcpy(¶ms, input.data(), input.size()); // TODO : To implement properly std::memcpy(output.data(), ¶ms, output.size()); - return 0; + return NvResult::Success; } -u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); IoctlFlushL2 params{}; std::memcpy(¶ms, input.data(), input.size()); // TODO : To implement properly std::memcpy(output.data(), ¶ms, output.size()); - return 0; + return NvResult::Success; } -u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlGetGpuTime params{}; std::memcpy(¶ms, input.data(), input.size()); params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count()); std::memcpy(output.data(), ¶ms, output.size()); - return 0; + return NvResult::Success; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index ef60f72ce..137b88238 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -16,32 +16,13 @@ public: explicit nvhost_ctrl_gpu(Core::System& system); ~nvhost_ctrl_gpu() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) override; + NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) override; + NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) override; private: - enum class IoctlCommand : u32_le { - IocGetCharacteristicsCommand = 0xC0B04705, - IocGetTPCMasksCommand = 0xC0184706, - IocGetActiveSlotMaskCommand = 0x80084714, - IocZcullGetCtxSizeCommand = 0x80044701, - IocZcullGetInfo = 0x80284702, - IocZbcSetTable = 0x402C4703, - IocZbcQueryTable = 0xC0344704, - IocFlushL2 = 0x40084707, - IocInvalICache = 0x4008470D, - IocSetMmudebugMode = 0x4008470E, - IocSetSmDebugMode = 0x4010470F, - IocWaitForPause = 0xC0084710, - IocGetTcpExceptionEnStatus = 0x80084711, - IocNumVsms = 0x80084712, - IocVsmsMapping = 0xC0044713, - IocGetErrorChannelUserData = 0xC008471B, - IocGetGpuTime = 0xC010471C, - IocGetCpuTimeCorrelationInfo = 0xC108471D, - }; - struct IoctlGpuCharacteristics { u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) u32_le impl; // 0xB (NVGPU_GPU_IMPL_GM20B) @@ -159,17 +140,21 @@ private: }; static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); - u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, - std::vector<u8>& output2, IoctlVersion version); - u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, std::vector<u8>& output2, - IoctlVersion version); - u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); - u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); - u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); - u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); - u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); - u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output); - u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); + NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); + NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output); + + NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); + NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output); + + NvResult GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); + NvResult ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); + NvResult ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); + NvResult ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); + NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); + NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output); + NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 152019548..af8b3d9f1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -23,107 +23,132 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, nvhost_gpu::~nvhost_gpu() = default; -u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) { - LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", - command.raw, input.size(), output.size()); - - switch (static_cast<IoctlCommand>(command.raw)) { - case IoctlCommand::IocSetNVMAPfdCommand: - return SetNVMAPfd(input, output); - case IoctlCommand::IocSetClientDataCommand: - return SetClientData(input, output); - case IoctlCommand::IocGetClientDataCommand: - return GetClientData(input, output); - case IoctlCommand::IocZCullBind: - return ZCullBind(input, output); - case IoctlCommand::IocSetErrorNotifierCommand: - return SetErrorNotifier(input, output); - case IoctlCommand::IocChannelSetPriorityCommand: - return SetChannelPriority(input, output); - case IoctlCommand::IocAllocGPFIFOEx2Command: - return AllocGPFIFOEx2(input, output); - case IoctlCommand::IocAllocObjCtxCommand: - return AllocateObjectContext(input, output); - case IoctlCommand::IocChannelGetWaitbaseCommand: - return GetWaitbase(input, output); - case IoctlCommand::IocChannelSetTimeoutCommand: - return ChannelSetTimeout(input, output); - case IoctlCommand::IocChannelSetTimeslice: - return ChannelSetTimeslice(input, output); - default: +NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + switch (command.group) { + case 0x0: + switch (command.cmd) { + case 0x3: + return GetWaitbase(input, output); + default: + break; + } + break; + case 'H': + switch (command.cmd) { + case 0x1: + return SetNVMAPfd(input, output); + case 0x3: + return ChannelSetTimeout(input, output); + case 0x8: + return SubmitGPFIFOBase(input, output, false); + case 0x9: + return AllocateObjectContext(input, output); + case 0xb: + return ZCullBind(input, output); + case 0xc: + return SetErrorNotifier(input, output); + case 0xd: + return SetChannelPriority(input, output); + case 0x1a: + return AllocGPFIFOEx2(input, output); + case 0x1b: + return SubmitGPFIFOBase(input, output, true); + case 0x1d: + return ChannelSetTimeslice(input, output); + default: + break; + } + break; + case 'G': + switch (command.cmd) { + case 0x14: + return SetClientData(input, output); + case 0x15: + return GetClientData(input, output); + default: + break; + } break; } + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +}; - if (command.group == NVGPU_IOCTL_MAGIC) { - if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) { - return SubmitGPFIFO(input, output); - } - if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) { - return KickoffPB(input, output, input2, version); +NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) { + switch (command.group) { + case 'H': + switch (command.cmd) { + case 0x1b: + return SubmitGPFIFOBase(input, inline_input, output); } + break; } + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} - UNIMPLEMENTED_MSG("Unimplemented ioctl"); - return 0; -}; +NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} -u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { IoctlSetNvmapFD params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); nvmap_fd = params.nvmap_fd; - return 0; + return NvResult::Success; } -u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlClientData params{}; std::memcpy(¶ms, input.data(), input.size()); user_data = params.data; - return 0; + return NvResult::Success; } -u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlClientData params{}; std::memcpy(¶ms, input.data(), input.size()); params.data = user_data; std::memcpy(output.data(), ¶ms, output.size()); - return 0; + return NvResult::Success; } -u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) { std::memcpy(&zcull_params, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, zcull_params.mode); std::memcpy(output.data(), &zcull_params, output.size()); - return 0; + return NvResult::Success; } -u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) { IoctlSetErrorNotifier params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, params.size, params.mem); std::memcpy(output.data(), ¶ms, output.size()); - return 0; + return NvResult::Success; } -u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) { std::memcpy(&channel_priority, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); - return 0; + return NvResult::Success; } -u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) { IoctlAllocGpfifoEx2 params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_WARNING(Service_NVDRV, @@ -137,10 +162,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou params.fence_out = channel_fence; std::memcpy(output.data(), ¶ms, output.size()); - return 0; + return NvResult::Success; } -u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) { IoctlAllocObjCtx params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, @@ -148,7 +173,7 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector< params.obj_id = 0x0; std::memcpy(output.data(), ¶ms, output.size()); - return 0; + return NvResult::Success; } static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { @@ -192,8 +217,8 @@ static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence return result; } -u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, - Tegra::CommandList&& entries) { +NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, + Tegra::CommandList&& entries) { LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, params.num_entries, params.flags.raw); @@ -214,7 +239,6 @@ u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& out params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id); } - entries.RefreshIntegrityChecks(gpu); gpu.PushGPUEntries(std::move(entries)); if (params.flags.add_increment.Value()) { @@ -228,69 +252,70 @@ u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& out } std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); - return 0; + return NvResult::Success; } -u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output, + bool kickoff) { if (input.size() < sizeof(IoctlSubmitGpfifo)) { UNIMPLEMENTED(); + return NvResult::InvalidSize; } IoctlSubmitGpfifo params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); - Tegra::CommandList entries(params.num_entries); - std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)], - params.num_entries * sizeof(Tegra::CommandListHeader)); + + if (kickoff) { + system.Memory().ReadBlock(params.address, entries.command_lists.data(), + params.num_entries * sizeof(Tegra::CommandListHeader)); + } else { + std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)], + params.num_entries * sizeof(Tegra::CommandListHeader)); + } return SubmitGPFIFOImpl(params, output, std::move(entries)); } -u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, - const std::vector<u8>& input2, IoctlVersion version) { +NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, + const std::vector<u8>& input_inline, + std::vector<u8>& output) { if (input.size() < sizeof(IoctlSubmitGpfifo)) { UNIMPLEMENTED(); + return NvResult::InvalidSize; } IoctlSubmitGpfifo params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); - Tegra::CommandList entries(params.num_entries); - if (version == IoctlVersion::Version2) { - std::memcpy(entries.command_lists.data(), input2.data(), - params.num_entries * sizeof(Tegra::CommandListHeader)); - } else { - system.Memory().ReadBlock(params.address, entries.command_lists.data(), - params.num_entries * sizeof(Tegra::CommandListHeader)); - } - + std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size()); return SubmitGPFIFOImpl(params, output, std::move(entries)); } -u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { IoctlGetWaitbase params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); params.value = 0; // Seems to be hard coded at 0 std::memcpy(output.data(), ¶ms, output.size()); - return 0; + return NvResult::Success; } -u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) { IoctlChannelSetTimeout params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlChannelSetTimeout)); LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); - return 0; + return NvResult::Success; } -u32 nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) { IoctlSetTimeslice params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSetTimeslice)); LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); channel_timeslice = params.timeslice; - return 0; + return NvResult::Success; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index a252fc06d..e0298b4fe 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -20,43 +20,19 @@ class SyncpointManager; namespace Service::Nvidia::Devices { class nvmap; -constexpr u32 NVGPU_IOCTL_MAGIC('H'); -constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8); -constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b); - class nvhost_gpu final : public nvdevice { public: explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, SyncpointManager& syncpoint_manager); ~nvhost_gpu() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) override; + NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) override; + NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) override; private: - enum class IoctlCommand : u32_le { - IocSetNVMAPfdCommand = 0x40044801, - IocAllocGPFIFOCommand = 0x40084805, - IocSetClientDataCommand = 0x40084714, - IocGetClientDataCommand = 0x80084715, - IocZCullBind = 0xc010480b, - IocSetErrorNotifierCommand = 0xC018480C, - IocChannelSetPriorityCommand = 0x4004480D, - IocEnableCommand = 0x0000480E, - IocDisableCommand = 0x0000480F, - IocPreemptCommand = 0x00004810, - IocForceResetCommand = 0x00004811, - IocEventIdControlCommand = 0x40084812, - IocGetErrorNotificationCommand = 0xC0104817, - IocAllocGPFIFOExCommand = 0x40204818, - IocAllocGPFIFOEx2Command = 0xC020481A, - IocAllocObjCtxCommand = 0xC0104809, - IocChannelGetWaitbaseCommand = 0xC0080003, - IocChannelSetTimeoutCommand = 0x40044803, - IocChannelSetTimeslice = 0xC004481D, - }; - enum class CtxObjects : u32_le { Ctx2D = 0x902D, Ctx3D = 0xB197, @@ -67,63 +43,63 @@ private: }; struct IoctlSetNvmapFD { - u32_le nvmap_fd; + s32_le nvmap_fd{}; }; static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); struct IoctlChannelSetTimeout { - u32_le timeout; + u32_le timeout{}; }; static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size"); struct IoctlAllocGPFIFO { - u32_le num_entries; - u32_le flags; + u32_le num_entries{}; + u32_le flags{}; }; static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size"); struct IoctlClientData { - u64_le data; + u64_le data{}; }; static_assert(sizeof(IoctlClientData) == 8, "IoctlClientData is incorrect size"); struct IoctlZCullBind { - u64_le gpu_va; - u32_le mode; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf + u64_le gpu_va{}; + u32_le mode{}; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf INSERT_PADDING_WORDS(1); }; static_assert(sizeof(IoctlZCullBind) == 16, "IoctlZCullBind is incorrect size"); struct IoctlSetErrorNotifier { - u64_le offset; - u64_le size; - u32_le mem; // nvmap object handle + u64_le offset{}; + u64_le size{}; + u32_le mem{}; // nvmap object handle INSERT_PADDING_WORDS(1); }; static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size"); struct IoctlChannelSetPriority { - u32_le priority; + u32_le priority{}; }; static_assert(sizeof(IoctlChannelSetPriority) == 4, "IoctlChannelSetPriority is incorrect size"); struct IoctlSetTimeslice { - u32_le timeslice; + u32_le timeslice{}; }; static_assert(sizeof(IoctlSetTimeslice) == 4, "IoctlSetTimeslice is incorrect size"); struct IoctlEventIdControl { - u32_le cmd; // 0=disable, 1=enable, 2=clear - u32_le id; + u32_le cmd{}; // 0=disable, 1=enable, 2=clear + u32_le id{}; }; static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size"); struct IoctlGetErrorNotification { - u64_le timestamp; - u32_le info32; - u16_le info16; - u16_le status; // always 0xFFFF + u64_le timestamp{}; + u32_le info32{}; + u16_le info16{}; + u16_le status{}; // always 0xFFFF }; static_assert(sizeof(IoctlGetErrorNotification) == 16, "IoctlGetErrorNotification is incorrect size"); @@ -131,39 +107,39 @@ private: static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); struct IoctlAllocGpfifoEx { - u32_le num_entries; - u32_le flags; - u32_le unk0; - u32_le unk1; - u32_le unk2; - u32_le unk3; - u32_le unk4; - u32_le unk5; + u32_le num_entries{}; + u32_le flags{}; + u32_le unk0{}; + u32_le unk1{}; + u32_le unk2{}; + u32_le unk3{}; + u32_le unk4{}; + u32_le unk5{}; }; static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size"); struct IoctlAllocGpfifoEx2 { - u32_le num_entries; // in - u32_le flags; // in - u32_le unk0; // in (1 works) - Fence fence_out; // out - u32_le unk1; // in - u32_le unk2; // in - u32_le unk3; // in + u32_le num_entries{}; // in + u32_le flags{}; // in + u32_le unk0{}; // in (1 works) + Fence fence_out{}; // out + u32_le unk1{}; // in + u32_le unk2{}; // in + u32_le unk3{}; // in }; static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size"); struct IoctlAllocObjCtx { - u32_le class_num; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA, - // 0xB06F=channel_gpfifo - u32_le flags; - u64_le obj_id; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported + u32_le class_num{}; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA, + // 0xB06F=channel_gpfifo + u32_le flags{}; + u64_le obj_id{}; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported }; static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size"); struct IoctlSubmitGpfifo { - u64_le address; // pointer to gpfifo entry structs - u32_le num_entries; // number of fence objects being submitted + u64_le address{}; // pointer to gpfifo entry structs + u32_le num_entries{}; // number of fence objects being submitted union { u32_le raw; BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list @@ -172,7 +148,7 @@ private: BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt BitField<8, 1, u32_le> increment; // increment the returned fence } flags; - Fence fence_out; // returned new fence object for others to wait on + Fence fence_out{}; // returned new fence object for others to wait on u32 AddIncrementValue() const { return flags.add_increment.Value() << 1; @@ -182,33 +158,34 @@ private: "IoctlSubmitGpfifo is incorrect size"); struct IoctlGetWaitbase { - u32 unknown; // seems to be ignored? Nintendo added this - u32 value; + u32 unknown{}; // seems to be ignored? Nintendo added this + u32 value{}; }; static_assert(sizeof(IoctlGetWaitbase) == 8, "IoctlGetWaitbase is incorrect size"); - u32_le nvmap_fd{}; + s32_le nvmap_fd{}; u64_le user_data{}; IoctlZCullBind zcull_params{}; u32_le channel_priority{}; u32_le channel_timeslice{}; - u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); - u32 SetClientData(const std::vector<u8>& input, std::vector<u8>& output); - u32 GetClientData(const std::vector<u8>& input, std::vector<u8>& output); - u32 ZCullBind(const std::vector<u8>& input, std::vector<u8>& output); - u32 SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output); - u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output); - u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); - u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); - u32 SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, - Tegra::CommandList&& entries); - u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); - u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, - const std::vector<u8>& input2, IoctlVersion version); - u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); - u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); - u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); + NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); + NvResult SetClientData(const std::vector<u8>& input, std::vector<u8>& output); + NvResult GetClientData(const std::vector<u8>& input, std::vector<u8>& output); + NvResult ZCullBind(const std::vector<u8>& input, std::vector<u8>& output); + NvResult SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output); + NvResult SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output); + NvResult AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); + NvResult AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); + NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, + Tegra::CommandList&& entries); + NvResult SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output, + bool kickoff = false); + NvResult SubmitGPFIFOBase(const std::vector<u8>& input, const std::vector<u8>& input_inline, + std::vector<u8>& output); + NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); + NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); + NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); std::shared_ptr<nvmap> nvmap_dev; SyncpointManager& syncpoint_manager; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index b6df48360..d8735491c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -15,46 +15,58 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_de : nvhost_nvdec_common(system, std::move(nvmap_dev)) {} nvhost_nvdec::~nvhost_nvdec() = default; -u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) { - LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", - command.raw, input.size(), output.size()); - - switch (static_cast<IoctlCommand>(command.raw)) { - case IoctlCommand::IocSetNVMAPfdCommand: - return SetNVMAPfd(input); - case IoctlCommand::IocSubmit: - return Submit(input, output); - case IoctlCommand::IocGetSyncpoint: - return GetSyncpoint(input, output); - case IoctlCommand::IocGetWaitbase: - return GetWaitbase(input, output); - case IoctlCommand::IocMapBuffer: - case IoctlCommand::IocMapBuffer2: - case IoctlCommand::IocMapBuffer3: - case IoctlCommand::IocMapBufferEx: - return MapBuffer(input, output); - case IoctlCommand::IocUnmapBufferEx: { - // This command is sent when the video stream has ended, flush all video contexts - // This is usually sent in the folowing order: vic, nvdec, vic. - // Inform the GPU to clear any remaining nvdec buffers when this is detected. - LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); - Tegra::ChCommandHeaderList cmdlist(1); - cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F}; - system.GPU().PushCommandBuffer(cmdlist); - [[fallthrough]]; // fallthrough to unmap buffers - }; - case IoctlCommand::IocUnmapBuffer: - case IoctlCommand::IocUnmapBuffer2: - case IoctlCommand::IocUnmapBuffer3: - return UnmapBuffer(input, output); - case IoctlCommand::IocSetSubmitTimeout: - return SetSubmitTimeout(input, output); +NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, + std::vector<u8>& output) { + switch (command.group) { + case 0x0: + switch (command.cmd) { + case 0x1: + return Submit(input, output); + case 0x2: + return GetSyncpoint(input, output); + case 0x3: + return GetWaitbase(input, output); + case 0x7: + return SetSubmitTimeout(input, output); + case 0x9: + return MapBuffer(input, output); + case 0xa: { + if (command.length == 0x1c) { + LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); + Tegra::ChCommandHeaderList cmdlist(1); + cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F}; + system.GPU().PushCommandBuffer(cmdlist); + } + return UnmapBuffer(input, output); + } + default: + break; + } + break; + case 'H': + switch (command.cmd) { + case 0x1: + return SetNVMAPfd(input); + default: + break; + } + break; } - UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw); - return 0; + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 102777ddd..79b8b6de1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -14,26 +14,11 @@ public: explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvhost_nvdec() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) override; - -private: - enum class IoctlCommand : u32_le { - IocSetNVMAPfdCommand = 0x40044801, - IocSubmit = 0xC0400001, - IocGetSyncpoint = 0xC0080002, - IocGetWaitbase = 0xC0080003, - IocMapBuffer = 0xC01C0009, - IocMapBuffer2 = 0xC16C0009, - IocMapBuffer3 = 0xC15C0009, - IocMapBufferEx = 0xC0A40009, - IocUnmapBuffer = 0xC0A4000A, - IocUnmapBuffer2 = 0xC16C000A, - IocUnmapBufferEx = 0xC01C000A, - IocUnmapBuffer3 = 0xC15C000A, - IocSetSubmitTimeout = 0x40040007, - }; + NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) override; + NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) override; }; } // namespace Service::Nvidia::Devices 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 30f03f845..b49cecb42 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -36,26 +36,20 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s } } // Anonymous namespace -namespace NvErrCodes { -constexpr u32 Success{}; -[[maybe_unused]] constexpr u32 OutOfMemory{static_cast<u32>(-12)}; -constexpr u32 InvalidInput{static_cast<u32>(-22)}; -} // namespace NvErrCodes - nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} nvhost_nvdec_common::~nvhost_nvdec_common() = default; -u32 nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { +NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { IoctlSetNvmapFD params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSetNvmapFD)); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); nvmap_fd = params.nvmap_fd; - return 0; + return NvResult::Success; } -u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) { IoctlSubmit params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit)); LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); @@ -83,12 +77,12 @@ u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& o for (const auto& cmd_buffer : command_buffers) { auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); - ASSERT_OR_EXECUTE(object, return NvErrCodes::InvalidInput;); + ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); const auto map = FindBufferMap(object->dma_map_addr); if (!map) { LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}", object->addr, object->dma_map_addr); - return 0; + return NvResult::Success; } Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(), @@ -105,10 +99,10 @@ u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& o offset = WriteVectors(output, syncpt_increments, offset); offset = WriteVectors(output, wait_checks, offset); - return NvErrCodes::Success; + return NvResult::Success; } -u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { IoctlGetSyncpoint params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); @@ -118,18 +112,18 @@ u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector< params.value = 0; std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); - return NvErrCodes::Success; + return NvResult::Success; } -u32 nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { IoctlGetWaitbase params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); params.value = 0; // Seems to be hard coded at 0 std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); - return 0; + return NvResult::Success; } -u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { IoctlMapBuffer params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); @@ -143,7 +137,7 @@ u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8> if (!object) { LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle); std::memcpy(output.data(), ¶ms, output.size()); - return NvErrCodes::InvalidInput; + return NvResult::InvalidState; } if (object->dma_map_addr == 0) { // NVDEC and VIC memory is in the 32-bit address space @@ -165,10 +159,10 @@ u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8> std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(), cmd_buffer_handles.size() * sizeof(MapBufferEntry)); - return NvErrCodes::Success; + return NvResult::Success; } -u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { IoctlMapBuffer params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); @@ -181,7 +175,7 @@ u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u if (!object) { LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle); std::memcpy(output.data(), ¶ms, output.size()); - return NvErrCodes::InvalidInput; + return NvResult::InvalidState; } if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) { gpu.MemoryManager().Unmap(object->dma_map_addr, *size); @@ -193,13 +187,14 @@ u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u object->dma_map_addr = 0; } std::memset(output.data(), 0, output.size()); - return NvErrCodes::Success; + return NvResult::Success; } -u32 nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, + std::vector<u8>& output) { std::memcpy(&submit_timeout, input.data(), input.size()); LOG_WARNING(Service_NVDRV, "(STUBBED) called"); - return NvErrCodes::Success; + return NvResult::Success; } std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap( diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index c249c5349..86ba3a4d1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -18,9 +18,37 @@ public: explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvhost_nvdec_common() override; - virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) = 0; + /** + * Handles an ioctl1 request. + * @param command The ioctl command id. + * @param input A buffer containing the input data for the ioctl. + * @param output A buffer where the output data will be written to. + * @returns The result code of the ioctl. + */ + virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, + std::vector<u8>& output) = 0; + + /** + * Handles an ioctl2 request. + * @param command The ioctl command id. + * @param input A buffer containing the input data for the ioctl. + * @param inline_input A buffer containing the input data for the ioctl which has been inlined. + * @param output A buffer where the output data will be written to. + * @returns The result code of the ioctl. + */ + virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) = 0; + + /** + * Handles an ioctl3 request. + * @param command The ioctl command id. + * @param input A buffer containing the input data for the ioctl. + * @param output A buffer where the output data will be written to. + * @param inline_output A buffer where the inlined output data will be written to. + * @returns The result code of the ioctl. + */ + virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) = 0; protected: class BufferMap final { @@ -63,102 +91,102 @@ protected: }; struct IoctlSetNvmapFD { - u32_le nvmap_fd; + s32_le nvmap_fd{}; }; static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); struct IoctlSubmitCommandBuffer { - u32_le id; - u32_le offset; - u32_le count; + u32_le id{}; + u32_le offset{}; + u32_le count{}; }; static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC, "IoctlSubmitCommandBuffer is incorrect size"); struct IoctlSubmit { - u32_le cmd_buffer_count; - u32_le relocation_count; - u32_le syncpoint_count; - u32_le fence_count; + u32_le cmd_buffer_count{}; + u32_le relocation_count{}; + u32_le syncpoint_count{}; + u32_le fence_count{}; }; static_assert(sizeof(IoctlSubmit) == 0x10, "IoctlSubmit has incorrect size"); struct CommandBuffer { - s32 memory_id; - u32 offset; - s32 word_count; + s32 memory_id{}; + u32 offset{}; + s32 word_count{}; }; static_assert(sizeof(CommandBuffer) == 0xC, "CommandBuffer has incorrect size"); struct Reloc { - s32 cmdbuffer_memory; - s32 cmdbuffer_offset; - s32 target; - s32 target_offset; + s32 cmdbuffer_memory{}; + s32 cmdbuffer_offset{}; + s32 target{}; + s32 target_offset{}; }; static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size"); struct SyncptIncr { - u32 id; - u32 increments; + u32 id{}; + u32 increments{}; }; static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size"); struct Fence { - u32 id; - u32 value; + u32 id{}; + u32 value{}; }; static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size"); struct IoctlGetSyncpoint { // Input - u32_le param; + u32_le param{}; // Output - u32_le value; + u32_le value{}; }; static_assert(sizeof(IoctlGetSyncpoint) == 8, "IocGetIdParams has wrong size"); struct IoctlGetWaitbase { - u32_le unknown; // seems to be ignored? Nintendo added this - u32_le value; + u32_le unknown{}; // seems to be ignored? Nintendo added this + u32_le value{}; }; static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size"); struct IoctlMapBuffer { - u32_le num_entries; - u32_le data_address; // Ignored by the driver. - u32_le attach_host_ch_das; + u32_le num_entries{}; + u32_le data_address{}; // Ignored by the driver. + u32_le attach_host_ch_das{}; }; static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); struct IocGetIdParams { // Input - u32_le param; + u32_le param{}; // Output - u32_le value; + u32_le value{}; }; static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); // Used for mapping and unmapping command buffers struct MapBufferEntry { - u32_le map_handle; - u32_le map_address; + u32_le map_handle{}; + u32_le map_address{}; }; static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); /// Ioctl command implementations - u32 SetNVMAPfd(const std::vector<u8>& input); - u32 Submit(const std::vector<u8>& input, std::vector<u8>& output); - u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); - u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); - u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); - u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); - u32 SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); + NvResult SetNVMAPfd(const std::vector<u8>& input); + NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output); + NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); + NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); + NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); + NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); + NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr); - u32_le nvmap_fd{}; + s32_le nvmap_fd{}; u32_le submit_timeout{}; std::shared_ptr<nvmap> nvmap_dev; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index 96e7b7dab..2d06955c0 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp @@ -13,28 +13,44 @@ namespace Service::Nvidia::Devices { nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} nvhost_nvjpg::~nvhost_nvjpg() = default; -u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) { - LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", - command.raw, input.size(), output.size()); - - switch (static_cast<IoctlCommand>(command.raw)) { - case IoctlCommand::IocSetNVMAPfdCommand: - return SetNVMAPfd(input, output); +NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, + std::vector<u8>& output) { + switch (command.group) { + case 'H': + switch (command.cmd) { + case 0x1: + return SetNVMAPfd(input, output); + default: + break; + } + break; + default: + break; } - UNIMPLEMENTED_MSG("Unimplemented ioctl"); - return 0; + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; } -u32 nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { IoctlSetNvmapFD params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); nvmap_fd = params.nvmap_fd; - return 0; + return NvResult::Success; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 98dcac52f..43948d18d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h @@ -16,23 +16,21 @@ public: explicit nvhost_nvjpg(Core::System& system); ~nvhost_nvjpg() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) override; + NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) override; + NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) override; private: - enum class IoctlCommand : u32_le { - IocSetNVMAPfdCommand = 0x40044801, - }; - struct IoctlSetNvmapFD { - u32_le nvmap_fd; + s32_le nvmap_fd{}; }; static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); - u32_le nvmap_fd{}; + s32_le nvmap_fd{}; - u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); + NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 60db54d00..805fe86ae 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -15,36 +15,50 @@ nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) nvhost_vic::~nvhost_vic() = default; -u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) { - LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", - command.raw, input.size(), output.size()); - - switch (static_cast<IoctlCommand>(command.raw)) { - case IoctlCommand::IocSetNVMAPfdCommand: - return SetNVMAPfd(input); - case IoctlCommand::IocSubmit: - return Submit(input, output); - case IoctlCommand::IocGetSyncpoint: - return GetSyncpoint(input, output); - case IoctlCommand::IocGetWaitbase: - return GetWaitbase(input, output); - case IoctlCommand::IocMapBuffer: - case IoctlCommand::IocMapBuffer2: - case IoctlCommand::IocMapBuffer3: - case IoctlCommand::IocMapBuffer4: - case IoctlCommand::IocMapBufferEx: - return MapBuffer(input, output); - case IoctlCommand::IocUnmapBuffer: - case IoctlCommand::IocUnmapBuffer2: - case IoctlCommand::IocUnmapBuffer3: - case IoctlCommand::IocUnmapBufferEx: - return UnmapBuffer(input, output); +NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + switch (command.group) { + case 0x0: + switch (command.cmd) { + case 0x1: + return Submit(input, output); + case 0x2: + return GetSyncpoint(input, output); + case 0x3: + return GetWaitbase(input, output); + case 0x9: + return MapBuffer(input, output); + case 0xa: + return UnmapBuffer(input, output); + default: + break; + } + break; + case 'H': + switch (command.cmd) { + case 0x1: + return SetNVMAPfd(input); + default: + break; + } + break; + default: + break; } - UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw); - return 0; + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index f975b190c..b2e11f4d4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -13,25 +13,11 @@ class nvhost_vic final : public nvhost_nvdec_common { public: explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvhost_vic(); - u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) override; -private: - enum class IoctlCommand : u32_le { - IocSetNVMAPfdCommand = 0x40044801, - IocSubmit = 0xC0400001, - IocGetSyncpoint = 0xC0080002, - IocGetWaitbase = 0xC0080003, - IocMapBuffer = 0xC01C0009, - IocMapBuffer2 = 0xC0340009, - IocMapBuffer3 = 0xC0140009, - IocMapBuffer4 = 0xC00C0009, - IocMapBufferEx = 0xC03C0009, - IocUnmapBuffer = 0xC03C000A, - IocUnmapBuffer2 = 0xC034000A, - IocUnmapBuffer3 = 0xC00C000A, - IocUnmapBufferEx = 0xC01C000A, - }; + NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) override; + NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) override; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 9436e16ad..4015a2740 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -11,13 +11,6 @@ namespace Service::Nvidia::Devices { -namespace NvErrCodes { -enum { - OperationNotPermitted = -1, - InvalidValue = -22, -}; -} - nvmap::nvmap(Core::System& system) : nvdevice(system) { // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to // represent this. @@ -26,6 +19,46 @@ nvmap::nvmap(Core::System& system) : nvdevice(system) { nvmap::~nvmap() = default; +NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + switch (command.group) { + case 0x1: + switch (command.cmd) { + case 0x1: + return IocCreate(input, output); + case 0x3: + return IocFromId(input, output); + case 0x4: + return IocAlloc(input, output); + case 0x5: + return IocFree(input, output); + case 0x9: + return IocParam(input, output); + case 0xe: + return IocGetId(input, output); + default: + break; + } + break; + default: + break; + } + + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + +NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) { + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + return NvResult::NotImplemented; +} + VAddr nvmap::GetObjectAddress(u32 handle) const { auto object = GetObject(handle); ASSERT(object); @@ -33,28 +66,6 @@ VAddr nvmap::GetObjectAddress(u32 handle) const { return object->addr; } -u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) { - switch (static_cast<IoctlCommand>(command.raw)) { - case IoctlCommand::Create: - return IocCreate(input, output); - case IoctlCommand::Alloc: - return IocAlloc(input, output); - case IoctlCommand::GetId: - return IocGetId(input, output); - case IoctlCommand::FromId: - return IocFromId(input, output); - case IoctlCommand::Param: - return IocParam(input, output); - case IoctlCommand::Free: - return IocFree(input, output); - } - - UNIMPLEMENTED_MSG("Unimplemented ioctl"); - return 0; -} - u32 nvmap::CreateObject(u32 size) { // Create a new nvmap object and obtain a handle to it. auto object = std::make_shared<Object>(); @@ -70,35 +81,35 @@ u32 nvmap::CreateObject(u32 size) { return handle; } -u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { IocCreateParams params; std::memcpy(¶ms, input.data(), sizeof(params)); LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); if (!params.size) { LOG_ERROR(Service_NVDRV, "Size is 0"); - return static_cast<u32>(NvErrCodes::InvalidValue); + return NvResult::BadValue; } params.handle = CreateObject(params.size); std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; + return NvResult::Success; } -u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { IocAllocParams params; std::memcpy(¶ms, input.data(), sizeof(params)); LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); if (!params.handle) { LOG_ERROR(Service_NVDRV, "Handle is 0"); - return static_cast<u32>(NvErrCodes::InvalidValue); + return NvResult::BadValue; } if ((params.align - 1) & params.align) { LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); - return static_cast<u32>(NvErrCodes::InvalidValue); + return NvResult::BadValue; } const u32 min_alignment = 0x1000; @@ -109,12 +120,12 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { auto object = GetObject(params.handle); if (!object) { LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return static_cast<u32>(NvErrCodes::InvalidValue); + return NvResult::BadValue; } if (object->status == Object::Status::Allocated) { LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); - return static_cast<u32>(NvErrCodes::OperationNotPermitted); + return NvResult::InsufficientMemory; } object->flags = params.flags; @@ -124,10 +135,10 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { object->status = Object::Status::Allocated; std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; + return NvResult::Success; } -u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { IocGetIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); @@ -135,22 +146,22 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { if (!params.handle) { LOG_ERROR(Service_NVDRV, "Handle is zero"); - return static_cast<u32>(NvErrCodes::InvalidValue); + return NvResult::BadValue; } auto object = GetObject(params.handle); if (!object) { LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return static_cast<u32>(NvErrCodes::OperationNotPermitted); + return NvResult::BadValue; } params.id = object->id; std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; + return NvResult::Success; } -u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { IocFromIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); @@ -160,13 +171,13 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { [&](const auto& entry) { return entry.second->id == params.id; }); if (itr == handles.end()) { LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return static_cast<u32>(NvErrCodes::InvalidValue); + return NvResult::BadValue; } auto& object = itr->second; if (object->status != Object::Status::Allocated) { LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); - return static_cast<u32>(NvErrCodes::InvalidValue); + return NvResult::BadValue; } itr->second->refcount++; @@ -175,10 +186,10 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { params.handle = itr->first; std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; + return NvResult::Success; } -u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; IocParamParams params; @@ -189,12 +200,12 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { auto object = GetObject(params.handle); if (!object) { LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return static_cast<u32>(NvErrCodes::InvalidValue); + return NvResult::BadValue; } if (object->status != Object::Status::Allocated) { LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); - return static_cast<u32>(NvErrCodes::OperationNotPermitted); + return NvResult::BadValue; } switch (static_cast<ParamTypes>(params.param)) { @@ -216,10 +227,10 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { } std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; + return NvResult::Success; } -u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { +NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { // TODO(Subv): These flags are unconfirmed. enum FreeFlags { Freed = 0, @@ -234,14 +245,14 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { auto itr = handles.find(params.handle); if (itr == handles.end()) { LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return static_cast<u32>(NvErrCodes::InvalidValue); + return NvResult::BadValue; } if (!itr->second->refcount) { LOG_ERROR( Service_NVDRV, "There is no references to this object. The object is already freed. handle={:08X}", params.handle); - return static_cast<u32>(NvErrCodes::InvalidValue); + return NvResult::BadValue; } itr->second->refcount--; @@ -261,7 +272,7 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { handles.erase(params.handle); std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; + return NvResult::Success; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 04b9ef540..4484bd79f 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -19,13 +19,15 @@ public: explicit nvmap(Core::System& system); ~nvmap() override; + NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) override; + NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& inline_output) override; + /// Returns the allocated address of an nvmap object given its handle. VAddr GetObjectAddress(u32 handle) const; - u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) override; - /// Represents an nvmap object. struct Object { enum class Status { Created, Allocated }; @@ -58,76 +60,68 @@ private: /// Mapping of currently allocated handles to the objects they represent. std::unordered_map<u32, std::shared_ptr<Object>> handles; - enum class IoctlCommand : u32 { - Create = 0xC0080101, - FromId = 0xC0080103, - Alloc = 0xC0200104, - Free = 0xC0180105, - Param = 0xC00C0109, - GetId = 0xC008010E, - }; struct IocCreateParams { // Input - u32_le size; + u32_le size{}; // Output - u32_le handle; + u32_le handle{}; }; static_assert(sizeof(IocCreateParams) == 8, "IocCreateParams has wrong size"); struct IocFromIdParams { // Input - u32_le id; + u32_le id{}; // Output - u32_le handle; + u32_le handle{}; }; static_assert(sizeof(IocFromIdParams) == 8, "IocFromIdParams has wrong size"); struct IocAllocParams { // Input - u32_le handle; - u32_le heap_mask; - u32_le flags; - u32_le align; - u8 kind; + u32_le handle{}; + u32_le heap_mask{}; + u32_le flags{}; + u32_le align{}; + u8 kind{}; INSERT_PADDING_BYTES(7); - u64_le addr; + u64_le addr{}; }; static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size"); struct IocFreeParams { - u32_le handle; + u32_le handle{}; INSERT_PADDING_BYTES(4); - u64_le address; - u32_le size; - u32_le flags; + u64_le address{}; + u32_le size{}; + u32_le flags{}; }; static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size"); struct IocParamParams { // Input - u32_le handle; - u32_le param; + u32_le handle{}; + u32_le param{}; // Output - u32_le result; + u32_le result{}; }; static_assert(sizeof(IocParamParams) == 12, "IocParamParams has wrong size"); struct IocGetIdParams { // Output - u32_le id; + u32_le id{}; // Input - u32_le handle; + u32_le handle{}; }; static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); u32 CreateObject(u32 size); - u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output); - u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); - u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output); - u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output); - u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output); - u32 IocFree(const std::vector<u8>& input, std::vector<u8>& output); + NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output); + NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); + NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output); + NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output); + NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output); + NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output); }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index 88fbfa9b0..f6c38e853 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -23,124 +23,170 @@ void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { void NVDRV::Open(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NVDRV, "called"); + if (!is_initialized) { + ServiceError(ctx, NvResult::NotInitialized); + LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + return; + } + const auto& buffer = ctx.ReadBuffer(); - std::string device_name(buffer.begin(), buffer.end()); + const std::string device_name(buffer.begin(), buffer.end()); + DeviceFD fd = nvdrv->Open(device_name); - u32 fd = nvdrv->Open(device_name); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(fd); - rb.Push<u32>(0); + rb.Push<DeviceFD>(fd); + rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed); +} + +void NVDRV::ServiceError(Kernel::HLERequestContext& ctx, NvResult result) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum(result); } -void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) { +void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - u32 fd = rp.Pop<u32>(); - u32 command = rp.Pop<u32>(); - - /// Ioctl 3 has 2 outputs, first in the input params, second is the result - std::vector<u8> output(ctx.GetWriteBufferSize(0)); - std::vector<u8> output2; - if (version == IoctlVersion::Version3) { - output2.resize((ctx.GetWriteBufferSize(1))); + const auto fd = rp.Pop<DeviceFD>(); + const auto command = rp.PopRaw<Ioctl>(); + LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw); + + if (!is_initialized) { + ServiceError(ctx, NvResult::NotInitialized); + LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + return; } - /// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer. - /// KickOfPB uses this - auto input = ctx.ReadBuffer(0); + // Check device + std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); + const auto input_buffer = ctx.ReadBuffer(0); - std::vector<u8> input2; - if (version == IoctlVersion::Version2) { - input2 = ctx.ReadBuffer(1); - } + const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer); - IoctlCtrl ctrl{}; - - u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version); - - if (ctrl.must_delay) { - ctrl.fresh_call = false; - ctx.SleepClientThread( - "NVServices::DelayedResponse", ctrl.timeout, - [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_, - Kernel::ThreadWakeupReason reason) { - IoctlCtrl ctrl2{ctrl}; - std::vector<u8> tmp_output = output; - std::vector<u8> tmp_output2 = output2; - const u32 ioctl_result = nvdrv->Ioctl(fd, command, input, input2, tmp_output, - tmp_output2, ctrl2, version); - ctx_.WriteBuffer(tmp_output, 0); - if (version == IoctlVersion::Version3) { - ctx_.WriteBuffer(tmp_output2, 1); - } - IPC::ResponseBuilder rb{ctx_, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push(ioctl_result); - }, - nvdrv->GetEventWriteable(ctrl.event_id)); - } else { - ctx.WriteBuffer(output); - if (version == IoctlVersion::Version3) { - ctx.WriteBuffer(output2, 1); - } + if (command.is_out != 0) { + ctx.WriteBuffer(output_buffer); } + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(result); -} - -void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NVDRV, "called"); - IoctlBase(ctx, IoctlVersion::Version1); + rb.PushEnum(nv_result); } void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NVDRV, "called"); - IoctlBase(ctx, IoctlVersion::Version2); + IPC::RequestParser rp{ctx}; + const auto fd = rp.Pop<DeviceFD>(); + const auto command = rp.PopRaw<Ioctl>(); + LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw); + + if (!is_initialized) { + ServiceError(ctx, NvResult::NotInitialized); + LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + return; + } + + const auto input_buffer = ctx.ReadBuffer(0); + const auto input_inlined_buffer = ctx.ReadBuffer(1); + std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); + + const auto nv_result = + nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer); + + if (command.is_out != 0) { + ctx.WriteBuffer(output_buffer); + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum(nv_result); } void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NVDRV, "called"); - IoctlBase(ctx, IoctlVersion::Version3); + IPC::RequestParser rp{ctx}; + const auto fd = rp.Pop<DeviceFD>(); + const auto command = rp.PopRaw<Ioctl>(); + LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw); + + if (!is_initialized) { + ServiceError(ctx, NvResult::NotInitialized); + LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + return; + } + + const auto input_buffer = ctx.ReadBuffer(0); + std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); + std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1)); + + const auto nv_result = + nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline); + + if (command.is_out != 0) { + ctx.WriteBuffer(output_buffer, 0); + ctx.WriteBuffer(output_buffer_inline, 1); + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum(nv_result); } void NVDRV::Close(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NVDRV, "called"); - IPC::RequestParser rp{ctx}; - u32 fd = rp.Pop<u32>(); + if (!is_initialized) { + ServiceError(ctx, NvResult::NotInitialized); + LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + return; + } - auto result = nvdrv->Close(fd); + IPC::RequestParser rp{ctx}; + const auto fd = rp.Pop<DeviceFD>(); + const auto result = nvdrv->Close(fd); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum(result); } void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + is_initialized = true; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); + rb.PushEnum(NvResult::Success); } void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - u32 fd = rp.Pop<u32>(); - // TODO(Blinkhawk): Figure the meaning of the flag at bit 16 - u32 event_id = rp.Pop<u32>() & 0x000000FF; + const auto fd = rp.Pop<DeviceFD>(); + const auto event_id = rp.Pop<u32>() & 0x00FF; LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); - IPC::ResponseBuilder rb{ctx, 3, 1}; - rb.Push(RESULT_SUCCESS); + if (!is_initialized) { + ServiceError(ctx, NvResult::NotInitialized); + LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + return; + } + + const auto nv_result = nvdrv->VerifyFD(fd); + if (nv_result != NvResult::Success) { + LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd); + ServiceError(ctx, nv_result); + return; + } + if (event_id < MaxNvEvents) { + IPC::ResponseBuilder rb{ctx, 3, 1}; + rb.Push(RESULT_SUCCESS); auto event = nvdrv->GetEvent(event_id); event->Clear(); rb.PushCopyObjects(event); - rb.Push<u32>(NvResult::Success); + rb.PushEnum(NvResult::Success); } else { - rb.Push<u32>(0); - rb.Push<u32>(NvResult::BadParameter); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum(NvResult::BadParameter); } } @@ -151,7 +197,7 @@ void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); + rb.PushEnum(NvResult::Success); } void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) { @@ -164,8 +210,9 @@ void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ct void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); + rb.PushEnum(NvResult::Success); } void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) { @@ -181,7 +228,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) : ServiceFramework(name), nvdrv(std::move(nvdrv)) { static const FunctionInfo functions[] = { {0, &NVDRV::Open, "Open"}, - {1, &NVDRV::Ioctl, "Ioctl"}, + {1, &NVDRV::Ioctl1, "Ioctl"}, {2, &NVDRV::Close, "Close"}, {3, &NVDRV::Initialize, "Initialize"}, {4, &NVDRV::QueryEvent, "QueryEvent"}, diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index 72e17a728..e05f905ae 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h @@ -23,7 +23,7 @@ public: private: void Open(Kernel::HLERequestContext& ctx); - void Ioctl(Kernel::HLERequestContext& ctx); + void Ioctl1(Kernel::HLERequestContext& ctx); void Ioctl2(Kernel::HLERequestContext& ctx); void Ioctl3(Kernel::HLERequestContext& ctx); void Close(Kernel::HLERequestContext& ctx); @@ -33,11 +33,13 @@ private: void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx); void GetStatus(Kernel::HLERequestContext& ctx); void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx); - void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version); + + void ServiceError(Kernel::HLERequestContext& ctx, NvResult result); std::shared_ptr<Module> nvdrv; u64 pid{}; + bool is_initialized{}; }; } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index 529b03471..3294bc0e7 100644 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -1,12 +1,16 @@ #pragma once #include <array> +#include "common/bit_field.h" #include "common/common_types.h" namespace Service::Nvidia { constexpr u32 MaxSyncPoints = 192; constexpr u32 MaxNvEvents = 64; +using DeviceFD = s32; + +constexpr DeviceFD INVALID_NVDRV_FD = -1; struct Fence { s32 id; @@ -20,11 +24,61 @@ struct MultiFence { std::array<Fence, 4> fences; }; -enum NvResult : u32 { - Success = 0, - BadParameter = 4, - Timeout = 5, - ResourceError = 15, +enum class NvResult : u32 { + Success = 0x0, + NotImplemented = 0x1, + NotSupported = 0x2, + NotInitialized = 0x3, + BadParameter = 0x4, + Timeout = 0x5, + InsufficientMemory = 0x6, + ReadOnlyAttribute = 0x7, + InvalidState = 0x8, + InvalidAddress = 0x9, + InvalidSize = 0xA, + BadValue = 0xB, + AlreadyAllocated = 0xD, + Busy = 0xE, + ResourceError = 0xF, + CountMismatch = 0x10, + OverFlow = 0x11, + InsufficientTransferMemory = 0x1000, + InsufficientVideoMemory = 0x10000, + BadSurfaceColorScheme = 0x10001, + InvalidSurface = 0x10002, + SurfaceNotSupported = 0x10003, + DispInitFailed = 0x20000, + DispAlreadyAttached = 0x20001, + DispTooManyDisplays = 0x20002, + DispNoDisplaysAttached = 0x20003, + DispModeNotSupported = 0x20004, + DispNotFound = 0x20005, + DispAttachDissallowed = 0x20006, + DispTypeNotSupported = 0x20007, + DispAuthenticationFailed = 0x20008, + DispNotAttached = 0x20009, + DispSamePwrState = 0x2000A, + DispEdidFailure = 0x2000B, + DispDsiReadAckError = 0x2000C, + DispDsiReadInvalidResp = 0x2000D, + FileWriteFailed = 0x30000, + FileReadFailed = 0x30001, + EndOfFile = 0x30002, + FileOperationFailed = 0x30003, + DirOperationFailed = 0x30004, + EndOfDirList = 0x30005, + ConfigVarNotFound = 0x30006, + InvalidConfigVar = 0x30007, + LibraryNotFound = 0x30008, + SymbolNotFound = 0x30009, + MemoryMapFailed = 0x3000A, + IoctlFailed = 0x3000F, + AccessDenied = 0x30010, + DeviceNotFound = 0x30011, + KernelDriverNotFound = 0x30012, + FileNotFound = 0x30013, + PathAlreadyExists = 0x30014, + ModuleNotPresent = 0xA000E, }; enum class EventState { @@ -34,21 +88,13 @@ enum class EventState { Busy = 3, }; -enum class IoctlVersion : u32 { - Version1, - Version2, - Version3, -}; - -struct IoctlCtrl { - // First call done to the servioce for services that call itself again after a call. - bool fresh_call{true}; - // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep - bool must_delay{}; - // Timeout for the delay - s64 timeout{}; - // NV Event Id - s32 event_id{-1}; +union Ioctl { + u32_le raw; + BitField<0, 8, u32> cmd; + BitField<8, 8, u32> group; + BitField<16, 14, u32> length; + BitField<30, 1, u32> is_in; + BitField<31, 1, u32> is_out; }; } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index a46755cdc..bdbbedd0d 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -5,6 +5,7 @@ #include <utility> #include <fmt/format.h> +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/writable_event.h" @@ -61,36 +62,101 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} { Module::~Module() = default; -u32 Module::Open(const std::string& device_name) { - ASSERT_MSG(devices.find(device_name) != devices.end(), "Trying to open unknown device {}", - device_name); +NvResult Module::VerifyFD(DeviceFD fd) const { + if (fd < 0) { + LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); + return NvResult::InvalidState; + } + + if (open_files.find(fd) == open_files.end()) { + LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); + return NvResult::NotImplemented; + } + + return NvResult::Success; +} + +DeviceFD Module::Open(const std::string& device_name) { + if (devices.find(device_name) == devices.end()) { + LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); + return INVALID_NVDRV_FD; + } auto device = devices[device_name]; - const u32 fd = next_fd++; + const DeviceFD fd = next_fd++; open_files[fd] = std::move(device); return fd; } -u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version) { - auto itr = open_files.find(fd); - ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); +NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, + std::vector<u8>& output) { + if (fd < 0) { + LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); + return NvResult::InvalidState; + } - auto& device = itr->second; - return device->ioctl({command}, input, input2, output, output2, ctrl, version); + const auto itr = open_files.find(fd); + + if (itr == open_files.end()) { + LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); + return NvResult::NotImplemented; + } + + return itr->second->Ioctl1(command, input, output); } -ResultCode Module::Close(u32 fd) { - auto itr = open_files.find(fd); - ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); +NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output) { + if (fd < 0) { + LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); + return NvResult::InvalidState; + } + + const auto itr = open_files.find(fd); + + if (itr == open_files.end()) { + LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); + return NvResult::NotImplemented; + } + + return itr->second->Ioctl2(command, input, inline_input, output); +} + +NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, + std::vector<u8>& output, std::vector<u8>& inline_output) { + if (fd < 0) { + LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); + return NvResult::InvalidState; + } + + const auto itr = open_files.find(fd); + + if (itr == open_files.end()) { + LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); + return NvResult::NotImplemented; + } + + return itr->second->Ioctl3(command, input, output, inline_output); +} + +NvResult Module::Close(DeviceFD fd) { + if (fd < 0) { + LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); + return NvResult::InvalidState; + } + + const auto itr = open_files.find(fd); + + if (itr == open_files.end()) { + LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); + return NvResult::NotImplemented; + } open_files.erase(itr); - // TODO(flerovium): return correct result code if operation failed. - return RESULT_SUCCESS; + return NvResult::Success; } void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index f3d863dac..7654bb026 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -112,14 +112,23 @@ public: return std::static_pointer_cast<T>(itr->second); } + NvResult VerifyFD(DeviceFD fd) const; + /// Opens a device node and returns a file descriptor to it. - u32 Open(const std::string& device_name); + DeviceFD Open(const std::string& device_name); + /// Sends an ioctl command to the specified file descriptor. - u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2, - std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, - IoctlVersion version); + NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, + std::vector<u8>& output); + + NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& inline_input, std::vector<u8>& output); + + NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, + std::vector<u8>& output, std::vector<u8>& inline_output); + /// Closes a device file descriptor and returns operation success. - ResultCode Close(u32 fd); + NvResult Close(DeviceFD fd); void SignalSyncpt(const u32 syncpoint_id, const u32 value); @@ -132,10 +141,10 @@ private: SyncpointManager syncpoint_manager; /// Id to use for the next open file descriptor. - u32 next_fd = 1; + DeviceFD next_fd = 1; /// Mapping of file descriptors to the devices they reference. - std::unordered_map<u32, std::shared_ptr<Devices::nvdevice>> open_files; + std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files; /// Mapping of device node names to their implementation. std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; diff --git a/src/core/hle/service/olsc/olsc.cpp b/src/core/hle/service/olsc/olsc.cpp new file mode 100644 index 000000000..aad4ca706 --- /dev/null +++ b/src/core/hle/service/olsc/olsc.cpp @@ -0,0 +1,69 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/olsc/olsc.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::OLSC { + +class OLSC final : public ServiceFramework<OLSC> { +public: + explicit OLSC() : ServiceFramework{"olsc:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &OLSC::Initialize, "Initialize"}, + {10, nullptr, "VerifySaveDataBackupLicenseAsync"}, + {13, nullptr, "GetSaveDataBackupSetting"}, + {14, &OLSC::SetSaveDataBackupSettingEnabled, "SetSaveDataBackupSettingEnabled"}, + {15, nullptr, "SetCustomData"}, + {16, nullptr, "DeleteSaveDataBackupSetting"}, + {18, nullptr, "GetSaveDataBackupInfoCache"}, + {19, nullptr, "UpdateSaveDataBackupInfoCacheAsync"}, + {22, nullptr, "DeleteSaveDataBackupAsync"}, + {25, nullptr, "ListDownloadableSaveDataBackupInfoAsync"}, + {26, nullptr, "DownloadSaveDataBackupAsync"}, + {9010, nullptr, "VerifySaveDataBackupLicenseAsyncForDebug"}, + {9013, nullptr, "GetSaveDataBackupSettingForDebug"}, + {9014, nullptr, "SetSaveDataBackupSettingEnabledForDebug"}, + {9015, nullptr, "SetCustomDataForDebug"}, + {9016, nullptr, "DeleteSaveDataBackupSettingForDebug"}, + {9018, nullptr, "GetSaveDataBackupInfoCacheForDebug"}, + {9019, nullptr, "UpdateSaveDataBackupInfoCacheAsyncForDebug"}, + {9022, nullptr, "DeleteSaveDataBackupAsyncForDebug"}, + {9025, nullptr, "ListDownloadableSaveDataBackupInfoAsyncForDebug"}, + {9026, nullptr, "DownloadSaveDataBackupAsyncForDebug"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void Initialize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_OLSC, "(STUBBED) called"); + + initialized = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void SetSaveDataBackupSettingEnabled(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_OLSC, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + bool initialized{}; +}; + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<OLSC>()->InstallAsService(service_manager); +} + +} // namespace Service::OLSC diff --git a/src/core/hle/service/olsc/olsc.h b/src/core/hle/service/olsc/olsc.h new file mode 100644 index 000000000..edee4376b --- /dev/null +++ b/src/core/hle/service/olsc/olsc.h @@ -0,0 +1,16 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::OLSC { + +/// Registers all SSL services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace Service::OLSC diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index f43122ad2..a771a51b4 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index cde3312da..b9ef86b72 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -4,6 +4,7 @@ #include "common/hex_util.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/process.h" #include "core/hle/service/acc/profile_manager.h" diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index ba9159ee0..fb4979af2 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -51,6 +51,7 @@ #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/olsc/olsc.h" #include "core/hle/service/pcie/pcie.h" #include "core/hle/service/pctl/module.h" #include "core/hle/service/pcv/pcv.h" @@ -187,17 +188,19 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co return RESULT_SUCCESS; } -/// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { +/// Initialize Services +Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) + : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} { + // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it // here and pass it into the respective InstallInterfaces functions. - auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system); + system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); SM::ServiceManager::InstallInterfaces(sm, system.Kernel()); Account::InstallInterfaces(system); - AM::InstallInterfaces(*sm, nv_flinger, system); + AM::InstallInterfaces(*sm, *nv_flinger, system); AOC::InstallInterfaces(*sm, system); APM::InstallInterfaces(system); Audio::InstallInterfaces(*sm, system); @@ -231,6 +234,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { NPNS::InstallInterfaces(*sm); NS::InstallInterfaces(*sm, system); Nvidia::InstallInterfaces(*sm, *nv_flinger, system); + OLSC::InstallInterfaces(*sm); PCIe::InstallInterfaces(*sm); PCTL::InstallInterfaces(*sm); PCV::InstallInterfaces(*sm); @@ -244,14 +248,10 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { SSL::InstallInterfaces(*sm); Time::InstallInterfaces(system); USB::InstallInterfaces(*sm); - VI::InstallInterfaces(*sm, nv_flinger); + VI::InstallInterfaces(*sm, *nv_flinger); WLAN::InstallInterfaces(*sm); - - LOG_DEBUG(Service, "initialized OK"); } -/// Shutdown ServiceManager -void Shutdown() { - LOG_DEBUG(Service, "shutdown OK"); -} +Services::~Services() = default; + } // namespace Service diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index a01ef3353..ed4792289 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -29,7 +29,11 @@ namespace Service { namespace FileSystem { class FileSystemController; -} // namespace FileSystem +} + +namespace NVFlinger { +class NVFlinger; +} namespace SM { class ServiceManager; @@ -181,10 +185,17 @@ private: } }; -/// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system); +/** + * The purpose of this class is to own any objects that need to be shared across the other service + * implementations. Will be torn down when the global system instance is shutdown. + */ +class Services final { +public: + explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system); + ~Services(); -/// Shutdown ServiceManager -void Shutdown(); +private: + std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; +}; } // namespace Service diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 5b0e371fe..86bd604f4 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -492,8 +492,8 @@ private: class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { public: - explicit IHOSBinderDriver(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) - : ServiceFramework("IHOSBinderDriver"), nv_flinger(std::move(nv_flinger)) { + explicit IHOSBinderDriver(NVFlinger::NVFlinger& nv_flinger) + : ServiceFramework("IHOSBinderDriver"), nv_flinger(nv_flinger) { static const FunctionInfo functions[] = { {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, @@ -530,8 +530,8 @@ private: LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, static_cast<u32>(transaction), flags); - const auto guard = nv_flinger->Lock(); - auto& buffer_queue = nv_flinger->FindBufferQueue(id); + const auto guard = nv_flinger.Lock(); + auto& buffer_queue = nv_flinger.FindBufferQueue(id); switch (transaction) { case TransactionId::Connect: { @@ -570,8 +570,8 @@ private: [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { // Repeat TransactParcel DequeueBuffer when a buffer is available - const auto guard = nv_flinger->Lock(); - auto& buffer_queue = nv_flinger->FindBufferQueue(id); + const auto guard = nv_flinger.Lock(); + auto& buffer_queue = nv_flinger.FindBufferQueue(id); auto result = buffer_queue.DequeueBuffer(width, height); ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); @@ -676,7 +676,7 @@ private: LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); - const auto& buffer_queue = nv_flinger->FindBufferQueue(id); + const auto& buffer_queue = nv_flinger.FindBufferQueue(id); // TODO(Subv): Find out what this actually is. IPC::ResponseBuilder rb{ctx, 2, 1}; @@ -684,8 +684,8 @@ private: rb.PushCopyObjects(buffer_queue.GetBufferWaitEvent()); } - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; -}; // namespace VI + NVFlinger::NVFlinger& nv_flinger; +}; class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { public: @@ -771,7 +771,7 @@ private: IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); - if (Settings::values.use_docked_mode) { + if (Settings::values.use_docked_mode.GetValue()) { rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * static_cast<u32>(Settings::values.resolution_factor.GetValue())); rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * @@ -790,8 +790,8 @@ private: class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { public: - explicit IManagerDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) - : ServiceFramework("IManagerDisplayService"), nv_flinger(std::move(nv_flinger)) { + explicit IManagerDisplayService(NVFlinger::NVFlinger& nv_flinger) + : ServiceFramework("IManagerDisplayService"), nv_flinger(nv_flinger) { // clang-format off static const FunctionInfo functions[] = { {200, nullptr, "AllocateProcessHeapBlock"}, @@ -893,7 +893,7 @@ private: "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}", unknown, display, aruid); - const auto layer_id = nv_flinger->CreateLayer(display); + const auto layer_id = nv_flinger.CreateLayer(display); if (!layer_id) { LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); IPC::ResponseBuilder rb{ctx, 2}; @@ -930,12 +930,12 @@ private: rb.Push(RESULT_SUCCESS); } - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; + NVFlinger::NVFlinger& nv_flinger; }; class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { public: - explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); + explicit IApplicationDisplayService(NVFlinger::NVFlinger& nv_flinger); private: enum class ConvertedScaleMode : u64 { @@ -1010,7 +1010,7 @@ private: ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); - const auto display_id = nv_flinger->OpenDisplay(name); + const auto display_id = nv_flinger.OpenDisplay(name); if (!display_id) { LOG_ERROR(Service_VI, "Display not found! display_name={}", name); IPC::ResponseBuilder rb{ctx, 2}; @@ -1110,7 +1110,7 @@ private: LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid); - const auto display_id = nv_flinger->OpenDisplay(display_name); + const auto display_id = nv_flinger.OpenDisplay(display_name); if (!display_id) { LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -1118,7 +1118,7 @@ private: return; } - const auto buffer_queue_id = nv_flinger->FindBufferQueueId(*display_id, layer_id); + const auto buffer_queue_id = nv_flinger.FindBufferQueueId(*display_id, layer_id); if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -1138,7 +1138,7 @@ private: LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id); - nv_flinger->CloseLayer(layer_id); + nv_flinger.CloseLayer(layer_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -1154,7 +1154,7 @@ private: // TODO(Subv): What's the difference between a Stray and a Managed layer? - const auto layer_id = nv_flinger->CreateLayer(display_id); + const auto layer_id = nv_flinger.CreateLayer(display_id); if (!layer_id) { LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -1162,7 +1162,7 @@ private: return; } - const auto buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, *layer_id); + const auto buffer_queue_id = nv_flinger.FindBufferQueueId(display_id, *layer_id); if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -1193,7 +1193,7 @@ private: LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); - const auto vsync_event = nv_flinger->FindVsyncEvent(display_id); + const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); if (!vsync_event) { LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -1258,12 +1258,11 @@ private: } } - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; + NVFlinger::NVFlinger& nv_flinger; }; -IApplicationDisplayService::IApplicationDisplayService( - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) - : ServiceFramework("IApplicationDisplayService"), nv_flinger(std::move(nv_flinger)) { +IApplicationDisplayService::IApplicationDisplayService(NVFlinger::NVFlinger& nv_flinger) + : ServiceFramework("IApplicationDisplayService"), nv_flinger(nv_flinger) { static const FunctionInfo functions[] = { {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"}, @@ -1304,8 +1303,7 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) { return false; } -void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, +void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, NVFlinger::NVFlinger& nv_flinger, Permission permission) { IPC::RequestParser rp{ctx}; const auto policy = rp.PopEnum<Policy>(); @@ -1319,11 +1317,10 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationDisplayService>(std::move(nv_flinger)); + rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger); } -void InstallInterfaces(SM::ServiceManager& service_manager, - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) { +void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nv_flinger) { std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager); std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager); std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager); diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index 6b66f8b81..5229fa753 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -43,12 +43,11 @@ enum class Policy { }; namespace detail { -void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, Permission permission); +void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, NVFlinger::NVFlinger& nv_flinger, + Permission permission); } // namespace detail /// Registers all VI services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager, - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); +void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nv_flinger); } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp index 06070087f..41da3ee93 100644 --- a/src/core/hle/service/vi/vi_m.cpp +++ b/src/core/hle/service/vi/vi_m.cpp @@ -8,8 +8,7 @@ namespace Service::VI { -VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) - : ServiceFramework{"vi:m"}, nv_flinger{std::move(nv_flinger)} { +VI_M::VI_M(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:m"}, nv_flinger{nv_flinger} { static const FunctionInfo functions[] = { {2, &VI_M::GetDisplayService, "GetDisplayService"}, {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h index 290e06689..ee2489874 100644 --- a/src/core/hle/service/vi/vi_m.h +++ b/src/core/hle/service/vi/vi_m.h @@ -18,13 +18,13 @@ namespace Service::VI { class VI_M final : public ServiceFramework<VI_M> { public: - explicit VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); + explicit VI_M(NVFlinger::NVFlinger& nv_flinger); ~VI_M() override; private: void GetDisplayService(Kernel::HLERequestContext& ctx); - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; + NVFlinger::NVFlinger& nv_flinger; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp index 57c596cc4..6acb51e2a 100644 --- a/src/core/hle/service/vi/vi_s.cpp +++ b/src/core/hle/service/vi/vi_s.cpp @@ -8,8 +8,7 @@ namespace Service::VI { -VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) - : ServiceFramework{"vi:s"}, nv_flinger{std::move(nv_flinger)} { +VI_S::VI_S(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:s"}, nv_flinger{nv_flinger} { static const FunctionInfo functions[] = { {1, &VI_S::GetDisplayService, "GetDisplayService"}, {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h index 47804dc0b..6790673ab 100644 --- a/src/core/hle/service/vi/vi_s.h +++ b/src/core/hle/service/vi/vi_s.h @@ -18,13 +18,13 @@ namespace Service::VI { class VI_S final : public ServiceFramework<VI_S> { public: - explicit VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); + explicit VI_S(NVFlinger::NVFlinger& nv_flinger); ~VI_S() override; private: void GetDisplayService(Kernel::HLERequestContext& ctx); - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; + NVFlinger::NVFlinger& nv_flinger; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp index 6b7329345..44e00a4f6 100644 --- a/src/core/hle/service/vi/vi_u.cpp +++ b/src/core/hle/service/vi/vi_u.cpp @@ -8,8 +8,7 @@ namespace Service::VI { -VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) - : ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} { +VI_U::VI_U(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:u"}, nv_flinger{nv_flinger} { static const FunctionInfo functions[] = { {0, &VI_U::GetDisplayService, "GetDisplayService"}, {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h index 19bdb73b0..b59f986f0 100644 --- a/src/core/hle/service/vi/vi_u.h +++ b/src/core/hle/service/vi/vi_u.h @@ -18,13 +18,13 @@ namespace Service::VI { class VI_U final : public ServiceFramework<VI_U> { public: - explicit VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); + explicit VI_U(NVFlinger::NVFlinger& nv_flinger); ~VI_U() override; private: void GetDisplayService(Kernel::HLERequestContext& ctx); - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; + NVFlinger::NVFlinger& nv_flinger; }; } // namespace Service::VI diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 394a1bf26..2002dc4f2 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -114,7 +114,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect } if (override_update) { - const FileSys::PatchManager patch_manager(metadata.GetTitleID()); + const FileSys::PatchManager patch_manager( + metadata.GetTitleID(), system.GetFileSystemController(), system.GetContentProvider()); dir = patch_manager.PatchExeFS(dir); } @@ -160,7 +161,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect modules.clear(); const VAddr base_address{process.PageTable().GetCodeRegionStart()}; VAddr next_load_addr{base_address}; - const FileSys::PatchManager pm{metadata.GetTitleID()}; + const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(), + system.GetContentProvider()}; for (const auto& module : static_modules) { const FileSys::VirtualFile module_file{dir->GetFile(module)}; if (!module_file) { diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 9bc3a8840..d91c15561 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -10,6 +10,7 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/string_util.h" +#include "core/core.h" #include "core/hle/kernel/process.h" #include "core/loader/deconstructed_rom_directory.h" #include "core/loader/elf.h" @@ -194,15 +195,15 @@ AppLoader::~AppLoader() = default; /** * Get a loader for a file with a specific type - * @param file The file to load - * @param type The type of the file - * @param file the file to retrieve the loader for - * @param type the file type + * @param system The system context to use. + * @param file The file to retrieve the loader for + * @param type The file type + * @param program_index Specifies the index within the container of the program to launch. * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type */ -static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileType type) { +static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file, + FileType type, std::size_t program_index) { switch (type) { - // Standard ELF file format. case FileType::ELF: return std::make_unique<AppLoader_ELF>(std::move(file)); @@ -221,7 +222,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT // NX XCI (nX Card Image) file format. case FileType::XCI: - return std::make_unique<AppLoader_XCI>(std::move(file)); + return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(), + system.GetContentProvider(), program_index); // NX NAX (NintendoAesXts) file format. case FileType::NAX: @@ -229,7 +231,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT // NX NSP (Nintendo Submission Package) file format case FileType::NSP: - return std::make_unique<AppLoader_NSP>(std::move(file)); + return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(), + system.GetContentProvider(), program_index); // NX KIP (Kernel Internal Process) file format case FileType::KIP: @@ -244,20 +247,22 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT } } -std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file) { +std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, + std::size_t program_index) { FileType type = IdentifyFile(file); - FileType filename_type = GuessFromFilename(file->GetName()); + const FileType filename_type = GuessFromFilename(file->GetName()); // Special case: 00 is either a NCA or NAX. if (type != filename_type && !(file->GetName() == "00" && type == FileType::NAX)) { LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName()); - if (FileType::Unknown == type) + if (FileType::Unknown == type) { type = filename_type; + } } LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type)); - return GetFileLoader(std::move(file), type); + return GetFileLoader(system, std::move(file), type, program_index); } } // namespace Loader diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index ac60b097a..36e79e71d 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -290,9 +290,14 @@ protected: /** * Identifies a bootable file and return a suitable loader - * @param file The bootable file - * @return the best loader for this file + * + * @param system The system context. + * @param file The bootable file. + * @param program_index Specifies the index within the container of the program to launch. + * + * @return the best loader for this file. */ -std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file); +std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, + std::size_t program_index = 0); } // namespace Loader diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 497f438a1..aa85c1a29 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -149,7 +149,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::S // Apply cheats if they exist and the program has a valid title ID if (pm) { system.SetCurrentProcessBuildID(nso_header.build_id); - const auto cheats = pm->CreateCheatList(system, nso_header.build_id); + const auto cheats = pm->CreateCheatList(nso_header.build_id); if (!cheats.empty()) { system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size); } diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 15e528fa8..928f64c8c 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -21,26 +21,34 @@ namespace Loader { -AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file) - : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)), +AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file, + const Service::FileSystem::FileSystemController& fsc, + const FileSys::ContentProvider& content_provider, + std::size_t program_index) + : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file, program_index)), title_id(nsp->GetProgramTitleID()) { - if (nsp->GetStatus() != ResultStatus::Success) + if (nsp->GetStatus() != ResultStatus::Success) { return; + } if (nsp->IsExtractedType()) { secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); } else { const auto control_nca = nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control); - if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) + if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) { return; + } - std::tie(nacp_file, icon_file) = - FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca); + std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] { + const FileSys::PatchManager pm{nsp->GetProgramTitleID(), fsc, content_provider}; + return pm.ParseControlNCA(*control_nca); + }(); - if (title_id == 0) + if (title_id == 0) { return; + } secondary_loader = std::make_unique<AppLoader_NCA>( nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program)); diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index b27deb686..f0518ac47 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h @@ -9,15 +9,16 @@ #include "core/file_sys/vfs.h" #include "core/loader/loader.h" -namespace Core { -class System; -} - namespace FileSys { +class ContentProvider; class NACP; class NSP; } // namespace FileSys +namespace Service::FileSystem { +class FileSystemController; +} + namespace Loader { class AppLoader_NCA; @@ -25,7 +26,10 @@ class AppLoader_NCA; /// Loads an XCI file class AppLoader_NSP final : public AppLoader { public: - explicit AppLoader_NSP(FileSys::VirtualFile file); + explicit AppLoader_NSP(FileSys::VirtualFile file, + const Service::FileSystem::FileSystemController& fsc, + const FileSys::ContentProvider& content_provider, + std::size_t program_index); ~AppLoader_NSP() override; /** diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 25e83af0f..aaa250cea 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -20,18 +20,25 @@ namespace Loader { -AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) - : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), +AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file, + const Service::FileSystem::FileSystemController& fsc, + const FileSys::ContentProvider& content_provider, + std::size_t program_index) + : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file, program_index)), nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) { - if (xci->GetStatus() != ResultStatus::Success) + if (xci->GetStatus() != ResultStatus::Success) { return; + } const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control); - if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) + if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) { return; + } - std::tie(nacp_file, icon_file) = - FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(*control_nca); + std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] { + const FileSys::PatchManager pm{xci->GetProgramTitleID(), fsc, content_provider}; + return pm.ParseControlNCA(*control_nca); + }(); } AppLoader_XCI::~AppLoader_XCI() = default; diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index 04aea286f..764dc8328 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h @@ -9,15 +9,16 @@ #include "core/file_sys/vfs.h" #include "core/loader/loader.h" -namespace Core { -class System; -} - namespace FileSys { +class ContentProvider; class NACP; class XCI; } // namespace FileSys +namespace Service::FileSystem { +class FileSystemController; +} + namespace Loader { class AppLoader_NCA; @@ -25,7 +26,10 @@ class AppLoader_NCA; /// Loads an XCI file class AppLoader_XCI final : public AppLoader { public: - explicit AppLoader_XCI(FileSys::VirtualFile file); + explicit AppLoader_XCI(FileSys::VirtualFile file, + const Service::FileSystem::FileSystemController& fsc, + const FileSys::ContentProvider& content_provider, + std::size_t program_index); ~AppLoader_XCI() override; /** diff --git a/src/core/settings.cpp b/src/core/settings.cpp index a99d3cf5a..aadbc3932 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -49,13 +49,14 @@ void LogSettings() { }; LOG_INFO(Config, "yuzu Configuration:"); - log_setting("Controls_UseDockedMode", values.use_docked_mode); + log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); log_setting("System_CurrentUser", values.current_user); log_setting("System_LanguageIndex", values.language_index.GetValue()); log_setting("System_RegionIndex", values.region_index.GetValue()); log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue()); log_setting("Core_UseMultiCore", values.use_multi_core.GetValue()); + log_setting("CPU_Accuracy", values.cpu_accuracy); log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue()); log_setting("Renderer_UseFrameLimit", values.use_frame_limit.GetValue()); log_setting("Renderer_FrameLimit", values.frame_limit.GetValue()); @@ -144,6 +145,12 @@ void RestoreGlobalState() { values.rng_seed.SetGlobal(true); values.custom_rtc.SetGlobal(true); values.sound_index.SetGlobal(true); + + // Controls + values.players.SetGlobal(true); + values.use_docked_mode.SetGlobal(true); + values.vibration_enabled.SetGlobal(true); + values.motion_enabled.SetGlobal(true); } void Sanitize() { diff --git a/src/core/settings.h b/src/core/settings.h index 28616a574..1143aba5d 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -65,6 +65,38 @@ private: Type local{}; }; +/** + * The InputSetting class allows for getting a reference to either the global or local members. + * This is required as we cannot easily modify the values of user-defined types within containers + * using the SetValue() member function found in the Setting class. The primary purpose of this + * class is to store an array of 10 PlayerInput structs for both the global and local (per-game) + * setting and allows for easily accessing and modifying both settings. + */ +template <typename Type> +class InputSetting final { +public: + InputSetting() = default; + explicit InputSetting(Type val) : global{val} {} + ~InputSetting() = default; + void SetGlobal(bool to_global) { + use_global = to_global; + } + bool UsingGlobal() const { + return use_global; + } + Type& GetValue(bool need_global = false) { + if (use_global || need_global) { + return global; + } + return local; + } + +private: + bool use_global = true; + Type global{}; + Type local{}; +}; + struct TouchFromButtonMap { std::string name; std::vector<std::string> buttons; @@ -133,9 +165,18 @@ struct Values { Setting<s32> sound_index; // Controls - std::array<PlayerInput, 10> players; + InputSetting<std::array<PlayerInput, 10>> players; + + Setting<bool> use_docked_mode; - bool use_docked_mode; + Setting<bool> vibration_enabled; + Setting<bool> enable_accurate_vibrations; + + Setting<bool> motion_enabled; + std::string motion_device; + std::string udp_input_address; + u16 udp_input_port; + u8 udp_pad_index; bool mouse_enabled; std::string mouse_device; @@ -149,20 +190,15 @@ struct Values { ButtonsRaw debug_pad_buttons; AnalogsRaw debug_pad_analogs; - bool vibration_enabled; - - bool motion_enabled; - std::string motion_device; - std::string touch_device; TouchscreenInput touchscreen; - std::atomic_bool is_device_reload_pending{true}; + bool use_touch_from_button; + std::string touch_device; int touch_from_button_map_index; - std::string udp_input_address; - u16 udp_input_port; - u8 udp_pad_index; std::vector<TouchFromButtonMap> touch_from_button_maps; + std::atomic_bool is_device_reload_pending{true}; + // Data Storage bool use_virtual_sd; bool gamecard_inserted; @@ -179,6 +215,7 @@ struct Values { bool reporting_services; bool quest_flag; bool disable_macro_jit; + bool extended_logging; // Misceallaneous std::string log_filter; diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index ebc19e18a..d11b15f38 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -147,7 +147,9 @@ TelemetrySession::~TelemetrySession() { } } -void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { +void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader, + const Service::FileSystem::FileSystemController& fsc, + const FileSys::ContentProvider& content_provider) { // Log one-time top-level information AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId()); @@ -167,7 +169,10 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { app_loader.ReadTitle(name); if (name.empty()) { - const auto metadata = FileSys::PatchManager(program_id).GetControlMetadata(); + const auto metadata = [&content_provider, &fsc, program_id] { + const FileSys::PatchManager pm{program_id, fsc, content_provider}; + return pm.GetControlMetadata(); + }(); if (metadata.first != nullptr) { name = metadata.first->GetApplicationName(); } @@ -213,7 +218,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { Settings::values.use_assembly_shaders.GetValue()); AddField(field_type, "Renderer_UseAsynchronousShaders", Settings::values.use_asynchronous_shaders.GetValue()); - AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); + AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue()); } bool TelemetrySession::SubmitTestcase() { diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index 66789d4bd..6f3d45bea 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h @@ -7,10 +7,18 @@ #include <string> #include "common/telemetry.h" +namespace FileSys { +class ContentProvider; +} + namespace Loader { class AppLoader; } +namespace Service::FileSystem { +class FileSystemController; +} + namespace Core { /** @@ -40,10 +48,14 @@ public: * - Title file format * - Miscellaneous settings values. * - * @param app_loader The application loader to use to retrieve - * title-specific information. + * @param app_loader The application loader to use to retrieve + * title-specific information. + * @param fsc Filesystem controller to use to retrieve info. + * @param content_provider Content provider to use to retrieve info. */ - void AddInitialInfo(Loader::AppLoader& app_loader); + void AddInitialInfo(Loader::AppLoader& app_loader, + const Service::FileSystem::FileSystemController& fsc, + const FileSys::ContentProvider& content_provider); /** * Wrapper around the Telemetry::FieldCollection::AddField method. diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 7b39a38c1..1d1b2e08a 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -31,6 +31,9 @@ add_library(input_common STATIC if (MSVC) target_compile_options(input_common PRIVATE + /W4 + /WX + # 'expression' : signed/unsigned mismatch /we4018 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) @@ -46,6 +49,7 @@ if (MSVC) ) else() target_compile_options(input_common PRIVATE + -Werror -Werror=conversion -Werror=ignored-qualifiers -Werror=implicit-fallthrough diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp index b912188b6..d80195c82 100644 --- a/src/input_common/gcadapter/gc_adapter.cpp +++ b/src/input_common/gcadapter/gc_adapter.cpp @@ -230,10 +230,8 @@ void Adapter::SendVibrations() { vibration_changed = false; } -bool Adapter::RumblePlay(std::size_t port, f32 amplitude) { - amplitude = std::clamp(amplitude, 0.0f, 1.0f); - const auto raw_amp = static_cast<u8>(amplitude * 0x8); - pads[port].rumble_amplitude = raw_amp; +bool Adapter::RumblePlay(std::size_t port, u8 amplitude) { + pads[port].rumble_amplitude = amplitude; return rumble_enabled; } diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h index d28dcfad3..f1256c9da 100644 --- a/src/input_common/gcadapter/gc_adapter.h +++ b/src/input_common/gcadapter/gc_adapter.h @@ -77,8 +77,8 @@ public: Adapter(); ~Adapter(); - /// Request a vibration for a controlelr - bool RumblePlay(std::size_t port, f32 amplitude); + /// Request a vibration for a controller + bool RumblePlay(std::size_t port, u8 amplitude); /// Used for polling void BeginConfiguration(); diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index 6bd6f57fc..4d1052414 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -15,7 +15,7 @@ namespace InputCommon { class GCButton final : public Input::ButtonDevice { public: - explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter) + explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter) : port(port_), button(button_), gcadapter(adapter) {} ~GCButton() override; @@ -27,18 +27,10 @@ public: return false; } - bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override { - const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f; - const auto new_amp = - static_cast<f32>(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f))); - - return gcadapter->RumblePlay(port, new_amp); - } - private: const u32 port; const s32 button; - GCAdapter::Adapter* gcadapter; + const GCAdapter::Adapter* gcadapter; }; class GCAxisButton final : public Input::ButtonDevice { @@ -104,7 +96,6 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param adapter.get()); } - UNREACHABLE(); return nullptr; } @@ -299,4 +290,43 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() { return params; } +class GCVibration final : public Input::VibrationDevice { +public: + explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter) + : port(port_), gcadapter(adapter) {} + + u8 GetStatus() const override { + return gcadapter->RumblePlay(port, 0); + } + + bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high, + [[maybe_unused]] f32 freq_high) const override { + const auto mean_amplitude = (amp_low + amp_high) * 0.5f; + const auto processed_amplitude = + static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8); + + return gcadapter->RumblePlay(port, processed_amplitude); + } + +private: + const u32 port; + GCAdapter::Adapter* gcadapter; +}; + +/// An vibration device factory that creates vibration devices from GC Adapter +GCVibrationFactory::GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) + : adapter(std::move(adapter_)) {} + +/** + * Creates a vibration device from a joystick + * @param params contains parameters for creating the device: + * - "port": the nth gcpad on the adapter + */ +std::unique_ptr<Input::VibrationDevice> GCVibrationFactory::Create( + const Common::ParamPackage& params) { + const auto port = static_cast<u32>(params.Get("port", 0)); + + return std::make_unique<GCVibration>(port, adapter.get()); +} + } // namespace InputCommon diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h index 0527f328f..d1271e3ea 100644 --- a/src/input_common/gcadapter/gc_poller.h +++ b/src/input_common/gcadapter/gc_poller.h @@ -64,4 +64,15 @@ private: bool polling = false; }; +/// A vibration device factory creates vibration devices from GC Adapter +class GCVibrationFactory final : public Input::Factory<Input::VibrationDevice> { +public: + explicit GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_); + + std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override; + +private: + std::shared_ptr<GCAdapter::Adapter> adapter; +}; + } // namespace InputCommon diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index d32fd8b81..e59ad4ff5 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -28,6 +28,8 @@ struct InputSubsystem::Impl { Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons); gcanalog = std::make_shared<GCAnalogFactory>(gcadapter); Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog); + gcvibration = std::make_shared<GCVibrationFactory>(gcadapter); + Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration); keyboard = std::make_shared<Keyboard>(); Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); @@ -64,9 +66,11 @@ struct InputSubsystem::Impl { #endif Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); + Input::UnregisterFactory<Input::VibrationDevice>("gcpad"); gcbuttons.reset(); gcanalog.reset(); + gcvibration.reset(); Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); @@ -78,7 +82,7 @@ struct InputSubsystem::Impl { [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { std::vector<Common::ParamPackage> devices = { Common::ParamPackage{{"display", "Any"}, {"class", "any"}}, - Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}}, + Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}}, }; #ifdef HAVE_SDL2 auto sdl_devices = sdl->GetInputDevices(); @@ -96,10 +100,6 @@ struct InputSubsystem::Impl { if (!params.Has("class") || params.Get("class", "") == "any") { return {}; } - if (params.Get("class", "") == "key") { - // TODO consider returning the SDL key codes for the default keybindings - return {}; - } if (params.Get("class", "") == "gcpad") { return gcadapter->GetAnalogMappingForDevice(params); } @@ -116,10 +116,6 @@ struct InputSubsystem::Impl { if (!params.Has("class") || params.Get("class", "") == "any") { return {}; } - if (params.Get("class", "") == "key") { - // TODO consider returning the SDL key codes for the default keybindings - return {}; - } if (params.Get("class", "") == "gcpad") { return gcadapter->GetButtonMappingForDevice(params); } @@ -150,6 +146,7 @@ struct InputSubsystem::Impl { #endif std::shared_ptr<GCButtonFactory> gcbuttons; std::shared_ptr<GCAnalogFactory> gcanalog; + std::shared_ptr<GCVibrationFactory> gcvibration; std::shared_ptr<UDPMotionFactory> udpmotion; std::shared_ptr<UDPTouchFactory> udptouch; std::shared_ptr<CemuhookUDP::Client> udp; diff --git a/src/input_common/motion_input.h b/src/input_common/motion_input.h index abb957f04..efe74cf19 100644 --- a/src/input_common/motion_input.h +++ b/src/input_common/motion_input.h @@ -13,7 +13,7 @@ namespace InputCommon { class MotionInput { public: - MotionInput(f32 new_kp, f32 new_ki, f32 new_kd); + explicit MotionInput(f32 new_kp, f32 new_ki, f32 new_kd); MotionInput(const MotionInput&) = default; MotionInput& operator=(const MotionInput&) = default; @@ -33,16 +33,17 @@ public: void UpdateRotation(u64 elapsed_time); void UpdateOrientation(u64 elapsed_time); - std::array<Common::Vec3f, 3> GetOrientation() const; - Common::Vec3f GetAcceleration() const; - Common::Vec3f GetGyroscope() const; - Common::Vec3f GetRotations() const; - Common::Quaternion<f32> GetQuaternion() const; - Input::MotionStatus GetMotion() const; - Input::MotionStatus GetRandomMotion(int accel_magnitude, int gyro_magnitude) const; - - bool IsMoving(f32 sensitivity) const; - bool IsCalibrated(f32 sensitivity) const; + [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const; + [[nodiscard]] Common::Vec3f GetAcceleration() const; + [[nodiscard]] Common::Vec3f GetGyroscope() const; + [[nodiscard]] Common::Vec3f GetRotations() const; + [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const; + [[nodiscard]] Input::MotionStatus GetMotion() const; + [[nodiscard]] Input::MotionStatus GetRandomMotion(int accel_magnitude, + int gyro_magnitude) const; + + [[nodiscard]] bool IsMoving(f32 sensitivity) const; + [[nodiscard]] bool IsCalibrated(f32 sensitivity) const; private: void ResetOrientation(); diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h index f3554be9a..42bbf14d4 100644 --- a/src/input_common/sdl/sdl.h +++ b/src/input_common/sdl/sdl.h @@ -23,7 +23,7 @@ public: /// Unregisters SDL device factories and shut them down. virtual ~State() = default; - virtual Pollers GetPollers(Polling::DeviceType type) { + virtual Pollers GetPollers(Polling::DeviceType) { return {}; } diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 10883e2d9..7827e324c 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -80,30 +80,13 @@ public: return static_cast<float>(state.axes.at(axis)) / (32767.0f * range); } - bool RumblePlay(f32 amp_low, f32 amp_high, u32 time) { - const u16 raw_amp_low = static_cast<u16>(amp_low * 0xFFFF); - const u16 raw_amp_high = static_cast<u16>(amp_high * 0xFFFF); - // Lower drastically the number of state changes - if (raw_amp_low >> 11 == last_state_rumble_low >> 11 && - raw_amp_high >> 11 == last_state_rumble_high >> 11) { - if (raw_amp_low + raw_amp_high != 0 || - last_state_rumble_low + last_state_rumble_high == 0) { - return false; - } - } - // Don't change state if last vibration was < 20ms - const auto now = std::chrono::system_clock::now(); - if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_vibration) < - std::chrono::milliseconds(20)) { - return raw_amp_low + raw_amp_high == 0; + bool RumblePlay(u16 amp_low, u16 amp_high) { + if (sdl_controller) { + return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0; + } else if (sdl_joystick) { + return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0; } - last_vibration = now; - last_state_rumble_low = raw_amp_low; - last_state_rumble_high = raw_amp_high; - if (sdl_joystick) { - SDL_JoystickRumble(sdl_joystick.get(), raw_amp_low, raw_amp_high, time); - } return false; } @@ -172,9 +155,6 @@ private: } state; std::string guid; int port; - u16 last_state_rumble_high = 0; - u16 last_state_rumble_low = 0; - std::chrono::time_point<std::chrono::system_clock> last_vibration; std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; mutable std::mutex mutex; @@ -327,12 +307,6 @@ public: return joystick->GetButton(button); } - bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override { - const f32 new_amp_low = pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f)); - const f32 new_amp_high = pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f)); - return joystick->RumblePlay(new_amp_low, new_amp_high, 250); - } - private: std::shared_ptr<SDLJoystick> joystick; int button; @@ -416,6 +390,32 @@ private: const float range; }; +class SDLVibration final : public Input::VibrationDevice { +public: + explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_) + : joystick(std::move(joystick_)) {} + + u8 GetStatus() const override { + joystick->RumblePlay(1, 1); + return joystick->RumblePlay(0, 0); + } + + bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high, + [[maybe_unused]] f32 freq_high) const override { + const auto process_amplitude = [](f32 amplitude) { + return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF); + }; + + const auto processed_amp_low = process_amplitude(amp_low); + const auto processed_amp_high = process_amplitude(amp_high); + + return joystick->RumblePlay(processed_amp_low, processed_amp_high); + } + +private: + std::shared_ptr<SDLJoystick> joystick; +}; + class SDLDirectionMotion final : public Input::MotionDevice { public: explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_) @@ -558,7 +558,7 @@ class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> { public: explicit SDLAnalogFactory(SDLState& state_) : state(state_) {} /** - * Creates analog device from joystick axes + * Creates an analog device from joystick axes * @param params contains parameters for creating the device: * - "guid": the guid of the joystick to bind * - "port": the nth joystick of the same type @@ -584,6 +584,26 @@ private: SDLState& state; }; +/// An vibration device factory that creates vibration devices from SDL joystick +class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> { +public: + explicit SDLVibrationFactory(SDLState& state_) : state(state_) {} + /** + * Creates a vibration device from a joystick + * @param params contains parameters for creating the device: + * - "guid": the guid of the joystick to bind + * - "port": the nth joystick of the same type + */ + std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override { + const std::string guid = params.Get("guid", "0"); + const int port = params.Get("port", 0); + return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port)); + } + +private: + SDLState& state; +}; + /// A motion device factory that creates motion devices from SDL joystick class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> { public: @@ -650,11 +670,13 @@ private: SDLState::SDLState() { using namespace Input; - analog_factory = std::make_shared<SDLAnalogFactory>(*this); button_factory = std::make_shared<SDLButtonFactory>(*this); + analog_factory = std::make_shared<SDLAnalogFactory>(*this); + vibration_factory = std::make_shared<SDLVibrationFactory>(*this); motion_factory = std::make_shared<SDLMotionFactory>(*this); - RegisterFactory<AnalogDevice>("sdl", analog_factory); RegisterFactory<ButtonDevice>("sdl", button_factory); + RegisterFactory<AnalogDevice>("sdl", analog_factory); + RegisterFactory<VibrationDevice>("sdl", vibration_factory); RegisterFactory<MotionDevice>("sdl", motion_factory); // If the frontend is going to manage the event loop, then we don't start one here @@ -676,7 +698,7 @@ SDLState::SDLState() { using namespace std::chrono_literals; while (initialized) { SDL_PumpEvents(); - std::this_thread::sleep_for(5ms); + std::this_thread::sleep_for(1ms); } }); } @@ -691,6 +713,7 @@ SDLState::~SDLState() { using namespace Input; UnregisterFactory<ButtonDevice>("sdl"); UnregisterFactory<AnalogDevice>("sdl"); + UnregisterFactory<VibrationDevice>("sdl"); UnregisterFactory<MotionDevice>("sdl"); CloseJoysticks(); @@ -842,6 +865,8 @@ Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Eve Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) { switch (binding.bindType) { + case SDL_CONTROLLER_BINDTYPE_NONE: + break; case SDL_CONTROLLER_BINDTYPE_AXIS: return BuildAnalogParamPackageForButton(port, guid, binding.value.axis); case SDL_CONTROLLER_BINDTYPE_BUTTON: @@ -962,7 +987,7 @@ class SDLPoller : public InputCommon::Polling::DevicePoller { public: explicit SDLPoller(SDLState& state_) : state(state_) {} - void Start(const std::string& device_id) override { + void Start([[maybe_unused]] const std::string& device_id) override { state.event_queue.Clear(); state.polling = true; } @@ -1045,7 +1070,6 @@ public: void Start(const std::string& device_id) override { SDLPoller::Start(device_id); - // Load the game controller // Reset stored axes analog_x_axis = -1; analog_y_axis = -1; @@ -1058,40 +1082,21 @@ public: if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) { continue; } - // Simplify controller config by testing if game controller support is enabled. if (event.type == SDL_JOYAXISMOTION) { const auto axis = event.jaxis.axis; - if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); - auto* const controller = joystick->GetSDLGameController()) { - const auto axis_left_x = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) - .value.axis; - const auto axis_left_y = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) - .value.axis; - const auto axis_right_x = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) - .value.axis; - const auto axis_right_y = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) - .value.axis; - - if (axis == axis_left_x || axis == axis_left_y) { - analog_x_axis = axis_left_x; - analog_y_axis = axis_left_y; - break; - } else if (axis == axis_right_x || axis == axis_right_y) { - analog_x_axis = axis_right_x; - analog_y_axis = axis_right_y; - break; - } + // In order to return a complete analog param, we need inputs for both axes. + // First we take the x-axis (horizontal) input, then the y-axis (vertical) input. + if (analog_x_axis == -1) { + analog_x_axis = axis; + } else if (analog_y_axis == -1 && analog_x_axis != axis) { + analog_y_axis = axis; + } + } else { + // If the press wasn't accepted as a joy axis, check for a button press + auto button_press = button_poller.FromEvent(event); + if (button_press) { + return *button_press; } - } - - // If the press wasn't accepted as a joy axis, check for a button press - auto button_press = button_poller.FromEvent(event); - if (button_press) { - return *button_press; } } @@ -1104,6 +1109,7 @@ public: return params; } } + return {}; } diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index b9bb4dc56..08044b00d 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -22,6 +22,7 @@ namespace InputCommon::SDL { class SDLAnalogFactory; class SDLButtonFactory; class SDLMotionFactory; +class SDLVibrationFactory; class SDLJoystick; class SDLState : public State { @@ -72,6 +73,7 @@ private: std::shared_ptr<SDLButtonFactory> button_factory; std::shared_ptr<SDLAnalogFactory> analog_factory; + std::shared_ptr<SDLVibrationFactory> vibration_factory; std::shared_ptr<SDLMotionFactory> motion_factory; bool start_thread = false; diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp index b66c05856..557e7a9a0 100644 --- a/src/input_common/settings.cpp +++ b/src/input_common/settings.cpp @@ -14,13 +14,6 @@ const std::array<const char*, NumButtons> mapping = {{ }}; } -namespace NativeMotion { -const std::array<const char*, NumMotions> mapping = {{ - "motionleft", - "motionright", -}}; -} - namespace NativeAnalog { const std::array<const char*, NumAnalogs> mapping = {{ "lstick", @@ -28,6 +21,20 @@ const std::array<const char*, NumAnalogs> mapping = {{ }}; } +namespace NativeVibration { +const std::array<const char*, NumVibrations> mapping = {{ + "left_vibration_device", + "right_vibration_device", +}}; +} + +namespace NativeMotion { +const std::array<const char*, NumMotions> mapping = {{ + "motionleft", + "motionright", +}}; +} + namespace NativeMouseButton { const std::array<const char*, NumMouseButtons> mapping = {{ "left", diff --git a/src/input_common/settings.h b/src/input_common/settings.h index f52d28540..75486554b 100644 --- a/src/input_common/settings.h +++ b/src/input_common/settings.h @@ -66,17 +66,32 @@ constexpr int NUM_STICKS_HID = NumAnalogs; extern const std::array<const char*, NumAnalogs> mapping; } // namespace NativeAnalog +namespace NativeVibration { +enum Values : int { + LeftVibrationDevice, + RightVibrationDevice, + + NumVibrations, +}; + +constexpr int VIBRATION_HID_BEGIN = LeftVibrationDevice; +constexpr int VIBRATION_HID_END = NumVibrations; +constexpr int NUM_VIBRATIONS_HID = NumVibrations; + +extern const std::array<const char*, NumVibrations> mapping; +}; // namespace NativeVibration + namespace NativeMotion { enum Values : int { - MOTIONLEFT, - MOTIONRIGHT, + MotionLeft, + MotionRight, NumMotions, }; -constexpr int MOTION_HID_BEGIN = MOTIONLEFT; +constexpr int MOTION_HID_BEGIN = MotionLeft; constexpr int MOTION_HID_END = NumMotions; -constexpr int NUM_MOTION_HID = NumMotions; +constexpr int NUM_MOTIONS_HID = NumMotions; extern const std::array<const char*, NumMotions> mapping; } // namespace NativeMotion @@ -305,9 +320,11 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods; } // namespace NativeKeyboard -using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; -using MotionRaw = std::array<std::string, NativeMotion::NumMotions>; +using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; +using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>; +using VibrationsRaw = std::array<std::string, NativeVibration::NumVibrations>; + using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; @@ -330,7 +347,11 @@ struct PlayerInput { ControllerType controller_type; ButtonsRaw buttons; AnalogsRaw analogs; - MotionRaw motions; + VibrationsRaw vibrations; + MotionsRaw motions; + + bool vibration_enabled; + int vibration_strength; u32 body_color_left; u32 body_color_right; diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp index c37716aae..a07124a86 100644 --- a/src/input_common/touch_from_button.cpp +++ b/src/input_common/touch_from_button.cpp @@ -44,8 +44,7 @@ private: std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map; }; -std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create( - const Common::ParamPackage& params) { +std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create(const Common::ParamPackage&) { return std::make_unique<TouchFromButtonDevice>(); } diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 7039d6fc3..c0bb90048 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -63,7 +63,7 @@ public: } private: - void HandleReceive(const boost::system::error_code& error, std::size_t bytes_transferred) { + void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) { if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) { switch (*type) { case Type::Version: { @@ -90,7 +90,7 @@ private: StartReceive(); } - void HandleSend(const boost::system::error_code& error) { + 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}}; @@ -189,11 +189,11 @@ void Client::ReloadSocket(const std::string& host, u16 port, std::size_t pad_ind StartCommunication(client, host, port, pad_index, client_id); } -void Client::OnVersion(Response::Version data) { +void Client::OnVersion([[maybe_unused]] Response::Version data) { LOG_TRACE(Input, "Version packet received: {}", data.version); } -void Client::OnPortInfo(Response::PortInfo data) { +void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) { LOG_TRACE(Input, "PortInfo packet received: {}", data.model); } @@ -344,7 +344,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, }; Socket socket{host, port, pad_index, client_id, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; - const bool result = success_event.WaitFor(std::chrono::seconds(8)); + const bool result = success_event.WaitFor(std::chrono::seconds(5)); socket.Stop(); worker_thread.join(); if (result) { @@ -369,7 +369,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( u16 max_y{}; Status current_status{Status::Initialized}; - SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, + SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, [&](Response::PadData data) { if (current_status == Status::Initialized) { // Receiving data means the communication is ready now diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h index 3ba4d1fc8..fc1aea4b9 100644 --- a/src/input_common/udp/protocol.h +++ b/src/input_common/udp/protocol.h @@ -7,7 +7,16 @@ #include <array> #include <optional> #include <type_traits> + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4701) +#endif #include <boost/crc.hpp> +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #include "common/bit_field.h" #include "common/swap.h" @@ -93,7 +102,7 @@ static_assert(std::is_trivially_copyable_v<PadData>, /** * Creates a message with the proper header data that can be sent to the server. - * @param T data Request body to send + * @param data Request body to send * @param client_id ID of the udp client (usually not checked on the server) */ template <typename T> diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 105b85a92..d8801b1f5 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -13,20 +13,6 @@ namespace Tegra { -void CommandList::RefreshIntegrityChecks(GPU& gpu) { - command_list_hashes.resize(command_lists.size()); - - for (std::size_t index = 0; index < command_lists.size(); ++index) { - const CommandListHeader command_list_header = command_lists[index]; - std::vector<CommandHeader> command_headers(command_list_header.size); - gpu.MemoryManager().ReadBlockUnsafe(command_list_header.addr, command_headers.data(), - command_list_header.size * sizeof(u32)); - command_list_hashes[index] = - Common::CityHash64(reinterpret_cast<char*>(command_headers.data()), - command_list_header.size * sizeof(u32)); - } -} - DmaPusher::DmaPusher(Core::System& system, GPU& gpu) : gpu{gpu}, system{system} {} DmaPusher::~DmaPusher() = default; @@ -77,8 +63,7 @@ bool DmaPusher::Step() { dma_pushbuffer.pop(); } else { const CommandListHeader command_list_header{ - command_list.command_lists[dma_pushbuffer_subindex]}; - const u64 next_hash = command_list.command_list_hashes[dma_pushbuffer_subindex++]; + command_list.command_lists[dma_pushbuffer_subindex++]}; const GPUVAddr dma_get = command_list_header.addr; if (dma_pushbuffer_subindex >= command_list.command_lists.size()) { @@ -95,15 +80,6 @@ bool DmaPusher::Step() { command_headers.resize(command_list_header.size); gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), command_list_header.size * sizeof(u32)); - - // Integrity check - const u64 new_hash = Common::CityHash64(reinterpret_cast<char*>(command_headers.data()), - command_list_header.size * sizeof(u32)); - if (new_hash != next_hash) { - LOG_CRITICAL(HW_GPU, "CommandList at addr=0x{:X} is corrupt, skipping!", dma_get); - dma_pushbuffer.pop(); - return true; - } } for (std::size_t index = 0; index < command_headers.size();) { const CommandHeader& command_header = command_headers[index]; diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index 9d9a750d9..96ac267f7 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h @@ -90,10 +90,7 @@ struct CommandList final { explicit CommandList(std::vector<Tegra::CommandHeader>&& prefetch_command_list) : prefetch_command_list{std::move(prefetch_command_list)} {} - void RefreshIntegrityChecks(GPU& gpu); - std::vector<Tegra::CommandListHeader> command_lists; - std::vector<u64> command_list_hashes; std::vector<Tegra::CommandHeader> prefetch_command_list; }; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 57ebc785f..6287df633 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -124,6 +124,112 @@ void Maxwell3D::InitializeRegisterDefaults() { mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true; } +void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { + if (executing_macro == 0) { + // A macro call must begin by writing the macro method's register, not its argument. + ASSERT_MSG((method % 2) == 0, + "Can't start macro execution by writing to the ARGS register"); + executing_macro = method; + } + + macro_params.insert(macro_params.end(), base_start, base_start + amount); + + // Call the macro when there are no more parameters in the command buffer + if (is_last_call) { + CallMacroMethod(executing_macro, macro_params); + macro_params.clear(); + } +} + +u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { + // Keep track of the register value in shadow_state when requested. + const auto control = shadow_state.shadow_ram_control; + if (control == Regs::ShadowRamControl::Track || + control == Regs::ShadowRamControl::TrackWithFilter) { + shadow_state.reg_array[method] = argument; + return argument; + } + if (control == Regs::ShadowRamControl::Replay) { + return shadow_state.reg_array[method]; + } + return argument; +} + +void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) { + if (regs.reg_array[method] == argument) { + return; + } + regs.reg_array[method] = argument; + + for (const auto& table : dirty.tables) { + dirty.flags[table[method]] = true; + } +} + +void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, + bool is_last_call) { + switch (method) { + case MAXWELL3D_REG_INDEX(wait_for_idle): + return rasterizer->WaitForIdle(); + case MAXWELL3D_REG_INDEX(shadow_ram_control): + shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(nonshadow_argument); + return; + case MAXWELL3D_REG_INDEX(macros.data): + return macro_engine->AddCode(regs.macros.upload_address, argument); + case MAXWELL3D_REG_INDEX(macros.bind): + return ProcessMacroBind(argument); + case MAXWELL3D_REG_INDEX(firmware[4]): + return ProcessFirmwareCall4(); + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): + return StartCBData(method); + case MAXWELL3D_REG_INDEX(cb_bind[0]): + return ProcessCBBind(0); + case MAXWELL3D_REG_INDEX(cb_bind[1]): + return ProcessCBBind(1); + case MAXWELL3D_REG_INDEX(cb_bind[2]): + return ProcessCBBind(2); + case MAXWELL3D_REG_INDEX(cb_bind[3]): + return ProcessCBBind(3); + case MAXWELL3D_REG_INDEX(cb_bind[4]): + return ProcessCBBind(4); + case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): + return DrawArrays(); + case MAXWELL3D_REG_INDEX(clear_buffers): + return ProcessClearBuffers(); + case MAXWELL3D_REG_INDEX(query.query_get): + return ProcessQueryGet(); + case MAXWELL3D_REG_INDEX(condition.mode): + return ProcessQueryCondition(); + case MAXWELL3D_REG_INDEX(counter_reset): + return ProcessCounterReset(); + case MAXWELL3D_REG_INDEX(sync_info): + return ProcessSyncPoint(); + case MAXWELL3D_REG_INDEX(exec_upload): + return upload_state.ProcessExec(regs.exec_upload.linear != 0); + case MAXWELL3D_REG_INDEX(data_upload): + upload_state.ProcessData(argument, is_last_call); + if (is_last_call) { + OnMemoryWrite(); + } + return; + } +} + void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) { // Reset the current macro. executing_macro = 0; @@ -157,142 +263,16 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { // Methods after 0xE00 are special, they're actually triggers for some microcode that was // uploaded to the GPU during initialization. if (method >= MacroRegistersStart) { - // We're trying to execute a macro - if (executing_macro == 0) { - // A macro call must begin by writing the macro method's register, not its argument. - ASSERT_MSG((method % 2) == 0, - "Can't start macro execution by writing to the ARGS register"); - executing_macro = method; - } - - macro_params.push_back(method_argument); - - // Call the macro when there are no more parameters in the command buffer - if (is_last_call) { - CallMacroMethod(executing_macro, macro_params); - macro_params.clear(); - } + ProcessMacro(method, &method_argument, 1, is_last_call); return; } ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register, increase the size of the Regs structure"); - u32 arg = method_argument; - // Keep track of the register value in shadow_state when requested. - if (shadow_state.shadow_ram_control == Regs::ShadowRamControl::Track || - shadow_state.shadow_ram_control == Regs::ShadowRamControl::TrackWithFilter) { - shadow_state.reg_array[method] = arg; - } else if (shadow_state.shadow_ram_control == Regs::ShadowRamControl::Replay) { - arg = shadow_state.reg_array[method]; - } - - if (regs.reg_array[method] != arg) { - regs.reg_array[method] = arg; - - for (const auto& table : dirty.tables) { - dirty.flags[table[method]] = true; - } - } - - switch (method) { - case MAXWELL3D_REG_INDEX(wait_for_idle): { - rasterizer->WaitForIdle(); - break; - } - case MAXWELL3D_REG_INDEX(shadow_ram_control): { - shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(method_argument); - break; - } - case MAXWELL3D_REG_INDEX(macros.data): { - macro_engine->AddCode(regs.macros.upload_address, arg); - break; - } - case MAXWELL3D_REG_INDEX(macros.bind): { - ProcessMacroBind(arg); - break; - } - case MAXWELL3D_REG_INDEX(firmware[4]): { - ProcessFirmwareCall4(); - break; - } - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): { - StartCBData(method); - break; - } - case MAXWELL3D_REG_INDEX(cb_bind[0]): { - ProcessCBBind(0); - break; - } - case MAXWELL3D_REG_INDEX(cb_bind[1]): { - ProcessCBBind(1); - break; - } - case MAXWELL3D_REG_INDEX(cb_bind[2]): { - ProcessCBBind(2); - break; - } - case MAXWELL3D_REG_INDEX(cb_bind[3]): { - ProcessCBBind(3); - break; - } - case MAXWELL3D_REG_INDEX(cb_bind[4]): { - ProcessCBBind(4); - break; - } - case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): { - DrawArrays(); - break; - } - case MAXWELL3D_REG_INDEX(clear_buffers): { - ProcessClearBuffers(); - break; - } - case MAXWELL3D_REG_INDEX(query.query_get): { - ProcessQueryGet(); - break; - } - case MAXWELL3D_REG_INDEX(condition.mode): { - ProcessQueryCondition(); - break; - } - case MAXWELL3D_REG_INDEX(counter_reset): { - ProcessCounterReset(); - break; - } - case MAXWELL3D_REG_INDEX(sync_info): { - ProcessSyncPoint(); - break; - } - case MAXWELL3D_REG_INDEX(exec_upload): { - upload_state.ProcessExec(regs.exec_upload.linear != 0); - break; - } - case MAXWELL3D_REG_INDEX(data_upload): { - upload_state.ProcessData(arg, is_last_call); - if (is_last_call) { - OnMemoryWrite(); - } - break; - } - default: - break; - } + const u32 argument = ProcessShadowRam(method, method_argument); + ProcessDirtyRegisters(method, argument); + ProcessMethodCall(method, argument, method_argument, is_last_call); } void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, @@ -300,23 +280,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, // Methods after 0xE00 are special, they're actually triggers for some microcode that was // uploaded to the GPU during initialization. if (method >= MacroRegistersStart) { - // We're trying to execute a macro - if (executing_macro == 0) { - // A macro call must begin by writing the macro method's register, not its argument. - ASSERT_MSG((method % 2) == 0, - "Can't start macro execution by writing to the ARGS register"); - executing_macro = method; - } - - for (std::size_t i = 0; i < amount; i++) { - macro_params.push_back(base_start[i]); - } - - // Call the macro when there are no more parameters in the command buffer - if (amount == methods_pending) { - CallMacroMethod(executing_macro, macro_params); - macro_params.clear(); - } + ProcessMacro(method, base_start, amount, amount == methods_pending); return; } switch (method) { @@ -335,15 +299,14 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]): case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]): case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): { + case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): ProcessCBMultiData(method, base_start, amount); break; - } - default: { + default: for (std::size_t i = 0; i < amount; i++) { CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1); } - } + break; } } diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index bc289c55d..1cbe8fe67 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -1461,6 +1461,14 @@ public: private: void InitializeRegisterDefaults(); + void ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call); + + u32 ProcessShadowRam(u32 method, u32 argument); + + void ProcessDirtyRegisters(u32 method, u32 argument); + + void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call); + Core::System& system; MemoryManager& memory_manager; diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index a3c05d1b0..37d17efdc 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -32,31 +32,31 @@ struct Register { constexpr Register() = default; - constexpr Register(u64 value) : value(value) {} + constexpr Register(u64 value_) : value(value_) {} - constexpr operator u64() const { + [[nodiscard]] constexpr operator u64() const { return value; } template <typename T> - constexpr u64 operator-(const T& oth) const { + [[nodiscard]] constexpr u64 operator-(const T& oth) const { return value - oth; } template <typename T> - constexpr u64 operator&(const T& oth) const { + [[nodiscard]] constexpr u64 operator&(const T& oth) const { return value & oth; } - constexpr u64 operator&(const Register& oth) const { + [[nodiscard]] constexpr u64 operator&(const Register& oth) const { return value & oth.value; } - constexpr u64 operator~() const { + [[nodiscard]] constexpr u64 operator~() const { return ~value; } - u64 GetSwizzledIndex(u64 elem) const { + [[nodiscard]] u64 GetSwizzledIndex(u64 elem) const { elem = (value + elem) & 3; return (value & ~3) + elem; } @@ -75,7 +75,7 @@ enum class AttributeSize : u64 { union Attribute { Attribute() = default; - constexpr explicit Attribute(u64 value) : value(value) {} + constexpr explicit Attribute(u64 value_) : value(value_) {} enum class Index : u64 { LayerViewportPointSize = 6, @@ -107,7 +107,7 @@ union Attribute { BitField<31, 1, u64> patch; BitField<47, 3, AttributeSize> size; - bool IsPhysical() const { + [[nodiscard]] bool IsPhysical() const { return patch == 0 && element == 0 && static_cast<u64>(index.Value()) == 0; } } fmt20; @@ -124,7 +124,7 @@ union Attribute { union Sampler { Sampler() = default; - constexpr explicit Sampler(u64 value) : value(value) {} + constexpr explicit Sampler(u64 value_) : value(value_) {} enum class Index : u64 { Sampler_0 = 8, @@ -137,7 +137,7 @@ union Sampler { union Image { Image() = default; - constexpr explicit Image(u64 value) : value{value} {} + constexpr explicit Image(u64 value_) : value{value_} {} BitField<36, 13, u64> index; u64 value; @@ -505,14 +505,14 @@ struct IpaMode { IpaInterpMode interpolation_mode; IpaSampleMode sampling_mode; - bool operator==(const IpaMode& a) const { + [[nodiscard]] bool operator==(const IpaMode& a) const { return std::tie(interpolation_mode, sampling_mode) == std::tie(a.interpolation_mode, a.sampling_mode); } - bool operator!=(const IpaMode& a) const { + [[nodiscard]] bool operator!=(const IpaMode& a) const { return !operator==(a); } - bool operator<(const IpaMode& a) const { + [[nodiscard]] bool operator<(const IpaMode& a) const { return std::tie(interpolation_mode, sampling_mode) < std::tie(a.interpolation_mode, a.sampling_mode); } @@ -658,10 +658,10 @@ union Instruction { return *this; } - constexpr Instruction(u64 value) : value{value} {} + constexpr Instruction(u64 value_) : value{value_} {} constexpr Instruction(const Instruction& instr) : value(instr.value) {} - constexpr bool Bit(u64 offset) const { + [[nodiscard]] constexpr bool Bit(u64 offset) const { return ((value >> offset) & 1) != 0; } @@ -746,34 +746,34 @@ union Instruction { BitField<28, 8, u64> imm_lut28; BitField<48, 8, u64> imm_lut48; - u32 GetImmLut28() const { + [[nodiscard]] u32 GetImmLut28() const { return static_cast<u32>(imm_lut28); } - u32 GetImmLut48() const { + [[nodiscard]] u32 GetImmLut48() const { return static_cast<u32>(imm_lut48); } } lop3; - u16 GetImm20_16() const { + [[nodiscard]] u16 GetImm20_16() const { return static_cast<u16>(imm20_16); } - u32 GetImm20_19() const { + [[nodiscard]] u32 GetImm20_19() const { u32 imm{static_cast<u32>(imm20_19)}; imm <<= 12; imm |= negate_imm ? 0x80000000 : 0; return imm; } - u32 GetImm20_32() const { + [[nodiscard]] u32 GetImm20_32() const { return static_cast<u32>(imm20_32); } - s32 GetSignedImm20_20() const { - u32 immediate = static_cast<u32>(imm20_19 | (negate_imm << 19)); + [[nodiscard]] s32 GetSignedImm20_20() const { + const auto immediate = static_cast<u32>(imm20_19 | (negate_imm << 19)); // Sign extend the 20-bit value. - u32 mask = 1U << (20 - 1); + const auto mask = 1U << (20 - 1); return static_cast<s32>((immediate ^ mask) - mask); } } alu; @@ -857,7 +857,7 @@ union Instruction { BitField<56, 1, u64> second_negate; BitField<30, 9, u64> second; - u32 PackImmediates() const { + [[nodiscard]] u32 PackImmediates() const { // Immediates are half floats shifted. constexpr u32 imm_shift = 6; return static_cast<u32>((first << imm_shift) | (second << (16 + imm_shift))); @@ -1033,7 +1033,7 @@ union Instruction { BitField<28, 2, AtomicType> type; BitField<30, 22, s64> offset; - s32 GetImmediateOffset() const { + [[nodiscard]] s32 GetImmediateOffset() const { return static_cast<s32>(offset << 2); } } atoms; @@ -1215,7 +1215,7 @@ union Instruction { BitField<39, 4, u64> rounding; // H0, H1 extract for F16 missing BitField<41, 1, u64> selector; // Guessed as some games set it, TODO: reverse this value - F2fRoundingOp GetRoundingMode() const { + [[nodiscard]] F2fRoundingOp GetRoundingMode() const { constexpr u64 rounding_mask = 0x0B; return static_cast<F2fRoundingOp>(rounding.Value() & rounding_mask); } @@ -1239,15 +1239,15 @@ union Instruction { BitField<54, 1, u64> aoffi_flag; BitField<55, 3, TextureProcessMode> process_mode; - bool IsComponentEnabled(std::size_t component) const { - return ((1ull << component) & component_mask) != 0; + [[nodiscard]] bool IsComponentEnabled(std::size_t component) const { + return ((1ULL << component) & component_mask) != 0; } - TextureProcessMode GetTextureProcessMode() const { + [[nodiscard]] TextureProcessMode GetTextureProcessMode() const { return process_mode; } - bool UsesMiscMode(TextureMiscMode mode) const { + [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const { switch (mode) { case TextureMiscMode::DC: return dc_flag != 0; @@ -1271,15 +1271,15 @@ union Instruction { BitField<36, 1, u64> aoffi_flag; BitField<37, 3, TextureProcessMode> process_mode; - bool IsComponentEnabled(std::size_t component) const { + [[nodiscard]] bool IsComponentEnabled(std::size_t component) const { return ((1ULL << component) & component_mask) != 0; } - TextureProcessMode GetTextureProcessMode() const { + [[nodiscard]] TextureProcessMode GetTextureProcessMode() const { return process_mode; } - bool UsesMiscMode(TextureMiscMode mode) const { + [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const { switch (mode) { case TextureMiscMode::DC: return dc_flag != 0; @@ -1299,7 +1299,7 @@ union Instruction { BitField<31, 4, u64> component_mask; BitField<49, 1, u64> nodep_flag; - bool UsesMiscMode(TextureMiscMode mode) const { + [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const { switch (mode) { case TextureMiscMode::NODEP: return nodep_flag != 0; @@ -1309,7 +1309,7 @@ union Instruction { return false; } - bool IsComponentEnabled(std::size_t component) const { + [[nodiscard]] bool IsComponentEnabled(std::size_t component) const { return ((1ULL << component) & component_mask) != 0; } } txq; @@ -1321,11 +1321,11 @@ union Instruction { BitField<35, 1, u64> ndv_flag; BitField<49, 1, u64> nodep_flag; - bool IsComponentEnabled(std::size_t component) const { - return ((1ull << component) & component_mask) != 0; + [[nodiscard]] bool IsComponentEnabled(std::size_t component) const { + return ((1ULL << component) & component_mask) != 0; } - bool UsesMiscMode(TextureMiscMode mode) const { + [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const { switch (mode) { case TextureMiscMode::NDV: return (ndv_flag != 0); @@ -1347,7 +1347,7 @@ union Instruction { BitField<54, 2, u64> offset_mode; BitField<56, 2, u64> component; - bool UsesMiscMode(TextureMiscMode mode) const { + [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const { switch (mode) { case TextureMiscMode::NDV: return ndv_flag != 0; @@ -1373,7 +1373,7 @@ union Instruction { BitField<33, 2, u64> offset_mode; BitField<37, 2, u64> component; - bool UsesMiscMode(TextureMiscMode mode) const { + [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const { switch (mode) { case TextureMiscMode::NDV: return ndv_flag != 0; @@ -1399,7 +1399,7 @@ union Instruction { BitField<52, 2, u64> component; BitField<55, 1, u64> fp16_flag; - bool UsesMiscMode(TextureMiscMode mode) const { + [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const { switch (mode) { case TextureMiscMode::DC: return dc_flag != 0; @@ -1422,16 +1422,20 @@ union Instruction { BitField<53, 4, u64> texture_info; BitField<59, 1, u64> fp32_flag; - TextureType GetTextureType() const { + [[nodiscard]] TextureType GetTextureType() const { // The TEXS instruction has a weird encoding for the texture type. - if (texture_info == 0) + if (texture_info == 0) { return TextureType::Texture1D; - if (texture_info >= 1 && texture_info <= 9) + } + if (texture_info >= 1 && texture_info <= 9) { return TextureType::Texture2D; - if (texture_info >= 10 && texture_info <= 11) + } + if (texture_info >= 10 && texture_info <= 11) { return TextureType::Texture3D; - if (texture_info >= 12 && texture_info <= 13) + } + if (texture_info >= 12 && texture_info <= 13) { return TextureType::TextureCube; + } LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}", static_cast<u32>(texture_info.Value())); @@ -1439,7 +1443,7 @@ union Instruction { return TextureType::Texture1D; } - TextureProcessMode GetTextureProcessMode() const { + [[nodiscard]] TextureProcessMode GetTextureProcessMode() const { switch (texture_info) { case 0: case 2: @@ -1458,7 +1462,7 @@ union Instruction { return TextureProcessMode::None; } - bool UsesMiscMode(TextureMiscMode mode) const { + [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const { switch (mode) { case TextureMiscMode::DC: return (texture_info >= 4 && texture_info <= 6) || texture_info == 9; @@ -1470,16 +1474,16 @@ union Instruction { return false; } - bool IsArrayTexture() const { + [[nodiscard]] bool IsArrayTexture() const { // TEXS only supports Texture2D arrays. return texture_info >= 7 && texture_info <= 9; } - bool HasTwoDestinations() const { + [[nodiscard]] bool HasTwoDestinations() const { return gpr28.Value() != Register::ZeroIndex; } - bool IsComponentEnabled(std::size_t component) const { + [[nodiscard]] bool IsComponentEnabled(std::size_t component) const { static constexpr std::array<std::array<u32, 8>, 4> mask_lut{{ {}, {0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc}, @@ -1506,7 +1510,7 @@ union Instruction { BitField<54, 1, u64> cl; BitField<55, 1, u64> process_mode; - TextureProcessMode GetTextureProcessMode() const { + [[nodiscard]] TextureProcessMode GetTextureProcessMode() const { return process_mode == 0 ? TextureProcessMode::LZ : TextureProcessMode::LL; } } tld; @@ -1516,7 +1520,7 @@ union Instruction { BitField<53, 4, u64> texture_info; BitField<59, 1, u64> fp32_flag; - TextureType GetTextureType() const { + [[nodiscard]] TextureType GetTextureType() const { // The TLDS instruction has a weird encoding for the texture type. if (texture_info <= 1) { return TextureType::Texture1D; @@ -1535,13 +1539,14 @@ union Instruction { return TextureType::Texture1D; } - TextureProcessMode GetTextureProcessMode() const { - if (texture_info == 1 || texture_info == 5 || texture_info == 12) + [[nodiscard]] TextureProcessMode GetTextureProcessMode() const { + if (texture_info == 1 || texture_info == 5 || texture_info == 12) { return TextureProcessMode::LL; + } return TextureProcessMode::LZ; } - bool UsesMiscMode(TextureMiscMode mode) const { + [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const { switch (mode) { case TextureMiscMode::AOFFI: return texture_info == 12 || texture_info == 4; @@ -1555,7 +1560,7 @@ union Instruction { return false; } - bool IsArrayTexture() const { + [[nodiscard]] bool IsArrayTexture() const { // TEXS only supports Texture2D arrays. return texture_info == 8; } @@ -1567,7 +1572,7 @@ union Instruction { BitField<35, 1, u64> aoffi_flag; BitField<49, 1, u64> nodep_flag; - bool UsesMiscMode(TextureMiscMode mode) const { + [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const { switch (mode) { case TextureMiscMode::AOFFI: return aoffi_flag != 0; @@ -1591,7 +1596,7 @@ union Instruction { BitField<20, 3, StoreType> store_data_layout; BitField<20, 4, u64> component_mask_selector; - bool IsComponentEnabled(std::size_t component) const { + [[nodiscard]] bool IsComponentEnabled(std::size_t component) const { ASSERT(mode == SurfaceDataMode::P); constexpr u8 R = 0b0001; constexpr u8 G = 0b0010; @@ -1604,7 +1609,7 @@ union Instruction { return std::bitset<4>{mask.at(component_mask_selector)}.test(component); } - StoreType GetStoreDataLayout() const { + [[nodiscard]] StoreType GetStoreDataLayout() const { ASSERT(mode == SurfaceDataMode::D_BA); return store_data_layout; } @@ -1622,14 +1627,15 @@ union Instruction { BitField<20, 24, u64> target; BitField<5, 1, u64> constant_buffer; - s32 GetBranchTarget() const { + [[nodiscard]] s32 GetBranchTarget() const { // Sign extend the branch target offset - u32 mask = 1U << (24 - 1); - u32 value = static_cast<u32>(target); + const auto mask = 1U << (24 - 1); + const auto target_value = static_cast<u32>(target); + constexpr auto instruction_size = static_cast<s32>(sizeof(Instruction)); + // The branch offset is relative to the next instruction and is stored in bytes, so // divide it by the size of an instruction and add 1 to it. - return static_cast<s32>((value ^ mask) - mask) / static_cast<s32>(sizeof(Instruction)) + - 1; + return static_cast<s32>((target_value ^ mask) - mask) / instruction_size + 1; } } bra; @@ -1637,14 +1643,15 @@ union Instruction { BitField<20, 24, u64> target; BitField<5, 1, u64> constant_buffer; - s32 GetBranchExtend() const { + [[nodiscard]] s32 GetBranchExtend() const { // Sign extend the branch target offset - u32 mask = 1U << (24 - 1); - u32 value = static_cast<u32>(target); + const auto mask = 1U << (24 - 1); + const auto target_value = static_cast<u32>(target); + constexpr auto instruction_size = static_cast<s32>(sizeof(Instruction)); + // The branch offset is relative to the next instruction and is stored in bytes, so // divide it by the size of an instruction and add 1 to it. - return static_cast<s32>((value ^ mask) - mask) / static_cast<s32>(sizeof(Instruction)) + - 1; + return static_cast<s32>((target_value ^ mask) - mask) / instruction_size + 1; } } brx; @@ -1697,7 +1704,7 @@ union Instruction { BitField<50, 1, u64> is_op_b_register; BitField<51, 3, VmnmxOperation> operation; - VmnmxType SourceFormatA() const { + [[nodiscard]] VmnmxType SourceFormatA() const { switch (src_format_a) { case 0b11: return VmnmxType::Bits32; @@ -1708,7 +1715,7 @@ union Instruction { } } - VmnmxType SourceFormatB() const { + [[nodiscard]] VmnmxType SourceFormatB() const { switch (src_format_b) { case 0b11: return VmnmxType::Bits32; @@ -1739,7 +1746,7 @@ union Instruction { BitField<20, 14, u64> shifted_offset; BitField<34, 5, u64> index; - u64 GetOffset() const { + [[nodiscard]] u64 GetOffset() const { return shifted_offset * 4; } } cbuf34; @@ -1748,7 +1755,7 @@ union Instruction { BitField<20, 16, s64> offset; BitField<36, 5, u64> index; - s64 GetOffset() const { + [[nodiscard]] s64 GetOffset() const { return offset; } } cbuf36; @@ -1997,29 +2004,29 @@ public: /// Returns whether an opcode has an execution predicate field or not (ie, whether it can be /// conditionally executed). - static bool IsPredicatedInstruction(Id opcode) { + [[nodiscard]] static bool IsPredicatedInstruction(Id opcode) { // TODO(Subv): Add the rest of unpredicated instructions. return opcode != Id::SSY && opcode != Id::PBK; } class Matcher { public: - constexpr Matcher(const char* const name, u16 mask, u16 expected, Id id, Type type) - : name{name}, mask{mask}, expected{expected}, id{id}, type{type} {} + constexpr Matcher(const char* const name_, u16 mask_, u16 expected_, Id id_, Type type_) + : name{name_}, mask{mask_}, expected{expected_}, id{id_}, type{type_} {} - constexpr const char* GetName() const { + [[nodiscard]] constexpr const char* GetName() const { return name; } - constexpr u16 GetMask() const { + [[nodiscard]] constexpr u16 GetMask() const { return mask; } - constexpr Id GetId() const { + [[nodiscard]] constexpr Id GetId() const { return id; } - constexpr Type GetType() const { + [[nodiscard]] constexpr Type GetType() const { return type; } @@ -2028,7 +2035,7 @@ public: * @param instruction The instruction to test * @returns true if the given instruction matches. */ - constexpr bool Matches(u16 instruction) const { + [[nodiscard]] constexpr bool Matches(u16 instruction) const { return (instruction & mask) == expected; } @@ -2040,7 +2047,8 @@ public: Type type; }; - static std::optional<std::reference_wrapper<const Matcher>> Decode(Instruction instr) { + using DecodeResult = std::optional<std::reference_wrapper<const Matcher>>; + [[nodiscard]] static DecodeResult Decode(Instruction instr) { static const auto table{GetDecodeTable()}; const auto matches_instruction = [instr](const auto& matcher) { @@ -2062,7 +2070,7 @@ private: * A '0' in a bitstring indicates that a zero must be present at that bit position. * A '1' in a bitstring indicates that a one must be present at that bit position. */ - static constexpr auto GetMaskAndExpect(const char* const bitstring) { + [[nodiscard]] static constexpr auto GetMaskAndExpect(const char* const bitstring) { u16 mask = 0, expect = 0; for (std::size_t i = 0; i < opcode_bitsize; i++) { const std::size_t bit_position = opcode_bitsize - i - 1; @@ -2084,14 +2092,14 @@ private: public: /// Creates a matcher that can match and parse instructions based on bitstring. - static constexpr auto GetMatcher(const char* const bitstring, Id op, Type type, - const char* const name) { + [[nodiscard]] static constexpr auto GetMatcher(const char* const bitstring, Id op, + Type type, const char* const name) { const auto [mask, expected] = GetMaskAndExpect(bitstring); return Matcher(name, mask, expected, op, type); } }; - static std::vector<Matcher> GetDecodeTable() { + [[nodiscard]] static std::vector<Matcher> GetDecodeTable() { std::vector<Matcher> table = { #define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) INST("111000110011----", Id::KIL, Type::Flow, "KIL"), diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index cf5235a79..21410e125 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -17,11 +17,11 @@ #include "video_core/dma_pusher.h" using CacheAddr = std::uintptr_t; -inline CacheAddr ToCacheAddr(const void* host_ptr) { +[[nodiscard]] inline CacheAddr ToCacheAddr(const void* host_ptr) { return reinterpret_cast<CacheAddr>(host_ptr); } -inline u8* FromCacheAddr(CacheAddr cache_addr) { +[[nodiscard]] inline u8* FromCacheAddr(CacheAddr cache_addr) { return reinterpret_cast<u8*>(cache_addr); } @@ -149,13 +149,13 @@ public: u32 subchannel{}; u32 method_count{}; - bool IsLastCall() const { - return method_count <= 1; - } - MethodCall(u32 method, u32 argument, u32 subchannel = 0, u32 method_count = 0) : method(method), argument(argument), subchannel(subchannel), method_count(method_count) {} + + [[nodiscard]] bool IsLastCall() const { + return method_count <= 1; + } }; explicit GPU(Core::System& system, bool is_async, bool use_nvdec); @@ -179,10 +179,10 @@ public: virtual void OnCommandListEnd(); /// Request a host GPU memory flush from the CPU. - u64 RequestFlush(VAddr addr, std::size_t size); + [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); /// Obtains current flush request fence id. - u64 CurrentFlushRequestFence() const { + [[nodiscard]] u64 CurrentFlushRequestFence() const { return current_flush_fence.load(std::memory_order_relaxed); } @@ -190,48 +190,52 @@ public: void TickWork(); /// Returns a reference to the Maxwell3D GPU engine. - Engines::Maxwell3D& Maxwell3D(); + [[nodiscard]] Engines::Maxwell3D& Maxwell3D(); /// Returns a const reference to the Maxwell3D GPU engine. - const Engines::Maxwell3D& Maxwell3D() const; + [[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const; /// Returns a reference to the KeplerCompute GPU engine. - Engines::KeplerCompute& KeplerCompute(); + [[nodiscard]] Engines::KeplerCompute& KeplerCompute(); /// Returns a reference to the KeplerCompute GPU engine. - const Engines::KeplerCompute& KeplerCompute() const; + [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const; /// Returns a reference to the GPU memory manager. - Tegra::MemoryManager& MemoryManager(); + [[nodiscard]] Tegra::MemoryManager& MemoryManager(); /// Returns a const reference to the GPU memory manager. - const Tegra::MemoryManager& MemoryManager() const; + [[nodiscard]] const Tegra::MemoryManager& MemoryManager() const; /// Returns a reference to the GPU DMA pusher. - Tegra::DmaPusher& DmaPusher(); + [[nodiscard]] Tegra::DmaPusher& DmaPusher(); /// Returns a const reference to the GPU DMA pusher. - const Tegra::DmaPusher& DmaPusher() const; + [[nodiscard]] const Tegra::DmaPusher& DmaPusher() const; /// Returns a reference to the GPU CDMA pusher. - Tegra::CDmaPusher& CDmaPusher(); + [[nodiscard]] Tegra::CDmaPusher& CDmaPusher(); /// Returns a const reference to the GPU CDMA pusher. - const Tegra::CDmaPusher& CDmaPusher() const; + [[nodiscard]] const Tegra::CDmaPusher& CDmaPusher() const; - VideoCore::RendererBase& Renderer() { + /// Returns a reference to the underlying renderer. + [[nodiscard]] VideoCore::RendererBase& Renderer() { return *renderer; } - const VideoCore::RendererBase& Renderer() const { + /// Returns a const reference to the underlying renderer. + [[nodiscard]] const VideoCore::RendererBase& Renderer() const { return *renderer; } - VideoCore::ShaderNotify& ShaderNotify() { + /// Returns a reference to the shader notifier. + [[nodiscard]] VideoCore::ShaderNotify& ShaderNotify() { return *shader_notify; } - const VideoCore::ShaderNotify& ShaderNotify() const { + /// Returns a const reference to the shader notifier. + [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const { return *shader_notify; } @@ -243,23 +247,23 @@ public: void IncrementSyncPoint(u32 syncpoint_id); - u32 GetSyncpointValue(u32 syncpoint_id) const; + [[nodiscard]] u32 GetSyncpointValue(u32 syncpoint_id) const; void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value); - bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value); + [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value); - u64 GetTicks() const; + [[nodiscard]] u64 GetTicks() const; - std::unique_lock<std::mutex> LockSync() { + [[nodiscard]] std::unique_lock<std::mutex> LockSync() { return std::unique_lock{sync_mutex}; } - bool IsAsync() const { + [[nodiscard]] bool IsAsync() const { return is_async; } - bool UseNvdec() const { + [[nodiscard]] bool UseNvdec() const { return use_nvdec; } @@ -273,7 +277,7 @@ public: BitField<0, 1, FenceOperation> op; BitField<8, 24, u32> syncpoint_id; - static CommandHeader Build(FenceOperation op, u32 syncpoint_id) { + [[nodiscard]] static CommandHeader Build(FenceOperation op, u32 syncpoint_id) { FenceAction result{}; result.op.Assign(op); result.syncpoint_id.Assign(syncpoint_id); @@ -291,7 +295,7 @@ public: u32 address_high; u32 address_low; - GPUVAddr SemaphoreAddress() const { + [[nodiscard]] GPUVAddr SemaphoreAddress() const { return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); } @@ -374,7 +378,7 @@ private: u32 methods_pending); /// Determines where the method should be executed. - bool ExecuteMethodOnEngine(u32 method); + [[nodiscard]] bool ExecuteMethodOnEngine(u32 method); protected: Core::System& system; diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index b3e0919f8..27ef4c69a 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -32,7 +32,7 @@ using DiskResourceLoadCallback = std::function<void(LoadCallbackStage, std::size class RasterizerInterface { public: - virtual ~RasterizerInterface() {} + virtual ~RasterizerInterface() = default; /// Dispatches a draw invocation virtual void Draw(bool is_indexed, bool is_instanced) = 0; @@ -90,15 +90,16 @@ public: virtual void TickFrame() = 0; /// Attempt to use a faster method to perform a surface copy - virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, - const Tegra::Engines::Fermi2D::Regs::Surface& dst, - const Tegra::Engines::Fermi2D::Config& copy_config) { + [[nodiscard]] virtual bool AccelerateSurfaceCopy( + const Tegra::Engines::Fermi2D::Regs::Surface& src, + const Tegra::Engines::Fermi2D::Regs::Surface& dst, + const Tegra::Engines::Fermi2D::Config& copy_config) { return false; } /// Attempt to use a faster method to display the framebuffer to screen - virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, - u32 pixel_stride) { + [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, + VAddr framebuffer_addr, u32 pixel_stride) { return false; } @@ -110,12 +111,12 @@ public: const DiskResourceLoadCallback& callback) {} /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. - GuestDriverProfile& AccessGuestDriverProfile() { + [[nodiscard]] GuestDriverProfile& AccessGuestDriverProfile() { return guest_driver_profile; } /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. - const GuestDriverProfile& AccessGuestDriverProfile() const { + [[nodiscard]] const GuestDriverProfile& AccessGuestDriverProfile() const { return guest_driver_profile; } diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 5c650808b..51dde8eb5 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -38,7 +38,7 @@ public: virtual ~RendererBase(); /// Initialize the renderer - virtual bool Init() = 0; + [[nodiscard]] virtual bool Init() = 0; /// Shutdown the renderer virtual void ShutDown() = 0; @@ -49,43 +49,43 @@ public: // Getter/setter functions: // ------------------------ - f32 GetCurrentFPS() const { + [[nodiscard]] f32 GetCurrentFPS() const { return m_current_fps; } - int GetCurrentFrame() const { + [[nodiscard]] int GetCurrentFrame() const { return m_current_frame; } - RasterizerInterface& Rasterizer() { + [[nodiscard]] RasterizerInterface& Rasterizer() { return *rasterizer; } - const RasterizerInterface& Rasterizer() const { + [[nodiscard]] const RasterizerInterface& Rasterizer() const { return *rasterizer; } - Core::Frontend::GraphicsContext& Context() { + [[nodiscard]] Core::Frontend::GraphicsContext& Context() { return *context; } - const Core::Frontend::GraphicsContext& Context() const { + [[nodiscard]] const Core::Frontend::GraphicsContext& Context() const { return *context; } - Core::Frontend::EmuWindow& GetRenderWindow() { + [[nodiscard]] Core::Frontend::EmuWindow& GetRenderWindow() { return render_window; } - const Core::Frontend::EmuWindow& GetRenderWindow() const { + [[nodiscard]] const Core::Frontend::EmuWindow& GetRenderWindow() const { return render_window; } - RendererSettings& Settings() { + [[nodiscard]] RendererSettings& Settings() { return renderer_settings; } - const RendererSettings& Settings() const { + [[nodiscard]] const RendererSettings& Settings() const { return renderer_settings; } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 36bf92808..cfddbde5d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -1156,7 +1156,7 @@ void RasterizerOpenGL::SyncViewport() { flags[Dirty::ClipControl] = false; bool flip_y = false; - if (regs.viewport_transform[0].scale_y < 0.0) { + if (regs.viewport_transform[0].scale_y < 0.0f) { flip_y = !flip_y; } if (regs.screen_y_control.y_negate != 0) { @@ -1579,10 +1579,6 @@ void RasterizerOpenGL::SyncAlphaTest() { flags[Dirty::AlphaTest] = false; const auto& regs = maxwell3d.regs; - if (regs.alpha_test_enabled && regs.rt_control.count > 1) { - LOG_WARNING(Render_OpenGL, "Alpha testing with more than one render target is not tested"); - } - if (regs.alpha_test_enabled) { glEnable(GL_ALPHA_TEST); glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref); diff --git a/src/video_core/shader/async_shaders.cpp b/src/video_core/shader/async_shaders.cpp index 39cc3b869..6920afdf2 100644 --- a/src/video_core/shader/async_shaders.cpp +++ b/src/video_core/shader/async_shaders.cpp @@ -43,8 +43,8 @@ void AsyncShaders::AllocateWorkers() { // Create workers for (std::size_t i = 0; i < num_workers; i++) { context_list.push_back(emu_window.CreateSharedContext()); - worker_threads.push_back( - std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get())); + worker_threads.emplace_back(&AsyncShaders::ShaderCompilerThread, this, + context_list[i].get()); } } @@ -106,8 +106,7 @@ std::vector<AsyncShaders::Result> AsyncShaders::GetCompletedWork() { std::vector<Result> results; { std::unique_lock lock{completed_mutex}; - results.assign(std::make_move_iterator(finished_work.begin()), - std::make_move_iterator(finished_work.end())); + results = std::move(finished_work); finished_work.clear(); } return results; @@ -116,11 +115,10 @@ std::vector<AsyncShaders::Result> AsyncShaders::GetCompletedWork() { void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device, Tegra::Engines::ShaderType shader_type, u64 uid, std::vector<u64> code, std::vector<u64> code_b, - u32 main_offset, - VideoCommon::Shader::CompilerSettings compiler_settings, - const VideoCommon::Shader::Registry& registry, - VAddr cpu_addr) { - WorkerParams params{ + u32 main_offset, CompilerSettings compiler_settings, + const Registry& registry, VAddr cpu_addr) { + std::unique_lock lock(queue_mutex); + pending_queue.push({ .backend = device.UseAssemblyShaders() ? Backend::GLASM : Backend::OpenGL, .device = &device, .shader_type = shader_type, @@ -131,9 +129,7 @@ void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device, .compiler_settings = compiler_settings, .registry = registry, .cpu_address = cpu_addr, - }; - std::unique_lock lock(queue_mutex); - pending_queue.push(std::move(params)); + }); cv.notify_one(); } @@ -145,7 +141,8 @@ void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, std::vector<VkDescriptorSetLayoutBinding> bindings, Vulkan::SPIRVProgram program, Vulkan::GraphicsPipelineCacheKey key) { - WorkerParams params{ + std::unique_lock lock(queue_mutex); + pending_queue.push({ .backend = Backend::Vulkan, .pp_cache = pp_cache, .vk_device = &device, @@ -153,13 +150,10 @@ void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, .descriptor_pool = &descriptor_pool, .update_descriptor_queue = &update_descriptor_queue, .renderpass_cache = &renderpass_cache, - .bindings = bindings, - .program = program, + .bindings = std::move(bindings), + .program = std::move(program), .key = key, - }; - - std::unique_lock lock(queue_mutex); - pending_queue.push(std::move(params)); + }); cv.notify_one(); } diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp index 618d309d2..1ed4212ee 100644 --- a/src/video_core/shader/decode/image.cpp +++ b/src/video_core/shader/decode/image.cpp @@ -212,10 +212,10 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) { return 0; case TextureFormat::R8G24: if (component == 0) { - return 8; + return 24; } if (component == 1) { - return 24; + return 8; } return 0; case TextureFormat::R8G8: diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 8abb74d56..b16b54032 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -68,12 +68,12 @@ add_executable(yuzu configuration/configure_input_advanced.cpp configuration/configure_input_advanced.h configuration/configure_input_advanced.ui - configuration/configure_input_dialog.cpp - configuration/configure_input_dialog.h - configuration/configure_input_dialog.ui configuration/configure_input_player.cpp configuration/configure_input_player.h configuration/configure_input_player.ui + configuration/configure_input_profile_dialog.cpp + configuration/configure_input_profile_dialog.h + configuration/configure_input_profile_dialog.ui configuration/configure_motion_touch.cpp configuration/configure_motion_touch.h configuration/configure_motion_touch.ui @@ -105,9 +105,14 @@ add_executable(yuzu configuration/configure_ui.cpp configuration/configure_ui.h configuration/configure_ui.ui + configuration/configure_vibration.cpp + configuration/configure_vibration.h + configuration/configure_vibration.ui configuration/configure_web.cpp configuration/configure_web.h configuration/configure_web.ui + configuration/input_profiles.cpp + configuration/input_profiles.h debugger/console.cpp debugger/console.h debugger/profiler.cpp diff --git a/src/yuzu/aboutdialog.ui b/src/yuzu/aboutdialog.ui index f122ba39d..1b320630c 100644 --- a/src/yuzu/aboutdialog.ui +++ b/src/yuzu/aboutdialog.ui @@ -160,32 +160,12 @@ p, li { white-space: pre-wrap; } <signal>accepted()</signal> <receiver>AboutDialog</receiver> <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>248</x> - <y>254</y> - </hint> - <hint type="destinationlabel"> - <x>157</x> - <y>274</y> - </hint> - </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>AboutDialog</receiver> <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>316</x> - <y>260</y> - </hint> - <hint type="destinationlabel"> - <x>286</x> - <y>274</y> - </hint> - </hints> </connection> </connections> </ui> diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index c6fa3e4f6..8ecfec770 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> +#include <thread> #include "common/assert.h" #include "common/string_util.h" @@ -13,11 +14,16 @@ #include "core/hle/service/sm/sm.h" #include "ui_controller.h" #include "yuzu/applets/controller.h" -#include "yuzu/configuration/configure_input_dialog.h" +#include "yuzu/configuration/configure_input.h" +#include "yuzu/configuration/configure_input_profile_dialog.h" +#include "yuzu/configuration/configure_vibration.h" +#include "yuzu/configuration/input_profiles.h" #include "yuzu/main.h" namespace { +constexpr std::size_t HANDHELD_INDEX = 8; + constexpr std::array<std::array<bool, 4>, 8> led_patterns{{ {true, false, false, false}, {true, true, false, false}, @@ -106,7 +112,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( QWidget* parent, Core::Frontend::ControllerParameters parameters_, InputCommon::InputSubsystem* input_subsystem_) : QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()), - parameters(std::move(parameters_)), input_subsystem(input_subsystem_) { + parameters(std::move(parameters_)), input_subsystem{input_subsystem_}, + input_profiles(std::make_unique<InputProfiles>()) { ui->setupUi(this); player_widgets = { @@ -223,12 +230,22 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( } } + connect(ui->vibrationButton, &QPushButton::clicked, this, + &QtControllerSelectorDialog::CallConfigureVibrationDialog); + connect(ui->inputConfigButton, &QPushButton::clicked, this, - &QtControllerSelectorDialog::CallConfigureInputDialog); + &QtControllerSelectorDialog::CallConfigureInputProfileDialog); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QtControllerSelectorDialog::ApplyConfiguration); + // Enhancement: Check if the parameters have already been met before disconnecting controllers. + // If all the parameters are met AND only allows a single player, + // stop the constructor here as we do not need to continue. + if (CheckIfParametersMet() && parameters.enable_single_mode) { + return; + } + // If keep_controllers_connected is false, forcefully disconnect all controllers if (!parameters.keep_controllers_connected) { for (auto player : player_groupboxes) { @@ -236,58 +253,66 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( } } - CheckIfParametersMet(); - resize(0, 0); } QtControllerSelectorDialog::~QtControllerSelectorDialog() = default; -void QtControllerSelectorDialog::ApplyConfiguration() { - // Update the controller state once more, just to be sure they are properly applied. - for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { - UpdateControllerState(index); +int QtControllerSelectorDialog::exec() { + if (parameters_met && parameters.enable_single_mode) { + return QDialog::Accepted; } + return QDialog::exec(); +} - const bool pre_docked_mode = Settings::values.use_docked_mode; - Settings::values.use_docked_mode = ui->radioDocked->isChecked(); - OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); +void QtControllerSelectorDialog::ApplyConfiguration() { + const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); + Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); + OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue()); - Settings::values.vibration_enabled = ui->vibrationGroup->isChecked(); + Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); + Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); } void QtControllerSelectorDialog::LoadConfiguration() { for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { - const auto connected = Settings::values.players[index].connected || - (index == 0 && Settings::values.players[8].connected); + const auto connected = + Settings::values.players.GetValue()[index].connected || + (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected); player_groupboxes[index]->setChecked(connected); connected_controller_checkboxes[index]->setChecked(connected); emulated_controllers[index]->setCurrentIndex( - GetIndexFromControllerType(Settings::values.players[index].controller_type)); + GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type)); } - UpdateDockedState(Settings::values.players[8].connected); + UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected); - ui->vibrationGroup->setChecked(Settings::values.vibration_enabled); + ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); + ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); } -void QtControllerSelectorDialog::CallConfigureInputDialog() { - const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; - - ConfigureInputDialog dialog(this, max_supported_players, input_subsystem); +void QtControllerSelectorDialog::CallConfigureVibrationDialog() { + ConfigureVibration dialog(this); dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint); dialog.setWindowModality(Qt::WindowModal); - dialog.exec(); - dialog.ApplyConfiguration(); + if (dialog.exec() == QDialog::Accepted) { + dialog.ApplyConfiguration(); + } +} - LoadConfiguration(); - CheckIfParametersMet(); +void QtControllerSelectorDialog::CallConfigureInputProfileDialog() { + ConfigureInputProfileDialog dialog(this, input_subsystem, input_profiles.get()); + + dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | + Qt::WindowSystemMenuHint); + dialog.setWindowModality(Qt::WindowModal); + dialog.exec(); } -void QtControllerSelectorDialog::CheckIfParametersMet() { +bool QtControllerSelectorDialog::CheckIfParametersMet() { // Here, we check and validate the current configuration against all applicable parameters. const auto num_connected_players = static_cast<int>( std::count_if(player_groupboxes.begin(), player_groupboxes.end(), @@ -301,7 +326,7 @@ void QtControllerSelectorDialog::CheckIfParametersMet() { num_connected_players > max_supported_players) { parameters_met = false; ui->buttonBox->setEnabled(parameters_met); - return; + return parameters_met; } // Next, check against all connected controllers. @@ -326,18 +351,13 @@ void QtControllerSelectorDialog::CheckIfParametersMet() { return true; }(); - if (!all_controllers_compatible) { - parameters_met = false; - ui->buttonBox->setEnabled(parameters_met); - return; - } - - parameters_met = true; + parameters_met = all_controllers_compatible; ui->buttonBox->setEnabled(parameters_met); + return parameters_met; } void QtControllerSelectorDialog::SetSupportedControllers() { - const QString theme = [this] { + const QString theme = [] { if (QIcon::themeName().contains(QStringLiteral("dark"))) { return QStringLiteral("_dark"); } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { @@ -426,7 +446,7 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) } }(); - const QString theme = [this] { + const QString theme = [] { if (QIcon::themeName().contains(QStringLiteral("dark"))) { return QStringLiteral("_dark"); } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { @@ -441,32 +461,48 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) } void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { - auto& player = Settings::values.players[player_index]; + auto& player = Settings::values.players.GetValue()[player_index]; - player.controller_type = + const auto controller_type = GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()); - player.connected = player_groupboxes[player_index]->isChecked(); + const auto player_connected = player_groupboxes[player_index]->isChecked() && + controller_type != Settings::ControllerType::Handheld; - // Player 2-8 - if (player_index != 0) { - UpdateController(player.controller_type, player_index, player.connected); + if (player.controller_type == controller_type && player.connected == player_connected) { + // Set vibration devices in the event that the input device has changed. + ConfigureVibration::SetVibrationDevices(player_index); return; } - // Player 1 and Handheld - auto& handheld = Settings::values.players[8]; - // If Handheld is selected, copy all the settings from Player 1 to Handheld. - if (player.controller_type == Settings::ControllerType::Handheld) { - handheld = player; - handheld.connected = player_groupboxes[player_index]->isChecked(); - player.connected = false; // Disconnect Player 1 - } else { - player.connected = player_groupboxes[player_index]->isChecked(); - handheld.connected = false; // Disconnect Handheld + // Disconnect the controller first. + UpdateController(controller_type, player_index, false); + + player.controller_type = controller_type; + player.connected = player_connected; + + ConfigureVibration::SetVibrationDevices(player_index); + + // Handheld + if (player_index == 0) { + auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; + if (controller_type == Settings::ControllerType::Handheld) { + handheld = player; + } + handheld.connected = player_groupboxes[player_index]->isChecked() && + controller_type == Settings::ControllerType::Handheld; + UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected); } - UpdateController(player.controller_type, player_index, player.connected); - UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected); + if (!player.connected) { + return; + } + + // This emulates a delay between disconnecting and reconnecting controllers as some games + // do not respond to a change in controller type if it was instantaneous. + using namespace std::chrono_literals; + std::this_thread::sleep_for(20ms); + + UpdateController(controller_type, player_index, player_connected); } void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { @@ -520,8 +556,8 @@ void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) { ui->radioDocked->setEnabled(!is_handheld); ui->radioUndocked->setEnabled(!is_handheld); - ui->radioDocked->setChecked(Settings::values.use_docked_mode); - ui->radioUndocked->setChecked(!Settings::values.use_docked_mode); + ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue()); + ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue()); // Also force into undocked mode if the controller type is handheld. if (is_handheld) { @@ -564,8 +600,8 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() { for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) { // Disconnect any unsupported players here and disable or hide them if applicable. - Settings::values.players[index].connected = false; - UpdateController(Settings::values.players[index].controller_type, index, false); + Settings::values.players.GetValue()[index].connected = false; + UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false); // Hide the player widgets when max_supported_controllers is less than or equal to 4. if (max_supported_players <= 4) { player_widgets[index]->hide(); diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h index 729ecc831..4344e1dd0 100644 --- a/src/yuzu/applets/controller.h +++ b/src/yuzu/applets/controller.h @@ -16,6 +16,8 @@ class QDialogButtonBox; class QGroupBox; class QLabel; +class InputProfiles; + namespace InputCommon { class InputSubsystem; } @@ -33,6 +35,8 @@ public: InputCommon::InputSubsystem* input_subsystem_); ~QtControllerSelectorDialog() override; + int exec() override; + private: // Applies the current configuration. void ApplyConfiguration(); @@ -40,12 +44,15 @@ private: // Loads the current input configuration into the frontend applet. void LoadConfiguration(); - // Initializes the "Configure Input" Dialog. - void CallConfigureInputDialog(); + // Initializes the "Configure Vibration" Dialog. + void CallConfigureVibrationDialog(); - // Checks the current configuration against the given parameters and - // sets the value of parameters_met. - void CheckIfParametersMet(); + // Initializes the "Create Input Profile" Dialog. + void CallConfigureInputProfileDialog(); + + // Checks the current configuration against the given parameters. + // This sets and returns the value of parameters_met. + bool CheckIfParametersMet(); // Sets the controller icons for "Supported Controller Types". void SetSupportedControllers(); @@ -78,6 +85,8 @@ private: InputCommon::InputSubsystem* input_subsystem; + std::unique_ptr<InputProfiles> input_profiles; + // This is true if and only if all parameters are met. Otherwise, this is false. // This determines whether the "OK" button can be clicked to exit the applet. bool parameters_met{false}; diff --git a/src/yuzu/applets/controller.ui b/src/yuzu/applets/controller.ui index c4108a979..c8cb6bcf3 100644 --- a/src/yuzu/applets/controller.ui +++ b/src/yuzu/applets/controller.ui @@ -1217,9 +1217,6 @@ </item> <item> <widget class="QComboBox" name="comboPlayer3Emulated"> - <property name="editable"> - <bool>false</bool> - </property> <item> <property name="text"> <string>Pro Controller</string> @@ -2279,7 +2276,7 @@ <number>6</number> </property> <property name="leftMargin"> - <number>6</number> + <number>8</number> </property> <property name="topMargin"> <number>6</number> @@ -2332,30 +2329,24 @@ <number>3</number> </property> <item> - <widget class="QSpinBox" name="vibrationSpin"> + <widget class="QPushButton" name="vibrationButton"> <property name="minimumSize"> <size> - <width>65</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>65</width> + <width>68</width> <height>16777215</height> </size> </property> - <property name="suffix"> - <string>%</string> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>200</number> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> </property> - <property name="value"> - <number>100</number> + <property name="text"> + <string>Configure</string> </property> </widget> </item> @@ -2387,18 +2378,18 @@ <widget class="QPushButton" name="motionButton"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Configure</string> @@ -2411,7 +2402,7 @@ <item> <widget class="QGroupBox" name="inputConfigGroup"> <property name="title"> - <string>Input Config</string> + <string>Profiles</string> </property> <layout class="QHBoxLayout" name="horizontalLayout_7"> <property name="leftMargin"> @@ -2430,15 +2421,15 @@ <widget class="QPushButton" name="inputConfigButton"> <property name="maximumSize"> <size> - <width>65</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> - <string>Open</string> + <string>Create</string> </property> </widget> </item> @@ -2657,16 +2648,6 @@ <signal>accepted()</signal> <receiver>QtControllerSelectorDialog</receiver> <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>20</x> - <y>20</y> - </hint> - <hint type="destinationlabel"> - <x>20</x> - <y>20</y> - </hint> - </hints> </connection> </connections> </ui> diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 408eac2b7..f0338cf7a 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -10,6 +10,7 @@ #include <QMessageBox> #include <QPainter> #include <QScreen> +#include <QString> #include <QStringList> #include <QWindow> @@ -301,6 +302,12 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, this->setMouseTracking(true); connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); + connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, + Qt::QueuedConnection); +} + +void GRenderWindow::ExecuteProgram(std::size_t program_index) { + emit ExecuteProgramSignal(program_index); } GRenderWindow::~GRenderWindow() { @@ -381,7 +388,12 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { } void GRenderWindow::mousePressEvent(QMouseEvent* event) { - // touch input is handled in TouchBeginEvent + if (!Settings::values.touchscreen.enabled) { + input_subsystem->GetKeyboard()->PressKey(event->button()); + return; + } + + // Touch input is handled in TouchBeginEvent if (event->source() == Qt::MouseEventSynthesizedBySystem) { return; } @@ -397,7 +409,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { } void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { - // touch input is handled in TouchUpdateEvent + // Touch input is handled in TouchUpdateEvent if (event->source() == Qt::MouseEventSynthesizedBySystem) { return; } @@ -410,7 +422,12 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { } void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { - // touch input is handled in TouchEndEvent + if (!Settings::values.touchscreen.enabled) { + input_subsystem->GetKeyboard()->ReleaseKey(event->button()); + return; + } + + // Touch input is handled in TouchEndEvent if (event->source() == Qt::MouseEventSynthesizedBySystem) { return; } @@ -603,19 +620,33 @@ bool GRenderWindow::LoadOpenGL() { auto context = CreateSharedContext(); auto scope = context->Acquire(); if (!gladLoadGL()) { - QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), - tr("Your GPU may not support OpenGL 4.3, or you do not have the " - "latest graphics driver.")); + QMessageBox::warning( + this, tr("Error while initializing OpenGL!"), + tr("Your GPU may not support OpenGL, or you do not have the latest graphics driver.")); + return false; + } + + const QString renderer = + QString::fromUtf8(reinterpret_cast<const char*>(glGetString(GL_RENDERER))); + + if (!GLAD_GL_VERSION_4_3) { + LOG_ERROR(Frontend, "GPU does not support OpenGL 4.3: {}", renderer.toStdString()); + QMessageBox::warning(this, tr("Error while initializing OpenGL 4.3!"), + tr("Your GPU may not support OpenGL 4.3, or you do not have the " + "latest graphics driver.<br><br>GL Renderer:<br>%1") + .arg(renderer)); return false; } QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); if (!unsupported_gl_extensions.empty()) { - QMessageBox::critical( + QMessageBox::warning( this, tr("Error while initializing OpenGL!"), tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you " - "have the latest graphics driver.<br><br>Unsupported extensions:<br>") + - unsupported_gl_extensions.join(QStringLiteral("<br>"))); + "have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported " + "extensions:<br>%2") + .arg(renderer) + .arg(unsupported_gl_extensions.join(QStringLiteral("<br>")))); return false; } return true; @@ -645,8 +676,13 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const { if (!GLAD_GL_ARB_depth_buffer_float) unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); - for (const QString& ext : unsupported_ext) - LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); + if (!unsupported_ext.empty()) { + LOG_ERROR(Frontend, "GPU does not support all required extensions: {}", + glGetString(GL_RENDERER)); + } + for (const QString& ext : unsupported_ext) { + LOG_ERROR(Frontend, "Unsupported GL extension: {}", ext.toStdString()); + } return unsupported_ext; } diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index ca35cf831..503b4f89e 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -166,6 +166,12 @@ public: std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; + /** + * Instructs the window to re-launch the application using the specified program_index. + * @param program_index Specifies the index within the application of the program to launch. + */ + void ExecuteProgram(std::size_t program_index); + public slots: void OnEmulationStarting(EmuThread* emu_thread); void OnEmulationStopping(); @@ -175,6 +181,7 @@ signals: /// Emitted when the window is closed void Closed(); void FirstFrameDisplayed(); + void ExecuteProgramSignal(std::size_t program_index); private: void TouchBeginEvent(const QTouchEvent* event); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 1ce62e4a6..3c423a271 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -5,6 +5,7 @@ #include <array> #include <QKeySequence> #include <QSettings> +#include "common/common_paths.h" #include "common/file_util.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/hid/controllers/npad.h" @@ -14,14 +15,10 @@ namespace FS = Common::FS; -Config::Config(const std::string& config_file, bool is_global) { - // TODO: Don't hardcode the path; let the frontend decide where to put the config files. - qt_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + config_file; - FS::CreateFullPath(qt_config_loc); - qt_config = - std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); - global = is_global; - Reload(); +Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) { + global = config_type == ConfigType::GlobalConfig; + + Initialize(config_name); } Config::~Config() { @@ -242,84 +239,152 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{ }}; // clang-format on -void Config::ReadPlayerValues() { - for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { - auto& player = Settings::values.players[p]; +void Config::Initialize(const std::string& config_name) { + switch (type) { + case ConfigType::GlobalConfig: + qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", FS::GetUserPath(FS::UserPath::ConfigDir), + config_name); + FS::CreateFullPath(qt_config_loc); + qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), + QSettings::IniFormat); + Reload(); + break; + case ConfigType::PerGameConfig: + qt_config_loc = fmt::format("{}custom" DIR_SEP "{}.ini", + FS::GetUserPath(FS::UserPath::ConfigDir), config_name); + FS::CreateFullPath(qt_config_loc); + qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), + QSettings::IniFormat); + Reload(); + break; + case ConfigType::InputProfile: + qt_config_loc = fmt::format("{}input" DIR_SEP "{}.ini", + FS::GetUserPath(FS::UserPath::ConfigDir), config_name); + FS::CreateFullPath(qt_config_loc); + qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), + QSettings::IniFormat); + break; + } +} + +void Config::ReadPlayerValue(std::size_t player_index) { + const QString player_prefix = [this, player_index] { + if (type == ConfigType::InputProfile) { + return QString{}; + } else { + return QStringLiteral("player_%1_").arg(player_index); + } + }(); + + auto& player = Settings::values.players.GetValue()[player_index]; + + if (player_prefix.isEmpty()) { + const auto controller = static_cast<Settings::ControllerType>( + qt_config + ->value(QStringLiteral("%1type").arg(player_prefix), + static_cast<u8>(Settings::ControllerType::ProController)) + .toUInt()); + if (controller == Settings::ControllerType::LeftJoycon || + controller == Settings::ControllerType::RightJoycon) { + player.controller_type = controller; + } + } else { player.connected = - ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool(); + ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0) + .toBool(); player.controller_type = static_cast<Settings::ControllerType>( qt_config - ->value(QStringLiteral("player_%1_type").arg(p), + ->value(QStringLiteral("%1type").arg(player_prefix), static_cast<u8>(Settings::ControllerType::ProController)) .toUInt()); + player.vibration_enabled = + qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true) + .toBool(); + + player.vibration_strength = + qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100) + .toInt(); + player.body_color_left = qt_config - ->value(QStringLiteral("player_%1_body_color_left").arg(p), + ->value(QStringLiteral("%1body_color_left").arg(player_prefix), Settings::JOYCON_BODY_NEON_BLUE) .toUInt(); - player.body_color_right = qt_config - ->value(QStringLiteral("player_%1_body_color_right").arg(p), - Settings::JOYCON_BODY_NEON_RED) - .toUInt(); - player.button_color_left = qt_config - ->value(QStringLiteral("player_%1_button_color_left").arg(p), - Settings::JOYCON_BUTTONS_NEON_BLUE) - .toUInt(); + player.body_color_right = + qt_config + ->value(QStringLiteral("%1body_color_right").arg(player_prefix), + Settings::JOYCON_BODY_NEON_RED) + .toUInt(); + player.button_color_left = + qt_config + ->value(QStringLiteral("%1button_color_left").arg(player_prefix), + Settings::JOYCON_BUTTONS_NEON_BLUE) + .toUInt(); player.button_color_right = qt_config - ->value(QStringLiteral("player_%1_button_color_right").arg(p), + ->value(QStringLiteral("%1button_color_right").arg(player_prefix), Settings::JOYCON_BUTTONS_NEON_RED) .toUInt(); + } - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = - InputCommon::GenerateKeyboardParam(default_buttons[i]); - auto& player_buttons = player.buttons[i]; - - player_buttons = qt_config - ->value(QStringLiteral("player_%1_").arg(p) + - QString::fromUtf8(Settings::NativeButton::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_buttons.empty()) { - player_buttons = default_param; - } + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& player_buttons = player.buttons[i]; + + player_buttons = qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeButton::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_buttons.empty()) { + player_buttons = default_param; } + } - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = - InputCommon::GenerateKeyboardParam(default_motions[i]); - auto& player_motions = player.motions[i]; - - player_motions = qt_config - ->value(QStringLiteral("player_%1_").arg(p) + - QString::fromUtf8(Settings::NativeMotion::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_motions.empty()) { - player_motions = default_param; - } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + auto& player_analogs = player.analogs[i]; + + player_analogs = qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeAnalog::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_analogs.empty()) { + player_analogs = default_param; } + } - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_stick_mod[i], 0.5f); - auto& player_analogs = player.analogs[i]; - - player_analogs = qt_config - ->value(QStringLiteral("player_%1_").arg(p) + - QString::fromUtf8(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_analogs.empty()) { - player_analogs = default_param; - } + for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) { + auto& player_vibrations = player.vibrations[i]; + + player_vibrations = + qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeVibration::mapping[i]), + QString{}) + .toString() + .toStdString(); + } + + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + auto& player_motions = player.motions[i]; + + player_motions = qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeMotion::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_motions.empty()) { + player_motions = default_param; } } } @@ -436,18 +501,21 @@ void Config::ReadAudioValues() { void Config::ReadControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); - ReadPlayerValues(); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + ReadPlayerValue(p); + } ReadDebugValues(); ReadKeyboardValues(); ReadMouseValues(); ReadTouchscreenValues(); ReadMotionTouchValues(); - Settings::values.vibration_enabled = - ReadSetting(QStringLiteral("vibration_enabled"), true).toBool(); - Settings::values.motion_enabled = ReadSetting(QStringLiteral("motion_enabled"), true).toBool(); - Settings::values.use_docked_mode = - ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); + ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false); + ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"), + true); + ReadSettingGlobal(Settings::values.enable_accurate_vibrations, + QStringLiteral("enable_accurate_vibrations"), false); + ReadSettingGlobal(Settings::values.motion_enabled, QStringLiteral("motion_enabled"), true); qt_config->endGroup(); } @@ -581,6 +649,8 @@ void Config::ReadDebuggingValues() { Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool(); Settings::values.disable_macro_jit = ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool(); + Settings::values.extended_logging = + ReadSetting(QStringLiteral("extended_logging"), false).toBool(); qt_config->endGroup(); } @@ -920,49 +990,64 @@ void Config::ReadValues() { ReadSystemValues(); } -void Config::SavePlayerValues() { - for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { - const auto& player = Settings::values.players[p]; +void Config::SavePlayerValue(std::size_t player_index) { + const QString player_prefix = [this, player_index] { + if (type == ConfigType::InputProfile) { + return QString{}; + } else { + return QStringLiteral("player_%1_").arg(player_index); + } + }(); - WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false); - WriteSetting(QStringLiteral("player_%1_type").arg(p), - static_cast<u8>(player.controller_type), - static_cast<u8>(Settings::ControllerType::ProController)); + const auto& player = Settings::values.players.GetValue()[player_index]; - WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left, + WriteSetting(QStringLiteral("%1type").arg(player_prefix), + static_cast<u8>(player.controller_type), + static_cast<u8>(Settings::ControllerType::ProController)); + + if (!player_prefix.isEmpty()) { + WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, false); + WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix), + player.vibration_enabled, true); + WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix), + player.vibration_strength, 100); + WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left, Settings::JOYCON_BODY_NEON_BLUE); - WriteSetting(QStringLiteral("player_%1_body_color_right").arg(p), player.body_color_right, - Settings::JOYCON_BODY_NEON_RED); - WriteSetting(QStringLiteral("player_%1_button_color_left").arg(p), player.button_color_left, - Settings::JOYCON_BUTTONS_NEON_BLUE); - WriteSetting(QStringLiteral("player_%1_button_color_right").arg(p), + WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix), + player.body_color_right, Settings::JOYCON_BODY_NEON_RED); + WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix), + player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE); + WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix), player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED); + } - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = - InputCommon::GenerateKeyboardParam(default_buttons[i]); - WriteSetting(QStringLiteral("player_%1_").arg(p) + - QString::fromStdString(Settings::NativeButton::mapping[i]), - QString::fromStdString(player.buttons[i]), - QString::fromStdString(default_param)); - } - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = - InputCommon::GenerateKeyboardParam(default_motions[i]); - WriteSetting(QStringLiteral("player_%1_").arg(p) + - QString::fromStdString(Settings::NativeMotion::mapping[i]), - QString::fromStdString(player.motions[i]), - QString::fromStdString(default_param)); - } - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_stick_mod[i], 0.5f); - WriteSetting(QStringLiteral("player_%1_").arg(p) + - QString::fromStdString(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(player.analogs[i]), - QString::fromStdString(default_param)); - } + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeButton::mapping[i]), + QString::fromStdString(player.buttons[i]), + QString::fromStdString(default_param)); + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeAnalog::mapping[i]), + QString::fromStdString(player.analogs[i]), + QString::fromStdString(default_param)); + } + for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) { + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeVibration::mapping[i]), + QString::fromStdString(player.vibrations[i]), QString{}); + } + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeMotion::mapping[i]), + QString::fromStdString(player.motions[i]), + QString::fromStdString(default_param)); } } @@ -1087,14 +1172,20 @@ void Config::SaveAudioValues() { void Config::SaveControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); - SavePlayerValues(); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + SavePlayerValue(p); + } SaveDebugValues(); SaveMouseValues(); SaveTouchscreenValues(); SaveMotionTouchValues(); - WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true); - WriteSetting(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true); + WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); + WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, + true); + WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"), + Settings::values.enable_accurate_vibrations, false); + WriteSettingGlobal(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true); WriteSetting(QStringLiteral("motion_device"), QString::fromStdString(Settings::values.motion_device), QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); @@ -1102,7 +1193,6 @@ void Config::SaveControlValues() { QString::fromStdString(Settings::values.touch_device), QStringLiteral("engine:emu_window")); WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); - WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); qt_config->endGroup(); } @@ -1515,3 +1605,19 @@ void Config::Save() { Settings::Sanitize(); SaveValues(); } + +void Config::ReadControlPlayerValue(std::size_t player_index) { + qt_config->beginGroup(QStringLiteral("Controls")); + ReadPlayerValue(player_index); + qt_config->endGroup(); +} + +void Config::SaveControlPlayerValue(std::size_t player_index) { + qt_config->beginGroup(QStringLiteral("Controls")); + SavePlayerValue(player_index); + qt_config->endGroup(); +} + +const std::string& Config::GetConfigFilePath() const { + return qt_config_loc; +} diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 5d8e45d78..8a600e19d 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -16,12 +16,24 @@ class QSettings; class Config { public: - explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true); + enum class ConfigType { + GlobalConfig, + PerGameConfig, + InputProfile, + }; + + explicit Config(const std::string& config_name = "qt-config", + ConfigType config_type = ConfigType::GlobalConfig); ~Config(); void Reload(); void Save(); + void ReadControlPlayerValue(std::size_t player_index); + void SaveControlPlayerValue(std::size_t player_index); + + const std::string& GetConfigFilePath() const; + static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; @@ -33,8 +45,10 @@ public: static const std::array<UISettings::Shortcut, 16> default_hotkeys; private: + void Initialize(const std::string& config_name); + void ReadValues(); - void ReadPlayerValues(); + void ReadPlayerValue(std::size_t player_index); void ReadDebugValues(); void ReadKeyboardValues(); void ReadMouseValues(); @@ -62,7 +76,7 @@ private: void ReadWebServiceValues(); void SaveValues(); - void SavePlayerValues(); + void SavePlayerValue(std::size_t player_index); void SaveDebugValues(); void SaveMouseValues(); void SaveTouchscreenValues(); @@ -111,9 +125,9 @@ private: void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global, const QVariant& default_value); + ConfigType type; std::unique_ptr<QSettings> qt_config; std::string qt_config_loc; - bool global; }; diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index fcf42cdcb..f92c3aff3 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -275,32 +275,12 @@ <signal>accepted()</signal> <receiver>ConfigureDialog</receiver> <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>220</x> - <y>380</y> - </hint> - <hint type="destinationlabel"> - <x>220</x> - <y>200</y> - </hint> - </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>ConfigureDialog</receiver> <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>220</x> - <y>380</y> - </hint> - <hint type="destinationlabel"> - <x>220</x> - <y>200</y> - </hint> - </hints> </connection> </connections> </ui> diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 2bfe2c306..027099ab7 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -41,6 +41,7 @@ void ConfigureDebug::SetConfiguration() { ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit); + ui->extended_logging->setChecked(Settings::values.extended_logging); } void ConfigureDebug::ApplyConfiguration() { @@ -53,6 +54,7 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.quest_flag = ui->quest_flag->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; filter.ParseFilterString(Settings::values.log_filter); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 9d6feb9f7..6f94fe304 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -90,7 +90,7 @@ <item> <widget class="QCheckBox" name="toggle_console"> <property name="text"> - <string>Show Log Console (Windows Only)</string> + <string>Show Log in Console</string> </property> </widget> </item> @@ -103,6 +103,34 @@ </item> </layout> </item> + <item> + <widget class="QCheckBox" name="extended_logging"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="toolTip"> + <string>When checked, the max size of the log increases from 100 MB to 1 GB</string> + </property> + <property name="text"> + <string>Enable Extended Logging</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> @@ -115,7 +143,7 @@ <item> <layout class="QHBoxLayout" name="horizontalLayout_4"> <item> - <widget class="QLabel" name="label_3"> + <widget class="QLabel" name="label_4"> <property name="text"> <string>Arguments String</string> </property> @@ -140,8 +168,8 @@ <property name="enabled"> <bool>true</bool> </property> - <property name="whatsThis"> - <string>When checked, the graphics API enters in a slower debugging mode</string> + <property name="toolTip"> + <string>When checked, the graphics API enters a slower debugging mode</string> </property> <property name="text"> <string>Enable Graphics Debugging</string> @@ -153,8 +181,8 @@ <property name="enabled"> <bool>true</bool> </property> - <property name="whatsThis"> - <string>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</string> + <property name="toolTip"> + <string>When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower</string> </property> <property name="text"> <string>Disable Macro JIT</string> @@ -169,7 +197,7 @@ <property name="title"> <string>Dump</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_6"> + <layout class="QVBoxLayout" name="verticalLayout_7"> <item> <widget class="QCheckBox" name="reporting_services"> <property name="text"> @@ -178,7 +206,7 @@ </widget> </item> <item> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="label_5"> <property name="font"> <font> <italic>true</italic> @@ -200,7 +228,7 @@ <property name="title"> <string>Advanced</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_7"> + <layout class="QVBoxLayout" name="verticalLayout_8"> <item> <widget class="QCheckBox" name="quest_flag"> <property name="text"> diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp index 0097c9a29..a878ef9c6 100644 --- a/src/yuzu/configuration/configure_debug_controller.cpp +++ b/src/yuzu/configuration/configure_debug_controller.cpp @@ -4,11 +4,14 @@ #include "ui_configure_debug_controller.h" #include "yuzu/configuration/configure_debug_controller.h" +#include "yuzu/configuration/configure_input_player.h" ConfigureDebugController::ConfigureDebugController(QWidget* parent, - InputCommon::InputSubsystem* input_subsystem) + InputCommon::InputSubsystem* input_subsystem, + InputProfiles* profiles) : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()), - debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, true)) { + debug_controller( + new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, true)) { ui->setupUi(this); ui->controllerLayout->addWidget(debug_controller); diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h index 34dcf705f..b4f53fad5 100644 --- a/src/yuzu/configuration/configure_debug_controller.h +++ b/src/yuzu/configuration/configure_debug_controller.h @@ -6,10 +6,13 @@ #include <memory> #include <QDialog> -#include "yuzu/configuration/configure_input_player.h" class QPushButton; +class ConfigureInputPlayer; + +class InputProfiles; + namespace InputCommon { class InputSubsystem; } @@ -22,8 +25,8 @@ class ConfigureDebugController : public QDialog { Q_OBJECT public: - explicit ConfigureDebugController(QWidget* parent, - InputCommon::InputSubsystem* input_subsystem); + explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem, + InputProfiles* profiles); ~ConfigureDebugController() override; void ApplyConfiguration(); diff --git a/src/yuzu/configuration/configure_debug_controller.ui b/src/yuzu/configuration/configure_debug_controller.ui index a95ed50ff..7b7e6582c 100644 --- a/src/yuzu/configuration/configure_debug_controller.ui +++ b/src/yuzu/configuration/configure_debug_controller.ui @@ -66,32 +66,12 @@ <signal>accepted()</signal> <receiver>ConfigureDebugController</receiver> <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>140</x> - <y>318</y> - </hint> - <hint type="destinationlabel"> - <x>140</x> - <y>169</y> - </hint> - </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>ConfigureDebugController</receiver> <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>140</x> - <y>318</y> - </hint> - <hint type="destinationlabel"> - <x>140</x> - <y>169</y> - </hint> - </hints> </connection> </connections> </ui> diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 2725fcb2b..d9009091b 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -23,6 +23,8 @@ #include "yuzu/configuration/configure_motion_touch.h" #include "yuzu/configuration/configure_mouse_advanced.h" #include "yuzu/configuration/configure_touchscreen_advanced.h" +#include "yuzu/configuration/configure_vibration.h" +#include "yuzu/configuration/input_profiles.h" namespace { template <typename Dialog, typename... Args> @@ -64,7 +66,8 @@ void OnDockedModeChanged(bool last_state, bool new_state) { } ConfigureInput::ConfigureInput(QWidget* parent) - : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { + : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), + profiles(std::make_unique<InputProfiles>()) { ui->setupUi(this); } @@ -73,14 +76,22 @@ ConfigureInput::~ConfigureInput() = default; void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, std::size_t max_players) { player_controllers = { - new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem), + new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem, + profiles.get()), }; player_tabs = { @@ -113,8 +124,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, } } }); - connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, - [this] { UpdateAllInputDevices(); }); + connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this, + &ConfigureInput::UpdateAllInputDevices); + connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputProfiles, this, + &ConfigureInput::UpdateAllInputProfiles, Qt::QueuedConnection); connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) { player_controllers[i]->ConnectPlayer(state == Qt::Checked); }); @@ -134,7 +147,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); ui->tabAdvanced->layout()->addWidget(advanced); connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] { - CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem); + CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem, profiles.get()); }); connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] { CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem); @@ -146,6 +159,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); }); + connect(ui->vibrationButton, &QPushButton::clicked, + [this] { CallConfigureDialog<ConfigureVibration>(*this); }); + connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] { CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); }); @@ -171,12 +187,12 @@ void ConfigureInput::ApplyConfiguration() { advanced->ApplyConfiguration(); - const bool pre_docked_mode = Settings::values.use_docked_mode; - Settings::values.use_docked_mode = ui->radioDocked->isChecked(); - OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); + const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); + Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); + OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue()); - Settings::values.vibration_enabled = ui->vibrationGroup->isChecked(); - Settings::values.motion_enabled = ui->motionGroup->isChecked(); + Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); + Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); } void ConfigureInput::changeEvent(QEvent* event) { @@ -193,16 +209,16 @@ void ConfigureInput::RetranslateUI() { void ConfigureInput::LoadConfiguration() { LoadPlayerControllerIndices(); - UpdateDockedState(Settings::values.players[8].connected); + UpdateDockedState(Settings::values.players.GetValue()[8].connected); - ui->vibrationGroup->setChecked(Settings::values.vibration_enabled); - ui->motionGroup->setChecked(Settings::values.motion_enabled); + ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); + ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); } void ConfigureInput::LoadPlayerControllerIndices() { for (std::size_t i = 0; i < player_connected.size(); ++i) { - const auto connected = Settings::values.players[i].connected || - (i == 0 && Settings::values.players[8].connected); + const auto connected = Settings::values.players.GetValue()[i].connected || + (i == 0 && Settings::values.players.GetValue()[8].connected); player_connected[i]->setChecked(connected); } } @@ -231,8 +247,8 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) { ui->radioDocked->setEnabled(!is_handheld); ui->radioUndocked->setEnabled(!is_handheld); - ui->radioDocked->setChecked(Settings::values.use_docked_mode); - ui->radioUndocked->setChecked(!Settings::values.use_docked_mode); + ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue()); + ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue()); // Also force into undocked mode if the controller type is handheld. if (is_handheld) { @@ -242,6 +258,16 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) { void ConfigureInput::UpdateAllInputDevices() { for (const auto& player : player_controllers) { - player->UpdateInputDevices(); + player->UpdateInputDeviceCombobox(); + } +} + +void ConfigureInput::UpdateAllInputProfiles(std::size_t player_index) { + for (std::size_t i = 0; i < player_controllers.size(); ++i) { + if (i == player_index) { + continue; + } + + player_controllers[i]->UpdateInputProfiles(); } } diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index 0e8b2fd4e..f4eb0d78b 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -8,17 +8,18 @@ #include <memory> #include <QKeyEvent> +#include <QList> #include <QWidget> -#include "yuzu/configuration/configure_input_advanced.h" -#include "yuzu/configuration/configure_input_player.h" - -#include "ui_configure_input.h" - class QCheckBox; class QString; class QTimer; +class ConfigureInputAdvanced; +class ConfigureInputPlayer; + +class InputProfiles; + namespace InputCommon { class InputSubsystem; } @@ -51,6 +52,7 @@ private: void UpdateDockedState(bool is_handheld); void UpdateAllInputDevices(); + void UpdateAllInputProfiles(std::size_t player_index); /// Load configuration settings. void LoadConfiguration(); @@ -61,6 +63,8 @@ private: std::unique_ptr<Ui::ConfigureInput> ui; + std::unique_ptr<InputProfiles> profiles; + std::array<ConfigureInputPlayer*, 8> player_controllers; std::array<QWidget*, 8> player_tabs; std::array<QCheckBox*, 8> player_connected; diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui index 136955224..2707025e7 100644 --- a/src/yuzu/configuration/configure_input.ui +++ b/src/yuzu/configuration/configure_input.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>700</width> + <width>680</width> <height>540</height> </rect> </property> @@ -142,7 +142,7 @@ <number>6</number> </property> <property name="leftMargin"> - <number>3</number> + <number>8</number> </property> <property name="topMargin"> <number>6</number> @@ -195,30 +195,24 @@ <number>3</number> </property> <item> - <widget class="QSpinBox" name="vibrationSpin"> + <widget class="QPushButton" name="vibrationButton"> <property name="minimumSize"> <size> - <width>65</width> - <height>21</height> + <width>68</width> + <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>65</width> + <width>68</width> <height>16777215</height> </size> </property> - <property name="suffix"> - <string>%</string> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>200</number> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> </property> - <property name="value"> - <number>100</number> + <property name="text"> + <string>Configure</string> </property> </widget> </item> @@ -250,18 +244,18 @@ <widget class="QPushButton" name="motionButton"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Configure</string> @@ -272,7 +266,7 @@ </widget> </item> <item alignment="Qt::AlignVCenter"> - <widget class="QWidget" name="widget" native="true"> + <widget class="QWidget" name="connectedControllers" native="true"> <layout class="QGridLayout" name="gridLayout_2"> <property name="leftMargin"> <number>5</number> @@ -468,13 +462,13 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> @@ -494,7 +488,7 @@ <enum>Qt::LeftToRight</enum> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Defaults</string> @@ -511,13 +505,13 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> @@ -537,7 +531,7 @@ <enum>Qt::LeftToRight</enum> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Clear</string> diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 81f9dc16c..abaf03630 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -68,8 +68,7 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) for (std::size_t button_idx = 0; button_idx < color_buttons.size(); ++button_idx) { connect(color_buttons[button_idx], &QPushButton::clicked, this, [this, player_idx, button_idx] { - OnControllerButtonClick(static_cast<int>(player_idx), - static_cast<int>(button_idx)); + OnControllerButtonClick(player_idx, button_idx); }); } } @@ -94,20 +93,21 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) ConfigureInputAdvanced::~ConfigureInputAdvanced() = default; -void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int button_idx) { +void ConfigureInputAdvanced::OnControllerButtonClick(std::size_t player_idx, + std::size_t button_idx) { const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]); if (!new_bg_color.isValid()) { return; } controllers_colors[player_idx][button_idx] = new_bg_color; controllers_color_buttons[player_idx][button_idx]->setStyleSheet( - QStringLiteral("background-color: %1; min-width: 55px;") + QStringLiteral("background-color: %1; min-width: 60px;") .arg(controllers_colors[player_idx][button_idx].name())); } void ConfigureInputAdvanced::ApplyConfiguration() { for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) { - auto& player = Settings::values.players[player_idx]; + auto& player = Settings::values.players.GetValue()[player_idx]; std::array<u32, 4> colors{}; std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(), colors.begin(), [](QColor color) { return color.rgb(); }); @@ -126,7 +126,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() { void ConfigureInputAdvanced::LoadConfiguration() { for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) { - auto& player = Settings::values.players[player_idx]; + auto& player = Settings::values.players.GetValue()[player_idx]; std::array<u32, 4> colors = { player.body_color_left, player.button_color_left, @@ -139,7 +139,7 @@ void ConfigureInputAdvanced::LoadConfiguration() { for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) { controllers_color_buttons[player_idx][button_idx]->setStyleSheet( - QStringLiteral("background-color: %1; min-width: 55px;") + QStringLiteral("background-color: %1; min-width: 60px;") .arg(controllers_colors[player_idx][button_idx].name())); } } diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h index 50bb87768..3083d55c1 100644 --- a/src/yuzu/configuration/configure_input_advanced.h +++ b/src/yuzu/configuration/configure_input_advanced.h @@ -35,7 +35,7 @@ private: void RetranslateUI(); void UpdateUIEnabled(); - void OnControllerButtonClick(int player_idx, int button_idx); + void OnControllerButtonClick(std::size_t player_idx, std::size_t button_idx); void LoadConfiguration(); diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui index 5958435fc..a880a7c68 100644 --- a/src/yuzu/configuration/configure_input_advanced.ui +++ b/src/yuzu/configuration/configure_input_advanced.ui @@ -192,18 +192,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -247,18 +247,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -323,18 +323,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -378,18 +378,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -478,18 +478,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -533,18 +533,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -609,18 +609,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -664,18 +664,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -782,18 +782,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -837,18 +837,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -913,18 +913,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -968,18 +968,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1068,18 +1068,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1123,18 +1123,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1199,18 +1199,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1254,18 +1254,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1393,18 +1393,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1448,18 +1448,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1524,18 +1524,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1579,18 +1579,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1679,18 +1679,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1734,18 +1734,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1810,18 +1810,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1865,18 +1865,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -1983,18 +1983,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -2038,18 +2038,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -2114,18 +2114,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -2169,18 +2169,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -2269,18 +2269,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -2324,18 +2324,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -2400,18 +2400,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> @@ -2455,18 +2455,18 @@ </property> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string/> diff --git a/src/yuzu/configuration/configure_input_dialog.cpp b/src/yuzu/configuration/configure_input_dialog.cpp deleted file mode 100644 index 1866003c2..000000000 --- a/src/yuzu/configuration/configure_input_dialog.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "ui_configure_input_dialog.h" -#include "yuzu/configuration/configure_input_dialog.h" - -ConfigureInputDialog::ConfigureInputDialog(QWidget* parent, std::size_t max_players, - InputCommon::InputSubsystem* input_subsystem) - : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputDialog>()), - input_widget(new ConfigureInput(this)) { - ui->setupUi(this); - - input_widget->Initialize(input_subsystem, max_players); - - ui->inputLayout->addWidget(input_widget); - - RetranslateUI(); -} - -ConfigureInputDialog::~ConfigureInputDialog() = default; - -void ConfigureInputDialog::ApplyConfiguration() { - input_widget->ApplyConfiguration(); -} - -void ConfigureInputDialog::changeEvent(QEvent* event) { - if (event->type() == QEvent::LanguageChange) { - RetranslateUI(); - } - - QDialog::changeEvent(event); -} - -void ConfigureInputDialog::RetranslateUI() { - ui->retranslateUi(this); -} diff --git a/src/yuzu/configuration/configure_input_dialog.h b/src/yuzu/configuration/configure_input_dialog.h deleted file mode 100644 index d1bd865f9..000000000 --- a/src/yuzu/configuration/configure_input_dialog.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> -#include <QDialog> -#include "yuzu/configuration/configure_input.h" - -class QPushButton; - -namespace InputCommon { -class InputSubsystem; -} - -namespace Ui { -class ConfigureInputDialog; -} - -class ConfigureInputDialog : public QDialog { - Q_OBJECT - -public: - explicit ConfigureInputDialog(QWidget* parent, std::size_t max_players, - InputCommon::InputSubsystem* input_subsystem); - ~ConfigureInputDialog() override; - - void ApplyConfiguration(); - -private: - void changeEvent(QEvent* event) override; - void RetranslateUI(); - - std::unique_ptr<Ui::ConfigureInputDialog> ui; - - ConfigureInput* input_widget; -}; diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index f58ca29d7..56ab32a35 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -4,6 +4,7 @@ #include <algorithm> #include <memory> +#include <thread> #include <utility> #include <QGridLayout> #include <QInputDialog> @@ -22,8 +23,9 @@ #include "ui_configure_input_player.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_player.h" - -constexpr std::size_t HANDHELD_INDEX = 8; +#include "yuzu/configuration/configure_vibration.h" +#include "yuzu/configuration/input_profiles.h" +#include "yuzu/util/limitable_input_dialog.h" const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> ConfigureInputPlayer::analog_sub_buttons{{ @@ -35,6 +37,8 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> namespace { +constexpr std::size_t HANDHELD_INDEX = 8; + void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, bool connected) { Core::System& system{Core::System::GetInstance()}; @@ -240,10 +244,11 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row, InputCommon::InputSubsystem* input_subsystem_, - bool debug) + InputProfiles* profiles_, bool debug) : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), - debug(debug), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique<QTimer>()), - poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row) { + debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_), + timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()), + bottom_row(bottom_row) { ui->setupUi(this); setFocusPolicy(Qt::ClickFocus); @@ -366,6 +371,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } connect(analog_button, &QPushButton::clicked, [=, this] { + if (!map_analog_stick_accepted) { + map_analog_stick_accepted = + QMessageBox::information( + this, tr("Map Analog Stick"), + tr("After pressing OK, first move your joystick horizontally, and then " + "vertically.\nTo invert the axes, first move your joystick " + "vertically, and then horizontally."), + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok; + if (!map_analog_stick_accepted) { + return; + } + } HandleClick( analog_map_buttons[analog_id][sub_button_id], [=, this](const Common::ParamPackage& params) { @@ -455,11 +472,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i }); } + if (debug || player_index == 9) { + ui->groupConnectedController->setCheckable(false); + } + // The Debug Controller can only choose the Pro Controller. if (debug) { ui->buttonScreenshot->setEnabled(false); ui->buttonHome->setEnabled(false); - ui->groupConnectedController->setCheckable(false); QStringList debug_controller_types = { tr("Pro Controller"), }; @@ -477,11 +497,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i UpdateMotionButtons(); }); - connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this, + connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this, &ConfigureInputPlayer::UpdateMappingWithDefaults); + ui->comboDevices->setCurrentIndex(-1); + ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); - UpdateInputDevices(); connect(ui->buttonRefreshDevices, &QPushButton::clicked, [this] { emit RefreshInputDevices(); }); @@ -492,14 +513,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i Common::ParamPackage params; if (input_subsystem->GetGCButtons()->IsPolling()) { params = input_subsystem->GetGCButtons()->GetNextInput(); - if (params.Has("engine")) { + if (params.Has("engine") && IsInputAcceptable(params)) { SetPollingResult(params, false); return; } } if (input_subsystem->GetGCAnalogs()->IsPolling()) { params = input_subsystem->GetGCAnalogs()->GetNextInput(); - if (params.Has("engine")) { + if (params.Has("engine") && IsInputAcceptable(params)) { SetPollingResult(params, false); return; } @@ -513,13 +534,24 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } for (auto& poller : device_pollers) { params = poller->GetNextInput(); - if (params.Has("engine")) { + if (params.Has("engine") && IsInputAcceptable(params)) { SetPollingResult(params, false); return; } } }); + UpdateInputProfiles(); + + connect(ui->buttonProfilesNew, &QPushButton::clicked, this, + &ConfigureInputPlayer::CreateProfile); + connect(ui->buttonProfilesDelete, &QPushButton::clicked, this, + &ConfigureInputPlayer::DeleteProfile); + connect(ui->comboProfiles, qOverload<int>(&QComboBox::activated), this, + &ConfigureInputPlayer::LoadProfile); + connect(ui->buttonProfilesSave, &QPushButton::clicked, this, + &ConfigureInputPlayer::SaveProfile); + LoadConfiguration(); // TODO(wwylele): enable this when we actually emulate it @@ -529,7 +561,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i ConfigureInputPlayer::~ConfigureInputPlayer() = default; void ConfigureInputPlayer::ApplyConfiguration() { - auto& player = Settings::values.players[player_index]; + auto& player = Settings::values.players.GetValue()[player_index]; auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons; auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs; @@ -543,33 +575,58 @@ void ConfigureInputPlayer::ApplyConfiguration() { } auto& motions = player.motions; + std::transform(motions_param.begin(), motions_param.end(), motions.begin(), [](const Common::ParamPackage& param) { return param.Serialize(); }); - player.controller_type = - static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex()); - player.connected = ui->groupConnectedController->isChecked(); + const auto controller_type = + GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); + const auto player_connected = ui->groupConnectedController->isChecked() && + controller_type != Settings::ControllerType::Handheld; - // Player 2-8 - if (player_index != 0) { - UpdateController(player.controller_type, player_index, player.connected); + if (player.controller_type == controller_type && player.connected == player_connected) { + // Set vibration devices in the event that the input device has changed. + ConfigureVibration::SetVibrationDevices(player_index); return; } - // Player 1 and Handheld - auto& handheld = Settings::values.players[HANDHELD_INDEX]; - // If Handheld is selected, copy all the settings from Player 1 to Handheld. - if (player.controller_type == Settings::ControllerType::Handheld) { - handheld = player; - handheld.connected = ui->groupConnectedController->isChecked(); - player.connected = false; // Disconnect Player 1 - } else { - player.connected = ui->groupConnectedController->isChecked(); - handheld.connected = false; // Disconnect Handheld + // Disconnect the controller first. + UpdateController(controller_type, player_index, false); + + player.controller_type = controller_type; + player.connected = player_connected; + + ConfigureVibration::SetVibrationDevices(player_index); + + // Handheld + if (player_index == 0) { + auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; + if (controller_type == Settings::ControllerType::Handheld) { + handheld = player; + } + handheld.connected = ui->groupConnectedController->isChecked() && + controller_type == Settings::ControllerType::Handheld; + UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); + } + + if (!player.connected) { + return; } - UpdateController(player.controller_type, player_index, player.connected); - UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); + // This emulates a delay between disconnecting and reconnecting controllers as some games + // do not respond to a change in controller type if it was instantaneous. + using namespace std::chrono_literals; + std::this_thread::sleep_for(20ms); + + UpdateController(controller_type, player_index, player_connected); +} + +void ConfigureInputPlayer::showEvent(QShowEvent* event) { + if (bottom_row == nullptr) { + return; + } + QWidget::showEvent(event); + ui->main->addWidget(bottom_row); } void ConfigureInputPlayer::changeEvent(QEvent* event) { @@ -586,7 +643,7 @@ void ConfigureInputPlayer::RetranslateUI() { } void ConfigureInputPlayer::LoadConfiguration() { - auto& player = Settings::values.players[player_index]; + auto& player = Settings::values.players.GetValue()[player_index]; if (debug) { std::transform(Settings::values.debug_pad_buttons.begin(), Settings::values.debug_pad_buttons.end(), buttons_param.begin(), @@ -604,6 +661,7 @@ void ConfigureInputPlayer::LoadConfiguration() { } UpdateUI(); + UpdateInputDeviceCombobox(); if (debug) { return; @@ -612,44 +670,75 @@ void ConfigureInputPlayer::LoadConfiguration() { ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type)); ui->groupConnectedController->setChecked( player.connected || - (player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected)); + (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected)); } -void ConfigureInputPlayer::UpdateInputDevices() { - input_devices = input_subsystem->GetInputDevices(); - ui->comboDevices->clear(); - for (auto device : input_devices) { - ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {}); - } +void ConfigureInputPlayer::ConnectPlayer(bool connected) { + ui->groupConnectedController->setChecked(connected); } -void ConfigureInputPlayer::RestoreDefaults() { - // Reset Buttons - for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { - buttons_param[button_id] = Common::ParamPackage{ - InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; +void ConfigureInputPlayer::UpdateInputDeviceCombobox() { + // Skip input device persistence if "Input Devices" is set to "Any". + if (ui->comboDevices->currentIndex() == 0) { + UpdateInputDevices(); + return; } - // Reset Analogs and Modifier Buttons - for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { - for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { - Common::ParamPackage params{InputCommon::GenerateKeyboardParam( - Config::default_analogs[analog_id][sub_button_id])}; - SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); - } + // Find the first button that isn't empty. + const auto button_param = + std::find_if(buttons_param.begin(), buttons_param.end(), + [](const Common::ParamPackage param) { return param.Has("engine"); }); + const bool buttons_empty = button_param == buttons_param.end(); - analogs_param[analog_id].Set( - "modifier", InputCommon::GenerateKeyboardParam(Config::default_stick_mod[analog_id])); + const auto current_engine = button_param->Get("engine", ""); + const auto current_guid = button_param->Get("guid", ""); + const auto current_port = button_param->Get("port", ""); + + const bool is_keyboard_mouse = current_engine == "keyboard" || current_engine == "mouse"; + + UpdateInputDevices(); + + if (buttons_empty) { + return; } - for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { - motions_param[motion_id] = Common::ParamPackage{ - InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])}; + const bool all_one_device = + std::all_of(buttons_param.begin(), buttons_param.end(), + [current_engine, current_guid, current_port, + is_keyboard_mouse](const Common::ParamPackage param) { + if (is_keyboard_mouse) { + return !param.Has("engine") || param.Get("engine", "") == "keyboard" || + param.Get("engine", "") == "mouse"; + } + return !param.Has("engine") || (param.Get("engine", "") == current_engine && + param.Get("guid", "") == current_guid && + param.Get("port", "") == current_port); + }); + + if (all_one_device) { + if (is_keyboard_mouse) { + ui->comboDevices->setCurrentIndex(1); + return; + } + const auto devices_it = std::find_if( + input_devices.begin(), input_devices.end(), + [current_engine, current_guid, current_port](const Common::ParamPackage param) { + return param.Get("class", "") == current_engine && + param.Get("guid", "") == current_guid && + param.Get("port", "") == current_port; + }); + const int device_index = + devices_it != input_devices.end() + ? static_cast<int>(std::distance(input_devices.begin(), devices_it)) + : 0; + ui->comboDevices->setCurrentIndex(device_index); + } else { + ui->comboDevices->setCurrentIndex(0); } +} - UpdateUI(); - UpdateInputDevices(); - ui->comboControllerType->setCurrentIndex(0); +void ConfigureInputPlayer::RestoreDefaults() { + UpdateMappingWithDefaults(); } void ConfigureInputPlayer::ClearAll() { @@ -752,10 +841,167 @@ void ConfigureInputPlayer::UpdateUI() { } } +void ConfigureInputPlayer::UpdateInputDevices() { + input_devices = input_subsystem->GetInputDevices(); + ui->comboDevices->clear(); + for (auto device : input_devices) { + ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {}); + } +} + +void ConfigureInputPlayer::UpdateControllerIcon() { + // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its + // "nonstandard" to use an image through the icon support) + const QString stylesheet = [this] { + switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { + case Settings::ControllerType::ProController: + return QStringLiteral("image: url(:/controller/pro_controller%0)"); + case Settings::ControllerType::DualJoyconDetached: + return QStringLiteral("image: url(:/controller/dual_joycon%0)"); + case Settings::ControllerType::LeftJoycon: + return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)"); + case Settings::ControllerType::RightJoycon: + return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)"); + case Settings::ControllerType::Handheld: + return QStringLiteral("image: url(:/controller/handheld%0)"); + default: + return QString{}; + } + }(); + + const QString theme = [] { + if (QIcon::themeName().contains(QStringLiteral("dark"))) { + return QStringLiteral("_dark"); + } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { + return QStringLiteral("_midnight"); + } else { + return QString{}; + } + }(); + + ui->controllerFrame->setStyleSheet(stylesheet.arg(theme)); +} + +void ConfigureInputPlayer::UpdateControllerAvailableButtons() { + auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); + if (debug) { + layout = Settings::ControllerType::ProController; + } + + // List of all the widgets that will be hidden by any of the following layouts that need + // "unhidden" after the controller type changes + const std::array<QWidget*, 9> layout_show = { + ui->buttonShoulderButtonsSLSR, + ui->horizontalSpacerShoulderButtonsWidget, + ui->horizontalSpacerShoulderButtonsWidget2, + ui->buttonShoulderButtonsLeft, + ui->buttonMiscButtonsMinusScreenshot, + ui->bottomLeft, + ui->buttonShoulderButtonsRight, + ui->buttonMiscButtonsPlusHome, + ui->bottomRight, + }; + + for (auto* widget : layout_show) { + widget->show(); + } + + std::vector<QWidget*> layout_hidden; + switch (layout) { + case Settings::ControllerType::ProController: + case Settings::ControllerType::DualJoyconDetached: + case Settings::ControllerType::Handheld: + layout_hidden = { + ui->buttonShoulderButtonsSLSR, + ui->horizontalSpacerShoulderButtonsWidget2, + }; + break; + case Settings::ControllerType::LeftJoycon: + layout_hidden = { + ui->horizontalSpacerShoulderButtonsWidget2, + ui->buttonShoulderButtonsRight, + ui->buttonMiscButtonsPlusHome, + ui->bottomRight, + }; + break; + case Settings::ControllerType::RightJoycon: + layout_hidden = { + ui->horizontalSpacerShoulderButtonsWidget, + ui->buttonShoulderButtonsLeft, + ui->buttonMiscButtonsMinusScreenshot, + ui->bottomLeft, + }; + break; + } + + for (auto* widget : layout_hidden) { + widget->hide(); + } +} + +void ConfigureInputPlayer::UpdateMotionButtons() { + if (debug) { + // Motion isn't used with the debug controller, hide both groupboxes. + ui->buttonMotionLeftGroup->hide(); + ui->buttonMotionRightGroup->hide(); + return; + } + + // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller. + switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { + case Settings::ControllerType::ProController: + case Settings::ControllerType::LeftJoycon: + case Settings::ControllerType::Handheld: + // Show "Motion 1" and hide "Motion 2". + ui->buttonMotionLeftGroup->show(); + ui->buttonMotionRightGroup->hide(); + break; + case Settings::ControllerType::RightJoycon: + // Show "Motion 2" and hide "Motion 1". + ui->buttonMotionLeftGroup->hide(); + ui->buttonMotionRightGroup->show(); + break; + case Settings::ControllerType::DualJoyconDetached: + default: + // Show both "Motion 1/2". + ui->buttonMotionLeftGroup->show(); + ui->buttonMotionRightGroup->show(); + break; + } +} + void ConfigureInputPlayer::UpdateMappingWithDefaults() { - if (ui->comboDevices->currentIndex() < 2) { + if (ui->comboDevices->currentIndex() == 0) { return; } + + if (ui->comboDevices->currentIndex() == 1) { + // Reset keyboard bindings + for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { + buttons_param[button_id] = Common::ParamPackage{ + InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; + } + for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { + for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { + Common::ParamPackage params{InputCommon::GenerateKeyboardParam( + Config::default_analogs[analog_id][sub_button_id])}; + SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); + } + + analogs_param[analog_id].Set("modifier", InputCommon::GenerateKeyboardParam( + Config::default_stick_mod[analog_id])); + } + + for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { + motions_param[motion_id] = Common::ParamPackage{ + InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])}; + } + + UpdateUI(); + return; + } + + // Reset controller bindings const auto& device = input_devices[ui->comboDevices->currentIndex()]; auto button_mapping = input_subsystem->GetButtonMappingForDevice(device); auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device); @@ -828,9 +1074,27 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, } UpdateUI(); + UpdateInputDeviceCombobox(); + input_setter = std::nullopt; } +bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) const { + if (ui->comboDevices->currentIndex() == 0) { + return true; + } + + // Keyboard/Mouse + if (ui->comboDevices->currentIndex() == 1) { + return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse"; + } + + const auto current_input_device = input_devices[ui->comboDevices->currentIndex()]; + return params.Get("engine", "") == current_input_device.Get("class", "") && + params.Get("guid", "") == current_input_device.Get("guid", "") && + params.Get("port", "") == current_input_device.Get("port", ""); +} + void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) { if (!input_setter || !event) { return; @@ -865,135 +1129,101 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { SetPollingResult({}, true); } -void ConfigureInputPlayer::UpdateControllerIcon() { - // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its - // "nonstandard" to use an image through the icon support) - const QString stylesheet = [this] { - switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { - case Settings::ControllerType::ProController: - return QStringLiteral("image: url(:/controller/pro_controller%0)"); - case Settings::ControllerType::DualJoyconDetached: - return QStringLiteral("image: url(:/controller/dual_joycon%0)"); - case Settings::ControllerType::LeftJoycon: - return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)"); - case Settings::ControllerType::RightJoycon: - return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)"); - case Settings::ControllerType::Handheld: - return QStringLiteral("image: url(:/controller/handheld%0)"); - default: - return QString{}; - } - }(); - - const QString theme = [this] { - if (QIcon::themeName().contains(QStringLiteral("dark"))) { - return QStringLiteral("_dark"); - } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { - return QStringLiteral("_midnight"); - } else { - return QString{}; - } - }(); +void ConfigureInputPlayer::CreateProfile() { + const auto profile_name = + LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 20); - ui->controllerFrame->setStyleSheet(stylesheet.arg(theme)); -} + if (profile_name.isEmpty()) { + return; + } -void ConfigureInputPlayer::UpdateControllerAvailableButtons() { - auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); - if (debug) { - layout = Settings::ControllerType::ProController; + if (!InputProfiles::IsProfileNameValid(profile_name.toStdString())) { + QMessageBox::critical(this, tr("Create Input Profile"), + tr("The given profile name is not valid!")); + return; } - // List of all the widgets that will be hidden by any of the following layouts that need - // "unhidden" after the controller type changes - const std::array<QWidget*, 9> layout_show = { - ui->buttonShoulderButtonsSLSR, - ui->horizontalSpacerShoulderButtonsWidget, - ui->horizontalSpacerShoulderButtonsWidget2, - ui->buttonShoulderButtonsLeft, - ui->buttonMiscButtonsMinusScreenshot, - ui->bottomLeft, - ui->buttonShoulderButtonsRight, - ui->buttonMiscButtonsPlusHome, - ui->bottomRight, - }; + ApplyConfiguration(); - for (auto* widget : layout_show) { - widget->show(); + if (!profiles->CreateProfile(profile_name.toStdString(), player_index)) { + QMessageBox::critical(this, tr("Create Input Profile"), + tr("Failed to create the input profile \"%1\"").arg(profile_name)); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); + return; } - std::vector<QWidget*> layout_hidden; - switch (layout) { - case Settings::ControllerType::ProController: - case Settings::ControllerType::DualJoyconDetached: - case Settings::ControllerType::Handheld: - layout_hidden = { - ui->buttonShoulderButtonsSLSR, - ui->horizontalSpacerShoulderButtonsWidget2, - }; - break; - case Settings::ControllerType::LeftJoycon: - layout_hidden = { - ui->horizontalSpacerShoulderButtonsWidget2, - ui->buttonShoulderButtonsRight, - ui->buttonMiscButtonsPlusHome, - ui->bottomRight, - }; - break; - case Settings::ControllerType::RightJoycon: - layout_hidden = { - ui->horizontalSpacerShoulderButtonsWidget, - ui->buttonShoulderButtonsLeft, - ui->buttonMiscButtonsMinusScreenshot, - ui->bottomLeft, - }; - break; + emit RefreshInputProfiles(player_index); + + ui->comboProfiles->addItem(profile_name); + ui->comboProfiles->setCurrentIndex(ui->comboProfiles->count() - 1); +} + +void ConfigureInputPlayer::DeleteProfile() { + const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex()); + + if (profile_name.isEmpty()) { + return; } - for (auto* widget : layout_hidden) { - widget->hide(); + if (!profiles->DeleteProfile(profile_name.toStdString())) { + QMessageBox::critical(this, tr("Delete Input Profile"), + tr("Failed to delete the input profile \"%1\"").arg(profile_name)); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); + return; } + + emit RefreshInputProfiles(player_index); + + ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex()); + ui->comboProfiles->setCurrentIndex(-1); } -void ConfigureInputPlayer::UpdateMotionButtons() { - if (debug) { - // Motion isn't used with the debug controller, hide both groupboxes. - ui->buttonMotionLeftGroup->hide(); - ui->buttonMotionRightGroup->hide(); +void ConfigureInputPlayer::LoadProfile() { + const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex()); + + if (profile_name.isEmpty()) { return; } - // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller. - switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { - case Settings::ControllerType::ProController: - case Settings::ControllerType::LeftJoycon: - case Settings::ControllerType::Handheld: - // Show "Motion 1" and hide "Motion 2". - ui->buttonMotionLeftGroup->show(); - ui->buttonMotionRightGroup->hide(); - break; - case Settings::ControllerType::RightJoycon: - // Show "Motion 2" and hide "Motion 1". - ui->buttonMotionLeftGroup->hide(); - ui->buttonMotionRightGroup->show(); - break; - case Settings::ControllerType::DualJoyconDetached: - default: - // Show both "Motion 1/2". - ui->buttonMotionLeftGroup->show(); - ui->buttonMotionRightGroup->show(); - break; + ApplyConfiguration(); + + if (!profiles->LoadProfile(profile_name.toStdString(), player_index)) { + QMessageBox::critical(this, tr("Load Input Profile"), + tr("Failed to load the input profile \"%1\"").arg(profile_name)); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); + return; } + + LoadConfiguration(); } -void ConfigureInputPlayer::showEvent(QShowEvent* event) { - if (bottom_row == nullptr) { +void ConfigureInputPlayer::SaveProfile() { + const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex()); + + if (profile_name.isEmpty()) { + return; + } + + ApplyConfiguration(); + + if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) { + QMessageBox::critical(this, tr("Save Input Profile"), + tr("Failed to save the input profile \"%1\"").arg(profile_name)); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); return; } - QWidget::showEvent(event); - ui->main->addWidget(bottom_row); } -void ConfigureInputPlayer::ConnectPlayer(bool connected) { - ui->groupConnectedController->setChecked(connected); +void ConfigureInputPlayer::UpdateInputProfiles() { + ui->comboProfiles->clear(); + + for (const auto& profile_name : profiles->GetInputProfileNames()) { + ui->comboProfiles->addItem(QString::fromStdString(profile_name)); + } + + ui->comboProfiles->setCurrentIndex(-1); } diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index c19aefffa..23cf6f958 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -26,6 +26,8 @@ class QString; class QTimer; class QWidget; +class InputProfiles; + namespace InputCommon { class InputSubsystem; } @@ -45,14 +47,20 @@ class ConfigureInputPlayer : public QWidget { public: explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row, InputCommon::InputSubsystem* input_subsystem_, - bool debug = false); + InputProfiles* profiles_, bool debug = false); ~ConfigureInputPlayer() override; /// Save all button configurations to settings file. void ApplyConfiguration(); + /// Set the connection state checkbox (used to sync state). + void ConnectPlayer(bool connected); + /// Update the input devices combobox. - void UpdateInputDevices(); + void UpdateInputDeviceCombobox(); + + /// Updates the list of controller profiles. + void UpdateInputProfiles(); /// Restore all buttons to their default values. void RestoreDefaults(); @@ -60,9 +68,6 @@ public: /// Clear all input configuration. void ClearAll(); - /// Set the connection state checkbox (used to sync state). - void ConnectPlayer(bool connected); - signals: /// Emitted when this controller is connected by the user. void Connected(bool connected); @@ -70,6 +75,12 @@ signals: void HandheldStateChanged(bool is_handheld); /// Emitted when the input devices combobox is being refreshed. void RefreshInputDevices(); + /** + * Emitted when the input profiles combobox is being refreshed. + * The player_index represents the current player's index, and the profile combobox + * will not be updated for this index as they are already updated by other mechanisms. + */ + void RefreshInputProfiles(std::size_t player_index); protected: void showEvent(QShowEvent* event) override; @@ -89,6 +100,9 @@ private: /// Finish polling and configure input using the input_setter. void SetPollingResult(const Common::ParamPackage& params, bool abort); + /// Checks whether a given input can be accepted. + bool IsInputAcceptable(const Common::ParamPackage& params) const; + /// Handle mouse button press events. void mousePressEvent(QMouseEvent* event) override; @@ -98,8 +112,8 @@ private: /// Update UI to reflect current configuration. void UpdateUI(); - /// Update the controller selection combobox - void UpdateControllerCombobox(); + /// Update the available input devices. + void UpdateInputDevices(); /// Update the current controller icon. void UpdateControllerIcon(); @@ -113,6 +127,18 @@ private: /// Gets the default controller mapping for this device and auto configures the input to match. void UpdateMappingWithDefaults(); + /// Creates a controller profile. + void CreateProfile(); + + /// Deletes the selected controller profile. + void DeleteProfile(); + + /// Loads the selected controller profile. + void LoadProfile(); + + /// Saves the current controller configuration into a selected controller profile. + void SaveProfile(); + std::unique_ptr<Ui::ConfigureInputPlayer> ui; std::size_t player_index; @@ -120,6 +146,8 @@ private: InputCommon::InputSubsystem* input_subsystem; + InputProfiles* profiles; + std::unique_ptr<QTimer> timeout_timer; std::unique_ptr<QTimer> poll_timer; @@ -159,12 +187,15 @@ private: std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; + /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once. + bool map_analog_stick_accepted{}; + /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, /// keyboard events are ignored. - bool want_keyboard_mouse = false; + bool want_keyboard_mouse{}; /// List of physical devices users can map with. If a SDL backed device is selected, then you - /// can usue this device to get a default mapping. + /// can use this device to get a default mapping. std::vector<Common::ParamPackage> input_devices; /// Bottom row is where console wide settings are held, and its "owned" by the parent diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index e03461d9d..1e78b4c10 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui @@ -83,6 +83,12 @@ </property> <item> <widget class="QComboBox" name="comboControllerType"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>21</height> + </size> + </property> <item> <property name="text"> <string>Pro Controller</string> @@ -136,6 +142,12 @@ </property> <item> <widget class="QComboBox" name="comboDevices"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>21</height> + </size> + </property> <item> <property name="text"> <string>Any</string> @@ -152,14 +164,14 @@ <widget class="QPushButton" name="buttonRefreshDevices"> <property name="minimumSize"> <size> - <width>24</width> - <height>22</height> + <width>21</width> + <height>21</height> </size> </property> <property name="maximumSize"> <size> - <width>24</width> - <height>22</height> + <width>21</width> + <height>21</height> </size> </property> <property name="styleSheet"> @@ -198,18 +210,25 @@ <number>5</number> </property> <item> - <widget class="QComboBox" name="comboProfiles"/> + <widget class="QComboBox" name="comboProfiles"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>21</height> + </size> + </property> + </widget> </item> <item> <widget class="QPushButton" name="buttonProfilesSave"> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Save</string> @@ -220,12 +239,12 @@ <widget class="QPushButton" name="buttonProfilesNew"> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>New</string> @@ -236,12 +255,12 @@ <widget class="QPushButton" name="buttonProfilesDelete"> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Delete</string> @@ -393,18 +412,18 @@ <widget class="QPushButton" name="buttonLStickUp"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Up</string> @@ -463,18 +482,18 @@ <widget class="QPushButton" name="buttonLStickLeft"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Left</string> @@ -512,18 +531,18 @@ <widget class="QPushButton" name="buttonLStickRight"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Right</string> @@ -594,18 +613,18 @@ <widget class="QPushButton" name="buttonLStickDown"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Down</string> @@ -664,18 +683,18 @@ <widget class="QPushButton" name="buttonLStick"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Pressed</string> @@ -713,18 +732,18 @@ <widget class="QPushButton" name="buttonLStickMod"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Modifier</string> @@ -759,13 +778,13 @@ <widget class="QSpinBox" name="spinboxLStickRange"> <property name="minimumSize"> <size> - <width>55</width> + <width>68</width> <height>21</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> @@ -966,18 +985,18 @@ <widget class="QPushButton" name="buttonDpadUp"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Up</string> @@ -1036,18 +1055,18 @@ <widget class="QPushButton" name="buttonDpadLeft"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Left</string> @@ -1085,18 +1104,18 @@ <widget class="QPushButton" name="buttonDpadRight"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Right</string> @@ -1167,18 +1186,18 @@ <widget class="QPushButton" name="buttonDpadDown"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Down</string> @@ -1292,18 +1311,18 @@ <widget class="QPushButton" name="buttonL"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>L</string> @@ -1341,18 +1360,18 @@ <widget class="QPushButton" name="buttonZL"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>ZL</string> @@ -1445,18 +1464,18 @@ <widget class="QPushButton" name="buttonMinus"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Minus</string> @@ -1494,18 +1513,18 @@ <widget class="QPushButton" name="buttonScreenshot"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Capture</string> @@ -1564,18 +1583,18 @@ <widget class="QPushButton" name="buttonPlus"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Plus</string> @@ -1613,18 +1632,18 @@ <widget class="QPushButton" name="buttonHome"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Home</string> @@ -1717,18 +1736,18 @@ <widget class="QPushButton" name="buttonR"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>R</string> @@ -1766,18 +1785,18 @@ <widget class="QPushButton" name="buttonZR"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>ZR</string> @@ -1870,18 +1889,18 @@ <widget class="QPushButton" name="buttonSL"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>SL</string> @@ -1919,18 +1938,18 @@ <widget class="QPushButton" name="buttonSR"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>SR</string> @@ -2027,18 +2046,18 @@ <widget class="QPushButton" name="buttonMotionLeft"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Left</string> @@ -2076,18 +2095,18 @@ <widget class="QPushButton" name="buttonMotionRight"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Right</string> @@ -2225,18 +2244,18 @@ <widget class="QPushButton" name="buttonX"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>X</string> @@ -2295,18 +2314,18 @@ <widget class="QPushButton" name="buttonY"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Y</string> @@ -2344,18 +2363,18 @@ <widget class="QPushButton" name="buttonA"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>A</string> @@ -2426,18 +2445,18 @@ <widget class="QPushButton" name="buttonB"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>B</string> @@ -2580,18 +2599,18 @@ <widget class="QPushButton" name="buttonRStickUp"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Up</string> @@ -2650,18 +2669,18 @@ <widget class="QPushButton" name="buttonRStickLeft"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Left</string> @@ -2699,18 +2718,18 @@ <widget class="QPushButton" name="buttonRStickRight"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Right</string> @@ -2781,18 +2800,18 @@ <widget class="QPushButton" name="buttonRStickDown"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Down</string> @@ -2851,18 +2870,18 @@ <widget class="QPushButton" name="buttonRStick"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Pressed</string> @@ -2900,18 +2919,18 @@ <widget class="QPushButton" name="buttonRStickMod"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> <property name="styleSheet"> - <string notr="true">min-width: 55px;</string> + <string notr="true">min-width: 68px;</string> </property> <property name="text"> <string>Modifier</string> @@ -2946,13 +2965,13 @@ <widget class="QSpinBox" name="spinboxRStickRange"> <property name="minimumSize"> <size> - <width>55</width> + <width>68</width> <height>21</height> </size> </property> <property name="maximumSize"> <size> - <width>55</width> + <width>68</width> <height>16777215</height> </size> </property> diff --git a/src/yuzu/configuration/configure_input_profile_dialog.cpp b/src/yuzu/configuration/configure_input_profile_dialog.cpp new file mode 100644 index 000000000..1f5cfa75b --- /dev/null +++ b/src/yuzu/configuration/configure_input_profile_dialog.cpp @@ -0,0 +1,37 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "ui_configure_input_profile_dialog.h" +#include "yuzu/configuration/configure_input_player.h" +#include "yuzu/configuration/configure_input_profile_dialog.h" + +ConfigureInputProfileDialog::ConfigureInputProfileDialog( + QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles) + : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()), + profile_widget(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, false)) { + ui->setupUi(this); + + ui->controllerLayout->addWidget(profile_widget); + + connect(ui->clear_all_button, &QPushButton::clicked, this, + [this] { profile_widget->ClearAll(); }); + connect(ui->restore_defaults_button, &QPushButton::clicked, this, + [this] { profile_widget->RestoreDefaults(); }); + + RetranslateUI(); +} + +ConfigureInputProfileDialog::~ConfigureInputProfileDialog() = default; + +void ConfigureInputProfileDialog::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QDialog::changeEvent(event); +} + +void ConfigureInputProfileDialog::RetranslateUI() { + ui->retranslateUi(this); +} diff --git a/src/yuzu/configuration/configure_input_profile_dialog.h b/src/yuzu/configuration/configure_input_profile_dialog.h new file mode 100644 index 000000000..e6386bdbb --- /dev/null +++ b/src/yuzu/configuration/configure_input_profile_dialog.h @@ -0,0 +1,40 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QDialog> + +class QPushButton; + +class ConfigureInputPlayer; + +class InputProfiles; + +namespace InputCommon { +class InputSubsystem; +} + +namespace Ui { +class ConfigureInputProfileDialog; +} + +class ConfigureInputProfileDialog : public QDialog { + Q_OBJECT + +public: + explicit ConfigureInputProfileDialog(QWidget* parent, + InputCommon::InputSubsystem* input_subsystem, + InputProfiles* profiles); + ~ConfigureInputProfileDialog() override; + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + std::unique_ptr<Ui::ConfigureInputProfileDialog> ui; + + ConfigureInputPlayer* profile_widget; +}; diff --git a/src/yuzu/configuration/configure_input_dialog.ui b/src/yuzu/configuration/configure_input_profile_dialog.ui index b92ddb200..726cf6905 100644 --- a/src/yuzu/configuration/configure_input_dialog.ui +++ b/src/yuzu/configuration/configure_input_profile_dialog.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>ConfigureInputDialog</class> - <widget class="QDialog" name="ConfigureInputDialog"> + <class>ConfigureInputProfileDialog</class> + <widget class="QDialog" name="ConfigureInputProfileDialog"> <property name="geometry"> <rect> <x>0</x> @@ -11,7 +11,7 @@ </rect> </property> <property name="windowTitle"> - <string>Configure Input</string> + <string>Create Input Profile</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <property name="spacing"> @@ -30,11 +30,25 @@ <number>9</number> </property> <item> - <layout class="QHBoxLayout" name="inputLayout"/> + <layout class="QHBoxLayout" name="controllerLayout"/> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> + <widget class="QPushButton" name="clear_all_button"> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="restore_defaults_button"> + <property name="text"> + <string>Defaults</string> + </property> + </widget> + </item> + <item> <widget class="QDialogButtonBox" name="buttonBox"> <property name="standardButtons"> <set>QDialogButtonBox::Ok</set> @@ -50,7 +64,7 @@ <connection> <sender>buttonBox</sender> <signal>accepted()</signal> - <receiver>ConfigureInputDialog</receiver> + <receiver>ConfigureInputProfileDialog</receiver> <slot>accept()</slot> </connection> </connections> diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui index 602cf8cd8..5b78c5a4b 100644 --- a/src/yuzu/configuration/configure_motion_touch.ui +++ b/src/yuzu/configuration/configure_motion_touch.ui @@ -312,16 +312,6 @@ <signal>accepted()</signal> <receiver>ConfigureMotionTouch</receiver> <slot>ApplyConfiguration()</slot> - <hints> - <hint type="sourcelabel"> - <x>220</x> - <y>380</y> - </hint> - <hint type="destinationlabel"> - <x>220</x> - <y>200</y> - </hint> - </hints> </connection> </connections> </ui> diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui index 74552fdbd..5b99e1c37 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.ui +++ b/src/yuzu/configuration/configure_mouse_advanced.ui @@ -15,7 +15,7 @@ </property> <property name="styleSheet"> <string notr="true">QPushButton { - min-width: 55px; + min-width: 60px; }</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> @@ -42,13 +42,13 @@ <widget class="QPushButton" name="forward_button"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>16777215</width> + <width>68</width> <height>16777215</height> </size> </property> @@ -82,7 +82,7 @@ <widget class="QPushButton" name="back_button"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> @@ -110,7 +110,7 @@ <widget class="QPushButton" name="left_button"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> @@ -138,13 +138,13 @@ <widget class="QPushButton" name="middle_button"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>16777215</width> + <width>68</width> <height>16777215</height> </size> </property> @@ -204,13 +204,13 @@ <widget class="QPushButton" name="right_button"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>16777215</width> + <width>68</width> <height>16777215</height> </size> </property> @@ -256,13 +256,13 @@ <widget class="QPushButton" name="buttonClearAll"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>16777215</width> + <width>68</width> <height>16777215</height> </size> </property> @@ -275,13 +275,13 @@ <widget class="QPushButton" name="buttonRestoreDefaults"> <property name="minimumSize"> <size> - <width>57</width> + <width>68</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>16777215</width> + <width>68</width> <height>16777215</height> </size> </property> @@ -324,32 +324,12 @@ <signal>accepted()</signal> <receiver>ConfigureMouseAdvanced</receiver> <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>124</x> - <y>266</y> - </hint> - <hint type="destinationlabel"> - <x>124</x> - <y>143</y> - </hint> - </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>ConfigureMouseAdvanced</receiver> <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>124</x> - <y>266</y> - </hint> - <hint type="destinationlabel"> - <x>124</x> - <y>143</y> - </hint> - </hints> </connection> </connections> </ui> diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index 002db3f93..8eac3bd9d 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -16,6 +16,7 @@ #include "common/common_paths.h" #include "common/file_util.h" +#include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/xts_archive.h" @@ -29,7 +30,8 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id) : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) { - game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false); + game_config = std::make_unique<Config>(fmt::format("{:016X}", title_id), + Config::ConfigType::PerGameConfig); Settings::SetConfiguringGlobal(false); @@ -88,9 +90,11 @@ void ConfigurePerGame::LoadConfiguration() { ui->display_title_id->setText( QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper()); - FileSys::PatchManager pm{title_id}; + auto& system = Core::System::GetInstance(); + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + system.GetContentProvider()}; const auto control = pm.GetControlMetadata(); - const auto loader = Loader::GetLoader(file); + const auto loader = Loader::GetLoader(system, file); if (control.first != nullptr) { ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui index d2057c4ab..25975b3b9 100644 --- a/src/yuzu/configuration/configure_per_game.ui +++ b/src/yuzu/configuration/configure_per_game.ui @@ -319,32 +319,12 @@ <signal>accepted()</signal> <receiver>ConfigurePerGame</receiver> <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>248</x> - <y>254</y> - </hint> - <hint type="destinationlabel"> - <x>157</x> - <y>274</y> - </hint> - </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>ConfigurePerGame</receiver> <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>316</x> - <y>260</y> - </hint> - <hint type="destinationlabel"> - <x>286</x> - <y>274</y> - </hint> - </hints> </connection> </connections> </ui> diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp index 793fd8975..cdeeec01c 100644 --- a/src/yuzu/configuration/configure_per_game_addons.cpp +++ b/src/yuzu/configuration/configure_per_game_addons.cpp @@ -112,8 +112,10 @@ void ConfigurePerGameAddons::LoadConfiguration() { return; } - FileSys::PatchManager pm{title_id}; - const auto loader = Loader::GetLoader(file); + auto& system = Core::System::GetInstance(); + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + system.GetContentProvider()}; + const auto loader = Loader::GetLoader(system, file); FileSys::VirtualFile update_raw; loader->ReadUpdateRaw(update_raw); diff --git a/src/yuzu/configuration/configure_touch_from_button.ui b/src/yuzu/configuration/configure_touch_from_button.ui index f581e27e0..757219d54 100644 --- a/src/yuzu/configuration/configure_touch_from_button.ui +++ b/src/yuzu/configuration/configure_touch_from_button.ui @@ -216,16 +216,6 @@ Drag points to change position, or double-click table cells to edit values.</str <signal>rejected()</signal> <receiver>ConfigureTouchFromButton</receiver> <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>249</x> - <y>428</y> - </hint> - <hint type="destinationlabel"> - <x>249</x> - <y>224</y> - </hint> - </hints> </connection> </connections> </ui> diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.ui b/src/yuzu/configuration/configure_touchscreen_advanced.ui index 1171c2dd1..30ceccddb 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.ui +++ b/src/yuzu/configuration/configure_touchscreen_advanced.ui @@ -168,32 +168,12 @@ <signal>accepted()</signal> <receiver>ConfigureTouchscreenAdvanced</receiver> <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>140</x> - <y>318</y> - </hint> - <hint type="destinationlabel"> - <x>140</x> - <y>169</y> - </hint> - </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>ConfigureTouchscreenAdvanced</receiver> <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>140</x> - <y>318</y> - </hint> - <hint type="destinationlabel"> - <x>140</x> - <y>169</y> - </hint> - </hints> - </connection> + </connection> </connections> </ui> diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp new file mode 100644 index 000000000..7dcb2c5b9 --- /dev/null +++ b/src/yuzu/configuration/configure_vibration.cpp @@ -0,0 +1,146 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <unordered_map> + +#include <fmt/format.h> + +#include "common/param_package.h" +#include "core/settings.h" +#include "ui_configure_vibration.h" +#include "yuzu/configuration/configure_vibration.h" + +ConfigureVibration::ConfigureVibration(QWidget* parent) + : QDialog(parent), ui(std::make_unique<Ui::ConfigureVibration>()) { + ui->setupUi(this); + + vibration_groupboxes = { + ui->vibrationGroupPlayer1, ui->vibrationGroupPlayer2, ui->vibrationGroupPlayer3, + ui->vibrationGroupPlayer4, ui->vibrationGroupPlayer5, ui->vibrationGroupPlayer6, + ui->vibrationGroupPlayer7, ui->vibrationGroupPlayer8, + }; + + vibration_spinboxes = { + ui->vibrationSpinPlayer1, ui->vibrationSpinPlayer2, ui->vibrationSpinPlayer3, + ui->vibrationSpinPlayer4, ui->vibrationSpinPlayer5, ui->vibrationSpinPlayer6, + ui->vibrationSpinPlayer7, ui->vibrationSpinPlayer8, + }; + + const auto& players = Settings::values.players.GetValue(); + + for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { + vibration_groupboxes[i]->setChecked(players[i].vibration_enabled); + vibration_spinboxes[i]->setValue(players[i].vibration_strength); + } + + ui->checkBoxAccurateVibration->setChecked( + Settings::values.enable_accurate_vibrations.GetValue()); + + if (!Settings::IsConfiguringGlobal()) { + ui->checkBoxAccurateVibration->setDisabled(true); + } + + RetranslateUI(); +} + +ConfigureVibration::~ConfigureVibration() = default; + +void ConfigureVibration::ApplyConfiguration() { + auto& players = Settings::values.players.GetValue(); + + for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { + players[i].vibration_enabled = vibration_groupboxes[i]->isChecked(); + players[i].vibration_strength = vibration_spinboxes[i]->value(); + } + + Settings::values.enable_accurate_vibrations.SetValue( + ui->checkBoxAccurateVibration->isChecked()); +} + +void ConfigureVibration::SetVibrationDevices(std::size_t player_index) { + using namespace Settings::NativeButton; + static constexpr std::array<std::array<Settings::NativeButton::Values, 6>, 2> buttons{{ + {DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons + {A, B, X, Y, R, ZR}, // Right Buttons + }}; + + auto& player = Settings::values.players.GetValue()[player_index]; + + for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) { + std::unordered_map<std::string, int> params_count; + + for (const auto button_index : buttons[device_idx]) { + const auto& player_button = player.buttons[button_index]; + + if (params_count.find(player_button) != params_count.end()) { + ++params_count[player_button]; + continue; + } + + params_count.insert_or_assign(player_button, 1); + } + + const auto it = std::max_element( + params_count.begin(), params_count.end(), + [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; }); + + auto& vibration_param_str = player.vibrations[device_idx]; + vibration_param_str.clear(); + + if (it->first.empty()) { + continue; + } + + const auto param = Common::ParamPackage(it->first); + + const auto engine = param.Get("engine", ""); + const auto guid = param.Get("guid", ""); + const auto port = param.Get("port", ""); + + if (engine.empty() || engine == "keyboard" || engine == "mouse") { + continue; + } + + vibration_param_str += fmt::format("engine:{}", engine); + + if (!port.empty()) { + vibration_param_str += fmt::format(",port:{}", port); + } + if (!guid.empty()) { + vibration_param_str += fmt::format(",guid:{}", guid); + } + } + + if (player.vibrations[0] != player.vibrations[1]) { + return; + } + + if (!player.vibrations[0].empty() && + player.controller_type != Settings::ControllerType::RightJoycon) { + player.vibrations[1].clear(); + } else if (!player.vibrations[1].empty() && + player.controller_type == Settings::ControllerType::RightJoycon) { + player.vibrations[0].clear(); + } +} + +void ConfigureVibration::SetAllVibrationDevices() { + // Set vibration devices for all player indices including handheld + for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) { + SetVibrationDevices(player_idx); + } +} + +void ConfigureVibration::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QDialog::changeEvent(event); +} + +void ConfigureVibration::RetranslateUI() { + ui->retranslateUi(this); +} diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h new file mode 100644 index 000000000..07411a86f --- /dev/null +++ b/src/yuzu/configuration/configure_vibration.h @@ -0,0 +1,43 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <memory> +#include <QDialog> + +class QGroupBox; +class QSpinBox; + +namespace Ui { +class ConfigureVibration; +} + +class ConfigureVibration : public QDialog { + Q_OBJECT + +public: + explicit ConfigureVibration(QWidget* parent); + ~ConfigureVibration() override; + + void ApplyConfiguration(); + + static void SetVibrationDevices(std::size_t player_index); + static void SetAllVibrationDevices(); + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + std::unique_ptr<Ui::ConfigureVibration> ui; + + static constexpr std::size_t NUM_PLAYERS = 8; + + // Groupboxes encapsulating the vibration strength spinbox. + std::array<QGroupBox*, NUM_PLAYERS> vibration_groupboxes; + + // Spinboxes representing the vibration strength percentage. + std::array<QSpinBox*, NUM_PLAYERS> vibration_spinboxes; +}; diff --git a/src/yuzu/configuration/configure_vibration.ui b/src/yuzu/configuration/configure_vibration.ui new file mode 100644 index 000000000..efdf317a9 --- /dev/null +++ b/src/yuzu/configuration/configure_vibration.ui @@ -0,0 +1,546 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureVibration</class> + <widget class="QDialog" name="ConfigureVibration"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>364</width> + <height>242</height> + </rect> + </property> + <property name="windowTitle"> + <string>Configure Vibration</string> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <layout class="QVBoxLayout"> + <item> + <widget class="QGroupBox" name="vibrationStrengthGroup"> + <property name="title"> + <string>Vibration</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,0"> + <property name="leftMargin"> + <number>9</number> + </property> + <property name="topMargin"> + <number>9</number> + </property> + <property name="rightMargin"> + <number>9</number> + </property> + <property name="bottomMargin"> + <number>9</number> + </property> + <item> + <widget class="QWidget" name="player14Widget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <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="QGroupBox" name="vibrationGroupPlayer1"> + <property name="title"> + <string>Player 1</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer1"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroupPlayer2"> + <property name="title"> + <string>Player 2</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_9"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer2"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroupPlayer3"> + <property name="title"> + <string>Player 3</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_10"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer3"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroupPlayer4"> + <property name="title"> + <string>Player 4</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_11"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer4"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player58Widget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_6"> + <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="QGroupBox" name="vibrationGroupPlayer7"> + <property name="title"> + <string>Player 5</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_14"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer7"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroupPlayer8"> + <property name="title"> + <string>Player 6</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_15"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer8"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroupPlayer5"> + <property name="title"> + <string>Player 7</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_12"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer5"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroupPlayer6"> + <property name="title"> + <string>Player 8</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_13"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer6"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationSettingsGroup"> + <property name="title"> + <string>Settings</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="checkBoxAccurateVibration"> + <property name="text"> + <string>Enable Accurate Vibration</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="spacerVibration"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>167</width> + <height>55</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBoxVibration"> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBoxVibration</sender> + <signal>accepted()</signal> + <receiver>ConfigureVibration</receiver> + <slot>accept()</slot> + </connection> + <connection> + <sender>buttonBoxVibration</sender> + <signal>rejected()</signal> + <receiver>ConfigureVibration</receiver> + <slot>reject()</slot> + </connection> + </connections> +</ui> diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp new file mode 100644 index 000000000..e87aededb --- /dev/null +++ b/src/yuzu/configuration/input_profiles.cpp @@ -0,0 +1,131 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <fmt/format.h> + +#include "common/common_paths.h" +#include "common/file_util.h" +#include "yuzu/configuration/config.h" +#include "yuzu/configuration/input_profiles.h" + +namespace FS = Common::FS; + +namespace { + +bool ProfileExistsInFilesystem(std::string_view profile_name) { + return FS::Exists(fmt::format("{}input" DIR_SEP "{}.ini", + FS::GetUserPath(FS::UserPath::ConfigDir), profile_name)); +} + +bool IsINI(std::string_view filename) { + const std::size_t index = filename.rfind('.'); + + if (index == std::string::npos) { + return false; + } + + return filename.substr(index) == ".ini"; +} + +std::string GetNameWithoutExtension(const std::string& filename) { + const std::size_t index = filename.rfind('.'); + + if (index == std::string::npos) { + return filename; + } + + return filename.substr(0, index); +} + +} // namespace + +InputProfiles::InputProfiles() { + const std::string input_profile_loc = + fmt::format("{}input", FS::GetUserPath(FS::UserPath::ConfigDir)); + + FS::ForeachDirectoryEntry( + nullptr, input_profile_loc, + [this](u64* entries_out, const std::string& directory, const std::string& filename) { + if (IsINI(filename) && IsProfileNameValid(GetNameWithoutExtension(filename))) { + map_profiles.insert_or_assign( + GetNameWithoutExtension(filename), + std::make_unique<Config>(GetNameWithoutExtension(filename), + Config::ConfigType::InputProfile)); + } + return true; + }); +} + +InputProfiles::~InputProfiles() = default; + +std::vector<std::string> InputProfiles::GetInputProfileNames() { + std::vector<std::string> profile_names; + profile_names.reserve(map_profiles.size()); + + for (const auto& [profile_name, config] : map_profiles) { + if (!ProfileExistsInFilesystem(profile_name)) { + DeleteProfile(profile_name); + continue; + } + + profile_names.push_back(profile_name); + } + + return profile_names; +} + +bool InputProfiles::IsProfileNameValid(std::string_view profile_name) { + return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos; +} + +bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t player_index) { + if (ProfileExistsInMap(profile_name)) { + return false; + } + + map_profiles.insert_or_assign( + profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile)); + + return SaveProfile(profile_name, player_index); +} + +bool InputProfiles::DeleteProfile(const std::string& profile_name) { + if (!ProfileExistsInMap(profile_name)) { + return false; + } + + if (!ProfileExistsInFilesystem(profile_name) || + FS::Delete(map_profiles[profile_name]->GetConfigFilePath())) { + map_profiles.erase(profile_name); + } + + return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name); +} + +bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t player_index) { + if (!ProfileExistsInMap(profile_name)) { + return false; + } + + if (!ProfileExistsInFilesystem(profile_name)) { + map_profiles.erase(profile_name); + return false; + } + + map_profiles[profile_name]->ReadControlPlayerValue(player_index); + return true; +} + +bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t player_index) { + if (!ProfileExistsInMap(profile_name)) { + return false; + } + + map_profiles[profile_name]->SaveControlPlayerValue(player_index); + return true; +} + +bool InputProfiles::ProfileExistsInMap(const std::string& profile_name) const { + return map_profiles.find(profile_name) != map_profiles.end(); +} diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h new file mode 100644 index 000000000..cb41fd9be --- /dev/null +++ b/src/yuzu/configuration/input_profiles.h @@ -0,0 +1,32 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <string_view> +#include <unordered_map> + +class Config; + +class InputProfiles { + +public: + explicit InputProfiles(); + virtual ~InputProfiles(); + + std::vector<std::string> GetInputProfileNames(); + + static bool IsProfileNameValid(std::string_view profile_name); + + bool CreateProfile(const std::string& profile_name, std::size_t player_index); + bool DeleteProfile(const std::string& profile_name); + bool LoadProfile(const std::string& profile_name, std::size_t player_index); + bool SaveProfile(const std::string& profile_name, std::size_t player_index); + +private: + bool ProfileExistsInMap(const std::string& profile_name) const; + + std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles; +}; diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index e0ce45fd9..23643aea2 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -235,12 +235,11 @@ GameListWorker::~GameListWorker() = default; void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { using namespace FileSys; - const auto& cache = - dynamic_cast<ContentProviderUnion&>(Core::System::GetInstance().GetContentProvider()); + auto& system = Core::System::GetInstance(); + const auto& cache = dynamic_cast<ContentProviderUnion&>(system.GetContentProvider()); - std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> installed_games; - installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application, - ContentRecordType::Program); + auto installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application, + ContentRecordType::Program); if (parent_dir->type() == static_cast<int>(GameListItemType::SdmcDir)) { installed_games = cache.ListEntriesFilterOrigin( @@ -254,23 +253,27 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { } for (const auto& [slot, game] : installed_games) { - if (slot == ContentProviderUnionSlot::FrontendManual) + if (slot == ContentProviderUnionSlot::FrontendManual) { continue; + } const auto file = cache.GetEntryUnparsed(game.title_id, game.type); - std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); - if (!loader) + std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(system, file); + if (!loader) { continue; + } std::vector<u8> icon; std::string name; u64 program_id = 0; loader->ReadProgramId(program_id); - const PatchManager patch{program_id}; + const PatchManager patch{program_id, system.GetFileSystemController(), + system.GetContentProvider()}; const auto control = cache.GetEntry(game.title_id, ContentRecordType::Control); - if (control != nullptr) + if (control != nullptr) { GetMetadataFromControlNCA(patch, *control, icon, name); + } emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id, compatibility_list, patch), @@ -280,9 +283,11 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, unsigned int recursion, GameListDir* parent_dir) { - const auto callback = [this, target, recursion, - parent_dir](u64* num_entries_out, const std::string& directory, - const std::string& virtual_name) -> bool { + auto& system = Core::System::GetInstance(); + + const auto callback = [this, target, recursion, parent_dir, + &system](u64* num_entries_out, const std::string& directory, + const std::string& virtual_name) -> bool { if (stop_processing) { // Breaks the callback loop. return false; @@ -293,7 +298,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa if (!is_dir && (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); - auto loader = Loader::GetLoader(file); + auto loader = Loader::GetLoader(system, file); if (!loader) { return true; } @@ -331,7 +336,8 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa std::string name = " "; [[maybe_unused]] const auto res3 = loader->ReadTitle(name); - const FileSys::PatchManager patch{program_id}; + const FileSys::PatchManager patch{program_id, system.GetFileSystemController(), + system.GetContentProvider()}; emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, compatibility_list, patch), diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 18e68e590..805619ccf 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -18,6 +18,7 @@ #include "applets/web_browser.h" #include "configuration/configure_input.h" #include "configuration/configure_per_game.h" +#include "configuration/configure_vibration.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" #include "core/frontend/applets/controller.h" @@ -50,12 +51,14 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include <QDesktopServices> #include <QDesktopWidget> #include <QDialogButtonBox> +#include <QDir> #include <QFile> #include <QFileDialog> #include <QInputDialog> #include <QMessageBox> #include <QProgressBar> #include <QProgressDialog> +#include <QPushButton> #include <QShortcut> #include <QStatusBar> #include <QSysInfo> @@ -277,6 +280,8 @@ GMainWindow::GMainWindow() if (args.length() >= 2) { BootGame(args[1]); } + + MigrateConfigFiles(); } GMainWindow::~GMainWindow() { @@ -288,6 +293,7 @@ GMainWindow::~GMainWindow() { void GMainWindow::ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters) { QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get()); + dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint); dialog.setWindowModality(Qt::WindowModal); @@ -547,13 +553,14 @@ void GMainWindow::InitializeWidgets() { dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); dock_status_button->setFocusPolicy(Qt::NoFocus); connect(dock_status_button, &QPushButton::clicked, [&] { - Settings::values.use_docked_mode = !Settings::values.use_docked_mode; - dock_status_button->setChecked(Settings::values.use_docked_mode); - OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode); + Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue()); + dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); + OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(), + Settings::values.use_docked_mode.GetValue()); }); dock_status_button->setText(tr("DOCK")); dock_status_button->setCheckable(true); - dock_status_button->setChecked(Settings::values.use_docked_mode); + dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); statusBar()->insertPermanentWidget(0, dock_status_button); // Setup ASync button @@ -792,10 +799,11 @@ void GMainWindow::InitializeHotkeys() { }); connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Change Docked Mode"), this), &QShortcut::activated, this, [&] { - Settings::values.use_docked_mode = !Settings::values.use_docked_mode; - OnDockedModeChanged(!Settings::values.use_docked_mode, - Settings::values.use_docked_mode); - dock_status_button->setChecked(Settings::values.use_docked_mode); + Settings::values.use_docked_mode.SetValue( + !Settings::values.use_docked_mode.GetValue()); + OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(), + Settings::values.use_docked_mode.GetValue()); + dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); }); connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this), &QShortcut::activated, this, @@ -970,7 +978,7 @@ void GMainWindow::AllowOSSleep() { #endif } -bool GMainWindow::LoadROM(const QString& filename) { +bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) { // Shutdown previous session if the emu thread is still active... if (emu_thread != nullptr) ShutdownGame(); @@ -995,7 +1003,8 @@ bool GMainWindow::LoadROM(const QString& filename) { system.RegisterHostThread(); - const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; + const Core::System::ResultStatus result{ + system.Load(*render_window, filename.toStdString(), program_index)}; const auto drd_callout = (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0; @@ -1077,26 +1086,32 @@ void GMainWindow::SelectAndSetCurrentUser() { Settings::values.current_user = dialog.GetIndex(); } -void GMainWindow::BootGame(const QString& filename) { +void GMainWindow::BootGame(const QString& filename, std::size_t program_index) { LOG_INFO(Frontend, "yuzu starting..."); StoreRecentFile(filename); // Put the filename on top of the list u64 title_id{0}; + last_filename_booted = filename; + + auto& system = Core::System::GetInstance(); const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData()); - const auto loader = Loader::GetLoader(v_file); + const auto loader = Loader::GetLoader(system, v_file, program_index); + if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) { // Load per game settings - Config per_game_config(fmt::format("{:016X}.ini", title_id), false); + Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig); } + ConfigureVibration::SetAllVibrationDevices(); + Settings::LogSettings(); if (UISettings::values.select_user_on_boot) { SelectAndSetCurrentUser(); } - if (!LoadROM(filename)) + if (!LoadROM(filename, program_index)) return; // Create and start the emulation thread @@ -1104,6 +1119,10 @@ void GMainWindow::BootGame(const QString& filename) { emit EmulationStarting(emu_thread.get()); emu_thread->start(); + // Register an ExecuteProgram callback such that Core can execute a sub-program + system.RegisterExecuteProgramCallback( + [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); }); + connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views // before the CPU continues @@ -1134,9 +1153,13 @@ void GMainWindow::BootGame(const QString& filename) { std::string title_name; std::string title_version; - const auto res = Core::System::GetInstance().GetGameName(title_name); + const auto res = system.GetGameName(title_name); - const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata(); + const auto metadata = [&system, title_id] { + const FileSys::PatchManager pm(title_id, system.GetFileSystemController(), + system.GetContentProvider()); + return pm.GetControlMetadata(); + }(); if (metadata.first != nullptr) { title_version = metadata.first->GetVersionString(); title_name = metadata.first->GetApplicationName(); @@ -1147,7 +1170,7 @@ void GMainWindow::BootGame(const QString& filename) { LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); UpdateWindowTitle(title_name, title_version); - loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); + loading_screen->Prepare(system.GetAppLoader()); loading_screen->show(); emulation_running = true; @@ -1266,16 +1289,18 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target const std::string& game_path) { std::string path; QString open_target; + auto& system = Core::System::GetInstance(); - const auto [user_save_size, device_save_size] = [this, &program_id, &game_path] { - FileSys::PatchManager pm{program_id}; + const auto [user_save_size, device_save_size] = [this, &game_path, &program_id, &system] { + const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), + system.GetContentProvider()}; const auto control = pm.GetControlMetadata().first; if (control != nullptr) { return std::make_pair(control->GetDefaultNormalSaveSize(), control->GetDeviceSaveDataSize()); } else { const auto file = Core::GetGameFileFromPath(vfs, game_path); - const auto loader = Loader::GetLoader(file); + const auto loader = Loader::GetLoader(system, file); FileSys::NACP nacp{}; loader->ReadControlData(nacp); @@ -1577,7 +1602,8 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id) { const QString config_dir = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir)); const QString custom_config_file_path = - config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id)); + config_dir + QStringLiteral("custom") + QDir::separator() + + QString::fromStdString(fmt::format("{:016X}.ini", program_id)); if (!QFile::exists(custom_config_file_path)) { QMessageBox::warning(this, tr("Error Removing Custom Configuration"), @@ -1601,7 +1627,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa "cancelled the operation.")); }; - const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); + auto& system = Core::System::GetInstance(); + const auto loader = Loader::GetLoader(system, vfs->OpenFile(game_path, FileSys::Mode::Read)); if (loader == nullptr) { failed(); return; @@ -1613,7 +1640,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } - const auto& installed = Core::System::GetInstance().GetContentProvider(); + const auto& installed = system.GetContentProvider(); const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); if (!romfs_title_id) { @@ -1628,7 +1655,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa if (*romfs_title_id == program_id) { const u64 ivfc_offset = loader->ReadRomFSIVFCOffset(); - FileSys::PatchManager pm{program_id}; + const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), installed}; romfs = pm.PatchRomFS(file, ivfc_offset, FileSys::ContentRecordType::Program); } else { romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS(); @@ -1745,7 +1772,8 @@ void GMainWindow::OnGameListShowList(bool show) { void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { u64 title_id{}; const auto v_file = Core::GetGameFileFromPath(vfs, file); - const auto loader = Loader::GetLoader(v_file); + const auto loader = Loader::GetLoader(Core::System::GetInstance(), v_file); + if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { QMessageBox::information(this, tr("Properties"), tr("The game properties could not be loaded.")); @@ -2117,6 +2145,11 @@ void GMainWindow::OnLoadComplete() { loading_screen->OnLoadComplete(); } +void GMainWindow::OnExecuteProgram(std::size_t program_index) { + ShutdownGame(); + BootGame(last_filename_booted, program_index); +} + void GMainWindow::ErrorDisplayDisplayError(QString body) { QMessageBox::critical(this, tr("Error Display"), body); emit ErrorDisplayFinished(); @@ -2393,6 +2426,29 @@ void GMainWindow::OnCaptureScreenshot() { OnStartGame(); } +// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant +void GMainWindow::MigrateConfigFiles() { + const std::string& config_dir_str = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir); + const QDir config_dir = QDir(QString::fromStdString(config_dir_str)); + const QStringList config_dir_list = config_dir.entryList(QStringList(QStringLiteral("*.ini"))); + + Common::FS::CreateFullPath(fmt::format("{}custom" DIR_SEP, config_dir_str)); + for (QStringList::const_iterator it = config_dir_list.constBegin(); + it != config_dir_list.constEnd(); ++it) { + const auto filename = it->toStdString(); + if (filename.find_first_not_of("0123456789abcdefACBDEF", 0) < 16) { + continue; + } + const auto origin = fmt::format("{}{}", config_dir_str, filename); + const auto destination = fmt::format("{}custom" DIR_SEP "{}", config_dir_str, filename); + LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination); + if (!Common::FS::Rename(origin, destination)) { + // Delete the old config file if one already exists in the new location. + Common::FS::Delete(origin); + } + } +} + void GMainWindow::UpdateWindowTitle(const std::string& title_name, const std::string& title_version) { const auto full_name = std::string(Common::g_build_fullname); @@ -2450,7 +2506,7 @@ void GMainWindow::UpdateStatusBar() { } void GMainWindow::UpdateStatusButtons() { - dock_status_button->setChecked(Settings::values.use_docked_mode); + dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); Settings::values.use_asynchronous_gpu_emulation.SetValue( Settings::values.use_asynchronous_gpu_emulation.GetValue() || diff --git a/src/yuzu/main.h b/src/yuzu/main.h index afcfa68a9..6242341d1 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -131,6 +131,7 @@ signals: public slots: void OnLoadComplete(); + void OnExecuteProgram(std::size_t program_index); void ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters); void ErrorDisplayDisplayError(QString body); @@ -154,8 +155,8 @@ private: void PreventOSSleep(); void AllowOSSleep(); - bool LoadROM(const QString& filename); - void BootGame(const QString& filename); + bool LoadROM(const QString& filename, std::size_t program_index); + void BootGame(const QString& filename, std::size_t program_index = 0); void ShutdownGame(); void ShowTelemetryCallout(); @@ -251,6 +252,7 @@ private: std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); InstallResult InstallNSPXCI(const QString& filename); InstallResult InstallNCA(const QString& filename); + void MigrateConfigFiles(); void UpdateWindowTitle(const std::string& title_name = {}, const std::string& title_version = {}); void UpdateStatusBar(); @@ -316,6 +318,9 @@ private: // Install progress dialog QProgressDialog* install_progress; + // Last game booted, used for multi-process apps + QString last_filename_booted; + protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 334038ef9..e1adbbf2b 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -228,24 +228,24 @@ static const std::array<int, 8> keyboard_mods{ void Config::ReadValues() { // Controls - for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { const auto group = fmt::format("ControlsP{}", p); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - Settings::values.players[p].buttons[i] = + Settings::values.players.GetValue()[p].buttons[i] = sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param); - if (Settings::values.players[p].buttons[i].empty()) - Settings::values.players[p].buttons[i] = default_param; + if (Settings::values.players.GetValue()[p].buttons[i].empty()) + Settings::values.players.GetValue()[p].buttons[i] = default_param; } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][3], default_analogs[i][4], 0.5f); - Settings::values.players[p].analogs[i] = + Settings::values.players.GetValue()[p].analogs[i] = sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param); - if (Settings::values.players[p].analogs[i].empty()) - Settings::values.players[p].analogs[i] = default_param; + if (Settings::values.players.GetValue()[p].analogs[i].empty()) + Settings::values.players.GetValue()[p].analogs[i] = default_param; } } @@ -288,10 +288,12 @@ void Config::ReadValues() { Settings::values.debug_pad_analogs[i] = default_param; } - Settings::values.vibration_enabled = - sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true); - Settings::values.motion_enabled = - sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true); + Settings::values.vibration_enabled.SetValue( + sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true)); + Settings::values.enable_accurate_vibrations.SetValue( + sdl2_config->GetBoolean("ControlsGeneral", "enable_accurate_vibrations", false)); + Settings::values.motion_enabled.SetValue( + sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true)); Settings::values.touchscreen.enabled = sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); Settings::values.touchscreen.device = @@ -343,7 +345,8 @@ void Config::ReadValues() { Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", ""); // System - Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); + Settings::values.use_docked_mode.SetValue( + sdl2_config->GetBoolean("System", "use_docked_mode", false)); const auto size = sdl2_config->GetInteger("System", "users_size", 0); Settings::values.current_user = std::clamp<int>( diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 796e27df4..bcbbcd4ca 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -65,6 +65,14 @@ button_screenshot= lstick= rstick= +# Whether to enable or disable vibration +# 0: Disabled, 1 (default): Enabled +vibration_enabled= + +# Whether to enable or disable accurate vibrations +# 0 (default): Disabled, 1: Enabled +enable_accurate_vibrations= + # for motion input, the following devices are available: # - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: # - "update_period": update period in milliseconds (default to 100) diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 3a76c785f..14a23c71b 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -240,11 +240,11 @@ int main(int argc, char** argv) { system.CurrentProcess()->GetTitleID(), false, [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); - system.Run(); + void(system.Run()); while (emu_window->IsOpen()) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - system.Pause(); + void(system.Pause()); system.Shutdown(); detached_tasks.WaitForAllTasks(); diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp index bc273fb51..b6cdc7c1c 100644 --- a/src/yuzu_tester/config.cpp +++ b/src/yuzu_tester/config.cpp @@ -47,13 +47,13 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) { void Config::ReadValues() { // Controls - for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - Settings::values.players[p].buttons[i] = ""; + Settings::values.players.GetValue()[p].buttons[i] = ""; } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - Settings::values.players[p].analogs[i] = ""; + Settings::values.players.GetValue()[p].analogs[i] = ""; } } @@ -75,8 +75,9 @@ void Config::ReadValues() { Settings::values.debug_pad_analogs[i] = ""; } - Settings::values.vibration_enabled = true; - Settings::values.motion_enabled = true; + Settings::values.vibration_enabled.SetValue(true); + Settings::values.enable_accurate_vibrations.SetValue(false); + Settings::values.motion_enabled.SetValue(true); Settings::values.touchscreen.enabled = ""; Settings::values.touchscreen.device = ""; Settings::values.touchscreen.finger = 0; @@ -84,8 +85,8 @@ void Config::ReadValues() { Settings::values.touchscreen.diameter_x = 15; Settings::values.touchscreen.diameter_y = 15; - Settings::values.use_docked_mode = - sdl2_config->GetBoolean("Controls", "use_docked_mode", false); + Settings::values.use_docked_mode.SetValue( + sdl2_config->GetBoolean("Controls", "use_docked_mode", false)); // Data Storage Settings::values.use_virtual_sd = diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index 5798ce43a..88e4bd1f7 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp @@ -256,11 +256,11 @@ int main(int argc, char** argv) { system.GPU().Start(); - system.Run(); + void(system.Run()); while (!finished) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - system.Pause(); + void(system.Pause()); detached_tasks.WaitForAllTasks(); return return_value; |