From efb3a9f4b23d3e056ed9736ee6d61b61299a168e Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Sat, 17 Dec 2022 10:43:51 +0100 Subject: [PATCH] early-access version 0032 --- README.md | 2 +- src/audio_core/device/audio_buffers.h | 8 +- src/audio_core/device/device_session.cpp | 6 + src/audio_core/device/device_session.h | 5 + src/audio_core/in/audio_in_system.cpp | 7 +- src/audio_core/out/audio_out_system.cpp | 7 +- src/common/assert.h | 4 +- src/common/settings.cpp | 1 + src/common/settings.h | 1 + src/core/hle/kernel/hle_ipc.cpp | 22 ++- src/core/hle/kernel/k_process.cpp | 11 ++ src/core/hle/kernel/k_process.h | 3 + src/core/hle/kernel/svc.cpp | 130 ++++++-------- src/core/hle/service/set/set.cpp | 9 +- src/core/hle/service/set/set.h | 1 + src/core/hle/service/set/set_sys.cpp | 10 +- src/core/hle/service/set/set_sys.h | 1 + src/yuzu/bootmanager.cpp | 7 +- src/yuzu/configuration/config.cpp | 3 + src/yuzu/configuration/configure_system.cpp | 4 + src/yuzu/configuration/configure_system.ui | 14 ++ src/yuzu/game_list.cpp | 14 ++ src/yuzu/game_list.h | 7 + src/yuzu/main.cpp | 184 +++++++++++++++++++- src/yuzu/main.h | 7 + src/yuzu/uisettings.h | 1 + 26 files changed, 366 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 0e3675181..17ebd2eb9 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 3217. +This is the source code for early-access 0032. ## Legal Notice diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h index 986590c6d..1e1616f4e 100755 --- a/src/audio_core/device/audio_buffers.h +++ b/src/audio_core/device/audio_buffers.h @@ -91,9 +91,10 @@ public: * @param core_timing - The CoreTiming instance * @param session - The device session * - * @return Is the buffer was released. + * @return If any buffer was released. */ - bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session) { + bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session, + bool force) { std::scoped_lock l{lock}; bool buffer_released{false}; while (registered_count > 0) { @@ -103,7 +104,8 @@ public: } // Check with the backend if this buffer can be released yet. - if (!session.IsBufferConsumed(buffers[index])) { + // If we're shutting down, we don't care if it's been played or not. + if (!force && !session.IsBufferConsumed(buffers[index])) { break; } diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index c1b270cac..f60bd14e9 100755 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp @@ -73,6 +73,12 @@ void DeviceSession::Stop() { } } +void DeviceSession::ClearBuffers() { + if (stream) { + stream->ClearQueue(); + } +} + void DeviceSession::AppendBuffers(std::span buffers) const { for (const auto& buffer : buffers) { Sink::SinkBuffer new_buffer{ diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h index 676580ea9..f65c850bd 100755 --- a/src/audio_core/device/device_session.h +++ b/src/audio_core/device/device_session.h @@ -90,6 +90,11 @@ public: */ void Stop(); + /** + * Clear out the underlying audio buffers in the backend stream. + */ + void ClearBuffers(); + /** * Set this device session's volume. * diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index 46b0cf2b3..45aade449 100755 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp @@ -23,7 +23,6 @@ System::~System() { void System::Finalize() { Stop(); session->Finalize(); - buffer_event->Signal(); } void System::StartSession() { @@ -102,6 +101,10 @@ Result System::Stop() { if (state == State::Started) { session->Stop(); session->SetVolume(0.0f); + session->ClearBuffers(); + if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) { + buffer_event->Signal(); + } state = State::Stopped; } @@ -138,7 +141,7 @@ void System::RegisterBuffers() { } void System::ReleaseBuffers() { - bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; + bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)}; if (signal) { // Signal if any buffer was released, or if none are registered, we need more. diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index 432820128..9f75cd1a8 100755 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp @@ -24,7 +24,6 @@ System::~System() { void System::Finalize() { Stop(); session->Finalize(); - buffer_event->Signal(); } std::string_view System::GetDefaultOutputDeviceName() const { @@ -102,6 +101,10 @@ Result System::Stop() { if (state == State::Started) { session->Stop(); session->SetVolume(0.0f); + session->ClearBuffers(); + if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) { + buffer_event->Signal(); + } state = State::Stopped; } @@ -138,7 +141,7 @@ void System::RegisterBuffers() { } void System::ReleaseBuffers() { - bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; + bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)}; if (signal) { // Signal if any buffer was released, or if none are registered, we need more. buffer_event->Signal(); diff --git a/src/common/assert.h b/src/common/assert.h index eef2055ed..9bac95677 100755 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -69,7 +69,7 @@ void assert_fail_impl(); #define ASSERT_OR_EXECUTE(_a_, _b_) \ do { \ ASSERT(_a_); \ - if (!(_a_)) { \ + if (!(_a_)) [[unlikely]] { \ _b_ \ } \ } while (0) @@ -78,7 +78,7 @@ void assert_fail_impl(); #define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \ do { \ ASSERT_MSG(_a_, __VA_ARGS__); \ - if (!(_a_)) { \ + if (!(_a_)) [[unlikely]] { \ _b_ \ } \ } while (0) diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 0a5fffd25..d11d7620e 100755 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -40,6 +40,7 @@ void LogSettings() { LOG_INFO(Config, "yuzu Configuration:"); log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); + log_setting("System_DeviceName", values.device_name.GetValue()); log_setting("System_CurrentUser", values.current_user.GetValue()); log_setting("System_LanguageIndex", values.language_index.GetValue()); log_setting("System_RegionIndex", values.region_index.GetValue()); diff --git a/src/common/settings.h b/src/common/settings.h index 5bc31c12b..4c32f3ee8 100755 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -458,6 +458,7 @@ struct Values { // System SwitchableSetting> rng_seed{std::optional(), "rng_seed"}; + Setting device_name{"Yuzu", "device_name"}; // Measured in seconds since epoch std::optional custom_rtc; // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index a3bfae208..b2879a24d 100755 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -167,6 +167,9 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 } if (incoming) { // Populate the object lists with the data in the IPC request. + incoming_copy_handles.reserve(handle_descriptor_header->num_handles_to_copy); + incoming_move_handles.reserve(handle_descriptor_header->num_handles_to_move); + for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { incoming_copy_handles.push_back(rp.Pop()); } @@ -181,6 +184,11 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 } } + buffer_x_desciptors.reserve(command_header->num_buf_x_descriptors); + buffer_a_desciptors.reserve(command_header->num_buf_a_descriptors); + buffer_b_desciptors.reserve(command_header->num_buf_b_descriptors); + buffer_w_desciptors.reserve(command_header->num_buf_w_descriptors); + for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) { buffer_x_desciptors.push_back(rp.PopRaw()); } @@ -318,25 +326,23 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa } std::vector HLERequestContext::ReadBuffer(std::size_t buffer_index) const { - std::vector buffer{}; const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && BufferDescriptorA()[buffer_index].Size()}; - if (is_buffer_a) { ASSERT_OR_EXECUTE_MSG( - BufferDescriptorA().size() > buffer_index, { return buffer; }, + BufferDescriptorA().size() > buffer_index, { return {}; }, "BufferDescriptorA invalid buffer_index {}", buffer_index); - buffer.resize(BufferDescriptorA()[buffer_index].Size()); + std::vector buffer(BufferDescriptorA()[buffer_index].Size()); memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); + return buffer; } else { ASSERT_OR_EXECUTE_MSG( - BufferDescriptorX().size() > buffer_index, { return buffer; }, + BufferDescriptorX().size() > buffer_index, { return {}; }, "BufferDescriptorX invalid buffer_index {}", buffer_index); - buffer.resize(BufferDescriptorX()[buffer_index].Size()); + std::vector buffer(BufferDescriptorX()[buffer_index].Size()); memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); + return buffer; } - - return buffer; } std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 09864c478..33dee56ae 100755 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -285,6 +285,17 @@ void KProcess::UnregisterThread(KThread* thread) { thread_list.remove(thread); } +u64 KProcess::GetFreeThreadCount() const { + if (resource_limit != nullptr) { + const auto current_value = + resource_limit->GetCurrentValue(LimitableResource::ThreadCountMax); + const auto limit_value = resource_limit->GetLimitValue(LimitableResource::ThreadCountMax); + return limit_value - current_value; + } else { + return 0; + } +} + Result KProcess::Reset() { // Lock the process and the scheduler. KScopedLightLock lk(state_lock); diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 9eed67dcd..2cf132bef 100755 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -304,6 +304,9 @@ public: /// from this process' thread list. void UnregisterThread(KThread* thread); + /// Retrieves the number of available threads for this process. + u64 GetFreeThreadCount() const; + /// Clears the signaled state of the process if and only if it's signaled. /// /// @pre The process must not be already terminated. If this is called on a diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index f564e8690..735a74258 100755 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -784,63 +784,29 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, info_sub_id, handle); - enum class GetInfoType : u64 { - // 1.0.0+ - AllowedCPUCoreMask = 0, - AllowedThreadPriorityMask = 1, - MapRegionBaseAddr = 2, - MapRegionSize = 3, - HeapRegionBaseAddr = 4, - HeapRegionSize = 5, - TotalPhysicalMemoryAvailable = 6, - TotalPhysicalMemoryUsed = 7, - IsCurrentProcessBeingDebugged = 8, - RegisterResourceLimit = 9, - IdleTickCount = 10, - RandomEntropy = 11, - ThreadTickCount = 0xF0000002, - // 2.0.0+ - ASLRRegionBaseAddr = 12, - ASLRRegionSize = 13, - StackRegionBaseAddr = 14, - StackRegionSize = 15, - // 3.0.0+ - SystemResourceSize = 16, - SystemResourceUsage = 17, - TitleId = 18, - // 4.0.0+ - PrivilegedProcessId = 19, - // 5.0.0+ - UserExceptionContextAddr = 20, - // 6.0.0+ - TotalPhysicalMemoryAvailableWithoutSystemResource = 21, - TotalPhysicalMemoryUsedWithoutSystemResource = 22, - - // Homebrew only - MesosphereCurrentProcess = 65001, - }; - - const auto info_id_type = static_cast(info_id); + const auto info_id_type = static_cast(info_id); switch (info_id_type) { - case GetInfoType::AllowedCPUCoreMask: - case GetInfoType::AllowedThreadPriorityMask: - case GetInfoType::MapRegionBaseAddr: - case GetInfoType::MapRegionSize: - case GetInfoType::HeapRegionBaseAddr: - case GetInfoType::HeapRegionSize: - case GetInfoType::ASLRRegionBaseAddr: - case GetInfoType::ASLRRegionSize: - case GetInfoType::StackRegionBaseAddr: - case GetInfoType::StackRegionSize: - case GetInfoType::TotalPhysicalMemoryAvailable: - case GetInfoType::TotalPhysicalMemoryUsed: - case GetInfoType::SystemResourceSize: - case GetInfoType::SystemResourceUsage: - case GetInfoType::TitleId: - case GetInfoType::UserExceptionContextAddr: - case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: - case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: { + case InfoType::CoreMask: + case InfoType::PriorityMask: + case InfoType::AliasRegionAddress: + case InfoType::AliasRegionSize: + case InfoType::HeapRegionAddress: + case InfoType::HeapRegionSize: + case InfoType::AslrRegionAddress: + case InfoType::AslrRegionSize: + case InfoType::StackRegionAddress: + case InfoType::StackRegionSize: + case InfoType::TotalMemorySize: + case InfoType::UsedMemorySize: + case InfoType::SystemResourceSizeTotal: + case InfoType::SystemResourceSizeUsed: + case InfoType::ProgramId: + case InfoType::UserExceptionContextAddress: + case InfoType::TotalNonSystemMemorySize: + case InfoType::UsedNonSystemMemorySize: + case InfoType::IsApplication: + case InfoType::FreeThreadCount: { if (info_sub_id != 0) { LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, info_sub_id); @@ -856,79 +822,83 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han } switch (info_id_type) { - case GetInfoType::AllowedCPUCoreMask: + case InfoType::CoreMask: *result = process->GetCoreMask(); return ResultSuccess; - case GetInfoType::AllowedThreadPriorityMask: + case InfoType::PriorityMask: *result = process->GetPriorityMask(); return ResultSuccess; - case GetInfoType::MapRegionBaseAddr: + case InfoType::AliasRegionAddress: *result = process->PageTable().GetAliasRegionStart(); return ResultSuccess; - case GetInfoType::MapRegionSize: + case InfoType::AliasRegionSize: *result = process->PageTable().GetAliasRegionSize(); return ResultSuccess; - case GetInfoType::HeapRegionBaseAddr: + case InfoType::HeapRegionAddress: *result = process->PageTable().GetHeapRegionStart(); return ResultSuccess; - case GetInfoType::HeapRegionSize: + case InfoType::HeapRegionSize: *result = process->PageTable().GetHeapRegionSize(); return ResultSuccess; - case GetInfoType::ASLRRegionBaseAddr: + case InfoType::AslrRegionAddress: *result = process->PageTable().GetAliasCodeRegionStart(); return ResultSuccess; - case GetInfoType::ASLRRegionSize: + case InfoType::AslrRegionSize: *result = process->PageTable().GetAliasCodeRegionSize(); return ResultSuccess; - case GetInfoType::StackRegionBaseAddr: + case InfoType::StackRegionAddress: *result = process->PageTable().GetStackRegionStart(); return ResultSuccess; - case GetInfoType::StackRegionSize: + case InfoType::StackRegionSize: *result = process->PageTable().GetStackRegionSize(); return ResultSuccess; - case GetInfoType::TotalPhysicalMemoryAvailable: + case InfoType::TotalMemorySize: *result = process->GetTotalPhysicalMemoryAvailable(); return ResultSuccess; - case GetInfoType::TotalPhysicalMemoryUsed: + case InfoType::UsedMemorySize: *result = process->GetTotalPhysicalMemoryUsed(); return ResultSuccess; - case GetInfoType::SystemResourceSize: + case InfoType::SystemResourceSizeTotal: *result = process->GetSystemResourceSize(); return ResultSuccess; - case GetInfoType::SystemResourceUsage: + case InfoType::SystemResourceSizeUsed: LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); *result = process->GetSystemResourceUsage(); return ResultSuccess; - case GetInfoType::TitleId: + case InfoType::ProgramId: *result = process->GetProgramID(); return ResultSuccess; - case GetInfoType::UserExceptionContextAddr: + case InfoType::UserExceptionContextAddress: *result = process->GetProcessLocalRegionAddress(); return ResultSuccess; - case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: + case InfoType::TotalNonSystemMemorySize: *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); return ResultSuccess; - case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: + case InfoType::UsedNonSystemMemorySize: *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); return ResultSuccess; + case InfoType::FreeThreadCount: + *result = process->GetFreeThreadCount(); + return ResultSuccess; + default: break; } @@ -937,11 +907,11 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han return ResultInvalidEnumValue; } - case GetInfoType::IsCurrentProcessBeingDebugged: + case InfoType::DebuggerAttached: *result = 0; return ResultSuccess; - case GetInfoType::RegisterResourceLimit: { + case InfoType::ResourceLimit: { if (handle != 0) { LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle); return ResultInvalidHandle; @@ -969,7 +939,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han return ResultSuccess; } - case GetInfoType::RandomEntropy: + case InfoType::RandomEntropy: if (handle != 0) { LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", handle); @@ -985,13 +955,13 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); return ResultSuccess; - case GetInfoType::PrivilegedProcessId: + case InfoType::InitialProcessIdRange: LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query privileged process id bounds, returned 0"); *result = 0; return ResultSuccess; - case GetInfoType::ThreadTickCount: { + case InfoType::ThreadTickCount: { constexpr u64 num_cpus = 4; if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, @@ -1026,7 +996,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han *result = out_ticks; return ResultSuccess; } - case GetInfoType::IdleTickCount: { + case InfoType::IdleTickCount: { // Verify the input handle is invalid. R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); @@ -1040,7 +1010,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); return ResultSuccess; } - case GetInfoType::MesosphereCurrentProcess: { + case InfoType::MesosphereCurrentProcess: { // Verify the input handle is invalid. R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 524376505..34a3009fa 100755 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -191,6 +191,13 @@ void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) { GetKeyCodeMapImpl(ctx); } +void SET::GetDeviceNickName(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + ctx.WriteBuffer(Settings::values.device_name.GetValue()); +} + SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} { // clang-format off static const FunctionInfo functions[] = { @@ -205,7 +212,7 @@ SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} { {8, &SET::GetQuestFlag, "GetQuestFlag"}, {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"}, {10, nullptr, "GetFirmwareVersionForDebug"}, - {11, nullptr, "GetDeviceNickName"}, + {11, &SET::GetDeviceNickName, "GetDeviceNickName"}, }; // clang-format on diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 0c2f1e081..38fa16366 100755 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -50,6 +50,7 @@ private: void GetRegionCode(Kernel::HLERequestContext& ctx); void GetKeyCodeMap(Kernel::HLERequestContext& ctx); void GetKeyCodeMap2(Kernel::HLERequestContext& ctx); + void GetDeviceNickName(Kernel::HLERequestContext& ctx); }; } // namespace Service::Set diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index c7313450e..ae6770101 100755 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -3,6 +3,7 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/file_sys/errors.h" #include "core/file_sys/system_archive/system_version.h" #include "core/hle/ipc_helpers.h" @@ -176,6 +177,13 @@ void SET_SYS::GetSettingsItemValue(Kernel::HLERequestContext& ctx) { rb.Push(response); } +void SET_SYS::GetDeviceNickName(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + ctx.WriteBuffer(::Settings::values.device_name.GetValue()); +} + SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { // clang-format off static const FunctionInfo functions[] = { @@ -253,7 +261,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {74, nullptr, "SetWirelessLanEnableFlag"}, {75, nullptr, "GetInitialLaunchSettings"}, {76, nullptr, "SetInitialLaunchSettings"}, - {77, nullptr, "GetDeviceNickName"}, + {77, &SET_SYS::GetDeviceNickName, "GetDeviceNickName"}, {78, nullptr, "SetDeviceNickName"}, {79, nullptr, "GetProductModel"}, {80, nullptr, "GetLdnChannel"}, diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index 7add7f526..1116f6bb3 100755 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -29,6 +29,7 @@ private: void GetFirmwareVersion2(Kernel::HLERequestContext& ctx); void GetColorSetId(Kernel::HLERequestContext& ctx); void SetColorSetId(Kernel::HLERequestContext& ctx); + void GetDeviceNickName(Kernel::HLERequestContext& ctx); ColorSet color_set = ColorSet::BasicWhite; }; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 750902fa6..33aa66b33 100755 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -274,12 +274,14 @@ static Core::Frontend::WindowSystemType GetWindowSystemType() { return Core::Frontend::WindowSystemType::X11; else if (platform_name == QStringLiteral("wayland")) return Core::Frontend::WindowSystemType::Wayland; + else if (platform_name == QStringLiteral("wayland-egl")) + return Core::Frontend::WindowSystemType::Wayland; else if (platform_name == QStringLiteral("cocoa")) return Core::Frontend::WindowSystemType::Cocoa; else if (platform_name == QStringLiteral("android")) return Core::Frontend::WindowSystemType::Android; - LOG_CRITICAL(Frontend, "Unknown Qt platform!"); + LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString()); return Core::Frontend::WindowSystemType::Windows; } @@ -319,7 +321,8 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, input_subsystem->Initialize(); this->setMouseTracking(true); - strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland"); + strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") || + QGuiApplication::platformName() == QStringLiteral("wayland-egl"); connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 9f17d4f41..746dcdd8f 100755 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -783,6 +783,8 @@ void Config::ReadSystemValues() { } } + ReadBasicSetting(Settings::values.device_name); + if (global) { ReadBasicSetting(Settings::values.current_user); Settings::values.current_user = std::clamp(Settings::values.current_user.GetValue(), 0, @@ -1405,6 +1407,7 @@ void Config::SaveSystemValues() { Settings::values.rng_seed.UsingGlobal()); WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0), 0, Settings::values.rng_seed.UsingGlobal()); + WriteBasicSetting(Settings::values.device_name); if (global) { WriteBasicSetting(Settings::values.current_user); diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index b49a382bb..cf0d902b7 100755 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -72,6 +72,8 @@ void ConfigureSystem::SetConfiguration() { ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time)); + ui->device_name_edit->setText( + QString::fromUtf8(Settings::values.device_name.GetValue().c_str())); if (Settings::IsConfiguringGlobal()) { ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); @@ -115,6 +117,8 @@ void ConfigureSystem::ApplyConfiguration() { } } + Settings::values.device_name = ui->device_name_edit->text().toStdString(); + if (!enabled) { return; } diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index 70fec763b..c36060ef8 100755 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -432,6 +432,13 @@ + + + + Device Name + + + @@ -476,6 +483,13 @@ + + + + 128 + + + diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 2e9f4e76a..9476e3fce 100755 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -554,6 +554,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); +#ifndef WIN32 + QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); + QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); + QAction* create_applications_menu_shortcut = + shortcut_menu->addAction(tr("Add to Applications Menu")); +#endif context_menu.addSeparator(); QAction* properties = context_menu.addAction(tr("Properties")); @@ -619,6 +625,14 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); +#ifndef WIN32 + connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { + emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); + }); + connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { + emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); + }); +#endif connect(properties, &QAction::triggered, [this, path]() { emit OpenPerGameGeneralRequested(path); }); }; diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 5ee456ace..71b26d2b5 100755 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -52,6 +52,11 @@ enum class DumpRomFSTarget { SDMC, }; +enum class GameListShortcutTarget { + Desktop, + Applications, +}; + enum class InstalledEntryType { Game, Update, @@ -108,6 +113,8 @@ signals: const std::string& game_path); void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); void CopyTIDRequested(u64 program_id); + void CreateShortcut(u64 program_id, const std::string& game_path, + GameListShortcutTarget target); void NavigateToGamedbEntryRequested(u64 program_id, const CompatibilityList& compatibility_list); void OpenPerGameGeneralRequested(const std::string& file); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 47e2c865d..aa725d8c2 100755 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #ifdef __APPLE__ @@ -1248,6 +1250,7 @@ void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, &GMainWindow::OnGameListNavigateToGamedbEntry); + connect(game_list, &GameList::CreateShortcut, this, &GMainWindow::OnGameListCreateShortcut); connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); @@ -2378,6 +2381,152 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); } +void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, + GameListShortcutTarget target) { + // Get path to yuzu executable + const QStringList args = QApplication::arguments(); + std::filesystem::path yuzu_command = args[0].toStdString(); + +#if defined(__linux__) || defined(__FreeBSD__) + // If relative path, make it an absolute path + if (yuzu_command.c_str()[0] == '.') { + yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; + } + +#if defined(__linux__) + // Warn once if we are making a shortcut to a volatile AppImage + const std::string appimage_ending = + std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); + if (yuzu_command.string().ends_with(appimage_ending) && + !UISettings::values.shortcut_already_warned) { + if (QMessageBox::warning(this, tr("Create Shortcut"), + tr("This will create a shortcut to the current AppImage. This may " + "not work well if you update. Continue?"), + QMessageBox::StandardButton::Ok | + QMessageBox::StandardButton::Cancel) == + QMessageBox::StandardButton::Cancel) { + return; + } + UISettings::values.shortcut_already_warned = true; + } +#endif // __linux__ +#endif // __linux__ || __FreeBSD__ + + std::filesystem::path target_directory{}; + // Determine target directory for shortcut +#if defined(__linux__) || defined(__FreeBSD__) + const char* home = std::getenv("HOME"); + const std::filesystem::path home_path = (home == nullptr ? "~" : home); + const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); + + if (target == GameListShortcutTarget::Desktop) { + target_directory = home_path / "Desktop"; + if (!Common::FS::IsDir(target_directory)) { + QMessageBox::critical( + this, tr("Create Shortcut"), + tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.") + .arg(QString::fromStdString(target_directory)), + QMessageBox::StandardButton::Ok); + return; + } + } else if (target == GameListShortcutTarget::Applications) { + target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) / + "applications"; + if (!Common::FS::CreateDirs(target_directory)) { + QMessageBox::critical(this, tr("Create Shortcut"), + tr("Cannot create shortcut in applications menu. Path \"%1\" " + "does not exist and cannot be created.") + .arg(QString::fromStdString(target_directory)), + QMessageBox::StandardButton::Ok); + return; + } + } +#endif + + const std::string game_file_name = std::filesystem::path(game_path).filename().string(); + // Determine full paths for icon and shortcut +#if defined(__linux__) || defined(__FreeBSD__) + std::filesystem::path system_icons_path = + (xdg_data_home == nullptr ? home_path / ".local/share/" : xdg_data_home) / + "icons/hicolor/256x256"; + if (!Common::FS::CreateDirs(system_icons_path)) { + QMessageBox::critical( + this, tr("Create Icon"), + tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") + .arg(QString::fromStdString(system_icons_path)), + QMessageBox::StandardButton::Ok); + return; + } + std::filesystem::path icon_path = + system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name) + : fmt::format("yuzu-{:016X}.png", program_id)); + const std::filesystem::path shortcut_path = + target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) + : fmt::format("yuzu-{:016X}.desktop", program_id)); +#else + const std::filesystem::path icon_path{}; + const std::filesystem::path shortcut_path{}; +#endif + + // Get title from game file + const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), + system->GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); + + std::string title{fmt::format("{:016X}", program_id)}; + + if (control.first != nullptr) { + title = control.first->GetApplicationName(); + } else { + loader->ReadTitle(title); + } + + // Get icon from game file + std::vector icon_image_file{}; + if (control.second != nullptr) { + icon_image_file = control.second->ReadAllBytes(); + } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { + LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); + } + + QImage icon_jpeg = + QImage::fromData(icon_image_file.data(), static_cast(icon_image_file.size())); +#if defined(__linux__) || defined(__FreeBSD__) + // Convert and write the icon as a PNG + if (!icon_jpeg.save(QString::fromStdString(icon_path.string()))) { + LOG_ERROR(Frontend, "Could not write icon as PNG to file"); + } else { + LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); + } +#endif // __linux__ + +#if defined(__linux__) || defined(__FreeBSD__) + const std::string comment = + tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); + const std::string arguments = fmt::format("-g \"{:s}\"", game_path); + const std::string categories = "Game;Emulator;Qt;"; + const std::string keywords = "Switch;Nintendo;"; +#else + const std::string comment{}; + const std::string arguments{}; + const std::string categories{}; + const std::string keywords{}; +#endif + if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), + yuzu_command.string(), arguments, categories, keywords)) { + QMessageBox::critical(this, tr("Create Shortcut"), + tr("Failed to create a shortcut at %1") + .arg(QString::fromStdString(shortcut_path.string()))); + return; + } + + LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string()); + QMessageBox::information( + this, tr("Create Shortcut"), + tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title))); +} + void GMainWindow::OnGameListOpenDirectory(const QString& directory) { std::filesystem::path fs_path; if (directory == QStringLiteral("SDMC")) { @@ -2917,7 +3066,8 @@ static QScreen* GuessCurrentScreen(QWidget* window) { bool GMainWindow::UsingExclusiveFullscreen() { return Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive || - QGuiApplication::platformName() == QStringLiteral("wayland"); + QGuiApplication::platformName() == QStringLiteral("wayland") || + QGuiApplication::platformName() == QStringLiteral("wayland-egl"); } void GMainWindow::ShowFullscreen() { @@ -3301,6 +3451,38 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file } } +bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title, + const std::string& comment, const std::string& icon_path, + const std::string& command, const std::string& arguments, + const std::string& categories, const std::string& keywords) { +#if defined(__linux__) || defined(__FreeBSD__) + // This desktop file template was writting referencing + // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html + std::string shortcut_contents{}; + shortcut_contents.append("[Desktop Entry]\n"); + shortcut_contents.append("Type=Application\n"); + shortcut_contents.append("Version=1.0\n"); + shortcut_contents.append(fmt::format("Name={:s}\n", title)); + shortcut_contents.append(fmt::format("Comment={:s}\n", comment)); + shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path)); + shortcut_contents.append(fmt::format("TryExec={:s}\n", command)); + shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments)); + shortcut_contents.append(fmt::format("Categories={:s}\n", categories)); + shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords)); + + std::ofstream shortcut_stream(shortcut_path); + if (!shortcut_stream.is_open()) { + LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path); + return false; + } + shortcut_stream << shortcut_contents; + shortcut_stream.close(); + + return true; +#endif + return false; +} + void GMainWindow::OnLoadAmiibo() { if (emu_thread == nullptr || !emu_thread->IsRunning()) { return; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 7e6868ad2..29cf0205b 100755 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -38,6 +38,7 @@ class QProgressDialog; class WaitTreeWidget; enum class GameListOpenTarget; enum class GameListRemoveTarget; +enum class GameListShortcutTarget; enum class DumpRomFSTarget; enum class InstalledEntryType; class GameListPlaceholder; @@ -293,6 +294,8 @@ private slots: void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, const CompatibilityList& compatibility_list); + void OnGameListCreateShortcut(u64 program_id, const std::string& game_path, + GameListShortcutTarget target); void OnGameListOpenDirectory(const QString& directory); void OnGameListAddDirectory(); void OnGameListShowList(bool show); @@ -366,6 +369,10 @@ private: bool CheckDarkMode(); QString GetTasStateDescription() const; + bool CreateShortcut(const std::string& shortcut_path, const std::string& title, + const std::string& comment, const std::string& icon_path, + const std::string& command, const std::string& arguments, + const std::string& categories, const std::string& keywords); std::unique_ptr ui; diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 054a36c84..8941d7c6f 100755 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -138,6 +138,7 @@ struct Values { bool configuration_applied; bool reset_to_defaults; + bool shortcut_already_warned{false}; Settings::Setting disable_web_applet{true, "disable_web_applet"}; };