diff options
Diffstat (limited to 'src')
109 files changed, 3462 insertions, 1536 deletions
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index 22a3f8c84..11481a776 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -57,7 +57,9 @@ Stream::State Stream::GetState() const { s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; - return Core::Timing::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); + const auto us = + std::chrono::microseconds((static_cast<u64>(num_samples) * 1000000) / sample_rate); + return Core::Timing::usToCycles(us); } static void VolumeAdjustSamples(std::vector<s16>& samples) { diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 1e8e1b215..cb514a0d2 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -123,6 +123,8 @@ add_library(common STATIC timer.h uint128.cpp uint128.h + uuid.cpp + uuid.h vector_math.h web_result.h zstd_compression.cpp diff --git a/src/common/math_util.h b/src/common/math_util.h index cff3d48c5..d6c35ee89 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h @@ -41,4 +41,7 @@ struct Rectangle { } }; +template <typename T> +Rectangle(T, T, T, T)->Rectangle<T>; + } // namespace Common diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp new file mode 100644 index 000000000..26db03fba --- /dev/null +++ b/src/common/uuid.cpp @@ -0,0 +1,33 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <random> + +#include <fmt/format.h> + +#include "common/uuid.h" + +namespace Common { + +UUID UUID::Generate() { + std::random_device device; + std::mt19937 gen(device()); + std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); + return UUID{distribution(gen), distribution(gen)}; +} + +std::string UUID::Format() const { + return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); +} + +std::string UUID::FormatSwitch() const { + std::array<u8, 16> s{}; + std::memcpy(s.data(), uuid.data(), sizeof(u128)); + return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{" + ":02x}{:02x}{:02x}{:02x}{:02x}", + s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], + s[12], s[13], s[14], s[15]); +} + +} // namespace Common diff --git a/src/common/uuid.h b/src/common/uuid.h new file mode 100644 index 000000000..f6ad064fb --- /dev/null +++ b/src/common/uuid.h @@ -0,0 +1,48 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> + +#include "common/common_types.h" + +namespace Common { + +constexpr u128 INVALID_UUID{{0, 0}}; + +struct UUID { + // UUIDs which are 0 are considered invalid! + u128 uuid = INVALID_UUID; + constexpr UUID() = default; + constexpr explicit UUID(const u128& id) : uuid{id} {} + constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} + + constexpr explicit operator bool() const { + return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1]; + } + + constexpr bool operator==(const UUID& rhs) const { + // TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20 + return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1]; + } + + constexpr bool operator!=(const UUID& rhs) const { + return !operator==(rhs); + } + + // TODO(ogniK): Properly generate uuids based on RFC-4122 + static UUID Generate(); + + // Set the UUID to {0,0} to be considered an invalid user + constexpr void Invalidate() { + uuid = INVALID_UUID; + } + + std::string Format() const; + std::string FormatSwitch() const; +}; +static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); + +} // namespace Common diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2ace866ee..6bf512e12 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -310,6 +310,8 @@ add_library(core STATIC hle/service/mig/mig.h hle/service/mii/mii.cpp hle/service/mii/mii.h + hle/service/mii/mii_manager.cpp + hle/service/mii/mii_manager.h hle/service/mm/mm_u.cpp hle/service/mm/mm_u.h hle/service/ncm/ncm.cpp @@ -326,6 +328,9 @@ add_library(core STATIC hle/service/nim/nim.h hle/service/npns/npns.cpp hle/service/npns/npns.h + hle/service/ns/errors.h + hle/service/ns/language.cpp + hle/service/ns/language.h hle/service/ns/ns.cpp hle/service/ns/ns.h hle/service/ns/pl_u.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 7106151bd..ff0721079 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -18,11 +18,6 @@ #include "core/file_sys/registered_cache.h" #include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_real.h" -#include "core/frontend/applets/error.h" -#include "core/frontend/applets/general_frontend.h" -#include "core/frontend/applets/profile_select.h" -#include "core/frontend/applets/software_keyboard.h" -#include "core/frontend/applets/web_browser.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/kernel.h" @@ -37,9 +32,6 @@ #include "core/settings.h" #include "core/telemetry_session.h" #include "file_sys/cheat_engine.h" -#include "frontend/applets/profile_select.h" -#include "frontend/applets/software_keyboard.h" -#include "frontend/applets/web_browser.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" @@ -144,20 +136,10 @@ struct System::Impl { ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath) { app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); - if (!app_loader) { LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); return ResultStatus::ErrorGetLoader; } - std::pair<std::optional<u32>, Loader::ResultStatus> system_mode = - app_loader->LoadKernelSystemMode(); - - if (system_mode.second != Loader::ResultStatus::Success) { - LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", - static_cast<int>(system_mode.second)); - - return ResultStatus::ErrorSystemMode; - } ResultStatus init_result{Init(system, emu_window)}; if (init_result != ResultStatus::Success) { @@ -167,6 +149,7 @@ struct System::Impl { return init_result; } + telemetry_session->AddInitialInfo(*app_loader); auto main_process = Kernel::Process::Create(system, "main"); const auto [load_result, load_parameters] = app_loader->Load(*main_process); if (load_result != Loader::ResultStatus::Success) { diff --git a/src/core/core.h b/src/core/core.h index a9a756a4c..20959de54 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -98,7 +98,6 @@ public: Success, ///< Succeeded ErrorNotInitialized, ///< Error trying to use core prior to initialization ErrorGetLoader, ///< Error finding the correct application loader - ErrorSystemMode, ///< Error determining the system mode ErrorSystemFiles, ///< Error in finding system files ErrorSharedFont, ///< Error in finding shared font ErrorVideoCore, ///< Error in the video core diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index 7942f30d6..a10472a95 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp @@ -13,52 +13,40 @@ namespace Core::Timing { constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE; -s64 usToCycles(s64 us) { - if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) { +s64 msToCycles(std::chrono::milliseconds ms) { + if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) { LOG_ERROR(Core_Timing, "Integer overflow, use max value"); return std::numeric_limits<s64>::max(); } - if (us > MAX_VALUE_TO_MULTIPLY) { + if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) { LOG_DEBUG(Core_Timing, "Time very big, do rounding"); - return BASE_CLOCK_RATE * (us / 1000000); + return BASE_CLOCK_RATE * (ms.count() / 1000); } - return (BASE_CLOCK_RATE * us) / 1000000; + return (BASE_CLOCK_RATE * ms.count()) / 1000; } -s64 usToCycles(u64 us) { - if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) { +s64 usToCycles(std::chrono::microseconds us) { + if (static_cast<u64>(us.count() / 1000000) > MAX_VALUE_TO_MULTIPLY) { LOG_ERROR(Core_Timing, "Integer overflow, use max value"); return std::numeric_limits<s64>::max(); } - if (us > MAX_VALUE_TO_MULTIPLY) { + if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) { LOG_DEBUG(Core_Timing, "Time very big, do rounding"); - return BASE_CLOCK_RATE * static_cast<s64>(us / 1000000); + return BASE_CLOCK_RATE * (us.count() / 1000000); } - return (BASE_CLOCK_RATE * static_cast<s64>(us)) / 1000000; + return (BASE_CLOCK_RATE * us.count()) / 1000000; } -s64 nsToCycles(s64 ns) { - if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) { +s64 nsToCycles(std::chrono::nanoseconds ns) { + if (static_cast<u64>(ns.count() / 1000000000) > MAX_VALUE_TO_MULTIPLY) { LOG_ERROR(Core_Timing, "Integer overflow, use max value"); return std::numeric_limits<s64>::max(); } - if (ns > MAX_VALUE_TO_MULTIPLY) { + if (static_cast<u64>(ns.count()) > MAX_VALUE_TO_MULTIPLY) { LOG_DEBUG(Core_Timing, "Time very big, do rounding"); - return BASE_CLOCK_RATE * (ns / 1000000000); + return BASE_CLOCK_RATE * (ns.count() / 1000000000); } - return (BASE_CLOCK_RATE * ns) / 1000000000; -} - -s64 nsToCycles(u64 ns) { - if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) { - LOG_ERROR(Core_Timing, "Integer overflow, use max value"); - return std::numeric_limits<s64>::max(); - } - if (ns > MAX_VALUE_TO_MULTIPLY) { - LOG_DEBUG(Core_Timing, "Time very big, do rounding"); - return BASE_CLOCK_RATE * (static_cast<s64>(ns) / 1000000000); - } - return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000; + return (BASE_CLOCK_RATE * ns.count()) / 1000000000; } u64 CpuCyclesToClockCycles(u64 ticks) { diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index 679aa3123..cdd84d70f 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h @@ -4,6 +4,7 @@ #pragma once +#include <chrono> #include "common/common_types.h" namespace Core::Timing { @@ -13,53 +14,20 @@ namespace Core::Timing { constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked constexpr u64 CNTFREQ = 19200000; // Value from fusee. -inline s64 msToCycles(int ms) { - // since ms is int there is no way to overflow - return BASE_CLOCK_RATE * static_cast<s64>(ms) / 1000; -} - -inline s64 msToCycles(float ms) { - return static_cast<s64>(BASE_CLOCK_RATE * (0.001f) * ms); -} - -inline s64 msToCycles(double ms) { - return static_cast<s64>(BASE_CLOCK_RATE * (0.001) * ms); -} - -inline s64 usToCycles(float us) { - return static_cast<s64>(BASE_CLOCK_RATE * (0.000001f) * us); -} - -inline s64 usToCycles(int us) { - return (BASE_CLOCK_RATE * static_cast<s64>(us) / 1000000); -} - -s64 usToCycles(s64 us); - -s64 usToCycles(u64 us); - -inline s64 nsToCycles(float ns) { - return static_cast<s64>(BASE_CLOCK_RATE * (0.000000001f) * ns); -} - -inline s64 nsToCycles(int ns) { - return BASE_CLOCK_RATE * static_cast<s64>(ns) / 1000000000; -} - -s64 nsToCycles(s64 ns); - -s64 nsToCycles(u64 ns); +s64 msToCycles(std::chrono::milliseconds ms); +s64 usToCycles(std::chrono::microseconds us); +s64 nsToCycles(std::chrono::nanoseconds ns); -inline u64 cyclesToNs(s64 cycles) { - return cycles * 1000000000 / BASE_CLOCK_RATE; +inline std::chrono::milliseconds CyclesToMs(s64 cycles) { + return std::chrono::milliseconds(cycles * 1000 / BASE_CLOCK_RATE); } -inline s64 cyclesToUs(s64 cycles) { - return cycles * 1000000 / BASE_CLOCK_RATE; +inline std::chrono::nanoseconds CyclesToNs(s64 cycles) { + return std::chrono::nanoseconds(cycles * 1000000000 / BASE_CLOCK_RATE); } -inline u64 cyclesToMs(s64 cycles) { - return cycles * 1000 / BASE_CLOCK_RATE; +inline std::chrono::microseconds CyclesToUs(s64 cycles) { + return std::chrono::microseconds(cycles * 1000000 / BASE_CLOCK_RATE); } u64 CpuCyclesToClockCycles(u64 ticks); diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index 60ea9ad12..04da30825 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -87,6 +87,10 @@ u64 NACP::GetDefaultJournalSaveSize() const { return raw.user_account_save_data_journal_size; } +u32 NACP::GetSupportedLanguages() const { + return raw.supported_languages; +} + std::vector<u8> NACP::GetRawBytes() const { std::vector<u8> out(sizeof(RawNACP)); std::memcpy(out.data(), &raw, sizeof(RawNACP)); diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 280710ddf..1be34ed55 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -109,6 +109,7 @@ public: std::string GetVersionString() const; u64 GetDefaultNormalSaveSize() const; u64 GetDefaultJournalSaveSize() const; + u32 GetSupportedLanguages() const; std::vector<u8> GetRawBytes() const; private: diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp index fbf5f2a9e..4df3574d2 100644 --- a/src/core/frontend/applets/profile_select.cpp +++ b/src/core/frontend/applets/profile_select.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "core/frontend/applets/profile_select.h" +#include "core/hle/service/acc/profile_manager.h" #include "core/settings.h" namespace Core::Frontend { @@ -10,9 +11,9 @@ namespace Core::Frontend { ProfileSelectApplet::~ProfileSelectApplet() = default; void DefaultProfileSelectApplet::SelectProfile( - std::function<void(std::optional<Service::Account::UUID>)> callback) const { + std::function<void(std::optional<Common::UUID>)> callback) const { Service::Account::ProfileManager manager; - callback(manager.GetUser(Settings::values.current_user).value_or(Service::Account::UUID{})); + callback(manager.GetUser(Settings::values.current_user).value_or(Common::UUID{})); LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); } diff --git a/src/core/frontend/applets/profile_select.h b/src/core/frontend/applets/profile_select.h index fc8f7ae94..3506b9885 100644 --- a/src/core/frontend/applets/profile_select.h +++ b/src/core/frontend/applets/profile_select.h @@ -6,7 +6,7 @@ #include <functional> #include <optional> -#include "core/hle/service/acc/profile_manager.h" +#include "common/uuid.h" namespace Core::Frontend { @@ -14,14 +14,12 @@ class ProfileSelectApplet { public: virtual ~ProfileSelectApplet(); - virtual void SelectProfile( - std::function<void(std::optional<Service::Account::UUID>)> callback) const = 0; + virtual void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const = 0; }; class DefaultProfileSelectApplet final : public ProfileSelectApplet { public: - void SelectProfile( - std::function<void(std::optional<Service::Account::UUID>)> callback) const override; + void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const override; }; } // namespace Core::Frontend diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index e2c290dc1..4a9912641 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -169,8 +169,7 @@ private: * For the request to be honored, EmuWindow implementations will usually reimplement this * function. */ - virtual void OnMinimalClientAreaChangeRequest( - const std::pair<unsigned, unsigned>& minimal_size) { + virtual void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned>) { // By default, ignore this request and do nothing. } diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index a1357179f..d6d2cf3f0 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -20,7 +20,7 @@ static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area, static_cast<T>(std::round(scale * screen_aspect_ratio))}; } -FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) { +FramebufferLayout DefaultFrameLayout(u32 width, u32 height) { ASSERT(width > 0); ASSERT(height > 0); // The drawing code needs at least somewhat valid values for both screens @@ -29,22 +29,23 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) { const float emulation_aspect_ratio{static_cast<float>(ScreenUndocked::Height) / ScreenUndocked::Width}; - Common::Rectangle<unsigned> screen_window_area{0, 0, width, height}; - Common::Rectangle<unsigned> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio); + const auto window_aspect_ratio = static_cast<float>(height) / width; - float window_aspect_ratio = static_cast<float>(height) / width; + const Common::Rectangle<u32> screen_window_area{0, 0, width, height}; + Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio); if (window_aspect_ratio < emulation_aspect_ratio) { screen = screen.TranslateX((screen_window_area.GetWidth() - screen.GetWidth()) / 2); } else { screen = screen.TranslateY((height - screen.GetHeight()) / 2); } + res.screen = screen; return res; } -FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale) { - int width, height; +FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) { + u32 width, height; if (Settings::values.use_docked_mode) { width = ScreenDocked::WidthDocked * res_scale; diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index c2c63d08c..d2370adde 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -8,15 +8,22 @@ namespace Layout { -enum ScreenUndocked : unsigned { Width = 1280, Height = 720 }; -enum ScreenDocked : unsigned { WidthDocked = 1920, HeightDocked = 1080 }; +enum ScreenUndocked : u32 { + Width = 1280, + Height = 720, +}; + +enum ScreenDocked : u32 { + WidthDocked = 1920, + HeightDocked = 1080, +}; /// Describes the layout of the window framebuffer struct FramebufferLayout { - unsigned width{ScreenUndocked::Width}; - unsigned height{ScreenUndocked::Height}; + u32 width{ScreenUndocked::Width}; + u32 height{ScreenUndocked::Height}; - Common::Rectangle<unsigned> screen; + Common::Rectangle<u32> screen; /** * Returns the ration of pixel size of the screen, compared to the native size of the undocked @@ -33,12 +40,12 @@ struct FramebufferLayout { * @param height Window framebuffer height in pixels * @return Newly created FramebufferLayout object with default screen regions initialized */ -FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height); +FramebufferLayout DefaultFrameLayout(u32 width, u32 height); /** * Convenience method to get frame layout by resolution scale * @param res_scale resolution scale factor */ -FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale); +FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale); } // namespace Layout diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 2abf9efca..c73a40977 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -75,9 +75,9 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { // This function might be called from any thread so we have to be cautious and use the // thread-safe version of ScheduleEvent. + const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe( - Core::Timing::nsToCycles(nanoseconds), kernel.ThreadWakeupCallbackEventType(), - callback_handle); + cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle); } void Thread::CancelWakeupTimer() { diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index ba7d7acbd..86bf53d08 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -34,7 +34,7 @@ constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{ 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, }}; -static std::string GetImagePath(UUID uuid) { +static std::string GetImagePath(Common::UUID uuid) { return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; } @@ -46,7 +46,7 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) { class IProfile final : public ServiceFramework<IProfile> { public: - explicit IProfile(UUID user_id, ProfileManager& profile_manager) + explicit IProfile(Common::UUID user_id, ProfileManager& profile_manager) : ServiceFramework("IProfile"), profile_manager(profile_manager), user_id(user_id) { static const FunctionInfo functions[] = { {0, &IProfile::Get, "Get"}, @@ -131,7 +131,7 @@ private: } const ProfileManager& profile_manager; - UUID user_id; ///< The user id this profile refers to. + Common::UUID user_id; ///< The user id this profile refers to. }; class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { @@ -179,7 +179,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - UUID user_id = rp.PopRaw<UUID>(); + Common::UUID user_id = rp.PopRaw<Common::UUID>(); LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); IPC::ResponseBuilder rb{ctx, 3}; @@ -205,12 +205,12 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { LOG_INFO(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); - rb.PushRaw<UUID>(profile_manager->GetLastOpenedUser()); + rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser()); } void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - UUID user_id = rp.PopRaw<UUID>(); + Common::UUID user_id = rp.PopRaw<Common::UUID>(); LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; @@ -245,15 +245,15 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex IPC::ResponseBuilder rb{ctx, 6}; if (profile_manager->GetUserCount() != 1) { rb.Push(RESULT_SUCCESS); - rb.PushRaw<u128>(INVALID_UUID); + rb.PushRaw<u128>(Common::INVALID_UUID); return; } const auto user_list = profile_manager->GetAllUsers(); if (std::all_of(user_list.begin(), user_list.end(), - [](const auto& user) { return user.uuid == INVALID_UUID; })) { + [](const auto& user) { return user.uuid == Common::INVALID_UUID; })) { rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code - rb.PushRaw<u128>(INVALID_UUID); + rb.PushRaw<u128>(Common::INVALID_UUID); return; } diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 1316d0b07..49aa5908b 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -13,6 +13,8 @@ namespace Service::Account { +using Common::UUID; + struct UserRaw { UUID uuid; UUID uuid2; @@ -35,26 +37,6 @@ constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/"; -UUID UUID::Generate() { - std::random_device device; - std::mt19937 gen(device()); - std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); - return UUID{distribution(gen), distribution(gen)}; -} - -std::string UUID::Format() const { - return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); -} - -std::string UUID::FormatSwitch() const { - std::array<u8, 16> s{}; - std::memcpy(s.data(), uuid.data(), sizeof(u128)); - return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{" - ":02x}{:02x}{:02x}{:02x}{:02x}", - s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], - s[12], s[13], s[14], s[15]); -} - ProfileManager::ProfileManager() { ParseUserSaveFile(); @@ -217,7 +199,7 @@ bool ProfileManager::UserExists(UUID uuid) const { bool ProfileManager::UserExistsIndex(std::size_t index) const { if (index >= MAX_USERS) return false; - return profiles[index].user_uuid.uuid != INVALID_UUID; + return profiles[index].user_uuid.uuid != Common::INVALID_UUID; } /// Opens a specific user @@ -311,7 +293,7 @@ bool ProfileManager::RemoveUser(UUID uuid) { bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { const auto index = GetUserIndex(uuid); - if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) { + if (!index || profile_new.user_uuid == UUID(Common::INVALID_UUID)) { return false; } @@ -342,7 +324,7 @@ void ProfileManager::ParseUserSaveFile() { } for (const auto& user : data.users) { - if (user.uuid == UUID(INVALID_UUID)) { + if (user.uuid == UUID(Common::INVALID_UUID)) { continue; } diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index c4ce2e0b3..fd7abb541 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -9,47 +9,15 @@ #include "common/common_types.h" #include "common/swap.h" +#include "common/uuid.h" #include "core/hle/result.h" namespace Service::Account { constexpr std::size_t MAX_USERS = 8; -constexpr u128 INVALID_UUID{{0, 0}}; - -struct UUID { - // UUIDs which are 0 are considered invalid! - u128 uuid = INVALID_UUID; - UUID() = default; - explicit UUID(const u128& id) : uuid{id} {} - explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} - - explicit operator bool() const { - return uuid != INVALID_UUID; - } - - bool operator==(const UUID& rhs) const { - return uuid == rhs.uuid; - } - - bool operator!=(const UUID& rhs) const { - return !operator==(rhs); - } - - // TODO(ogniK): Properly generate uuids based on RFC-4122 - static UUID Generate(); - - // Set the UUID to {0,0} to be considered an invalid user - void Invalidate() { - uuid = INVALID_UUID; - } - - std::string Format() const; - std::string FormatSwitch() const; -}; -static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); constexpr std::size_t profile_username_size = 32; using ProfileUsername = std::array<u8, profile_username_size>; -using UserIDArray = std::array<UUID, MAX_USERS>; +using UserIDArray = std::array<Common::UUID, MAX_USERS>; /// Contains extra data related to a user. /// TODO: RE this structure @@ -66,7 +34,7 @@ static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect /// This holds general information about a users profile. This is where we store all the information /// based on a specific user struct ProfileInfo { - UUID user_uuid; + Common::UUID user_uuid; ProfileUsername username; u64 creation_time; ProfileData data; // TODO(ognik): Work out what this is @@ -74,7 +42,7 @@ struct ProfileInfo { }; struct ProfileBase { - UUID user_uuid; + Common::UUID user_uuid; u64_le timestamp; ProfileUsername username; @@ -96,33 +64,33 @@ public: ~ProfileManager(); ResultCode AddUser(const ProfileInfo& user); - ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); - ResultCode CreateNewUser(UUID uuid, const std::string& username); - std::optional<UUID> GetUser(std::size_t index) const; - std::optional<std::size_t> GetUserIndex(const UUID& uuid) const; + ResultCode CreateNewUser(Common::UUID uuid, const ProfileUsername& username); + ResultCode CreateNewUser(Common::UUID uuid, const std::string& username); + std::optional<Common::UUID> GetUser(std::size_t index) const; + std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const; std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const; - bool GetProfileBase(UUID uuid, ProfileBase& profile) const; + bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const; bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, ProfileData& data) const; - bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; + bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, ProfileData& data) const; bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, ProfileData& data) const; std::size_t GetUserCount() const; std::size_t GetOpenUserCount() const; - bool UserExists(UUID uuid) const; + bool UserExists(Common::UUID uuid) const; bool UserExistsIndex(std::size_t index) const; - void OpenUser(UUID uuid); - void CloseUser(UUID uuid); + void OpenUser(Common::UUID uuid); + void CloseUser(Common::UUID uuid); UserIDArray GetOpenUsers() const; UserIDArray GetAllUsers() const; - UUID GetLastOpenedUser() const; + Common::UUID GetLastOpenedUser() const; bool CanSystemRegisterUser() const; - bool RemoveUser(UUID uuid); - bool SetProfileBase(UUID uuid, const ProfileBase& profile_new); + bool RemoveUser(Common::UUID uuid); + bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new); private: void ParseUserSaveFile(); @@ -132,7 +100,7 @@ private: std::array<ProfileInfo, MAX_USERS> profiles{}; std::size_t user_count = 0; - UUID last_opened_user{INVALID_UUID}; + Common::UUID last_opened_user{Common::INVALID_UUID}; }; }; // namespace Service::Account diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 1a32a109f..3f201c821 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -8,6 +8,8 @@ #include <cstring> #include "audio_core/audio_renderer.h" #include "core/core.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" #include "core/file_sys/savedata_factory.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/kernel.h" @@ -29,9 +31,11 @@ #include "core/hle/service/am/tcap.h" #include "core/hle/service/apm/apm.h" #include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/ns/ns.h" #include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/pm/pm.h" #include "core/hle/service/set/set.h" +#include "core/hle/service/sm/sm.h" #include "core/hle/service/vi/vi.h" #include "core/settings.h" @@ -1100,10 +1104,42 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { // TODO(bunnei): This should be configurable LOG_DEBUG(Service_AM, "called"); + // Get supported languages from NACP, if possible + // Default to 0 (all languages supported) + u32 supported_languages = 0; + FileSys::PatchManager pm{Core::System::GetInstance().CurrentProcess()->GetTitleID()}; + + const auto res = pm.GetControlMetadata(); + if (res.first != nullptr) { + supported_languages = res.first->GetSupportedLanguages(); + } + + // Call IApplicationManagerInterface implementation. + auto& service_manager = Core::System::GetInstance().ServiceManager(); + auto ns_am2 = service_manager.GetService<Service::NS::NS>("ns:am2"); + auto app_man = ns_am2->GetApplicationManagerInterface(); + + // Get desired application language + const auto res_lang = app_man->GetApplicationDesiredLanguage(supported_languages); + if (res_lang.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res_lang.Code()); + return; + } + + // Convert to settings language code. + const auto res_code = app_man->ConvertApplicationLanguageToLanguageCode(*res_lang); + if (res_code.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res_code.Code()); + return; + } + + LOG_DEBUG(Service_AM, "got desired_language={:016X}", *res_code); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push( - static_cast<u64>(Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index))); + rb.Push(*res_code); } void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index e812c66e9..14fa92318 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -121,6 +121,21 @@ void Applet::Initialize() { initialized = true; } +AppletFrontendSet::AppletFrontendSet() = default; + +AppletFrontendSet::AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, + ProfileSelect profile_select, + SoftwareKeyboard software_keyboard, WebBrowser web_browser) + : error{std::move(error)}, photo_viewer{std::move(photo_viewer)}, profile_select{std::move( + profile_select)}, + software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)} {} + +AppletFrontendSet::~AppletFrontendSet() = default; + +AppletFrontendSet::AppletFrontendSet(AppletFrontendSet&&) noexcept = default; + +AppletFrontendSet& AppletFrontendSet::operator=(AppletFrontendSet&&) noexcept = default; + AppletManager::AppletManager() = default; AppletManager::~AppletManager() = default; diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 7f932672c..b46e10a4a 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -137,11 +137,28 @@ protected: }; struct AppletFrontendSet { - std::unique_ptr<Core::Frontend::ErrorApplet> error; - std::unique_ptr<Core::Frontend::PhotoViewerApplet> photo_viewer; - std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_select; - std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; - std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser; + using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; + using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; + using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; + using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>; + using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; + + AppletFrontendSet(); + AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, ProfileSelect profile_select, + SoftwareKeyboard software_keyboard, WebBrowser web_browser); + ~AppletFrontendSet(); + + AppletFrontendSet(const AppletFrontendSet&) = delete; + AppletFrontendSet& operator=(const AppletFrontendSet&) = delete; + + AppletFrontendSet(AppletFrontendSet&&) noexcept; + AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; + + ErrorApplet error; + PhotoViewer photo_viewer; + ProfileSelect profile_select; + SoftwareKeyboard software_keyboard; + WebBrowser web_browser; }; class AppletManager { diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp index d113bd2eb..57b5419e8 100644 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ b/src/core/hle/service/am/applets/profile_select.cpp @@ -53,19 +53,19 @@ void ProfileSelect::Execute() { return; } - frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); }); + frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); }); } -void ProfileSelect::SelectionComplete(std::optional<Account::UUID> uuid) { +void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) { UserSelectionOutput output{}; - if (uuid.has_value() && uuid->uuid != Account::INVALID_UUID) { + if (uuid.has_value() && uuid->uuid != Common::INVALID_UUID) { output.result = 0; output.uuid_selected = uuid->uuid; } else { status = ERR_USER_CANCELLED_SELECTION; output.result = ERR_USER_CANCELLED_SELECTION.raw; - output.uuid_selected = Account::INVALID_UUID; + output.uuid_selected = Common::INVALID_UUID; } final_data = std::vector<u8>(sizeof(UserSelectionOutput)); diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h index a2ac6cf50..563cd744a 100644 --- a/src/core/hle/service/am/applets/profile_select.h +++ b/src/core/hle/service/am/applets/profile_select.h @@ -7,7 +7,8 @@ #include <vector> #include "common/common_funcs.h" -#include "core/hle/service/acc/profile_manager.h" +#include "common/uuid.h" +#include "core/hle/result.h" #include "core/hle/service/am/applets/applets.h" namespace Service::AM::Applets { @@ -38,7 +39,7 @@ public: void ExecuteInteractive() override; void Execute() override; - void SelectionComplete(std::optional<Account::UUID> uuid); + void SelectionComplete(std::optional<Common::UUID> uuid); private: const Core::Frontend::ProfileSelectApplet& frontend; diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index a6197124a..ce84e25ed 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -4,42 +4,50 @@ #include <memory> +#include <fmt/ostream.h> + #include "common/logging/log.h" +#include "common/string_util.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/mii/mii.h" +#include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" namespace Service::Mii { +constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1}; +constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; +constexpr ResultCode ERROR_NOT_IN_TEST_MODE{ErrorModule::Mii, 99}; + class IDatabaseService final : public ServiceFramework<IDatabaseService> { public: explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "IsUpdated"}, - {1, nullptr, "IsFullDatabase"}, - {2, nullptr, "GetCount"}, - {3, nullptr, "Get"}, - {4, nullptr, "Get1"}, + {0, &IDatabaseService::IsUpdated, "IsUpdated"}, + {1, &IDatabaseService::IsFullDatabase, "IsFullDatabase"}, + {2, &IDatabaseService::GetCount, "GetCount"}, + {3, &IDatabaseService::Get, "Get"}, + {4, &IDatabaseService::Get1, "Get1"}, {5, nullptr, "UpdateLatest"}, - {6, nullptr, "BuildRandom"}, - {7, nullptr, "BuildDefault"}, - {8, nullptr, "Get2"}, - {9, nullptr, "Get3"}, + {6, &IDatabaseService::BuildRandom, "BuildRandom"}, + {7, &IDatabaseService::BuildDefault, "BuildDefault"}, + {8, &IDatabaseService::Get2, "Get2"}, + {9, &IDatabaseService::Get3, "Get3"}, {10, nullptr, "UpdateLatest1"}, - {11, nullptr, "FindIndex"}, - {12, nullptr, "Move"}, - {13, nullptr, "AddOrReplace"}, - {14, nullptr, "Delete"}, - {15, nullptr, "DestroyFile"}, - {16, nullptr, "DeleteFile"}, - {17, nullptr, "Format"}, + {11, &IDatabaseService::FindIndex, "FindIndex"}, + {12, &IDatabaseService::Move, "Move"}, + {13, &IDatabaseService::AddOrReplace, "AddOrReplace"}, + {14, &IDatabaseService::Delete, "Delete"}, + {15, &IDatabaseService::DestroyFile, "DestroyFile"}, + {16, &IDatabaseService::DeleteFile, "DeleteFile"}, + {17, &IDatabaseService::Format, "Format"}, {18, nullptr, "Import"}, {19, nullptr, "Export"}, {20, nullptr, "IsBrokenDatabaseWithClearFlag"}, - {21, nullptr, "GetIndex"}, + {21, &IDatabaseService::GetIndex, "GetIndex"}, {22, nullptr, "SetInterfaceVersion"}, {23, nullptr, "Convert"}, }; @@ -47,6 +55,305 @@ public: RegisterHandlers(functions); } + +private: + template <typename OutType> + std::vector<u8> SerializeArray(OutType (MiiManager::*getter)(u32) const, u32 offset, + u32 requested_size, u32& read_size) { + read_size = std::min(requested_size, db.Size() - offset); + + std::vector<u8> out(read_size * sizeof(OutType)); + + for (u32 i = 0; i < read_size; ++i) { + const auto obj = (db.*getter)(offset + i); + std::memcpy(out.data() + i * sizeof(OutType), &obj, sizeof(OutType)); + } + + return out; + } + + void IsUpdated(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto source{rp.PopRaw<Source>()}; + + LOG_DEBUG(Service_Mii, "called with source={}", source); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(db.CheckUpdatedFlag()); + db.ResetUpdatedFlag(); + } + + void IsFullDatabase(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Mii, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(db.Full()); + } + + void GetCount(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto source{rp.PopRaw<Source>()}; + + LOG_DEBUG(Service_Mii, "called with source={}", source); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(db.Size()); + } + + // Gets Miis from database at offset and index in format MiiInfoElement + void Get(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto size{rp.PopRaw<u32>()}; + const auto source{rp.PopRaw<Source>()}; + + LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size, + offsets[0], source); + + u32 read_size{}; + ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfoElement, offsets[0], size, read_size)); + offsets[0] += read_size; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(read_size); + } + + // Gets Miis from database at offset and index in format MiiInfo + void Get1(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto size{rp.PopRaw<u32>()}; + const auto source{rp.PopRaw<Source>()}; + + LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size, + offsets[1], source); + + u32 read_size{}; + ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfo, offsets[1], size, read_size)); + offsets[1] += read_size; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(read_size); + } + + void BuildRandom(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto [unknown1, unknown2, unknown3] = rp.PopRaw<RandomParameters>(); + + if (unknown1 > 3) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + LOG_ERROR(Service_Mii, "Invalid unknown1 value: {}", unknown1); + return; + } + + if (unknown2 > 2) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + LOG_ERROR(Service_Mii, "Invalid unknown2 value: {}", unknown2); + return; + } + + if (unknown3 > 3) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + LOG_ERROR(Service_Mii, "Invalid unknown3 value: {}", unknown3); + return; + } + + LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}", + unknown1, unknown2, unknown3); + + const auto info = db.CreateRandom({unknown1, unknown2, unknown3}); + IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<MiiInfo>(info); + } + + void BuildDefault(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto index{rp.PopRaw<u32>()}; + + if (index > 5) { + LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}", + index); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + return; + } + + LOG_DEBUG(Service_Mii, "called with index={:08X}", index); + + const auto info = db.CreateDefault(index); + IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<MiiInfo>(info); + } + + // Gets Miis from database at offset and index in format MiiStoreDataElement + void Get2(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto size{rp.PopRaw<u32>()}; + const auto source{rp.PopRaw<Source>()}; + + LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size, + offsets[2], source); + + u32 read_size{}; + ctx.WriteBuffer( + SerializeArray(&MiiManager::GetStoreDataElement, offsets[2], size, read_size)); + offsets[2] += read_size; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(read_size); + } + + // Gets Miis from database at offset and index in format MiiStoreData + void Get3(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto size{rp.PopRaw<u32>()}; + const auto source{rp.PopRaw<Source>()}; + + LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size, + offsets[3], source); + + u32 read_size{}; + ctx.WriteBuffer(SerializeArray(&MiiManager::GetStoreData, offsets[3], size, read_size)); + offsets[3] += read_size; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(read_size); + } + + void FindIndex(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid{rp.PopRaw<Common::UUID>()}; + const auto unknown{rp.PopRaw<bool>()}; + + LOG_DEBUG(Service_Mii, "called with uuid={}, unknown={}", uuid.FormatSwitch(), unknown); + + IPC::ResponseBuilder rb{ctx, 3}; + + const auto index = db.IndexOf(uuid); + if (index > MAX_MIIS) { + // TODO(DarkLordZach): Find a better error code + rb.Push(ResultCode(-1)); + rb.Push(index); + } else { + rb.Push(RESULT_SUCCESS); + rb.Push(index); + } + } + + void Move(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid{rp.PopRaw<Common::UUID>()}; + const auto index{rp.PopRaw<s32>()}; + + if (index < 0) { + LOG_ERROR(Service_Mii, "Index cannot be negative but is {:08X}!", index); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + return; + } + + LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index); + + const auto success = db.Move(uuid, index); + + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(DarkLordZach): Find a better error code + rb.Push(success ? RESULT_SUCCESS : ResultCode(-1)); + } + + void AddOrReplace(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto data{rp.PopRaw<MiiStoreData>()}; + + LOG_DEBUG(Service_Mii, "called with Mii data uuid={}, name={}", data.uuid.FormatSwitch(), + Common::UTF16ToUTF8(data.Name())); + + const auto success = db.AddOrReplace(data); + + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(DarkLordZach): Find a better error code + rb.Push(success ? RESULT_SUCCESS : ResultCode(-1)); + } + + void Delete(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid{rp.PopRaw<Common::UUID>()}; + + LOG_DEBUG(Service_Mii, "called with uuid={}", uuid.FormatSwitch()); + + const auto success = db.Remove(uuid); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(success ? RESULT_SUCCESS : ERROR_CANNOT_FIND_ENTRY); + } + + void DestroyFile(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Mii, "called"); + + if (!db.IsTestModeEnabled()) { + LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot destory database file."); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NOT_IN_TEST_MODE); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(db.DestroyFile()); + } + + void DeleteFile(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Mii, "called"); + + if (!db.IsTestModeEnabled()) { + LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot delete database file."); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NOT_IN_TEST_MODE); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(db.DeleteFile()); + } + + void Format(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Mii, "called"); + + db.Clear(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetIndex(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto info{rp.PopRaw<MiiInfo>()}; + + LOG_DEBUG(Service_Mii, "called with Mii info uuid={}, name={}", info.uuid.FormatSwitch(), + Common::UTF16ToUTF8(info.Name())); + + const auto index = db.IndexOf(info); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + rb.Push(index); + } + + MiiManager db; + + // Last read offsets of Get functions + std::array<u32, 4> offsets{}; }; class MiiDBModule final : public ServiceFramework<MiiDBModule> { diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp new file mode 100644 index 000000000..131b01d62 --- /dev/null +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -0,0 +1,416 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <cstring> +#include "common/assert.h" +#include "common/file_util.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/hle/service/mii/mii_manager.h" + +namespace Service::Mii { + +namespace { + +constexpr char MII_SAVE_DATABASE_PATH[] = "/system/save/8000000000000030/MiiDatabase.dat"; +constexpr std::array<char16_t, 11> DEFAULT_MII_NAME = {u'y', u'u', u'z', u'u', u'\0'}; + +// This value was retrieved from HW test +constexpr MiiStoreData DEFAULT_MII = { + { + 0x21, 0x40, 0x40, 0x01, 0x08, 0x01, 0x13, 0x08, 0x08, 0x02, 0x17, 0x8C, 0x06, 0x01, + 0x69, 0x6D, 0x8A, 0x6A, 0x82, 0x14, 0x00, 0x00, 0x00, 0x20, 0x64, 0x72, 0x44, 0x44, + }, + {'y', 'u', 'z', 'u', '\0'}, + Common::UUID{1, 0}, + 0, + 0, +}; + +// Default values taken from multiple real databases +const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0}; + +constexpr std::array<const char*, 4> SOURCE_NAMES{ + "Database", + "Default", + "Account", + "Friend", +}; + +template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize> +std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) { + std::array<T, DestArraySize> out{}; + std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize)); + return out; +} + +MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) { + MiiStoreBitFields bf{}; + std::memcpy(&bf, data.data.data(), sizeof(MiiStoreBitFields)); + return { + data.uuid, + ResizeArray<char16_t, 10, 11>(data.name), + static_cast<u8>(bf.font_region.Value()), + static_cast<u8>(bf.favorite_color.Value()), + static_cast<u8>(bf.gender.Value()), + static_cast<u8>(bf.height.Value()), + static_cast<u8>(bf.weight.Value()), + static_cast<u8>(bf.mii_type.Value()), + static_cast<u8>(bf.mii_region.Value()), + static_cast<u8>(bf.face_type.Value()), + static_cast<u8>(bf.face_color.Value()), + static_cast<u8>(bf.face_wrinkle.Value()), + static_cast<u8>(bf.face_makeup.Value()), + static_cast<u8>(bf.hair_type.Value()), + static_cast<u8>(bf.hair_color.Value()), + static_cast<bool>(bf.hair_flip.Value()), + static_cast<u8>(bf.eye_type.Value()), + static_cast<u8>(bf.eye_color.Value()), + static_cast<u8>(bf.eye_scale.Value()), + static_cast<u8>(bf.eye_aspect.Value()), + static_cast<u8>(bf.eye_rotate.Value()), + static_cast<u8>(bf.eye_x.Value()), + static_cast<u8>(bf.eye_y.Value()), + static_cast<u8>(bf.eyebrow_type.Value()), + static_cast<u8>(bf.eyebrow_color.Value()), + static_cast<u8>(bf.eyebrow_scale.Value()), + static_cast<u8>(bf.eyebrow_aspect.Value()), + static_cast<u8>(bf.eyebrow_rotate.Value()), + static_cast<u8>(bf.eyebrow_x.Value()), + static_cast<u8>(bf.eyebrow_y.Value()), + static_cast<u8>(bf.nose_type.Value()), + static_cast<u8>(bf.nose_scale.Value()), + static_cast<u8>(bf.nose_y.Value()), + static_cast<u8>(bf.mouth_type.Value()), + static_cast<u8>(bf.mouth_color.Value()), + static_cast<u8>(bf.mouth_scale.Value()), + static_cast<u8>(bf.mouth_aspect.Value()), + static_cast<u8>(bf.mouth_y.Value()), + static_cast<u8>(bf.facial_hair_color.Value()), + static_cast<u8>(bf.beard_type.Value()), + static_cast<u8>(bf.mustache_type.Value()), + static_cast<u8>(bf.mustache_scale.Value()), + static_cast<u8>(bf.mustache_y.Value()), + static_cast<u8>(bf.glasses_type.Value()), + static_cast<u8>(bf.glasses_color.Value()), + static_cast<u8>(bf.glasses_scale.Value()), + static_cast<u8>(bf.glasses_y.Value()), + static_cast<u8>(bf.mole_type.Value()), + static_cast<u8>(bf.mole_scale.Value()), + static_cast<u8>(bf.mole_x.Value()), + static_cast<u8>(bf.mole_y.Value()), + 0x00, + }; +} +MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) { + MiiStoreData out{}; + out.name = ResizeArray<char16_t, 11, 10>(info.name); + out.uuid = info.uuid; + + MiiStoreBitFields bf{}; + + bf.hair_type.Assign(info.hair_type); + bf.mole_type.Assign(info.mole_type); + bf.height.Assign(info.height); + bf.hair_flip.Assign(info.hair_flip); + bf.weight.Assign(info.weight); + bf.hair_color.Assign(info.hair_color); + + bf.gender.Assign(info.gender); + bf.eye_color.Assign(info.eye_color); + bf.eyebrow_color.Assign(info.eyebrow_color); + bf.mouth_color.Assign(info.mouth_color); + bf.facial_hair_color.Assign(info.facial_hair_color); + + bf.mii_type.Assign(info.mii_type); + bf.glasses_color.Assign(info.glasses_color); + bf.font_region.Assign(info.font_region); + bf.eye_type.Assign(info.eye_type); + bf.mii_region.Assign(info.mii_region); + bf.mouth_type.Assign(info.mouth_type); + bf.glasses_scale.Assign(info.glasses_scale); + bf.eye_y.Assign(info.eye_y); + + bf.mustache_type.Assign(info.mustache_type); + bf.eyebrow_type.Assign(info.eyebrow_type); + bf.beard_type.Assign(info.beard_type); + bf.nose_type.Assign(info.nose_type); + bf.mouth_aspect.Assign(info.mouth_aspect_ratio); + bf.nose_y.Assign(info.nose_y); + bf.eyebrow_aspect.Assign(info.eyebrow_aspect_ratio); + bf.mouth_y.Assign(info.mouth_y); + + bf.eye_rotate.Assign(info.eye_rotate); + bf.mustache_y.Assign(info.mustache_y); + bf.eye_aspect.Assign(info.eye_aspect_ratio); + bf.glasses_y.Assign(info.glasses_y); + bf.eye_scale.Assign(info.eye_scale); + bf.mole_x.Assign(info.mole_x); + bf.mole_y.Assign(info.mole_y); + + bf.glasses_type.Assign(info.glasses_type); + bf.face_type.Assign(info.face_type); + bf.favorite_color.Assign(info.favorite_color); + bf.face_wrinkle.Assign(info.face_wrinkle); + bf.face_color.Assign(info.face_color); + bf.eye_x.Assign(info.eye_x); + bf.face_makeup.Assign(info.face_makeup); + + bf.eyebrow_rotate.Assign(info.eyebrow_rotate); + bf.eyebrow_scale.Assign(info.eyebrow_scale); + bf.eyebrow_y.Assign(info.eyebrow_y); + bf.eyebrow_x.Assign(info.eyebrow_x); + bf.mouth_scale.Assign(info.mouth_scale); + bf.nose_scale.Assign(info.nose_scale); + bf.mole_scale.Assign(info.mole_scale); + bf.mustache_scale.Assign(info.mustache_scale); + + std::memcpy(out.data.data(), &bf, sizeof(MiiStoreBitFields)); + + return out; +} + +} // namespace + +std::ostream& operator<<(std::ostream& os, Source source) { + os << SOURCE_NAMES.at(static_cast<std::size_t>(source)); + return os; +} + +std::u16string MiiInfo::Name() const { + return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size()); +} + +bool operator==(const MiiInfo& lhs, const MiiInfo& rhs) { + return std::memcmp(&lhs, &rhs, sizeof(MiiInfo)) == 0; +} + +bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs) { + return !operator==(lhs, rhs); +} + +std::u16string MiiStoreData::Name() const { + return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size()); +} + +MiiManager::MiiManager() = default; + +MiiManager::~MiiManager() = default; + +MiiInfo MiiManager::CreateRandom(RandomParameters params) { + LOG_WARNING(Service_Mii, + "(STUBBED) called with params={:08X}{:08X}{:08X}, returning default Mii", + params.unknown_1, params.unknown_2, params.unknown_3); + + return ConvertStoreDataToInfo(CreateMiiWithUniqueUUID()); +} + +MiiInfo MiiManager::CreateDefault(u32 index) { + const auto new_mii = CreateMiiWithUniqueUUID(); + + database.miis.at(index) = new_mii; + + EnsureDatabasePartition(); + return ConvertStoreDataToInfo(new_mii); +} + +bool MiiManager::CheckUpdatedFlag() const { + return updated_flag; +} + +void MiiManager::ResetUpdatedFlag() { + updated_flag = false; +} + +bool MiiManager::IsTestModeEnabled() const { + return is_test_mode_enabled; +} + +bool MiiManager::Empty() const { + return Size() == 0; +} + +bool MiiManager::Full() const { + return Size() == MAX_MIIS; +} + +void MiiManager::Clear() { + updated_flag = true; + std::fill(database.miis.begin(), database.miis.end(), MiiStoreData{}); +} + +u32 MiiManager::Size() const { + return static_cast<u32>(std::count_if(database.miis.begin(), database.miis.end(), + [](const MiiStoreData& elem) { return elem.uuid; })); +} + +MiiInfo MiiManager::GetInfo(u32 index) const { + return ConvertStoreDataToInfo(GetStoreData(index)); +} + +MiiInfoElement MiiManager::GetInfoElement(u32 index) const { + return {GetInfo(index), Source::Database}; +} + +MiiStoreData MiiManager::GetStoreData(u32 index) const { + return database.miis.at(index); +} + +MiiStoreDataElement MiiManager::GetStoreDataElement(u32 index) const { + return {GetStoreData(index), Source::Database}; +} + +bool MiiManager::Remove(Common::UUID uuid) { + const auto iter = std::find_if(database.miis.begin(), database.miis.end(), + [uuid](const MiiStoreData& elem) { return elem.uuid == uuid; }); + + if (iter == database.miis.end()) + return false; + + updated_flag = true; + *iter = MiiStoreData{}; + EnsureDatabasePartition(); + return true; +} + +u32 MiiManager::IndexOf(Common::UUID uuid) const { + const auto iter = std::find_if(database.miis.begin(), database.miis.end(), + [uuid](const MiiStoreData& elem) { return elem.uuid == uuid; }); + + if (iter == database.miis.end()) + return INVALID_INDEX; + + return static_cast<u32>(std::distance(database.miis.begin(), iter)); +} + +u32 MiiManager::IndexOf(const MiiInfo& info) const { + const auto iter = + std::find_if(database.miis.begin(), database.miis.end(), [&info](const MiiStoreData& elem) { + return ConvertStoreDataToInfo(elem) == info; + }); + + if (iter == database.miis.end()) + return INVALID_INDEX; + + return static_cast<u32>(std::distance(database.miis.begin(), iter)); +} + +bool MiiManager::Move(Common::UUID uuid, u32 new_index) { + const auto index = IndexOf(uuid); + + if (index == INVALID_INDEX || new_index >= MAX_MIIS) + return false; + + updated_flag = true; + const auto moving = database.miis[index]; + const auto replacing = database.miis[new_index]; + if (replacing.uuid) { + database.miis[index] = replacing; + database.miis[new_index] = moving; + } else { + database.miis[index] = MiiStoreData{}; + database.miis[new_index] = moving; + } + + EnsureDatabasePartition(); + return true; +} + +bool MiiManager::AddOrReplace(const MiiStoreData& data) { + const auto index = IndexOf(data.uuid); + + updated_flag = true; + if (index == INVALID_INDEX) { + const auto size = Size(); + if (size == MAX_MIIS) + return false; + database.miis[size] = data; + } else { + database.miis[index] = data; + } + + return true; +} + +bool MiiManager::DestroyFile() { + database = DEFAULT_MII_DATABASE; + updated_flag = false; + return DeleteFile(); +} + +bool MiiManager::DeleteFile() { + const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH; + return FileUtil::Exists(path) && FileUtil::Delete(path); +} + +void MiiManager::WriteToFile() { + const auto raw_path = + FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030"; + if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) + FileUtil::Delete(raw_path); + + const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH; + + if (!FileUtil::CreateFullPath(path)) { + LOG_WARNING(Service_Mii, + "Failed to create full path of MiiDatabase.dat. Create the directory " + "nand/system/save/8000000000000030 to mitigate this " + "issue."); + return; + } + + FileUtil::IOFile save(path, "wb"); + + if (!save.IsOpen()) { + LOG_WARNING(Service_Mii, "Failed to write save data to file... No changes to user data " + "made in current session will be saved."); + return; + } + + save.Resize(sizeof(MiiDatabase)); + if (save.WriteBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) { + LOG_WARNING(Service_Mii, "Failed to write all data to save file... Data may be malformed " + "and/or regenerated on next run."); + save.Resize(0); + } +} + +void MiiManager::ReadFromFile() { + FileUtil::IOFile save( + FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH, "rb"); + + if (!save.IsOpen()) { + LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " + "blank Mii database with no Miis."); + std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase)); + return; + } + + if (save.ReadBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) { + LOG_WARNING(Service_ACC, "MiiDatabase.dat is smaller than expected... Generating new blank " + "Mii database with no Miis."); + std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase)); + return; + } + + EnsureDatabasePartition(); +} + +MiiStoreData MiiManager::CreateMiiWithUniqueUUID() const { + auto new_mii = DEFAULT_MII; + + do { + new_mii.uuid = Common::UUID::Generate(); + } while (IndexOf(new_mii.uuid) != INVALID_INDEX); + + return new_mii; +} + +void MiiManager::EnsureDatabasePartition() { + std::stable_partition(database.miis.begin(), database.miis.end(), + [](const MiiStoreData& elem) { return elem.uuid; }); +} + +} // namespace Service::Mii diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h new file mode 100644 index 000000000..38ad78a0d --- /dev/null +++ b/src/core/hle/service/mii/mii_manager.h @@ -0,0 +1,273 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/uuid.h" + +namespace Service::Mii { + +constexpr std::size_t MAX_MIIS = 100; +constexpr u32 INVALID_INDEX = 0xFFFFFFFF; + +struct RandomParameters { + u32 unknown_1; + u32 unknown_2; + u32 unknown_3; +}; +static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size."); + +enum class Source : u32 { + Database = 0, + Default = 1, + Account = 2, + Friend = 3, +}; + +std::ostream& operator<<(std::ostream& os, Source source); + +struct MiiInfo { + Common::UUID uuid; + std::array<char16_t, 11> name; + u8 font_region; + u8 favorite_color; + u8 gender; + u8 height; + u8 weight; + u8 mii_type; + u8 mii_region; + u8 face_type; + u8 face_color; + u8 face_wrinkle; + u8 face_makeup; + u8 hair_type; + u8 hair_color; + bool hair_flip; + u8 eye_type; + u8 eye_color; + u8 eye_scale; + u8 eye_aspect_ratio; + u8 eye_rotate; + u8 eye_x; + u8 eye_y; + u8 eyebrow_type; + u8 eyebrow_color; + u8 eyebrow_scale; + u8 eyebrow_aspect_ratio; + u8 eyebrow_rotate; + u8 eyebrow_x; + u8 eyebrow_y; + u8 nose_type; + u8 nose_scale; + u8 nose_y; + u8 mouth_type; + u8 mouth_color; + u8 mouth_scale; + u8 mouth_aspect_ratio; + u8 mouth_y; + u8 facial_hair_color; + u8 beard_type; + u8 mustache_type; + u8 mustache_scale; + u8 mustache_y; + u8 glasses_type; + u8 glasses_color; + u8 glasses_scale; + u8 glasses_y; + u8 mole_type; + u8 mole_scale; + u8 mole_x; + u8 mole_y; + INSERT_PADDING_BYTES(1); + + std::u16string Name() const; +}; +static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size."); +static_assert(std::has_unique_object_representations_v<MiiInfo>, + "All bits of MiiInfo must contribute to its value."); + +bool operator==(const MiiInfo& lhs, const MiiInfo& rhs); +bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs); + +#pragma pack(push, 4) +struct MiiInfoElement { + MiiInfo info; + Source source; +}; +static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size."); + +struct MiiStoreBitFields { + union { + u32 word_0; + + BitField<24, 8, u32> hair_type; + BitField<23, 1, u32> mole_type; + BitField<16, 7, u32> height; + BitField<15, 1, u32> hair_flip; + BitField<8, 7, u32> weight; + BitField<0, 7, u32> hair_color; + }; + + union { + u32 word_1; + + BitField<31, 1, u32> gender; + BitField<24, 7, u32> eye_color; + BitField<16, 7, u32> eyebrow_color; + BitField<8, 7, u32> mouth_color; + BitField<0, 7, u32> facial_hair_color; + }; + + union { + u32 word_2; + + BitField<31, 1, u32> mii_type; + BitField<24, 7, u32> glasses_color; + BitField<22, 2, u32> font_region; + BitField<16, 6, u32> eye_type; + BitField<14, 2, u32> mii_region; + BitField<8, 6, u32> mouth_type; + BitField<5, 3, u32> glasses_scale; + BitField<0, 5, u32> eye_y; + }; + + union { + u32 word_3; + + BitField<29, 3, u32> mustache_type; + BitField<24, 5, u32> eyebrow_type; + BitField<21, 3, u32> beard_type; + BitField<16, 5, u32> nose_type; + BitField<13, 3, u32> mouth_aspect; + BitField<8, 5, u32> nose_y; + BitField<5, 3, u32> eyebrow_aspect; + BitField<0, 5, u32> mouth_y; + }; + + union { + u32 word_4; + + BitField<29, 3, u32> eye_rotate; + BitField<24, 5, u32> mustache_y; + BitField<21, 3, u32> eye_aspect; + BitField<16, 5, u32> glasses_y; + BitField<13, 3, u32> eye_scale; + BitField<8, 5, u32> mole_x; + BitField<0, 5, u32> mole_y; + }; + + union { + u32 word_5; + + BitField<24, 5, u32> glasses_type; + BitField<20, 4, u32> face_type; + BitField<16, 4, u32> favorite_color; + BitField<12, 4, u32> face_wrinkle; + BitField<8, 4, u32> face_color; + BitField<4, 4, u32> eye_x; + BitField<0, 4, u32> face_makeup; + }; + + union { + u32 word_6; + + BitField<28, 4, u32> eyebrow_rotate; + BitField<24, 4, u32> eyebrow_scale; + BitField<20, 4, u32> eyebrow_y; + BitField<16, 4, u32> eyebrow_x; + BitField<12, 4, u32> mouth_scale; + BitField<8, 4, u32> nose_scale; + BitField<4, 4, u32> mole_scale; + BitField<0, 4, u32> mustache_scale; + }; +}; +static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size."); +static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>, + "MiiStoreBitFields is not trivially copyable."); + +struct MiiStoreData { + // This corresponds to the above structure MiiStoreBitFields. I did it like this because the + // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is + // not suitable for our uses. + std::array<u8, 0x1C> data; + static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); + + std::array<char16_t, 10> name; + Common::UUID uuid; + u16 crc_1; + u16 crc_2; + + std::u16string Name() const; +}; +static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size."); + +struct MiiStoreDataElement { + MiiStoreData data; + Source source; +}; +static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size."); + +struct MiiDatabase { + u32 magic; // 'NFDB' + std::array<MiiStoreData, MAX_MIIS> miis; + INSERT_PADDING_BYTES(1); + u8 count; + u16 crc; +}; +static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); +#pragma pack(pop) + +// The Mii manager is responsible for loading and storing the Miis to the database in NAND along +// with providing an easy interface for HLE emulation of the mii service. +class MiiManager { +public: + MiiManager(); + ~MiiManager(); + + MiiInfo CreateRandom(RandomParameters params); + MiiInfo CreateDefault(u32 index); + + bool CheckUpdatedFlag() const; + void ResetUpdatedFlag(); + + bool IsTestModeEnabled() const; + + bool Empty() const; + bool Full() const; + + void Clear(); + + u32 Size() const; + + MiiInfo GetInfo(u32 index) const; + MiiInfoElement GetInfoElement(u32 index) const; + MiiStoreData GetStoreData(u32 index) const; + MiiStoreDataElement GetStoreDataElement(u32 index) const; + + bool Remove(Common::UUID uuid); + u32 IndexOf(Common::UUID uuid) const; + u32 IndexOf(const MiiInfo& info) const; + + bool Move(Common::UUID uuid, u32 new_index); + bool AddOrReplace(const MiiStoreData& data); + + bool DestroyFile(); + bool DeleteFile(); + +private: + void WriteToFile(); + void ReadFromFile(); + + MiiStoreData CreateMiiWithUniqueUUID() const; + + void EnsureDatabasePartition(); + + MiiDatabase database; + bool updated_flag = false; + bool is_test_mode_enabled = false; +}; + +}; // namespace Service::Mii diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp index 5d31f638f..b405a4b66 100644 --- a/src/core/hle/service/ncm/ncm.cpp +++ b/src/core/hle/service/ncm/ncm.cpp @@ -4,15 +4,89 @@ #include <memory> +#include "core/file_sys/romfs_factory.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/service/ncm/ncm.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" namespace Service::NCM { -class LocationResolver final : public ServiceFramework<LocationResolver> { +class ILocationResolver final : public ServiceFramework<ILocationResolver> { public: - explicit LocationResolver() : ServiceFramework{"lr"} { + explicit ILocationResolver(FileSys::StorageId id) + : ServiceFramework{"ILocationResolver"}, storage(id) { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ResolveProgramPath"}, + {1, nullptr, "RedirectProgramPath"}, + {2, nullptr, "ResolveApplicationControlPath"}, + {3, nullptr, "ResolveApplicationHtmlDocumentPath"}, + {4, nullptr, "ResolveDataPath"}, + {5, nullptr, "RedirectApplicationControlPath"}, + {6, nullptr, "RedirectApplicationHtmlDocumentPath"}, + {7, nullptr, "ResolveApplicationLegalInformationPath"}, + {8, nullptr, "RedirectApplicationLegalInformationPath"}, + {9, nullptr, "Refresh"}, + {10, nullptr, "RedirectProgramPath2"}, + {11, nullptr, "Refresh2"}, + {12, nullptr, "DeleteProgramPath"}, + {13, nullptr, "DeleteApplicationControlPath"}, + {14, nullptr, "DeleteApplicationHtmlDocumentPath"}, + {15, nullptr, "DeleteApplicationLegalInformationPath"}, + {16, nullptr, ""}, + {17, nullptr, ""}, + {18, nullptr, ""}, + {19, nullptr, ""}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + FileSys::StorageId storage; +}; + +class IRegisteredLocationResolver final : public ServiceFramework<IRegisteredLocationResolver> { +public: + explicit IRegisteredLocationResolver() : ServiceFramework{"IRegisteredLocationResolver"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ResolveProgramPath"}, + {1, nullptr, "RegisterProgramPath"}, + {2, nullptr, "UnregisterProgramPath"}, + {3, nullptr, "RedirectProgramPath"}, + {4, nullptr, "ResolveHtmlDocumentPath"}, + {5, nullptr, "RegisterHtmlDocumentPath"}, + {6, nullptr, "UnregisterHtmlDocumentPath"}, + {7, nullptr, "RedirectHtmlDocumentPath"}, + {8, nullptr, ""}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IAddOnContentLocationResolver final : public ServiceFramework<IAddOnContentLocationResolver> { +public: + explicit IAddOnContentLocationResolver() : ServiceFramework{"IAddOnContentLocationResolver"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ResolveAddOnContentPath"}, + {1, nullptr, "RegisterAddOnContentStorage"}, + {2, nullptr, "UnregisterAllAddOnContentPath"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class LR final : public ServiceFramework<LR> { +public: + explicit LR() : ServiceFramework{"lr"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "OpenLocationResolver"}, @@ -52,7 +126,7 @@ public: }; void InstallInterfaces(SM::ServiceManager& sm) { - std::make_shared<LocationResolver>()->InstallAsService(sm); + std::make_shared<LR>()->InstallAsService(sm); std::make_shared<NCM>()->InstallAsService(sm); } diff --git a/src/core/hle/service/ns/errors.h b/src/core/hle/service/ns/errors.h new file mode 100644 index 000000000..f4aea8a65 --- /dev/null +++ b/src/core/hle/service/ns/errors.h @@ -0,0 +1,12 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Service::NS { + +constexpr ResultCode ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300}; +}
\ No newline at end of file diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp new file mode 100644 index 000000000..29c4a820c --- /dev/null +++ b/src/core/hle/service/ns/language.cpp @@ -0,0 +1,392 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/ns/language.h" +#include "core/hle/service/set/set.h" + +namespace Service::NS { + +constexpr ApplicationLanguagePriorityList priority_list_american_english = {{ + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::French, + ApplicationLanguage::German, + ApplicationLanguage::Spanish, + ApplicationLanguage::Italian, + ApplicationLanguage::Dutch, + ApplicationLanguage::Portuguese, + ApplicationLanguage::Russian, + ApplicationLanguage::Japanese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_british_english = {{ + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::French, + ApplicationLanguage::German, + ApplicationLanguage::Spanish, + ApplicationLanguage::Italian, + ApplicationLanguage::Dutch, + ApplicationLanguage::Portuguese, + ApplicationLanguage::Russian, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::Japanese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_japanese = {{ + ApplicationLanguage::Japanese, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::French, + ApplicationLanguage::German, + ApplicationLanguage::Spanish, + ApplicationLanguage::Italian, + ApplicationLanguage::Dutch, + ApplicationLanguage::Portuguese, + ApplicationLanguage::Russian, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_french = {{ + ApplicationLanguage::French, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::German, + ApplicationLanguage::Spanish, + ApplicationLanguage::Italian, + ApplicationLanguage::Dutch, + ApplicationLanguage::Portuguese, + ApplicationLanguage::Russian, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::Japanese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_german = {{ + ApplicationLanguage::German, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::French, + ApplicationLanguage::Spanish, + ApplicationLanguage::Italian, + ApplicationLanguage::Dutch, + ApplicationLanguage::Portuguese, + ApplicationLanguage::Russian, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::Japanese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_latin_american_spanish = {{ + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::Spanish, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::Portuguese, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::French, + ApplicationLanguage::Italian, + ApplicationLanguage::German, + ApplicationLanguage::Dutch, + ApplicationLanguage::Russian, + ApplicationLanguage::Japanese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_spanish = {{ + ApplicationLanguage::Spanish, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::French, + ApplicationLanguage::German, + ApplicationLanguage::Italian, + ApplicationLanguage::Dutch, + ApplicationLanguage::Portuguese, + ApplicationLanguage::Russian, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::Japanese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_italian = {{ + ApplicationLanguage::Italian, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::French, + ApplicationLanguage::German, + ApplicationLanguage::Spanish, + ApplicationLanguage::Dutch, + ApplicationLanguage::Portuguese, + ApplicationLanguage::Russian, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::Japanese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_dutch = {{ + ApplicationLanguage::Dutch, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::French, + ApplicationLanguage::German, + ApplicationLanguage::Spanish, + ApplicationLanguage::Italian, + ApplicationLanguage::Portuguese, + ApplicationLanguage::Russian, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::Japanese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_canadian_french = {{ + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::French, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::Spanish, + ApplicationLanguage::German, + ApplicationLanguage::Italian, + ApplicationLanguage::Dutch, + ApplicationLanguage::Portuguese, + ApplicationLanguage::Russian, + ApplicationLanguage::Japanese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_portuguese = {{ + ApplicationLanguage::Portuguese, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::French, + ApplicationLanguage::German, + ApplicationLanguage::Spanish, + ApplicationLanguage::Italian, + ApplicationLanguage::Dutch, + ApplicationLanguage::Russian, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::Japanese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_russian = {{ + ApplicationLanguage::Russian, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::French, + ApplicationLanguage::German, + ApplicationLanguage::Spanish, + ApplicationLanguage::Italian, + ApplicationLanguage::Dutch, + ApplicationLanguage::Portuguese, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::Japanese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_korean = {{ + ApplicationLanguage::Korean, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::French, + ApplicationLanguage::German, + ApplicationLanguage::Spanish, + ApplicationLanguage::Italian, + ApplicationLanguage::Dutch, + ApplicationLanguage::Portuguese, + ApplicationLanguage::Russian, + ApplicationLanguage::Japanese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_traditional_chinese = {{ + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::Japanese, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::French, + ApplicationLanguage::German, + ApplicationLanguage::Spanish, + ApplicationLanguage::Italian, + ApplicationLanguage::Dutch, + ApplicationLanguage::Portuguese, + ApplicationLanguage::Russian, + ApplicationLanguage::Korean, +}}; + +constexpr ApplicationLanguagePriorityList priority_list_simplified_chinese = {{ + ApplicationLanguage::SimplifiedChinese, + ApplicationLanguage::TraditionalChinese, + ApplicationLanguage::AmericanEnglish, + ApplicationLanguage::BritishEnglish, + ApplicationLanguage::Japanese, + ApplicationLanguage::LatinAmericanSpanish, + ApplicationLanguage::CanadianFrench, + ApplicationLanguage::French, + ApplicationLanguage::German, + ApplicationLanguage::Spanish, + ApplicationLanguage::Italian, + ApplicationLanguage::Dutch, + ApplicationLanguage::Portuguese, + ApplicationLanguage::Russian, + ApplicationLanguage::Korean, +}}; + +const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList( + const ApplicationLanguage lang) { + switch (lang) { + case ApplicationLanguage::AmericanEnglish: + return &priority_list_american_english; + case ApplicationLanguage::BritishEnglish: + return &priority_list_british_english; + case ApplicationLanguage::Japanese: + return &priority_list_japanese; + case ApplicationLanguage::French: + return &priority_list_french; + case ApplicationLanguage::German: + return &priority_list_german; + case ApplicationLanguage::LatinAmericanSpanish: + return &priority_list_latin_american_spanish; + case ApplicationLanguage::Spanish: + return &priority_list_spanish; + case ApplicationLanguage::Italian: + return &priority_list_italian; + case ApplicationLanguage::Dutch: + return &priority_list_dutch; + case ApplicationLanguage::CanadianFrench: + return &priority_list_canadian_french; + case ApplicationLanguage::Portuguese: + return &priority_list_portuguese; + case ApplicationLanguage::Russian: + return &priority_list_russian; + case ApplicationLanguage::Korean: + return &priority_list_korean; + case ApplicationLanguage::TraditionalChinese: + return &priority_list_traditional_chinese; + case ApplicationLanguage::SimplifiedChinese: + return &priority_list_simplified_chinese; + default: + return nullptr; + } +} + +std::optional<ApplicationLanguage> ConvertToApplicationLanguage( + const Set::LanguageCode language_code) { + switch (language_code) { + case Set::LanguageCode::EN_US: + return ApplicationLanguage::AmericanEnglish; + case Set::LanguageCode::EN_GB: + return ApplicationLanguage::BritishEnglish; + case Set::LanguageCode::JA: + return ApplicationLanguage::Japanese; + case Set::LanguageCode::FR: + return ApplicationLanguage::French; + case Set::LanguageCode::DE: + return ApplicationLanguage::German; + case Set::LanguageCode::ES_419: + return ApplicationLanguage::LatinAmericanSpanish; + case Set::LanguageCode::ES: + return ApplicationLanguage::Spanish; + case Set::LanguageCode::IT: + return ApplicationLanguage::Italian; + case Set::LanguageCode::NL: + return ApplicationLanguage::Dutch; + case Set::LanguageCode::FR_CA: + return ApplicationLanguage::CanadianFrench; + case Set::LanguageCode::PT: + return ApplicationLanguage::Portuguese; + case Set::LanguageCode::RU: + return ApplicationLanguage::Russian; + case Set::LanguageCode::KO: + return ApplicationLanguage::Korean; + case Set::LanguageCode::ZH_HANT: + return ApplicationLanguage::TraditionalChinese; + case Set::LanguageCode::ZH_HANS: + return ApplicationLanguage::SimplifiedChinese; + default: + return std::nullopt; + } +} + +std::optional<Set::LanguageCode> ConvertToLanguageCode(const ApplicationLanguage lang) { + switch (lang) { + case ApplicationLanguage::AmericanEnglish: + return Set::LanguageCode::EN_US; + case ApplicationLanguage::BritishEnglish: + return Set::LanguageCode::EN_GB; + case ApplicationLanguage::Japanese: + return Set::LanguageCode::JA; + case ApplicationLanguage::French: + return Set::LanguageCode::FR; + case ApplicationLanguage::German: + return Set::LanguageCode::DE; + case ApplicationLanguage::LatinAmericanSpanish: + return Set::LanguageCode::ES_419; + case ApplicationLanguage::Spanish: + return Set::LanguageCode::ES; + case ApplicationLanguage::Italian: + return Set::LanguageCode::IT; + case ApplicationLanguage::Dutch: + return Set::LanguageCode::NL; + case ApplicationLanguage::CanadianFrench: + return Set::LanguageCode::FR_CA; + case ApplicationLanguage::Portuguese: + return Set::LanguageCode::PT; + case ApplicationLanguage::Russian: + return Set::LanguageCode::RU; + case ApplicationLanguage::Korean: + return Set::LanguageCode::KO; + case ApplicationLanguage::TraditionalChinese: + return Set::LanguageCode::ZH_HANT; + case ApplicationLanguage::SimplifiedChinese: + return Set::LanguageCode::ZH_HANS; + default: + return std::nullopt; + } +} +} // namespace Service::NS
\ No newline at end of file diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h new file mode 100644 index 000000000..e9829f9d2 --- /dev/null +++ b/src/core/hle/service/ns/language.h @@ -0,0 +1,45 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <optional> +#include <string> +#include "common/common_types.h" + +namespace Service::Set { +enum class LanguageCode : u64; +} + +namespace Service::NS { +/// This is nn::ns::detail::ApplicationLanguage +enum class ApplicationLanguage : u8 { + AmericanEnglish = 0, + BritishEnglish, + Japanese, + French, + German, + LatinAmericanSpanish, + Spanish, + Italian, + Dutch, + CanadianFrench, + Portuguese, + Russian, + Korean, + TraditionalChinese, + SimplifiedChinese, + Count +}; +using ApplicationLanguagePriorityList = + const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>; + +constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) { + return 1U << static_cast<u32>(lang); +} + +const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang); +std::optional<ApplicationLanguage> ConvertToApplicationLanguage(Set::LanguageCode language_code); +std::optional<Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang); +} // namespace Service::NS
\ No newline at end of file diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 0eb04037a..ce88a2941 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -7,445 +7,507 @@ #include "core/file_sys/patch_manager.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/ns/errors.h" +#include "core/hle/service/ns/language.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/pl_u.h" +#include "core/hle/service/set/set.h" +#include "core/settings.h" namespace Service::NS { -class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> { -public: - explicit IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "CreateUserAccount"}, - }; - // clang-format on +IAccountProxyInterface::IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "CreateUserAccount"}, + }; + // clang-format on - RegisterHandlers(functions); - } -}; + RegisterHandlers(functions); +} -class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> { -public: - explicit IApplicationManagerInterface() : ServiceFramework{"IApplicationManagerInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "ListApplicationRecord"}, - {1, nullptr, "GenerateApplicationRecordCount"}, - {2, nullptr, "GetApplicationRecordUpdateSystemEvent"}, - {3, nullptr, "GetApplicationViewDeprecated"}, - {4, nullptr, "DeleteApplicationEntity"}, - {5, nullptr, "DeleteApplicationCompletely"}, - {6, nullptr, "IsAnyApplicationEntityRedundant"}, - {7, nullptr, "DeleteRedundantApplicationEntity"}, - {8, nullptr, "IsApplicationEntityMovable"}, - {9, nullptr, "MoveApplicationEntity"}, - {11, nullptr, "CalculateApplicationOccupiedSize"}, - {16, nullptr, "PushApplicationRecord"}, - {17, nullptr, "ListApplicationRecordContentMeta"}, - {19, nullptr, "LaunchApplicationOld"}, - {21, nullptr, "GetApplicationContentPath"}, - {22, nullptr, "TerminateApplication"}, - {23, nullptr, "ResolveApplicationContentPath"}, - {26, nullptr, "BeginInstallApplication"}, - {27, nullptr, "DeleteApplicationRecord"}, - {30, nullptr, "RequestApplicationUpdateInfo"}, - {32, nullptr, "CancelApplicationDownload"}, - {33, nullptr, "ResumeApplicationDownload"}, - {35, nullptr, "UpdateVersionList"}, - {36, nullptr, "PushLaunchVersion"}, - {37, nullptr, "ListRequiredVersion"}, - {38, nullptr, "CheckApplicationLaunchVersion"}, - {39, nullptr, "CheckApplicationLaunchRights"}, - {40, nullptr, "GetApplicationLogoData"}, - {41, nullptr, "CalculateApplicationDownloadRequiredSize"}, - {42, nullptr, "CleanupSdCard"}, - {43, nullptr, "CheckSdCardMountStatus"}, - {44, nullptr, "GetSdCardMountStatusChangedEvent"}, - {45, nullptr, "GetGameCardAttachmentEvent"}, - {46, nullptr, "GetGameCardAttachmentInfo"}, - {47, nullptr, "GetTotalSpaceSize"}, - {48, nullptr, "GetFreeSpaceSize"}, - {49, nullptr, "GetSdCardRemovedEvent"}, - {52, nullptr, "GetGameCardUpdateDetectionEvent"}, - {53, nullptr, "DisableApplicationAutoDelete"}, - {54, nullptr, "EnableApplicationAutoDelete"}, - {55, nullptr, "GetApplicationDesiredLanguage"}, - {56, nullptr, "SetApplicationTerminateResult"}, - {57, nullptr, "ClearApplicationTerminateResult"}, - {58, nullptr, "GetLastSdCardMountUnexpectedResult"}, - {59, nullptr, "ConvertApplicationLanguageToLanguageCode"}, - {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, - {61, nullptr, "GetBackgroundDownloadStressTaskInfo"}, - {62, nullptr, "GetGameCardStopper"}, - {63, nullptr, "IsSystemProgramInstalled"}, - {64, nullptr, "StartApplyDeltaTask"}, - {65, nullptr, "GetRequestServerStopper"}, - {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"}, - {67, nullptr, "CancelApplicationApplyDelta"}, - {68, nullptr, "ResumeApplicationApplyDelta"}, - {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"}, - {70, nullptr, "ResumeAll"}, - {71, nullptr, "GetStorageSize"}, - {80, nullptr, "RequestDownloadApplication"}, - {81, nullptr, "RequestDownloadAddOnContent"}, - {82, nullptr, "DownloadApplication"}, - {83, nullptr, "CheckApplicationResumeRights"}, - {84, nullptr, "GetDynamicCommitEvent"}, - {85, nullptr, "RequestUpdateApplication2"}, - {86, nullptr, "EnableApplicationCrashReport"}, - {87, nullptr, "IsApplicationCrashReportEnabled"}, - {90, nullptr, "BoostSystemMemoryResourceLimit"}, - {91, nullptr, "DeprecatedLaunchApplication"}, - {92, nullptr, "GetRunningApplicationProgramId"}, - {93, nullptr, "GetMainApplicationProgramIndex"}, - {94, nullptr, "LaunchApplication"}, - {95, nullptr, "GetApplicationLaunchInfo"}, - {96, nullptr, "AcquireApplicationLaunchInfo"}, - {97, nullptr, "GetMainApplicationProgramIndex2"}, - {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, - {100, nullptr, "ResetToFactorySettings"}, - {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, - {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, - {200, nullptr, "CalculateUserSaveDataStatistics"}, - {201, nullptr, "DeleteUserSaveDataAll"}, - {210, nullptr, "DeleteUserSystemSaveData"}, - {211, nullptr, "DeleteSaveData"}, - {220, nullptr, "UnregisterNetworkServiceAccount"}, - {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"}, - {300, nullptr, "GetApplicationShellEvent"}, - {301, nullptr, "PopApplicationShellEventInfo"}, - {302, nullptr, "LaunchLibraryApplet"}, - {303, nullptr, "TerminateLibraryApplet"}, - {304, nullptr, "LaunchSystemApplet"}, - {305, nullptr, "TerminateSystemApplet"}, - {306, nullptr, "LaunchOverlayApplet"}, - {307, nullptr, "TerminateOverlayApplet"}, - {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"}, - {401, nullptr, "InvalidateAllApplicationControlCache"}, - {402, nullptr, "RequestDownloadApplicationControlData"}, - {403, nullptr, "GetMaxApplicationControlCacheCount"}, - {404, nullptr, "InvalidateApplicationControlCache"}, - {405, nullptr, "ListApplicationControlCacheEntryInfo"}, - {406, nullptr, "GetApplicationControlProperty"}, - {502, nullptr, "RequestCheckGameCardRegistration"}, - {503, nullptr, "RequestGameCardRegistrationGoldPoint"}, - {504, nullptr, "RequestRegisterGameCard"}, - {505, nullptr, "GetGameCardMountFailureEvent"}, - {506, nullptr, "IsGameCardInserted"}, - {507, nullptr, "EnsureGameCardAccess"}, - {508, nullptr, "GetLastGameCardMountFailureResult"}, - {509, nullptr, "ListApplicationIdOnGameCard"}, - {600, nullptr, "CountApplicationContentMeta"}, - {601, nullptr, "ListApplicationContentMetaStatus"}, - {602, nullptr, "ListAvailableAddOnContent"}, - {603, nullptr, "GetOwnedApplicationContentMetaStatus"}, - {604, nullptr, "RegisterContentsExternalKey"}, - {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, - {606, nullptr, "GetContentMetaStorage"}, - {607, nullptr, "ListAvailableAddOnContent"}, - {700, nullptr, "PushDownloadTaskList"}, - {701, nullptr, "ClearTaskStatusList"}, - {702, nullptr, "RequestDownloadTaskList"}, - {703, nullptr, "RequestEnsureDownloadTask"}, - {704, nullptr, "ListDownloadTaskStatus"}, - {705, nullptr, "RequestDownloadTaskListData"}, - {800, nullptr, "RequestVersionList"}, - {801, nullptr, "ListVersionList"}, - {802, nullptr, "RequestVersionListData"}, - {900, nullptr, "GetApplicationRecord"}, - {901, nullptr, "GetApplicationRecordProperty"}, - {902, nullptr, "EnableApplicationAutoUpdate"}, - {903, nullptr, "DisableApplicationAutoUpdate"}, - {904, nullptr, "TouchApplication"}, - {905, nullptr, "RequestApplicationUpdate"}, - {906, nullptr, "IsApplicationUpdateRequested"}, - {907, nullptr, "WithdrawApplicationUpdateRequest"}, - {908, nullptr, "ListApplicationRecordInstalledContentMeta"}, - {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"}, - {910, nullptr, "HasApplicationRecord"}, - {911, nullptr, "SetPreInstalledApplication"}, - {912, nullptr, "ClearPreInstalledApplicationFlag"}, - {1000, nullptr, "RequestVerifyApplicationDeprecated"}, - {1001, nullptr, "CorruptApplicationForDebug"}, - {1002, nullptr, "RequestVerifyAddOnContentsRights"}, - {1003, nullptr, "RequestVerifyApplication"}, - {1004, nullptr, "CorruptContentForDebug"}, - {1200, nullptr, "NeedsUpdateVulnerability"}, - {1300, nullptr, "IsAnyApplicationEntityInstalled"}, - {1301, nullptr, "DeleteApplicationContentEntities"}, - {1302, nullptr, "CleanupUnrecordedApplicationEntity"}, - {1303, nullptr, "CleanupAddOnContentsWithNoRights"}, - {1304, nullptr, "DeleteApplicationContentEntity"}, - {1305, nullptr, "TryDeleteRunningApplicationEntity"}, - {1306, nullptr, "TryDeleteRunningApplicationCompletely"}, - {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, - {1308, nullptr, "DeleteApplicationCompletelyForDebug"}, - {1309, nullptr, "CleanupUnavailableAddOnContents"}, - {1400, nullptr, "PrepareShutdown"}, - {1500, nullptr, "FormatSdCard"}, - {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, - {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"}, - {1504, nullptr, "InsertSdCard"}, - {1505, nullptr, "RemoveSdCard"}, - {1600, nullptr, "GetSystemSeedForPseudoDeviceId"}, - {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"}, - {1700, nullptr, "ListApplicationDownloadingContentMeta"}, - {1701, nullptr, "GetApplicationView"}, - {1702, nullptr, "GetApplicationDownloadTaskStatus"}, - {1703, nullptr, "GetApplicationViewDownloadErrorContext"}, - {1800, nullptr, "IsNotificationSetupCompleted"}, - {1801, nullptr, "GetLastNotificationInfoCount"}, - {1802, nullptr, "ListLastNotificationInfo"}, - {1803, nullptr, "ListNotificationTask"}, - {1900, nullptr, "IsActiveAccount"}, - {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"}, - {1902, nullptr, "GetApplicationTicketInfo"}, - {2000, nullptr, "GetSystemDeliveryInfo"}, - {2001, nullptr, "SelectLatestSystemDeliveryInfo"}, - {2002, nullptr, "VerifyDeliveryProtocolVersion"}, - {2003, nullptr, "GetApplicationDeliveryInfo"}, - {2004, nullptr, "HasAllContentsToDeliver"}, - {2005, nullptr, "CompareApplicationDeliveryInfo"}, - {2006, nullptr, "CanDeliverApplication"}, - {2007, nullptr, "ListContentMetaKeyToDeliverApplication"}, - {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"}, - {2009, nullptr, "EstimateRequiredSize"}, - {2010, nullptr, "RequestReceiveApplication"}, - {2011, nullptr, "CommitReceiveApplication"}, - {2012, nullptr, "GetReceiveApplicationProgress"}, - {2013, nullptr, "RequestSendApplication"}, - {2014, nullptr, "GetSendApplicationProgress"}, - {2015, nullptr, "CompareSystemDeliveryInfo"}, - {2016, nullptr, "ListNotCommittedContentMeta"}, - {2017, nullptr, "CreateDownloadTask"}, - {2018, nullptr, "GetApplicationDeliveryInfoHash"}, - {2050, nullptr, "GetApplicationRightsOnClient"}, - {2100, nullptr, "GetApplicationTerminateResult"}, - {2101, nullptr, "GetRawApplicationTerminateResult"}, - {2150, nullptr, "CreateRightsEnvironment"}, - {2151, nullptr, "DestroyRightsEnvironment"}, - {2152, nullptr, "ActivateRightsEnvironment"}, - {2153, nullptr, "DeactivateRightsEnvironment"}, - {2154, nullptr, "ForceActivateRightsContextForExit"}, - {2160, nullptr, "AddTargetApplicationToRightsEnvironment"}, - {2161, nullptr, "SetUsersToRightsEnvironment"}, - {2170, nullptr, "GetRightsEnvironmentStatus"}, - {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"}, - {2180, nullptr, "RequestExtendRightsInRightsEnvironment"}, - {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"}, - {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"}, - {2190, nullptr, "GetRightsEnvironmentHandleForApplication"}, - {2199, nullptr, "GetRightsEnvironmentCountForDebug"}, - {2200, nullptr, "GetGameCardApplicationCopyIdentifier"}, - {2201, nullptr, "GetInstalledApplicationCopyIdentifier"}, - {2250, nullptr, "RequestReportActiveELicence"}, - {2300, nullptr, "ListEventLog"}, - }; - // clang-format on +IAccountProxyInterface::~IAccountProxyInterface() = default; + +IApplicationManagerInterface::IApplicationManagerInterface() + : ServiceFramework{"IApplicationManagerInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ListApplicationRecord"}, + {1, nullptr, "GenerateApplicationRecordCount"}, + {2, nullptr, "GetApplicationRecordUpdateSystemEvent"}, + {3, nullptr, "GetApplicationViewDeprecated"}, + {4, nullptr, "DeleteApplicationEntity"}, + {5, nullptr, "DeleteApplicationCompletely"}, + {6, nullptr, "IsAnyApplicationEntityRedundant"}, + {7, nullptr, "DeleteRedundantApplicationEntity"}, + {8, nullptr, "IsApplicationEntityMovable"}, + {9, nullptr, "MoveApplicationEntity"}, + {11, nullptr, "CalculateApplicationOccupiedSize"}, + {16, nullptr, "PushApplicationRecord"}, + {17, nullptr, "ListApplicationRecordContentMeta"}, + {19, nullptr, "LaunchApplicationOld"}, + {21, nullptr, "GetApplicationContentPath"}, + {22, nullptr, "TerminateApplication"}, + {23, nullptr, "ResolveApplicationContentPath"}, + {26, nullptr, "BeginInstallApplication"}, + {27, nullptr, "DeleteApplicationRecord"}, + {30, nullptr, "RequestApplicationUpdateInfo"}, + {32, nullptr, "CancelApplicationDownload"}, + {33, nullptr, "ResumeApplicationDownload"}, + {35, nullptr, "UpdateVersionList"}, + {36, nullptr, "PushLaunchVersion"}, + {37, nullptr, "ListRequiredVersion"}, + {38, nullptr, "CheckApplicationLaunchVersion"}, + {39, nullptr, "CheckApplicationLaunchRights"}, + {40, nullptr, "GetApplicationLogoData"}, + {41, nullptr, "CalculateApplicationDownloadRequiredSize"}, + {42, nullptr, "CleanupSdCard"}, + {43, nullptr, "CheckSdCardMountStatus"}, + {44, nullptr, "GetSdCardMountStatusChangedEvent"}, + {45, nullptr, "GetGameCardAttachmentEvent"}, + {46, nullptr, "GetGameCardAttachmentInfo"}, + {47, nullptr, "GetTotalSpaceSize"}, + {48, nullptr, "GetFreeSpaceSize"}, + {49, nullptr, "GetSdCardRemovedEvent"}, + {52, nullptr, "GetGameCardUpdateDetectionEvent"}, + {53, nullptr, "DisableApplicationAutoDelete"}, + {54, nullptr, "EnableApplicationAutoDelete"}, + {55, &IApplicationManagerInterface::GetApplicationDesiredLanguage, "GetApplicationDesiredLanguage"}, + {56, nullptr, "SetApplicationTerminateResult"}, + {57, nullptr, "ClearApplicationTerminateResult"}, + {58, nullptr, "GetLastSdCardMountUnexpectedResult"}, + {59, &IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode, "ConvertApplicationLanguageToLanguageCode"}, + {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, + {61, nullptr, "GetBackgroundDownloadStressTaskInfo"}, + {62, nullptr, "GetGameCardStopper"}, + {63, nullptr, "IsSystemProgramInstalled"}, + {64, nullptr, "StartApplyDeltaTask"}, + {65, nullptr, "GetRequestServerStopper"}, + {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"}, + {67, nullptr, "CancelApplicationApplyDelta"}, + {68, nullptr, "ResumeApplicationApplyDelta"}, + {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"}, + {70, nullptr, "ResumeAll"}, + {71, nullptr, "GetStorageSize"}, + {80, nullptr, "RequestDownloadApplication"}, + {81, nullptr, "RequestDownloadAddOnContent"}, + {82, nullptr, "DownloadApplication"}, + {83, nullptr, "CheckApplicationResumeRights"}, + {84, nullptr, "GetDynamicCommitEvent"}, + {85, nullptr, "RequestUpdateApplication2"}, + {86, nullptr, "EnableApplicationCrashReport"}, + {87, nullptr, "IsApplicationCrashReportEnabled"}, + {90, nullptr, "BoostSystemMemoryResourceLimit"}, + {91, nullptr, "DeprecatedLaunchApplication"}, + {92, nullptr, "GetRunningApplicationProgramId"}, + {93, nullptr, "GetMainApplicationProgramIndex"}, + {94, nullptr, "LaunchApplication"}, + {95, nullptr, "GetApplicationLaunchInfo"}, + {96, nullptr, "AcquireApplicationLaunchInfo"}, + {97, nullptr, "GetMainApplicationProgramIndex2"}, + {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, + {100, nullptr, "ResetToFactorySettings"}, + {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, + {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, + {200, nullptr, "CalculateUserSaveDataStatistics"}, + {201, nullptr, "DeleteUserSaveDataAll"}, + {210, nullptr, "DeleteUserSystemSaveData"}, + {211, nullptr, "DeleteSaveData"}, + {220, nullptr, "UnregisterNetworkServiceAccount"}, + {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"}, + {300, nullptr, "GetApplicationShellEvent"}, + {301, nullptr, "PopApplicationShellEventInfo"}, + {302, nullptr, "LaunchLibraryApplet"}, + {303, nullptr, "TerminateLibraryApplet"}, + {304, nullptr, "LaunchSystemApplet"}, + {305, nullptr, "TerminateSystemApplet"}, + {306, nullptr, "LaunchOverlayApplet"}, + {307, nullptr, "TerminateOverlayApplet"}, + {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"}, + {401, nullptr, "InvalidateAllApplicationControlCache"}, + {402, nullptr, "RequestDownloadApplicationControlData"}, + {403, nullptr, "GetMaxApplicationControlCacheCount"}, + {404, nullptr, "InvalidateApplicationControlCache"}, + {405, nullptr, "ListApplicationControlCacheEntryInfo"}, + {406, nullptr, "GetApplicationControlProperty"}, + {502, nullptr, "RequestCheckGameCardRegistration"}, + {503, nullptr, "RequestGameCardRegistrationGoldPoint"}, + {504, nullptr, "RequestRegisterGameCard"}, + {505, nullptr, "GetGameCardMountFailureEvent"}, + {506, nullptr, "IsGameCardInserted"}, + {507, nullptr, "EnsureGameCardAccess"}, + {508, nullptr, "GetLastGameCardMountFailureResult"}, + {509, nullptr, "ListApplicationIdOnGameCard"}, + {600, nullptr, "CountApplicationContentMeta"}, + {601, nullptr, "ListApplicationContentMetaStatus"}, + {602, nullptr, "ListAvailableAddOnContent"}, + {603, nullptr, "GetOwnedApplicationContentMetaStatus"}, + {604, nullptr, "RegisterContentsExternalKey"}, + {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, + {606, nullptr, "GetContentMetaStorage"}, + {607, nullptr, "ListAvailableAddOnContent"}, + {700, nullptr, "PushDownloadTaskList"}, + {701, nullptr, "ClearTaskStatusList"}, + {702, nullptr, "RequestDownloadTaskList"}, + {703, nullptr, "RequestEnsureDownloadTask"}, + {704, nullptr, "ListDownloadTaskStatus"}, + {705, nullptr, "RequestDownloadTaskListData"}, + {800, nullptr, "RequestVersionList"}, + {801, nullptr, "ListVersionList"}, + {802, nullptr, "RequestVersionListData"}, + {900, nullptr, "GetApplicationRecord"}, + {901, nullptr, "GetApplicationRecordProperty"}, + {902, nullptr, "EnableApplicationAutoUpdate"}, + {903, nullptr, "DisableApplicationAutoUpdate"}, + {904, nullptr, "TouchApplication"}, + {905, nullptr, "RequestApplicationUpdate"}, + {906, nullptr, "IsApplicationUpdateRequested"}, + {907, nullptr, "WithdrawApplicationUpdateRequest"}, + {908, nullptr, "ListApplicationRecordInstalledContentMeta"}, + {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"}, + {910, nullptr, "HasApplicationRecord"}, + {911, nullptr, "SetPreInstalledApplication"}, + {912, nullptr, "ClearPreInstalledApplicationFlag"}, + {1000, nullptr, "RequestVerifyApplicationDeprecated"}, + {1001, nullptr, "CorruptApplicationForDebug"}, + {1002, nullptr, "RequestVerifyAddOnContentsRights"}, + {1003, nullptr, "RequestVerifyApplication"}, + {1004, nullptr, "CorruptContentForDebug"}, + {1200, nullptr, "NeedsUpdateVulnerability"}, + {1300, nullptr, "IsAnyApplicationEntityInstalled"}, + {1301, nullptr, "DeleteApplicationContentEntities"}, + {1302, nullptr, "CleanupUnrecordedApplicationEntity"}, + {1303, nullptr, "CleanupAddOnContentsWithNoRights"}, + {1304, nullptr, "DeleteApplicationContentEntity"}, + {1305, nullptr, "TryDeleteRunningApplicationEntity"}, + {1306, nullptr, "TryDeleteRunningApplicationCompletely"}, + {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, + {1308, nullptr, "DeleteApplicationCompletelyForDebug"}, + {1309, nullptr, "CleanupUnavailableAddOnContents"}, + {1400, nullptr, "PrepareShutdown"}, + {1500, nullptr, "FormatSdCard"}, + {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, + {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"}, + {1504, nullptr, "InsertSdCard"}, + {1505, nullptr, "RemoveSdCard"}, + {1600, nullptr, "GetSystemSeedForPseudoDeviceId"}, + {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"}, + {1700, nullptr, "ListApplicationDownloadingContentMeta"}, + {1701, nullptr, "GetApplicationView"}, + {1702, nullptr, "GetApplicationDownloadTaskStatus"}, + {1703, nullptr, "GetApplicationViewDownloadErrorContext"}, + {1800, nullptr, "IsNotificationSetupCompleted"}, + {1801, nullptr, "GetLastNotificationInfoCount"}, + {1802, nullptr, "ListLastNotificationInfo"}, + {1803, nullptr, "ListNotificationTask"}, + {1900, nullptr, "IsActiveAccount"}, + {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"}, + {1902, nullptr, "GetApplicationTicketInfo"}, + {2000, nullptr, "GetSystemDeliveryInfo"}, + {2001, nullptr, "SelectLatestSystemDeliveryInfo"}, + {2002, nullptr, "VerifyDeliveryProtocolVersion"}, + {2003, nullptr, "GetApplicationDeliveryInfo"}, + {2004, nullptr, "HasAllContentsToDeliver"}, + {2005, nullptr, "CompareApplicationDeliveryInfo"}, + {2006, nullptr, "CanDeliverApplication"}, + {2007, nullptr, "ListContentMetaKeyToDeliverApplication"}, + {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"}, + {2009, nullptr, "EstimateRequiredSize"}, + {2010, nullptr, "RequestReceiveApplication"}, + {2011, nullptr, "CommitReceiveApplication"}, + {2012, nullptr, "GetReceiveApplicationProgress"}, + {2013, nullptr, "RequestSendApplication"}, + {2014, nullptr, "GetSendApplicationProgress"}, + {2015, nullptr, "CompareSystemDeliveryInfo"}, + {2016, nullptr, "ListNotCommittedContentMeta"}, + {2017, nullptr, "CreateDownloadTask"}, + {2018, nullptr, "GetApplicationDeliveryInfoHash"}, + {2050, nullptr, "GetApplicationRightsOnClient"}, + {2100, nullptr, "GetApplicationTerminateResult"}, + {2101, nullptr, "GetRawApplicationTerminateResult"}, + {2150, nullptr, "CreateRightsEnvironment"}, + {2151, nullptr, "DestroyRightsEnvironment"}, + {2152, nullptr, "ActivateRightsEnvironment"}, + {2153, nullptr, "DeactivateRightsEnvironment"}, + {2154, nullptr, "ForceActivateRightsContextForExit"}, + {2160, nullptr, "AddTargetApplicationToRightsEnvironment"}, + {2161, nullptr, "SetUsersToRightsEnvironment"}, + {2170, nullptr, "GetRightsEnvironmentStatus"}, + {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"}, + {2180, nullptr, "RequestExtendRightsInRightsEnvironment"}, + {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"}, + {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"}, + {2190, nullptr, "GetRightsEnvironmentHandleForApplication"}, + {2199, nullptr, "GetRightsEnvironmentCountForDebug"}, + {2200, nullptr, "GetGameCardApplicationCopyIdentifier"}, + {2201, nullptr, "GetInstalledApplicationCopyIdentifier"}, + {2250, nullptr, "RequestReportActiveELicence"}, + {2300, nullptr, "ListEventLog"}, + }; + // clang-format on + + RegisterHandlers(functions); +} - RegisterHandlers(functions); - } +IApplicationManagerInterface::~IApplicationManagerInterface() = default; + +void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto flag = rp.PopRaw<u64>(); + LOG_DEBUG(Service_NS, "called with flag={:016X}", flag); - void GetApplicationControlData(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto flag = rp.PopRaw<u64>(); - LOG_DEBUG(Service_NS, "called with flag={:016X}", flag); - - const auto title_id = rp.PopRaw<u64>(); - - const auto size = ctx.GetWriteBufferSize(); - - const FileSys::PatchManager pm{title_id}; - const auto control = pm.GetControlMetadata(); - - std::vector<u8> out; - - if (control.first != nullptr) { - if (size < 0x4000) { - LOG_ERROR(Service_NS, - "output buffer is too small! (actual={:016X}, expected_min=0x4000)", - size); - IPC::ResponseBuilder rb{ctx, 2}; - // TODO(DarkLordZach): Find a better error code for this. - rb.Push(ResultCode(-1)); - return; - } - - out.resize(0x4000); - const auto bytes = control.first->GetRawBytes(); - std::memcpy(out.data(), bytes.data(), bytes.size()); - } else { - LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.", - title_id); - out.resize(std::min<u64>(0x4000, size)); + const auto title_id = rp.PopRaw<u64>(); + + const auto size = ctx.GetWriteBufferSize(); + + const FileSys::PatchManager pm{title_id}; + const auto control = pm.GetControlMetadata(); + + std::vector<u8> out; + + if (control.first != nullptr) { + if (size < 0x4000) { + LOG_ERROR(Service_NS, + "output buffer is too small! (actual={:016X}, expected_min=0x4000)", size); + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(DarkLordZach): Find a better error code for this. + rb.Push(ResultCode(-1)); + return; } - if (control.second != nullptr) { - if (size < 0x4000 + control.second->GetSize()) { - LOG_ERROR(Service_NS, - "output buffer is too small! (actual={:016X}, expected_min={:016X})", - size, 0x4000 + control.second->GetSize()); - IPC::ResponseBuilder rb{ctx, 2}; - // TODO(DarkLordZach): Find a better error code for this. - rb.Push(ResultCode(-1)); - return; - } - - out.resize(0x4000 + control.second->GetSize()); - control.second->Read(out.data() + 0x4000, control.second->GetSize()); - } else { - LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.", - title_id); + out.resize(0x4000); + const auto bytes = control.first->GetRawBytes(); + std::memcpy(out.data(), bytes.data(), bytes.size()); + } else { + LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.", + title_id); + out.resize(std::min<u64>(0x4000, size)); + } + + if (control.second != nullptr) { + if (size < 0x4000 + control.second->GetSize()) { + LOG_ERROR(Service_NS, + "output buffer is too small! (actual={:016X}, expected_min={:016X})", size, + 0x4000 + control.second->GetSize()); + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(DarkLordZach): Find a better error code for this. + rb.Push(ResultCode(-1)); + return; } - ctx.WriteBuffer(out); + out.resize(0x4000 + control.second->GetSize()); + control.second->Read(out.data() + 0x4000, control.second->GetSize()); + } else { + LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.", + title_id); + } + + ctx.WriteBuffer(out); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(out.size())); +} + +void IApplicationManagerInterface::GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto supported_languages = rp.Pop<u32>(); + const auto res = GetApplicationDesiredLanguage(supported_languages); + if (res.Succeeded()) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(static_cast<u32>(out.size())); + rb.Push<u32>(*res); + } else { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); } -}; +} -class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { -public: - explicit IApplicationVersionInterface() : ServiceFramework{"IApplicationVersionInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetLaunchRequiredVersion"}, - {1, nullptr, "UpgradeLaunchRequiredVersion"}, - {35, nullptr, "UpdateVersionList"}, - {36, nullptr, "PushLaunchVersion"}, - {37, nullptr, "ListRequiredVersion"}, - {800, nullptr, "RequestVersionList"}, - {801, nullptr, "ListVersionList"}, - {802, nullptr, "RequestVersionListData"}, - {1000, nullptr, "PerformAutoUpdate"}, - }; - // clang-format on +ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage( + const u32 supported_languages) { + LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages); - RegisterHandlers(functions); - } -}; + // Get language code from settings + const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index); -class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> { -public: - explicit IContentManagerInterface() : ServiceFramework{"IContentManagerInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {11, nullptr, "CalculateApplicationOccupiedSize"}, - {43, nullptr, "CheckSdCardMountStatus"}, - {47, nullptr, "GetTotalSpaceSize"}, - {48, nullptr, "GetFreeSpaceSize"}, - {600, nullptr, "CountApplicationContentMeta"}, - {601, nullptr, "ListApplicationContentMetaStatus"}, - {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, - {607, nullptr, "IsAnyApplicationRunning"}, - }; - // clang-format on + // Convert to application language, get priority list + const auto application_language = ConvertToApplicationLanguage(language_code); + if (application_language == std::nullopt) { + return ERR_APPLICATION_LANGUAGE_NOT_FOUND; + } + const auto priority_list = GetApplicationLanguagePriorityList(*application_language); + if (!priority_list) { + return ERR_APPLICATION_LANGUAGE_NOT_FOUND; + } - RegisterHandlers(functions); + // Try to find a valid language. + for (const auto lang : *priority_list) { + const auto supported_flag = GetSupportedLanguageFlag(lang); + if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) { + return MakeResult(static_cast<u8>(lang)); + } } -}; -class IDocumentInterface final : public ServiceFramework<IDocumentInterface> { -public: - explicit IDocumentInterface() : ServiceFramework{"IDocumentInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {21, nullptr, "GetApplicationContentPath"}, - {23, nullptr, "ResolveApplicationContentPath"}, - {93, nullptr, "GetRunningApplicationProgramId"}, - }; - // clang-format on + return ERR_APPLICATION_LANGUAGE_NOT_FOUND; +} - RegisterHandlers(functions); - } -}; +void IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode( + Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto application_language = rp.Pop<u8>(); -class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> { -public: - explicit IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {701, nullptr, "ClearTaskStatusList"}, - {702, nullptr, "RequestDownloadTaskList"}, - {703, nullptr, "RequestEnsureDownloadTask"}, - {704, nullptr, "ListDownloadTaskStatus"}, - {705, nullptr, "RequestDownloadTaskListData"}, - {706, nullptr, "TryCommitCurrentApplicationDownloadTask"}, - {707, nullptr, "EnableAutoCommit"}, - {708, nullptr, "DisableAutoCommit"}, - {709, nullptr, "TriggerDynamicCommitEvent"}, - }; - // clang-format on + const auto res = ConvertApplicationLanguageToLanguageCode(application_language); + if (res.Succeeded()) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(*res); + } else { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + } +} - RegisterHandlers(functions); +ResultVal<u64> IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode( + u8 application_language) { + const auto language_code = + ConvertToLanguageCode(static_cast<ApplicationLanguage>(application_language)); + if (language_code == std::nullopt) { + return ERR_APPLICATION_LANGUAGE_NOT_FOUND; } -}; -class IECommerceInterface final : public ServiceFramework<IECommerceInterface> { -public: - explicit IECommerceInterface() : ServiceFramework{"IECommerceInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "RequestLinkDevice"}, - {1, nullptr, "RequestCleanupAllPreInstalledApplications"}, - {2, nullptr, "RequestCleanupPreInstalledApplication"}, - {3, nullptr, "RequestSyncRights"}, - {4, nullptr, "RequestUnlinkDevice"}, - {5, nullptr, "RequestRevokeAllELicense"}, - }; - // clang-format on + return MakeResult(static_cast<u64>(*language_code)); +} - RegisterHandlers(functions); - } -}; +IApplicationVersionInterface::IApplicationVersionInterface() + : ServiceFramework{"IApplicationVersionInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetLaunchRequiredVersion"}, + {1, nullptr, "UpgradeLaunchRequiredVersion"}, + {35, nullptr, "UpdateVersionList"}, + {36, nullptr, "PushLaunchVersion"}, + {37, nullptr, "ListRequiredVersion"}, + {800, nullptr, "RequestVersionList"}, + {801, nullptr, "ListVersionList"}, + {802, nullptr, "RequestVersionListData"}, + {1000, nullptr, "PerformAutoUpdate"}, + }; + // clang-format on + + RegisterHandlers(functions); +} -class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> { -public: - explicit IFactoryResetInterface() : ServiceFramework{"IFactoryResetInterface"} { - // clang-format off +IApplicationVersionInterface::~IApplicationVersionInterface() = default; + +IContentManagerInterface::IContentManagerInterface() + : ServiceFramework{"IContentManagerInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {11, nullptr, "CalculateApplicationOccupiedSize"}, + {43, nullptr, "CheckSdCardMountStatus"}, + {47, nullptr, "GetTotalSpaceSize"}, + {48, nullptr, "GetFreeSpaceSize"}, + {600, nullptr, "CountApplicationContentMeta"}, + {601, nullptr, "ListApplicationContentMetaStatus"}, + {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, + {607, nullptr, "IsAnyApplicationRunning"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IContentManagerInterface::~IContentManagerInterface() = default; + +IDocumentInterface::IDocumentInterface() : ServiceFramework{"IDocumentInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {21, nullptr, "GetApplicationContentPath"}, + {23, nullptr, "ResolveApplicationContentPath"}, + {93, nullptr, "GetRunningApplicationProgramId"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IDocumentInterface::~IDocumentInterface() = default; + +IDownloadTaskInterface::IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {701, nullptr, "ClearTaskStatusList"}, + {702, nullptr, "RequestDownloadTaskList"}, + {703, nullptr, "RequestEnsureDownloadTask"}, + {704, nullptr, "ListDownloadTaskStatus"}, + {705, nullptr, "RequestDownloadTaskListData"}, + {706, nullptr, "TryCommitCurrentApplicationDownloadTask"}, + {707, nullptr, "EnableAutoCommit"}, + {708, nullptr, "DisableAutoCommit"}, + {709, nullptr, "TriggerDynamicCommitEvent"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IDownloadTaskInterface::~IDownloadTaskInterface() = default; + +IECommerceInterface::IECommerceInterface() : ServiceFramework{"IECommerceInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestLinkDevice"}, + {1, nullptr, "RequestCleanupAllPreInstalledApplications"}, + {2, nullptr, "RequestCleanupPreInstalledApplication"}, + {3, nullptr, "RequestSyncRights"}, + {4, nullptr, "RequestUnlinkDevice"}, + {5, nullptr, "RequestRevokeAllELicense"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IECommerceInterface::~IECommerceInterface() = default; + +IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface() + : ServiceFramework{"IFactoryResetInterface"} { + // clang-format off static const FunctionInfo functions[] = { {100, nullptr, "ResetToFactorySettings"}, {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, }; - // clang-format on + // clang-format on - RegisterHandlers(functions); - } -}; - -class NS final : public ServiceFramework<NS> { -public: - explicit NS(const char* name) : ServiceFramework{name} { - // 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"}, - {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"}, - {7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"}, - {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"}, - }; - // clang-format on + RegisterHandlers(functions); +} - RegisterHandlers(functions); - } +IFactoryResetInterface::~IFactoryResetInterface() = default; + +NS::NS(const char* name) : ServiceFramework{name} { + // 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"}, + {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"}, + {7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"}, + {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); +} -private: - template <typename T> - void PushInterface(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NS, "called"); +NS::~NS() = default; - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<T>(); - } -}; +std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const { + return GetInterface<IApplicationManagerInterface>(); +} class NS_DEV final : public ServiceFramework<NS_DEV> { public: diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index b81ca8f1e..0f4bab4cb 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -8,6 +8,88 @@ namespace Service::NS { +class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> { +public: + explicit IAccountProxyInterface(); + ~IAccountProxyInterface(); +}; + +class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> { +public: + explicit IApplicationManagerInterface(); + ~IApplicationManagerInterface(); + + ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages); + ResultVal<u64> ConvertApplicationLanguageToLanguageCode(u8 application_language); + +private: + void GetApplicationControlData(Kernel::HLERequestContext& ctx); + void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx); + void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx); +}; + +class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { +public: + explicit IApplicationVersionInterface(); + ~IApplicationVersionInterface(); +}; + +class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> { +public: + explicit IContentManagerInterface(); + ~IContentManagerInterface(); +}; + +class IDocumentInterface final : public ServiceFramework<IDocumentInterface> { +public: + explicit IDocumentInterface(); + ~IDocumentInterface(); +}; + +class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> { +public: + explicit IDownloadTaskInterface(); + ~IDownloadTaskInterface(); +}; + +class IECommerceInterface final : public ServiceFramework<IECommerceInterface> { +public: + explicit IECommerceInterface(); + ~IECommerceInterface(); +}; + +class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> { +public: + explicit IFactoryResetInterface(); + ~IFactoryResetInterface(); +}; + +class NS final : public ServiceFramework<NS> { +public: + explicit NS(const char* name); + ~NS(); + + std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const; + +private: + template <typename T> + void PushInterface(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NS, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<T>(); + } + + template <typename T> + std::shared_ptr<T> GetInterface() const { + static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>, + "Not a base of ServiceFrameworkBase"); + + return std::make_shared<T>(); + } +}; + /// Registers all NS services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); diff --git a/src/core/hle/service/ns/ns_language.h b/src/core/hle/service/ns/ns_language.h new file mode 100644 index 000000000..59ac85a19 --- /dev/null +++ b/src/core/hle/service/ns/ns_language.h @@ -0,0 +1,42 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once +#include <optional> +#include <string> +#include "common/common_types.h" +#include "core/hle/service/set/set.h" + +namespace Service::NS { +/// This is nn::ns::detail::ApplicationLanguage +enum class ApplicationLanguage : u8 { + AmericanEnglish = 0, + BritishEnglish, + Japanese, + French, + German, + LatinAmericanSpanish, + Spanish, + Italian, + Dutch, + CanadianFrench, + Portuguese, + Russian, + Korean, + TraditionalChinese, + SimplifiedChinese, + Count +}; +using ApplicationLanguagePriorityList = + const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>; + +constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) { + return 1U << static_cast<u32>(lang); +} + +const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang); +std::optional<ApplicationLanguage> ConvertToApplicationLanguage( + Service::Set::LanguageCode language_code); +std::optional<Service::Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang); +} // namespace Service::NS
\ No newline at end of file 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 45812d238..0e28755bd 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -185,7 +185,8 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o IoctlGetGpuTime params{}; std::memcpy(¶ms, input.data(), input.size()); - params.gpu_time = Core::Timing::cyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks()); + const auto ns = Core::Timing::CyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks()); + params.gpu_time = static_cast<u64_le>(ns.count()); std::memcpy(output.data(), ¶ms, output.size()); return 0; } diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index aa115935d..346bad80d 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -108,8 +108,9 @@ private: LOG_DEBUG(Service_Time, "called"); const auto& core_timing = Core::System::GetInstance().CoreTiming(); - const SteadyClockTimePoint steady_clock_time_point{ - Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000}; + const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); + const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), + {}}; IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; rb.Push(RESULT_SUCCESS); rb.PushRaw(steady_clock_time_point); @@ -284,8 +285,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { } const auto& core_timing = Core::System::GetInstance().CoreTiming(); - const SteadyClockTimePoint steady_clock_time_point{ - Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000, {}}; + const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); + const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}}; CalendarTime calendar_time{}; calendar_time.year = tm->tm_year + 1900; diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index f7846db52..869406b75 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -154,17 +154,6 @@ public: virtual LoadResult Load(Kernel::Process& process) = 0; /** - * Loads the system mode that this application needs. - * This function defaults to 2 (96MB allocated to the application) if it can't read the - * information. - * @returns A pair with the optional system mode, and and the status. - */ - virtual std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() { - // 96MB allocated to the application. - return std::make_pair(2, ResultStatus::Success); - } - - /** * Get the code (typically .code section) of the application * @param buffer Reference to buffer to store data * @return ResultStatus result of function diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 8592b1f44..62c090353 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -39,7 +39,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, const std::vector<u8> uncompressed_data = Common::Compression::DecompressDataLZ4(compressed_data, header.size); - ASSERT_MSG(uncompressed_data.size() == static_cast<int>(header.size), "{} != {}", header.size, + ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size, uncompressed_data.size()); return uncompressed_data; diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 4b17bada5..90d06830f 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -12,7 +12,6 @@ #include "common/file_util.h" #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/loader/loader.h" @@ -101,7 +100,30 @@ bool VerifyLogin(const std::string& username, const std::string& token) { #endif } -TelemetrySession::TelemetrySession() { +TelemetrySession::TelemetrySession() = default; + +TelemetrySession::~TelemetrySession() { + // Log one-time session end information + const s64 shutdown_time{std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count()}; + AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time); + +#ifdef ENABLE_WEB_SERVICE + auto backend = std::make_unique<WebService::TelemetryJson>( + Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); +#else + auto backend = std::make_unique<Telemetry::NullVisitor>(); +#endif + + // Complete the session, submitting to the web service backend if necessary + field_collection.Accept(*backend); + if (Settings::values.enable_telemetry) { + backend->Complete(); + } +} + +void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { // Log one-time top-level information AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId()); @@ -112,26 +134,28 @@ TelemetrySession::TelemetrySession() { AddField(Telemetry::FieldType::Session, "Init_Time", init_time); u64 program_id{}; - const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadProgramId(program_id)}; + const Loader::ResultStatus res{app_loader.ReadProgramId(program_id)}; if (res == Loader::ResultStatus::Success) { const std::string formatted_program_id{fmt::format("{:016X}", program_id)}; AddField(Telemetry::FieldType::Session, "ProgramId", formatted_program_id); std::string name; - System::GetInstance().GetAppLoader().ReadTitle(name); + app_loader.ReadTitle(name); if (name.empty()) { auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata(); - if (nacp != nullptr) + if (nacp != nullptr) { name = nacp->GetApplicationName(); + } } - if (!name.empty()) + if (!name.empty()) { AddField(Telemetry::FieldType::Session, "ProgramName", name); + } } AddField(Telemetry::FieldType::Session, "ProgramFormat", - static_cast<u8>(System::GetInstance().GetAppLoader().GetFileType())); + static_cast<u8>(app_loader.GetFileType())); // Log application information Telemetry::AppendBuildInfo(field_collection); @@ -162,27 +186,6 @@ TelemetrySession::TelemetrySession() { Settings::values.use_docked_mode); } -TelemetrySession::~TelemetrySession() { - // Log one-time session end information - const s64 shutdown_time{std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::system_clock::now().time_since_epoch()) - .count()}; - AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time); - -#ifdef ENABLE_WEB_SERVICE - auto backend = std::make_unique<WebService::TelemetryJson>( - Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); -#else - auto backend = std::make_unique<Telemetry::NullVisitor>(); -#endif - - // Complete the session, submitting to web service if necessary - field_collection.Accept(*backend); - if (Settings::values.enable_telemetry) - backend->Complete(); - backend = nullptr; -} - bool TelemetrySession::SubmitTestcase() { #ifdef ENABLE_WEB_SERVICE auto backend = std::make_unique<WebService::TelemetryJson>( diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index cae5a45a0..17ac22377 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h @@ -4,10 +4,13 @@ #pragma once -#include <memory> #include <string> #include "common/telemetry.h" +namespace Loader { +class AppLoader; +} + namespace Core { /** @@ -15,11 +18,33 @@ namespace Core { * session, logging any one-time fields. Interfaces with the telemetry backend used for submitting * data to the web service. Submits session data on close. */ -class TelemetrySession : NonCopyable { +class TelemetrySession { public: - TelemetrySession(); + explicit TelemetrySession(); ~TelemetrySession(); + TelemetrySession(const TelemetrySession&) = delete; + TelemetrySession& operator=(const TelemetrySession&) = delete; + + TelemetrySession(TelemetrySession&&) = delete; + TelemetrySession& operator=(TelemetrySession&&) = delete; + + /** + * Adds the initial telemetry info necessary when starting up a title. + * + * This includes information such as: + * - Telemetry ID + * - Initialization time + * - Title ID + * - Title name + * - Title file format + * - Miscellaneous settings values. + * + * @param app_loader The application loader to use to retrieve + * title-specific information. + */ + void AddInitialInfo(Loader::AppLoader& app_loader); + /** * Wrapper around the Telemetry::FieldCollection::AddField method. * @param type Type of the field to add. diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h index d7f24c68a..5306daa70 100644 --- a/src/input_common/sdl/sdl.h +++ b/src/input_common/sdl/sdl.h @@ -6,15 +6,8 @@ #include <memory> #include <vector> -#include "core/frontend/input.h" #include "input_common/main.h" -union SDL_Event; - -namespace Common { -class ParamPackage; -} // namespace Common - namespace InputCommon::Polling { class DevicePoller; enum class DeviceType; diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 5949ecbae..d2e9d278f 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -6,7 +6,6 @@ #include <atomic> #include <cmath> #include <functional> -#include <iterator> #include <mutex> #include <string> #include <thread> @@ -15,7 +14,6 @@ #include <utility> #include <vector> #include <SDL.h> -#include "common/assert.h" #include "common/logging/log.h" #include "common/math_util.h" #include "common/param_package.h" @@ -23,12 +21,10 @@ #include "core/frontend/input.h" #include "input_common/sdl/sdl_impl.h" -namespace InputCommon { - -namespace SDL { +namespace InputCommon::SDL { static std::string GetGUID(SDL_Joystick* joystick) { - SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); + const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); char guid_str[33]; SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); return guid_str; @@ -37,26 +33,27 @@ static std::string GetGUID(SDL_Joystick* joystick) { /// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event); -static int SDLEventWatcher(void* userdata, SDL_Event* event) { - SDLState* sdl_state = reinterpret_cast<SDLState*>(userdata); +static int SDLEventWatcher(void* user_data, SDL_Event* event) { + auto* const sdl_state = static_cast<SDLState*>(user_data); + // Don't handle the event if we are configuring if (sdl_state->polling) { sdl_state->event_queue.Push(*event); } else { sdl_state->HandleGameControllerEvent(*event); } + return 0; } class SDLJoystick { public: - SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, - decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) - : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {} + SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick) + : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {} void SetButton(int button, bool value) { std::lock_guard lock{mutex}; - state.buttons[button] = value; + state.buttons.insert_or_assign(button, value); } bool GetButton(int button) const { @@ -66,7 +63,7 @@ public: void SetAxis(int axis, Sint16 value) { std::lock_guard lock{mutex}; - state.axes[axis] = value; + state.axes.insert_or_assign(axis, value); } float GetAxis(int axis) const { @@ -93,7 +90,7 @@ public: void SetHat(int hat, Uint8 direction) { std::lock_guard lock{mutex}; - state.hats[hat] = direction; + state.hats.insert_or_assign(hat, direction); } bool GetHatDirection(int hat, Uint8 direction) const { @@ -118,10 +115,8 @@ public: return sdl_joystick.get(); } - void SetSDLJoystick(SDL_Joystick* joystick, - decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) { - sdl_joystick = - std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter); + void SetSDLJoystick(SDL_Joystick* joystick) { + sdl_joystick.reset(joystick); } private: @@ -136,59 +131,57 @@ private: mutable std::mutex mutex; }; -/** - * Get the nth joystick with the corresponding GUID - */ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { std::lock_guard lock{joystick_map_mutex}; const auto it = joystick_map.find(guid); if (it != joystick_map.end()) { - while (it->second.size() <= port) { - auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr, - [](SDL_Joystick*) {}); + while (it->second.size() <= static_cast<std::size_t>(port)) { + auto joystick = + std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr); it->second.emplace_back(std::move(joystick)); } return it->second[port]; } - auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, [](SDL_Joystick*) {}); + auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr); return joystick_map[guid].emplace_back(std::move(joystick)); } -/** - * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie - * it to a SDLJoystick with the same guid and that port - */ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); const std::string guid = GetGUID(sdl_joystick); std::lock_guard lock{joystick_map_mutex}; - auto map_it = joystick_map.find(guid); + const auto map_it = joystick_map.find(guid); if (map_it != joystick_map.end()) { - auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(), - [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { - return sdl_joystick == joystick->GetSDLJoystick(); - }); + const auto vec_it = + std::find_if(map_it->second.begin(), map_it->second.end(), + [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { + return sdl_joystick == joystick->GetSDLJoystick(); + }); if (vec_it != map_it->second.end()) { // This is the common case: There is already an existing SDL_Joystick maped to a // SDLJoystick. return the SDLJoystick return *vec_it; } + // Search for a SDLJoystick without a mapped SDL_Joystick... - auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(), - [](const std::shared_ptr<SDLJoystick>& joystick) { - return !joystick->GetSDLJoystick(); - }); + const auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(), + [](const std::shared_ptr<SDLJoystick>& joystick) { + return !joystick->GetSDLJoystick(); + }); if (nullptr_it != map_it->second.end()) { // ... and map it (*nullptr_it)->SetSDLJoystick(sdl_joystick); return *nullptr_it; } + // There is no SDLJoystick without a mapped SDL_Joystick // Create a new SDLJoystick - auto joystick = std::make_shared<SDLJoystick>(guid, map_it->second.size(), sdl_joystick); + const int port = static_cast<int>(map_it->second.size()); + auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); return map_it->second.emplace_back(std::move(joystick)); } + auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); return joystick_map[guid].emplace_back(std::move(joystick)); } @@ -215,17 +208,19 @@ void SDLState::InitJoystick(int joystick_index) { (*it)->SetSDLJoystick(sdl_joystick); return; } - auto joystick = std::make_shared<SDLJoystick>(guid, joystick_guid_list.size(), sdl_joystick); + const int port = static_cast<int>(joystick_guid_list.size()); + auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); joystick_guid_list.emplace_back(std::move(joystick)); } void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { - std::string guid = GetGUID(sdl_joystick); + const std::string guid = GetGUID(sdl_joystick); + std::shared_ptr<SDLJoystick> joystick; { std::lock_guard lock{joystick_map_mutex}; // This call to guid is safe since the joystick is guaranteed to be in the map - auto& joystick_guid_list = joystick_map[guid]; + const auto& joystick_guid_list = joystick_map[guid]; const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { @@ -233,9 +228,10 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { }); joystick = *joystick_it; } - // Destruct SDL_Joystick outside the lock guard because SDL can internally call event calback - // which locks the mutex again - joystick->SetSDLJoystick(nullptr, [](SDL_Joystick*) {}); + + // Destruct SDL_Joystick outside the lock guard because SDL can internally call the + // event callback which locks the mutex again. + joystick->SetSDLJoystick(nullptr); } void SDLState::HandleGameControllerEvent(const SDL_Event& event) { @@ -317,9 +313,10 @@ public: trigger_if_greater(trigger_if_greater_) {} bool GetStatus() const override { - float axis_value = joystick->GetAxis(axis); - if (trigger_if_greater) + const float axis_value = joystick->GetAxis(axis); + if (trigger_if_greater) { return axis_value > threshold; + } return axis_value < threshold; } @@ -444,7 +441,7 @@ public: const int port = params.Get("port", 0); const int axis_x = params.Get("axis_x", 0); const int axis_y = params.Get("axis_y", 1); - float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); + const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); auto joystick = state.GetSDLJoystickByGUID(guid, port); @@ -470,7 +467,7 @@ SDLState::SDLState() { return; } if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) { - LOG_ERROR(Input, "Failed to set Hint for background events", SDL_GetError()); + LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError()); } SDL_AddEventWatch(&SDLEventWatcher, this); @@ -507,12 +504,12 @@ SDLState::~SDLState() { } } -Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { +static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { Common::ParamPackage params({{"engine", "sdl"}}); switch (event.type) { case SDL_JOYAXISMOTION: { - auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); + const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); params.Set("port", joystick->GetPort()); params.Set("guid", joystick->GetGUID()); params.Set("axis", event.jaxis.axis); @@ -526,14 +523,14 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve break; } case SDL_JOYBUTTONUP: { - auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); + const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); params.Set("port", joystick->GetPort()); params.Set("guid", joystick->GetGUID()); params.Set("button", event.jbutton.button); break; } case SDL_JOYHATMOTION: { - auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); + const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); params.Set("port", joystick->GetPort()); params.Set("guid", joystick->GetGUID()); params.Set("hat", event.jhat.hat); @@ -607,8 +604,8 @@ public: SDLPoller::Start(); // Reset stored axes - analog_xaxis = -1; - analog_yaxis = -1; + analog_x_axis = -1; + analog_y_axis = -1; analog_axes_joystick = -1; } @@ -620,25 +617,25 @@ public: } // An analog device needs two axes, so we need to store the axis for later and wait for // a second SDL event. The axes also must be from the same joystick. - int axis = event.jaxis.axis; - if (analog_xaxis == -1) { - analog_xaxis = axis; + const int axis = event.jaxis.axis; + if (analog_x_axis == -1) { + analog_x_axis = axis; analog_axes_joystick = event.jaxis.which; - } else if (analog_yaxis == -1 && analog_xaxis != axis && + } else if (analog_y_axis == -1 && analog_x_axis != axis && analog_axes_joystick == event.jaxis.which) { - analog_yaxis = axis; + analog_y_axis = axis; } } Common::ParamPackage params; - if (analog_xaxis != -1 && analog_yaxis != -1) { - auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); + if (analog_x_axis != -1 && analog_y_axis != -1) { + const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); params.Set("engine", "sdl"); params.Set("port", joystick->GetPort()); params.Set("guid", joystick->GetGUID()); - params.Set("axis_x", analog_xaxis); - params.Set("axis_y", analog_yaxis); - analog_xaxis = -1; - analog_yaxis = -1; + params.Set("axis_x", analog_x_axis); + params.Set("axis_y", analog_y_axis); + analog_x_axis = -1; + analog_y_axis = -1; analog_axes_joystick = -1; return params; } @@ -646,8 +643,8 @@ public: } private: - int analog_xaxis = -1; - int analog_yaxis = -1; + int analog_x_axis = -1; + int analog_y_axis = -1; SDL_JoystickID analog_axes_joystick = -1; }; } // namespace Polling @@ -667,5 +664,4 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) { return pollers; } -} // namespace SDL -} // namespace InputCommon +} // namespace InputCommon::SDL diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index 2579741d6..606a32c5b 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -6,7 +6,10 @@ #include <atomic> #include <memory> +#include <mutex> #include <thread> +#include <unordered_map> +#include "common/common_types.h" #include "common/threadsafe_queue.h" #include "input_common/sdl/sdl.h" @@ -16,9 +19,9 @@ using SDL_JoystickID = s32; namespace InputCommon::SDL { -class SDLJoystick; -class SDLButtonFactory; class SDLAnalogFactory; +class SDLButtonFactory; +class SDLJoystick; class SDLState : public State { public: @@ -31,7 +34,13 @@ public: /// Handle SDL_Events for joysticks from SDL_PollEvent void HandleGameControllerEvent(const SDL_Event& event); + /// Get the nth joystick with the corresponding GUID std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id); + + /** + * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so + * tie it to a SDLJoystick with the same guid and that port + */ std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port); /// Get all DevicePoller that use the SDL backend for a specific device type diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index e83f25fa1..ffb3ec3e0 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -1663,6 +1663,7 @@ private: INST("111000100100----", Id::BRA, Type::Flow, "BRA"), INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"), INST("111000110100---", Id::BRK, Type::Flow, "BRK"), + INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"), INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"), @@ -1686,7 +1687,6 @@ private: INST("1101111100------", Id::TLD4S, Type::Texture, "TLD4S"), INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"), INST("1101111101011---", Id::TMML, Type::Texture, "TMML"), - INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 1e2ff46b0..3f0939ec9 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -75,7 +75,7 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPus void ThreadManager::SubmitList(Tegra::CommandList&& entries) { const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))}; - const s64 synchronization_ticks{Core::Timing::usToCycles(9000)}; + const s64 synchronization_ticks{Core::Timing::usToCycles(std::chrono::microseconds{9000})}; system.CoreTiming().ScheduleEvent(synchronization_ticks, synchronization_event, fence); } diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 38497678a..65a88b06c 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -2,11 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> #include <cstddef> #include <glad/glad.h> #include "common/logging/log.h" +#include "common/scope_exit.h" #include "video_core/renderer_opengl/gl_device.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" namespace OpenGL { @@ -24,6 +27,7 @@ Device::Device() { max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS); max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS); has_variable_aoffi = TestVariableAoffi(); + has_component_indexing_bug = TestComponentIndexingBug(); } Device::Device(std::nullptr_t) { @@ -31,10 +35,12 @@ Device::Device(std::nullptr_t) { max_vertex_attributes = 16; max_varyings = 15; has_variable_aoffi = true; + has_component_indexing_bug = false; } bool Device::TestVariableAoffi() { const GLchar* AOFFI_TEST = R"(#version 430 core +// This is a unit test, please ignore me on apitrace bug reports. uniform sampler2D tex; uniform ivec2 variable_offset; void main() { @@ -51,4 +57,53 @@ void main() { return supported; } +bool Device::TestComponentIndexingBug() { + constexpr char log_message[] = "Renderer_ComponentIndexingBug: {}"; + const GLchar* COMPONENT_TEST = R"(#version 430 core +layout (std430, binding = 0) buffer OutputBuffer { + uint output_value; +}; +layout (std140, binding = 0) uniform InputBuffer { + uvec4 input_value[4096]; +}; +layout (location = 0) uniform uint idx; +void main() { + output_value = input_value[idx >> 2][idx & 3]; +})"; + const GLuint shader{glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &COMPONENT_TEST)}; + SCOPE_EXIT({ glDeleteProgram(shader); }); + glUseProgram(shader); + + OGLVertexArray vao; + vao.Create(); + glBindVertexArray(vao.handle); + + constexpr std::array<GLuint, 8> values{0, 0, 0, 0, 0x1236327, 0x985482, 0x872753, 0x2378432}; + OGLBuffer ubo; + ubo.Create(); + glNamedBufferData(ubo.handle, sizeof(values), values.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo.handle); + + OGLBuffer ssbo; + ssbo.Create(); + glNamedBufferStorage(ssbo.handle, sizeof(GLuint), nullptr, GL_CLIENT_STORAGE_BIT); + + for (GLuint index = 4; index < 8; ++index) { + glInvalidateBufferData(ssbo.handle); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo.handle); + + glProgramUniform1ui(shader, 0, index); + glDrawArrays(GL_POINTS, 0, 1); + + GLuint result; + glGetNamedBufferSubData(ssbo.handle, 0, sizeof(result), &result); + if (result != values.at(index)) { + LOG_INFO(Render_OpenGL, log_message, true); + return true; + } + } + LOG_INFO(Render_OpenGL, log_message, false); + return false; +} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index de8490682..8c8c93760 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -30,13 +30,19 @@ public: return has_variable_aoffi; } + bool HasComponentIndexingBug() const { + return has_component_indexing_bug; + } + private: static bool TestVariableAoffi(); + static bool TestComponentIndexingBug(); std::size_t uniform_buffer_alignment{}; u32 max_vertex_attributes{}; u32 max_varyings{}; bool has_variable_aoffi{}; + bool has_component_indexing_bug{}; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 7ee1c99c0..ac8a9e6b7 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -35,8 +35,8 @@ struct UnspecializedShader { namespace { /// Gets the address for the specified shader stage program -GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) { - const auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()}; +GPUVAddr GetShaderAddress(Core::System& system, Maxwell::ShaderProgram program) { + const auto& gpu{system.GPU().Maxwell3D()}; const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]}; return gpu.regs.code_address.CodeAddress() + shader_config.offset; } @@ -170,7 +170,8 @@ GLShader::ProgramResult CreateProgram(const Device& device, Maxwell::ShaderProgr CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEntries& entries, Maxwell::ShaderProgram program_type, BaseBindings base_bindings, GLenum primitive_mode, bool hint_retrievable = false) { - std::string source = "#version 430 core\n"; + std::string source = "#version 430 core\n" + "#extension GL_ARB_separate_shader_objects : enable\n\n"; source += fmt::format("#define EMULATION_UBO_BINDING {}\n", base_bindings.cbuf++); for (const auto& cbuf : entries.const_buffers) { @@ -349,7 +350,8 @@ ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode, ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, Core::Frontend::EmuWindow& emu_window, const Device& device) - : RasterizerCache{rasterizer}, emu_window{emu_window}, device{device}, disk_cache{system} {} + : RasterizerCache{rasterizer}, system{system}, emu_window{emu_window}, device{device}, + disk_cache{system} {} void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback) { @@ -545,42 +547,45 @@ std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecia } Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { - if (!Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.shaders) { - return last_shaders[static_cast<u32>(program)]; + if (!system.GPU().Maxwell3D().dirty_flags.shaders) { + return last_shaders[static_cast<std::size_t>(program)]; } - auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; - const GPUVAddr program_addr{GetShaderAddress(program)}; + auto& memory_manager{system.GPU().MemoryManager()}; + const GPUVAddr program_addr{GetShaderAddress(system, program)}; // Look up shader in the cache based on address - const auto& host_ptr{memory_manager.GetPointer(program_addr)}; + const auto host_ptr{memory_manager.GetPointer(program_addr)}; Shader shader{TryGet(host_ptr)}; + if (shader) { + return last_shaders[static_cast<std::size_t>(program)] = shader; + } - if (!shader) { - // No shader found - create a new one - ProgramCode program_code{GetShaderCode(memory_manager, program_addr, host_ptr)}; - ProgramCode program_code_b; - if (program == Maxwell::ShaderProgram::VertexA) { - const GPUVAddr program_addr_b{GetShaderAddress(Maxwell::ShaderProgram::VertexB)}; - program_code_b = GetShaderCode(memory_manager, program_addr_b, - memory_manager.GetPointer(program_addr_b)); - } - const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); - const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)}; - const auto found = precompiled_shaders.find(unique_identifier); - if (found != precompiled_shaders.end()) { - shader = - std::make_shared<CachedShader>(cpu_addr, unique_identifier, program, disk_cache, - precompiled_programs, found->second, host_ptr); - } else { - shader = std::make_shared<CachedShader>( - device, cpu_addr, unique_identifier, program, disk_cache, precompiled_programs, - std::move(program_code), std::move(program_code_b), host_ptr); - } - Register(shader); + // No shader found - create a new one + ProgramCode program_code{GetShaderCode(memory_manager, program_addr, host_ptr)}; + ProgramCode program_code_b; + if (program == Maxwell::ShaderProgram::VertexA) { + const GPUVAddr program_addr_b{GetShaderAddress(system, Maxwell::ShaderProgram::VertexB)}; + program_code_b = GetShaderCode(memory_manager, program_addr_b, + memory_manager.GetPointer(program_addr_b)); + } + + const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); + const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)}; + const auto found = precompiled_shaders.find(unique_identifier); + if (found != precompiled_shaders.end()) { + // Create a shader from the cache + shader = std::make_shared<CachedShader>(cpu_addr, unique_identifier, program, disk_cache, + precompiled_programs, found->second, host_ptr); + } else { + // Create a shader from guest memory + shader = std::make_shared<CachedShader>( + device, cpu_addr, unique_identifier, program, disk_cache, precompiled_programs, + std::move(program_code), std::move(program_code_b), host_ptr); } + Register(shader); - return last_shaders[static_cast<u32>(program)] = shader; + return last_shaders[static_cast<std::size_t>(program)] = shader; } } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 64e5a5594..09bd0761d 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -137,6 +137,7 @@ private: CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, const std::set<GLenum>& supported_formats); + Core::System& system; Core::Frontend::EmuWindow& emu_window; const Device& device; ShaderDiskCacheOpenGL disk_cache; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 6d4658c8b..29de5c9db 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -31,6 +31,8 @@ using Tegra::Shader::IpaInterpMode; using Tegra::Shader::IpaMode; using Tegra::Shader::IpaSampleMode; using Tegra::Shader::Register; + +using namespace std::string_literals; using namespace VideoCommon::Shader; using Maxwell = Tegra::Engines::Maxwell3D::Regs; @@ -43,7 +45,6 @@ struct TextureAoffi {}; using TextureArgument = std::pair<Type, Node>; using TextureIR = std::variant<TextureAoffi, TextureArgument>; -enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 }; constexpr u32 MAX_CONSTBUFFER_ELEMENTS = static_cast<u32>(RasterizerOpenGL::MaxConstbufferSize) / (4 * sizeof(float)); @@ -93,11 +94,9 @@ private: }; /// Generates code to use for a swizzle operation. -std::string GetSwizzle(u32 elem) { - ASSERT(elem <= 3); - std::string swizzle = "."; - swizzle += "xyzw"[elem]; - return swizzle; +constexpr const char* GetSwizzle(u32 element) { + constexpr std::array<const char*, 4> swizzle = {".x", ".y", ".z", ".w"}; + return swizzle.at(element); } /// Translate topology @@ -247,6 +246,12 @@ private: code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices); code.AddNewLine(); + code.AddLine("in gl_PerVertex {{"); + ++code.scope; + code.AddLine("vec4 gl_Position;"); + --code.scope; + code.AddLine("}} gl_in[];"); + DeclareVertexRedeclarations(); } @@ -349,7 +354,7 @@ private: } void DeclareInputAttribute(Attribute::Index index, bool skip_unused) { - const u32 generic_index{GetGenericAttributeIndex(index)}; + const u32 location{GetGenericAttributeIndex(index)}; std::string name{GetInputAttribute(index)}; if (stage == ShaderStage::Geometry) { @@ -358,19 +363,13 @@ private: std::string suffix; if (stage == ShaderStage::Fragment) { - const auto input_mode{header.ps.GetAttributeUse(generic_index)}; + const auto input_mode{header.ps.GetAttributeUse(location)}; if (skip_unused && input_mode == AttributeUse::Unused) { return; } suffix = GetInputFlags(input_mode); } - u32 location = generic_index; - if (stage != ShaderStage::Vertex) { - // If inputs are varyings, add an offset - location += GENERIC_VARYING_START_LOCATION; - } - code.AddLine("layout (location = {}) {} in vec4 {};", location, suffix, name); } @@ -395,7 +394,7 @@ private: } void DeclareOutputAttribute(Attribute::Index index) { - const u32 location{GetGenericAttributeIndex(index) + GENERIC_VARYING_START_LOCATION}; + const u32 location{GetGenericAttributeIndex(index)}; code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index)); } @@ -577,9 +576,26 @@ private: if (std::holds_alternative<OperationNode>(*offset)) { // Indirect access const std::string final_offset = code.GenerateTemporary(); - code.AddLine("uint {} = (ftou({}) / 4);", final_offset, Visit(offset)); - return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), - final_offset, final_offset); + code.AddLine("uint {} = ftou({}) >> 2;", final_offset, Visit(offset)); + + if (!device.HasComponentIndexingBug()) { + return fmt::format("{}[{} >> 2][{} & 3]", GetConstBuffer(cbuf->GetIndex()), + final_offset, final_offset); + } + + // AMD's proprietary GLSL compiler emits ill code for variable component access. + // To bypass this driver bug generate 4 ifs, one per each component. + const std::string pack = code.GenerateTemporary(); + code.AddLine("vec4 {} = {}[{} >> 2];", pack, GetConstBuffer(cbuf->GetIndex()), + final_offset); + + const std::string result = code.GenerateTemporary(); + code.AddLine("float {};", result); + for (u32 swizzle = 0; swizzle < 4; ++swizzle) { + code.AddLine("if (({} & 3) == {}) {} = {}{};", final_offset, swizzle, result, + pack, GetSwizzle(swizzle)); + } + return result; } UNREACHABLE_MSG("Unmanaged offset node type"); @@ -633,10 +649,14 @@ private: switch (attribute) { case Attribute::Index::Position: - if (stage != ShaderStage::Fragment) { - return GeometryPass("position") + GetSwizzle(element); - } else { - return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element); + switch (stage) { + case ShaderStage::Geometry: + return fmt::format("gl_in[ftou({})].gl_Position{}", Visit(buffer), + GetSwizzle(element)); + case ShaderStage::Fragment: + return element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element)); + default: + UNREACHABLE(); } case Attribute::Index::PointCoord: switch (element) { @@ -921,7 +941,7 @@ private: target = [&]() -> std::string { switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) { case Attribute::Index::Position: - return "position" + GetSwizzle(abuf->GetElement()); + return "gl_Position"s + GetSwizzle(abuf->GetElement()); case Attribute::Index::PointSize: return "gl_PointSize"; case Attribute::Index::ClipDistances0123: @@ -1506,9 +1526,7 @@ private: // If a geometry shader is attached, it will always flip (it's the last stage before // fragment). For more info about flipping, refer to gl_shader_gen.cpp. - code.AddLine("position.xy *= viewport_flip.xy;"); - code.AddLine("gl_Position = position;"); - code.AddLine("position.w = 1.0;"); + code.AddLine("gl_Position.xy *= viewport_flip.xy;"); code.AddLine("EmitVertex();"); return {}; } @@ -1526,6 +1544,16 @@ private: return "uintBitsToFloat(config_pack[2])"; } + template <u32 element> + std::string LocalInvocationId(Operation) { + return "utof(gl_LocalInvocationID"s + GetSwizzle(element) + ')'; + } + + template <u32 element> + std::string WorkGroupId(Operation) { + return "utof(gl_WorkGroupID"s + GetSwizzle(element) + ')'; + } + static constexpr OperationDecompilersArray operation_decompilers = { &GLSLDecompiler::Assign, @@ -1665,6 +1693,12 @@ private: &GLSLDecompiler::EndPrimitive, &GLSLDecompiler::YNegate, + &GLSLDecompiler::LocalInvocationId<0>, + &GLSLDecompiler::LocalInvocationId<1>, + &GLSLDecompiler::LocalInvocationId<2>, + &GLSLDecompiler::WorkGroupId<0>, + &GLSLDecompiler::WorkGroupId<1>, + &GLSLDecompiler::WorkGroupId<2>, }; std::string GetRegister(u32 index) const { @@ -1730,8 +1764,7 @@ private: } u32 GetNumPhysicalVaryings() const { - return std::min<u32>(device.GetMaxVaryings() - GENERIC_VARYING_START_LOCATION, - Maxwell::NumVaryings); + return std::min<u32>(device.GetMaxVaryings(), Maxwell::NumVaryings); } const Device& device; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 7ab0b4553..c845b29aa 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -19,13 +19,10 @@ static constexpr u32 PROGRAM_OFFSET{10}; ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) { const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); - std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; - out += "// Shader Unique Id: VS" + id + "\n\n"; + std::string out = "// Shader Unique Id: VS" + id + "\n\n"; out += GetCommonDeclarations(); out += R"( -layout (location = 0) out vec4 position; - layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config { vec4 viewport_flip; uvec4 config_pack; // instance_id, flip_stage, y_direction, padding @@ -49,7 +46,6 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config { out += R"( void main() { - position = vec4(0.0, 0.0, 0.0, 0.0); execute_vertex(); )"; @@ -60,19 +56,12 @@ void main() { out += R"( // Set Position Y direction - position.y *= utof(config_pack[2]); + gl_Position.y *= utof(config_pack[2]); // Check if the flip stage is VertexB // Config pack's second value is flip_stage if (config_pack[1] == 1) { // Viewport can be flipped, which is unsupported by glViewport - position.xy *= viewport_flip.xy; - } - gl_Position = position; - - // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0 - // For now, this is here to bring order in lieu of proper emulation - if (config_pack[1] == 1) { - position.w = 1.0; + gl_Position.xy *= viewport_flip.xy; } })"; @@ -82,14 +71,10 @@ void main() { ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup) { const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); - std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; - out += "// Shader Unique Id: GS" + id + "\n\n"; + std::string out = "// Shader Unique Id: GS" + id + "\n\n"; out += GetCommonDeclarations(); out += R"( -layout (location = 0) in vec4 gs_position[]; -layout (location = 0) out vec4 position; - layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config { vec4 viewport_flip; uvec4 config_pack; // instance_id, flip_stage, y_direction, padding @@ -113,8 +98,7 @@ void main() { ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup) { const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); - std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; - out += "// Shader Unique Id: FS" + id + "\n\n"; + std::string out = "// Shader Unique Id: FS" + id + "\n\n"; out += GetCommonDeclarations(); out += R"( @@ -127,8 +111,6 @@ layout (location = 5) out vec4 FragColor5; layout (location = 6) out vec4 FragColor6; layout (location = 7) out vec4 FragColor7; -layout (location = 0) in noperspective vec4 position; - layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config { vec4 viewport_flip; uvec4 config_pack; // instance_id, flip_stage, y_direction, padding diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 2fb368014..a85fcae5a 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -1045,6 +1045,18 @@ private: return {}; } + template <u32 element> + Id LocalInvocationId(Operation) { + UNIMPLEMENTED(); + return {}; + } + + template <u32 element> + Id WorkGroupId(Operation) { + UNIMPLEMENTED(); + return {}; + } + Id DeclareBuiltIn(spv::BuiltIn builtin, spv::StorageClass storage, Id type, const std::string& name) { const Id id = OpVariable(type, storage); @@ -1301,6 +1313,12 @@ private: &SPIRVDecompiler::EndPrimitive, &SPIRVDecompiler::YNegate, + &SPIRVDecompiler::LocalInvocationId<0>, + &SPIRVDecompiler::LocalInvocationId<1>, + &SPIRVDecompiler::LocalInvocationId<2>, + &SPIRVDecompiler::WorkGroupId<0>, + &SPIRVDecompiler::WorkGroupId<1>, + &SPIRVDecompiler::WorkGroupId<2>, }; const VKDevice& device; diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp index ca7af72e1..a6c123573 100644 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp @@ -14,6 +14,7 @@ using Tegra::Shader::ConditionCode; using Tegra::Shader::Instruction; using Tegra::Shader::OpCode; using Tegra::Shader::Register; +using Tegra::Shader::SystemVariable; u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { const Instruction instr = {program_code[pc]}; @@ -59,20 +60,33 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { break; } case OpCode::Id::MOV_SYS: { - switch (instr.sys20) { - case Tegra::Shader::SystemVariable::InvocationInfo: { - LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); - SetRegister(bb, instr.gpr0, Immediate(0u)); - break; - } - case Tegra::Shader::SystemVariable::Ydirection: { - // Config pack's third value is Y_NEGATE's state. - SetRegister(bb, instr.gpr0, Operation(OperationCode::YNegate)); - break; - } - default: - UNIMPLEMENTED_MSG("Unhandled system move: {}", static_cast<u32>(instr.sys20.Value())); - } + const Node value = [&]() { + switch (instr.sys20) { + case SystemVariable::Ydirection: + return Operation(OperationCode::YNegate); + case SystemVariable::InvocationInfo: + LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); + return Immediate(0u); + case SystemVariable::TidX: + return Operation(OperationCode::LocalInvocationIdX); + case SystemVariable::TidY: + return Operation(OperationCode::LocalInvocationIdY); + case SystemVariable::TidZ: + return Operation(OperationCode::LocalInvocationIdZ); + case SystemVariable::CtaIdX: + return Operation(OperationCode::WorkGroupIdX); + case SystemVariable::CtaIdY: + return Operation(OperationCode::WorkGroupIdY); + case SystemVariable::CtaIdZ: + return Operation(OperationCode::WorkGroupIdZ); + default: + UNIMPLEMENTED_MSG("Unhandled system move: {}", + static_cast<u32>(instr.sys20.Value())); + return Immediate(0u); + } + }(); + SetRegister(bb, instr.gpr0, value); + break; } case OpCode::Id::BRA: { diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 35f72bddb..ff7472e30 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -181,7 +181,13 @@ enum class OperationCode { EmitVertex, /// () -> void EndPrimitive, /// () -> void - YNegate, /// () -> float + YNegate, /// () -> float + LocalInvocationIdX, /// () -> uint + LocalInvocationIdY, /// () -> uint + LocalInvocationIdZ, /// () -> uint + WorkGroupIdX, /// () -> uint + WorkGroupIdY, /// () -> uint + WorkGroupIdZ, /// () -> uint Amount, }; diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp index 7fbc9deeb..6b5c7ba61 100644 --- a/src/yuzu/applets/profile_select.cpp +++ b/src/yuzu/applets/profile_select.cpp @@ -27,20 +27,20 @@ constexpr std::array<u8, 107> backup_jpeg{ 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, }; -QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) { +QString FormatUserEntryText(const QString& username, Common::UUID uuid) { return QtProfileSelectionDialog::tr( "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " "00112233-4455-6677-8899-AABBCCDDEEFF))") .arg(username, QString::fromStdString(uuid.FormatSwitch())); } -QString GetImagePath(Service::Account::UUID uuid) { +QString GetImagePath(Common::UUID uuid) { const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; return QString::fromStdString(path); } -QPixmap GetIcon(Service::Account::UUID uuid) { +QPixmap GetIcon(Common::UUID uuid) { QPixmap icon{GetImagePath(uuid)}; if (!icon) { @@ -122,21 +122,15 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) QtProfileSelectionDialog::~QtProfileSelectionDialog() = default; void QtProfileSelectionDialog::accept() { - ok = true; QDialog::accept(); } void QtProfileSelectionDialog::reject() { - ok = false; user_index = 0; QDialog::reject(); } -bool QtProfileSelectionDialog::GetStatus() const { - return ok; -} - -u32 QtProfileSelectionDialog::GetIndex() const { +int QtProfileSelectionDialog::GetIndex() const { return user_index; } @@ -154,12 +148,12 @@ QtProfileSelector::QtProfileSelector(GMainWindow& parent) { QtProfileSelector::~QtProfileSelector() = default; void QtProfileSelector::SelectProfile( - std::function<void(std::optional<Service::Account::UUID>)> callback) const { + std::function<void(std::optional<Common::UUID>)> callback) const { this->callback = std::move(callback); emit MainWindowSelectProfile(); } -void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) { +void QtProfileSelector::MainWindowFinishedSelection(std::optional<Common::UUID> uuid) { // Acquire the HLE mutex std::lock_guard lock{HLE::g_hle_lock}; callback(uuid); diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h index 1c2922e54..cee886a77 100644 --- a/src/yuzu/applets/profile_select.h +++ b/src/yuzu/applets/profile_select.h @@ -9,6 +9,7 @@ #include <QList> #include <QTreeView> #include "core/frontend/applets/profile_select.h" +#include "core/hle/service/acc/profile_manager.h" class GMainWindow; class QDialogButtonBox; @@ -29,15 +30,13 @@ public: void accept() override; void reject() override; - bool GetStatus() const; - u32 GetIndex() const; + int GetIndex() const; private: - bool ok = false; - u32 user_index = 0; - void SelectUser(const QModelIndex& index); + int user_index = 0; + QVBoxLayout* layout; QTreeView* tree_view; QStandardItemModel* item_model; @@ -60,14 +59,13 @@ public: explicit QtProfileSelector(GMainWindow& parent); ~QtProfileSelector() override; - void SelectProfile( - std::function<void(std::optional<Service::Account::UUID>)> callback) const override; + void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const override; signals: void MainWindowSelectProfile() const; private: - void MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid); + void MainWindowFinishedSelection(std::optional<Common::UUID> uuid); - mutable std::function<void(std::optional<Service::Account::UUID>)> callback; + mutable std::function<void(std::optional<Common::UUID>)> callback; }; diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index 5223ec977..af36f07c6 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -104,13 +104,11 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default; void QtSoftwareKeyboardDialog::accept() { - ok = true; text = line_edit->text().toStdU16String(); QDialog::accept(); } void QtSoftwareKeyboardDialog::reject() { - ok = false; text.clear(); QDialog::reject(); } @@ -119,10 +117,6 @@ std::u16string QtSoftwareKeyboardDialog::GetText() const { return text; } -bool QtSoftwareKeyboardDialog::GetStatus() const { - return ok; -} - QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) { connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window, &GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection); diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h index 78c5a042b..44bcece75 100644 --- a/src/yuzu/applets/software_keyboard.h +++ b/src/yuzu/applets/software_keyboard.h @@ -36,10 +36,8 @@ public: void reject() override; std::u16string GetText() const; - bool GetStatus() const; private: - bool ok = false; std::u16string text; QDialogButtonBox* buttons; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index eeee603d1..afec33b61 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -26,6 +26,8 @@ EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} +EmuThread::~EmuThread() = default; + void EmuThread::run() { render_window->MakeCurrent(); @@ -185,7 +187,7 @@ private: bool do_painting; }; -GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) +GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) : QWidget(parent), emu_thread(emu_thread) { setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") .arg(QString::fromUtf8(Common::g_build_name), @@ -194,8 +196,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) setAttribute(Qt::WA_AcceptTouchEvents); InputCommon::Init(); - connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast<GMainWindow*>(parent), - &GMainWindow::OnLoadComplete); + connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); } GRenderWindow::~GRenderWindow() { @@ -247,9 +248,9 @@ void GRenderWindow::PollEvents() {} void GRenderWindow::OnFramebufferSizeChanged() { // Screen changes potentially incur a change in screen DPI, hence we should update the // framebuffer size - qreal pixelRatio = GetWindowPixelRatio(); - unsigned width = child->QPaintDevice::width() * pixelRatio; - unsigned height = child->QPaintDevice::height() * pixelRatio; + const qreal pixel_ratio = GetWindowPixelRatio(); + const u32 width = child->QPaintDevice::width() * pixel_ratio; + const u32 height = child->QPaintDevice::height() * pixel_ratio; UpdateCurrentFramebufferLayout(width, height); } @@ -266,7 +267,7 @@ void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) { } void GRenderWindow::BackupGeometry() { - geometry = ((QWidget*)this)->saveGeometry(); + geometry = QWidget::saveGeometry(); } void GRenderWindow::RestoreGeometry() { @@ -283,10 +284,11 @@ void GRenderWindow::restoreGeometry(const QByteArray& geometry) { QByteArray GRenderWindow::saveGeometry() { // If we are a top-level widget, store the current geometry // otherwise, store the last backup - if (parent() == nullptr) - return ((QWidget*)this)->saveGeometry(); - else - return geometry; + if (parent() == nullptr) { + return QWidget::saveGeometry(); + } + + return geometry; } qreal GRenderWindow::GetWindowPixelRatio() const { @@ -294,10 +296,10 @@ qreal GRenderWindow::GetWindowPixelRatio() const { return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; } -std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { +std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { const qreal pixel_ratio = GetWindowPixelRatio(); - return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), - static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; + return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), + static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; } void GRenderWindow::closeEvent(QCloseEvent* event) { @@ -353,7 +355,7 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) { InputCommon::GetKeyboard()->ReleaseAllKeys(); } -void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { +void GRenderWindow::OnClientAreaResized(u32 width, u32 height) { NotifyClientAreaSizeChanged(std::make_pair(width, height)); } @@ -394,7 +396,7 @@ void GRenderWindow::InitRenderTarget() { context->setShareContext(shared_context.get()); context->setFormat(fmt); context->create(); - fmt.setSwapInterval(false); + fmt.setSwapInterval(0); child = new GGLWidgetInternal(this, shared_context.get()); container = QWidget::createWindowContainer(child, this); @@ -424,24 +426,29 @@ void GRenderWindow::InitRenderTarget() { BackupGeometry(); } -void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) { +void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { auto& renderer = Core::System::GetInstance().Renderer(); - if (!res_scale) + if (res_scale == 0) { res_scale = VideoCore::GetResolutionScaleFactor(renderer); + } const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)}; screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); - renderer.RequestScreenshot(screenshot_image.bits(), - [=] { - screenshot_image.mirrored(false, true).save(screenshot_path); - LOG_INFO(Frontend, "The screenshot is saved."); - }, - layout); + renderer.RequestScreenshot( + screenshot_image.bits(), + [=] { + const std::string std_screenshot_path = screenshot_path.toStdString(); + if (screenshot_image.mirrored(false, true).save(screenshot_path)) { + LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path); + } else { + LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path); + } + }, + layout); } -void GRenderWindow::OnMinimalClientAreaChangeRequest( - const std::pair<unsigned, unsigned>& minimal_size) { +void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) { setMinimumSize(minimal_size.first, minimal_size.second); } diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 3df33aca1..2fc64895f 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -27,11 +27,12 @@ namespace VideoCore { enum class LoadCallbackStage; } -class EmuThread : public QThread { +class EmuThread final : public QThread { Q_OBJECT public: explicit EmuThread(GRenderWindow* render_window); + ~EmuThread() override; /** * Start emulation (on new thread) @@ -114,7 +115,7 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { Q_OBJECT public: - GRenderWindow(QWidget* parent, EmuThread* emu_thread); + GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); ~GRenderWindow() override; // EmuWindow implementation @@ -133,17 +134,17 @@ public: QByteArray saveGeometry(); // overridden qreal GetWindowPixelRatio() const; - std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; + std::pair<u32, u32> ScaleTouch(QPointF pos) const; void closeEvent(QCloseEvent* event) override; bool event(QEvent* event) override; void focusOutEvent(QFocusEvent* event) override; - void OnClientAreaResized(unsigned width, unsigned height); + void OnClientAreaResized(u32 width, u32 height); void InitRenderTarget(); - void CaptureScreenshot(u16 res_scale, const QString& screenshot_path); + void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); public slots: void moveContext(); // overridden @@ -162,8 +163,7 @@ private: void TouchUpdateEvent(const QTouchEvent* event); void TouchEndEvent(); - void OnMinimalClientAreaChangeRequest( - const std::pair<unsigned, unsigned>& minimal_size) override; + void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; QWidget* container = nullptr; GGLWidgetInternal* child = nullptr; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index db27da23e..b1942bedc 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -645,6 +645,8 @@ void Config::ReadUIGamelistValues() { UISettings::values.icon_size = ReadSetting(QStringLiteral("icon_size"), 64).toUInt(); UISettings::values.row_1_text_id = ReadSetting(QStringLiteral("row_1_text_id"), 3).toUInt(); UISettings::values.row_2_text_id = ReadSetting(QStringLiteral("row_2_text_id"), 2).toUInt(); + UISettings::values.cache_game_list = + ReadSetting(QStringLiteral("cache_game_list"), true).toBool(); qt_config->endGroup(); } @@ -1009,6 +1011,7 @@ void Config::SaveUIGamelistValues() { WriteSetting(QStringLiteral("icon_size"), UISettings::values.icon_size, 64); WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3); WriteSetting(QStringLiteral("row_2_text_id"), UISettings::values.row_2_text_id, 2); + WriteSetting(QStringLiteral("cache_game_list"), UISettings::values.cache_game_list, true); qt_config->endGroup(); } diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index b0f9b814d..110842eb2 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -22,11 +22,11 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) } connect(ui->volume_slider, &QSlider::valueChanged, this, - &ConfigureAudio::setVolumeIndicatorText); + &ConfigureAudio::SetVolumeIndicatorText); - this->setConfiguration(); + SetConfiguration(); connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this, - &ConfigureAudio::updateAudioDevices); + &ConfigureAudio::UpdateAudioDevices); const bool is_powered_on = Core::System::GetInstance().IsPoweredOn(); ui->output_sink_combo_box->setEnabled(!is_powered_on); @@ -35,20 +35,20 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) ConfigureAudio::~ConfigureAudio() = default; -void ConfigureAudio::setConfiguration() { - setOutputSinkFromSinkID(); +void ConfigureAudio::SetConfiguration() { + SetOutputSinkFromSinkID(); // The device list cannot be pre-populated (nor listed) until the output sink is known. - updateAudioDevices(ui->output_sink_combo_box->currentIndex()); + UpdateAudioDevices(ui->output_sink_combo_box->currentIndex()); - setAudioDeviceFromDeviceID(); + SetAudioDeviceFromDeviceID(); ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum()); - setVolumeIndicatorText(ui->volume_slider->sliderPosition()); + SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); } -void ConfigureAudio::setOutputSinkFromSinkID() { +void ConfigureAudio::SetOutputSinkFromSinkID() { int new_sink_index = 0; const QString sink_id = QString::fromStdString(Settings::values.sink_id); @@ -62,7 +62,7 @@ void ConfigureAudio::setOutputSinkFromSinkID() { ui->output_sink_combo_box->setCurrentIndex(new_sink_index); } -void ConfigureAudio::setAudioDeviceFromDeviceID() { +void ConfigureAudio::SetAudioDeviceFromDeviceID() { int new_device_index = -1; const QString device_id = QString::fromStdString(Settings::values.audio_device_id); @@ -76,11 +76,11 @@ void ConfigureAudio::setAudioDeviceFromDeviceID() { ui->audio_device_combo_box->setCurrentIndex(new_device_index); } -void ConfigureAudio::setVolumeIndicatorText(int percentage) { +void ConfigureAudio::SetVolumeIndicatorText(int percentage) { ui->volume_indicator->setText(tr("%1%", "Volume percentage (e.g. 50%)").arg(percentage)); } -void ConfigureAudio::applyConfiguration() { +void ConfigureAudio::ApplyConfiguration() { Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) .toStdString(); @@ -92,7 +92,7 @@ void ConfigureAudio::applyConfiguration() { static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum(); } -void ConfigureAudio::updateAudioDevices(int sink_index) { +void ConfigureAudio::UpdateAudioDevices(int sink_index) { ui->audio_device_combo_box->clear(); ui->audio_device_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name)); @@ -102,6 +102,6 @@ void ConfigureAudio::updateAudioDevices(int sink_index) { } } -void ConfigureAudio::retranslateUi() { +void ConfigureAudio::RetranslateUI() { ui->retranslateUi(this); } diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h index 8771421c0..2bb92d9b5 100644 --- a/src/yuzu/configuration/configure_audio.h +++ b/src/yuzu/configuration/configure_audio.h @@ -18,16 +18,16 @@ public: explicit ConfigureAudio(QWidget* parent = nullptr); ~ConfigureAudio() override; - void applyConfiguration(); - void retranslateUi(); + void ApplyConfiguration(); + void RetranslateUI(); private: - void updateAudioDevices(int sink_index); + void UpdateAudioDevices(int sink_index); - void setConfiguration(); - void setOutputSinkFromSinkID(); - void setAudioDeviceFromDeviceID(); - void setVolumeIndicatorText(int percentage); + void SetConfiguration(); + void SetOutputSinkFromSinkID(); + void SetAudioDeviceFromDeviceID(); + void SetVolumeIndicatorText(int percentage); std::unique_ptr<Ui::ConfigureAudio> ui; }; diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui index a29a0e265..a098b9acc 100644 --- a/src/yuzu/configuration/configure_audio.ui +++ b/src/yuzu/configuration/configure_audio.ui @@ -20,7 +20,7 @@ <item> <layout class="QHBoxLayout"> <item> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="label_1"> <property name="text"> <string>Output Engine:</string> </property> @@ -44,7 +44,7 @@ <item> <layout class="QHBoxLayout"> <item> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="label_2"> <property name="text"> <string>Audio Device:</string> </property> @@ -61,7 +61,7 @@ <number>0</number> </property> <item> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="label_3"> <property name="text"> <string>Volume:</string> </property> diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 550cf9dca..afc2e3b38 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -16,7 +16,8 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) { ui->setupUi(this); - this->setConfiguration(); + SetConfiguration(); + connect(ui->open_log_button, &QPushButton::pressed, []() { QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir)); QDesktopServices::openUrl(QUrl::fromLocalFile(path)); @@ -25,7 +26,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co ConfigureDebug::~ConfigureDebug() = default; -void ConfigureDebug::setConfiguration() { +void ConfigureDebug::SetConfiguration() { ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub); ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port); @@ -37,7 +38,7 @@ void ConfigureDebug::setConfiguration() { ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); } -void ConfigureDebug::applyConfiguration() { +void ConfigureDebug::ApplyConfiguration() { Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked(); Settings::values.gdbstub_port = ui->gdbport_spinbox->value(); UISettings::values.show_console = ui->toggle_console->isChecked(); diff --git a/src/yuzu/configuration/configure_debug.h b/src/yuzu/configuration/configure_debug.h index c6420b18c..06b89026c 100644 --- a/src/yuzu/configuration/configure_debug.h +++ b/src/yuzu/configuration/configure_debug.h @@ -18,10 +18,10 @@ public: explicit ConfigureDebug(QWidget* parent = nullptr); ~ConfigureDebug() override; - void applyConfiguration(); + void ApplyConfiguration(); private: - void setConfiguration(); + void SetConfiguration(); std::unique_ptr<Ui::ConfigureDebug> ui; }; diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 758a92335..5ca9ce0e6 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -45,7 +45,7 @@ </spacer> </item> <item> - <widget class="QLabel" name="label_2"> + <widget class="QLabel" name="label_1"> <property name="text"> <string>Port:</string> </property> @@ -70,11 +70,11 @@ <property name="title"> <string>Logging</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> + <layout class="QVBoxLayout" name="verticalLayout_4"> <item> - <layout class="QHBoxLayout" name="horizontalLayout"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="label_2"> <property name="text"> <string>Global Log Filter</string> </property> @@ -86,7 +86,7 @@ </layout> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> <widget class="QCheckBox" name="toggle_console"> <property name="text"> @@ -111,11 +111,11 @@ <property name="title"> <string>Homebrew</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> + <layout class="QVBoxLayout" name="verticalLayout_5"> <item> - <layout class="QHBoxLayout" name="horizontalLayout"> + <layout class="QHBoxLayout" name="horizontalLayout_4"> <item> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="label_3"> <property name="text"> <string>Arguments String</string> </property> @@ -134,7 +134,7 @@ <property name="title"> <string>Dump</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_4"> + <layout class="QVBoxLayout" name="verticalLayout_6"> <item> <widget class="QCheckBox" name="dump_decompressed_nso"> <property name="whatsThis"> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 8086f9d6b..a1b1e00bc 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -15,11 +15,11 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { ui->setupUi(this); ui->hotkeysTab->Populate(registry); - this->setConfiguration(); - this->PopulateSelectionList(); - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + SetConfiguration(); + PopulateSelectionList(); + connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, &ConfigureDialog::UpdateVisibleTabs); @@ -29,19 +29,19 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) ConfigureDialog::~ConfigureDialog() = default; -void ConfigureDialog::setConfiguration() {} - -void ConfigureDialog::applyConfiguration() { - ui->generalTab->applyConfiguration(); - ui->gameListTab->applyConfiguration(); - ui->systemTab->applyConfiguration(); - ui->profileManagerTab->applyConfiguration(); - ui->inputTab->applyConfiguration(); - ui->hotkeysTab->applyConfiguration(registry); - ui->graphicsTab->applyConfiguration(); - ui->audioTab->applyConfiguration(); - ui->debugTab->applyConfiguration(); - ui->webTab->applyConfiguration(); +void ConfigureDialog::SetConfiguration() {} + +void ConfigureDialog::ApplyConfiguration() { + ui->generalTab->ApplyConfiguration(); + ui->gameListTab->ApplyConfiguration(); + ui->systemTab->ApplyConfiguration(); + ui->profileManagerTab->ApplyConfiguration(); + ui->inputTab->ApplyConfiguration(); + ui->hotkeysTab->ApplyConfiguration(registry); + ui->graphicsTab->ApplyConfiguration(); + ui->audioTab->ApplyConfiguration(); + ui->debugTab->ApplyConfiguration(); + ui->webTab->ApplyConfiguration(); Settings::Apply(); Settings::LogSettings(); } diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 2363ba584..7e7d90f59 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -20,10 +20,10 @@ public: explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry); ~ConfigureDialog() override; - void applyConfiguration(); + void ApplyConfiguration(); private: - void setConfiguration(); + void SetConfiguration(); void UpdateVisibleTabs(); void PopulateSelectionList(); diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp index 6f0d75605..4b5b0ee48 100644 --- a/src/yuzu/configuration/configure_gamelist.cpp +++ b/src/yuzu/configuration/configure_gamelist.cpp @@ -12,20 +12,20 @@ #include "yuzu/ui_settings.h" namespace { -constexpr std::array<std::pair<u32, const char*>, 5> default_icon_sizes{{ +constexpr std::array default_icon_sizes{ std::make_pair(0, QT_TR_NOOP("None")), std::make_pair(32, QT_TR_NOOP("Small (32x32)")), std::make_pair(64, QT_TR_NOOP("Standard (64x64)")), std::make_pair(128, QT_TR_NOOP("Large (128x128)")), std::make_pair(256, QT_TR_NOOP("Full Size (256x256)")), -}}; +}; -constexpr std::array<const char*, 4> row_text_names{{ +constexpr std::array row_text_names{ QT_TR_NOOP("Filename"), QT_TR_NOOP("Filetype"), QT_TR_NOOP("Title ID"), QT_TR_NOOP("Title Name"), -}}; +}; } // Anonymous namespace ConfigureGameList::ConfigureGameList(QWidget* parent) @@ -35,7 +35,7 @@ ConfigureGameList::ConfigureGameList(QWidget* parent) InitializeIconSizeComboBox(); InitializeRowComboBoxes(); - this->setConfiguration(); + SetConfiguration(); // Force game list reload if any of the relevant settings are changed. connect(ui->show_unknown, &QCheckBox::stateChanged, this, @@ -50,7 +50,7 @@ ConfigureGameList::ConfigureGameList(QWidget* parent) ConfigureGameList::~ConfigureGameList() = default; -void ConfigureGameList::applyConfiguration() { +void ConfigureGameList::ApplyConfiguration() { UISettings::values.show_unknown = ui->show_unknown->isChecked(); UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); @@ -63,7 +63,7 @@ void ConfigureGameList::RequestGameListUpdate() { UISettings::values.is_game_list_reload_pending.exchange(true); } -void ConfigureGameList::setConfiguration() { +void ConfigureGameList::SetConfiguration() { ui->show_unknown->setChecked(UISettings::values.show_unknown); ui->show_add_ons->setChecked(UISettings::values.show_add_ons); ui->icon_size_combobox->setCurrentIndex( diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_gamelist.h index bf3f1cdfa..e11822919 100644 --- a/src/yuzu/configuration/configure_gamelist.h +++ b/src/yuzu/configuration/configure_gamelist.h @@ -18,12 +18,12 @@ public: explicit ConfigureGameList(QWidget* parent = nullptr); ~ConfigureGameList() override; - void applyConfiguration(); + void ApplyConfiguration(); private: void RequestGameListUpdate(); - void setConfiguration(); + void SetConfiguration(); void changeEvent(QEvent*) override; void RetranslateUI(); diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index dd25dc6e1..8bdc1787a 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -18,7 +18,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) QString::fromUtf8(theme.second)); } - this->setConfiguration(); + SetConfiguration(); connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this, [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); @@ -28,7 +28,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) ConfigureGeneral::~ConfigureGeneral() = default; -void ConfigureGeneral::setConfiguration() { +void ConfigureGeneral::SetConfiguration() { ui->toggle_deepscan->setChecked(UISettings::values.game_directory_deepscan); ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); @@ -36,7 +36,7 @@ void ConfigureGeneral::setConfiguration() { ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); } -void ConfigureGeneral::applyConfiguration() { +void ConfigureGeneral::ApplyConfiguration() { UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked(); UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h index df41d995b..fd5fd2a91 100644 --- a/src/yuzu/configuration/configure_general.h +++ b/src/yuzu/configuration/configure_general.h @@ -20,10 +20,10 @@ public: explicit ConfigureGeneral(QWidget* parent = nullptr); ~ConfigureGeneral() override; - void applyConfiguration(); + void ApplyConfiguration(); private: - void setConfiguration(); + void SetConfiguration(); std::unique_ptr<Ui::ConfigureGeneral> ui; }; diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 08ea41b0f..02e91701a 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -51,29 +51,29 @@ Resolution FromResolutionFactor(float factor) { ConfigureGraphics::ConfigureGraphics(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureGraphics) { - ui->setupUi(this); - this->setConfiguration(); - ui->frame_limit->setEnabled(Settings::values.use_frame_limit); - connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, - &QSpinBox::setEnabled); + SetConfiguration(); + + connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled); connect(ui->bg_button, &QPushButton::clicked, this, [this] { const QColor new_bg_color = QColorDialog::getColor(bg_color); - if (!new_bg_color.isValid()) + if (!new_bg_color.isValid()) { return; + } UpdateBackgroundColorButton(new_bg_color); }); } ConfigureGraphics::~ConfigureGraphics() = default; -void ConfigureGraphics::setConfiguration() { +void ConfigureGraphics::SetConfiguration() { const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); ui->resolution_factor_combobox->setCurrentIndex( static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); + ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); ui->frame_limit->setValue(Settings::values.frame_limit); ui->use_compatibility_profile->setEnabled(runtime_lock); ui->use_compatibility_profile->setChecked(Settings::values.use_compatibility_profile); @@ -88,7 +88,7 @@ void ConfigureGraphics::setConfiguration() { Settings::values.bg_blue)); } -void ConfigureGraphics::applyConfiguration() { +void ConfigureGraphics::ApplyConfiguration() { Settings::values.resolution_factor = ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index f2799822d..6b3c39705 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -18,10 +18,10 @@ public: explicit ConfigureGraphics(QWidget* parent = nullptr); ~ConfigureGraphics() override; - void applyConfiguration(); + void ApplyConfiguration(); private: - void setConfiguration(); + void SetConfiguration(); void UpdateBackgroundColorButton(QColor color); diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index 9fb970c21..04119e9d7 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp @@ -92,7 +92,7 @@ bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const { return false; } -void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) { +void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { for (int key_id = 0; key_id < model->rowCount(); key_id++) { const QStandardItem* parent = model->item(key_id, 0); for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) { @@ -113,6 +113,6 @@ void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) { registry.SaveHotkeys(); } -void ConfigureHotkeys::retranslateUi() { +void ConfigureHotkeys::RetranslateUI() { ui->retranslateUi(this); } diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h index e77d73c35..a1f4c7134 100644 --- a/src/yuzu/configuration/configure_hotkeys.h +++ b/src/yuzu/configuration/configure_hotkeys.h @@ -21,8 +21,8 @@ public: explicit ConfigureHotkeys(QWidget* parent = nullptr); ~ConfigureHotkeys() override; - void applyConfiguration(HotkeyRegistry& registry); - void retranslateUi(); + void ApplyConfiguration(HotkeyRegistry& registry); + void RetranslateUI(); /** * Populates the hotkey list widget using data from the provided registry. diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 87e459714..fb1210bcb 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -50,12 +50,12 @@ void OnDockedModeChanged(bool last_state, bool new_state) { namespace { template <typename Dialog, typename... Args> void CallConfigureDialog(ConfigureInput& parent, Args&&... args) { - parent.applyConfiguration(); + parent.ApplyConfiguration(); Dialog dialog(&parent, std::forward<Args>(args)...); const auto res = dialog.exec(); if (res == QDialog::Accepted) { - dialog.applyConfiguration(); + dialog.ApplyConfiguration(); } } } // Anonymous namespace @@ -79,24 +79,24 @@ ConfigureInput::ConfigureInput(QWidget* parent) tr("Single Right Joycon"), tr("Single Left Joycon")}); } - this->loadConfiguration(); - updateUIEnabled(); + LoadConfiguration(); + UpdateUIEnabled(); connect(ui->restore_defaults_button, &QPushButton::pressed, this, - &ConfigureInput::restoreDefaults); + &ConfigureInput::RestoreDefaults); for (auto* enabled : players_controller) { connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this, - &ConfigureInput::updateUIEnabled); + &ConfigureInput::UpdateUIEnabled); } - connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled); + connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); connect(ui->handheld_connected, &QCheckBox::stateChanged, this, - &ConfigureInput::updateUIEnabled); - connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled); - connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled); - connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled); + &ConfigureInput::UpdateUIEnabled); + connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); + connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); + connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this, - &ConfigureInput::updateUIEnabled); + &ConfigureInput::UpdateUIEnabled); for (std::size_t i = 0; i < players_configure.size(); ++i) { connect(players_configure[i], &QPushButton::pressed, this, @@ -118,7 +118,7 @@ ConfigureInput::ConfigureInput(QWidget* parent) ConfigureInput::~ConfigureInput() = default; -void ConfigureInput::applyConfiguration() { +void ConfigureInput::ApplyConfiguration() { for (std::size_t i = 0; i < players_controller.size(); ++i) { const auto controller_type_index = players_controller[i]->currentIndex(); @@ -144,7 +144,7 @@ void ConfigureInput::applyConfiguration() { Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked(); } -void ConfigureInput::updateUIEnabled() { +void ConfigureInput::UpdateUIEnabled() { bool hit_disabled = false; for (auto* player : players_controller) { player->setDisabled(hit_disabled); @@ -168,7 +168,7 @@ void ConfigureInput::updateUIEnabled() { ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); } -void ConfigureInput::loadConfiguration() { +void ConfigureInput::LoadConfiguration() { std::stable_partition( Settings::values.players.begin(), Settings::values.players.begin() + @@ -191,10 +191,10 @@ void ConfigureInput::loadConfiguration() { ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled); ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); - updateUIEnabled(); + UpdateUIEnabled(); } -void ConfigureInput::restoreDefaults() { +void ConfigureInput::RestoreDefaults() { players_controller[0]->setCurrentIndex(2); for (std::size_t i = 1; i < players_controller.size(); ++i) { @@ -207,5 +207,5 @@ void ConfigureInput::restoreDefaults() { ui->keyboard_enabled->setCheckState(Qt::Unchecked); ui->debug_enabled->setCheckState(Qt::Unchecked); ui->touchscreen_enabled->setCheckState(Qt::Checked); - updateUIEnabled(); + UpdateUIEnabled(); } diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index b8e62cc2b..6a2125215 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -30,15 +30,15 @@ public: ~ConfigureInput() override; /// Save all button configurations to settings file - void applyConfiguration(); + void ApplyConfiguration(); private: - void updateUIEnabled(); + void UpdateUIEnabled(); /// Load configuration settings. - void loadConfiguration(); + void LoadConfiguration(); /// Restore all buttons to their default values. - void restoreDefaults(); + void RestoreDefaults(); std::unique_ptr<Ui::ConfigureInput> ui; diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui index 0a2d9f024..8378451c8 100644 --- a/src/yuzu/configuration/configure_input.ui +++ b/src/yuzu/configuration/configure_input.ui @@ -17,7 +17,7 @@ <item> <layout class="QVBoxLayout" name="verticalLayout"> <item> - <widget class="QGroupBox" name="gridGroupBox"> + <widget class="QGroupBox" name="gridGroupBox_1"> <property name="title"> <string>Players</string> </property> @@ -260,7 +260,7 @@ </widget> </item> <item> - <widget class="QGroupBox" name="gridGroupBox"> + <widget class="QGroupBox" name="gridGroupBox_2"> <property name="title"> <string>Handheld</string> </property> @@ -332,7 +332,7 @@ </widget> </item> <item> - <widget class="QGroupBox" name="gridGroupBox"> + <widget class="QGroupBox" name="gridGroupBox_3"> <property name="title"> <string>Other</string> </property> diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 95b0a656a..32d2a6815 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -245,7 +245,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i button->setContextMenuPolicy(Qt::CustomContextMenu); connect(button, &QPushButton::released, [=] { - handleClick( + HandleClick( button_map[button_id], [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, InputCommon::Polling::DeviceType::Button); @@ -274,7 +274,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i analog_button->setContextMenuPolicy(Qt::CustomContextMenu); connect(analog_button, &QPushButton::released, [=]() { - handleClick(analog_map_buttons[analog_id][sub_button_id], + HandleClick(analog_map_buttons[analog_id][sub_button_id], [=](const Common::ParamPackage& params) { SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); @@ -304,7 +304,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i QMessageBox::information(this, tr("Information"), tr("After pressing OK, first move your joystick horizontally, " "and then vertically.")); - handleClick( + HandleClick( analog_map_stick[analog_id], [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; }, InputCommon::Polling::DeviceType::Analog); @@ -312,17 +312,17 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); }); - connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); + connect(ui->buttonRestoreDefaults, &QPushButton::released, [this] { RestoreDefaults(); }); timeout_timer->setSingleShot(true); - connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); }); + connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); - connect(poll_timer.get(), &QTimer::timeout, [this]() { + connect(poll_timer.get(), &QTimer::timeout, [this] { Common::ParamPackage params; for (auto& poller : device_pollers) { params = poller->GetNextInput(); if (params.Has("engine")) { - setPollingResult(params, false); + SetPollingResult(params, false); return; } } @@ -340,8 +340,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i [this, i] { OnControllerButtonClick(static_cast<int>(i)); }); } - this->loadConfiguration(); - this->resize(0, 0); + LoadConfiguration(); + resize(0, 0); // TODO(wwylele): enable this when we actually emulate it ui->buttonHome->setEnabled(false); @@ -349,7 +349,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i ConfigureInputPlayer::~ConfigureInputPlayer() = default; -void ConfigureInputPlayer::applyConfiguration() { +void ConfigureInputPlayer::ApplyConfiguration() { auto& buttons = debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons; auto& analogs = @@ -382,7 +382,7 @@ void ConfigureInputPlayer::OnControllerButtonClick(int i) { QStringLiteral("QPushButton { background-color: %1 }").arg(controller_colors[i].name())); } -void ConfigureInputPlayer::loadConfiguration() { +void ConfigureInputPlayer::LoadConfiguration() { if (debug) { std::transform(Settings::values.debug_pad_buttons.begin(), Settings::values.debug_pad_buttons.end(), buttons_param.begin(), @@ -399,7 +399,7 @@ void ConfigureInputPlayer::loadConfiguration() { [](const std::string& str) { return Common::ParamPackage(str); }); } - updateButtonLabels(); + UpdateButtonLabels(); if (debug) return; @@ -421,7 +421,7 @@ void ConfigureInputPlayer::loadConfiguration() { } } -void ConfigureInputPlayer::restoreDefaults() { +void ConfigureInputPlayer::RestoreDefaults() { 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])}; @@ -434,7 +434,7 @@ void ConfigureInputPlayer::restoreDefaults() { SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); } } - updateButtonLabels(); + UpdateButtonLabels(); } void ConfigureInputPlayer::ClearAll() { @@ -458,10 +458,10 @@ void ConfigureInputPlayer::ClearAll() { } } - updateButtonLabels(); + UpdateButtonLabels(); } -void ConfigureInputPlayer::updateButtonLabels() { +void ConfigureInputPlayer::UpdateButtonLabels() { for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { button_map[button]->setText(ButtonToText(buttons_param[button])); } @@ -481,7 +481,7 @@ void ConfigureInputPlayer::updateButtonLabels() { } } -void ConfigureInputPlayer::handleClick( +void ConfigureInputPlayer::HandleClick( QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, InputCommon::Polling::DeviceType type) { button->setText(tr("[press key]")); @@ -509,7 +509,7 @@ void ConfigureInputPlayer::handleClick( poll_timer->start(200); // Check for new inputs every 200ms } -void ConfigureInputPlayer::setPollingResult(const Common::ParamPackage& params, bool abort) { +void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) { releaseKeyboard(); releaseMouse(); timeout_timer->stop(); @@ -522,22 +522,23 @@ void ConfigureInputPlayer::setPollingResult(const Common::ParamPackage& params, (*input_setter)(params); } - updateButtonLabels(); + UpdateButtonLabels(); input_setter = std::nullopt; } void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { - if (!input_setter || !event) + if (!input_setter || !event) { return; + } if (event->key() != Qt::Key_Escape) { if (want_keyboard_keys) { - setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, + SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, false); } else { // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling return; } } - setPollingResult({}, true); + SetPollingResult({}, true); } diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index ade8d4435..27924fcce 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -38,28 +38,28 @@ public: ~ConfigureInputPlayer() override; /// Save all button configurations to settings file - void applyConfiguration(); + void ApplyConfiguration(); private: void OnControllerButtonClick(int i); /// Load configuration settings. - void loadConfiguration(); + void LoadConfiguration(); /// Restore all buttons to their default values. - void restoreDefaults(); + void RestoreDefaults(); /// Clear all input configuration void ClearAll(); /// Update UI to reflect current configuration. - void updateButtonLabels(); + void UpdateButtonLabels(); /// Called when the button was pressed. - void handleClick(QPushButton* button, + void HandleClick(QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, InputCommon::Polling::DeviceType type); /// Finish polling and configure input using the input_setter - void setPollingResult(const Common::ParamPackage& params, bool abort); + void SetPollingResult(const Common::ParamPackage& params, bool abort); /// Handle key press events. void keyPressEvent(QKeyEvent* event) override; diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp index 07d71e9d1..6140a5573 100644 --- a/src/yuzu/configuration/configure_input_simple.cpp +++ b/src/yuzu/configuration/configure_input_simple.cpp @@ -15,12 +15,12 @@ namespace { template <typename Dialog, typename... Args> void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) { - caller->applyConfiguration(); + caller->ApplyConfiguration(); Dialog dialog(caller, std::forward<Args>(args)...); const auto res = dialog.exec(); if (res == QDialog::Accepted) { - dialog.applyConfiguration(); + dialog.ApplyConfiguration(); } } @@ -103,27 +103,29 @@ ConfigureInputSimple::ConfigureInputSimple(QWidget* parent) &ConfigureInputSimple::OnSelectProfile); connect(ui->profile_configure, &QPushButton::pressed, this, &ConfigureInputSimple::OnConfigure); - this->loadConfiguration(); + LoadConfiguration(); } ConfigureInputSimple::~ConfigureInputSimple() = default; -void ConfigureInputSimple::applyConfiguration() { +void ConfigureInputSimple::ApplyConfiguration() { auto index = ui->profile_combobox->currentIndex(); // Make the stored index for "Custom" very large so that if new profiles are added it // doesn't change. - if (index >= static_cast<int>(INPUT_PROFILES.size() - 1)) + if (index >= static_cast<int>(INPUT_PROFILES.size() - 1)) { index = std::numeric_limits<int>::max(); + } UISettings::values.profile_index = index; } -void ConfigureInputSimple::loadConfiguration() { +void ConfigureInputSimple::LoadConfiguration() { const auto index = UISettings::values.profile_index; - if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0) + if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0) { ui->profile_combobox->setCurrentIndex(static_cast<int>(INPUT_PROFILES.size() - 1)); - else + } else { ui->profile_combobox->setCurrentIndex(index); + } } void ConfigureInputSimple::OnSelectProfile(int index) { diff --git a/src/yuzu/configuration/configure_input_simple.h b/src/yuzu/configuration/configure_input_simple.h index 5b6b69994..573c2db2b 100644 --- a/src/yuzu/configuration/configure_input_simple.h +++ b/src/yuzu/configuration/configure_input_simple.h @@ -27,11 +27,11 @@ public: ~ConfigureInputSimple() override; /// Save all button configurations to settings file - void applyConfiguration(); + void ApplyConfiguration(); private: /// Load configuration settings. - void loadConfiguration(); + void LoadConfiguration(); void OnSelectProfile(int index); void OnConfigure(); diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp index a14bb1475..26f53128f 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.cpp +++ b/src/yuzu/configuration/configure_mouse_advanced.cpp @@ -84,7 +84,7 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) button->setContextMenuPolicy(Qt::CustomContextMenu); connect(button, &QPushButton::released, [=] { - handleClick( + HandleClick( button_map[button_id], [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, InputCommon::Polling::DeviceType::Button); @@ -105,48 +105,48 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) } connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); }); - connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); + connect(ui->buttonRestoreDefaults, &QPushButton::released, [this] { RestoreDefaults(); }); timeout_timer->setSingleShot(true); - connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); }); + connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); - connect(poll_timer.get(), &QTimer::timeout, [this]() { + connect(poll_timer.get(), &QTimer::timeout, [this] { Common::ParamPackage params; for (auto& poller : device_pollers) { params = poller->GetNextInput(); if (params.Has("engine")) { - setPollingResult(params, false); + SetPollingResult(params, false); return; } } }); - loadConfiguration(); + LoadConfiguration(); resize(0, 0); } ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default; -void ConfigureMouseAdvanced::applyConfiguration() { +void ConfigureMouseAdvanced::ApplyConfiguration() { std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.mouse_buttons.begin(), [](const Common::ParamPackage& param) { return param.Serialize(); }); } -void ConfigureMouseAdvanced::loadConfiguration() { +void ConfigureMouseAdvanced::LoadConfiguration() { std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(), buttons_param.begin(), [](const std::string& str) { return Common::ParamPackage(str); }); - updateButtonLabels(); + UpdateButtonLabels(); } -void ConfigureMouseAdvanced::restoreDefaults() { +void ConfigureMouseAdvanced::RestoreDefaults() { for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) { buttons_param[button_id] = Common::ParamPackage{ InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])}; } - updateButtonLabels(); + UpdateButtonLabels(); } void ConfigureMouseAdvanced::ClearAll() { @@ -157,16 +157,16 @@ void ConfigureMouseAdvanced::ClearAll() { } } - updateButtonLabels(); + UpdateButtonLabels(); } -void ConfigureMouseAdvanced::updateButtonLabels() { +void ConfigureMouseAdvanced::UpdateButtonLabels() { for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) { button_map[button]->setText(ButtonToText(buttons_param[button])); } } -void ConfigureMouseAdvanced::handleClick( +void ConfigureMouseAdvanced::HandleClick( QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, InputCommon::Polling::DeviceType type) { button->setText(tr("[press key]")); @@ -194,7 +194,7 @@ void ConfigureMouseAdvanced::handleClick( poll_timer->start(200); // Check for new inputs every 200ms } -void ConfigureMouseAdvanced::setPollingResult(const Common::ParamPackage& params, bool abort) { +void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) { releaseKeyboard(); releaseMouse(); timeout_timer->stop(); @@ -207,22 +207,23 @@ void ConfigureMouseAdvanced::setPollingResult(const Common::ParamPackage& params (*input_setter)(params); } - updateButtonLabels(); + UpdateButtonLabels(); input_setter = std::nullopt; } void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) { - if (!input_setter || !event) + if (!input_setter || !event) { return; + } if (event->key() != Qt::Key_Escape) { if (want_keyboard_keys) { - setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, + SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, false); } else { // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling return; } } - setPollingResult({}, true); + SetPollingResult({}, true); } diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h index e04da4bf2..373f992c9 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.h +++ b/src/yuzu/configuration/configure_mouse_advanced.h @@ -25,26 +25,26 @@ public: explicit ConfigureMouseAdvanced(QWidget* parent); ~ConfigureMouseAdvanced() override; - void applyConfiguration(); + void ApplyConfiguration(); private: /// Load configuration settings. - void loadConfiguration(); + void LoadConfiguration(); /// Restore all buttons to their default values. - void restoreDefaults(); + void RestoreDefaults(); /// Clear all input configuration void ClearAll(); /// Update UI to reflect current configuration. - void updateButtonLabels(); + void UpdateButtonLabels(); /// Called when the button was pressed. - void handleClick(QPushButton* button, + void HandleClick(QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, InputCommon::Polling::DeviceType type); /// Finish polling and configure input using the input_setter - void setPollingResult(const Common::ParamPackage& params, bool abort); + void SetPollingResult(const Common::ParamPackage& params, bool abort); /// Handle key press events. void keyPressEvent(QKeyEvent* event) override; diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_general.cpp index 2bdfc8e5a..275519c7b 100644 --- a/src/yuzu/configuration/configure_per_general.cpp +++ b/src/yuzu/configuration/configure_per_general.cpp @@ -13,6 +13,8 @@ #include <QTimer> #include <QTreeView> +#include "common/common_paths.h" +#include "common/file_util.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/xts_archive.h" @@ -65,12 +67,12 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id) connect(item_model, &QStandardItemModel::itemChanged, [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); - this->loadConfiguration(); + LoadConfiguration(); } ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default; -void ConfigurePerGameGeneral::applyConfiguration() { +void ConfigurePerGameGeneral::ApplyConfiguration() { std::vector<std::string> disabled_addons; for (const auto& item : list_items) { @@ -79,15 +81,23 @@ void ConfigurePerGameGeneral::applyConfiguration() { disabled_addons.push_back(item.front()->text().toStdString()); } + auto current = Settings::values.disabled_addons[title_id]; + std::sort(disabled_addons.begin(), disabled_addons.end()); + std::sort(current.begin(), current.end()); + if (disabled_addons != current) { + FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + + "game_list" + DIR_SEP + fmt::format("{:016X}.pv.txt", title_id)); + } + Settings::values.disabled_addons[title_id] = disabled_addons; } -void ConfigurePerGameGeneral::loadFromFile(FileSys::VirtualFile file) { +void ConfigurePerGameGeneral::LoadFromFile(FileSys::VirtualFile file) { this->file = std::move(file); - this->loadConfiguration(); + LoadConfiguration(); } -void ConfigurePerGameGeneral::loadConfiguration() { +void ConfigurePerGameGeneral::LoadConfiguration() { if (file == nullptr) { return; } diff --git a/src/yuzu/configuration/configure_per_general.h b/src/yuzu/configuration/configure_per_general.h index f8a7d5326..b95e07079 100644 --- a/src/yuzu/configuration/configure_per_general.h +++ b/src/yuzu/configuration/configure_per_general.h @@ -30,11 +30,13 @@ public: ~ConfigurePerGameGeneral() override; /// Save all button configurations to settings file - void applyConfiguration(); + void ApplyConfiguration(); - void loadFromFile(FileSys::VirtualFile file); + void LoadFromFile(FileSys::VirtualFile file); private: + void LoadConfiguration(); + std::unique_ptr<Ui::ConfigurePerGameGeneral> ui; FileSys::VirtualFile file; u64 title_id; @@ -45,6 +47,4 @@ private: QGraphicsScene* scene; std::vector<QList<QStandardItem*>> list_items; - - void loadConfiguration(); }; diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index 002a51780..7c1597488 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -33,14 +33,13 @@ constexpr std::array<u8, 107> backup_jpeg{ 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, }; -QString GetImagePath(Service::Account::UUID uuid) { +QString GetImagePath(Common::UUID uuid) { const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; return QString::fromStdString(path); } -QString GetAccountUsername(const Service::Account::ProfileManager& manager, - Service::Account::UUID uuid) { +QString GetAccountUsername(const Service::Account::ProfileManager& manager, Common::UUID uuid) { Service::Account::ProfileBase profile; if (!manager.GetProfileBase(uuid, profile)) { return {}; @@ -51,14 +50,14 @@ QString GetAccountUsername(const Service::Account::ProfileManager& manager, return QString::fromStdString(text); } -QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) { +QString FormatUserEntryText(const QString& username, Common::UUID uuid) { return ConfigureProfileManager::tr("%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " "00112233-4455-6677-8899-AABBCCDDEEFF))") .arg(username, QString::fromStdString(uuid.FormatSwitch())); } -QPixmap GetIcon(Service::Account::UUID uuid) { +QPixmap GetIcon(Common::UUID uuid) { QPixmap icon{GetImagePath(uuid)}; if (!icon) { @@ -120,12 +119,12 @@ ConfigureProfileManager ::ConfigureProfileManager(QWidget* parent) scene = new QGraphicsScene; ui->current_user_icon->setScene(scene); - this->setConfiguration(); + SetConfiguration(); } ConfigureProfileManager::~ConfigureProfileManager() = default; -void ConfigureProfileManager::setConfiguration() { +void ConfigureProfileManager::SetConfiguration() { enabled = !Core::System::GetInstance().IsPoweredOn(); item_model->removeRows(0, item_model->rowCount()); list_items.clear(); @@ -165,9 +164,10 @@ void ConfigureProfileManager::UpdateCurrentUser() { ui->current_user_username->setText(username); } -void ConfigureProfileManager::applyConfiguration() { - if (!enabled) +void ConfigureProfileManager::ApplyConfiguration() { + if (!enabled) { return; + } Settings::Apply(); } @@ -190,7 +190,7 @@ void ConfigureProfileManager::AddUser() { return; } - const auto uuid = Service::Account::UUID::Generate(); + const auto uuid = Common::UUID::Generate(); profile_manager->CreateNewUser(uuid, username.toStdString()); item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); diff --git a/src/yuzu/configuration/configure_profile_manager.h b/src/yuzu/configuration/configure_profile_manager.h index 7fe95a2a8..4e9b4e8ea 100644 --- a/src/yuzu/configuration/configure_profile_manager.h +++ b/src/yuzu/configuration/configure_profile_manager.h @@ -30,10 +30,11 @@ public: explicit ConfigureProfileManager(QWidget* parent = nullptr); ~ConfigureProfileManager() override; - void applyConfiguration(); - void setConfiguration(); + void ApplyConfiguration(); private: + void SetConfiguration(); + void PopulateUserList(); void UpdateCurrentUser(); diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index ff18ace40..5f0ed2f61 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -16,49 +16,31 @@ #include "ui_configure_system.h" #include "yuzu/configuration/configure_system.h" -namespace { -constexpr std::array<int, 12> days_in_month = {{ - 31, - 29, - 31, - 30, - 31, - 30, - 31, - 31, - 30, - 31, - 30, - 31, -}}; -} // Anonymous namespace - ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { ui->setupUi(this); - connect(ui->combo_birthmonth, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, - &ConfigureSystem::UpdateBirthdayComboBox); connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, &ConfigureSystem::RefreshConsoleID); connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) { ui->rng_seed_edit->setEnabled(checked); - if (!checked) + if (!checked) { ui->rng_seed_edit->setText(QStringLiteral("00000000")); + } }); connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) { ui->custom_rtc_edit->setEnabled(checked); - if (!checked) + if (!checked) { ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime()); + } }); - this->setConfiguration(); + SetConfiguration(); } ConfigureSystem::~ConfigureSystem() = default; -void ConfigureSystem::setConfiguration() { +void ConfigureSystem::SetConfiguration() { enabled = !Core::System::GetInstance().IsPoweredOn(); ui->combo_language->setCurrentIndex(Settings::values.language_index); @@ -81,49 +63,27 @@ void ConfigureSystem::setConfiguration() { void ConfigureSystem::ReadSystemSettings() {} -void ConfigureSystem::applyConfiguration() { - if (!enabled) +void ConfigureSystem::ApplyConfiguration() { + if (!enabled) { return; + } Settings::values.language_index = ui->combo_language->currentIndex(); - if (ui->rng_seed_checkbox->isChecked()) + if (ui->rng_seed_checkbox->isChecked()) { Settings::values.rng_seed = ui->rng_seed_edit->text().toULongLong(nullptr, 16); - else + } else { Settings::values.rng_seed = std::nullopt; + } - if (ui->custom_rtc_checkbox->isChecked()) + if (ui->custom_rtc_checkbox->isChecked()) { Settings::values.custom_rtc = std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()); - else + } else { Settings::values.custom_rtc = std::nullopt; - - Settings::Apply(); -} - -void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) { - if (birthmonth_index < 0 || birthmonth_index >= 12) - return; - - // store current day selection - int birthday_index = ui->combo_birthday->currentIndex(); - - // get number of days in the new selected month - int days = days_in_month[birthmonth_index]; - - // if the selected day is out of range, - // reset it to 1st - if (birthday_index < 0 || birthday_index >= days) - birthday_index = 0; - - // update the day combo box - ui->combo_birthday->clear(); - for (int i = 1; i <= days; ++i) { - ui->combo_birthday->addItem(QString::number(i)); } - // restore the day selection - ui->combo_birthday->setCurrentIndex(birthday_index); + Settings::Apply(); } void ConfigureSystem::RefreshConsoleID() { @@ -134,8 +94,10 @@ void ConfigureSystem::RefreshConsoleID() { "if you use an outdated config savegame. Continue?"); reply = QMessageBox::critical(this, tr("Warning"), warning_text, QMessageBox::No | QMessageBox::Yes); - if (reply == QMessageBox::No) + if (reply == QMessageBox::No) { return; + } + u64 console_id{}; ui->label_console_id->setText( tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h index cf1e54de5..4c0329d57 100644 --- a/src/yuzu/configuration/configure_system.h +++ b/src/yuzu/configuration/configure_system.h @@ -20,20 +20,18 @@ public: explicit ConfigureSystem(QWidget* parent = nullptr); ~ConfigureSystem() override; - void applyConfiguration(); - void setConfiguration(); + void ApplyConfiguration(); private: + void SetConfiguration(); + void ReadSystemSettings(); - void UpdateBirthdayComboBox(int birthmonth_index); void RefreshConsoleID(); std::unique_ptr<Ui::ConfigureSystem> ui; bool enabled = false; - int birthmonth = 0; - int birthday = 0; int language_index = 0; int sound_index = 0; }; diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index 073327298..65745a2f8 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -22,14 +22,21 @@ <string>System Settings</string> </property> <layout class="QGridLayout" name="gridLayout"> - <item row="2" column="0"> + <item row="1" column="0"> <widget class="QLabel" name="label_sound"> <property name="text"> <string>Sound output mode</string> </property> </widget> </item> - <item row="1" column="1"> + <item row="2" column="0"> + <widget class="QLabel" name="label_console_id"> + <property name="text"> + <string>Console ID:</string> + </property> + </widget> + </item> + <item row="0" column="1"> <widget class="QComboBox" name="combo_language"> <property name="toolTip"> <string>Note: this can be overridden when region setting is auto-select</string> @@ -121,108 +128,14 @@ </item> </widget> </item> - <item row="0" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> - <item> - <widget class="QComboBox" name="combo_birthmonth"> - <item> - <property name="text"> - <string>January</string> - </property> - </item> - <item> - <property name="text"> - <string>February</string> - </property> - </item> - <item> - <property name="text"> - <string>March</string> - </property> - </item> - <item> - <property name="text"> - <string>April</string> - </property> - </item> - <item> - <property name="text"> - <string>May</string> - </property> - </item> - <item> - <property name="text"> - <string>June</string> - </property> - </item> - <item> - <property name="text"> - <string>July</string> - </property> - </item> - <item> - <property name="text"> - <string>August</string> - </property> - </item> - <item> - <property name="text"> - <string>September</string> - </property> - </item> - <item> - <property name="text"> - <string>October</string> - </property> - </item> - <item> - <property name="text"> - <string>November</string> - </property> - </item> - <item> - <property name="text"> - <string>December</string> - </property> - </item> - </widget> - </item> - <item> - <widget class="QComboBox" name="combo_birthday"/> - </item> - </layout> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_console_id"> - <property name="text"> - <string>Console ID:</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_birthday"> - <property name="text"> - <string>Birthday</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QPushButton" name="button_regenerate_console_id"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="layoutDirection"> - <enum>Qt::RightToLeft</enum> - </property> + <item row="4" column="0"> + <widget class="QCheckBox" name="rng_seed_checkbox"> <property name="text"> - <string>Regenerate</string> + <string>RNG Seed</string> </property> </widget> </item> - <item row="2" column="1"> + <item row="1" column="1"> <widget class="QComboBox" name="combo_sound"> <item> <property name="text"> @@ -241,49 +154,37 @@ </item> </widget> </item> - <item row="5" column="0"> - <widget class="QCheckBox" name="rng_seed_checkbox"> - <property name="text"> - <string>RNG Seed</string> - </property> - </widget> - </item> - <item row="1" column="0"> + <item row="0" column="0"> <widget class="QLabel" name="label_language"> <property name="text"> <string>Language</string> </property> </widget> </item> - <item row="5" column="1"> - <widget class="QLineEdit" name="rng_seed_edit"> + <item row="2" column="1"> + <widget class="QPushButton" name="button_regenerate_console_id"> <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="font"> - <font> - <family>Lucida Console</family> - </font> - </property> - <property name="inputMask"> - <string notr="true">HHHHHHHH</string> + <property name="layoutDirection"> + <enum>Qt::RightToLeft</enum> </property> - <property name="maxLength"> - <number>8</number> + <property name="text"> + <string>Regenerate</string> </property> </widget> </item> - <item row="4" column="0"> + <item row="3" column="0"> <widget class="QCheckBox" name="custom_rtc_checkbox"> <property name="text"> <string>Custom RTC</string> </property> </widget> </item> - <item row="4" column="1"> + <item row="3" column="1"> <widget class="QDateTimeEdit" name="custom_rtc_edit"> <property name="minimumDate"> <date> @@ -297,6 +198,27 @@ </property> </widget> </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="rng_seed_edit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <family>Lucida Console</family> + </font> + </property> + <property name="inputMask"> + <string notr="true">HHHHHHHH</string> + </property> + <property name="maxLength"> + <number>8</number> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.cpp b/src/yuzu/configuration/configure_touchscreen_advanced.cpp index 9c1561e9d..bad224239 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.cpp +++ b/src/yuzu/configuration/configure_touchscreen_advanced.cpp @@ -12,29 +12,29 @@ ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent) ui->setupUi(this); connect(ui->restore_defaults_button, &QPushButton::pressed, this, - &ConfigureTouchscreenAdvanced::restoreDefaults); + &ConfigureTouchscreenAdvanced::RestoreDefaults); - loadConfiguration(); + LoadConfiguration(); resize(0, 0); } ConfigureTouchscreenAdvanced::~ConfigureTouchscreenAdvanced() = default; -void ConfigureTouchscreenAdvanced::applyConfiguration() { +void ConfigureTouchscreenAdvanced::ApplyConfiguration() { Settings::values.touchscreen.finger = ui->finger_box->value(); Settings::values.touchscreen.diameter_x = ui->diameter_x_box->value(); Settings::values.touchscreen.diameter_y = ui->diameter_y_box->value(); Settings::values.touchscreen.rotation_angle = ui->angle_box->value(); } -void ConfigureTouchscreenAdvanced::loadConfiguration() { +void ConfigureTouchscreenAdvanced::LoadConfiguration() { ui->finger_box->setValue(Settings::values.touchscreen.finger); ui->diameter_x_box->setValue(Settings::values.touchscreen.diameter_x); ui->diameter_y_box->setValue(Settings::values.touchscreen.diameter_y); ui->angle_box->setValue(Settings::values.touchscreen.rotation_angle); } -void ConfigureTouchscreenAdvanced::restoreDefaults() { +void ConfigureTouchscreenAdvanced::RestoreDefaults() { ui->finger_box->setValue(0); ui->diameter_x_box->setValue(15); ui->diameter_y_box->setValue(15); diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.h b/src/yuzu/configuration/configure_touchscreen_advanced.h index 3d0772c87..94edd85b1 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.h +++ b/src/yuzu/configuration/configure_touchscreen_advanced.h @@ -18,13 +18,13 @@ public: explicit ConfigureTouchscreenAdvanced(QWidget* parent); ~ConfigureTouchscreenAdvanced() override; - void applyConfiguration(); + void ApplyConfiguration(); private: /// Load configuration settings. - void loadConfiguration(); + void LoadConfiguration(); /// Restore all buttons to their default values. - void restoreDefaults(); + void RestoreDefaults(); std::unique_ptr<Ui::ConfigureTouchscreenAdvanced> ui; }; diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp index 9dc34412d..8cacb75f3 100644 --- a/src/yuzu/configuration/configure_web.cpp +++ b/src/yuzu/configuration/configure_web.cpp @@ -22,12 +22,12 @@ ConfigureWeb::ConfigureWeb(QWidget* parent) #ifndef USE_DISCORD_PRESENCE ui->discord_group->setVisible(false); #endif - this->setConfiguration(); + SetConfiguration(); } ConfigureWeb::~ConfigureWeb() = default; -void ConfigureWeb::setConfiguration() { +void ConfigureWeb::SetConfiguration() { ui->web_credentials_disclaimer->setWordWrap(true); ui->telemetry_learn_more->setOpenExternalLinks(true); ui->telemetry_learn_more->setText( @@ -56,7 +56,7 @@ void ConfigureWeb::setConfiguration() { ui->toggle_discordrpc->setChecked(UISettings::values.enable_discord_presence); } -void ConfigureWeb::applyConfiguration() { +void ConfigureWeb::ApplyConfiguration() { Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked(); UISettings::values.enable_discord_presence = ui->toggle_discordrpc->isChecked(); if (user_verified) { @@ -121,6 +121,6 @@ void ConfigureWeb::OnLoginVerified() { } } -void ConfigureWeb::retranslateUi() { +void ConfigureWeb::RetranslateUI() { ui->retranslateUi(this); } diff --git a/src/yuzu/configuration/configure_web.h b/src/yuzu/configuration/configure_web.h index 7752ae4a1..49bee171b 100644 --- a/src/yuzu/configuration/configure_web.h +++ b/src/yuzu/configuration/configure_web.h @@ -19,8 +19,8 @@ public: explicit ConfigureWeb(QWidget* parent = nullptr); ~ConfigureWeb() override; - void applyConfiguration(); - void retranslateUi(); + void ApplyConfiguration(); + void RetranslateUI(); private: void RefreshTelemetryID(); @@ -28,7 +28,7 @@ private: void VerifyLogin(); void OnLoginVerified(); - void setConfiguration(); + void SetConfiguration(); bool user_verified = true; QFutureWatcher<bool> verify_watcher; diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 82d2826ba..4f30e9147 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -8,7 +8,9 @@ #include <vector> #include <QDir> +#include <QFile> #include <QFileInfo> +#include <QSettings> #include "common/common_paths.h" #include "common/file_util.h" @@ -30,13 +32,108 @@ #include "yuzu/ui_settings.h" namespace { + +QString GetGameListCachedObject(const std::string& filename, const std::string& ext, + const std::function<QString()>& generator) { + if (!UISettings::values.cache_game_list || filename == "0000000000000000") { + return generator(); + } + + const auto path = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" + + DIR_SEP + filename + '.' + ext; + + FileUtil::CreateFullPath(path); + + if (!FileUtil::Exists(path)) { + const auto str = generator(); + + QFile file{QString::fromStdString(path)}; + if (file.open(QFile::WriteOnly)) { + file.write(str.toUtf8()); + } + + return str; + } + + QFile file{QString::fromStdString(path)}; + if (file.open(QFile::ReadOnly)) { + return QString::fromUtf8(file.readAll()); + } + + return generator(); +} + +std::pair<std::vector<u8>, std::string> GetGameListCachedObject( + const std::string& filename, const std::string& ext, + const std::function<std::pair<std::vector<u8>, std::string>()>& generator) { + if (!UISettings::values.cache_game_list || filename == "0000000000000000") { + return generator(); + } + + const auto path1 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" + + DIR_SEP + filename + ".jpeg"; + const auto path2 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" + + DIR_SEP + filename + ".appname.txt"; + + FileUtil::CreateFullPath(path1); + + if (!FileUtil::Exists(path1) || !FileUtil::Exists(path2)) { + const auto [icon, nacp] = generator(); + + QFile file1{QString::fromStdString(path1)}; + if (!file1.open(QFile::WriteOnly)) { + LOG_ERROR(Frontend, "Failed to open cache file."); + return generator(); + } + + if (!file1.resize(icon.size())) { + LOG_ERROR(Frontend, "Failed to resize cache file to necessary size."); + return generator(); + } + + if (file1.write(reinterpret_cast<const char*>(icon.data()), icon.size()) != icon.size()) { + LOG_ERROR(Frontend, "Failed to write data to cache file."); + return generator(); + } + + QFile file2{QString::fromStdString(path2)}; + if (file2.open(QFile::WriteOnly)) { + file2.write(nacp.data(), nacp.size()); + } + + return std::make_pair(icon, nacp); + } + + QFile file1(QString::fromStdString(path1)); + QFile file2(QString::fromStdString(path2)); + + if (!file1.open(QFile::ReadOnly)) { + LOG_ERROR(Frontend, "Failed to open cache file for reading."); + return generator(); + } + + if (!file2.open(QFile::ReadOnly)) { + LOG_ERROR(Frontend, "Failed to open cache file for reading."); + return generator(); + } + + std::vector<u8> vec(file1.size()); + if (file1.read(reinterpret_cast<char*>(vec.data()), vec.size()) != + static_cast<s64>(vec.size())) { + return generator(); + } + + const auto data = file2.readAll(); + return std::make_pair(vec, data.toStdString()); +} + void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const FileSys::NCA& nca, std::vector<u8>& icon, std::string& name) { - auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); - if (icon_file != nullptr) - icon = icon_file->ReadAllBytes(); - if (nacp != nullptr) - name = nacp->GetApplicationName(); + std::tie(icon, name) = GetGameListCachedObject( + fmt::format("{:016X}", patch_manager.GetTitleID()), {}, [&patch_manager, &nca] { + const auto [nacp, icon_f] = patch_manager.ParseControlNCA(nca); + return std::make_pair(icon_f->ReadAllBytes(), nacp->GetApplicationName()); + }); } bool HasSupportedFileExtension(const std::string& file_name) { @@ -114,8 +211,11 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri }; if (UISettings::values.show_add_ons) { - list.insert( - 2, new GameListItem(FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable()))); + const auto patch_versions = GetGameListCachedObject( + fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] { + return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable()); + }); + list.insert(2, new GameListItem(patch_versions)); } return list; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index cef2cc1ae..cd32623b4 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -238,15 +238,13 @@ void GMainWindow::ProfileSelectorSelectProfile() { dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); dialog.setWindowModality(Qt::WindowModal); - dialog.exec(); - - if (!dialog.GetStatus()) { + if (dialog.exec() == QDialog::Rejected) { emit ProfileSelectorFinishedSelection(std::nullopt); return; } Service::Account::ProfileManager manager; - const auto uuid = manager.GetUser(dialog.GetIndex()); + const auto uuid = manager.GetUser(static_cast<std::size_t>(dialog.GetIndex())); if (!uuid.has_value()) { emit ProfileSelectorFinishedSelection(std::nullopt); return; @@ -261,9 +259,8 @@ void GMainWindow::SoftwareKeyboardGetText( dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); dialog.setWindowModality(Qt::WindowModal); - dialog.exec(); - if (!dialog.GetStatus()) { + if (dialog.exec() == QDialog::Rejected) { emit SoftwareKeyboardFinishedText(std::nullopt); return; } @@ -850,11 +847,6 @@ bool GMainWindow::LoadROM(const QString& filename) { QMessageBox::critical(this, tr("Error while loading ROM!"), tr("The ROM format is not supported.")); break; - case Core::System::ResultStatus::ErrorSystemMode: - LOG_CRITICAL(Frontend, "Failed to load ROM!"); - QMessageBox::critical(this, tr("Error while loading ROM!"), - tr("Could not determine the system mode.")); - break; case Core::System::ResultStatus::ErrorVideoCore: QMessageBox::critical( this, tr("An error occurred initializing the video core."), @@ -901,11 +893,12 @@ void GMainWindow::SelectAndSetCurrentUser() { dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); dialog.setWindowModality(Qt::WindowModal); - dialog.exec(); - if (dialog.GetStatus()) { - Settings::values.current_user = static_cast<s32>(dialog.GetIndex()); + if (dialog.exec() == QDialog::Rejected) { + return; } + + Settings::values.current_user = dialog.GetIndex(); } void GMainWindow::BootGame(const QString& filename) { @@ -1055,14 +1048,13 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); ASSERT(program_id != 0); - const auto select_profile = [this]() -> s32 { + const auto select_profile = [this] { QtProfileSelectionDialog dialog(this); dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); dialog.setWindowModality(Qt::WindowModal); - dialog.exec(); - if (!dialog.GetStatus()) { + if (dialog.exec() == QDialog::Rejected) { return -1; } @@ -1070,11 +1062,12 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target }; const auto index = select_profile(); - if (index == -1) + if (index == -1) { return; + } Service::Account::ProfileManager manager; - const auto user_id = manager.GetUser(index); + const auto user_id = manager.GetUser(static_cast<std::size_t>(index)); ASSERT(user_id); path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, @@ -1296,10 +1289,10 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { } ConfigurePerGameGeneral dialog(this, title_id); - dialog.loadFromFile(v_file); + dialog.LoadFromFile(v_file); auto result = dialog.exec(); if (result == QDialog::Accepted) { - dialog.applyConfiguration(); + dialog.ApplyConfiguration(); const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); if (reload) { @@ -1396,6 +1389,8 @@ void GMainWindow::OnMenuInstallToNAND() { tr("The file was successfully installed.")); game_list->PopulateAsync(UISettings::values.game_directory_path, UISettings::values.game_directory_deepscan); + FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + + DIR_SEP + "game_list"); }; const auto failed = [this]() { @@ -1688,26 +1683,31 @@ void GMainWindow::ToggleWindowMode() { } void GMainWindow::OnConfigure() { - ConfigureDialog configureDialog(this, hotkey_registry); - auto old_theme = UISettings::values.theme; + const auto old_theme = UISettings::values.theme; const bool old_discord_presence = UISettings::values.enable_discord_presence; - auto result = configureDialog.exec(); - if (result == QDialog::Accepted) { - configureDialog.applyConfiguration(); - InitializeHotkeys(); - if (UISettings::values.theme != old_theme) - UpdateUITheme(); - if (UISettings::values.enable_discord_presence != old_discord_presence) - SetDiscordEnabled(UISettings::values.enable_discord_presence); - const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); - if (reload) { - game_list->PopulateAsync(UISettings::values.game_directory_path, - UISettings::values.game_directory_deepscan); - } + ConfigureDialog configure_dialog(this, hotkey_registry); + const auto result = configure_dialog.exec(); + if (result != QDialog::Accepted) { + return; + } - config->Save(); + configure_dialog.ApplyConfiguration(); + InitializeHotkeys(); + if (UISettings::values.theme != old_theme) { + UpdateUITheme(); + } + if (UISettings::values.enable_discord_presence != old_discord_presence) { + SetDiscordEnabled(UISettings::values.enable_discord_presence); } + + const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); + if (reload) { + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); + } + + config->Save(); } void GMainWindow::OnLoadAmiibo() { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 7bf82e665..1137bbc7a 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -104,7 +104,7 @@ signals: void ErrorDisplayFinished(); - void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid); + void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid); void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); void SoftwareKeyboardFinishedCheckDialog(); diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h index dbd318e20..a62cd6911 100644 --- a/src/yuzu/ui_settings.h +++ b/src/yuzu/ui_settings.h @@ -79,6 +79,7 @@ struct Values { uint8_t row_1_text_id; uint8_t row_2_text_id; std::atomic_bool is_game_list_reload_pending{false}; + bool cache_game_list; }; extern Values values; diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index 297dab653..b5f06ab9e 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -4,6 +4,8 @@ add_executable(yuzu-cmd config.cpp config.h default_ini.h + emu_window/emu_window_sdl2_gl.cpp + emu_window/emu_window_sdl2_gl.h emu_window/emu_window_sdl2.cpp emu_window/emu_window_sdl2.h resource.h diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 8f104062d..a6edc089a 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -2,53 +2,27 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <algorithm> -#include <cstdlib> -#include <string> -#define SDL_MAIN_HANDLED #include <SDL.h> -#include <fmt/format.h> -#include <glad/glad.h> #include "common/logging/log.h" -#include "common/scm_rev.h" -#include "common/string_util.h" -#include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" #include "input_common/motion_emu.h" #include "input_common/sdl/sdl.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" -class SDLGLContext : public Core::Frontend::GraphicsContext { -public: - explicit SDLGLContext() { - // create a hidden window to make the shared context against - window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position - SDL_WINDOWPOS_UNDEFINED, // y position - Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, - SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); - context = SDL_GL_CreateContext(window); - } - - ~SDLGLContext() { - SDL_GL_DeleteContext(context); - SDL_DestroyWindow(window); - } - - void MakeCurrent() override { - SDL_GL_MakeCurrent(window, context); - } - - void DoneCurrent() override { - SDL_GL_MakeCurrent(window, nullptr); +EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { + LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); + exit(1); } + InputCommon::Init(); + SDL_SetMainReady(); +} - void SwapBuffers() override {} - -private: - SDL_Window* window; - SDL_GLContext context; -}; +EmuWindow_SDL2::~EmuWindow_SDL2() { + InputCommon::Shutdown(); + SDL_Quit(); +} void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); @@ -139,112 +113,6 @@ void EmuWindow_SDL2::Fullscreen() { SDL_MaximizeWindow(render_window); } -bool EmuWindow_SDL2::SupportsRequiredGLExtensions() { - std::vector<std::string> unsupported_ext; - - if (!GLAD_GL_ARB_direct_state_access) - unsupported_ext.push_back("ARB_direct_state_access"); - if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) - unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); - if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) - unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); - if (!GLAD_GL_ARB_multi_bind) - unsupported_ext.push_back("ARB_multi_bind"); - - // Extensions required to support some texture formats. - if (!GLAD_GL_EXT_texture_compression_s3tc) - unsupported_ext.push_back("EXT_texture_compression_s3tc"); - if (!GLAD_GL_ARB_texture_compression_rgtc) - unsupported_ext.push_back("ARB_texture_compression_rgtc"); - if (!GLAD_GL_ARB_depth_buffer_float) - unsupported_ext.push_back("ARB_depth_buffer_float"); - - for (const std::string& ext : unsupported_ext) - LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext); - - return unsupported_ext.empty(); -} - -EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { - // Initialize the window - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { - LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); - exit(1); - } - - InputCommon::Init(); - - SDL_SetMainReady(); - - const SDL_GLprofile profile = Settings::values.use_compatibility_profile - ? SDL_GL_CONTEXT_PROFILE_COMPATIBILITY - : SDL_GL_CONTEXT_PROFILE_CORE; - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); - SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - - std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, - Common::g_scm_branch, Common::g_scm_desc); - render_window = - SDL_CreateWindow(window_title.c_str(), - SDL_WINDOWPOS_UNDEFINED, // x position - SDL_WINDOWPOS_UNDEFINED, // y position - Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, - SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - - if (render_window == nullptr) { - LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError()); - exit(1); - } - - if (fullscreen) { - Fullscreen(); - } - gl_context = SDL_GL_CreateContext(render_window); - - if (gl_context == nullptr) { - LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); - exit(1); - } - - if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { - LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError()); - exit(1); - } - - if (!SupportsRequiredGLExtensions()) { - LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting..."); - exit(1); - } - - OnResize(); - OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); - SDL_PumpEvents(); - SDL_GL_SetSwapInterval(false); - LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, - Common::g_scm_desc); - Settings::LogSettings(); - - DoneCurrent(); -} - -EmuWindow_SDL2::~EmuWindow_SDL2() { - InputCommon::Shutdown(); - SDL_GL_DeleteContext(gl_context); - SDL_Quit(); -} - -void EmuWindow_SDL2::SwapBuffers() { - SDL_GL_SwapWindow(render_window); -} - void EmuWindow_SDL2::PollEvents() { SDL_Event event; @@ -257,7 +125,11 @@ void EmuWindow_SDL2::PollEvents() { case SDL_WINDOWEVENT_RESIZED: case SDL_WINDOWEVENT_MAXIMIZED: case SDL_WINDOWEVENT_RESTORED: + OnResize(); + break; case SDL_WINDOWEVENT_MINIMIZED: + case SDL_WINDOWEVENT_EXPOSED: + is_shown = event.window.event == SDL_WINDOWEVENT_EXPOSED; OnResize(); break; case SDL_WINDOWEVENT_CLOSE: @@ -300,20 +172,6 @@ void EmuWindow_SDL2::PollEvents() { } } -void EmuWindow_SDL2::MakeCurrent() { - SDL_GL_MakeCurrent(render_window, gl_context); -} - -void EmuWindow_SDL2::DoneCurrent() { - SDL_GL_MakeCurrent(render_window, nullptr); -} - -void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( - const std::pair<unsigned, unsigned>& minimal_size) { - +void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) { SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); } - -std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const { - return std::make_unique<SDLGLContext>(); -} diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index 17e98227f..d8051ebdf 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h @@ -15,24 +15,13 @@ public: explicit EmuWindow_SDL2(bool fullscreen); ~EmuWindow_SDL2(); - /// Swap buffers to display the next frame - void SwapBuffers() override; - /// Polls window events void PollEvents() override; - /// Makes the graphics context current for the caller thread - void MakeCurrent() override; - - /// Releases the GL context from the caller thread - void DoneCurrent() override; - - std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; - /// Whether the window is still open, and a close request hasn't yet been sent bool IsOpen() const; -private: +protected: /// Called by PollEvents when a key is pressed or released. void OnKeyEvent(int key, u8 state); @@ -60,20 +49,15 @@ private: /// Called when user passes the fullscreen parameter flag void Fullscreen(); - /// Whether the GPU and driver supports the OpenGL extension required - bool SupportsRequiredGLExtensions(); - /// Called when a configuration change affects the minimal size of the window - void OnMinimalClientAreaChangeRequest( - const std::pair<unsigned, unsigned>& minimal_size) override; + void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; /// Is the window still open? bool is_open = true; + /// Is the window being shown? + bool is_shown = true; + /// Internal SDL2 render window SDL_Window* render_window; - - using SDL_GLContext = void*; - /// The OpenGL context associated with the window - SDL_GLContext gl_context; }; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp new file mode 100644 index 000000000..904022137 --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp @@ -0,0 +1,154 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <cstdlib> +#include <string> +#define SDL_MAIN_HANDLED +#include <SDL.h> +#include <fmt/format.h> +#include <glad/glad.h> +#include "common/logging/log.h" +#include "common/scm_rev.h" +#include "common/string_util.h" +#include "core/settings.h" +#include "input_common/keyboard.h" +#include "input_common/main.h" +#include "input_common/motion_emu.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" + +class SDLGLContext : public Core::Frontend::GraphicsContext { +public: + explicit SDLGLContext() { + // create a hidden window to make the shared context against + window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position + SDL_WINDOWPOS_UNDEFINED, // y position + Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, + SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + context = SDL_GL_CreateContext(window); + } + + ~SDLGLContext() { + SDL_GL_DeleteContext(context); + SDL_DestroyWindow(window); + } + + void MakeCurrent() override { + SDL_GL_MakeCurrent(window, context); + } + + void DoneCurrent() override { + SDL_GL_MakeCurrent(window, nullptr); + } + + void SwapBuffers() override {} + +private: + SDL_Window* window; + SDL_GLContext context; +}; + +bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { + std::vector<std::string> unsupported_ext; + + if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) + unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); + if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) + unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); + if (!GLAD_GL_ARB_multi_bind) + unsupported_ext.push_back("ARB_multi_bind"); + + // Extensions required to support some texture formats. + if (!GLAD_GL_EXT_texture_compression_s3tc) + unsupported_ext.push_back("EXT_texture_compression_s3tc"); + if (!GLAD_GL_ARB_texture_compression_rgtc) + unsupported_ext.push_back("ARB_texture_compression_rgtc"); + if (!GLAD_GL_ARB_depth_buffer_float) + unsupported_ext.push_back("ARB_depth_buffer_float"); + + for (const std::string& ext : unsupported_ext) + LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext); + + return unsupported_ext.empty(); +} + +EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscreen) { + const SDL_GLprofile profile = Settings::values.use_compatibility_profile + ? SDL_GL_CONTEXT_PROFILE_COMPATIBILITY + : SDL_GL_CONTEXT_PROFILE_CORE; + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + + std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, + Common::g_scm_branch, Common::g_scm_desc); + render_window = + SDL_CreateWindow(window_title.c_str(), + SDL_WINDOWPOS_UNDEFINED, // x position + SDL_WINDOWPOS_UNDEFINED, // y position + Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + + if (render_window == nullptr) { + LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError()); + exit(1); + } + + if (fullscreen) { + Fullscreen(); + } + gl_context = SDL_GL_CreateContext(render_window); + + if (gl_context == nullptr) { + LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); + exit(1); + } + + if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { + LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError()); + exit(1); + } + + if (!SupportsRequiredGLExtensions()) { + LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting..."); + exit(1); + } + + OnResize(); + OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); + SDL_PumpEvents(); + SDL_GL_SetSwapInterval(false); + LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, + Common::g_scm_desc); + Settings::LogSettings(); + + DoneCurrent(); +} + +EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { + SDL_GL_DeleteContext(gl_context); +} + +void EmuWindow_SDL2_GL::SwapBuffers() { + SDL_GL_SwapWindow(render_window); +} + +void EmuWindow_SDL2_GL::MakeCurrent() { + SDL_GL_MakeCurrent(render_window, gl_context); +} + +void EmuWindow_SDL2_GL::DoneCurrent() { + SDL_GL_MakeCurrent(render_window, nullptr); +} + +std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { + return std::make_unique<SDLGLContext>(); +} diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h new file mode 100644 index 000000000..630deba93 --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h @@ -0,0 +1,34 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "core/frontend/emu_window.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2.h" + +class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { +public: + explicit EmuWindow_SDL2_GL(bool fullscreen); + ~EmuWindow_SDL2_GL(); + + /// Swap buffers to display the next frame + void SwapBuffers() override; + + /// Makes the graphics context current for the caller thread + void MakeCurrent() override; + + /// Releases the GL context from the caller thread + void DoneCurrent() override; + + std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; + +private: + /// Whether the GPU and driver supports the OpenGL extension required + bool SupportsRequiredGLExtensions(); + + using SDL_GLContext = void*; + /// The OpenGL context associated with the window + SDL_GLContext gl_context; +}; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index d3734927b..129d8ca73 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -31,6 +31,7 @@ #include "video_core/renderer_base.h" #include "yuzu_cmd/config.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" #include "core/file_sys/registered_cache.h" @@ -173,7 +174,7 @@ int main(int argc, char** argv) { Settings::values.use_gdbstub = use_gdbstub; Settings::Apply(); - std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen)}; + std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2_GL>(fullscreen)}; if (!Settings::values.use_multi_core) { // Single core mode must acquire OpenGL context for entire emulation session @@ -191,7 +192,7 @@ int main(int argc, char** argv) { switch (load_result) { case Core::System::ResultStatus::ErrorGetLoader: - LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filepath.c_str()); + LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath); return -1; case Core::System::ResultStatus::ErrorLoader: LOG_CRITICAL(Frontend, "Failed to load ROM!"); @@ -199,9 +200,6 @@ int main(int argc, char** argv) { case Core::System::ResultStatus::ErrorNotInitialized: LOG_CRITICAL(Frontend, "CPUCore not initialized"); return -1; - case Core::System::ResultStatus::ErrorSystemMode: - LOG_CRITICAL(Frontend, "Failed to determine system mode!"); - return -1; case Core::System::ResultStatus::ErrorVideoCore: LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!"); return -1; |